diff options
| author | Goraf <22941576+Goraf@users.noreply.github.com> | 2018-01-10 13:27:54 +0100 | 
|---|---|---|
| committer | Goraf <22941576+Goraf@users.noreply.github.com> | 2018-01-19 01:39:33 +0100 | 
| commit | 97bb7e79c15952e7f9bf47caef4e3c09dd63c8df (patch) | |
| tree | b501cec701c8f98f738b3157f9195c090981d072 /protocols | |
| parent | 7f1d5f6915ecfbb804319d33e4712455def63182 (diff) | |
Gadu-Gadu: complete update to libgadu-1.10.1-post
Diffstat (limited to 'protocols')
25 files changed, 7417 insertions, 6028 deletions
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj index 330e678774..b290e4b0a5 100644 --- a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj +++ b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj @@ -233,15 +233,27 @@      <ClCompile Include="src\libgadu\dcc7.cpp">        <PrecompiledHeader>NotUsing</PrecompiledHeader>      </ClCompile> +    <ClCompile Include="src\libgadu\debug.cpp"> +      <PrecompiledHeader>NotUsing</PrecompiledHeader> +    </ClCompile> +    <ClCompile Include="src\libgadu\encoding.cpp"> +      <PrecompiledHeader>NotUsing</PrecompiledHeader> +    </ClCompile>      <ClCompile Include="src\libgadu\events.cpp">        <PrecompiledHeader>NotUsing</PrecompiledHeader>      </ClCompile> +    <ClCompile Include="src\libgadu\handlers.cpp"> +      <PrecompiledHeader>NotUsing</PrecompiledHeader> +    </ClCompile>      <ClCompile Include="src\libgadu\http.cpp">        <PrecompiledHeader>NotUsing</PrecompiledHeader>      </ClCompile>      <ClCompile Include="src\libgadu\libgadu.cpp">        <PrecompiledHeader>NotUsing</PrecompiledHeader>      </ClCompile> +    <ClCompile Include="src\libgadu\message.cpp"> +      <PrecompiledHeader>NotUsing</PrecompiledHeader> +    </ClCompile>      <ClCompile Include="src\libgadu\pthread.cpp">        <PrecompiledHeader>NotUsing</PrecompiledHeader>      </ClCompile> @@ -264,11 +276,15 @@    <ItemGroup>      <ClInclude Include="src\gg_proto.h" />      <ClInclude Include="src\libgadu\compat.h" /> +    <ClInclude Include="src\libgadu\debug.h" /> +    <ClInclude Include="src\libgadu\encoding.h" />      <ClInclude Include="src\libgadu\internal.h" />      <ClInclude Include="src\libgadu\libgadu.h" /> +    <ClInclude Include="src\libgadu\message.h" />      <ClInclude Include="src\libgadu\protocol.h" />      <ClInclude Include="src\libgadu\pthread.h" />      <ClInclude Include="src\libgadu\resolver.h" /> +    <ClInclude Include="src\libgadu\session.h" />      <ClInclude Include="src\libgadu\win32.h" />      <ClInclude Include="src\dynstuff.h" />      <ClInclude Include="src\gg.h" /> diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters index e73472cc97..ff862fd048 100644 --- a/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters +++ b/protocols/Gadu-Gadu/Gadu-Gadu_15.vcxproj.filters @@ -117,6 +117,18 @@      <ClCompile Include="src\stdafx.cpp">        <Filter>Source Files</Filter>      </ClCompile> +    <ClCompile Include="src\libgadu\debug.cpp"> +      <Filter>Source Files\libgadu</Filter> +    </ClCompile> +    <ClCompile Include="src\libgadu\encoding.cpp"> +      <Filter>Source Files\libgadu</Filter> +    </ClCompile> +    <ClCompile Include="src\libgadu\handlers.cpp"> +      <Filter>Source Files\libgadu</Filter> +    </ClCompile> +    <ClCompile Include="src\libgadu\message.cpp"> +      <Filter>Source Files\libgadu</Filter> +    </ClCompile>    </ItemGroup>    <ItemGroup>      <ClInclude Include="src\libgadu\compat.h"> @@ -137,6 +149,9 @@      <ClInclude Include="src\libgadu\resolver.h">        <Filter>Source Files\libgadu</Filter>      </ClInclude> +    <ClInclude Include="src\libgadu\session.h"> +      <Filter>Source Files\libgadu</Filter> +    </ClInclude>      <ClInclude Include="src\libgadu\win32.h">        <Filter>Source Files\libgadu</Filter>      </ClInclude> @@ -155,6 +170,15 @@      <ClInclude Include="src\gg_proto.h">        <Filter>Header Files</Filter>      </ClInclude> +    <ClInclude Include="src\libgadu\debug.h"> +      <Filter>Source Files\libgadu</Filter> +    </ClInclude> +    <ClInclude Include="src\libgadu\encoding.h"> +      <Filter>Source Files\libgadu</Filter> +    </ClInclude> +    <ClInclude Include="src\libgadu\message.h"> +      <Filter>Source Files\libgadu</Filter> +    </ClInclude>    </ItemGroup>    <ItemGroup>      <ResourceCompile Include="res\resource.rc"> diff --git a/protocols/Gadu-Gadu/src/core.cpp b/protocols/Gadu-Gadu/src/core.cpp index c0492fe247..e89ab034ea 100644 --- a/protocols/Gadu-Gadu/src/core.cpp +++ b/protocols/Gadu-Gadu/src/core.cpp @@ -1123,36 +1123,36 @@ retry:  		}
  		break;
 -		case GG_EVENT_XML_ACTION:
 -			if (getByte(GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) {
 -				wchar_t *xmlAction = mir_a2u(e->event.xml_action.data);
 -				wchar_t *tag = mir_a2u("events");
 -				HXML hXml = xmlParseString(xmlAction, nullptr, tag);
 -
 -				if (hXml != nullptr) {
 -					mir_free(tag);
 -					tag = mir_a2u("event/type");
 -					HXML node = xmlGetChildByPath(hXml, tag, 0);
 -					char *type = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
 -
 -					mir_free(tag);
 -					tag = mir_a2u("event/sender");
 -					node = xmlGetChildByPath(hXml, tag, 0);
 -					char *sender = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
 -					debugLogA("mainthread() (%x): XML Action type: %s.", this, type != nullptr ? type : "unknown");
 -					// Avatar change notify
 -					if (type != nullptr && !mir_strcmp(type, "28")) {
 -						debugLogA("mainthread() (%x): Client %s changed his avatar.", this, sender);
 -						requestAvatarInfo(getcontact(atoi(sender), 0, 0, nullptr), 0);
 -					}
 -					mir_free(type);
 -					mir_free(sender);
 -					xmlDestroyNode(hXml);
 -				}
 -				mir_free(tag);
 -				mir_free(xmlAction);
 -			}
 -			break;
 +		//case GG_EVENT_XML_ACTION:
 +		//	if (getByte(GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) {
 +		//		wchar_t *xmlAction = mir_a2u(e->event.xml_action.data);
 +		//		wchar_t *tag = mir_a2u("events");
 +		//		HXML hXml = xmlParseString(xmlAction, nullptr, tag);
 +
 +		//		if (hXml != nullptr) {
 +		//			mir_free(tag);
 +		//			tag = mir_a2u("event/type");
 +		//			HXML node = xmlGetChildByPath(hXml, tag, 0);
 +		//			char *type = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
 +
 +		//			mir_free(tag);
 +		//			tag = mir_a2u("event/sender");
 +		//			node = xmlGetChildByPath(hXml, tag, 0);
 +		//			char *sender = node != nullptr ? mir_u2a(xmlGetText(node)) : nullptr;
 +		//			debugLogA("mainthread() (%x): XML Action type: %s.", this, type != nullptr ? type : "unknown");
 +		//			// Avatar change notify
 +		//			if (type != nullptr && !mir_strcmp(type, "28")) {
 +		//				debugLogA("mainthread() (%x): Client %s changed his avatar.", this, sender);
 +		//				requestAvatarInfo(getcontact(atoi(sender), 0, 0, nullptr), 0);
 +		//			}
 +		//			mir_free(type);
 +		//			mir_free(sender);
 +		//			xmlDestroyNode(hXml);
 +		//		}
 +		//		mir_free(tag);
 +		//		mir_free(xmlAction);
 +		//	}
 +		//	break;
  		case GG_EVENT_TYPING_NOTIFICATION:
  		{
 diff --git a/protocols/Gadu-Gadu/src/gg.cpp b/protocols/Gadu-Gadu/src/gg.cpp index b07ebcb25c..52dfd08679 100644 --- a/protocols/Gadu-Gadu/src/gg.cpp +++ b/protocols/Gadu-Gadu/src/gg.cpp @@ -402,7 +402,6 @@ static const ggdebug_eventype2string[] =     {GG_EVENT_DCC7_PENDING,         "GG_EVENT_DCC7_PENDING"},
     {GG_EVENT_XML_EVENT,            "GG_EVENT_XML_EVENT"},
     {GG_EVENT_DISCONNECT_ACK,       "GG_EVENT_DISCONNECT_ACK"},
 -   {GG_EVENT_XML_ACTION,           "GG_EVENT_XML_ACTION"},
     {GG_EVENT_TYPING_NOTIFICATION,  "GG_EVENT_TYPING_NOTIFICATION"},
     {GG_EVENT_USER_DATA,            "GG_EVENT_USER_DATA"},
     {GG_EVENT_MULTILOGON_MSG,       "GG_EVENT_MULTILOGON_MSG"},
 diff --git a/protocols/Gadu-Gadu/src/libgadu/common.cpp b/protocols/Gadu-Gadu/src/libgadu/common.cpp index b115aa0df4..07e6a2646b 100644 --- a/protocols/Gadu-Gadu/src/libgadu/common.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/common.cpp @@ -1,9 +1,11 @@ -/* coding: UTF-8 */
  /* $Id: common.c 13762 2011-08-09 12:35:16Z dezred $ */
  /*
 - *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
 + *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
   *                          Robert J. Woźny <speedy@ziew.org>
 + *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
 + *                          Tomasz Chiliński <chilek@chilan.com>
 + *                          Adam Wysocki <gophi@ekg.chmurka.net>
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU Lesser General Public License Version
 @@ -20,29 +22,6 @@   *  USA.
   */
 -/*
 - * Funkcje konwersji między UTF-8 i CP1250 są oparte o kod biblioteki iconv.
 - * Informacje o prawach autorskich oryginalnego kodu zamieszczono poniżej:
 - *
 - * Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc.
 - * This file is part of the GNU LIBICONV Library.
 - *
 - * The GNU LIBICONV Library is free software; you can redistribute it
 - * and/or modify it under the terms of the GNU Library General Public
 - * License as published by the Free Software Foundation; either version 2
 - * of the License, or (at your option) any later version.
 - *
 - * The GNU LIBICONV Library is distributed in the hope that it will be
 - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * Library General Public License for more details.
 - *
 - * You should have received a copy of the GNU Library General Public
 - * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
 - * If not, write to the Free Software Foundation, Inc., 51 Franklin Street,
 - * Fifth Floor, Boston, MA 02110-1301, USA.
 - */
 -
  /**
   * \file common.c
   *
 @@ -75,111 +54,9 @@  #endif /* _WIN32 */
  #include "libgadu.h"
 -#include "internal.h"
 -
 -/**
 - * Plik, do którego będą przekazywane informacje odpluskwiania.
 - *
 - * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację
 - * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub
 - * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane
 - * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr).
 - *
 - * \ingroup debug
 - */
 -FILE *gg_debug_file = NULL;
 -
 -#ifndef GG_DEBUG_DISABLE
 -
 -/**
 - * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
 - *
 - * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w
 - * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana.
 - * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu.
 - *
 - * \param sess Struktura sesji (może być \c NULL)
 - * \param level Poziom informacji
 - * \param format Format wiadomości (zgodny z \c printf)
 - * \param ap Lista argumentów (zgodna z \c printf)
 - */
 -static void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
 -{
 -	if (gg_debug_handler_session)
 -		(*gg_debug_handler_session)(sess, level, format, ap);
 -	else if (gg_debug_handler)
 -		(*gg_debug_handler)(level, format, ap);
 -	else if (gg_debug_level & level)
 -		vfprintf(gg_debug_file ? gg_debug_file : stderr, format, ap);
 -}
  /**
 - * \internal Przekazuje informację odpluskawiania.
 - *
 - * \param level Poziom wiadomości
 - * \param format Format wiadomości (zgodny z \c printf)
 - *
 - * \ingroup debug
 - */
 -void gg_debug(int level, const char *format, ...)
 -{
 -	va_list ap;
 -	int old_errno = errno;
 -	va_start(ap, format);
 -	gg_debug_common(NULL, level, format, ap);
 -	va_end(ap);
 -	errno = old_errno;
 -}
 -
 -/**
 - * \internal Przekazuje informację odpluskwiania związaną z sesją.
 - *
 - * \param sess Struktura sesji
 - * \param level Poziom wiadomości
 - * \param format Format wiadomości (zgodny z \c printf)
 - *
 - * \ingroup debug
 - */
 -void gg_debug_session(struct gg_session *sess, int level, const char *format, ...)
 -{
 -	va_list ap;
 -	int old_errno = errno;
 -	va_start(ap, format);
 -	gg_debug_common(sess, level, format, ap);
 -	va_end(ap);
 -	errno = old_errno;
 -}
 -
 -/**
 - * \internal Przekazuje informację odpluskwiania związane z zawartością pamięci.
 - *
 - * \param sess Struktura sesji
 - * \param buf Adres w pamięci
 - * \param buf_length Ilość danych do wyświetlenia
 - * \param format Format wiadomości (zgodny z \c printf)
 - *
 - * \ingroup debug
 - */
 -void gg_debug_dump_session(struct gg_session *sess, const void *buf, unsigned int buf_length, const char *format, ...)
 -{
 -	va_list ap;
 -
 -	if ((gg_debug_level & GG_DEBUG_DUMP)) {
 -		  unsigned int i;
 -
 -		  va_start(ap, format);
 -		  gg_debug_common(sess, GG_DEBUG_DUMP, format, ap);
 -		  for (i = 0; i < buf_length; ++i)
 -			  gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", ((unsigned char*) buf)[i]);
 -		  gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
 -		  va_end(ap);
 -	  }
 -}
 -
 -#endif
 -
 -/**
   * \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik.
   *
   * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
 @@ -481,7 +358,8 @@ void gg_chomp(char *line)   */
  char *gg_urlencode(const char *str)
  {
 -	char *q, *buf, hex[] = "0123456789abcdef";
 +	char *q, *buf;
 +	const char hex[] = "0123456789abcdef";
  	const char *p;
  	unsigned int size = 0;
 @@ -694,7 +572,7 @@ char *gg_base64_decode(const char *buf)   * \return Zaalokowany bufor z tekstem lub NULL, jeśli serwer pośredniczący
   *         nie jest używany lub nie wymaga autoryzacji.
   */
 -char *gg_proxy_auth()
 +char *gg_proxy_auth(void)
  {
  	char *tmp, *enc, *out;
  	size_t tmp_size;
 @@ -729,33 +607,73 @@ char *gg_proxy_auth()  /**
   * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej.
   */
 -static uint32_t gg_crc32_table[256];
 -
 -/**
 - * \internal Flaga wypełnienia tablicy pomocniczej do wyznaczania sumy
 - * kontrolnej.
 - */
 -static int gg_crc32_initialized = 0;
 -
 -/**
 - * \internal Tworzy tablicę pomocniczą do wyznaczania sumy kontrolnej.
 - */
 -static void gg_crc32_make_table(void)
 +static const uint32_t gg_crc32_table[256] =
  {
 -	uint32_t h = 1;
 -	unsigned int i, j;
 -
 -	memset(gg_crc32_table, 0, sizeof(gg_crc32_table));
 -
 -	for (i = 128; i; i >>= 1) {
 -		h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
 -
 -		for (j = 0; j < 256; j += 2 * i)
 -			gg_crc32_table[i + j] = gg_crc32_table[j] ^ h;
 -	}
 -
 -	gg_crc32_initialized = 1;
 -}
 +	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
 +	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
 +	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
 +	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
 +	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
 +	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
 +	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
 +	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
 +	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
 +	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
 +	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
 +	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
 +	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
 +	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
 +	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
 +	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
 +	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
 +	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
 +	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
 +	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
 +	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
 +	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
 +	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
 +	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
 +	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
 +	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
 +	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
 +	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
 +	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
 +	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
 +	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
 +	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
 +	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
 +	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
 +	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
 +	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
 +	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
 +	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
 +	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
 +	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
 +	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
 +	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
 +	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
 +	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
 +	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
 +	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
 +	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
 +	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
 +	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
 +	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
 +	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
 +	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
 +	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
 +	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
 +	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
 +	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
 +	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
 +	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
 +	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
 +	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
 +	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
 +	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
 +	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
 +	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
 +};
  /**
   * Wyznacza sumę kontrolną CRC32.
 @@ -769,10 +687,7 @@ static void gg_crc32_make_table(void)   */
  uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
  {
 -	if (!gg_crc32_initialized)
 -		gg_crc32_make_table();
 -
 -	if (!buf || len < 0)
 +	if (buf == NULL || len < 0)
  		return crc;
  	crc ^= 0xffffffffL;
 @@ -783,187 +698,6 @@ uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)  	return crc ^ 0xffffffffL;
  }
 -/**
 - * \internal Tablica konwersji między CP1250 a UTF-8.
 - */
 -static const uint16_t table_cp1250[] = {
 -	0x20ac, '?',    0x201a,    '?', 0x201e, 0x2026, 0x2020, 0x2021, 
 -	   '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, 
 -	   '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 
 -	   '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, 
 -	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, 
 -	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, 
 -	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 
 -	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, 
 -	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 
 -	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, 
 -	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 
 -	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, 
 -	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 
 -	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, 
 -	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 
 -	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, 
 -};
 -
 -/**
 - * \internal Zamienia tekst kodowany CP1250 na UTF-8.
 - *
 - * \param b Tekst źródłowy w CP1250.
 - *
 - * \return Zaalokowany bufor z tekstem w UTF-8.
 - */
 -char *gg_cp_to_utf8(const char *b)
 -{
 -	unsigned char *buf = (unsigned char *) b;
 -	char *newbuf;
 -	int newlen = 0;
 -	int i, j;
 -
 -	for (i = 0; buf[i]; i++) {
 -		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
 -
 -		if (znak < 0x80)	newlen += 1;
 -		else if (znak < 0x800)	newlen += 2;
 -		else			newlen += 3;
 -	}
 -
 -	if (!(newbuf = (char*)malloc(newlen + 1))) {
 -		gg_debug(GG_DEBUG_MISC, "// gg_cp_to_utf8() not enough memory\n");
 -		return NULL;
 -	}
 -
 -	for (i = 0, j = 0; buf[i]; i++) {
 -		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
 -		int count;
 -
 -		if (znak < 0x80)	count = 1;
 -		else if (znak < 0x800)	count = 2;
 -		else			count = 3;
 -
 -		switch (count) {
 -			case 3: newbuf[j+2] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0x800;
 -			case 2: newbuf[j+1] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0xc0;
 -			case 1: newbuf[j] = (char)znak;
 -		}
 -		j += count;
 -	}
 -	newbuf[j] = '\0';
 -
 -	return newbuf;
 -}
 -
 -/**
 - * \internal Dekoduje jeden znak UTF-8.
 - *
 - * \note Funkcja nie jest kompletną implementacją UTF-8, a wersją uproszczoną
 - * do potrzeb kodowania CP1250.
 - *
 - * \param s Tekst źródłowy.
 - * \param n Długość tekstu źródłowego.
 - * \param ch Wskaźnik na wynik dekodowania.
 - *
 - * \return Długość zdekodowanej sekwencji w bajtach lub wartość mniejsza
 - * od zera w przypadku błędu.
 - */
 -static int gg_utf8_helper(unsigned char *s, int n, uint16_t *ch)
 -{
 -	unsigned char c = s[0];
 -
 -	if (c < 0x80) {
 -		*ch = c;
 -		return 1;
 -	}
 -
 -	if (c < 0xc2) 
 -		return -1;
 -
 -	if (c < 0xe0) {
 -		if (n < 2)
 -			return -2;
 -		if (!((s[1] ^ 0x80) < 0x40))
 -			return -1;
 -		*ch = ((uint16_t) (c & 0x1f) << 6) | (uint16_t) (s[1] ^ 0x80);
 -		return 2;
 -	} 
 -	
 -	if (c < 0xf0) {
 -		if (n < 3)
 -			return -2;
 -		if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0)))
 -			return -1;
 -		*ch = ((uint16_t) (c & 0x0f) << 12) | ((uint16_t) (s[1] ^ 0x80) << 6) | (uint16_t) (s[2] ^ 0x80);
 -		return 3;
 -	}
 -
 -	return -1;
 -}
 -
 -/**
 - * \internal Zamienia tekst kodowany UTF-8 na CP1250.
 - *
 - * \param b Tekst źródłowy w UTF-8.
 - *
 - * \return Zaalokowany bufor z tekstem w CP1250.
 - */
 -char *gg_utf8_to_cp(const char *b)
 -{
 -	unsigned char *buf = (unsigned char *) b;
 -	char *newbuf;
 -	int newlen = 0;
 -	int len;
 -	int i, j;
 -
 -	len = (int)strlen(b);
 -
 -	for (i = 0; i < len; newlen++) {
 -		uint16_t discard;
 -		int ret;
 -		
 -		ret = gg_utf8_helper(&buf[i], len - i, &discard);
 -
 -		if (ret > 0)
 -			i += ret;
 -		else
 -			i++;
 -	}
 -
 -	if (!(newbuf = (char*)malloc(newlen + 1))) {
 -		gg_debug(GG_DEBUG_MISC, "// gg_utf8_to_cp() not enough memory\n");
 -		return NULL;
 -	}
 -
 -	for (i = 0, j = 0; buf[i]; j++) {
 -		uint16_t znak;
 -		int ret, k;
 -
 -		ret = gg_utf8_helper(&buf[i], len - i, &znak);
 -
 -		if (ret > 0) {
 -			i += ret;
 -		} else {
 -			znak = '?';
 -			i++;
 -		}
 -
 -		if (znak < 0x80) {
 -			newbuf[j] = (char)znak;
 -			continue;
 -		}
 -
 -		newbuf[j] = '?';
 -
 -		for (k = 0; k < (sizeof(table_cp1250)/sizeof(table_cp1250[0])); k++) {
 -			if (table_cp1250[k] == znak) {
 -				newbuf[j] = (0x80 | k);
 -				break;
 -			}
 -		}
 -	}
 -	newbuf[j] = '\0';
 -
 -	return newbuf;
 -}
 -
  /*
   * Local variables:
   * c-indentation-style: k&r
 diff --git a/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp b/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp index 8131698be5..465a3cae4a 100644 --- a/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/dcc7.cpp @@ -1,5 +1,4 @@ -/* coding: UTF-8 */ -/* $Id: dcc7.c,v 1.2 2007-07-20 23:00:49 wojtekka Exp $ */ +/* $Id$ */  /*   *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl> @@ -65,6 +64,13 @@  #include "protocol.h"  #include "resolver.h"  #include "internal.h" +#include "debug.h" + +#define gg_debug_dcc(dcc, level, fmt, ...) \ +	gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt, __VA_ARGS__) + +#define gg_debug_dump_dcc(dcc, level, buf, len) \ +	gg_debug_dump(((dcc) != NULL) ? (dcc)->sess : NULL, level, buf, len)  /**   * \internal Dodaje połączenie bezpośrednie do sesji. @@ -148,7 +154,7 @@ static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_  	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {  		if (empty) { -			if (tmp->peer_uin == uin && tmp->state != GG_STATE_WAITING_FOR_ACCEPT) +			if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)  				return tmp;  		} else {  			if (!memcmp(&tmp->cid, &id, sizeof(id))) @@ -168,16 +174,16 @@ static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_   */  static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)  { -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc);  	if (dcc == NULL || dcc->sess == NULL) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n");  		errno = EINVAL;  		return -1;  	}  	if (dcc->sess->resolver_start(&dcc->fd, &dcc->resolver, GG_RELAY_HOST) == -1) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno));  		return -1;  	} @@ -197,16 +203,16 @@ static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)   */  static int gg_dcc7_connect(struct gg_dcc7 *dcc)  { -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc);  	if (dcc == NULL) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");  		errno = EINVAL;  		return -1;  	}  	if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");  		return -1;  	} @@ -229,60 +235,56 @@ static int gg_dcc7_connect(struct gg_dcc7 *dcc)  static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port)  {  	struct sockaddr_in sin; +	int sin_len = sizeof(sin); +	int errsv;  	SOCKET fd; -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);  	if (!dcc) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");  		errno = EINVAL;  		return -1;  	}  	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno)); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));  		return -1;  	} -	// XXX losować porty? -	 -	if (!port) -		port = GG_DEFAULT_DCC_PORT; - -	while (1) { -		sin.sin_family = AF_INET; -		sin.sin_addr.s_addr = INADDR_ANY; -		sin.sin_port = htons(port); - -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port); +	sin.sin_family = AF_INET; +	sin.sin_addr.s_addr = INADDR_ANY; +	sin.sin_port = htons(port); -		if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin))) -			break; +	if (bind(fd, (struct sockaddr*) &sin, sizeof(sin)) == -1) { +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port); +		goto fail; +	} -		if (port++ == 65535) { -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n"); -			gg_sock_close(fd); -			errno = ENOENT; -			return -1; -		} +	if (port == 0 && getsockname(fd, (struct sockaddr*) &sin, &sin_len) == -1) { +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port); +		goto fail;  	}  	if (listen(fd, 1)) { -		int errsv = errno; -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno)); -		gg_sock_close(fd); -		errno = errsv; -		return -1; +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno)); +		goto fail;  	}  	dcc->fd = fd; -	dcc->local_port = port; +	dcc->local_port = ntohs(sin.sin_port);  	dcc->state = GG_STATE_LISTENING;  	dcc->check = GG_CHECK_READ;  	dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;  	return 0; + +fail: +	errsv = errno; +	gg_sock_close(fd); +	errno = errsv; +	return -1;  }  /** @@ -299,7 +301,7 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)  	uint16_t local_port;  	uint32_t count; -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);  	if (dcc == NULL)  		return -1; @@ -321,18 +323,14 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)  	else  		dcc->local_addr = dcc->sess->external_addr; -	gg_debug_session((dcc)->sess, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port); +	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port);  	memset(&pkt, 0, sizeof(pkt));  	pkt.uin = gg_fix32(dcc->peer_uin);  	pkt.type = GG_DCC7_TYPE_P2P;  	pkt.id = dcc->cid;  	snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port); -	// TODO: implement hash count -	// we MUST fill hash to recive from server request for server connection -	//snprintf((char*) pkt.hash, sizeof(pkt.hash), "0"); -	count = dcc->local_addr + external_port * rand(); -	mir_snprintf(pkt.hash, sizeof(pkt.hash), "%d", count); +	snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", dcc->local_addr + external_port * rand());  	return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);  } @@ -346,14 +344,14 @@ static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)   */  static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)  { -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);  	if (dcc->reverse) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");  		return -1;  	} -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n"); +	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");  	gg_sock_close(dcc->fd);  	dcc->fd = -1;  	dcc->reverse = 1; @@ -584,10 +582,10 @@ int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)  {  	struct gg_dcc7_accept pkt; -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);  	if (!dcc || !dcc->sess) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");  		errno = EFAULT;  		return -1;  	} @@ -619,10 +617,10 @@ int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)  {  	struct gg_dcc7_reject pkt; -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);  	if (!dcc || !dcc->sess) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");  		errno = EFAULT;  		return -1;  	} @@ -674,9 +672,9 @@ int gg_dcc7_abort(struct gg_dcc7 *dcc)   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ -int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len)  { -	struct gg_dcc7_id_reply *p = (gg_dcc7_id_reply*)payload; +	const struct gg_dcc7_id_reply *p = (gg_dcc7_id_reply*)payload;  	struct gg_dcc7 *tmp;  	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -724,9 +722,9 @@ int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ -int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len)  { -	struct gg_dcc7_accept *p = (struct gg_dcc7_accept*)payload; +	const struct gg_dcc7_accept *p = (struct gg_dcc7_accept*)payload;  	struct gg_dcc7 *dcc;  	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -766,11 +764,11 @@ int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *pay   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ -int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len)  { -	struct gg_dcc7_info *p = (gg_dcc7_info*)payload; +	const struct gg_dcc7_info *p = (gg_dcc7_info*)payload;  	struct gg_dcc7 *dcc; -	char *tmp; +	const char *tmp;  	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);  	gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() received address: %s, hash: %s\n", p->info, p->hash); @@ -805,7 +803,7 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo  		}  		if (dcc->state == GG_STATE_WAITING_FOR_INFO) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() wainting for info so send one\n"); +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() waiting for info so send one\n");  			gg_dcc7_listen_and_send_info(dcc);  			return 0;  		} @@ -842,7 +840,7 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo  #endif  		if (gg_dcc7_get_relay_addr(dcc) == -1) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n");  			e->type = GG_EVENT_DCC7_ERROR;  			e->event.dcc7_error = GG_ERROR_DCC7_RELAY;  			e->event.dcc7_error_ex.dcc7 = dcc; @@ -853,7 +851,7 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo  		gg_send_packet(dcc->sess, GG_DCC7_INFO, payload, len, NULL); -		break; +		return 0;  	default:  		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type); @@ -916,9 +914,9 @@ int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *paylo   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ -int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len)  { -	struct gg_dcc7_reject *p = (struct gg_dcc7_reject*)payload; +	const struct gg_dcc7_reject *p = (struct gg_dcc7_reject*)payload;  	struct gg_dcc7 *dcc;  	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -955,9 +953,9 @@ int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *pay   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ -int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, const void *payload, int len)  { -	struct gg_dcc7_aborted *p = (gg_dcc7_aborted*)payload; +	const struct gg_dcc7_aborted *p = (gg_dcc7_aborted*)payload;  	struct gg_dcc7 *dcc;  	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_abort(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -996,9 +994,9 @@ int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, void *payl   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ -int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) +int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len)  { -	struct gg_dcc7_new *p = (gg_dcc7_new*)payload; +	const struct gg_dcc7_new *p = (gg_dcc7_new*)payload;  	struct gg_dcc7 *dcc;  	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len); @@ -1083,10 +1081,10 @@ int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payloa   */  static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)  { -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);  	if (!dcc) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");  		errno = EINVAL;  		return -1;  	} @@ -1130,16 +1128,16 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  {  	struct gg_event *e; -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);  	if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");  		errno = EINVAL;  		return NULL;  	}  	if (!(e = (gg_event*)malloc(sizeof(struct gg_event)))) { -		gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n"); +		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");  		return NULL;  	} @@ -1154,21 +1152,21 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			int one = 1;  			int sin_len = sizeof(sin); -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");  			if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));  				return e;  			} -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));  #ifdef FIONBIO  			if (ioctl(fd, FIONBIO, &one) == -1) {  #else  			if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {  #endif -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));  				gg_sock_close(fd);  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; @@ -1198,7 +1196,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			int res = 0, error = 0;  			int error_size = sizeof(error); -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");  			dcc->soft_timeout = 0; @@ -1206,7 +1204,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				error = ETIMEDOUT;  			if (error || (res = gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));  				if (dcc->relay) {  					for (dcc->relay_index++; dcc->relay_index < dcc->relay_count; dcc->relay_index++) { @@ -1218,7 +1216,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  					}  					if (dcc->relay_index >= dcc->relay_count) { -						gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available"); +						gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");  						e->type = GG_EVENT_DCC7_ERROR;  						e->event.dcc7_error = GG_ERROR_DCC7_RELAY;  						e->event.dcc7_error_ex.dcc7 = dcc; @@ -1238,7 +1236,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				}  			} -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");  			dcc->state = GG_STATE_SENDING_ID;  			dcc->check = GG_CHECK_WRITE; @@ -1252,14 +1250,14 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  		{  			int res; -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");  			if (!dcc->relay) {  				struct gg_dcc7_welcome_p2p welcome, welcome_ok;  				welcome_ok.id = dcc->cid;  				if ((res = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { -					gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); +					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));  					e->type = GG_EVENT_DCC7_ERROR;  					e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;  					e->event.dcc7_error_ex.dcc7 = dcc; @@ -1267,7 +1265,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				}  				if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) { -					gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); +					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");  					e->type = GG_EVENT_DCC7_ERROR;  					e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;  					e->event.dcc7_error_ex.dcc7 = dcc; @@ -1279,7 +1277,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				welcome_ok.id = dcc->cid;  				if ((res = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { -					gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); +					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));  					e->type = GG_EVENT_DCC7_ERROR;  					e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;  					e->event.dcc7_error_ex.dcc7 = dcc; @@ -1287,7 +1285,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				}  				if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) { -					gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); +					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");  					e->type = GG_EVENT_DCC7_ERROR;  					e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;  					e->event.dcc7_error_ex.dcc7 = dcc; @@ -1311,7 +1309,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  		{  			int res; -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");  			if (!dcc->relay) {  				struct gg_dcc7_welcome_p2p welcome; @@ -1319,7 +1317,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				welcome.id = dcc->cid;  				if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { -					gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno)); +					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));  					e->type = GG_EVENT_DCC7_ERROR;  					e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;  					e->event.dcc7_error_ex.dcc7 = dcc; @@ -1332,7 +1330,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				welcome.id = dcc->cid;  				if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) { -					gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno)); +					gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));  					e->type = GG_EVENT_DCC7_ERROR;  					e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;  					e->event.dcc7_error_ex.dcc7 = dcc; @@ -1357,17 +1355,17 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			char buf[1024];  			int chunk, res; -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);  			if (dcc->offset >= dcc->size) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");  				e->type = GG_EVENT_DCC7_DONE;  				e->event.dcc7_done.dcc7 = dcc;  				return e;  			}  			if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_FILE;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1378,7 +1376,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				chunk = sizeof(buf);  			if ((res = read(dcc->file_fd, buf, chunk)) < 1) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1386,7 +1384,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			}  			if ((res = gg_sock_write(dcc->fd, buf, res)) == -1) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_NET;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1396,7 +1394,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			dcc->offset += res;  			if (dcc->offset >= dcc->size) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");  				e->type = GG_EVENT_DCC7_DONE;  				e->event.dcc7_done.dcc7 = dcc;  				return e; @@ -1414,17 +1412,17 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			char buf[1024];  			int res, wres; -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);  			if (dcc->offset >= dcc->size) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");  				e->type = GG_EVENT_DCC7_DONE;  				e->event.dcc7_done.dcc7 = dcc;  				return e;  			}  			if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < 1) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1434,7 +1432,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			// XXX zapisywać do skutku?  			if ((wres = write(dcc->file_fd, buf, res)) < res) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_FILE;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1444,7 +1442,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			dcc->offset += res;  			if (dcc->offset >= dcc->size) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");  				e->type = GG_EVENT_DCC7_DONE;  				e->event.dcc7_done.dcc7 = dcc;  				return e; @@ -1461,12 +1459,12 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  		{  			struct in_addr addr; -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n");  			if (gg_sock_read(dcc->fd, &addr, sizeof(addr)) < sizeof(addr) || addr.s_addr == INADDR_NONE) {  				int errno_save = errno; -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n");  				gg_sock_close(dcc->fd);  				dcc->fd = -1;  				dcc->sess->resolver_cleanup(&dcc->resolver, 0); @@ -1477,10 +1475,10 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				return e;  			} -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT);  			if ((dcc->fd = gg_connect(&addr, GG_RELAY_PORT, 1)) == -1) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_RELAY;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1500,10 +1498,10 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			int res_size = sizeof(res);  			struct gg_dcc7_relay_req pkt; -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n");  			if (gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_RELAY;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1517,10 +1515,11 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			pkt.type = gg_fix16(GG_DCC7_RELAY_TYPE_SERVER);  			pkt.dunno1 = gg_fix16(GG_DCC7_RELAY_DUNNO1); -			gg_debug_dump_session((dcc) ? (dcc)->sess : NULL, &pkt, sizeof(pkt), "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic)); +			gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic)); +			gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, (const char*)&pkt, sizeof(pkt));  			if ((res = gg_sock_write(dcc->fd, &pkt, sizeof(pkt))) != sizeof(pkt)) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n");  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_RELAY;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1542,12 +1541,12 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			int res;  			int i; -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n");  			if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < sizeof(*pkt)) {  				if (res == 0)  					errno = ECONNRESET; -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_RELAY;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1557,7 +1556,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			pkt = (struct gg_dcc7_relay_reply*) buf;  			if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount > 256) || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n");  				errno = EINVAL;  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_RELAY; @@ -1565,7 +1564,8 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				return e;  			} -		        gg_debug_dump_session((dcc) ? (dcc)->sess : NULL, buf, res, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic)); +			gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic)); +			gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, buf, res);  			free(dcc->relay_list); @@ -1574,7 +1574,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			dcc->relay_list = (gg_dcc7_relay*)malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t));  			if (dcc->relay_list == NULL) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory");  				dcc->relay_count = 0;  				free(e);  				return NULL; @@ -1589,7 +1589,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  				dcc->relay_list[i].family = srv.family;  				addr.s_addr = srv.addr; -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "//    %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "//    %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family);  			}  			dcc->relay = 1; @@ -1603,7 +1603,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  			}  			if (dcc->relay_index >= dcc->relay_count) { -				gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available"); +				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");  				e->type = GG_EVENT_DCC7_ERROR;  				e->event.dcc7_error = GG_ERROR_DCC7_RELAY;  				e->event.dcc7_error_ex.dcc7 = dcc; @@ -1615,7 +1615,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)  		default:  		{ -			gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n"); +			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");  			e->type = GG_EVENT_DCC7_ERROR;  			e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;  			e->event.dcc7_error_ex.dcc7 = dcc; @@ -1636,7 +1636,7 @@ struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)   */  void gg_dcc7_free(struct gg_dcc7 *dcc)  { -	gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc); +	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);  	if (!dcc)  		return; @@ -1654,4 +1654,3 @@ void gg_dcc7_free(struct gg_dcc7 *dcc)  	free(dcc);  } - diff --git a/protocols/Gadu-Gadu/src/libgadu/debug.cpp b/protocols/Gadu-Gadu/src/libgadu/debug.cpp new file mode 100644 index 0000000000..5b7c0c6577 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/debug.cpp @@ -0,0 +1,301 @@ +/* + *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + *                          Robert J. Woźny <speedy@ziew.org> + *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org> + *                          Tomasz Chiliński <chilek@chilan.com> + *                          Adam Wysocki <gophi@ekg.chmurka.net> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +/** + * \file debug.c + * + * \brief Funkcje odpluskwiania + */ +#include <sys/types.h> +#ifdef _WIN32 +#include "win32.h" +#endif /* _WIN32 */ +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "libgadu.h" +#include "debug.h" + +/** + * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową + * składającą się ze stałych \c GG_DEBUG_... + * + * \ingroup debug + */ +int gg_debug_level = 0; + +/** + * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe + * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr). + * + * \param level Poziom rejestracji + * \param format Format wiadomości (zgodny z \c printf) + * \param ap Lista argumentów (zgodna z \c printf) + * + * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session. + * + * \ingroup debug + */ +void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; + +/** + * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe + * \c NULL, informacje są wysyłane do standardowego wyjścia błędu. + * + * \param sess Sesja której dotyczy informacja lub \c NULL + * \param level Poziom rejestracji + * \param format Format wiadomości (zgodny z \c printf) + * \param ap Lista argumentów (zgodna z \c printf) + * + * \note Funkcja przesłania przez \c gg_debug_handler_session. + * + * \ingroup debug + */ +void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL; + +/** + * Plik, do którego będą przekazywane informacje odpluskwiania. + * + * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację + * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub + * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane + * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr). + * + * \ingroup debug + */ +FILE *gg_debug_file = NULL; + +#ifndef GG_DEBUG_DISABLE + +/** + * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji. + * + * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w + * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana. + * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu. + * + * \param sess Struktura sesji (może być \c NULL) + * \param level Poziom informacji + * \param format Format wiadomości (zgodny z \c printf) + * \param ap Lista argumentów (zgodna z \c printf) + */ +void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap) +{ +	if (gg_debug_handler_session != NULL) +		(*gg_debug_handler_session)(sess, level, format, ap); +	else if (gg_debug_handler != NULL) +		(*gg_debug_handler)(level, format, ap); +	else if ((gg_debug_level & level) != 0) +		vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap); +} + + +/** + * \internal Przekazuje informację odpluskawiania. + * + * \param level Poziom wiadomości + * \param format Format wiadomości (zgodny z \c printf) + * + * \ingroup debug + */ +void gg_debug(int level, const char *format, ...) +{ +	va_list ap; +	int old_errno = errno; + +	va_start(ap, format); +	gg_debug_common(NULL, level, format, ap); +	va_end(ap); +	errno = old_errno; +} + +/** + * \internal Przekazuje informację odpluskwiania związaną z sesją. + * + * \param gs Struktura sesji + * \param level Poziom wiadomości + * \param format Format wiadomości (zgodny z \c printf) + * + * \ingroup debug + */ +void gg_debug_session(struct gg_session *gs, int level, const char *format, ...) +{ +	va_list ap; +	int old_errno = errno; + +	va_start(ap, format); +	gg_debug_common(gs, level, format, ap); +	va_end(ap); +	errno = old_errno; +} + +/** + * \internal Przekazuje zrzut bufora do odpluskwiania. + * + * \param gs Struktura sesji + * \param level Poziom wiadomości + * \param buf Bufor danych + * \param len Długość bufora danych + * + * \ingroup debug + */ +void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len) +{ +	char line[80]; +	int i, j; + +	for (i = 0; i < len; i += 16) { +		int ofs; + +		sprintf(line, "%.4x: ", i); +		ofs = 6; + +		for (j = 0; j < 16; j++) { +			if (i + j < len) +				sprintf(line + ofs, " %02x", (unsigned char) buf[i + j]); +			else +				sprintf(line + ofs, "   "); + +			ofs += 3; +		} + +		sprintf(line + ofs, "  "); +		ofs += 2; + +		for (j = 0; j < 16; j++) { +			unsigned char ch; + +			if (i + j < len) { +				ch = buf[i + j]; + +				if (ch < 32 || ch > 126) +					ch = '.'; +			} else { +				ch = ' '; +			} + +			line[ofs++] = ch; +		} + +		line[ofs++] = '\n'; +		line[ofs++] = 0; + +		gg_debug_session(gs, level, "%s", line); +	} +} + +/** + * \internal Zwraca ciąg z nazwą podanego stanu sesji. + * + * \param state Stan sesji. + * + * \return Ciąg z nazwą stanu + * + * \ingroup debug + */ +const char *gg_debug_state(enum gg_state_t state) +{ +	switch (state) { +#define GG_DEBUG_STATE(x) case x: return #x; +	GG_DEBUG_STATE(GG_STATE_IDLE) +	GG_DEBUG_STATE(GG_STATE_RESOLVING) +	GG_DEBUG_STATE(GG_STATE_CONNECTING) +	GG_DEBUG_STATE(GG_STATE_READING_DATA) +	GG_DEBUG_STATE(GG_STATE_ERROR) +	GG_DEBUG_STATE(GG_STATE_CONNECTING_HUB) +	GG_DEBUG_STATE(GG_STATE_CONNECTING_GG) +	GG_DEBUG_STATE(GG_STATE_READING_KEY) +	GG_DEBUG_STATE(GG_STATE_READING_REPLY) +	GG_DEBUG_STATE(GG_STATE_CONNECTED) +	GG_DEBUG_STATE(GG_STATE_SENDING_QUERY) +	GG_DEBUG_STATE(GG_STATE_READING_HEADER) +	GG_DEBUG_STATE(GG_STATE_PARSING) +	GG_DEBUG_STATE(GG_STATE_DONE) +	GG_DEBUG_STATE(GG_STATE_LISTENING) +	GG_DEBUG_STATE(GG_STATE_READING_UIN_1) +	GG_DEBUG_STATE(GG_STATE_READING_UIN_2) +	GG_DEBUG_STATE(GG_STATE_SENDING_ACK) +	GG_DEBUG_STATE(GG_STATE_READING_ACK) +	GG_DEBUG_STATE(GG_STATE_READING_REQUEST) +	GG_DEBUG_STATE(GG_STATE_SENDING_REQUEST) +	GG_DEBUG_STATE(GG_STATE_SENDING_FILE_INFO) +	GG_DEBUG_STATE(GG_STATE_READING_PRE_FILE_INFO) +	GG_DEBUG_STATE(GG_STATE_READING_FILE_INFO) +	GG_DEBUG_STATE(GG_STATE_SENDING_FILE_ACK) +	GG_DEBUG_STATE(GG_STATE_READING_FILE_ACK) +	GG_DEBUG_STATE(GG_STATE_SENDING_FILE_HEADER) +	GG_DEBUG_STATE(GG_STATE_READING_FILE_HEADER) +	GG_DEBUG_STATE(GG_STATE_GETTING_FILE) +	GG_DEBUG_STATE(GG_STATE_SENDING_FILE) +	GG_DEBUG_STATE(GG_STATE_READING_VOICE_ACK) +	GG_DEBUG_STATE(GG_STATE_READING_VOICE_HEADER) +	GG_DEBUG_STATE(GG_STATE_READING_VOICE_SIZE) +	GG_DEBUG_STATE(GG_STATE_READING_VOICE_DATA) +	GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_ACK) +	GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_REQUEST) +	GG_DEBUG_STATE(GG_STATE_READING_TYPE) +	GG_DEBUG_STATE(GG_STATE_TLS_NEGOTIATION) +	GG_DEBUG_STATE(GG_STATE_REQUESTING_ID) +	GG_DEBUG_STATE(GG_STATE_WAITING_FOR_ACCEPT) +	GG_DEBUG_STATE(GG_STATE_WAITING_FOR_INFO) +	GG_DEBUG_STATE(GG_STATE_READING_ID) +	GG_DEBUG_STATE(GG_STATE_SENDING_ID) +	GG_DEBUG_STATE(GG_STATE_RESOLVING_GG) +	GG_DEBUG_STATE(GG_STATE_RESOLVING_RELAY) +	GG_DEBUG_STATE(GG_STATE_CONNECTING_RELAY) +	GG_DEBUG_STATE(GG_STATE_READING_RELAY) +	GG_DEBUG_STATE(GG_STATE_DISCONNECTING) + +	// Celowo nie ma default, żeby kompilator wyłapał brakujące stany +	 +	} + +	return NULL; +} + +#else + +#undef gg_debug_common +void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap) +{ +} + +#undef gg_debug +void gg_debug(int level, const char *format, ...) +{ +} + +#undef gg_debug_session +void gg_debug_session(struct gg_session *gs, int level, const char *format, ...) +{ +} + +#undef gg_debug_dump +void gg_debug_dump(struct gg_session *gs, int level, const char *buf, int len) +{ +} + +#endif diff --git a/protocols/Gadu-Gadu/src/libgadu/debug.h b/protocols/Gadu-Gadu/src/libgadu/debug.h new file mode 100644 index 0000000000..8c51853b3e --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/debug.h @@ -0,0 +1,27 @@ +/* + *  (C) Copyright 2009 Wojtek Kaniewski <wojtekka@irc.pl> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +#ifndef LIBGADU_DEBUG_H +#define LIBGADU_DEBUG_H + +#include "libgadu.h" + +const char *gg_debug_state(enum gg_state_t state); +void gg_debug_dump(struct gg_session *sess, int level, const char *buf, size_t len); + +#endif /* LIBGADU_DEBUG_H */ diff --git a/protocols/Gadu-Gadu/src/libgadu/encoding.cpp b/protocols/Gadu-Gadu/src/libgadu/encoding.cpp new file mode 100644 index 0000000000..fa6af7eb6d --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/encoding.cpp @@ -0,0 +1,277 @@ +/* + *  (C) Copyright 2008-2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + *                          Wojtek Kaniewski <wojtekka@irc.pl> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +#ifdef _WIN32 +#include "win32.h" +#endif /* _WIN32 */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "libgadu.h" + +/** + * \file encoding.c + * + * \brief Funkcje konwersji kodowania tekstu + */ + +/** + * \internal Tablica konwersji CP1250 na Unikod. + */ +static const uint16_t table_cp1250[] = +{ +        0x20ac, '?',    0x201a, '?',    0x201e, 0x2026, 0x2020, 0x2021, +        '?',    0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, +        '?',    0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, +        '?',    0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, +        0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, +        0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, +        0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, +        0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, +        0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, +        0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, +        0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, +        0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, +        0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, +        0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, +        0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, +        0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, +}; + +/** + * \internal Zamienia tekst kodowany CP1250 na UTF-8. + * + * \param src Tekst źródłowy w CP1250. + * \param src_length Długość ciągu źródłowego (nigdy ujemna). + * \param dst_length Długość ciągu docelowego (jeśli -1, nieograniczona). + * + * \return Zaalokowany bufor z tekstem w UTF-8. + */ +static char *gg_encoding_convert_cp1250_utf8(const char *src, int src_length, int dst_length) +{ +	int i, j, len; +	char *result = NULL; + +	for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) { +		uint16_t uc; + +		if ((unsigned char) src[i] < 0x80) +			uc = (unsigned char) src[i]; +		else +			uc = table_cp1250[(unsigned char) src[i] - 128]; + +		if (uc < 0x80) +			len += 1; +		else if (uc < 0x800) +			len += 2; +		else +			len += 3; +	} + +	if ((dst_length != -1) && (len > dst_length)) +		len = dst_length; + +	result = (char *)malloc(len + 1); + +	if (result == NULL)  +		return NULL; + +	for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) { +		uint16_t uc; + +		if ((unsigned char) src[i] < 0x80) +			uc = (unsigned char) src[i]; +		else +			uc = table_cp1250[(unsigned char) src[i] - 128]; + +		if (uc < 0x80)  +			result[j++] = uc; +		else if (uc < 0x800) { +			if (j + 1 > len) +				break; +			result[j++] = 0xc0 | ((uc >> 6) & 0x1f); +			result[j++] = 0x80 | (uc & 0x3f); +		} else { +			if (j + 2 > len) +				break; +			result[j++] = 0xe0 | ((uc >> 12) & 0x1f); +			result[j++] = 0x80 | ((uc >> 6) & 0x3f); +			result[j++] = 0x80 | (uc & 0x3f); +		} +	} + +	result[j] = 0; + +	return result; +} + +/** + * \internal Zamienia tekst kodowany UTF-8 na CP1250. + * + * \param src Tekst źródłowy w UTF-8. + * \param src_length Długość ciągu źródłowego (nigdy ujemna). + * \param dst_length Długość ciągu docelowego (jeśli -1, nieograniczona). + * + * \return Zaalokowany bufor z tekstem w CP1250. + */ +static char *gg_encoding_convert_utf8_cp1250(const char *src, int src_length, int dst_length) +{ +	char *result; +	int i, j, len, uc_left = 0; +	uint32_t uc = 0, uc_min = 0; + +	for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) { +		if ((src[i] & 0xc0) == 0xc0) { +			len++; +		} else if ((src[i] & 0x80) == 0x00) { +			len++; +		} +	} + +	if ((dst_length != -1) && (len > dst_length)) +		len = dst_length; + +	result = (char *)malloc(len + 1); + +	if (result == NULL) +		return NULL; + +	for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) { +		if ((unsigned char) src[i] >= 0xf5) { +			if (uc_left != 0)  +				result[j++] = '?'; +			/* Restricted sequences */ +			result[j++] = '?'; +			uc_left = 0; +		} else if ((src[i] & 0xf8) == 0xf0) { +			if (uc_left != 0)  +				result[j++] = '?'; +			uc = src[i] & 0x07; +			uc_left = 3; +			uc_min = 0x10000; +		} else if ((src[i] & 0xf0) == 0xe0) { +			if (uc_left != 0)  +				result[j++] = '?'; +			uc = src[i] & 0x0f; +			uc_left = 2; +			uc_min = 0x800; +		} else if ((src[i] & 0xe0) == 0xc0) { +			if (uc_left != 0)  +				result[j++] = '?'; +			uc = src[i] & 0x1f; +			uc_left = 1; +			uc_min = 0x80; +		} else if ((src[i] & 0xc0) == 0x80) { +			if (uc_left > 0) { +				uc <<= 6; +				uc |= src[i] & 0x3f; +				uc_left--; + +				if (uc_left == 0) { +					int valid = 0; +					int k; + +					if (uc >= uc_min) { +						for (k = 0; k < 128; k++) { +							if (uc == table_cp1250[k]) { +								result[j++] = k + 128; +								valid = 1; +								break; +							} +						} +					} + +					if (!valid && uc != 0xfeff)	/* Byte Order Mark */ +						result[j++] = '?'; +				} +			} +		} else { +			if (uc_left != 0) { +				result[j++] = '?'; +				uc_left = 0; +			} +			result[j++] = src[i]; +		} +	} + +	if ((uc_left != 0) && (src[i] == 0)) +		result[j++] = '?'; + +	result[j] = 0; + +	return result; +} + +/** + * \internal Zamienia kodowanie tekstu. + * + * \param src Tekst źródłowy. + * \param src_encoding Kodowanie tekstu źródłowego. + * \param dst_encoding Kodowanie tekstu docelowego. + * \param src_length Długość ciągu źródłowego w bajtach (nigdy ujemna). + * \param dst_length Długość ciągu docelowego w bajtach (jeśli -1, nieograniczona). + * + * \return Zaalokowany bufor z tekstem w kodowaniu docelowym. + */ +char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding, gg_encoding_t dst_encoding, int src_length, int dst_length) +{ +	char *result; + +	if (src == NULL) { +		errno = EINVAL; +		return NULL; +	} + +	// specjalny przypadek obsługiwany ekspresowo +	if ((dst_encoding == src_encoding) && (dst_length == -1) && (src_length == -1)) +		return strdup(src); + +	if (src_length == -1) +		src_length = strlen(src); + +	if (dst_encoding == src_encoding) { +		int len; + +		if (dst_length == -1) +			len = src_length; +		else +			len = (src_length < dst_length) ? src_length : dst_length; + +		result = (char *)malloc(len + 1); + +		if (result == NULL) +			return NULL; + +		strncpy(result, src, len); +		result[len] = 0; + +		return result; +	} + +	if (dst_encoding == GG_ENCODING_CP1250 && src_encoding == GG_ENCODING_UTF8) +		return gg_encoding_convert_utf8_cp1250(src, src_length, dst_length); + +	if (dst_encoding == GG_ENCODING_UTF8 && src_encoding == GG_ENCODING_CP1250) +		return gg_encoding_convert_cp1250_utf8(src, src_length, dst_length); + +	errno = EINVAL; +	return NULL; +} diff --git a/protocols/Gadu-Gadu/src/libgadu/encoding.h b/protocols/Gadu-Gadu/src/libgadu/encoding.h new file mode 100644 index 0000000000..e386f48f18 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/encoding.h @@ -0,0 +1,27 @@ +/* + *  (C) Copyright 2008-2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + *                          Wojtek Kaniewski <wojtekka@irc.pl> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +#ifndef LIBGADU_ENCODING_H +#define LIBGADU_ENCODING_H + +#include "libgadu.h" + +char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding, gg_encoding_t dst_encoding, int src_length, int dst_length); + +#endif /* LIBGADU_SESSION_H */ diff --git a/protocols/Gadu-Gadu/src/libgadu/events.cpp b/protocols/Gadu-Gadu/src/libgadu/events.cpp index 0714b2b2a2..9cfd036730 100644 --- a/protocols/Gadu-Gadu/src/libgadu/events.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/events.cpp @@ -1,2862 +1,1070 @@ -/* coding: UTF-8 */ -/* $Id: events.c 13583 2011-04-12 12:51:18Z dezred $ */ - -/* - *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> - *                          Robert J. Woźny <speedy@ziew.org> - *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org> - *                          Adam Wysocki <gophi@ekg.chmurka.net> - * - *  This program is free software; you can redistribute it and/or modify - *  it under the terms of the GNU Lesser General Public License Version - *  2.1 as published by the Free Software Foundation. - * - *  This program is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU Lesser General Public License for more details. - * - *  You should have received a copy of the GNU Lesser General Public - *  License along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - *  USA. - */ - -/** - * \file events.c - * - * \brief Obsługa zdarzeń - */ - -#ifndef _WIN64 -#define _USE_32BIT_TIME_T -#endif - -#include <sys/types.h> -#ifdef _WIN32 -#include "win32.h" -#else -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#endif /* _WIN32 */ - -#include "compat.h" -#include "libgadu.h" -#include "protocol.h" -#include "internal.h" - -#include <errno.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <time.h> -#ifndef _WIN32 -#include <unistd.h> -#include <ctype.h> -#endif /* _WIN32 */ -#ifndef GG_CONFIG_MIRANDA -#ifdef GG_CONFIG_HAVE_OPENSSL -#  include <openssl/err.h> -#  include <openssl/x509.h> -#endif -#endif - +/* $Id$ */
 +
 +/*
 + *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
 + *                          Robert J. Woźny <speedy@ziew.org>
 + *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
 + *                          Adam Wysocki <gophi@ekg.chmurka.net>
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU Lesser General Public License Version
 + *  2.1 as published by the Free Software Foundation.
 + *
 + *  This program is distributed in the hope that it will be useful,
 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + *  GNU Lesser General Public License for more details.
 + *
 + *  You should have received a copy of the GNU Lesser General Public
 + *  License along with this program; if not, write to the Free Software
 + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
 + *  USA.
 + */
 +
 +/**
 + * \file events.c
 + *
 + * \brief Obsługa zdarzeń
 + */
 +
 +#ifndef _WIN64
 +#define _USE_32BIT_TIME_T
 +#endif
 +
 +#include <sys/types.h>
 +#ifdef _WIN32
 +#include "win32.h"
 +#else
 +#include <sys/ioctl.h>
 +#include <sys/socket.h>
 +#include <netinet/in.h>
 +#include <arpa/inet.h>
 +#endif /* _WIN32 */
 +
 +#include "compat.h"
 +#include "libgadu.h"
 +#include "protocol.h"
 +#include "internal.h"
 +#include "encoding.h"
 +#include "debug.h"
 +#include "session.h"
 +
 +#include <errno.h>
 +#include <stdio.h>
 +#include <string.h>
 +#include <stdlib.h>
 +#include <time.h>
 +#ifndef _WIN32
 +#include <unistd.h>
 +#include <ctype.h>
 +#endif /* _WIN32 */
 +#ifndef GG_CONFIG_MIRANDA
 +#ifdef GG_CONFIG_HAVE_GNUTLS
 +#  include <gnutls/gnutls.h>
 +#  include <gnutls/x509.h>
 +#endif
 +#ifdef GG_CONFIG_HAVE_OPENSSL
 +#  include <openssl/err.h>
 +#  include <openssl/x509.h>
 +#endif
 +#endif
 +
  extern SSL_API sslApi;
 - -/** - * Zwalnia pamięć zajmowaną przez informację o zdarzeniu. - * - * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci - * strukturę \c gg_event. - * - * \param e Struktura zdarzenia - * - * \ingroup events - */ -void gg_event_free(struct gg_event *e) -{ -	gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e); - -	if (!e) -		return; - -	switch (e->type) { -		case GG_EVENT_MSG: -		case GG_EVENT_MULTILOGON_MSG: -			free(e->event.msg.message); -			free(e->event.msg.formats); -			free(e->event.msg.recipients); -			free(e->event.msg.xhtml_message); -			break; - -		case GG_EVENT_NOTIFY: -			free(e->event.notify); -			break; - -		case GG_EVENT_NOTIFY60: -		{ -			int i; - -			for (i = 0; e->event.notify60[i].uin; i++) -				free(e->event.notify60[i].descr); - -			free(e->event.notify60); - -			break; -		} - -		case GG_EVENT_STATUS60: -			free(e->event.status60.descr); -			break; - -		case GG_EVENT_STATUS: -			free(e->event.status.descr); -			break; - -		case GG_EVENT_NOTIFY_DESCR: -			free(e->event.notify_descr.notify); -			free(e->event.notify_descr.descr); -			break; - -		case GG_EVENT_DCC_VOICE_DATA: -			free(e->event.dcc_voice_data.data); -			break; - -		case GG_EVENT_PUBDIR50_SEARCH_REPLY: -		case GG_EVENT_PUBDIR50_READ: -		case GG_EVENT_PUBDIR50_WRITE: -			gg_pubdir50_free(e->event.pubdir50); -			break; - -		case GG_EVENT_USERLIST: -			free(e->event.userlist.reply); -			break; - -		case GG_EVENT_IMAGE_REPLY: -			free(e->event.image_reply.filename); -			free(e->event.image_reply.image); -			break; - -		case GG_EVENT_XML_EVENT: -			free(e->event.xml_event.data); -			break; - -		case GG_EVENT_XML_ACTION: -			free(e->event.xml_action.data); -			break; - -		case GG_EVENT_USER_DATA: -		{ -			unsigned i, j; - -			for (i = 0; i < e->event.user_data.user_count; i++) { -				for (j = 0; j < e->event.user_data.users[i].attr_count; j++) { -					free(e->event.user_data.users[i].attrs[j].key); -					free(e->event.user_data.users[i].attrs[j].value); -				} - -				free(e->event.user_data.users[i].attrs); -			} - -			free(e->event.user_data.users); - -			break; -		} -	 -		case GG_EVENT_MULTILOGON_INFO: -		{ -			int i; - -			for (i = 0; i < e->event.multilogon_info.count; i++) -				free(e->event.multilogon_info.sessions[i].name); - -			free(e->event.multilogon_info.sessions); - -			break; -		} -	} - -	free(e); -} - -/** \cond internal */ - -/** - * \internal Usuwa obrazek z kolejki do wysłania. - * - * \param s Struktura sesji - * \param q Struktura obrazka - * \param freeq Flaga zwolnienia elementu kolejki - * - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd - */ -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) -{ -	if (!s || !q) { -		errno = EFAULT; -		return -1; -	} - -	if (s->images == q) -		s->images = q->next; -	else { -		struct gg_image_queue *qq; - -		for (qq = s->images; qq; qq = qq->next) { -			if (qq->next == q) { -				qq->next = q->next; -				break; -			} -		} -	} - -	if (freeq) { -		free(q->image); -		free(q->filename); -		free(q); -	} - -	return 0; -} - -/** - * \internal Analizuje przychodzący pakiet z obrazkiem. - * - * \param e Struktura zdarzenia - * \param p Bufor z danymi - * \param len Długość bufora - * \param sess Struktura sesji - * \param sender Numer nadawcy - * \param size Rozmiar pliku (z nagłówka) - * \param crc32 Suma kontrolna (z nagłówka) - */ -static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender, uint32_t size, uint32_t crc32) -{ -	struct gg_image_queue *q, *qq; - -	if (!p || !sess || !e) { -		errno = EFAULT; -		return; -	} - -	/* znajdź dany obrazek w kolejce danej sesji */ - -	for (qq = sess->images, q = NULL; qq; qq = qq->next) { -		if (sender == qq->sender && size == qq->size && crc32 == qq->crc32) { -			q = qq; -			break; -		} -	} - -	if (!q) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, size, crc32); -		return; -	} - -	if (p[0] == 0x05) { -		q->done = 0; - -		len -= sizeof(struct gg_msg_image_reply); -		p += sizeof(struct gg_msg_image_reply); - -		if (memchr(p, 0, len) == NULL) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); -			return; -		} - -		if (!(q->filename = strdup(p))) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); -			return; -		} - -		len -= (unsigned int)strlen(p) + 1; -		p += strlen(p) + 1; -	} else { -		len -= sizeof(struct gg_msg_image_reply); -		p += sizeof(struct gg_msg_image_reply); -	} - -	if (q->done + len > q->size) -		len = q->size - q->done; - -	memcpy(q->image + q->done, p, len); -	q->done += len; - -	/* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */ - -	if (q->done >= q->size) { -		e->type = GG_EVENT_IMAGE_REPLY; -		e->event.image_reply.sender = sender; -		e->event.image_reply.size = q->size; -		e->event.image_reply.crc32 = q->crc32; -		e->event.image_reply.filename = q->filename; -		e->event.image_reply.image = q->image; - -		gg_image_queue_remove(sess, q, 0); - -		free(q); -	} -} - -/** - * \internal Analizuje informacje rozszerzone wiadomości. - *  - * \param sess Struktura sesji. - * \param e Struktura zdarzenia. - * \param sender Numer nadawcy. - * \param p Wskaźnik na dane rozszerzone. - * \param packet_end Wskaźnik na koniec pakietu. - * - * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma - * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli - * wiadomość jest niepoprawna. - */ -static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end) -{ -	while (p < packet_end) { -		switch (*p) { -			case 0x01:		/* konferencja */ -			{ -				struct gg_msg_recipients *m = (gg_msg_recipients*)p; -				uint32_t i, count; - -				if (p + sizeof(*m) > packet_end) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1)\n"); -					goto malformed; -				} - -				memcpy(&count, &m->count, sizeof(count)); -				count = gg_fix32(count); -				p += sizeof(*m); - -				if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1.5)\n"); -					goto malformed; -				} - -				if (e->event.msg.recipients != NULL) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.recipients already exist\n"); -					goto malformed; -				} - -				e->event.msg.recipients = (uin_t*)malloc(count * sizeof(uin_t)); - -				if (e->event.msg.recipients == NULL) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for recipients data\n"); -					goto fail; -				} - -				memcpy(e->event.msg.recipients, p, count * sizeof(uin_t)); -				p += count * sizeof(uin_t); - -				for (i = 0; i < count; i++) -					e->event.msg.recipients[i] = gg_fix32(e->event.msg.recipients[i]); - -				e->event.msg.recipients_count = count; - -				break; -			} - -			case 0x02:		/* richtext */ -			{ -				uint16_t len; -				char *buf; - -				if (p + 3 > packet_end) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (2)\n"); -					goto malformed; -				} - -				memcpy(&len, p + 1, sizeof(uint16_t)); -				len = gg_fix16(len); - -				if (e->event.msg.formats != NULL) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.formats already exist\n"); -					goto malformed; -				} - -				buf = (char*)malloc(len); - -				if (buf == NULL) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for richtext data\n"); -					goto fail; -				} - -				p += 3; - -				if (p + len > packet_end) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3)\n"); -					free(buf); -					goto malformed; -				} - -				memcpy(buf, p, len); - -				e->event.msg.formats = buf; -				e->event.msg.formats_length = len; - -				p += len; - -				break; -			} - -			case 0x04:		/* image_request */ -			{ -				struct gg_msg_image_request *i = (gg_msg_image_request*)p; - -				if (p + sizeof(*i) > packet_end) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3.5)\n"); -					goto malformed; -				} - -				if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (1)\n"); -					goto malformed; -				} - -				memcpy(&e->event.image_request.size, &i->size, sizeof(i->size)); -				memcpy(&e->event.image_request.crc32, &i->crc32, sizeof(i->crc32)); - -				e->event.image_request.sender = sender; -				e->event.image_request.size = gg_fix32(e->event.image_request.size); -				e->event.image_request.crc32 = gg_fix32(e->event.image_request.crc32); - -				e->type = GG_EVENT_IMAGE_REQUEST; - -				goto handled; -			} - -			case 0x05:		/* image_reply */ -			case 0x06: -			{ -				struct gg_msg_image_reply *rep = (gg_msg_image_reply*)p; -				uint32_t size; -				uint32_t crc32; - -				if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (2)\n"); -					goto malformed; -				} - -				if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { - -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (4)\n"); -					goto malformed; -				} - -				memcpy(&size, &rep->size, sizeof(size)); -				memcpy(&crc32, &rep->crc32, sizeof(crc32)); -				size = gg_fix32(size); -				crc32 = gg_fix32(crc32); - -				if (p + sizeof(struct gg_msg_image_reply) == packet_end) { -					/* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */ - -					e->type = GG_EVENT_IMAGE_REPLY; -					e->event.image_reply.sender = sender; -					e->event.image_reply.size = 0; -					e->event.image_reply.crc32 = crc32; -					e->event.image_reply.filename = NULL; -					e->event.image_reply.image = NULL; -					goto handled; - -				} - -				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender, size, crc32); - -				goto handled; -			} - -			default: -			{ -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() unknown payload 0x%.2x\n", *p); -				p = packet_end; -			} -		} -	} - -	return 0; - -handled: -	return -1; - -fail: -	return -2; - -malformed: -	return -3; -} - -/** - * \internal Analizuje przychodzący pakiet z wiadomością. - * - * Rozbija pakiet na poszczególne składniki -- tekst, informacje - * o konferencjach, formatowani itd. - * - * \param h Wskaźnik do odebranego pakietu - * \param e Struktura zdarzenia - * \param sess Struktura sesji - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) -{ -	struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); -	char *p, *packet_end = (char*) r + h->length; -	int ctcp = 0; - -	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); - -	if (!r->seq && !r->msgclass) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); -		e->type = GG_EVENT_NONE; -		return 0; -	} - -	/* znajdź \0 */ -	for (p = (char*) r + sizeof(*r); ; p++) { -		if (p >= packet_end) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); -			goto malformed; -		} - -		if (*p == 0x02 && p == packet_end - 1) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); -			ctcp = 1; -			break; -		} - -		if (!*p) -			break; -	} - -	p++; - -	switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) { -		case -1:	// handled -			return 0; - -		case -2:	// failed -			goto fail; - -		case -3:	// malformed -			goto malformed; -	} - -	e->type = GG_EVENT_MSG; -	e->event.msg.msgclass = gg_fix32(r->msgclass); -	e->event.msg.sender = gg_fix32(r->sender); -	e->event.msg.time = gg_fix32(r->time); -	e->event.msg.seq = gg_fix32(r->seq); -	if (ctcp) -		e->event.msg.message = (char*) strdup("\x02"); -	else -		e->event.msg.message = (char*) strdup((char*) r + sizeof(*r)); - - -	return 0; - -malformed: -	e->type = GG_EVENT_NONE; -	free(e->event.msg.message); -	free(e->event.msg.recipients); -	free(e->event.msg.formats); - -	return 0; - -fail: -	free(e->event.msg.message); -	free(e->event.msg.recipients); -	free(e->event.msg.formats); -	return -1; -} - -/** - * \internal Zamienia tekst w formacie HTML na czysty tekst. - * - * \param dst Bufor wynikowy (może być \c NULL) - * \param html Tekst źródłowy - * - * \note Dokleja \c \\0 na końcu bufora wynikowego. - * - * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL). - */ -static int gg_convert_from_html(char *dst, const char *html) -{ -	const char *src, *entity, *tag; -	int len, in_tag, in_entity; - -	len = 0; -	in_tag = 0; -	tag = NULL; -	in_entity = 0; -	entity = NULL; - -	for (src = html; *src != 0; src++) { -		if (*src == '<') { -			tag = src; -			in_tag = 1; -			continue; -		} - -		if (in_tag && (*src == '>')) { -			if (strncmp(tag, "<br", 3) == 0) { -				if (dst != NULL) -					dst[len] = '\n'; -				len++; -			} -			in_tag = 0; -			continue; -		} - -		if (in_tag) -			continue; - -		if (*src == '&') { -			in_entity = 1; -			entity = src; -			continue; -		} - -		if (in_entity && *src == ';') { -			in_entity = 0; -			if (dst != NULL) { -				if (strncmp(entity, "<", 4) == 0) -					dst[len] = '<'; -				else if (strncmp(entity, ">", 4) == 0) -					dst[len] = '>'; -				else if (strncmp(entity, """, 6) == 0) -					dst[len] = '"'; -				else if (strncmp(entity, "'", 6) == 0) -					dst[len] = '\''; -				else if (strncmp(entity, "&", 5) == 0) -					dst[len] = '&'; -				else -					dst[len] = '?'; -			} -			len++; -			continue; -		} - -		if (in_entity && !(isalnum(*src) || *src == '#')) -			in_entity = 0; - -		if (in_entity) -			continue; - -		if (dst != NULL) -			dst[len] = *src; - -		len++; -	} - -	if (dst != NULL) -		dst[len] = 0; -	 -	return len; -} - -/** - * \internal Analizuje przychodzący pakiet z wiadomością protokołu Gadu-Gadu 8.0. - * - * Rozbija pakiet na poszczególne składniki -- tekst, informacje - * o konferencjach, formatowani itd. - * - * \param h Wskaźnik do odebranego pakietu - * \param e Struktura zdarzenia - * \param sess Struktura sesji - * \param event Typ zdarzenia - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess, int event) -{ -	char *packet = (char*) h + sizeof(struct gg_header); -	struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet; -	uint32_t offset_plain; -	uint32_t offset_attr; - -	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e); - -	if (!r->seq && !r->msgclass) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n"); -		goto malformed; -	} - -	offset_plain = gg_fix32(r->offset_plain); -	offset_attr  = gg_fix32(r->offset_attr); - -	if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n"); -		goto malformed; -	} - -	if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n"); -		offset_attr = 0;	/* nie parsuj attr. */ -		/* goto ignore; */ -	} - -	/* Normalna sytuacja, więc nie podpada pod powyższy warunek. */ -	if (offset_attr == h->length) -		offset_attr = 0; - -	if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n"); -		goto malformed; -	} - -	if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n"); -		goto malformed; -	} - -	e->type = event; -	e->event.msg.msgclass = gg_fix32(r->msgclass); -	e->event.msg.sender = gg_fix32(r->sender); -	e->event.msg.time = gg_fix32(r->time); -	e->event.msg.seq = gg_fix32(r->seq); - -	if (offset_attr != 0) { -		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) { -			case -1:	// handled -				return 0; - -			case -2:	// failed -				goto fail; - -			case -3:	// malformed -				goto malformed; -		} -	} - -	if (sess->encoding == GG_ENCODING_CP1250) { -		e->event.msg.message = (char*) strdup(packet + offset_plain); -	} else { -		if (offset_plain > sizeof(struct gg_recv_msg80)) { -			int len; - -			len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80)); - -			e->event.msg.message = (char*)malloc(len + 1); - -			if (e->event.msg.message == NULL) -				goto fail; - -			gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80)); -		} else { -			e->event.msg.message = (char*) gg_cp_to_utf8(packet + offset_plain); -		} -	} - -	if (offset_plain > sizeof(struct gg_recv_msg80)) { -		if (sess->encoding == GG_ENCODING_UTF8) -			e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80)); -		else -			e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80)); -	} else { -		e->event.msg.xhtml_message = NULL; -	} - -	return 0; - -fail: -	free(e->event.msg.message); -	free(e->event.msg.xhtml_message); -	free(e->event.msg.recipients); -	free(e->event.msg.formats); -	return -1; - -malformed: -	e->type = GG_EVENT_NONE; -	free(e->event.msg.message); -	free(e->event.msg.xhtml_message); -	free(e->event.msg.recipients); -	free(e->event.msg.formats); -	return 0; -} - -/** - * \internal Wysyła potwierdzenie odebrania wiadomości. - * - * \param sess Struktura sesji - * - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd - */ -static int gg_handle_recv_msg_ack(struct gg_header *h, struct gg_session *sess) -{ -	char *packet = (char*) h + sizeof(struct gg_header); -	struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet; -	struct gg_recv_msg_ack pkt; - -	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg_ack(%p);\n", sess); - -	if ((sess->protocol_features & GG_FEATURE_MSG_ACK) == 0) -		return 0; - -	pkt.seq = gg_fix32(r->seq); - -	return gg_send_packet(sess, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL); -} - -/** - * \internal Analizuje przychodzący pakiet z danymi kontaktów. - * - * \param sess Struktura sesji - * \param e Struktura zdarzenia - * \param payload Treść pakietu - * \param len Długość pakietu - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_handle_user_data(struct gg_session *sess, struct gg_event *e, void *packet, size_t len) -{ -	struct gg_user_data d; -	char *p = (char*) packet; -	char *packet_end = (char*) packet + len; -	struct gg_event_user_data_user *users; -	unsigned i, j; -	int res = 0; - -	gg_debug_session(sess, GG_DEBUG_MISC, "** gg_handle_user_data(%p, %p, %p, %d);\n", sess, e, packet, len); - -	e->event.user_data.user_count = 0; -	e->event.user_data.users = NULL; - -	if (p + sizeof(d) > packet_end) -		goto malformed; - -	memcpy(&d, p, sizeof(d)); -	p += sizeof(d); - -	d.type = gg_fix32(d.type); -	d.user_count = gg_fix32(d.user_count); - -	if (d.user_count > 0xffff) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (1)\n"); -		goto malformed; -	} - -	if (d.user_count > 0) { -		users = (gg_event_user_data_user*)calloc(d.user_count, sizeof(struct gg_event_user_data_user)); - -		if (users == NULL) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d*%d)\n", d.user_count, sizeof(struct gg_event_user_data_user)); -			goto fail; -		} -	} else { -		users = NULL; -	} - -	e->type = GG_EVENT_USER_DATA; -	e->event.user_data.type = d.type; -	e->event.user_data.user_count = d.user_count; -	e->event.user_data.users = users; -	 -	gg_debug_session(sess, GG_DEBUG_DUMP, "type=%d, count=%d\n", d.type, d.user_count); - -	for (i = 0; i < d.user_count; i++) { -		struct gg_user_data_user u; -		struct gg_event_user_data_attr *attrs; - -		if (p + sizeof(u) > packet_end) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (2)\n"); -			goto malformed; -		} - -		memcpy(&u, p, sizeof(u)); -		p += sizeof(u); - -		u.uin = gg_fix32(u.uin); -		u.attr_count = gg_fix32(u.attr_count); - -		if (u.attr_count > 0xffff) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (2)\n"); -			goto malformed; -		} - -		if (u.attr_count > 0) { -			attrs = (gg_event_user_data_attr*)calloc(u.attr_count, sizeof(struct gg_event_user_data_attr)); - -			if (attrs == NULL) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d*%d)\n", u.attr_count, sizeof(struct gg_event_user_data_attr)); -				goto fail; -			} -		} else { -			attrs = NULL; -		} - -		users[i].uin = u.uin; -		users[i].attr_count = u.attr_count; -		users[i].attrs = attrs; - -		gg_debug_session(sess, GG_DEBUG_DUMP, "    uin=%d, count=%d\n", u.uin, u.attr_count); - -		for (j = 0; j < u.attr_count; j++) { -			uint32_t key_size; -			uint32_t attr_type; -			uint32_t value_size; -			char *key; -			char *value; - -			if (p + sizeof(key_size) > packet_end) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (3)\n"); -				goto malformed; -			} - -			memcpy(&key_size, p, sizeof(key_size)); -			p += sizeof(key_size); - -			key_size = gg_fix32(key_size); - -			if (key_size > 0xffff || p + key_size > packet_end) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (3)\n"); -				goto malformed; -			} - -			key = (char*)malloc(key_size + 1); - -			if (key == NULL) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d)\n", key_size + 1); -				goto fail; -			} - -			memcpy(key, p, key_size); -			p += key_size; - -			key[key_size] = 0; - -			attrs[j].key = key; - -			if (p + sizeof(attr_type) + sizeof(value_size) > packet_end) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (4)\n"); -				goto malformed; -			} - -			memcpy(&attr_type, p, sizeof(attr_type)); -			p += sizeof(attr_type); -			memcpy(&value_size, p, sizeof(value_size)); -			p += sizeof(value_size); - -			attrs[j].type = gg_fix32(attr_type); -			value_size = gg_fix32(value_size); - -			if (value_size > 0xffff || p + value_size > packet_end) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (5)\n"); -				goto malformed; -			} - -			value = (char*)malloc(value_size + 1); - -			if (value == NULL) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d)\n", value_size + 1); -				goto fail; -			} - -			memcpy(value, p, value_size); -			p += value_size; - -			value[value_size] = 0; - -			attrs[j].value = value; - -			gg_debug_session(sess, GG_DEBUG_DUMP, "        key=\"%s\", type=%d, value=\"%s\"\n", key, attr_type, value); -		} -	} - -	return 0; - -fail: -	res = -1; - -malformed: -	e->type = GG_EVENT_NONE; - -	for (i = 0; i < e->event.user_data.user_count; i++) { -		for (j = 0; j < e->event.user_data.users[i].attr_count; j++) { -			free(e->event.user_data.users[i].attrs[j].key); -			free(e->event.user_data.users[i].attrs[j].value); -		} - -		free(e->event.user_data.users[i].attrs); -	} - -	free(e->event.user_data.users); - -	return res; -} - -/** - * \internal Analizuje przychodzący pakiet z listą sesji multilogowania. - * - * \param sess Struktura sesji - * \param e Struktura zdarzenia - * \param payload Treść pakietu - * \param len Długość pakietu - * - * \return 0 jeśli się powiodło, -1 w przypadku błędu - */ -static int gg_handle_multilogon_info(struct gg_session *sess, struct gg_event *e, void *packet, size_t len) -{ -	char *packet_end = (char*) packet + len; -	struct gg_multilogon_info *info = (struct gg_multilogon_info*) packet; -	char *p = (char*) packet + sizeof(*info); -	struct gg_multilogon_session *sessions = NULL; -	size_t count; -	size_t i; -	int res = 0; - -	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_multilogon_info(%p, %p, %p, %d);\n", sess, e, packet, len); - -	count = gg_fix32(info->count); - -	if (count > 0xffff) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (1)\n"); -		goto malformed; -	} - -	sessions = (gg_multilogon_session*)calloc(count, sizeof(struct gg_multilogon_session)); - -	if (sessions == NULL) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d*%d)\n", count, sizeof(struct gg_multilogon_session)); -		return -1; -	} -	 -	e->type = GG_EVENT_MULTILOGON_INFO; -	e->event.multilogon_info.count = (int)count; -	e->event.multilogon_info.sessions = sessions; - -	for (i = 0; i < count; i++) { -		struct gg_multilogon_info_item item; -		size_t name_size; - -		if (p + sizeof(item) > packet_end) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (2)\n"); -			goto malformed; -		} - -		memcpy(&item, p, sizeof(item)); - -		sessions[i].id = item.conn_id; -		sessions[i].remote_addr = item.addr; -		sessions[i].status_flags = gg_fix32(item.flags); -		sessions[i].protocol_features = gg_fix32(item.features); -		sessions[i].logon_time = gg_fix32(item.logon_time); - -		p += sizeof(item); - -		name_size = gg_fix32(item.name_size); - -		if (name_size > 0xffff || p + name_size > packet_end) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (3)\n"); -			goto malformed; -		} - -		sessions[i].name = (char*)malloc(name_size + 1); - -		if (sessions[i].name == NULL) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d)\n", name_size); -			goto fail; -		} - -		memcpy(sessions[i].name, p, name_size); -		sessions[i].name[name_size] = 0; - -		p += name_size; -	} - -	return 0; - -fail: -	res = -1; - -malformed: -	e->type = GG_EVENT_NONE; - -	for (i = 0; i < (size_t)e->event.multilogon_info.count; i++) -		free(e->event.multilogon_info.sessions[i].name); - -	free(e->event.multilogon_info.sessions); - -	return res; -} - -/** - * \internal Odbiera pakiet od serwera. - * - * Analizuje pakiet i wypełnia strukturę zdarzenia. - * - * \param sess Struktura sesji - * \param e Struktura zdarzenia - * - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd - */ -static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) -{ -	struct gg_header *h = NULL; -	char *p; - -	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); - -	if (!sess) { -		errno = EFAULT; -		return -1; -	} - -	if (!(h = (gg_header*)gg_recv_packet(sess))) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); -		goto fail; -	} - -	p = (char*) h + sizeof(struct gg_header); - -	switch (h->type) { -		case GG_RECV_MSG: -		{ -			if (h->length >= sizeof(struct gg_recv_msg)) { -				if (gg_handle_recv_msg(h, e, sess) != -1) -					gg_handle_recv_msg_ack(h, sess); -				else -					goto fail; -			} - -			break; -		} - -		case GG_RECV_MSG80: -		{ -			if (h->length >= sizeof(struct gg_recv_msg80)) { -				if (gg_handle_recv_msg80(h, e, sess, GG_EVENT_MSG) != -1) -					gg_handle_recv_msg_ack(h, sess); -				else -					goto fail; -			} - -			break; -		} - -		case GG_RECV_OWN_MSG: -		{ -			if (h->length >= sizeof(struct gg_recv_msg80)) { -				if (gg_handle_recv_msg80(h, e, sess, GG_EVENT_MULTILOGON_MSG) == -1) -					goto fail; -			} - -			break; -		} - -		case GG_NOTIFY_REPLY: -		{ -			struct gg_notify_reply *n = (gg_notify_reply*)p; -			unsigned int count, i; -			char *tmp; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); - -			if (h->length < sizeof(*n)) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); -				errno = EINVAL; -				goto fail; -			} - -			if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) { -				e->type = GG_EVENT_NOTIFY_DESCR; - -				if (!(e->event.notify_descr.notify = (gg_notify_reply*)malloc(sizeof(*n) * 2))) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -					goto fail; -				} -				e->event.notify_descr.notify[1].uin = 0; -				memcpy(e->event.notify_descr.notify, p, sizeof(*n)); -				e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); -				e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); -				e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); -				e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version); - -				count = h->length - sizeof(*n); -				if (!(tmp = (char*)malloc(count + 1))) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -					goto fail; -				} -				memcpy(tmp, p + sizeof(*n), count); -				tmp[count] = 0; -				e->event.notify_descr.descr = tmp; - -			} else { -				e->type = GG_EVENT_NOTIFY; - -				if (!(e->event.notify = (gg_notify_reply*)malloc(h->length + 2 * sizeof(*n)))) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -					goto fail; -				} - -				memcpy(e->event.notify, p, h->length); -				count = h->length / sizeof(*n); -				e->event.notify[count].uin = 0; - -				for (i = 0; i < count; i++) { -					e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); -					e->event.notify[i].status = gg_fix32(e->event.notify[i].status); -					e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); -					e->event.notify[i].version = gg_fix32(e->event.notify[i].version); -				} -			} - -			break; -		} - -		case GG_STATUS: -		{ -			struct gg_status *s = (gg_status*)p; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); - -			if (h->length >= sizeof(*s)) { -				e->type = GG_EVENT_STATUS; -				memcpy(&e->event.status, p, sizeof(*s)); -				e->event.status.uin = gg_fix32(e->event.status.uin); -				e->event.status.status = gg_fix32(e->event.status.status); -				if (h->length > sizeof(*s)) { -					int len = h->length - sizeof(*s); -					char *buf = (char*)malloc(len + 1); -					if (buf) { -						memcpy(buf, p + sizeof(*s), len); -						buf[len] = 0; -					} -					e->event.status.descr = buf; -				} else -					e->event.status.descr = NULL; -			} - -			break; -		} - -		case GG_NOTIFY_REPLY77: -		case GG_NOTIFY_REPLY80BETA: -		{ -			struct gg_notify_reply77 *n = (gg_notify_reply77*)p; -			unsigned int length = h->length, i = 0; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); - -			e->type = GG_EVENT_NOTIFY60; -			e->event.notify60 = (gg_event_notify60*)malloc(sizeof(*e->event.notify60)); - -			if (!e->event.notify60) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -				goto fail; -			} - -			e->event.notify60[0].uin = 0; - -			while (length >= sizeof(struct gg_notify_reply77)) { -				uin_t uin = gg_fix32(n->uin); -				char *tmp; - -				e->event.notify60[i].uin = uin & 0x00ffffff; -				e->event.notify60[i].status = n->status; -				e->event.notify60[i].remote_ip = n->remote_ip; -				e->event.notify60[i].remote_port = gg_fix16(n->remote_port); -				e->event.notify60[i].version = n->version; -				e->event.notify60[i].image_size = n->image_size; -				e->event.notify60[i].descr = NULL; -				e->event.notify60[i].time = 0; - -				if (uin & 0x40000000) -					e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; -				if (uin & 0x20000000) -					e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK; -				if (uin & 0x08000000) -					e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; - -				if (GG_S_D(n->status)) { -					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77)); - -					if (sizeof(struct gg_notify_reply77) + descr_len <= length) { -						char *descr; - -						if (!(descr = (char*)malloc(descr_len + 1))) { -							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -							goto fail; -						} - -						memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len); -						descr[descr_len] = 0; - -						if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) { -							char *cp_descr = gg_utf8_to_cp(descr); - -							if (!cp_descr) { -								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -								free(descr); -								goto fail; -							} - -							free(descr); -							descr = cp_descr; -						} - -						e->event.notify60[i].descr = descr; - -						/* XXX czas */ -							 -						length -= sizeof(struct gg_notify_reply77) + descr_len + 1; -						n = (gg_notify_reply77*)((char*)n + sizeof(struct gg_notify_reply77) + descr_len + 1); -					} else { -						length = 0; -					} - -				} else { -					length -= sizeof(struct gg_notify_reply77); -					n = (gg_notify_reply77*)((char*)n + sizeof(struct gg_notify_reply77)); -				} - -				if (!(tmp = (char*)realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -					free(e->event.notify60); -					goto fail; -				} - -				e->event.notify60 = (gg_event_notify60*)tmp; -				e->event.notify60[++i].uin = 0; -			} - -			break; -		} - -		case GG_STATUS77: -		case GG_STATUS80BETA: -		{ -			struct gg_status77 *s = (gg_status77*)p; -			uint32_t uin; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); - -			if (h->length < sizeof(*s)) -				break; - -			uin = gg_fix32(s->uin); - -			e->type = GG_EVENT_STATUS60; -			e->event.status60.uin = uin & 0x00ffffff; -			e->event.status60.status = s->status; -			e->event.status60.remote_ip = s->remote_ip; -			e->event.status60.remote_port = gg_fix16(s->remote_port); -			e->event.status60.version = s->version; -			e->event.status60.image_size = s->image_size; -			e->event.status60.descr = NULL; -			e->event.status60.time = 0; - -			if (uin & 0x40000000) -				e->event.status60.version |= GG_HAS_AUDIO_MASK; -			if (uin & 0x20000000) -				e->event.status60.version |= GG_HAS_AUDIO7_MASK; -			if (uin & 0x08000000) -				e->event.status60.version |= GG_ERA_OMNIX_MASK; - -			if (h->length > sizeof(*s)) { -				int len = h->length - sizeof(*s); -				char *buf = (char*)malloc(len + 1); - -				/* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ? -				 * 	- goto fail; (?) -				 */ -				if (buf) { -					memcpy(buf, (char*) p + sizeof(*s), len); -					buf[len] = 0; - -					if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) { -						char *cp_buf = gg_utf8_to_cp(buf); -						free(buf); -						buf = cp_buf; -					} -				} - -				e->event.status60.descr = buf; - -				if (len > 4 && p[h->length - 5] == 0) { -					uint32_t t; -					memcpy(&t, p + h->length - 4, sizeof(uint32_t)); -					e->event.status60.time = gg_fix32(t); -				} -			} - -			break; -		} - -		case GG_NOTIFY_REPLY60: -		{ -			struct gg_notify_reply60 *n = (gg_notify_reply60*)p; -			unsigned int length = h->length, i = 0; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); - -			e->type = GG_EVENT_NOTIFY60; -			e->event.notify60 = (gg_event_notify60*)malloc(sizeof(*e->event.notify60)); - -			if (!e->event.notify60) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -				goto fail; -			} - -			e->event.notify60[0].uin = 0; - -			while (length >= sizeof(struct gg_notify_reply60)) { -				uin_t uin = gg_fix32(n->uin); -				char *tmp; - -				e->event.notify60[i].uin = uin & 0x00ffffff; -				e->event.notify60[i].status = n->status; -				e->event.notify60[i].remote_ip = n->remote_ip; -				e->event.notify60[i].remote_port = gg_fix16(n->remote_port); -				e->event.notify60[i].version = n->version; -				e->event.notify60[i].image_size = n->image_size; -				e->event.notify60[i].descr = NULL; -				e->event.notify60[i].time = 0; - -				if (uin & 0x40000000) -					e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; -				if (uin & 0x08000000) -					e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; - -				if (GG_S_D(n->status)) { -					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); - -					if (sizeof(struct gg_notify_reply60) + descr_len <= length) { -						if (!(e->event.notify60[i].descr = (char*)malloc(descr_len + 1))) { -							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -							goto fail; -						} - -						memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len); -						e->event.notify60[i].descr[descr_len] = 0; - -						/* XXX czas */ -							 -						length -= sizeof(struct gg_notify_reply60) + descr_len + 1; -						n = (gg_notify_reply60*)((char*)n + sizeof(struct gg_notify_reply60) + descr_len + 1); -					} else { -						length = 0; -					} - -				} else { -					length -= sizeof(struct gg_notify_reply60); -					n = (gg_notify_reply60*)((char*)n + sizeof(struct gg_notify_reply60)); -				} - -				if (!(tmp = (char*)realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -					free(e->event.notify60); -					goto fail; -				} - -				e->event.notify60 = (gg_event_notify60*)tmp; -				e->event.notify60[++i].uin = 0; -			} - -			break; -		} - -		case GG_STATUS60: -		{ -			struct gg_status60 *s = (gg_status60*)p; -			uint32_t uin; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); - -			if (h->length < sizeof(*s)) -				break; - -			uin = gg_fix32(s->uin); - -			e->type = GG_EVENT_STATUS60; -			e->event.status60.uin = uin & 0x00ffffff; -			e->event.status60.status = s->status; -			e->event.status60.remote_ip = s->remote_ip; -			e->event.status60.remote_port = gg_fix16(s->remote_port); -			e->event.status60.version = s->version; -			e->event.status60.image_size = s->image_size; -			e->event.status60.descr = NULL; -			e->event.status60.time = 0; - -			if (uin & 0x40000000) -				e->event.status60.version |= GG_HAS_AUDIO_MASK; -			if (uin & 0x08000000) -				e->event.status60.version |= GG_ERA_OMNIX_MASK; - -			if (h->length > sizeof(*s)) { -				int len = h->length - sizeof(*s); -				char *buf = (char*)malloc(len + 1); - -				if (buf) { -					memcpy(buf, (char*) p + sizeof(*s), len); -					buf[len] = 0; -				} - -				e->event.status60.descr = buf; - -				if (len > 4 && p[h->length - 5] == 0) { -					uint32_t t; -					memcpy(&t, p + h->length - 4, sizeof(uint32_t)); -					e->event.status60.time = gg_fix32(t); -				} -			} - -			break; -		} - -		case GG_STATUS80: -		{ -			struct gg_notify_reply80 *s = (gg_notify_reply80*)p; -			uint32_t descr_len; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); - -			if (h->length < sizeof(*s)) -				break; - -			e->type = GG_EVENT_STATUS60; -			e->event.status60.uin		= gg_fix32(s->uin); -			e->event.status60.status	= gg_fix32(s->status); -			e->event.status60.remote_ip	= s->remote_ip; -			e->event.status60.remote_port	= gg_fix16(s->remote_port); -			e->event.status60.image_size	= s->image_size; -			e->event.status60.descr		= NULL; -			e->event.status60.version	= 0x00;	/* not-supported */ -			e->event.status60.time		= 0;	/* not-supported */ - -			descr_len = gg_fix32(s->descr_len); - -			if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) { -				char *buf = (char*)malloc(descr_len + 1); - -				if (buf) { -					memcpy(buf, (char*) p + sizeof(*s), descr_len); -					buf[descr_len] = 0; - -					if (sess->encoding != GG_ENCODING_UTF8) { -						char *cp_buf = gg_utf8_to_cp(buf); -						free(buf); -						buf = cp_buf; -					} -				} - -				e->event.status60.descr = buf; -			} -			break; -		} - -		case GG_NOTIFY_REPLY80: -		{ -			struct gg_notify_reply80 *n = (gg_notify_reply80*)p; -			unsigned int length = h->length, i = 0; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); - -			e->type = GG_EVENT_NOTIFY60; -			e->event.notify60 = (gg_event_notify60*)malloc(sizeof(*e->event.notify60)); - -			if (!e->event.notify60) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -				goto fail; -			} - -			e->event.notify60[0].uin = 0; - -			while (length >= sizeof(struct gg_notify_reply80)) { -				uint32_t descr_len; -				char *tmp; - -				e->event.notify60[i].uin	= gg_fix32(n->uin); -				e->event.notify60[i].status	= gg_fix32(n->status); -				e->event.notify60[i].remote_ip	= n->remote_ip; -				e->event.notify60[i].remote_port= gg_fix16(n->remote_port); -				e->event.notify60[i].image_size	= n->image_size; -				e->event.notify60[i].descr	= NULL; -				e->event.notify60[i].version	= 0x00;	/* not-supported */ -				e->event.notify60[i].time	= 0;	/* not-supported */ - -				descr_len = gg_fix32(n->descr_len); - -				length -= sizeof(struct gg_notify_reply80); -				n = (gg_notify_reply80*)((char*)n + sizeof(struct gg_notify_reply80)); - -				if (descr_len) { -					if (length >= descr_len) { -						/* XXX, GG_S_D(n->status) */ -						char *descr; - -						if (!(descr = (char*)malloc(descr_len + 1))) { -							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -							goto fail; -						} - -						memcpy(descr, n, descr_len); -						descr[descr_len] = 0; - -						if (sess->encoding != GG_ENCODING_UTF8) { -							char *cp_descr = gg_utf8_to_cp(descr); - -							if (!cp_descr) { -								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -								free(descr); -								goto fail; -							} - -							free(descr); -							descr = cp_descr; -						} -						e->event.notify60[i].descr = descr; - -						length -= descr_len; -						n = (gg_notify_reply80*)((char*)n + descr_len); -					} else -						length = 0; -				} - -				if (!(tmp = (char*)realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); -					free(e->event.notify60); -					goto fail; -				} - -				e->event.notify60 = (gg_event_notify60*)tmp; -				e->event.notify60[++i].uin = 0; -			} -			break; -		} - -		case GG_SEND_MSG_ACK: -		{ -			struct gg_send_msg_ack *s = (gg_send_msg_ack*)p; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); - -			if (h->length < sizeof(*s)) -				break; - -			e->type = GG_EVENT_ACK; -			e->event.ack.status = gg_fix32(s->status); -			e->event.ack.recipient = gg_fix32(s->recipient); -			e->event.ack.seq = gg_fix32(s->seq); - -			break; -		} - -		case GG_PONG: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); - -			e->type = GG_EVENT_PONG; -			sess->last_pong = (int)time(NULL); - -			break; -		} - -		case GG_DISCONNECTING: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); -			e->type = GG_EVENT_DISCONNECT; -			break; -		} - -		case GG_DISCONNECT_ACK: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n"); -			e->type = GG_EVENT_DISCONNECT_ACK; -			break; -		} - -		case GG_XML_EVENT: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n"); -			e->type = GG_EVENT_XML_EVENT; -			if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n"); -				goto fail; -			} -			memcpy(e->event.xml_event.data, p, h->length); -			e->event.xml_event.data[h->length] = 0; -			break; -		} - -		case GG_XML_ACTION: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML action\n"); -			e->type = GG_EVENT_XML_ACTION; -			if (!(e->event.xml_action.data = (char *) malloc(h->length + 1))) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML action data\n"); -				goto fail; -			} -			memcpy(e->event.xml_action.data, p, h->length); -			e->event.xml_action.data[h->length] = 0; -			break; -		} - -		case GG_PUBDIR50_REPLY: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); -			if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1) -				goto fail; -			break; -		} - -		case GG_USERLIST_REPLY: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); - -			if (h->length < 1) -				break; - -			/* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko -			 * gdy otrzymano wszystkie odpowiedzi */ -			if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { -				if (--sess->userlist_blocks) -					break; - -				p[0] = GG_USERLIST_PUT_REPLY; -			} - -			if (h->length > 1) { -				char *tmp; -				unsigned int len = (sess->userlist_reply) ? (unsigned int)strlen(sess->userlist_reply) : 0; - -				gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); - -				if (!(tmp = (char*)realloc(sess->userlist_reply, len + h->length))) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); -					free(sess->userlist_reply); -					sess->userlist_reply = NULL; -					goto fail; -				} - -				sess->userlist_reply = tmp; -				sess->userlist_reply[len + h->length - 1] = 0; -				memcpy(sess->userlist_reply + len, p + 1, h->length - 1); -			} - -			if (p[0] == GG_USERLIST_GET_MORE_REPLY) -				break; - -			e->type = GG_EVENT_USERLIST; -			e->event.userlist.type = p[0]; -			e->event.userlist.reply = sess->userlist_reply; -			sess->userlist_reply = NULL; - -			break; -		} - -		case GG_DCC7_ID_REPLY: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n"); - -			if (h->length < sizeof(struct gg_dcc7_id_reply)) -				break; - -			if (gg_dcc7_handle_id(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		case GG_DCC7_ACCEPT: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n"); - -			if (h->length < sizeof(struct gg_dcc7_accept)) -				break; - -			if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		case GG_DCC7_NEW: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n"); - -			if (h->length < sizeof(struct gg_dcc7_new)) -				break; - -			if (gg_dcc7_handle_new(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		case GG_DCC7_REJECT: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n"); - -			if (h->length < sizeof(struct gg_dcc7_reject)) -				break; - -			if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		case GG_DCC7_ABORT: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 abort\n"); - -			if (h->length < sizeof(struct gg_dcc7_aborted)) -				break; - -			if (gg_dcc7_handle_abort(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		case GG_DCC7_INFO: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n"); - -			if (h->length < sizeof(struct gg_dcc7_info)) -				break; - -			if (gg_dcc7_handle_info(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		case GG_USER_DATA: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received user data\n"); - -			if (h->length < sizeof(struct gg_user_data)) -				break; - -			if (gg_handle_user_data(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		case GG_TYPING_NOTIFICATION: -		{ -			struct gg_typing_notification *n = (struct gg_typing_notification*)p; -			uin_t uin; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received typing notification\n"); - -			if (h->length < sizeof(*n)) -				break; - -			memcpy(&uin, &n->uin, sizeof(uin_t)); - -			e->type = GG_EVENT_TYPING_NOTIFICATION; -			e->event.typing_notification.uin = gg_fix32(uin); -			e->event.typing_notification.length = gg_fix16(n->length); - -			break; -		} - -		case GG_MULTILOGON_INFO: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received multilogon info\n"); - -			if (h->length < sizeof(struct gg_multilogon_info)) -				break; - -			if (gg_handle_multilogon_info(sess, e, p, h->length) == -1) -				goto fail; - -			break; -		} - -		default: -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); -	} - -	free(h); -	return 0; - -fail: -	free(h); -	return -1; -} - -/** \endcond */ - -/** - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji. - * - * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia - * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. - * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free(). - * - * \param sess Struktura sesji - * - * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd - * - * \ingroup events - */ -struct gg_event *gg_watch_fd(struct gg_session *sess) -{ -	struct gg_event *e; -	int res = 0; -	int port = 0; -	int errno2 = 0; - -	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); - -	if (!sess) { -		errno = EFAULT; -		return NULL; -	} - -	if (!(e = (gg_event*)calloc(1, sizeof(*e)))) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); -		return NULL; -	} - -	e->type = GG_EVENT_NONE; - -	if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) { -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left); - -		res = gg_sock_write(sess->fd, sess->send_buf, sess->send_left); - -		if (res == -1 && errno != EAGAIN) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); - -			if (sess->state == GG_STATE_READING_REPLY) -				goto fail_connecting; -			else -				goto done; -		} - -		if (res == sess->send_left) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n"); -			free(sess->send_buf); -			sess->send_buf = NULL; -			sess->send_left = 0; -		} else if (res > 0) { -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res); - -			memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res); -			sess->send_left -= res; -		} - -		res = 0; -	} - -	switch (sess->state) { -		case GG_STATE_RESOLVING: -		{ -			struct in_addr addr; -			int failed = 0; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); - -			if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); -				failed = 1; -				errno2 = errno; -			} - -			gg_sock_close(sess->fd); -			sess->fd = -1; - -			sess->resolver_cleanup(&sess->resolver, 0); - -			if (failed) { -				errno = errno2; -				goto fail_resolving; -			} - -			/* jeśli jesteśmy w resolverze i mamy ustawiony port -			 * proxy, znaczy, że resolvowaliśmy proxy. zatem -			 * wpiszmy jego adres. */ -			if (sess->proxy_port) -				sess->proxy_addr = addr.s_addr; - -			/* zapiszmy sobie adres huba i adres serwera (do -			 * bezpośredniego połączenia, jeśli hub leży) -			 * z resolvera. */ -			if (sess->proxy_addr && sess->proxy_port) -				port = sess->proxy_port; -			else { -				sess->server_addr = sess->hub_addr = addr.s_addr; -				port = GG_APPMSG_PORT; -			} - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); - -			/* łączymy się albo z hubem, albo z proxy, zależnie -			 * od tego, co resolvowaliśmy. */ -			if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { -				/* jeśli w trybie asynchronicznym gg_connect() -				 * zwróci błąd, nie ma sensu próbować dalej. */ -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); -				goto fail_connecting; -			} - -			/* jeśli podano serwer i łączmy się przez proxy, -			 * jest to bezpośrednie połączenie, inaczej jest -			 * do huba. */ - -			if (sess->proxy_addr && sess->proxy_port && sess->server_addr) { -				sess->state = GG_STATE_CONNECTING_GG; -				sess->soft_timeout = 1; -			} else -				sess->state = GG_STATE_CONNECTING_HUB; - -			sess->check = GG_CHECK_WRITE; -			sess->timeout = GG_DEFAULT_TIMEOUT; - -			break; -		} - -		case GG_STATE_CONNECTING_HUB: -		{ -			char buf[1024], *client, *auth; -			int res = 0; -			int res_size = sizeof(res); -			const char *host; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); - -			/* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił -			 * przypadkiem jakiś błąd. */ -			if (sess->async && (gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { -				if (sess->proxy_addr && sess->proxy_port) -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); -				else -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res)); - -				goto fail_connecting; -			} - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); - -			if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); -				goto fail_connecting; -			} - -			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) -				host = "http://" GG_APPMSG_HOST; -			else -				host = ""; - -			auth = gg_proxy_auth(); - -#ifdef GG_CONFIG_MIRANDA -			if (sess->tls) { -				snprintf(buf, sizeof(buf) - 1, -					"GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n" -					"Connection: close\r\n" -					"Host: " GG_APPMSG_HOST "\r\n" -					"%s" -					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : ""); -			} else -#elif GG_CONFIG_HAVE_OPENSSL -			if (sess->ssl != NULL) { -				snprintf(buf, sizeof(buf) - 1, -					"GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n" -					"Connection: close\r\n" -					"Host: " GG_APPMSG_HOST "\r\n" -					"%s" -					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : ""); -			} else -#endif -			{ -				snprintf(buf, sizeof(buf) - 1, -					"GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n" -					"Host: " GG_APPMSG_HOST "\r\n" -					"%s" -					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : ""); -			} - -			free(auth); -			free(client); - -			/* zwolnij pamięć po wersji klienta. */ -			if (sess->client_version) { -				free(sess->client_version); -				sess->client_version = NULL; -			} - -			gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); - -			/* zapytanie jest krótkie, więc zawsze zmieści się -			 * do bufora gniazda. jeśli write() zwróci mniej, -			 * stało się coś złego. */ -			if (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); - -				e->type = GG_EVENT_CONN_FAILED; -				e->event.failure = GG_FAILURE_WRITING; -				sess->state = GG_STATE_IDLE; -				gg_sock_close(sess->fd); -				sess->fd = -1; -				break; -			} - -			sess->state = GG_STATE_READING_DATA; -			sess->check = GG_CHECK_READ; -			sess->timeout = GG_DEFAULT_TIMEOUT; - -			break; -		} - -		case GG_STATE_READING_DATA: -		{ -			char buf[1024], *tmp, *host; -			int port = GG_DEFAULT_PORT; -			struct in_addr addr; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); - -			/* czytamy linię z gniazda i obcinamy \r\n. */ -			gg_read_line(sess->fd, buf, sizeof(buf) - 1); -			gg_chomp(buf); -			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); - -			/* sprawdzamy, czy wszystko w porządku. */ -			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n"); -				goto fail_connecting; -			} - -			/* ignorujemy resztę nagłówka. */ -			while (strcmp(buf, "\r\n") && strcmp(buf, "")) -				gg_read_line(sess->fd, buf, sizeof(buf) - 1); - -			/* czytamy pierwszą linię danych. */ -			gg_read_line(sess->fd, buf, sizeof(buf) - 1); -			gg_chomp(buf); - -			/* jeśli pierwsza liczba w linii nie jest równa zeru, -			 * oznacza to, że mamy wiadomość systemową. */ -			if (atoi(buf)) { -				char tmp[1024], *foo, *sysmsg_buf = NULL; -				int len = 0; - -				while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { -					if (!(foo = (char*)realloc(sysmsg_buf, len + strlen(tmp) + 2))) { -						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); -						break; -					} - -					sysmsg_buf = foo; - -					if (!len) -						strcpy(sysmsg_buf, tmp); -					else -						strcat(sysmsg_buf, tmp); - -					len += (int)strlen(tmp); -				} - -				e->type = GG_EVENT_MSG; -				e->event.msg.msgclass = atoi(buf); -				e->event.msg.sender = 0; -				e->event.msg.message = (char*) sysmsg_buf; -			} - -			gg_sock_close(sess->fd); - -			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); - -			/* analizujemy otrzymane dane. */ -			tmp = buf; - -			while (*tmp && *tmp != ' ') -				tmp++; -			while (*tmp && *tmp == ' ') -				tmp++; -			while (*tmp && *tmp != ' ') -				tmp++; -			while (*tmp && *tmp == ' ') -				tmp++; -			host = tmp; -			while (*tmp && *tmp != ' ') -				tmp++; -			*tmp = 0; - -			if ((tmp = strchr(host, ':'))) { -				*tmp = 0; -				port = atoi(tmp + 1); -			} - -			if (!strcmp(host, "notoperating")) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); -				sess->fd = -1; -				goto fail_unavailable; -			} - -			addr.s_addr = inet_addr(host); -			sess->server_addr = addr.s_addr; - -			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { -				/* jeśli mamy proxy, łączymy się z nim. */ -				if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { -					/* nie wyszło? trudno. */ -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); -					goto fail_connecting; -				} - -				sess->state = GG_STATE_CONNECTING_GG; -				sess->check = GG_CHECK_WRITE; -				sess->timeout = GG_DEFAULT_TIMEOUT; -				sess->soft_timeout = 1; -				break; -			} - -			sess->port = port; - -			/* Jeśli podano nazwę, nie adres serwera... */ -			if (sess->server_addr == INADDR_NONE) { -				if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) { -					gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); -					goto fail_resolving; -				} - -				sess->state = GG_STATE_RESOLVING_GG; -				sess->check = GG_CHECK_READ; -				sess->timeout = GG_DEFAULT_TIMEOUT; -				break; -			} - -			/* łączymy się z właściwym serwerem. */ -			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); - -				sess->port = GG_HTTPS_PORT; - -				/* nie wyszło? próbujemy portu 443. */ -				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { -					/* ostatnia deska ratunku zawiodła? -					 * w takim razie zwijamy manatki. */ -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); -					goto fail_connecting; -				} -			} - -			sess->state = GG_STATE_CONNECTING_GG; -			sess->check = GG_CHECK_WRITE; -			sess->timeout = GG_DEFAULT_TIMEOUT; -			sess->soft_timeout = 1; - -			break; -		} - -		case GG_STATE_RESOLVING_GG: -		{ -			struct in_addr addr; -			int failed = 0; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING_GG\n"); - -			if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); -				failed = 1; -				errno2 = errno; -			} - -			gg_sock_close(sess->fd); -			sess->fd = -1; - -			sess->resolver_cleanup(&sess->resolver, 0); - -			if (failed) { -				errno = errno2; -				goto fail_resolving; -			} - -			sess->server_addr = addr.s_addr; - -			/* łączymy się z właściwym serwerem. */ -			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); - -				sess->port = GG_HTTPS_PORT; - -				/* nie wyszło? próbujemy portu 443. */ -				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { -					/* ostatnia deska ratunku zawiodła? -					 * w takim razie zwijamy manatki. */ -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); -					goto fail_connecting; -				} -			} - -			sess->state = GG_STATE_CONNECTING_GG; -			sess->check = GG_CHECK_WRITE; -			sess->timeout = GG_DEFAULT_TIMEOUT; -			sess->soft_timeout = 1; - -			break; -		} - -		case GG_STATE_CONNECTING_GG: -		{ -			int res = 0; -			int res_size = sizeof(res); - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); - -			sess->soft_timeout = 0; - -			/* jeśli wystąpił błąd podczas łączenia się... */ -			if (sess->async && (sess->timeout == 0 || gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { -				/* jeśli nie udało się połączenie z proxy, -				 * nie mamy czego próbować więcej. */ -				if (sess->proxy_addr && sess->proxy_port) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); -					goto fail_connecting; -				} - -				gg_sock_close(sess->fd); -				sess->fd = -1; - -#ifdef ETIMEDOUT -				if (sess->timeout == 0) -					errno = ETIMEDOUT; -#endif - -#ifdef GG_CONFIG_HAVE_OPENSSL -				/* jeśli logujemy się po TLS, nie próbujemy -				 * się łączyć już z niczym innym w przypadku -				 * błędu. nie dość, że nie ma sensu, to i -				 * trzeba by się bawić w tworzenie na nowo -				 * SSL i SSL_CTX. */ - -				if (sess->ssl) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); -					goto fail_connecting; -				} -#endif - -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); - -				if (sess->port == GG_HTTPS_PORT) -					goto fail_connecting; - -				sess->port = GG_HTTPS_PORT; - -				/* próbujemy na port 443. */ -				if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); -					goto fail_connecting; -				} - -				sess->state = GG_STATE_CONNECTING_GG; -				sess->check = GG_CHECK_WRITE; -				sess->timeout = GG_DEFAULT_TIMEOUT; -				sess->soft_timeout = 1; - -				break; -			} - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); - -			if (gg_proxy_http_only) -				sess->proxy_port = 0; - -			/* jeśli mamy proxy, wyślijmy zapytanie. */ -			if (sess->proxy_addr && sess->proxy_port) { -				char buf[100], *auth = gg_proxy_auth(); -				struct in_addr addr; - -				if (sess->server_addr) -					addr.s_addr = sess->server_addr; -				else -					addr.s_addr = sess->hub_addr; - -				snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); - -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf); - -				/* wysyłamy zapytanie. jest ono na tyle krótkie, -				 * że musi się zmieścić w buforze gniazda. jeśli -				 * write() zawiedzie, stało się coś złego. */ -				if (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); -					free(auth); -					goto fail_connecting; -				} - -				if (auth) { -					gg_debug_session(sess, GG_DEBUG_MISC, "//   %s", auth); -					if (gg_sock_write(sess->fd, auth, (int)strlen(auth)) < (signed)strlen(auth)) { -						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); -						free(auth); -						goto fail_connecting; -					} - -					free(auth); -				} - -				if (gg_sock_write(sess->fd, "\r\n", 2) < 2) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); -					goto fail_connecting; -				} -			} - -#ifdef GG_CONFIG_MIRANDA -			if (sess->tls) { -				sess->state = GG_STATE_TLS_NEGOTIATION; -				sess->check = GG_CHECK_WRITE; -				sess->timeout = GG_DEFAULT_TIMEOUT; - -				break; -			} -#elif GG_CONFIG_HAVE_OPENSSL -			if (sess->ssl != NULL) { -				SSL_set_fd(sess->ssl, (int)sess->fd); - -				sess->state = GG_STATE_TLS_NEGOTIATION; -				sess->check = GG_CHECK_WRITE; -				sess->timeout = GG_DEFAULT_TIMEOUT; - -				break; -			} -#endif - -			sess->state = GG_STATE_READING_KEY; -			sess->check = GG_CHECK_READ; -			sess->timeout = GG_DEFAULT_TIMEOUT; - -			break; -		} - -#ifdef GG_CONFIG_MIRANDA -		case GG_STATE_TLS_NEGOTIATION: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); - -			sess->ssl = sslApi.connect(sess->fd, 0, 0); - -			if (sess->ssl == NULL) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation failed\n"); - -				e->type = GG_EVENT_CONN_FAILED; -				e->event.failure = GG_FAILURE_TLS; -				sess->state = GG_STATE_IDLE; -				gg_sock_close(sess->fd); -				sess->fd = -1; -				break; -			} - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded\n"); - -			sess->state = GG_STATE_READING_KEY; -			sess->check = GG_CHECK_READ; -			sess->timeout = GG_DEFAULT_TIMEOUT; - -			break; -		} -#elif GG_CONFIG_HAVE_OPENSSL -		case GG_STATE_TLS_NEGOTIATION: -		{ -			int res; -			X509 *peer; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); - -			if ((res = SSL_connect(sess->ssl)) <= 0) { -				int err = SSL_get_error(sess->ssl, res); - -				if (res == 0) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); - -					e->type = GG_EVENT_CONN_FAILED; -					e->event.failure = GG_FAILURE_TLS; -					sess->state = GG_STATE_IDLE; -					gg_sock_close(sess->fd); -					sess->fd = -1; -					break; -				} - -				if (err == SSL_ERROR_WANT_READ) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); - -					sess->state = GG_STATE_TLS_NEGOTIATION; -					sess->check = GG_CHECK_READ; -					sess->timeout = GG_DEFAULT_TIMEOUT; - -					break; -				} else if (err == SSL_ERROR_WANT_WRITE) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); - -					sess->state = GG_STATE_TLS_NEGOTIATION; -					sess->check = GG_CHECK_WRITE; -					sess->timeout = GG_DEFAULT_TIMEOUT; - -					break; -				} else { -					char buf[256]; - -					ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); - -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); - -					e->type = GG_EVENT_CONN_FAILED; -					e->event.failure = GG_FAILURE_TLS; -					sess->state = GG_STATE_IDLE; -					gg_sock_close(sess->fd); -					sess->fd = -1; -					break; -				} -			} - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl)); - -			peer = SSL_get_peer_certificate(sess->ssl); - -			if (!peer) -				gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n"); -			else { -				char buf[256]; - -				X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); -				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf); - -				X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); -				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf); -			} - -			sess->state = GG_STATE_READING_KEY; -			sess->check = GG_CHECK_READ; -			sess->timeout = GG_DEFAULT_TIMEOUT; - -			break; -		} -#endif - -		case GG_STATE_READING_KEY: -		{ -			struct gg_header *h; -			struct gg_welcome *w; -			unsigned char *password = (unsigned char*) sess->password; -			int ret; -			uint8_t login_hash[64]; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); - -			memset(login_hash, 0, sizeof(login_hash)); - -			/* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie -			 * się tekstu wrzucanego przez proxy. */ -			if (sess->proxy_addr && sess->proxy_port) { -				char buf[100]; - -				strcpy(buf, ""); -				gg_read_line(sess->fd, buf, sizeof(buf) - 1); -				gg_chomp(buf); -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf); - -				while (strcmp(buf, "")) { -					gg_read_line(sess->fd, buf, sizeof(buf) - 1); -					gg_chomp(buf); -					if (strcmp(buf, "")) -						gg_debug_session(sess, GG_DEBUG_MISC, "//   %s\n", buf); -				} - -				/* XXX niech czeka jeszcze raz w tej samej -				 * fazie. głupio, ale działa. */ -				sess->proxy_port = 0; - -				break; -			} - -			/* czytaj pierwszy pakiet. */ -			if (!(h = (gg_header*)gg_recv_packet(sess))) { -				if (errno == EAGAIN) { -					sess->check = GG_CHECK_READ; -					break; -				} - -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); - -				e->type = GG_EVENT_CONN_FAILED; -				e->event.failure = GG_FAILURE_READING; -				sess->state = GG_STATE_IDLE; -				errno2 = errno; -				gg_sock_close(sess->fd); -				errno = errno2; -				sess->fd = -1; -				break; -			} - -			if (h->type != GG_WELCOME) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); -				free(h); -				gg_sock_close(sess->fd); -				sess->fd = -1; -				errno = EINVAL; -				e->type = GG_EVENT_CONN_FAILED; -				e->event.failure = GG_FAILURE_INVALID; -				sess->state = GG_STATE_IDLE; -				break; -			} - -			w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); -			w->key = gg_fix32(w->key); - -			switch (sess->hash_type) { -				case GG_LOGIN_HASH_GG32: -				{ -					unsigned int hash; - -					hash = gg_fix32(gg_login_hash(password, w->key)); -					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash); -					memcpy(login_hash, &hash, sizeof(hash)); - -					break; -				} - -				case GG_LOGIN_HASH_SHA1: -				{ -					char tmp[41]; -					gg_login_hash_sha1((char*) password, w->key, login_hash); -					bin2hex(login_hash, 20, tmp); -					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp); - -					break; -				} -			} - -			free(h); -			free(sess->password); -			sess->password = NULL; - -			if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { -				struct sockaddr_in sin; -				int sin_len = sizeof(sin); - -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); - -				if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); -					sess->client_addr = sin.sin_addr.s_addr; -				} else { -					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); -					sess->client_addr = 0; -				} -			} else -				sess->client_addr = gg_dcc_ip; - -			if (sess->protocol_version >= 0x2e) { -				struct gg_login80 l; -				const char *version = (sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION; -				uint32_t tmp_version_len	= gg_fix32((uint32_t)strlen(GG8_VERSION) + (uint32_t)strlen(version)); -				uint32_t tmp_descr_len		= gg_fix32((sess->initial_descr) ? (uint32_t)strlen(sess->initial_descr) : 0); -				 -				memset(&l, 0, sizeof(l)); -				l.uin           = gg_fix32(sess->uin); -				memcpy(l.language, GG8_LANG, sizeof(l.language)); -				l.hash_type     = sess->hash_type; -				memcpy(l.hash, login_hash, sizeof(login_hash)); -				l.status        = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); -				l.flags		= gg_fix32(sess->status_flags); -				l.features	= gg_fix32(sess->protocol_features); -				l.image_size    = sess->image_size; -				l.dunno2        = 0x64; - -				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n"); -				ret = gg_send_packet(sess, GG_LOGIN80,  -						&l, sizeof(l),  -						&tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION), version, strlen(version), -						&tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, -						NULL); - -			} else if (sess->protocol_version == 0x2d) { -				struct gg_login70 l; - -				memset(&l, 0, sizeof(l)); -				l.uin		= gg_fix32(sess->uin); -				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr; -				l.local_port	= gg_fix16((uint16_t)((sess->external_port > 1023) ? sess->external_port : gg_dcc_port)); -				l.hash_type	= sess->hash_type; -				memcpy(l.hash, login_hash, sizeof(login_hash)); -				l.image_size	= sess->image_size; -				l.dunno2 	= 0x64; -				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); -				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags); - -				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n"); -				ret = gg_send_packet(sess, GG_LOGIN80BETA, -						&l, sizeof(l), -						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, -						(sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0, -						NULL); -			} else { -				struct gg_login70 l; - -				memset(&l, 0, sizeof(l)); -				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr; -				l.uin		= gg_fix32(sess->uin); -				l.local_port	= gg_fix16((uint16_t)((sess->external_port > 1023) ? sess->external_port : gg_dcc_port)); -				l.hash_type	= sess->hash_type; -				memcpy(l.hash, login_hash, sizeof(login_hash)); -				l.image_size	= sess->image_size; -				l.dunno2	= 0xbe; -				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); -				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags); - -				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n"); -				ret = gg_send_packet(sess, GG_LOGIN70, -						&l, sizeof(l), -						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, -						NULL); -			} - -			free(sess->initial_descr); -			sess->initial_descr = NULL; - -			if (ret == -1) { -				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); -				errno2 = errno; -				gg_sock_close(sess->fd); -				errno = errno2; -				sess->fd = -1; -				e->type = GG_EVENT_CONN_FAILED; -				e->event.failure = GG_FAILURE_WRITING; -				sess->state = GG_STATE_IDLE; -				break; -			} - -			sess->state = GG_STATE_READING_REPLY; -			sess->check = GG_CHECK_READ; - -			break; -		} - -		case GG_STATE_READING_REPLY: -		{ -			struct gg_header *h; - -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); - -			if (!(h = (gg_header*)gg_recv_packet(sess))) { -				if (errno == EAGAIN) { -					sess->check = GG_CHECK_READ; -					break; -				} - -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); -				e->type = GG_EVENT_CONN_FAILED; -				e->event.failure = GG_FAILURE_READING; -				sess->state = GG_STATE_IDLE; -				errno2 = errno; -				gg_sock_close(sess->fd); -				errno = errno2; -				sess->fd = -1; -				break; -			} - -			if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); -				e->type = GG_EVENT_CONN_SUCCESS; -				sess->state = GG_STATE_CONNECTED; -				sess->check = GG_CHECK_READ; -				sess->timeout = -1; -				sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; -				free(h); -				break; -			} - -			if (h->type == GG_LOGIN_FAILED || h->type == GG_LOGIN80_FAILED) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); -				e->event.failure = GG_FAILURE_PASSWORD; -				errno = EACCES; -			} else if (h->type == GG_DISCONNECTING) { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); -				e->event.failure = GG_FAILURE_INTRUDER; -				errno = EACCES; -			} else { -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); -				e->event.failure = GG_FAILURE_INVALID; -				errno = EINVAL; -			} - -			e->type = GG_EVENT_CONN_FAILED; -			sess->state = GG_STATE_IDLE; -			errno2 = errno; -			gg_sock_close(sess->fd); -			errno = errno2; -			sess->fd = -1; -			free(h); - -			break; -		} - -		case GG_STATE_CONNECTED: -		{ -			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); - -			sess->last_event = (int)time(NULL); - -			if ((res = gg_watch_fd_connected(sess, e)) == -1) { - -				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); - - 				if (errno == EAGAIN) { -					e->type = GG_EVENT_NONE; -					res = 0; -				} else -					res = -1; -			} - -			sess->check = GG_CHECK_READ; - -			break; -		} -	} - -done: -	if (res == -1) { -		free(e); -		e = NULL; -	} else { -		if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) -			sess->check |= GG_CHECK_WRITE; -	} - -	return e; - -fail_connecting: -	if (sess->fd != -1) { -		errno2 = errno; -		gg_sock_close(sess->fd); -		errno = errno2; -		sess->fd = -1; -	} -	e->type = GG_EVENT_CONN_FAILED; -	e->event.failure = GG_FAILURE_CONNECTING; -	sess->state = GG_STATE_IDLE; -	goto done; - -fail_resolving: -	e->type = GG_EVENT_CONN_FAILED; -	e->event.failure = GG_FAILURE_RESOLVING; -	sess->state = GG_STATE_IDLE; -	goto done; - -fail_unavailable: -	e->type = GG_EVENT_CONN_FAILED; -	e->event.failure = GG_FAILURE_UNAVAILABLE; -	sess->state = GG_STATE_IDLE; -	goto done; -} - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ +
 +/**
 + * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
 + *
 + * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
 + * strukturę \c gg_event.
 + *
 + * \param e Struktura zdarzenia
 + *
 + * \ingroup events
 + */
 +void gg_event_free(struct gg_event *e)
 +{
 +	gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
 +
 +	if (!e)
 +		return;
 +
 +	switch (e->type) {
 +		case GG_EVENT_MSG:
 +		case GG_EVENT_MULTILOGON_MSG:
 +			free(e->event.msg.message);
 +			free(e->event.msg.formats);
 +			free(e->event.msg.recipients);
 +			free(e->event.msg.xhtml_message);
 +			break;
 +
 +		case GG_EVENT_NOTIFY:
 +			free(e->event.notify);
 +			break;
 +
 +		case GG_EVENT_NOTIFY60:
 +		{
 +			int i;
 +
 +			for (i = 0; e->event.notify60[i].uin; i++)
 +				free(e->event.notify60[i].descr);
 +
 +			free(e->event.notify60);
 +
 +			break;
 +		}
 +
 +		case GG_EVENT_STATUS60:
 +			free(e->event.status60.descr);
 +			break;
 +
 +		case GG_EVENT_STATUS:
 +			free(e->event.status.descr);
 +			break;
 +
 +		case GG_EVENT_NOTIFY_DESCR:
 +			free(e->event.notify_descr.notify);
 +			free(e->event.notify_descr.descr);
 +			break;
 +
 +		case GG_EVENT_DCC_VOICE_DATA:
 +			free(e->event.dcc_voice_data.data);
 +			break;
 +
 +		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
 +		case GG_EVENT_PUBDIR50_READ:
 +		case GG_EVENT_PUBDIR50_WRITE:
 +			gg_pubdir50_free(e->event.pubdir50);
 +			break;
 +
 +		case GG_EVENT_USERLIST:
 +			free(e->event.userlist.reply);
 +			break;
 +
 +		case GG_EVENT_IMAGE_REPLY:
 +			free(e->event.image_reply.filename);
 +			free(e->event.image_reply.image);
 +			break;
 +
 +		case GG_EVENT_XML_EVENT:
 +			free(e->event.xml_event.data);
 +			break;
 +
 +		case GG_EVENT_USER_DATA:
 +		{
 +			int i, j;
 +
 +			for (i = 0; i < e->event.user_data.user_count; i++) {
 +				for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
 +					free(e->event.user_data.users[i].attrs[j].key);
 +					free(e->event.user_data.users[i].attrs[j].value);
 +				}
 +
 +				free(e->event.user_data.users[i].attrs);
 +			}
 +
 +			free(e->event.user_data.users);
 +
 +			break;
 +		}
 +	
 +		case GG_EVENT_MULTILOGON_INFO:
 +		{
 +			int i;
 +
 +			for (i = 0; i < e->event.multilogon_info.count; i++)
 +				free(e->event.multilogon_info.sessions[i].name);
 +
 +			free(e->event.multilogon_info.sessions);
 +
 +			break;
 +		}
 +	}
 +
 +	free(e);
 +}
 +
 +/** \cond internal */
 +
 +/**
 + * \internal Usuwa obrazek z kolejki do wysłania.
 + *
 + * \param s Struktura sesji
 + * \param q Struktura obrazka
 + * \param freeq Flaga zwolnienia elementu kolejki
 + *
 + * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
 + */
 +int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
 +{
 +	if (!s || !q) {
 +		errno = EFAULT;
 +		return -1;
 +	}
 +
 +	if (s->images == q)
 +		s->images = q->next;
 +	else {
 +		struct gg_image_queue *qq;
 +
 +		for (qq = s->images; qq; qq = qq->next) {
 +			if (qq->next == q) {
 +				qq->next = q->next;
 +				break;
 +			}
 +		}
 +	}
 +
 +	if (freeq) {
 +		free(q->image);
 +		free(q->filename);
 +		free(q);
 +	}
 +
 +	return 0;
 +}
 +
 +/** \endcond */
 +
 +/**
 + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
 + *
 + * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
 + * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
 + * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
 + *
 + * \param sess Struktura sesji
 + *
 + * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
 + *
 + * \ingroup events
 + */
 +struct gg_event *gg_watch_fd(struct gg_session *sess)
 +{
 +	struct gg_event *e;
 +	int res = 0;
 +	int port = 0;
 +	int errno2 = 0;
 +
 +	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
 +
 +	if (!sess) {
 +		errno = EFAULT;
 +		return NULL;
 +	}
 +
 +	if (!(e = (gg_event*)calloc(1, sizeof(*e)))) {
 +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
 +		return NULL;
 +	}
 +
 +	e->type = GG_EVENT_NONE;
 +
 +	if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) {
 +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
 +
 +		res = gg_sock_write(sess->fd, sess->send_buf, sess->send_left);
 +
 +		if (res == -1 && errno != EAGAIN) {
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno));
 +
 +			if (sess->state == GG_STATE_READING_REPLY)
 +				goto fail_connecting;
 +			else
 +				goto done;
 +		}
 +
 +		if (res == sess->send_left) {
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
 +			free(sess->send_buf);
 +			sess->send_buf = NULL;
 +			sess->send_left = 0;
 +		} else if (res > 0) {
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res);
 +
 +			memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
 +			sess->send_left -= res;
 +		}
 +
 +		res = 0;
 +	}
 +
 +	switch (sess->state) {
 +		case GG_STATE_RESOLVING:
 +		{
 +			struct in_addr addr;
 +			int failed = 0;
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
 +
 +			if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
 +				failed = 1;
 +				errno2 = errno;
 +			}
 +
 +			gg_sock_close(sess->fd);
 +			sess->fd = -1;
 +
 +			sess->resolver_cleanup(&sess->resolver, 0);
 +
 +			if (failed) {
 +				errno = errno2;
 +				goto fail_resolving;
 +			}
 +
 +			/* jeśli jesteśmy w resolverze i mamy ustawiony port
 +			 * proxy, znaczy, że resolvowaliśmy proxy. zatem
 +			 * wpiszmy jego adres. */
 +			if (sess->proxy_port)
 +				sess->proxy_addr = addr.s_addr;
 +
 +			/* zapiszmy sobie adres huba i adres serwera (do
 +			 * bezpośredniego połączenia, jeśli hub leży)
 +			 * z resolvera. */
 +			if (sess->proxy_addr && sess->proxy_port)
 +				port = sess->proxy_port;
 +			else {
 +				sess->server_addr = sess->hub_addr = addr.s_addr;
 +				port = GG_APPMSG_PORT;
 +			}
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
 +
 +			/* łączymy się albo z hubem, albo z proxy, zależnie
 +			 * od tego, co resolvowaliśmy. */
 +			if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
 +				/* jeśli w trybie asynchronicznym gg_connect()
 +				 * zwróci błąd, nie ma sensu próbować dalej. */
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
 +				goto fail_connecting;
 +			}
 +
 +			/* jeśli podano serwer i łączmy się przez proxy,
 +			 * jest to bezpośrednie połączenie, inaczej jest
 +			 * do huba. */
 +
 +			if (sess->proxy_addr && sess->proxy_port && sess->server_addr) {
 +				sess->state = GG_STATE_CONNECTING_GG;
 +				sess->soft_timeout = 1;
 +			} else
 +				sess->state = GG_STATE_CONNECTING_HUB;
 +
 +			sess->check = GG_CHECK_WRITE;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +			break;
 +		}
 +
 +		case GG_STATE_CONNECTING_HUB:
 +		{
 +			char buf[1024], *client, *auth;
 +			int res = 0;
 +			int res_size = sizeof(res);
 +			const char *host;
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
 +
 +			/* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
 +			 * przypadkiem jakiś błąd. */
 +			if (sess->async && (gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
 +				if (sess->proxy_addr && sess->proxy_port)
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
 +				else
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res));
 +
 +				goto fail_connecting;
 +			}
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
 +
 +			if (sess->client_version != NULL && isdigit(sess->client_version[0]))
 +				client = gg_urlencode(sess->client_version);
 +			else
 +				client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION);
 +
 +			if (client == NULL) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
 +				goto fail_connecting;
 +			}
 +
 +			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
 +				host = "http://" GG_APPMSG_HOST;
 +			else
 +				host = "";
 +
 +			auth = gg_proxy_auth();
 +
 +#ifdef GG_CONFIG_MIRANDA
 +			if (sess->tls) {
 +				snprintf(buf, sizeof(buf) - 1,
 +					"GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
 +					"Connection: close\r\n"
 +					"Host: " GG_APPMSG_HOST "\r\n"
 +					"%s"
 +					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
 +			} else
 +#elif defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
 +			if (sess->ssl != NULL) {
 +				snprintf(buf, sizeof(buf) - 1,
 +					"GET %s/appsvc/appmsg_ver11.asp?tls=1&fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
 +					"Connection: close\r\n"
 +					"Host: " GG_APPMSG_HOST "\r\n"
 +					"%s"
 +					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
 +			} else
 +#endif
 +			{
 +				snprintf(buf, sizeof(buf) - 1,
 +					"GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
 +					"Host: " GG_APPMSG_HOST "\r\n"
 +					"%s"
 +					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
 +			}
 +
 +			free(auth);
 +			free(client);
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
 +
 +			/* zapytanie jest krótkie, więc zawsze zmieści się
 +			 * do bufora gniazda. jeśli write() zwróci mniej,
 +			 * stało się coś złego. */
 +			if (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
 +
 +				e->type = GG_EVENT_CONN_FAILED;
 +				e->event.failure = GG_FAILURE_WRITING;
 +				sess->state = GG_STATE_IDLE;
 +				gg_sock_close(sess->fd);
 +				sess->fd = -1;
 +				break;
 +			}
 +
 +			sess->state = GG_STATE_READING_DATA;
 +			sess->check = GG_CHECK_READ;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +			break;
 +		}
 +
 +		case GG_STATE_READING_DATA:
 +		{
 +			char buf[1024], *tmp, *host;
 +			int port = GG_DEFAULT_PORT;
 +			struct in_addr addr;
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
 +
 +			/* czytamy linię z gniazda i obcinamy \r\n. */
 +			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 +			gg_chomp(buf);
 +			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
 +
 +			/* sprawdzamy, czy wszystko w porządku. */
 +			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
 +				goto fail_connecting;
 +			}
 +
 +			/* ignorujemy resztę nagłówka. */
 +			while (strcmp(buf, "\r\n") && strcmp(buf, ""))
 +				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 +
 +			/* czytamy pierwszą linię danych. */
 +			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 +			gg_chomp(buf);
 +
 +			/* jeśli pierwsza liczba w linii nie jest równa zeru,
 +			 * oznacza to, że mamy wiadomość systemową. */
 +			if (atoi(buf)) {
 +				char tmp[1024], *foo, *sysmsg_buf = NULL;
 +				int len = 0;
 +
 +				while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) {
 +					if (!(foo = (char*)realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
 +						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
 +						break;
 +					}
 +
 +					sysmsg_buf = foo;
 +
 +					if (!len)
 +						strcpy(sysmsg_buf, tmp);
 +					else
 +						strcat(sysmsg_buf, tmp);
 +
 +					len += (int)strlen(tmp);
 +				}
 +
 +				e->type = GG_EVENT_MSG;
 +				e->event.msg.msgclass = atoi(buf);
 +				e->event.msg.sender = 0;
 +				e->event.msg.message = (char*) sysmsg_buf;
 +			}
 +
 +			gg_sock_close(sess->fd);
 +
 +			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
 +
 +			/* analizujemy otrzymane dane. */
 +			tmp = buf;
 +
 +			while (*tmp && *tmp != ' ')
 +				tmp++;
 +			while (*tmp && *tmp == ' ')
 +				tmp++;
 +			while (*tmp && *tmp != ' ')
 +				tmp++;
 +			while (*tmp && *tmp == ' ')
 +				tmp++;
 +			host = tmp;
 +			while (*tmp && *tmp != ' ')
 +				tmp++;
 +			*tmp = 0;
 +
 +			if ((tmp = strchr(host, ':'))) {
 +				*tmp = 0;
 +				port = atoi(tmp + 1);
 +			}
 +
 +			if (!strcmp(host, "notoperating")) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
 +				sess->fd = -1;
 +				goto fail_unavailable;
 +			}
 +
 +			addr.s_addr = inet_addr(host);
 +			sess->server_addr = addr.s_addr;
 +
 +			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
 +				/* jeśli mamy proxy, łączymy się z nim. */
 +				if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
 +					/* nie wyszło? trudno. */
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
 +					goto fail_connecting;
 +				}
 +
 +				sess->state = GG_STATE_CONNECTING_GG;
 +				sess->check = GG_CHECK_WRITE;
 +				sess->timeout = GG_DEFAULT_TIMEOUT;
 +				sess->soft_timeout = 1;
 +				break;
 +			}
 +
 +			sess->port = port;
 +
 +			/* Jeśli podano nazwę, nie adres serwera... */
 +			if (sess->server_addr == INADDR_NONE) {
 +				if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) {
 +					gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
 +					goto fail_resolving;
 +				}
 +
 +				sess->state = GG_STATE_RESOLVING_GG;
 +				sess->check = GG_CHECK_READ;
 +				sess->timeout = GG_DEFAULT_TIMEOUT;
 +				break;
 +			}
 +
 +			/* łączymy się z właściwym serwerem. */
 +			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
 +
 +				sess->port = GG_HTTPS_PORT;
 +
 +				/* nie wyszło? próbujemy portu 443. */
 +				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
 +					/* ostatnia deska ratunku zawiodła?
 +					 * w takim razie zwijamy manatki. */
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 +					goto fail_connecting;
 +				}
 +			}
 +
 +			sess->state = GG_STATE_CONNECTING_GG;
 +			sess->check = GG_CHECK_WRITE;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +			sess->soft_timeout = 1;
 +
 +			break;
 +		}
 +
 +		case GG_STATE_RESOLVING_GG:
 +		{
 +			struct in_addr addr;
 +			int failed = 0;
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING_GG\n");
 +
 +			if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
 +				failed = 1;
 +				errno2 = errno;
 +			}
 +
 +			gg_sock_close(sess->fd);
 +			sess->fd = -1;
 +
 +			sess->resolver_cleanup(&sess->resolver, 0);
 +
 +			if (failed) {
 +				errno = errno2;
 +				goto fail_resolving;
 +			}
 +
 +			sess->server_addr = addr.s_addr;
 +
 +			/* łączymy się z właściwym serwerem. */
 +			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
 +
 +				sess->port = GG_HTTPS_PORT;
 +
 +				/* nie wyszło? próbujemy portu 443. */
 +				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
 +					/* ostatnia deska ratunku zawiodła?
 +					 * w takim razie zwijamy manatki. */
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 +					goto fail_connecting;
 +				}
 +			}
 +
 +			sess->state = GG_STATE_CONNECTING_GG;
 +			sess->check = GG_CHECK_WRITE;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +			sess->soft_timeout = 1;
 +
 +			break;
 +		}
 +
 +		case GG_STATE_CONNECTING_GG:
 +		{
 +			int res = 0;
 +			int res_size = sizeof(res);
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
 +
 +			sess->soft_timeout = 0;
 +
 +			/* jeśli wystąpił błąd podczas łączenia się... */
 +			if (sess->async && (sess->timeout == 0 || gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
 +				/* jeśli nie udało się połączenie z proxy,
 +				 * nie mamy czego próbować więcej. */
 +				if (sess->proxy_addr && sess->proxy_port) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
 +					goto fail_connecting;
 +				}
 +
 +				gg_sock_close(sess->fd);
 +				sess->fd = -1;
 +
 +#ifdef ETIMEDOUT
 +				if (sess->timeout == 0)
 +					errno = ETIMEDOUT;
 +#endif
 +
 +#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
 +				/* jeśli logujemy się po TLS, nie próbujemy
 +				 * się łączyć już z niczym innym w przypadku
 +				 * błędu. nie dość, że nie ma sensu, to i
 +				 * trzeba by się bawić w tworzenie na nowo
 +				 * SSL i SSL_CTX. */
 +
 +				if (sess->ssl) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
 +					goto fail_connecting;
 +				}
 +#endif
 +
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
 +
 +				if (sess->port == GG_HTTPS_PORT)
 +					goto fail_connecting;
 +
 +				sess->port = GG_HTTPS_PORT;
 +
 +				/* próbujemy na port 443. */
 +				if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 +					goto fail_connecting;
 +				}
 +
 +				sess->state = GG_STATE_CONNECTING_GG;
 +				sess->check = GG_CHECK_WRITE;
 +				sess->timeout = GG_DEFAULT_TIMEOUT;
 +				sess->soft_timeout = 1;
 +
 +				break;
 +			}
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
 +
 +			if (gg_proxy_http_only)
 +				sess->proxy_port = 0;
 +
 +			/* jeśli mamy proxy, wyślijmy zapytanie. */
 +			if (sess->proxy_addr && sess->proxy_port) {
 +				char buf[100], *auth = gg_proxy_auth();
 +				struct in_addr addr;
 +
 +				if (sess->server_addr)
 +					addr.s_addr = sess->server_addr;
 +				else
 +					addr.s_addr = sess->hub_addr;
 +
 +				snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
 +
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
 +
 +				/* wysyłamy zapytanie. jest ono na tyle krótkie,
 +				 * że musi się zmieścić w buforze gniazda. jeśli
 +				 * write() zawiedzie, stało się coś złego. */
 +				if (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
 +					free(auth);
 +					goto fail_connecting;
 +				}
 +
 +				if (auth) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "//   %s", auth);
 +					if (gg_sock_write(sess->fd, auth, (int)strlen(auth)) < (signed)strlen(auth)) {
 +						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
 +						free(auth);
 +						goto fail_connecting;
 +					}
 +
 +					free(auth);
 +				}
 +
 +				if (gg_sock_write(sess->fd, "\r\n", 2) < 2) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
 +					goto fail_connecting;
 +				}
 +			}
 +
 +#ifdef GG_CONFIG_MIRANDA
 +			if (sess->tls) {
 +				sess->state = GG_STATE_TLS_NEGOTIATION;
 +				sess->check = GG_CHECK_WRITE;
 +				sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +				break;
 +			}
 +#elif defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
 +			if (sess->ssl != NULL) {
 +#ifdef GG_CONFIG_HAVE_GNUTLS
 +				gnutls_transport_set_ptr(GG_SESSION_GNUTLS(sess), (gnutls_transport_ptr_t) sess->fd);
 +#endif
 +#ifdef GG_CONFIG_HAVE_OPENSSL
 +				SSL_set_fd(sess->ssl, (int)sess->fd);
 +#endif
 +
 +				sess->state = GG_STATE_TLS_NEGOTIATION;
 +				sess->check = GG_CHECK_WRITE;
 +				sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +				break;
 +			}
 +#endif
 +
 +			sess->state = GG_STATE_READING_KEY;
 +			sess->check = GG_CHECK_READ;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +			break;
 +		}
 +
 +#ifdef GG_CONFIG_MIRANDA
 +		case GG_STATE_TLS_NEGOTIATION:
 +		{
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
 +
 +			sess->ssl = sslApi.connect(sess->fd, 0, 0);
 +
 +			if (sess->ssl == NULL) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation failed\n");
 +
 +				e->type = GG_EVENT_CONN_FAILED;
 +				e->event.failure = GG_FAILURE_TLS;
 +				sess->state = GG_STATE_IDLE;
 +				gg_sock_close(sess->fd);
 +				sess->fd = -1;
 +				break;
 +			}
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded\n");
 +
 +			sess->state = GG_STATE_READING_KEY;
 +			sess->check = GG_CHECK_READ;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +			break;
 +		}
 +#elif GG_CONFIG_HAVE_GNUTLS
 +		case GG_STATE_TLS_NEGOTIATION:
 +		{
 +			int res;
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
 +
 +gnutls_handshake_repeat:
 +			res = gnutls_handshake(GG_SESSION_GNUTLS(sess));
 +
 +			if (res == GNUTLS_E_AGAIN) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_AGAIN\n");
 +
 +				sess->state = GG_STATE_TLS_NEGOTIATION;
 +				if (gnutls_record_get_direction(GG_SESSION_GNUTLS(sess)) == 0)
 +					sess->check = GG_CHECK_READ;
 +				else
 +					sess->check = GG_CHECK_WRITE;
 +				sess->timeout = GG_DEFAULT_TIMEOUT;
 +				break;
 +			}
 +
 +			if (res == GNUTLS_E_INTERRUPTED) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_INTERRUPTED\n");
 +				goto gnutls_handshake_repeat;
 +			}
 +
 +			if (res != 0) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake error %d\n", res);
 +				e->type = GG_EVENT_CONN_FAILED;
 +				e->event.failure = GG_FAILURE_TLS;
 +				sess->state = GG_STATE_IDLE;
 +				gg_sock_close(sess->fd);
 +				sess->fd = -1;
 +				break;
 +			}
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n");
 +			gg_debug_session(sess, GG_DEBUG_MISC, "//   cipher: VERS-%s:%s:%s:%s:COMP-%s\n",
 +				gnutls_protocol_get_name(gnutls_protocol_get_version(GG_SESSION_GNUTLS(sess))),
 +				gnutls_cipher_get_name(gnutls_cipher_get(GG_SESSION_GNUTLS(sess))),
 +				gnutls_kx_get_name(gnutls_kx_get(GG_SESSION_GNUTLS(sess))),
 +				gnutls_mac_get_name(gnutls_mac_get(GG_SESSION_GNUTLS(sess))),
 +				gnutls_compression_get_name(gnutls_compression_get(GG_SESSION_GNUTLS(sess))));
 +
 +			if (gnutls_certificate_type_get(GG_SESSION_GNUTLS(sess)) == GNUTLS_CRT_X509) {
 +				unsigned int peer_count;
 +				const gnutls_datum_t *peers;
 +				gnutls_x509_crt_t cert;
 +
 +				if (gnutls_x509_crt_init(&cert) >= 0) {
 +					peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count);
 +
 +					if (peers != NULL) {
 +						char buf[256];
 +						size_t size;
 +
 +						if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) >= 0) {
 +							size = sizeof(buf);
 +							gnutls_x509_crt_get_dn(cert, buf, &size);
 +							gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
 +							size = sizeof(buf);
 +							gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
 +							gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
 +						}
 +					}
 +				}
 +			}
 +
 +			sess->state = GG_STATE_READING_KEY;
 +			sess->check = GG_CHECK_READ;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +			break;
 +		}
 +#elif GG_CONFIG_HAVE_OPENSSL
 +		case GG_STATE_TLS_NEGOTIATION:
 +		{
 +			int res;
 +			X509 *peer;
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
 +
 +			if ((res = SSL_connect(sess->ssl)) <= 0) {
 +				int err = SSL_get_error(sess->ssl, res);
 +
 +				if (res == 0) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
 +
 +					e->type = GG_EVENT_CONN_FAILED;
 +					e->event.failure = GG_FAILURE_TLS;
 +					sess->state = GG_STATE_IDLE;
 +					gg_sock_close(sess->fd);
 +					sess->fd = -1;
 +					break;
 +				}
 +
 +				if (err == SSL_ERROR_WANT_READ) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
 +
 +					sess->state = GG_STATE_TLS_NEGOTIATION;
 +					sess->check = GG_CHECK_READ;
 +					sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +					break;
 +				} else if (err == SSL_ERROR_WANT_WRITE) {
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
 +
 +					sess->state = GG_STATE_TLS_NEGOTIATION;
 +					sess->check = GG_CHECK_WRITE;
 +					sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +					break;
 +				} else {
 +					char buf[256];
 +
 +					ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
 +
 +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
 +
 +					e->type = GG_EVENT_CONN_FAILED;
 +					e->event.failure = GG_FAILURE_TLS;
 +					sess->state = GG_STATE_IDLE;
 +					gg_sock_close(sess->fd);
 +					sess->fd = -1;
 +					break;
 +				}
 +			}
 +
 +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
 +
 +			peer = SSL_get_peer_certificate(sess->ssl);
 +
 +			if (!peer)
 +				gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
 +			else {
 +				char buf[256];
 +
 +				X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
 +				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
 +
 +				X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
 +				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
 +			}
 +
 +			sess->state = GG_STATE_READING_KEY;
 +			sess->check = GG_CHECK_READ;
 +			sess->timeout = GG_DEFAULT_TIMEOUT;
 +
 +			break;
 +		}
 +#endif
 +
 +		case GG_STATE_READING_KEY:
 +		case GG_STATE_READING_REPLY:
 +		case GG_STATE_CONNECTED:
 +		case GG_STATE_DISCONNECTING:
 +		{
 +			struct gg_header *gh;
 +
 +			if (sess->state == GG_STATE_READING_KEY)
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
 +			else if (sess->state == GG_STATE_READING_REPLY)
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
 +			else if (sess->state == GG_STATE_CONNECTED)
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
 +			else if (sess->state == GG_STATE_DISCONNECTING)
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_DISCONNECTING\n");
 +
 +			/* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
 +			 * się tekstu wrzucanego przez proxy. */
 +			if (sess->state == GG_STATE_READING_KEY && sess->proxy_addr && sess->proxy_port) {
 +				char buf[100];
 +
 +				strcpy(buf, "");
 +				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 +				gg_chomp(buf);
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf);
 +
 +				while (strcmp(buf, "")) {
 +					gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 +					gg_chomp(buf);
 +					if (strcmp(buf, ""))
 +						gg_debug_session(sess, GG_DEBUG_MISC, "//   %s\n", buf);
 +				}
 +
 +				/* XXX niech czeka jeszcze raz w tej samej
 +				 * fazie. głupio, ale działa. */
 +				sess->proxy_port = 0;
 +
 +				break;
 +			}
 +
 +			sess->last_event = (int)time(NULL);
 +
 +			gh = (gg_header*)gg_recv_packet(sess);
 +
 +			if (gh == NULL) {
 +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
 + 				if (errno == EAGAIN) {
 +					e->type = GG_EVENT_NONE;
 +					res = 0;
 +				} else {
 +					res = -1;
 +				}
 +
 +				goto done;
 +			}
 +
 +			if (gg_session_handle_packet(sess, gh->type, (const char *) gh + sizeof(struct gg_header), gh->length, e) == -1) {
 +				free(gh);
 +				res = -1;
 +				goto done;
 +			}
 +
 +			free(gh);
 +
 +			sess->check = GG_CHECK_READ;
 +
 +			break;
 +		}
 +	}
 +
 +done:
 +	if (res == -1) {
 +		free(e);
 +		e = NULL;
 +	} else {
 +		if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED))
 +			sess->check |= GG_CHECK_WRITE;
 +	}
 +
 +	return e;
 +
 +fail_connecting:
 +	if (sess->fd != -1) {
 +		errno2 = errno;
 +		gg_sock_close(sess->fd);
 +		errno = errno2;
 +		sess->fd = -1;
 +	}
 +	e->type = GG_EVENT_CONN_FAILED;
 +	e->event.failure = GG_FAILURE_CONNECTING;
 +	sess->state = GG_STATE_IDLE;
 +	goto done;
 +
 +fail_resolving:
 +	e->type = GG_EVENT_CONN_FAILED;
 +	e->event.failure = GG_FAILURE_RESOLVING;
 +	sess->state = GG_STATE_IDLE;
 +	goto done;
 +
 +fail_unavailable:
 +	e->type = GG_EVENT_CONN_FAILED;
 +	e->event.failure = GG_FAILURE_UNAVAILABLE;
 +	sess->state = GG_STATE_IDLE;
 +	goto done;
 +}
 +
 +/*
 + * Local variables:
 + * c-indentation-style: k&r
 + * c-basic-offset: 8
 + * indent-tabs-mode: notnil
 + * End:
 + *
 + * vim: shiftwidth=8:
 + */
 diff --git a/protocols/Gadu-Gadu/src/libgadu/handlers.cpp b/protocols/Gadu-Gadu/src/libgadu/handlers.cpp new file mode 100644 index 0000000000..da09c40b5f --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/handlers.cpp @@ -0,0 +1,1810 @@ +/* + *  (C) Copyright 2001-2011 Wojtek Kaniewski <wojtekka@irc.pl> + *                          Robert J. Woźny <speedy@ziew.org> + *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org> + *                          Tomasz Chiliński <chilek@chilan.com> + *                          Adam Wysocki <gophi@ekg.chmurka.net> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +/** + * \file handlers.c + * + * \brief Funkcje obsługi przychodzących pakietów + */ + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif + +#include <sys/types.h> +#ifdef _WIN32 +#include "win32.h" +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> +#ifdef sun +#  include <sys/filio.h> +#endif +#endif /* _WIN32 */ + +#include "compat.h" +#include "libgadu.h" +#include "resolver.h" +#include "session.h" +#include "protocol.h" +#include "encoding.h" +#include "message.h" +#include "internal.h" + +#include <errno.h> +#ifndef _WIN32 +#include <netdb.h> +#endif /* _WIN32 */ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#ifndef _WIN32 +#include <unistd.h> +#endif /* _WIN32 */ +#include <time.h> +#ifndef GG_CONFIG_MIRANDA +#ifdef GG_CONFIG_HAVE_OPENSSL +#  include <openssl/err.h> +#  include <openssl/rand.h> +#endif +#endif + +/** + * \internal Struktura opisująca funkcję obsługi pakietu. + */ +typedef struct { +	/* Typ pakietu */ +	uint32_t type; +	/* Stan w którym pakiet jest obsługiwany */ +	int state; +	/* Minimalny rozmiar danych pakietu */ +	int min_length; +	/* Funkcja obsługująca pakiet. Patrz gg_session_handle_packet(). */ +	int (*handler)(struct gg_session *, uint32_t, const char *, size_t, struct gg_event *); +} gg_packet_handler_t; + +/** + * \internal Obsługuje pakiet GG_WELCOME. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_welcome *w; +	int ret; +	uint8_t hash_buf[64]; +	uint32_t local_ip; + +	if (len < sizeof(struct gg_welcome)) { +		ge->type = GG_EVENT_CONN_FAILED; +		ge->event.failure = GG_FAILURE_INVALID; +		gs->state = GG_STATE_IDLE; +		close(gs->fd); +		gs->fd = -1; +		return 0; +	} + +	w = (struct gg_welcome*) ptr; +	w->key = gg_fix32(w->key); + +	memset(hash_buf, 0, sizeof(hash_buf)); + +	switch (gs->hash_type) { +		case GG_LOGIN_HASH_GG32: +		{ +			uint32_t hash; + +			hash = gg_fix32(gg_login_hash((unsigned char*) gs->password, w->key)); +			gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash); +			memcpy(hash_buf, &hash, sizeof(hash)); + +			break; +		} + +		case GG_LOGIN_HASH_SHA1: +		{ +#ifndef GG_DEBUG_DISABLE +			char tmp[41]; +			int i; +#endif + +			gg_login_hash_sha1(gs->password, w->key, hash_buf); + +#ifndef GG_DEBUG_DISABLE +			for (i = 0; i < 40; i += 2) +				snprintf(tmp + i, sizeof(tmp) - i, "%02x", hash_buf[i / 2]); + +			gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp); +#endif + +			break; +		} + +		default: +			break; +	} + +#if 0 +	if (gs->password != NULL && (gs->flags & (1 << GG_SESSION_FLAG_CLEAR_PASSWORD))) { +		memset(gs->password, 0, strlen(gs->password)); +		free(gs->password); +		gs->password = NULL; +	} +#endif + +	if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { +		struct sockaddr_in sin; +		int sin_len = sizeof(sin); + +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); + +		if (!getsockname(gs->fd, (struct sockaddr*) &sin, &sin_len)) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); +			local_ip = sin.sin_addr.s_addr; +		} else { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); +			local_ip = 0; +		} +	} else +		local_ip = gg_dcc_ip; + +	gs->client_addr = local_ip; + +	if (GG_SESSION_IS_PROTOCOL_8_0(gs)) { +		struct gg_login80 l80; +		const char *client_name, *version, *descr; +		uint32_t client_name_len, version_len, descr_len; + +		memset(&l80, 0, sizeof(l80)); +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN80 packet\n"); +		l80.uin = gg_fix32(gs->uin); +		memcpy(l80.language, GG8_LANG, sizeof(l80.language)); +		l80.hash_type = gs->hash_type; +		memcpy(l80.hash, hash_buf, sizeof(l80.hash)); +		l80.status = gg_fix32(gs->initial_status ? gs->initial_status : GG_STATUS_AVAIL); +		l80.flags = gg_fix32(gs->status_flags); +		l80.features = gg_fix32(gs->protocol_features); +		l80.image_size = gs->image_size; +		l80.dunno2 = 0x64; + +		if (gs->client_version != NULL && !isdigit(gs->client_version[0])) { +			client_name = ""; +			client_name_len = 0; +		} else { +			client_name = GG8_VERSION; +			client_name_len = strlen(GG8_VERSION); +		} + +		version = (gs->client_version != NULL) ? gs->client_version : GG_DEFAULT_CLIENT_VERSION; +		version_len = gg_fix32(client_name_len + strlen(version)); + +		descr = (gs->initial_descr != NULL) ? gs->initial_descr : ""; +		descr_len = (gs->initial_descr != NULL) ? gg_fix32(strlen(gs->initial_descr)) : 0; + +		ret = gg_send_packet(gs, +				GG_LOGIN80, +				&l80, sizeof(l80), +				&version_len, sizeof(version_len), +				client_name, client_name_len, +				version, strlen(version), +				&descr_len, sizeof(descr_len), +				descr, strlen(descr), +				NULL); +	} else { +		struct gg_login70 l70; + +		memset(&l70, 0, sizeof(l70)); +		l70.uin = gg_fix32(gs->uin); +		l70.hash_type = gs->hash_type; +		memcpy(l70.hash, hash_buf, sizeof(l70.hash)); +		l70.status = gg_fix32(gs->initial_status ? gs->initial_status : GG_STATUS_AVAIL); +		l70.version = gg_fix32(gs->protocol_version | gs->protocol_flags); +		if (gs->external_addr && gs->external_port > 1023) { +			l70.local_ip = gs->external_addr; +			l70.local_port = gg_fix16(gs->external_port); +		} else { +			l70.local_ip = local_ip; +			l70.local_port = gg_fix16(gg_dcc_port); +		} + +		l70.image_size = gs->image_size; +		l70.dunno2 = 0xbe; + +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN70 packet\n"); +		ret = gg_send_packet(gs, GG_LOGIN70, &l70, sizeof(l70), gs->initial_descr, (gs->initial_descr) ? strlen(gs->initial_descr) : 0, NULL); +	} + +	if (ret == -1) { +		int errno_copy; + +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); +		errno_copy = errno; +		close(gs->fd); +		errno = errno_copy; +		gs->fd = -1; +		ge->type = GG_EVENT_CONN_FAILED; +		ge->event.failure = GG_FAILURE_WRITING; +		gs->state = GG_STATE_IDLE; +		return -1; +	} + +	gs->state = GG_STATE_READING_REPLY; +	gs->check = GG_CHECK_READ; + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_LOGIN_OK. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_login_ok(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); +	ge->type = GG_EVENT_CONN_SUCCESS; +	gs->state = GG_STATE_CONNECTED; +	gs->check = GG_CHECK_READ; +	gs->timeout = -1; +	gs->status = (gs->initial_status) ? gs->initial_status : GG_STATUS_AVAIL; +#if 0 +	free(gs->status_descr); +	gs->status_descr = gs->initial_descr; +#else +	free(gs->initial_descr); +#endif +	gs->initial_descr = NULL; + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_LOGIN_FAILED. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_login_failed(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	if (type != GG_DISCONNECTING) +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); +	else +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); +	ge->type = GG_EVENT_CONN_FAILED; +	ge->event.failure = (type != GG_DISCONNECTING) ? GG_FAILURE_PASSWORD : GG_FAILURE_INTRUDER; +	gs->state = GG_STATE_IDLE; +	close(gs->fd); +	gs->fd = -1; +	errno = EACCES; + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_SEND_MSG_ACK. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_send_msg_ack(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_send_msg_ack *s = (struct gg_send_msg_ack*) ptr; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); + +	ge->type = GG_EVENT_ACK; +	ge->event.ack.status = gg_fix32(s->status); +	ge->event.ack.recipient = gg_fix32(s->recipient); +	ge->event.ack.seq = gg_fix32(s->seq); + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_PONG. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_pong(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); + +	ge->type = GG_EVENT_PONG; + +	gs->last_pong = time(NULL); + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_DISCONNECTING. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_disconnecting(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); + +	ge->type = GG_EVENT_DISCONNECT; + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_DISCONNECT_ACK. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_disconnect_ack(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received logoff acknowledge\n"); + +	ge->type = GG_EVENT_DISCONNECT_ACK; + +	return 0; +} + +/** + * \internal Obsługuje pakiety GG_XML_EVENT i GG_XML_ACTION. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_xml_event(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n"); + +	ge->type = GG_EVENT_XML_EVENT; +	ge->event.xml_event.data = (char*)malloc(len + 1); + +	if (ge->event.xml_event.data == NULL) { +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +		return -1; +	} + +	memcpy(ge->event.xml_event.data, ptr, len); +	ge->event.xml_event.data[len] = 0; + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_PUBDIR50_REPLY. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_pubdir50_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); + +	return gg_pubdir50_handle_reply_sess(gs, ge, ptr, len); +} + +/** + * \internal Obsługuje pakiet GG_USERLIST_REPLY. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_userlist_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	char reply_type; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); + +	reply_type = ptr[0]; + +	/* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko +	 * gdy otrzymano wszystkie odpowiedzi */ +	if (reply_type == GG_USERLIST_PUT_REPLY || reply_type == GG_USERLIST_PUT_MORE_REPLY) { +		if (--gs->userlist_blocks) +			return 0; + +		reply_type = GG_USERLIST_PUT_REPLY; +	} + +	if (len > 1) { +		unsigned int reply_len = (gs->userlist_reply != NULL) ? strlen(gs->userlist_reply) : 0; +		char *tmp; + +		gg_debug_session(gs, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", gs->userlist_reply, len); + +		tmp = (char*)realloc(gs->userlist_reply, reply_len + len); + +		if (tmp == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			return -1; +		} + +		gs->userlist_reply = tmp; +		memcpy(gs->userlist_reply + reply_len, ptr + 1, len - 1); +		gs->userlist_reply[reply_len + len - 1] = 0; +	} + +	if (reply_type == GG_USERLIST_GET_MORE_REPLY) +		return 0; + +	ge->type = GG_EVENT_USERLIST; +	ge->event.userlist.type = reply_type; +	ge->event.userlist.reply = gs->userlist_reply; + +	gs->userlist_reply = NULL; + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_DCC7_ID_REPLY. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_dcc7_id_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n"); + +	return gg_dcc7_handle_id(gs, ge, ptr, len); +} + +/** + * \internal Obsługuje pakiet GG_DCC7_ACCEPT. + * + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_dcc7_accept(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n"); + +	return gg_dcc7_handle_accept(gs, ge, ptr, len); +} + +/** + * \internal Obsługuje pakiet GG_DCC7_NEW. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_dcc7_new(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n"); + +	return gg_dcc7_handle_new(gs, ge, ptr, len); +} + +/** + * \internal Obsługuje pakiet GG_DCC7_REJECT. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_dcc7_reject(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n"); + +	return gg_dcc7_handle_reject(gs, ge, ptr, len); +} + +/** +* \internal Obsługuje pakiet GG_DCC7_INFO. +* +* Patrz gg_packet_handler_t +*/ +static int gg_session_handle_dcc7_info(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n"); +	 +	return gg_dcc7_handle_info(gs, ge, ptr, len); +} + +/** + * \internal Analizuje przychodzący pakiet z obrazkiem. + * + * \param e Struktura zdarzenia + * \param p Bufor z danymi + * \param len Długość bufora + * \param sess Struktura sesji + * \param sender Numer nadawcy + */ +static void gg_image_queue_parse(struct gg_event *e, const char *p, unsigned int len, struct gg_session *sess, uin_t sender) +{ +	struct gg_msg_image_reply *i = (gg_msg_image_reply*) p; +	struct gg_image_queue *q, *qq; + +	if (!p || !sess || !e) { +		errno = EFAULT; +		return; +	} + +	/* znajdź dany obrazek w kolejce danej sesji */ + +	for (qq = sess->images, q = NULL; qq; qq = qq->next) { +		if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) { +			q = qq; +			break; +		} +	} + +	if (!q) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); +		return; +	} + +	if (p[0] == GG_MSG_OPTION_IMAGE_REPLY) { +		q->done = 0; + +		len -= sizeof(struct gg_msg_image_reply); +		p += sizeof(struct gg_msg_image_reply); + +		if (memchr(p, 0, len) == NULL) { +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); +			return; +		} + +		if (!(q->filename = strdup(p))) { +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() out of memory\n"); +			return; +		} + +		len -= strlen(p) + 1; +		p += strlen(p) + 1; +	} else { +		len -= sizeof(struct gg_msg_image_reply); +		p += sizeof(struct gg_msg_image_reply); +	} + +	if (q->done + len > q->size) +		len = q->size - q->done; + +	memcpy(q->image + q->done, p, len); +	q->done += len; + +	/* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */ + +	if (q->done >= q->size) { +		e->type = GG_EVENT_IMAGE_REPLY; +		e->event.image_reply.sender = sender; +		e->event.image_reply.size = q->size; +		e->event.image_reply.crc32 = q->crc32; +		e->event.image_reply.filename = q->filename; +		e->event.image_reply.image = q->image; + +		gg_image_queue_remove(sess, q, 0); + +		free(q); +	} +} + +/** + * \internal Analizuje informacje rozszerzone wiadomości. + *  + * \param sess Struktura sesji. + * \param e Struktura zdarzenia. + * \param sender Numer nadawcy. + * \param p Wskaźnik na dane rozszerzone. + * \param packet_end Wskaźnik na koniec pakietu. + * + * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma + * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli + * wiadomość jest niepoprawna. + */ +static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, const char *p, const char *packet_end) +{ +	while (p < packet_end) { +		switch (*p) { +			case GG_MSG_OPTION_CONFERENCE: +			{ +				struct gg_msg_recipients *m = (gg_msg_recipients*) p; +				uint32_t i, count; + +				p += sizeof(*m); + +				if (p > packet_end) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1)\n"); +					goto malformed; +				} + +				count = gg_fix32(m->count); + +				if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1.5)\n"); +					goto malformed; +				} + +				if (e->event.msg.recipients != NULL) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.recipients already exist\n"); +					goto malformed; +				} + +				e->event.msg.recipients = (uin_t*)malloc(count * sizeof(uin_t)); + +				if (e->event.msg.recipients == NULL) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for recipients data\n"); +					goto fail; +				} + +				memcpy(e->event.msg.recipients, p, count * sizeof(uin_t)); +				p += count * sizeof(uin_t); + +				for (i = 0; i < count; i++) +					e->event.msg.recipients[i] = gg_fix32(e->event.msg.recipients[i]); + +				e->event.msg.recipients_count = count; + +				break; +			} + +			case GG_MSG_OPTION_ATTRIBUTES: +			{ +				uint16_t len; +				char *buf; + +				if (p + 3 > packet_end) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (2)\n"); +					goto malformed; +				} + +				memcpy(&len, p + 1, sizeof(uint16_t)); +				len = gg_fix16(len); + +				if (e->event.msg.formats != NULL) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.formats already exist\n"); +					goto malformed; +				} + +				buf = (char*)malloc(len); + +				if (buf == NULL) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for richtext data\n"); +					goto fail; +				} + +				p += 3; + +				if (p + len > packet_end) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3)\n"); +					free(buf); +					goto malformed; +				} + +				memcpy(buf, p, len); + +				e->event.msg.formats = buf; +				e->event.msg.formats_length = len; + +				p += len; + +				break; +			} + +			case GG_MSG_OPTION_IMAGE_REQUEST: +			{ +				struct gg_msg_image_request *i = (gg_msg_image_request*) p; + +				if (p + sizeof(*i) > packet_end) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); +					goto malformed; +				} + +				if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (1)\n"); +					goto malformed; +				} + +				e->event.image_request.sender = sender; +				e->event.image_request.size = gg_fix32(i->size); +				e->event.image_request.crc32 = gg_fix32(i->crc32); + +				e->type = GG_EVENT_IMAGE_REQUEST; + +				goto handled; +			} + +			case GG_MSG_OPTION_IMAGE_REPLY: +			case GG_MSG_OPTION_IMAGE_REPLY_MORE: +			{ +				struct gg_msg_image_reply *rep = (gg_msg_image_reply*) p; + +				if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) { +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (2)\n"); +					goto malformed; +				} + +				if (p + sizeof(struct gg_msg_image_reply) == packet_end) { + +					/* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */ + +					e->type = GG_EVENT_IMAGE_REPLY; +					e->event.image_reply.sender = sender; +					e->event.image_reply.size = 0; +					e->event.image_reply.crc32 = gg_fix32(rep->crc32); +					e->event.image_reply.filename = NULL; +					e->event.image_reply.image = NULL; +					goto handled; + +				} else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { + +					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); +					goto malformed; +				} + +				rep->size = gg_fix32(rep->size); +				rep->crc32 = gg_fix32(rep->crc32); +				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender); + +				goto handled; +			} + +			default: +			{ +				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); +				p = packet_end; +			} +		} +	} + +	return 0; + +handled: +	return -1; + +fail: +	return -2; + +malformed: +	return -3; +} + +/** + * \internal Wysyła potwierdzenie odebrania wiadomości. + * + * \param gs Struktura sesji + * + * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd + */ +static int gg_session_send_msg_ack(struct gg_session *gs) +{ +	struct gg_recv_msg_ack pkt; + +	gg_debug_session(gs, GG_DEBUG_FUNCTION, "** gg_session_send_msg_ack(%p);\n", gs); + +	if ((gs->protocol_features & GG_FEATURE_MSG_ACK) == 0) +		return 0; + +	gs->recv_msg_count++; +	pkt.count = gg_fix32(gs->recv_msg_count); + +	return gg_send_packet(gs, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL); +} + +/** + * \internal Obsługuje pakiet GG_RECV_MSG. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_recv_msg(struct gg_session *sess, uint32_t type, const char *packet, size_t length, struct gg_event *e) +{ +	const struct gg_recv_msg *r = (const struct gg_recv_msg*) packet; +	const char *payload = packet + sizeof(struct gg_recv_msg); +	const char *payload_end = packet + length; +	char *tmp; + +	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %d, %p);\n", packet, length, e); + +	if ((r->seq == 0) && (r->msgclass == 0)) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); +		goto malformed; +	} + +	// jednobajtowa wiadomość o treści \x02 to żądanie połączenia DCC +	if (*payload == GG_MSG_CALLBACK && payload == payload_end - 1) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); +		length = 1; +	} else { +		const char *options; +		 +		options = (const char*)memchr(payload, 0, (size_t) (payload_end - payload)); + +		if (options == NULL) { +			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); +			goto malformed; +		} +		 +		length = (size_t) (options - payload); + +		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), options + 1, payload_end)) { +			case -1:	// handled +				gg_session_send_msg_ack(sess); +				return 0; + +			case -2:	// failed +				goto fail; + +			case -3:	// malformed +				goto malformed; +		} +	} + +	e->type = GG_EVENT_MSG; +	e->event.msg.msgclass = gg_fix32(r->msgclass); +	e->event.msg.sender = gg_fix32(r->sender); +	e->event.msg.time = gg_fix32(r->time); +	e->event.msg.seq = gg_fix32(r->seq); + +	tmp = gg_encoding_convert(payload, GG_ENCODING_CP1250, sess->encoding, length, -1); +	if (tmp == NULL) +		goto fail; +	e->event.msg.message = tmp; + +	gg_session_send_msg_ack(sess); +	return 0; + +fail: +	free(e->event.msg.message); +	free(e->event.msg.recipients); +	free(e->event.msg.formats); +	return -1; + +malformed: +	e->type = GG_EVENT_NONE; +	free(e->event.msg.message); +	free(e->event.msg.xhtml_message); +	free(e->event.msg.recipients); +	free(e->event.msg.formats); +	gg_session_send_msg_ack(sess); +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_RECV_MSG80. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_recv_msg_80(struct gg_session *sess, uint32_t type, const char *packet, size_t length, struct gg_event *e) +{ +	const struct gg_recv_msg80 *r = (const struct gg_recv_msg80*) packet; +	uint32_t offset_plain; +	uint32_t offset_attr; + +	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %d, %p);\n", packet, length, e); + +	if (r->seq == 0 && r->msgclass == 0) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n"); +		goto malformed; +	} + +	offset_plain = gg_fix32(r->offset_plain); +	offset_attr = gg_fix32(r->offset_attr); + +	if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= length) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n"); +		goto malformed; +	} + +	if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > length) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n"); +		offset_attr = 0;	/* nie parsuj attr. */ +	} + +	/* Normalna sytuacja, więc nie podpada pod powyższy warunek. */ +	if (offset_attr == length) +		offset_attr = 0; + +	if (memchr(packet + offset_plain, 0, length - offset_plain) == NULL) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n"); +		goto malformed; +	} + +	if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) { +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n"); +		goto malformed; +	} + +	e->type = (type != GG_RECV_OWN_MSG) ? GG_EVENT_MSG : GG_EVENT_MULTILOGON_MSG; +	e->event.msg.msgclass = gg_fix32(r->msgclass); +	e->event.msg.sender = gg_fix32(r->sender); +	e->event.msg.time = gg_fix32(r->time); +	e->event.msg.seq = gg_fix32(r->seq); + +	if (offset_attr != 0) { +		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + length)) { +			case -1:	// handled +				gg_session_send_msg_ack(sess); +				return 0; + +			case -2:	// failed +				goto fail; + +			case -3:	// malformed +				goto malformed; +		} +	} + +	if (sess->encoding == GG_ENCODING_CP1250) { +		e->event.msg.message = strdup(packet + offset_plain); +	} else { +		if (offset_plain > sizeof(struct gg_recv_msg80)) { +			int len; + +			len = gg_message_html_to_text(NULL, packet + sizeof(struct gg_recv_msg80)); +			e->event.msg.message = (char*)malloc(len + 1); + +			if (e->event.msg.message == NULL) +				goto fail; + +			gg_message_html_to_text((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80)); +		} else { +			e->event.msg.message = gg_encoding_convert(packet + offset_plain, GG_ENCODING_CP1250, sess->encoding, -1, -1); +		} +	} + +	if (offset_plain > sizeof(struct gg_recv_msg80)) +		e->event.msg.xhtml_message = gg_encoding_convert(packet + sizeof(struct gg_recv_msg80), GG_ENCODING_UTF8, sess->encoding, -1, -1); +	else +		e->event.msg.xhtml_message = NULL; + +	gg_session_send_msg_ack(sess); +	return 0; + +fail: +	free(e->event.msg.message); +	free(e->event.msg.xhtml_message); +	free(e->event.msg.recipients); +	free(e->event.msg.formats); +	return -1; + +malformed: +	e->type = GG_EVENT_NONE; +	free(e->event.msg.message); +	free(e->event.msg.xhtml_message); +	free(e->event.msg.recipients); +	free(e->event.msg.formats); +	gg_session_send_msg_ack(sess); +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_STATUS. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_status(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_status *s = (gg_status*) ptr; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + +	ge->type = GG_EVENT_STATUS; +	ge->event.status.uin = gg_fix32(s->uin); +	ge->event.status.status = gg_fix32(s->status); +	ge->event.status.descr = NULL; + +	if (len > sizeof(*s)) { +		ge->event.status.descr = gg_encoding_convert(ptr + sizeof(*s), GG_ENCODING_CP1250, gs->encoding, len - sizeof(*s), -1); + +		if (ge->event.status.descr == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			return -1; +		} +	} + +	return 0; +} + +/** + * \internal Obsługuje pakiety GG_STATUS60, GG_STATUS77 i GG_STATUS80BETA. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_status_60_77_80beta(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_status60 *s60 = (gg_status60*) ptr; +	struct gg_status77 *s77 = (gg_status77*) ptr; +	size_t struct_len; +	uint32_t uin; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + +	ge->type = GG_EVENT_STATUS60; +	ge->event.status60.descr = NULL; +	ge->event.status60.time = 0; + +	if (type == GG_STATUS60) { +		uin = gg_fix32(s60->uin); +		ge->event.status60.status = s60->status; +		ge->event.status60.remote_ip = s60->remote_ip; +		ge->event.status60.remote_port = gg_fix16(s60->remote_port); +		ge->event.status60.version = s60->version; +		ge->event.status60.image_size = s60->image_size; +		struct_len = sizeof(*s60); +	} else { +		uin = gg_fix32(s77->uin); +		ge->event.status60.status = s77->status; +		ge->event.status60.remote_ip = s77->remote_ip; +		ge->event.status60.remote_port = gg_fix16(s77->remote_port); +		ge->event.status60.version = s77->version; +		ge->event.status60.image_size = s77->image_size; +		struct_len = sizeof(*s77); +	} + +	ge->event.status60.uin = uin & 0x00ffffff; + +	if (uin & 0x40000000) +		ge->event.status60.version |= GG_HAS_AUDIO_MASK; +	if (uin & 0x20000000) +		ge->event.status60.version |= GG_HAS_AUDIO7_MASK; +	if (uin & 0x08000000) +		ge->event.status60.version |= GG_ERA_OMNIX_MASK; + +	if (len > struct_len) { +		size_t descr_len; + +		descr_len = len - struct_len; + +		ge->event.status60.descr = gg_encoding_convert(ptr + struct_len, (type == GG_STATUS80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250, gs->encoding, descr_len, -1); + +		if (ge->event.status60.descr == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			return -1; +		} + +		if (descr_len > 4 && ptr[len - 5] == 0) { +			uint32_t t; +			memcpy(&t, ptr + len - 4, sizeof(uint32_t)); +			ge->event.status60.time = gg_fix32(t); +		} +	} + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_NOTIFY_REPLY. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_notify_reply(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_notify_reply *n = (gg_notify_reply*) ptr; +	char *descr; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + +	if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) { +		size_t descr_len; + +		ge->type = GG_EVENT_NOTIFY_DESCR; + +		if (!(ge->event.notify_descr.notify = (gg_notify_reply*) malloc(sizeof(*n) * 2))) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			return -1; +		} +		ge->event.notify_descr.notify[1].uin = 0; +		memcpy(ge->event.notify_descr.notify, ptr, sizeof(*n)); +		ge->event.notify_descr.notify[0].uin = gg_fix32(ge->event.notify_descr.notify[0].uin); +		ge->event.notify_descr.notify[0].status = gg_fix32(ge->event.notify_descr.notify[0].status); +		ge->event.notify_descr.notify[0].remote_port = gg_fix16(ge->event.notify_descr.notify[0].remote_port); +		ge->event.notify_descr.notify[0].version = gg_fix32(ge->event.notify_descr.notify[0].version); + +		descr_len = len - sizeof(*n); + +		descr = gg_encoding_convert(ptr + sizeof(*n), GG_ENCODING_CP1250, gs->encoding, descr_len, -1); + +		if (descr == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			return -1; +		} + +		ge->event.notify_descr.descr = descr; + +	} else { +		unsigned int i, count; + +		ge->type = GG_EVENT_NOTIFY; + +		if (!(ge->event.notify = (gg_notify_reply*) malloc(len + 2 * sizeof(*n)))) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			return -1; +		} + +		memcpy(ge->event.notify, ptr, len); +		count = len / sizeof(*n); +		ge->event.notify[count].uin = 0; + +		for (i = 0; i < count; i++) { +			ge->event.notify[i].uin = gg_fix32(ge->event.notify[i].uin); +			ge->event.notify[i].status = gg_fix32(ge->event.notify[i].status); +			ge->event.notify[i].remote_port = gg_fix16(ge->event.notify[i].remote_port); +			ge->event.notify[i].version = gg_fix32(ge->event.notify[i].version); +		} +	} + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_STATUS80. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_status_80(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_notify_reply80 *n = (gg_notify_reply80*) ptr; +	size_t descr_len; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + +	ge->type = GG_EVENT_STATUS60; +	ge->event.status60.uin = gg_fix32(n->uin); +	ge->event.status60.status = gg_fix32(n->status); +	ge->event.status60.remote_ip = n->remote_ip; +	ge->event.status60.remote_port = gg_fix16(n->remote_port); +	ge->event.status60.version = 0; +	ge->event.status60.image_size = n->image_size; +	ge->event.status60.descr = NULL; +	ge->event.status60.time = 0; + +	descr_len = gg_fix32(n->descr_len); + +	if (descr_len != 0 && sizeof(struct gg_notify_reply80) + descr_len <= len) { +		ge->event.status60.descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply80), GG_ENCODING_UTF8, gs->encoding, descr_len, -1); + +		if (ge->event.status60.descr == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			return -1; +		} + +		/* XXX czas */ +	} + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_NOTIFY_REPLY80. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_notify_reply_80(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_notify_reply80 *n = (gg_notify_reply80*) ptr; +	unsigned int length = len, i = 0; + +	// TODO: najpierw przeanalizować strukturę i określić +	// liczbę rekordów, żeby obyć się bez realloc() + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + +	ge->type = GG_EVENT_NOTIFY60; +	ge->event.notify60 = (gg_event_notify60*)malloc(sizeof(*ge->event.notify60)); + +	if (!ge->event.notify60) { +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +		return -1; +	} + +	ge->event.notify60[0].uin = 0; + +	while (length >= sizeof(struct gg_notify_reply80)) { +		uin_t uin = gg_fix32(n->uin); +		int descr_len; +		char *tmp; + +		ge->event.notify60[i].uin = uin; +		ge->event.notify60[i].status = gg_fix32(n->status); +		ge->event.notify60[i].remote_ip = n->remote_ip; +		ge->event.notify60[i].remote_port = gg_fix16(n->remote_port); +		ge->event.notify60[i].version = 0; +		ge->event.notify60[i].image_size = n->image_size; +		ge->event.notify60[i].descr = NULL; +		ge->event.notify60[i].time = 0; + +		descr_len = gg_fix32(n->descr_len); + +		if (descr_len != 0) { +			if (sizeof(struct gg_notify_reply80) + descr_len <= length) { +				ge->event.notify60[i].descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply80), GG_ENCODING_UTF8, gs->encoding, descr_len, -1); + +				if (ge->event.notify60[i].descr == NULL) { +					gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +					return -1; +				} + +				/* XXX czas */ + +				length -= sizeof(struct gg_notify_reply80) + descr_len; +				n = (gg_notify_reply80*) ((char*) n + sizeof(struct gg_notify_reply80) + descr_len); +			} else { +				length = 0; +			} + +		} else { +			length -= sizeof(struct gg_notify_reply80); +			n = (gg_notify_reply80*) ((char*) n + sizeof(struct gg_notify_reply80)); +		} + +		if (!(tmp = (char*)realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			free(ge->event.notify60); +			return -1; +		} + +		ge->event.notify60 = (gg_event_notify60*) tmp; +		ge->event.notify60[++i].uin = 0; +	} + +	return 0; +} + +/** + * \internal Obsługuje pakiety GG_NOTIFY_REPLY77 i GG_NOTIFY_REPLY80BETA. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_notify_reply_77_80beta(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_notify_reply77 *n = (gg_notify_reply77*) ptr; +	unsigned int length = len, i = 0; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + +	ge->type = GG_EVENT_NOTIFY60; +	ge->event.notify60 = (gg_event_notify60*)malloc(sizeof(*ge->event.notify60)); + +	if (ge->event.notify60 == NULL) { +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +		return -1; +	} + +	ge->event.notify60[0].uin = 0; + +	while (length >= sizeof(struct gg_notify_reply77)) { +		uin_t uin = gg_fix32(n->uin); +		char *tmp; + +		ge->event.notify60[i].uin = uin & 0x00ffffff; +		ge->event.notify60[i].status = n->status; +		ge->event.notify60[i].remote_ip = n->remote_ip; +		ge->event.notify60[i].remote_port = gg_fix16(n->remote_port); +		ge->event.notify60[i].version = n->version; +		ge->event.notify60[i].image_size = n->image_size; +		ge->event.notify60[i].descr = NULL; +		ge->event.notify60[i].time = 0; + +		if (uin & 0x40000000) +			ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK; +		if (uin & 0x20000000) +			ge->event.notify60[i].version |= GG_HAS_AUDIO7_MASK; +		if (uin & 0x08000000) +			ge->event.notify60[i].version |= GG_ERA_OMNIX_MASK; + +		if (GG_S_D(n->status)) { +			unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77)); + +			if (sizeof(struct gg_notify_reply77) + descr_len <= length) { +				ge->event.notify60[i].descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply77) + 1, (type == GG_NOTIFY_REPLY80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250, gs->encoding, descr_len, -1); + +				if (ge->event.notify60[i].descr == NULL) { +					gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +					return -1; +				} + +				/* XXX czas */ +					 +				length -= sizeof(struct gg_notify_reply77) + descr_len + 1; +				n = (gg_notify_reply77*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1); +			} else { +				length = 0; +			} + +		} else { +			length -= sizeof(struct gg_notify_reply77); +			n = (gg_notify_reply77*) ((char*) n + sizeof(struct gg_notify_reply77)); +		} + +		if (!(tmp = (char*)realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			free(ge->event.notify60); +			return -1; +		} + +		ge->event.notify60 = (gg_event_notify60*) tmp; +		ge->event.notify60[++i].uin = 0; +	} + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_NOTIFY_REPLY60. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_notify_reply_60(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_notify_reply60 *n = (gg_notify_reply60*) ptr; +	unsigned int length = len, i = 0; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + +	ge->type = GG_EVENT_NOTIFY60; +	ge->event.notify60 = (gg_event_notify60*)malloc(sizeof(*ge->event.notify60)); + +	if (ge->event.notify60 == NULL) { +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +		return -1; +	} + +	ge->event.notify60[0].uin = 0; + +	while (length >= sizeof(struct gg_notify_reply60)) { +		uin_t uin = gg_fix32(n->uin); +		char *tmp; + +		ge->event.notify60[i].uin = uin & 0x00ffffff; +		ge->event.notify60[i].status = n->status; +		ge->event.notify60[i].remote_ip = n->remote_ip; +		ge->event.notify60[i].remote_port = gg_fix16(n->remote_port); +		ge->event.notify60[i].version = n->version; +		ge->event.notify60[i].image_size = n->image_size; +		ge->event.notify60[i].descr = NULL; +		ge->event.notify60[i].time = 0; + +		if (uin & 0x40000000) +			ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK; +		if (uin & 0x08000000) +			ge->event.notify60[i].version |= GG_ERA_OMNIX_MASK; + +		if (GG_S_D(n->status)) { +			unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); + +			if (sizeof(struct gg_notify_reply60) + descr_len <= length) { +				char *descr; + +				descr = gg_encoding_convert((char*) n + sizeof(struct gg_notify_reply60) + 1, GG_ENCODING_CP1250, gs->encoding, descr_len, -1); + +				if (descr == NULL) { +					gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +					return -1; +				} + +				ge->event.notify60[i].descr = descr; + +				/* XXX czas */ +					 +				length -= sizeof(struct gg_notify_reply60) + descr_len + 1; +				n = (gg_notify_reply60*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); +			} else { +				length = 0; +			} + +		} else { +			length -= sizeof(struct gg_notify_reply60); +			n = (gg_notify_reply60*) ((char*) n + sizeof(struct gg_notify_reply60)); +		} + +		if (!(tmp = (char*)realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n"); +			free(ge->event.notify60); +			return -1; +		} + +		ge->event.notify60 = (gg_event_notify60*) tmp; +		ge->event.notify60[++i].uin = 0; +	} + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_USER_DATA. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_user_data(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_user_data d; +	char *p = (char*) ptr; +	char *packet_end = (char*) ptr + len; +	struct gg_event_user_data_user *users; +	int i, j; +	int res = 0; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received user data\n"); + +	ge->event.user_data.user_count = 0; +	ge->event.user_data.users = NULL; + +	if (ptr + sizeof(d) > packet_end) +		goto malformed; + +	memcpy(&d, p, sizeof(d)); +	p += sizeof(d); + +	d.type = gg_fix32(d.type); +	d.user_count = gg_fix32(d.user_count); + +	if (d.user_count > 0xffff) { +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (1)\n"); +		goto malformed; +	} + +	if (d.user_count > 0) { +		users = (gg_event_user_data_user*)calloc(d.user_count, sizeof(struct gg_event_user_data_user)); + +		if (users == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d*%d)\n", d.user_count, sizeof(struct gg_event_user_data_user)); +			goto fail; +		} +	} else { +		users = NULL; +	} + +	ge->type = GG_EVENT_USER_DATA; +	ge->event.user_data.type = d.type; +	ge->event.user_data.user_count = d.user_count; +	ge->event.user_data.users = users; +	 +	gg_debug_session(gs, GG_DEBUG_DUMP, "type=%d, count=%d\n", d.type, d.user_count); + +	for (i = 0; i < d.user_count; i++) { +		struct gg_user_data_user u; +		struct gg_event_user_data_attr *attrs; + +		if (p + sizeof(u) > packet_end) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n"); +			goto malformed; +		} + +		memcpy(&u, p, sizeof(u)); +		p += sizeof(u); + +		u.uin = gg_fix32(u.uin); +		u.attr_count = gg_fix32(u.attr_count); + +		if (u.attr_count > 0xffff) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n"); +			goto malformed; +		} + +		if (u.attr_count > 0) { +			attrs = (gg_event_user_data_attr*)calloc(u.attr_count, sizeof(struct gg_event_user_data_attr)); + +			if (attrs == NULL) { +				gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d*%d)\n", u.attr_count, sizeof(struct gg_event_user_data_attr)); +				goto fail; +			} +		} else { +			attrs = NULL; +		} + +		users[i].uin = u.uin; +		users[i].attr_count = u.attr_count; +		users[i].attrs = attrs; + +		gg_debug_session(gs, GG_DEBUG_DUMP, "    uin=%d, count=%d\n", u.uin, u.attr_count); + +		for (j = 0; j < u.attr_count; j++) { +			uint32_t key_size; +			uint32_t attr_type; +			uint32_t value_size; +			char *key; +			char *value; + +			if (p + sizeof(key_size) > packet_end) { +				gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (3)\n"); +				goto malformed; +			} + +			memcpy(&key_size, p, sizeof(key_size)); +			p += sizeof(key_size); + +			key_size = gg_fix32(key_size); + +			if (key_size > 0xffff || p + key_size > packet_end) { +				gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (3)\n"); +				goto malformed; +			} + +			key = (char*)malloc(key_size + 1); + +			if (key == NULL) { +				gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d)\n", key_size + 1); +				goto fail; +			} + +			memcpy(key, p, key_size); +			p += key_size; + +			key[key_size] = 0; + +			attrs[j].key = key; + +			if (p + sizeof(attr_type) + sizeof(value_size) > packet_end) { +				gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (4)\n"); +				goto malformed; +			} + +			memcpy(&attr_type, p, sizeof(attr_type)); +			p += sizeof(attr_type); +			memcpy(&value_size, p, sizeof(value_size)); +			p += sizeof(value_size); + +			attrs[j].type = gg_fix32(attr_type); +			value_size = gg_fix32(value_size); + +			if (value_size > 0xffff || p + value_size > packet_end) { +				gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (5)\n"); +				goto malformed; +			} + +			value = (char*)malloc(value_size + 1); + +			if (value == NULL) { +				gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() out of memory (%d)\n", value_size + 1); +				goto fail; +			} + +			memcpy(value, p, value_size); +			p += value_size; + +			value[value_size] = 0; + +			attrs[j].value = value; + +			gg_debug_session(gs, GG_DEBUG_DUMP, "        key=\"%s\", type=%d, value=\"%s\"\n", key, attr_type, value); +		} +	} + +	return 0; + +fail: +	res = -1; + +malformed: +	ge->type = GG_EVENT_NONE; + +	for (i = 0; i < ge->event.user_data.user_count; i++) { +		for (j = 0; j < ge->event.user_data.users[i].attr_count; j++) { +			free(ge->event.user_data.users[i].attrs[j].key); +			free(ge->event.user_data.users[i].attrs[j].value); +		} + +		free(ge->event.user_data.users[i].attrs); +	} + +	free(ge->event.user_data.users); + +	return res; +} + +/** + * \internal Obsługuje pakiet GG_TYPING_NOTIFICATION. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_typing_notification(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	struct gg_typing_notification *n = (struct gg_typing_notification*) ptr; +	uin_t uin; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received typing notification\n"); + +	memcpy(&uin, &n->uin, sizeof(uin_t)); + +	ge->type = GG_EVENT_TYPING_NOTIFICATION; +	ge->event.typing_notification.uin = gg_fix32(uin); +	ge->event.typing_notification.length = gg_fix16(n->length); + +	return 0; +} + +/** + * \internal Obsługuje pakiet GG_MULTILOGON_INFO. + *  + * Patrz gg_packet_handler_t + */ +static int gg_session_handle_multilogon_info(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	char *packet_end = (char*) ptr + len; +	struct gg_multilogon_info *info = (struct gg_multilogon_info*) ptr; +	char *p = (char*) ptr + sizeof(*info); +	struct gg_multilogon_session *sessions = NULL; +	size_t count; +	size_t i; +	int res = 0; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received multilogon info\n"); + +	count = gg_fix32(info->count); + +	if (count > 0xffff) { +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (1)\n"); +		goto malformed; +	} + +	sessions = (gg_multilogon_session*)calloc(count, sizeof(struct gg_multilogon_session)); + +	if (sessions == NULL) { +		gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d*%d)\n", count, sizeof(struct gg_multilogon_session)); +		return -1; +	} +	 +	ge->type = GG_EVENT_MULTILOGON_INFO; +	ge->event.multilogon_info.count = count; +	ge->event.multilogon_info.sessions = sessions; + +	for (i = 0; i < count; i++) { +		struct gg_multilogon_info_item item; +		size_t name_size; + +		if (p + sizeof(item) > packet_end) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (2)\n"); +			goto malformed; +		} + +		memcpy(&item, p, sizeof(item)); + +		sessions[i].id = item.conn_id; +		sessions[i].remote_addr = item.addr; +		sessions[i].status_flags = gg_fix32(item.flags); +		sessions[i].protocol_features = gg_fix32(item.features); +		sessions[i].logon_time = gg_fix32(item.logon_time); + +		p += sizeof(item); + +		name_size = gg_fix32(item.name_size); + +		if (name_size > 0xffff || p + name_size > packet_end) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (3)\n"); +			goto malformed; +		} + +		sessions[i].name = (char*)malloc(name_size + 1); + +		if (sessions[i].name == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d)\n", name_size); +			goto fail; +		} + +		memcpy(sessions[i].name, p, name_size); +		sessions[i].name[name_size] = 0; + +		p += name_size; +	} + +	return 0; + +fail: +	res = -1; + +malformed: +	ge->type = GG_EVENT_NONE; + +	for (i = 0; i < ge->event.multilogon_info.count; i++) +		free(ge->event.multilogon_info.sessions[i].name); + +	free(ge->event.multilogon_info.sessions); + +	return res; +} + +/** + * \internal Tablica obsługiwanych pakietów + */ +static const gg_packet_handler_t handlers[] = +{ +	{ GG_WELCOME, GG_STATE_READING_KEY, 0, gg_session_handle_welcome }, +	{ GG_LOGIN_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok }, +	{ GG_LOGIN80_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok }, +	{ GG_NEED_EMAIL, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok }, +	{ GG_LOGIN_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed }, +	{ GG_LOGIN80_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed }, +	{ GG_SEND_MSG_ACK, GG_STATE_CONNECTED, sizeof(struct gg_send_msg_ack), gg_session_handle_send_msg_ack }, +	{ GG_PONG, GG_STATE_CONNECTED, 0, gg_session_handle_pong }, +	{ GG_DISCONNECTING, GG_STATE_CONNECTED, 0, gg_session_handle_disconnecting }, +	{ GG_DISCONNECT_ACK, GG_STATE_DISCONNECTING, 0, gg_session_handle_disconnect_ack }, +	{ GG_XML_EVENT, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event }, +	{ GG_PUBDIR50_REPLY, GG_STATE_CONNECTED, 0, gg_session_handle_pubdir50_reply }, +	{ GG_USERLIST_REPLY, GG_STATE_CONNECTED, 0, gg_session_handle_userlist_reply }, +	{ GG_DCC7_ID_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_id_reply), gg_session_handle_dcc7_id_reply }, +	{ GG_DCC7_ACCEPT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_accept), gg_session_handle_dcc7_accept }, +	{ GG_DCC7_NEW, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_new), gg_session_handle_dcc7_new }, +	{ GG_DCC7_REJECT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_reject), gg_session_handle_dcc7_reject }, +	{ GG_DCC7_INFO, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_info), gg_session_handle_dcc7_info }, +	{ GG_RECV_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg), gg_session_handle_recv_msg }, +	{ GG_RECV_MSG80, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 }, +	{ GG_STATUS, GG_STATE_CONNECTED, sizeof(struct gg_status), gg_session_handle_status }, +	{ GG_STATUS60, GG_STATE_CONNECTED, sizeof(struct gg_status60), gg_session_handle_status_60_77_80beta }, +	{ GG_STATUS77, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta }, +	{ GG_STATUS80BETA, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta }, +	{ GG_STATUS80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_status_80 }, +	{ GG_NOTIFY_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply), gg_session_handle_notify_reply }, +	{ GG_NOTIFY_REPLY60, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply60), gg_session_handle_notify_reply_60 }, +	{ GG_NOTIFY_REPLY77, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta }, +	{ GG_NOTIFY_REPLY80BETA, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta }, +	{ GG_NOTIFY_REPLY80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_notify_reply_80 }, +	{ GG_USER_DATA, GG_STATE_CONNECTED, sizeof(struct gg_user_data), gg_session_handle_user_data }, +	{ GG_TYPING_NOTIFICATION, GG_STATE_CONNECTED, sizeof(struct gg_typing_notification), gg_session_handle_typing_notification }, +	{ GG_MULTILOGON_INFO, GG_STATE_CONNECTED, sizeof(struct gg_multilogon_info), gg_session_handle_multilogon_info }, +	{ GG_XML_ACTION, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event }, +	{ GG_RECV_OWN_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 }, +}; + +/** + * \internal Analizuje przychodzący pakiet danych. + * + * \param gs Struktura sesji + * \param type Typ pakietu + * \param ptr Wskaźnik do bufora pakietu + * \param len Długość bufora pakietu + * \param[out] ge Struktura zdarzenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_session_handle_packet(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge) +{ +	int i; + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet(%d, %p, %d)\n", type, ptr, len); + +	gs->last_event = time(NULL); + +#if 0 +	if ((gs->flags & (1 << GG_SESSION_FLAG_RAW_PACKET)) != 0) { +		char *tmp; + +		tmp = malloc(len); + +		if (tmp == NULL) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() out of memory (%d bytes)\n", len); +			return -1; +		} + +		memcpy(tmp, ptr, len); + +		ge->type = GG_EVENT_RAW_PACKET; +		ge->event.raw_packet.type = type; +		ge->event.raw_packet.length = len; +		ge->event.raw_packet.data = tmp; + +		return 0; +	} +#endif + +	for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) { +		if (handlers[i].type != 0 && handlers[i].type != type) +			continue; + +		if (handlers[i].state != 0 && handlers[i].state != gs->state) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() packet 0x%02x unexpected in state %d\n", type, gs->state); +			continue; +		} + +		if (len < handlers[i].min_length) { +			gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() packet 0x%02x too short (%d bytes)\n", type, len); +			continue; +		} + +		return (*handlers[i].handler)(gs, type, ptr, len, ge); +	} + +	gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_packet() unhandled packet 0x%02x, len %d, state %d\n", type, len, gs->state); + +	return 0; +} diff --git a/protocols/Gadu-Gadu/src/libgadu/http.cpp b/protocols/Gadu-Gadu/src/libgadu/http.cpp index e1cc1b457f..5b8549dc3b 100644 --- a/protocols/Gadu-Gadu/src/libgadu/http.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/http.cpp @@ -1,4 +1,3 @@ -/* coding: UTF-8 */
  /* $Id: http.c 11370 2010-03-13 16:17:54Z dezred $ */
  /*
 @@ -137,21 +136,26 @@ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const  		h->check = GG_CHECK_READ;
  		h->timeout = GG_DEFAULT_TIMEOUT;
  	} else {
 -		struct in_addr addr;
 +		struct in_addr *addr_list = NULL;
 +		int addr_count;
 -		if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 +		if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 0) == -1 || addr_count == 0) {
  			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
  			gg_http_free(h);
 +			free(addr_list);
  			errno = ENOENT;
  			return NULL;
  		}
 -		if ((h->fd = gg_connect(&addr, port, 0)) == -1) {
 +		if ((h->fd = gg_connect(&addr_list[0], port, 0)) == -1) {
  			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
  			gg_http_free(h);
 +			free(addr_list);
  			return NULL;
  		}
 +		free(addr_list);
 +
  		h->state = GG_STATE_CONNECTING;
  		while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
 diff --git a/protocols/Gadu-Gadu/src/libgadu/internal.h b/protocols/Gadu-Gadu/src/libgadu/internal.h index 0ffd39fdd5..ba9655288a 100644 --- a/protocols/Gadu-Gadu/src/libgadu/internal.h +++ b/protocols/Gadu-Gadu/src/libgadu/internal.h @@ -1,48 +1,44 @@ -/* coding: UTF-8 */ -/* $Id$ */ - -/* - *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> - * - *  This program is free software; you can redistribute it and/or modify - *  it under the terms of the GNU Lesser General Public License Version - *  2.1 as published by the Free Software Foundation. - * - *  This program is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU Lesser General Public License for more details. - * - *  You should have received a copy of the GNU Lesser General Public - *  License along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - *  USA. - */ - -#ifndef LIBGADU_INTERNAL_H -#define LIBGADU_INTERNAL_H - -#include "libgadu.h" - +/* $Id$ */
 +
 +/*
 + *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU Lesser General Public License Version
 + *  2.1 as published by the Free Software Foundation.
 + *
 + *  This program is distributed in the hope that it will be useful,
 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + *  GNU Lesser General Public License for more details.
 + *
 + *  You should have received a copy of the GNU Lesser General Public
 + *  License along with this program; if not, write to the Free Software
 + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
 + *  USA.
 + */
 +
 +#ifndef LIBGADU_INTERNAL_H
 +#define LIBGADU_INTERNAL_H
 +
 +#include "libgadu.h"
 +
  struct gg_dcc7_relay {
  	uint32_t addr;
  	uint16_t port;
  	uint8_t family;
  };
 -typedef struct gg_dcc7_relay gg_dcc7_relay_t; - -char *gg_cp_to_utf8(const char *b); -char *gg_utf8_to_cp(const char *b); -int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length); -void gg_debug_dump_session(struct gg_session *sess, const void *buf, unsigned int buf_length, const char *format, ...); - -int gg_resolve(int *fd, int *pid, const char *hostname); -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname); -void gg_resolve_pthread_cleanup(void *resolver, int kill); - +typedef struct gg_dcc7_relay gg_dcc7_relay_t;
 +
 +int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
 +
 +int gg_resolve(int *fd, int *pid, const char *hostname);
 +int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
 +void gg_resolve_pthread_cleanup(void *resolver, int kill);
 +
  #ifdef GG_CONFIG_HAVE_UINT64_T
  uint64_t gg_fix64(uint64_t x);
 -#endif - -#endif /* LIBGADU_INTERNAL_H */ +#endif
 +
 +#endif /* LIBGADU_INTERNAL_H */
 diff --git a/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp b/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp index 197c6093d3..be9c2499b6 100644 --- a/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/libgadu.cpp @@ -1,5 +1,4 @@ -/* coding: UTF-8 */
 -/* $Id: libgadu.c 13762 2011-08-09 12:35:16Z dezred $ */
 +/* $Id$ */
  /*
   *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
 @@ -50,6 +49,9 @@  #include "protocol.h"
  #include "resolver.h"
  #include "internal.h"
 +#include "encoding.h"
 +#include "debug.h"
 +#include "session.h"
  #include <errno.h>
  #ifndef _WIN32
 @@ -64,53 +66,19 @@  #ifndef _WIN32
  #include <unistd.h>
  #endif /* _WIN32 */
 -#if !defined(GG_CONFIG_MIRANDA) && defined(GG_CONFIG_HAVE_OPENSSL)
 +#if !defined(GG_CONFIG_MIRANDA)
 +#ifdef GG_CONFIG_HAVE_GNUTLS
 +#  include <gnutls/gnutls.h>
 +#endif
 +#ifdef GG_CONFIG_HAVE_OPENSSL
  #  include <openssl/err.h>
  #  include <openssl/rand.h>
  #endif
 +#endif
  extern SSL_API sslApi;
  /**
 - * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową
 - * składającą się ze stałych \c GG_DEBUG_...
 - *
 - * \ingroup debug
 - */
 -int gg_debug_level = 0;
 -
 -/**
 - * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
 - * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
 - * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr).
 - *
 - * \param level Poziom rejestracji
 - * \param format Format wiadomości (zgodny z \c printf)
 - * \param ap Lista argumentów (zgodna z \c printf)
 - *
 - * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session.
 - *
 - * \ingroup debug
 - */
 -void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
 -
 -/**
 - * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
 - * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
 - * \c NULL, informacje są wysyłane do standardowego wyjścia błędu.
 - *
 - * \param sess Sesja której dotyczy informacja lub \c NULL
 - * \param level Poziom rejestracji
 - * \param format Format wiadomości (zgodny z \c printf)
 - * \param ap Lista argumentów (zgodna z \c printf)
 - *
 - * \note Funkcja przesłania przez \c gg_debug_handler_session.
 - *
 - * \ingroup debug
 - */
 -void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL;
 -
 -/**
   * Port gniazda nasłuchującego dla połączeń bezpośrednich.
   * 
   * \ingroup ip
 @@ -180,7 +148,7 @@ static char rcsid[]  #ifdef __GNUC__
  __attribute__ ((unused))
  #endif
 -= "$Id: libgadu.c 13762 2011-08-09 12:35:16Z dezred $";
 += "$Id$";
  #endif
  #endif /* DOXYGEN */
 @@ -323,13 +291,35 @@ unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)   */
  int gg_read(struct gg_session *sess, char *buf, int length)
  {
 +	int res;
 +
  #ifdef GG_CONFIG_MIRANDA
  	if (sess->ssl != NULL)
  		return sslApi.read(sess->ssl, buf, length, 0);
 +#elif GG_CONFIG_HAVE_GNUTLS
 +	if (sess->ssl != NULL) {
 +		for (;;) {
 +			res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length);
 +
 +			if (res < 0) {
 +				if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
 +					continue;
 +
 +				if (res == GNUTLS_E_AGAIN)
 +					errno = EAGAIN;
 +				else
 +					errno = EINVAL;
 +
 +				return -1;
 +			}
 +
 +			return res;
 +		}
 +	}
  #elif GG_CONFIG_HAVE_OPENSSL
  	if (sess->ssl != NULL) {
  		for (;;) {
 -			int res, err;
 +			int err;
  			res = SSL_read(sess->ssl, buf, length);
 @@ -352,7 +342,14 @@ int gg_read(struct gg_session *sess, char *buf, int length)  	}
  #endif
 -	return gg_sock_read(sess->fd, buf, length);
 +	for (;;) {
 +		res = gg_sock_read(sess->fd, buf, length);
 +
 +		if (res == -1 && errno == EINTR)
 +			continue;
 +
 +		return res;
 +	}
  }
  /**
 @@ -373,13 +370,35 @@ int gg_read(struct gg_session *sess, char *buf, int length)   */
  static int gg_write_common(struct gg_session *sess, const char *buf, int length)
  {
 +	int res;
 +
  #ifdef GG_CONFIG_MIRANDA
  	if (sess->ssl != NULL)
  		return sslApi.write(sess->ssl, buf, length);
 +#elif GG_CONFIG_HAVE_GNUTLS
 +	if (sess->ssl != NULL) {
 +		for (;;) {
 +			res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length);
 +
 +			if (res < 0) {
 +				if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
 +					continue;
 +
 +				if (res == GNUTLS_E_AGAIN)
 +					errno = EAGAIN;
 +				else
 +					errno = EINVAL;
 +
 +				return -1;
 +			}
 +
 +			return res;
 +		}
 +	}
  #elif GG_CONFIG_HAVE_OPENSSL
  	if (sess->ssl != NULL) {
  		for (;;) {
 -			int res, err;
 +			int err;
  			res = SSL_write(sess->ssl, (void *)buf, length);
 @@ -402,7 +421,14 @@ static int gg_write_common(struct gg_session *sess, const char *buf, int length)  	}
  #endif
 -	return gg_sock_write(sess->fd, buf, length);
 +	for (;;) {
 +		res = gg_sock_write(sess->fd, buf, length);
 +
 +		if (res == -1 && errno == EINTR)
 +			continue;
 +
 +		return res;
 +	}
  }
 @@ -480,7 +506,7 @@ int gg_write(struct gg_session *sess, const char *buf, int length)  void *gg_recv_packet(struct gg_session *sess)
  {
  	struct gg_header h;
 -	char *buf = NULL;
 +	char *packet;
  	int ret = 0;
  	unsigned int offset, size = 0;
 @@ -533,7 +559,6 @@ void *gg_recv_packet(struct gg_session *sess)  			}
  			sess->header_done += ret;
 -
  		}
  		h.type = gg_fix32(h.type);
 @@ -552,26 +577,25 @@ void *gg_recv_packet(struct gg_session *sess)  		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n");
  		size = sess->recv_left;
  		offset = sess->recv_done;
 -		buf = sess->recv_buf;
  	} else {
 -		if (!(buf = (char*)malloc(sizeof(h) + h.length + 1))) {
 +		if (!(sess->recv_buf = (char*)malloc(sizeof(h) + h.length + 1))) {
  			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
  			return NULL;
  		}
 -		memcpy(buf, &h, sizeof(h));
 +		memcpy(sess->recv_buf, &h, sizeof(h));
  		offset = 0;
  		size = h.length;
  	}
  	while (size > 0) {
 -		ret = gg_read(sess, buf + sizeof(h) + offset, size);
 -		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret);
 +		ret = gg_read(sess, sess->recv_buf + sizeof(h) + offset, size);
 +		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, sess->recv_buf + sizeof(h) + offset, size, ret);
  		if (!ret) {
  			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
  			errno = ECONNRESET;
 -			return NULL;
 +			goto fail;
  		}
  		if (ret > -1 && ret <= (int)size) {
  			offset += ret;
 @@ -581,22 +605,30 @@ void *gg_recv_packet(struct gg_session *sess)  			if (errno == EAGAIN) {
  				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size);
 -				sess->recv_buf = buf;
  				sess->recv_left = size;
  				sess->recv_done = offset;
  				return NULL;
  			}
 -			free(buf);
 -			return NULL;
 +			goto fail;
  		}
  	}
 +	packet = sess->recv_buf;
 +	sess->recv_buf = NULL;
  	sess->recv_left = 0;
 -	gg_debug_dump_session(sess, buf, sizeof(h) + h.length, "// gg_recv_packet(0x%.2x)", h.type);
 +	gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(type=0x%.2x, length=%d)\n", h.type, h.length);
 +	gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(h) + h.length);
 +
 +	return packet;
 +
 +fail:
 +	free(sess->recv_buf);
 +	sess->recv_buf = NULL;
 +	sess->recv_left = 0;
 -	return buf;
 +	return NULL;
  }
  /**
 @@ -662,7 +694,8 @@ int gg_send_packet(struct gg_session *sess, int type, ...)  	h->type = gg_fix32(type);
  	h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
 -	gg_debug_dump_session(sess, tmp, tmp_length, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
 +	gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(type=0x%.2x, length=%d)\n", gg_fix32(h->type), gg_fix32(h->length));
 +	gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length);
  	res = gg_write(sess, tmp, tmp_length);
 @@ -833,7 +866,7 @@ struct gg_session *gg_login(const struct gg_login_params *p)  			max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0;
  		if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8)
 -			sess->initial_descr = gg_cp_to_utf8(p->status_descr);
 +			sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1);
  		else
  			sess->initial_descr = strdup(p->status_descr);
 @@ -852,7 +885,25 @@ struct gg_session *gg_login(const struct gg_login_params *p)  	sess->tls = p->tls;
  #endif
  	if (p->tls == 1) {
 -#ifdef GG_CONFIG_HAVE_OPENSSL
 +#ifdef GG_CONFIG_HAVE_GNUTLS
 +		gg_session_gnutls_t *tmp;
 +
 +		tmp = malloc(sizeof(gg_session_gnutls_t));
 +
 +		if (tmp == NULL) {
 +			gg_debug(GG_DEBUG_MISC, "// gg_login() out of memory for GnuTLS session\n");
 +			goto fail;
 +		}
 +
 +		sess->ssl = tmp;
 +
 +		gnutls_global_init();
 +		gnutls_certificate_allocate_credentials(&tmp->xcred);
 +		gnutls_init(&tmp->session, GNUTLS_CLIENT);
 +		gnutls_priority_set_direct(tmp->session, "NORMAL:-VERS-TLS", NULL);
 +//		gnutls_priority_set_direct(tmp->session, "NONE:+VERS-SSL3.0:+AES-128-CBC:+RSA:+SHA1:+COMP-NULL", NULL);
 +		gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred);
 +#elif defined(GG_CONFIG_HAVE_OPENSSL)
  		char buf[1024];
  		OpenSSL_add_ssl_algorithms();
 @@ -911,14 +962,22 @@ struct gg_session *gg_login(const struct gg_login_params *p)  		if (!sess->server_addr) {
  			if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
 -				if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 +				struct in_addr *addr_list = NULL;
 +				int addr_count;
 +
 +				if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 0) == -1 || addr_count == 0) {
  					gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname);
 +					free(addr_list);
  #ifdef GG_CONFIG_MIRANDA
  					errno = EACCES;
  					*gg_failno = GG_FAILURE_RESOLVING;
  #endif
  					goto fail;
  				}
 +
 +				addr = addr_list[0];
 +
 +				free(addr_list);
  			}
  		} else {
  			addr.s_addr = sess->server_addr;
 @@ -1067,6 +1126,9 @@ void gg_logoff(struct gg_session *sess)  #ifdef GG_CONFIG_MIRANDA
  	if (sess->ssl != NULL)
  		sslApi.shutdown(sess->ssl);
 +#elif GG_CONFIG_HAVE_GNUTLS
 +	if (sess->ssl != NULL)
 +		gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR);
  #elif GG_CONFIG_HAVE_OPENSSL
  	if (sess->ssl != NULL)
  		SSL_shutdown(sess->ssl);
 @@ -1080,6 +1142,18 @@ void gg_logoff(struct gg_session *sess)  		sess->fd = -1;
  	}
 +#ifdef GG_CONFIG_HAVE_GNUTLS
 +	if (sess->ssl != NULL) {
 +		gg_session_gnutls_t *tmp;
 +
 +		tmp = (gg_session_gnutls_t*) sess->ssl;
 +		gnutls_deinit(tmp->session);
 +		gnutls_certificate_free_credentials(tmp->xcred);
 +		gnutls_global_deinit();
 +		free(sess->ssl);
 +	}
 +#endif
 +
  	if (sess->send_buf) {
  		free(sess->send_buf);
  		sess->send_buf = NULL;
 @@ -1108,6 +1182,7 @@ void gg_free_session(struct gg_session *sess)  	free(sess->password);
  	free(sess->initial_descr);
  	free(sess->client_version);
 +	free(sess->recv_buf);
  	free(sess->header_buf);
  #ifdef GG_CONFIG_MIRANDA
 @@ -1181,7 +1256,7 @@ static int gg_change_status_common(struct gg_session *sess, int status, const ch  	if (sess->protocol_version >= 0x2d) {
  		if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
 -			new_descr = gg_cp_to_utf8(descr);
 +			new_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1);
  			if (!new_descr)
  				return -1;
 @@ -1246,6 +1321,12 @@ static int gg_change_status_common(struct gg_session *sess, int status, const ch  	}
  	free(new_descr);
 +
 +	if (GG_S_NA(status)) {
 +		sess->state = GG_STATE_DISCONNECTING;
 +		sess->timeout = GG_TIMEOUT_DISCONNECT;
 +	}
 +
  	return res;
  }
 @@ -1652,13 +1733,13 @@ int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int r  	}
  	if (sess->encoding == GG_ENCODING_UTF8) {
 -		if (!(cp_msg = gg_utf8_to_cp((const char *) message)))
 +		if (!(cp_msg = gg_encoding_convert((const char *)message, GG_ENCODING_UTF8, GG_ENCODING_CP1250, -1, -1)))
  			return -1;
  		utf_msg = (char*) message;
  	} else {
  		if (sess->protocol_version >= 0x2d) {
 -			if (!(utf_msg = gg_cp_to_utf8((const char *) message)))
 +			if (!(utf_msg = gg_encoding_convert((const char *)message, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1)))
  				return -1;
  		}
 diff --git a/protocols/Gadu-Gadu/src/libgadu/libgadu.h b/protocols/Gadu-Gadu/src/libgadu/libgadu.h index 33224836c3..44e8318b8d 100644 --- a/protocols/Gadu-Gadu/src/libgadu/libgadu.h +++ b/protocols/Gadu-Gadu/src/libgadu/libgadu.h @@ -1,1023 +1,1017 @@ -/* coding: UTF-8 */ -/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */ - -/* - *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> - *                          Robert J. Woźny <speedy@ziew.org> - *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org> - *                          Tomasz Chiliński <chilek@chilan.com> - *                          Piotr Wysocki <wysek@linux.bydg.org> - *                          Dawid Jarosz <dawjar@poczta.onet.pl> - *                          Jakub Zawadzki <darkjames@darkjames.ath.cx> - * - *  This program is free software; you can redistribute it and/or modify - *  it under the terms of the GNU Lesser General Public License Version - *  2.1 as published by the Free Software Foundation. - * - *  This program is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU Lesser General Public License for more details. - * - *  You should have received a copy of the GNU Lesser General Public - *  License along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - *  USA. - */ - -/** - * \file libgadu.h - * - * \brief Główny plik nagłówkowy biblioteki - */ - -#ifndef __GG_LIBGADU_H -#define __GG_LIBGADU_H - -/* Defined if libgadu should be compatible with Miranda. */ -#define GG_CONFIG_MIRANDA - -#ifdef GG_CONFIG_MIRANDA -#include <m_ssl.h> -#include <m_netlib.h> -#endif - -#if defined(__cplusplus) || defined(_WIN32) -#pragma pack(push, 1) -#endif - -#include <sys/types.h> -#include <stdio.h> -#include <stdarg.h> - -/** \cond ignore */ - -/* Defined if libgadu was compiled for bigendian machine. */ -#undef GG_CONFIG_BIGENDIAN - -/* Defined if this machine has gethostbyname_r(). */ -#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R - -/* Defined if libgadu was compiled and linked with pthread support. */ -#define GG_CONFIG_HAVE_PTHREAD - -/* Defined if pthread resolver is the default one. */ -#define GG_CONFIG_PTHREAD_DEFAULT  - -/* Defined if this machine has C99-compiliant vsnprintf(). */ -#undef GG_CONFIG_HAVE_C99_VSNPRINTF - -/* Defined if this machine has va_copy(). */ -#undef GG_CONFIG_HAVE_VA_COPY - -/* Defined if this machine has __va_copy(). */ -#undef GG_CONFIG_HAVE___VA_COPY - -/* Defined if this machine supports long long. */ -/* Visual C++ 6.0 has no long long */ -#if !defined(_MSC_VER) || (_MSC_VER >= 1300) -#define GG_CONFIG_HAVE_LONG_LONG -#endif - -/* Defined if libgadu was compiled and linked with OpenSSL support. */ -#undef GG_CONFIG_HAVE_OPENSSL - -/* Defined if uintX_t types are defined in <stdint.h>. */ -#undef GG_CONFIG_HAVE_STDINT_H - -/* Defined if uintX_t types are defined in <inttypes.h>. */ -#undef GG_CONFIG_HAVE_INTTYPES_H - -/* Defined if uintX_t types are defined in <sys/inttypes.h>. */ -#undef GG_CONFIG_HAVE_SYS_INTTYPES_H - -/* Defined if uintX_t types are defined in <sys/int_types.h>. */ -#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H - -/* Defined if uintX_t types are defined in <sys/types.h>. */ -#undef GG_CONFIG_HAVE_SYS_TYPES_H - -/* MSC have no va_copy */ -#ifndef _MSC_VER -#define GG_CONFIG_HAVE_VA_COPY -#define GG_CONFIG_HAVE___VA_COPY -#endif - +/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */
 +
 +/*
 + *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
 + *                          Robert J. Woźny <speedy@ziew.org>
 + *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
 + *                          Tomasz Chiliński <chilek@chilan.com>
 + *                          Piotr Wysocki <wysek@linux.bydg.org>
 + *                          Dawid Jarosz <dawjar@poczta.onet.pl>
 + *                          Jakub Zawadzki <darkjames@darkjames.ath.cx>
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU Lesser General Public License Version
 + *  2.1 as published by the Free Software Foundation.
 + *
 + *  This program is distributed in the hope that it will be useful,
 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + *  GNU Lesser General Public License for more details.
 + *
 + *  You should have received a copy of the GNU Lesser General Public
 + *  License along with this program; if not, write to the Free Software
 + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
 + *  USA.
 + */
 +
 +/**
 + * \file libgadu.h
 + *
 + * \brief Główny plik nagłówkowy biblioteki
 + */
 +
 +#ifndef __GG_LIBGADU_H
 +#define __GG_LIBGADU_H
 +
 +/* Defined if libgadu should be compatible with Miranda. */
 +#define GG_CONFIG_MIRANDA
 +
 +#ifdef GG_CONFIG_MIRANDA
 +#include <m_ssl.h>
 +#include <m_netlib.h>
 +#endif
 +
 +#if defined(__cplusplus) || defined(_WIN32)
 +#pragma pack(push, 1)
 +#endif
 +
 +#include <sys/types.h>
 +#include <stdio.h>
 +#include <stdarg.h>
 +
 +/** \cond ignore */
 +
 +/* Defined if libgadu was compiled for bigendian machine. */
 +#undef GG_CONFIG_BIGENDIAN
 +
 +/* Defined if this machine has gethostbyname_r(). */
 +#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
 +
 +/* Defined if libgadu was compiled and linked with pthread support. */
 +#define GG_CONFIG_HAVE_PTHREAD
 +
 +/* Defined if pthread resolver is the default one. */
 +#define GG_CONFIG_PTHREAD_DEFAULT 
 +
 +/* Defined if this machine has C99-compiliant vsnprintf(). */
 +#undef GG_CONFIG_HAVE_C99_VSNPRINTF
 +
 +/* Defined if this machine has va_copy(). */
 +#undef GG_CONFIG_HAVE_VA_COPY
 +
 +/* Defined if this machine has __va_copy(). */
 +#undef GG_CONFIG_HAVE___VA_COPY
 +
 +/* Defined if this machine supports long long. */
 +#define GG_CONFIG_HAVE_LONG_LONG
 +
 +/* Defined if libgadu was compiled and linked with GnuTLS support. */
 +#undef GG_CONFIG_HAVE_GNUTLS
 +
 +/* Defined if libgadu was compiled and linked with OpenSSL support. */
 +#undef GG_CONFIG_HAVE_OPENSSL
 +
 +/* Defined if uintX_t types are defined in <stdint.h>. */
 +#undef GG_CONFIG_HAVE_STDINT_H
 +
 +/* Defined if uintX_t types are defined in <inttypes.h>. */
 +#undef GG_CONFIG_HAVE_INTTYPES_H
 +
 +/* Defined if uintX_t types are defined in <sys/inttypes.h>. */
 +#undef GG_CONFIG_HAVE_SYS_INTTYPES_H
 +
 +/* Defined if uintX_t types are defined in <sys/int_types.h>. */
 +#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H
 +
 +/* Defined if uintX_t types are defined in <sys/types.h>. */
 +#undef GG_CONFIG_HAVE_SYS_TYPES_H
 +
 +/* MSC have no va_copy */
 +#ifndef _MSC_VER
 +#define GG_CONFIG_HAVE_VA_COPY
 +#define GG_CONFIG_HAVE___VA_COPY
 +#endif
 +
  #if defined(GG_CONFIG_HAVE_OPENSSL) && !defined(GG_CONFIG_MIRANDA)
 -#include <openssl/ssl.h> -#endif - -#ifdef GG_CONFIG_HAVE_STDINT_H -#include <stdint.h> -#else -#  ifdef GG_CONFIG_HAVE_INTTYPES_H -#  include <inttypes.h> -#  else -#    ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H -#    include <sys/inttypes.h> -#    else -#      ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H -#      include <sys/int_types.h> -#      else -#        ifdef GG_CONFIG_HAVE_SYS_TYPES_H -#        include <sys/types.h> -#        else - -#ifndef __AC_STDINT_H -#define __AC_STDINT_H - -/* ISO C 9X: 7.18 Integer types <stdint.h> */ - -typedef unsigned char   uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int   uint32_t; +#include <openssl/ssl.h>
 +#endif
 +
 +#ifdef GG_CONFIG_HAVE_STDINT_H
 +#include <stdint.h>
 +#else
 +#  ifdef GG_CONFIG_HAVE_INTTYPES_H
 +#  include <inttypes.h>
 +#  else
 +#    ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H
 +#    include <sys/inttypes.h>
 +#    else
 +#      ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H
 +#      include <sys/int_types.h>
 +#      else
 +#        ifdef GG_CONFIG_HAVE_SYS_TYPES_H
 +#        include <sys/types.h>
 +#        else
 +
 +#ifndef __AC_STDINT_H
 +#define __AC_STDINT_H
 +
 +/* ISO C 9X: 7.18 Integer types <stdint.h> */
 +
 +typedef unsigned char   uint8_t;
 +typedef unsigned short uint16_t;
 +typedef unsigned int   uint32_t;
  #ifdef GG_CONFIG_HAVE_LONG_LONG
  typedef unsigned long long   uint64_t;
  #define GG_CONFIG_HAVE_UINT64_T
 -#endif - -#ifndef __CYGWIN__ -#define __int8_t_defined -typedef   signed char    int8_t; -typedef   signed short  int16_t; -typedef   signed int    int32_t; -#endif - -#endif /* __AC_STDINT_H */ - -#        endif -#      endif -#    endif -#  endif -#endif - -#ifdef _WIN32 -#  define kill(pid,sig) -#  ifdef _MSC_VER -#    define vsnprintf	_vsnprintf -#    define stat		_stat -#    ifndef strdup -#      define strdup	_strdup -#    endif -#    define strncasecmp _strnicmp -#    define vsnprintf	_vsnprintf -#    define snprintf	_snprintf -#    define strcasecmp	_stricmp -#    define GG_CONFIG_HAVE_STRTOULL -#    define strtoull	_strtoui64 -#  endif -#  define gg_sock_write(sock,buf,len)	send(sock,(const char*)(buf),len,0) -#  define gg_sock_read(sock,buf,len)	recv(sock,(char*)(buf),len,0) -#  define gg_sock_close(sock)			closesocket(sock) -#  define gg_getsockopt(sock,level,name,val,len) getsockopt(sock,level,name,(char *)val,len) -#else -   typedef int				SOCKET; -#  define gg_sock_write		write -#  define gg_sock_read		read -#  define gg_sock_close		close -#  define gg_getsockopt		getsockopt -#endif - -/** \endcond */ - -/** - * Numer Gadu-Gadu. - */ -typedef uint32_t uin_t; - -/** - * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x. - */ -typedef struct { -	uint8_t id[8]; -} gg_dcc7_id_t; - +#endif
 +
 +#ifndef __CYGWIN__
 +#define __int8_t_defined
 +typedef   signed char    int8_t;
 +typedef   signed short  int16_t;
 +typedef   signed int    int32_t;
 +#endif
 +
 +#endif /* __AC_STDINT_H */
 +
 +#        endif
 +#      endif
 +#    endif
 +#  endif
 +#endif
 +
 +#ifdef _WIN32
 +#  define kill(pid,sig)
 +#  ifdef _MSC_VER
 +#    define vsnprintf	_vsnprintf
 +#    define stat		_stat
 +#    ifndef strdup
 +#      define strdup	_strdup
 +#    endif
 +#    define strncasecmp _strnicmp
 +#    define vsnprintf	_vsnprintf
 +#    define snprintf	_snprintf
 +#    define strcasecmp	_stricmp
 +#    define GG_CONFIG_HAVE_STRTOULL
 +#    define strtoull	_strtoui64
 +#  endif
 +#  define gg_sock_write(sock,buf,len)	send(sock,(const char*)(buf),len,0)
 +#  define gg_sock_read(sock,buf,len)	recv(sock,(char*)(buf),len,0)
 +#  define gg_sock_close(sock)			closesocket(sock)
 +#  define gg_getsockopt(sock,level,name,val,len) getsockopt(sock,level,name,(char *)val,len)
 +#else
 +   typedef int				SOCKET;
 +#  define gg_sock_write		write
 +#  define gg_sock_read		read
 +#  define gg_sock_close		close
 +#  define gg_getsockopt		getsockopt
 +#endif
 +
 +/** \endcond */
 +
 +/**
 + * Numer Gadu-Gadu.
 + */
 +typedef uint32_t uin_t;
 +
 +/**
 + * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
 + */
 +typedef struct {
 +	uint8_t id[8];
 +} gg_dcc7_id_t;
 +
  /**
   * Identyfikator sesji multilogowania.
   */
  typedef struct {
  	uint8_t id[8];
  } gg_multilogon_id_t;
 - -/** - * Makro deklarujące pola wspólne dla struktur sesji. - */ -#define gg_common_head(x) \ -	SOCKET fd;		/**< Obserwowany deskryptor */ \ -	int check;		/**< Informacja o żądaniu odczytu/zapisu (patrz \ref gg_check_t) */ \ -	int state;		/**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \ -	int error;		/**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \ -	int type;		/**< Rodzaj sesji (patrz \ref gg_session_t) */ \ -	int id;			/**< Identyfikator sesji */ \ -	int timeout;		/**< Czas pozostały do zakończenia stanu */ \ -	int (*callback)(x*); 	/**< Funkcja zwrotna */ \ -	void (*destroy)(x*); 	/**< Funkcja zwalniania zasobów */ - -/** - * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste - * rzutowanie niezależne od rodzaju połączenia. - */ -struct gg_common { -	gg_common_head(struct gg_common) -}; - -struct gg_image_queue; - -struct gg_dcc7; - +
 +/**
 + * Makro deklarujące pola wspólne dla struktur sesji.
 + */
 +#define gg_common_head(x) \
 +	SOCKET fd;		/**< Obserwowany deskryptor */ \
 +	int check;		/**< Informacja o żądaniu odczytu/zapisu (patrz \ref gg_check_t) */ \
 +	int state;		/**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \
 +	int error;		/**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \
 +	int type;		/**< Rodzaj sesji (patrz \ref gg_session_t) */ \
 +	int id;			/**< Identyfikator sesji */ \
 +	int timeout;		/**< Czas pozostały do zakończenia stanu */ \
 +	int (*callback)(x*); 	/**< Funkcja zwrotna */ \
 +	void (*destroy)(x*); 	/**< Funkcja zwalniania zasobów */
 +
 +/**
 + * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste
 + * rzutowanie niezależne od rodzaju połączenia.
 + */
 +struct gg_common {
 +	gg_common_head(struct gg_common)
 +};
 +
 +struct gg_image_queue;
 +
 +struct gg_dcc7;
 +
  struct gg_dcc7_relay;
 - -/** - * Sposób rozwiązywania nazw serwerów. - */ -typedef enum { -	GG_RESOLVER_DEFAULT = 0,	/**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */ -	GG_RESOLVER_FORK,		/**< Rozwiązywanie nazw bazujące na procesach */ -	GG_RESOLVER_PTHREAD,		/**< Rozwiązywanie nazw bazujące na wątkach */ -	GG_RESOLVER_CUSTOM,		/**< Funkcje rozwiązywania nazw dostarczone przed aplikację */ -	GG_RESOLVER_INVALID = -1	/**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */ -} gg_resolver_t; - -/** - * Rodzaj kodowania znaków. - */ -typedef enum { -	GG_ENCODING_CP1250 = 0,		/**< Kodowanie CP1250 */ -	GG_ENCODING_UTF8,		/**< Kodowanie UTF-8 */ -	GG_ENCODING_INVALID = -1	/**< Nieprawidłowe kodowanie */ -} gg_encoding_t; - -/** - * Sesja Gadu-Gadu. - * - * Tworzona przez funkcję \c gg_login(), zwalniana przez \c gg_free_session(). - * - * \ingroup login - */ -struct gg_session { -	gg_common_head(struct gg_session) - -	int async;      	/**< Flaga połączenia asynchronicznego */ -	int pid;        	/**< Numer procesu rozwiązującego nazwę serwera */ -	int port;       	/**< Port serwera */ -	int seq;        	/**< Numer sekwencyjny ostatniej wiadomości */ -	int last_pong;  	/**< Czas otrzymania ostatniej ramki utrzymaniowej */ -	int last_event;		/**< Czas otrzymania ostatniego pakietu */ - -	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */ - -	uint32_t proxy_addr;	/**< Adres serwera pośredniczącego */ -	uint16_t proxy_port;	/**< Port serwera pośredniczącego */ - -	uint32_t hub_addr;	/**< Adres huba po rozwiązaniu nazwy */ -	uint32_t server_addr;	/**< Adres serwera otrzymany od huba */ - -	uint32_t client_addr;	/**< Adres gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ -	uint16_t client_port;	/**< Port gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ - -	uint32_t external_addr;	/**< Publiczny adres dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ -	uint16_t external_port;	/**< Publiczny port dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ - -	uin_t uin;		/**< Własny numer Gadu-Gadu */ -	char *password;		/**< Hasło (zwalniane po użyciu) */ - -	int initial_status;	/**< Początkowy status */ -	int status;		/**< Aktualny status */ - -	char *recv_buf;		/**< Bufor na odbierany pakiety */ -	int recv_done;		/**< Liczba wczytanych bajtów pakietu */ -	int recv_left;		/**< Liczba pozostałych do wczytania bajtów pakietu */ - -	int protocol_version;	/**< Wersja protokołu (bez flag) */ -	char *client_version;	/**< Wersja klienta */ -	int last_sysmsg;	/**< Numer ostatniej wiadomości systemowej */ - -	char *initial_descr;	/**< Początkowy opis statusu */ - -	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */ - -	char *header_buf;	/**< Bufor na początek nagłówka pakietu */ -	unsigned int header_done;	/**< Liczba wczytanych bajtów nagłówka pakietu */ - -#ifdef GG_CONFIG_MIRANDA -	HSSL ssl; -	int tls;			/**< Flaga połączenia szyfrowanego */ -#elif GG_CONFIG_HAVE_OPENSSL -	SSL *ssl;		/**< Struktura TLS */ -	SSL_CTX *ssl_ctx;	/**< Kontekst sesji TLS */ -#else -	void *ssl;		/**< Struktura TLS */ -	void *ssl_ctx;		/**< Kontekst sesji TLS */ -#endif - -	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */ - -	char *userlist_reply;	/**< Bufor z odbieraną listą kontaktów */ - -	int userlist_blocks;	/**< Liczba części listy kontaktów */ - -	struct gg_image_queue *images;	/**< Lista wczytywanych obrazków */ - -	int hash_type;		/**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */ - -	char *send_buf;		/**< Bufor z danymi do wysłania */ -	int send_left;		/**< Liczba bajtów do wysłania */ - -	struct gg_dcc7 *dcc7_list;	/**< Lista połączeń bezpośrednich skojarzonych z sesją */ -	 -	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */ - -	int protocol_flags;	/**< Flagi protokołu */ - -	gg_encoding_t encoding;		/**< Rodzaj kodowania znaków */ - -	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */ -	int (*resolver_start)(SOCKET *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */ -	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */ - -	int protocol_features;	/**< Opcje protokołu */ -	int status_flags;	/**< Flagi statusu */ -}; - -/** - * Połączenie HTTP. - * - * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free(). - * - * \ingroup http - */ -struct gg_http { -	gg_common_head(struct gg_http) - -	int async;              /**< Flaga połączenia asynchronicznego */ -	int pid;                /**< Identyfikator procesu rozwiązującego nazwę serwera */ -	int port;               /**< Port */ - -	char *query;            /**< Zapytanie HTTP */ -	char *header;           /**< Odebrany nagłówek */ -	int header_size;        /**< Rozmiar wczytanego nagłówka */ -	char *body;             /**< Odebrana strona */ -	unsigned int body_size; /**< Rozmiar strony */ - -	void *data;             /**< Dane prywatne usługi HTTP */ - -	char *user_data;	/**< Dane prywatne użytkownika (nie są zwalniane) */ - -	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę */ - -	unsigned int body_done;	/**< Liczba odebranych bajtów strony */ - -	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */ -	int (*resolver_start)(SOCKET *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */ -	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */ -}; - -/** \cond ignore */ - -#ifdef __GNUC__ -#define GG_PACKED __attribute__ ((packed)) -#ifndef GG_IGNORE_DEPRECATED -#define GG_DEPRECATED __attribute__ ((deprecated)) -#else -#define GG_DEPRECATED -#endif -#else -#define GG_PACKED -#define GG_DEPRECATED -#endif - -/** \endcond */ - -#define GG_MAX_PATH 276		/**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */ - -/** - * Odpowiednik struktury WIN32_FIND_DATA z API WIN32. - * - * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x. - */ -struct gg_file_info { -	uint32_t mode;			/**< dwFileAttributes */ -	uint32_t ctime[2];		/**< ftCreationTime */ -	uint32_t atime[2];		/**< ftLastAccessTime */ -	uint32_t mtime[2];		/**< ftLastWriteTime */ -	uint32_t size_hi;		/**< nFileSizeHigh */ -	uint32_t size;			/**< nFileSizeLow */ -	uint32_t reserved0;		/**< dwReserved0 */ -	uint32_t reserved1;		/**< dwReserved1 */ -	unsigned char filename[GG_MAX_PATH - 14];	/**< cFileName */ -	unsigned char short_filename[14];		/**< cAlternateFileName */ -} /** \cond ignore */ GG_PACKED /** \endcond */; - -/** - * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x. - * - * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(), - * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez - * \c gg_dcc_free(). - * - * \ingroup dcc6 - */ -struct gg_dcc { -	gg_common_head(struct gg_dcc) - -	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */ - -	int active;		/**< Flaga połączenia aktywnego (nieużywana) */ -	int port;		/**< Port gniazda nasłuchującego */ -	uin_t uin;		/**< Własny numer Gadu-Gadu */ -	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */ -	int file_fd;		/**< deskryptor pliku */ -	unsigned int offset;	/**< Położenie w pliku */ -	unsigned int chunk_size; -				/**< Rozmiar kawałka pliku */ -	unsigned int chunk_offset; -				/**< Położenie w aktualnym kawałku pliku */ -	struct gg_file_info file_info; -				/**< Informacje o pliku */ -	int established;	/**< Flaga ustanowienia połączenia */ -	char *voice_buf;	/**< Bufor na pakiet połączenia głosowego */ -	int incoming;		/**< Flaga połączenia przychodzącego */ -	char *chunk_buf;	/**< Bufor na fragment danych */ -	uint32_t remote_addr;	/**< Adres drugiej strony */ -	uint16_t remote_port;	/**< Port drugiej strony */ - -#ifdef GG_CONFIG_MIRANDA -	void *contact; -	char *folder; -	uint32_t tick; -#endif -}; - -#define GG_DCC7_HASH_LEN	20	/**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */ -#define GG_DCC7_FILENAME_LEN	255	/**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */ +
 +/**
 + * Sposób rozwiązywania nazw serwerów.
 + */
 +typedef enum {
 +	GG_RESOLVER_DEFAULT = 0,	/**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */
 +	GG_RESOLVER_FORK,		/**< Rozwiązywanie nazw bazujące na procesach */
 +	GG_RESOLVER_PTHREAD,		/**< Rozwiązywanie nazw bazujące na wątkach */
 +	GG_RESOLVER_CUSTOM,		/**< Funkcje rozwiązywania nazw dostarczone przed aplikację */
 +	GG_RESOLVER_INVALID = -1	/**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */
 +} gg_resolver_t;
 +
 +/**
 + * Rodzaj kodowania znaków.
 + */
 +typedef enum {
 +	GG_ENCODING_CP1250 = 0,		/**< Kodowanie CP1250 */
 +	GG_ENCODING_UTF8,		/**< Kodowanie UTF-8 */
 +	GG_ENCODING_INVALID = -1	/**< Nieprawidłowe kodowanie */
 +} gg_encoding_t;
 +
 +/**
 + * Sesja Gadu-Gadu.
 + *
 + * Tworzona przez funkcję \c gg_login(), zwalniana przez \c gg_free_session().
 + *
 + * \ingroup login
 + */
 +struct gg_session {
 +	gg_common_head(struct gg_session)
 +
 +	int async;      	/**< Flaga połączenia asynchronicznego */
 +	int pid;        	/**< Numer procesu rozwiązującego nazwę serwera */
 +	int port;       	/**< Port serwera */
 +	int seq;        	/**< Numer sekwencyjny ostatniej wiadomości */
 +	int last_pong;  	/**< Czas otrzymania ostatniej ramki utrzymaniowej */
 +	int last_event;		/**< Czas otrzymania ostatniego pakietu */
 +
 +	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */
 +
 +	uint32_t proxy_addr;	/**< Adres serwera pośredniczącego */
 +	uint16_t proxy_port;	/**< Port serwera pośredniczącego */
 +
 +	uint32_t hub_addr;	/**< Adres huba po rozwiązaniu nazwy */
 +	uint32_t server_addr;	/**< Adres serwera otrzymany od huba */
 +
 +	uint32_t client_addr;	/**< Adres gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 +	uint16_t client_port;	/**< Port gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 +
 +	uint32_t external_addr;	/**< Publiczny adres dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 +	uint16_t external_port;	/**< Publiczny port dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 +
 +	uin_t uin;		/**< Własny numer Gadu-Gadu */
 +	char *password;		/**< Hasło (zwalniane po użyciu) */
 +
 +	int initial_status;	/**< Początkowy status */
 +	int status;		/**< Aktualny status */
 +
 +	char *recv_buf;		/**< Bufor na odbierany pakiety */
 +	int recv_done;		/**< Liczba wczytanych bajtów pakietu */
 +	int recv_left;		/**< Liczba pozostałych do wczytania bajtów pakietu */
 +
 +	int protocol_version;	/**< Wersja protokołu (bez flag) */
 +	char *client_version;	/**< Wersja klienta */
 +	int last_sysmsg;	/**< Numer ostatniej wiadomości systemowej */
 +
 +	char *initial_descr;	/**< Początkowy opis statusu */
 +
 +	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
 +
 +	char *header_buf;	/**< Bufor na początek nagłówka pakietu */
 +	unsigned int header_done;	/**< Liczba wczytanych bajtów nagłówka pakietu */
 +
 +#ifdef GG_CONFIG_MIRANDA
 +	HSSL ssl;
 +	int tls;			/**< Flaga połączenia szyfrowanego */
 +#elif GG_CONFIG_HAVE_OPENSSL
 +	SSL *ssl;		/**< Struktura TLS */
 +	SSL_CTX *ssl_ctx;	/**< Kontekst sesji TLS */
 +#else
 +	void *ssl;		/**< Struktura TLS */
 +	void *ssl_ctx;		/**< Kontekst sesji TLS */
 +#endif
 +
 +	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
 +
 +	char *userlist_reply;	/**< Bufor z odbieraną listą kontaktów */
 +
 +	int userlist_blocks;	/**< Liczba części listy kontaktów */
 +
 +	struct gg_image_queue *images;	/**< Lista wczytywanych obrazków */
 +
 +	int hash_type;		/**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
 +
 +	char *send_buf;		/**< Bufor z danymi do wysłania */
 +	int send_left;		/**< Liczba bajtów do wysłania */
 +
 +	struct gg_dcc7 *dcc7_list;	/**< Lista połączeń bezpośrednich skojarzonych z sesją */
 +	
 +	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */
 +
 +	int protocol_flags;	/**< Flagi protokołu */
 +
 +	gg_encoding_t encoding;		/**< Rodzaj kodowania znaków */
 +
 +	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */
 +	int (*resolver_start)(SOCKET *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */
 +	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
 +
 +	int protocol_features;	/**< Opcje protokołu */
 +	int status_flags;	/**< Flagi statusu */
 +	int recv_msg_count;	/**< Liczba odebranych wiadomości */
 +};
 +
 +/**
 + * Połączenie HTTP.
 + *
 + * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free().
 + *
 + * \ingroup http
 + */
 +struct gg_http {
 +	gg_common_head(struct gg_http)
 +
 +	int async;              /**< Flaga połączenia asynchronicznego */
 +	int pid;                /**< Identyfikator procesu rozwiązującego nazwę serwera */
 +	int port;               /**< Port */
 +
 +	char *query;            /**< Zapytanie HTTP */
 +	char *header;           /**< Odebrany nagłówek */
 +	int header_size;        /**< Rozmiar wczytanego nagłówka */
 +	char *body;             /**< Odebrana strona */
 +	unsigned int body_size; /**< Rozmiar strony */
 +
 +	void *data;             /**< Dane prywatne usługi HTTP */
 +
 +	char *user_data;	/**< Dane prywatne użytkownika (nie są zwalniane) */
 +
 +	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę */
 +
 +	unsigned int body_done;	/**< Liczba odebranych bajtów strony */
 +
 +	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */
 +	int (*resolver_start)(SOCKET *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */
 +	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
 +};
 +
 +/** \cond ignore */
 +
 +#ifdef __GNUC__
 +#define GG_PACKED __attribute__ ((packed))
 +#ifndef GG_IGNORE_DEPRECATED
 +#define GG_DEPRECATED __attribute__ ((deprecated))
 +#else
 +#define GG_DEPRECATED
 +#endif
 +#else
 +#define GG_PACKED
 +#define GG_DEPRECATED
 +#endif
 +
 +/** \endcond */
 +
 +#define GG_MAX_PATH 276		/**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
 +
 +/**
 + * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
 + *
 + * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x.
 + */
 +struct gg_file_info {
 +	uint32_t mode;			/**< dwFileAttributes */
 +	uint32_t ctime[2];		/**< ftCreationTime */
 +	uint32_t atime[2];		/**< ftLastAccessTime */
 +	uint32_t mtime[2];		/**< ftLastWriteTime */
 +	uint32_t size_hi;		/**< nFileSizeHigh */
 +	uint32_t size;			/**< nFileSizeLow */
 +	uint32_t reserved0;		/**< dwReserved0 */
 +	uint32_t reserved1;		/**< dwReserved1 */
 +	unsigned char filename[GG_MAX_PATH - 14];	/**< cFileName */
 +	unsigned char short_filename[14];		/**< cAlternateFileName */
 +} /** \cond ignore */ GG_PACKED /** \endcond */;
 +
 +/**
 + * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
 + *
 + * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(),
 + * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez
 + * \c gg_dcc_free().
 + *
 + * \ingroup dcc6
 + */
 +struct gg_dcc {
 +	gg_common_head(struct gg_dcc)
 +
 +	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */
 +
 +	int active;		/**< Flaga połączenia aktywnego (nieużywana) */
 +	int port;		/**< Port gniazda nasłuchującego */
 +	uin_t uin;		/**< Własny numer Gadu-Gadu */
 +	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */
 +	int file_fd;		/**< deskryptor pliku */
 +	unsigned int offset;	/**< Położenie w pliku */
 +	unsigned int chunk_size;
 +				/**< Rozmiar kawałka pliku */
 +	unsigned int chunk_offset;
 +				/**< Położenie w aktualnym kawałku pliku */
 +	struct gg_file_info file_info;
 +				/**< Informacje o pliku */
 +	int established;	/**< Flaga ustanowienia połączenia */
 +	char *voice_buf;	/**< Bufor na pakiet połączenia głosowego */
 +	int incoming;		/**< Flaga połączenia przychodzącego */
 +	char *chunk_buf;	/**< Bufor na fragment danych */
 +	uint32_t remote_addr;	/**< Adres drugiej strony */
 +	uint16_t remote_port;	/**< Port drugiej strony */
 +
 +#ifdef GG_CONFIG_MIRANDA
 +	void *contact;
 +	char *folder;
 +	uint32_t tick;
 +#endif
 +};
 +
 +#define GG_DCC7_HASH_LEN	20	/**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */
 +#define GG_DCC7_FILENAME_LEN	255	/**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */
  #define GG_DCC7_INFO_LEN	32	/**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */
  #define GG_DCC7_INFO_HASH_LEN	32	/**< Maksymalny rozmiar skrótu ip informacji o połączeniach bezpośrednich */
 - -/** - * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x. - * - * \ingroup dcc7 - */ -struct gg_dcc7 { -	gg_common_head(struct gg_dcc7) - -	gg_dcc7_id_t cid;	/**< Identyfikator połączenia */ - -	struct gg_event *event;	/**< Struktura zdarzenia */ - -	uin_t uin;		/**< Własny numer Gadu-Gadu */ -	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */ - -	int file_fd;		/**< Deskryptor przesyłanego pliku */ -	unsigned int offset;	/**< Aktualne położenie w przesyłanym pliku */ -	unsigned int size;	/**< Rozmiar przesyłanego pliku */ -	unsigned char filename[GG_DCC7_FILENAME_LEN + 1]; -				/**< Nazwa przesyłanego pliku */ -	unsigned char hash[GG_DCC7_HASH_LEN]; -				/**< Skrót SHA1 przesyłanego pliku */ - -	int dcc_type;		/**< Rodzaj połączenia bezpośredniego */ -	int established;	/**< Flaga ustanowienia połączenia */ -	int incoming;		/**< Flaga połączenia przychodzącego */ -	int reverse;		/**< Flaga połączenia zwrotnego */ - -	uint32_t local_addr;	/**< Adres lokalny */ -	uint16_t local_port;	/**< Port lokalny */ - -	uint32_t remote_addr;	/**< Adres drugiej strony */ -	uint16_t remote_port;	/**< Port drugiej strony */ - -	struct gg_session *sess; -				/**< Sesja do której przypisano połączenie */ -	struct gg_dcc7 *next;	/**< Następne połączenie w liście */ - -	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */ -	int seek;		/**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */ - +
 +/**
 + * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
 + *
 + * \ingroup dcc7
 + */
 +struct gg_dcc7 {
 +	gg_common_head(struct gg_dcc7)
 +
 +	gg_dcc7_id_t cid;	/**< Identyfikator połączenia */
 +
 +	struct gg_event *event;	/**< Struktura zdarzenia */
 +
 +	uin_t uin;		/**< Własny numer Gadu-Gadu */
 +	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */
 +
 +	int file_fd;		/**< Deskryptor przesyłanego pliku */
 +	unsigned int offset;	/**< Aktualne położenie w przesyłanym pliku */
 +	unsigned int size;	/**< Rozmiar przesyłanego pliku */
 +	unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
 +				/**< Nazwa przesyłanego pliku */
 +	unsigned char hash[GG_DCC7_HASH_LEN];
 +				/**< Skrót SHA1 przesyłanego pliku */
 +
 +	int dcc_type;		/**< Rodzaj połączenia bezpośredniego */
 +	int established;	/**< Flaga ustanowienia połączenia */
 +	int incoming;		/**< Flaga połączenia przychodzącego */
 +	int reverse;		/**< Flaga połączenia zwrotnego */
 +
 +	uint32_t local_addr;	/**< Adres lokalny */
 +	uint16_t local_port;	/**< Port lokalny */
 +
 +	uint32_t remote_addr;	/**< Adres drugiej strony */
 +	uint16_t remote_port;	/**< Port drugiej strony */
 +
 +	struct gg_session *sess;
 +				/**< Sesja do której przypisano połączenie */
 +	struct gg_dcc7 *next;	/**< Następne połączenie w liście */
 +
 +	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */
 +	int seek;		/**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */
 +
  	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
  	int relay;		/**< Flaga mówiąca, że laczymy sie przez serwer */
  	int relay_index;	/**< Numer serwera pośredniczącego, do którego się łączymy */
  	int relay_count;	/**< Rozmiar listy serwerów pośredniczących */
  	struct gg_dcc7_relay *relay_list;	/**< Lista serwerów pośredniczących */
 - -#ifdef GG_CONFIG_MIRANDA -	void *contact; -	char *folder; -	uint32_t tick; -#endif -}; - -/** - * Rodzaj sesji. - */ -enum gg_session_t { -	GG_SESSION_GG = 1,	/**< Połączenie z serwerem Gadu-Gadu */ -	GG_SESSION_HTTP,	/**< Połączenie HTTP */ -	GG_SESSION_SEARCH,	/**< Wyszukiwanie w katalogu publicznym (nieaktualne) */ -	GG_SESSION_REGISTER,	/**< Rejestracja nowego konta */ -	GG_SESSION_REMIND,	/**< Przypominanie hasła */ -	GG_SESSION_PASSWD,	/**< Zmiana hasła */ -	GG_SESSION_CHANGE,	/**< Zmiana informacji w katalogu publicznym (nieaktualne) */ -	GG_SESSION_DCC,		/**< Połączenie bezpośrednie (do wersji 6.x) */ -	GG_SESSION_DCC_SOCKET,	/**< Gniazdo nasłuchujące (do wersji 6.x) */ -	GG_SESSION_DCC_SEND,	/**< Wysyłanie pliku (do wersji 6.x) */ -	GG_SESSION_DCC_GET,	/**< Odbieranie pliku (do wersji 6.x) */ -	GG_SESSION_DCC_VOICE,	/**< Rozmowa głosowa (do wersji 6.x) */ -	GG_SESSION_USERLIST_GET,	/**< Import listy kontaktów z serwera (nieaktualne) */ -	GG_SESSION_USERLIST_PUT,	/**< Eksport listy kontaktów do serwera (nieaktualne) */ -	GG_SESSION_UNREGISTER,	/**< Usuwanie konta */ -	GG_SESSION_USERLIST_REMOVE,	/**< Usuwanie listy kontaktów z serwera (nieaktualne) */ -	GG_SESSION_TOKEN,	/**< Pobieranie tokenu */ -	GG_SESSION_DCC7_SOCKET,	/**< Gniazdo nasłuchujące (od wersji 7.x) */ -	GG_SESSION_DCC7_SEND,	/**< Wysyłanie pliku (od wersji 7.x) */ -	GG_SESSION_DCC7_GET,	/**< Odbieranie pliku (od wersji 7.x) */ -	GG_SESSION_DCC7_VOICE,	/**< Rozmowa głosowa (od wersji 7.x) */ - -	GG_SESSION_USER0 = 256,	/**< Rodzaj zadeklarowany dla użytkownika */ -	GG_SESSION_USER1,	/**< Rodzaj zadeklarowany dla użytkownika */ -	GG_SESSION_USER2,	/**< Rodzaj zadeklarowany dla użytkownika */ -	GG_SESSION_USER3,	/**< Rodzaj zadeklarowany dla użytkownika */ -	GG_SESSION_USER4,	/**< Rodzaj zadeklarowany dla użytkownika */ -	GG_SESSION_USER5,	/**< Rodzaj zadeklarowany dla użytkownika */ -	GG_SESSION_USER6,	/**< Rodzaj zadeklarowany dla użytkownika */ -	GG_SESSION_USER7	/**< Rodzaj zadeklarowany dla użytkownika */ -}; - -/** - * Aktualny stan sesji. - */ -enum gg_state_t { -	/* wspólne */ -	GG_STATE_IDLE = 0,		/**< Nie dzieje się nic */ -	GG_STATE_RESOLVING,             /**< Oczekiwanie na rozwiązanie nazwy serwera */ -	GG_STATE_CONNECTING,            /**< Oczekiwanie na połączenie */ -	GG_STATE_READING_DATA,		/**< Oczekiwanie na dane */ -	GG_STATE_ERROR,			/**< Kod błędu w polu \c error */ - -	/* gg_session */ -	GG_STATE_CONNECTING_HUB,	/**< Oczekiwanie na połączenie z hubem */ -	GG_STATE_CONNECTING_GG,         /**< Oczekiwanie na połączenie z serwerem */ -	GG_STATE_READING_KEY,           /**< Oczekiwanie na klucz */ -	GG_STATE_READING_REPLY,         /**< Oczekiwanie na odpowiedź serwera */ -	GG_STATE_CONNECTED,             /**< Połączono z serwerem */ - -	/* gg_http */ -	GG_STATE_SENDING_QUERY,		/**< Wysłano zapytanie HTTP */ -	GG_STATE_READING_HEADER,	/**< Oczekiwanie na nagłówek HTTP */ -	GG_STATE_PARSING,               /**< Przetwarzanie danych */ -	GG_STATE_DONE,                  /**< Połączenie zakończone */ - -	/* gg_dcc */ -	GG_STATE_LISTENING,		/* czeka na połączenia */ -	GG_STATE_READING_UIN_1,		/* czeka na uin peera */ -	GG_STATE_READING_UIN_2,		/* czeka na swój uin */ -	GG_STATE_SENDING_ACK,		/* wysyła potwierdzenie dcc */ -	GG_STATE_READING_ACK,		/* czeka na potwierdzenie dcc */ -	GG_STATE_READING_REQUEST,	/* czeka na komendę */ -	GG_STATE_SENDING_REQUEST,	/* wysyła komendę */ -	GG_STATE_SENDING_FILE_INFO,	/* wysyła informacje o pliku */ -	GG_STATE_READING_PRE_FILE_INFO,	/* czeka na pakiet przed file_info */ -	GG_STATE_READING_FILE_INFO,	/* czeka na informacje o pliku */ -	GG_STATE_SENDING_FILE_ACK,	/* wysyła potwierdzenie pliku */ -	GG_STATE_READING_FILE_ACK,	/* czeka na potwierdzenie pliku */ -	GG_STATE_SENDING_FILE_HEADER,	/* wysyła nagłówek pliku */ -	GG_STATE_READING_FILE_HEADER,	/* czeka na nagłówek */ -	GG_STATE_GETTING_FILE,		/* odbiera plik */ -	GG_STATE_SENDING_FILE,		/* wysyła plik */ -	GG_STATE_READING_VOICE_ACK,	/* czeka na potwierdzenie voip */ -	GG_STATE_READING_VOICE_HEADER,	/* czeka na rodzaj bloku voip */ -	GG_STATE_READING_VOICE_SIZE,	/* czeka na rozmiar bloku voip */ -	GG_STATE_READING_VOICE_DATA,	/* czeka na dane voip */ -	GG_STATE_SENDING_VOICE_ACK,	/* wysyła potwierdzenie voip */ -	GG_STATE_SENDING_VOICE_REQUEST,	/* wysyła żądanie voip */ -	GG_STATE_READING_TYPE,		/* czeka na typ połączenia */ - -	/* nowe. bez sensu jest to API. */ -	GG_STATE_TLS_NEGOTIATION,	/**< Negocjacja połączenia szyfrowanego */ - -	GG_STATE_REQUESTING_ID,		/**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */ -	GG_STATE_WAITING_FOR_ACCEPT,	/**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */ -	GG_STATE_WAITING_FOR_INFO,	/**< Oczekiwanie na informacje o połączeniu bezpośrednim */ - -	GG_STATE_READING_ID,		/**< Odebranie identyfikatora połączenia bezpośredniego */ -	GG_STATE_SENDING_ID,		/**< Wysłano identyfikator połączenia bezpośredniego */ -	GG_STATE_RESOLVING_GG,		/**< Oczekiwanie na rozwiązanie nazwy serwera Gadu-Gadu */ - +
 +#ifdef GG_CONFIG_MIRANDA
 +	void *contact;
 +	char *folder;
 +	uint32_t tick;
 +#endif
 +};
 +
 +/**
 + * Rodzaj sesji.
 + */
 +enum gg_session_t {
 +	GG_SESSION_GG = 1,	/**< Połączenie z serwerem Gadu-Gadu */
 +	GG_SESSION_HTTP,	/**< Połączenie HTTP */
 +	GG_SESSION_SEARCH,	/**< Wyszukiwanie w katalogu publicznym (nieaktualne) */
 +	GG_SESSION_REGISTER,	/**< Rejestracja nowego konta */
 +	GG_SESSION_REMIND,	/**< Przypominanie hasła */
 +	GG_SESSION_PASSWD,	/**< Zmiana hasła */
 +	GG_SESSION_CHANGE,	/**< Zmiana informacji w katalogu publicznym (nieaktualne) */
 +	GG_SESSION_DCC,		/**< Połączenie bezpośrednie (do wersji 6.x) */
 +	GG_SESSION_DCC_SOCKET,	/**< Gniazdo nasłuchujące (do wersji 6.x) */
 +	GG_SESSION_DCC_SEND,	/**< Wysyłanie pliku (do wersji 6.x) */
 +	GG_SESSION_DCC_GET,	/**< Odbieranie pliku (do wersji 6.x) */
 +	GG_SESSION_DCC_VOICE,	/**< Rozmowa głosowa (do wersji 6.x) */
 +	GG_SESSION_USERLIST_GET,	/**< Import listy kontaktów z serwera (nieaktualne) */
 +	GG_SESSION_USERLIST_PUT,	/**< Eksport listy kontaktów do serwera (nieaktualne) */
 +	GG_SESSION_UNREGISTER,	/**< Usuwanie konta */
 +	GG_SESSION_USERLIST_REMOVE,	/**< Usuwanie listy kontaktów z serwera (nieaktualne) */
 +	GG_SESSION_TOKEN,	/**< Pobieranie tokenu */
 +	GG_SESSION_DCC7_SOCKET,	/**< Gniazdo nasłuchujące (od wersji 7.x) */
 +	GG_SESSION_DCC7_SEND,	/**< Wysyłanie pliku (od wersji 7.x) */
 +	GG_SESSION_DCC7_GET,	/**< Odbieranie pliku (od wersji 7.x) */
 +	GG_SESSION_DCC7_VOICE,	/**< Rozmowa głosowa (od wersji 7.x) */
 +
 +	GG_SESSION_USER0 = 256,	/**< Rodzaj zadeklarowany dla użytkownika */
 +	GG_SESSION_USER1,	/**< Rodzaj zadeklarowany dla użytkownika */
 +	GG_SESSION_USER2,	/**< Rodzaj zadeklarowany dla użytkownika */
 +	GG_SESSION_USER3,	/**< Rodzaj zadeklarowany dla użytkownika */
 +	GG_SESSION_USER4,	/**< Rodzaj zadeklarowany dla użytkownika */
 +	GG_SESSION_USER5,	/**< Rodzaj zadeklarowany dla użytkownika */
 +	GG_SESSION_USER6,	/**< Rodzaj zadeklarowany dla użytkownika */
 +	GG_SESSION_USER7	/**< Rodzaj zadeklarowany dla użytkownika */
 +};
 +
 +/**
 + * Aktualny stan sesji.
 + */
 +enum gg_state_t {
 +	/* wspólne */
 +	GG_STATE_IDLE = 0,		/**< Nie dzieje się nic */
 +	GG_STATE_RESOLVING,             /**< Oczekiwanie na rozwiązanie nazwy serwera */
 +	GG_STATE_CONNECTING,            /**< Oczekiwanie na połączenie */
 +	GG_STATE_READING_DATA,		/**< Oczekiwanie na dane */
 +	GG_STATE_ERROR,			/**< Kod błędu w polu \c error */
 +
 +	/* gg_session */
 +	GG_STATE_CONNECTING_HUB,	/**< Oczekiwanie na połączenie z hubem */
 +	GG_STATE_CONNECTING_GG,         /**< Oczekiwanie na połączenie z serwerem */
 +	GG_STATE_READING_KEY,           /**< Oczekiwanie na klucz */
 +	GG_STATE_READING_REPLY,         /**< Oczekiwanie na odpowiedź serwera */
 +	GG_STATE_CONNECTED,             /**< Połączono z serwerem */
 +
 +	/* gg_http */
 +	GG_STATE_SENDING_QUERY,		/**< Wysłano zapytanie HTTP */
 +	GG_STATE_READING_HEADER,	/**< Oczekiwanie na nagłówek HTTP */
 +	GG_STATE_PARSING,               /**< Przetwarzanie danych */
 +	GG_STATE_DONE,                  /**< Połączenie zakończone */
 +
 +	/* gg_dcc */
 +	GG_STATE_LISTENING,		/* czeka na połączenia */
 +	GG_STATE_READING_UIN_1,		/* czeka na uin peera */
 +	GG_STATE_READING_UIN_2,		/* czeka na swój uin */
 +	GG_STATE_SENDING_ACK,		/* wysyła potwierdzenie dcc */
 +	GG_STATE_READING_ACK,		/* czeka na potwierdzenie dcc */
 +	GG_STATE_READING_REQUEST,	/* czeka na komendę */
 +	GG_STATE_SENDING_REQUEST,	/* wysyła komendę */
 +	GG_STATE_SENDING_FILE_INFO,	/* wysyła informacje o pliku */
 +	GG_STATE_READING_PRE_FILE_INFO,	/* czeka na pakiet przed file_info */
 +	GG_STATE_READING_FILE_INFO,	/* czeka na informacje o pliku */
 +	GG_STATE_SENDING_FILE_ACK,	/* wysyła potwierdzenie pliku */
 +	GG_STATE_READING_FILE_ACK,	/* czeka na potwierdzenie pliku */
 +	GG_STATE_SENDING_FILE_HEADER,	/* wysyła nagłówek pliku */
 +	GG_STATE_READING_FILE_HEADER,	/* czeka na nagłówek */
 +	GG_STATE_GETTING_FILE,		/* odbiera plik */
 +	GG_STATE_SENDING_FILE,		/* wysyła plik */
 +	GG_STATE_READING_VOICE_ACK,	/* czeka na potwierdzenie voip */
 +	GG_STATE_READING_VOICE_HEADER,	/* czeka na rodzaj bloku voip */
 +	GG_STATE_READING_VOICE_SIZE,	/* czeka na rozmiar bloku voip */
 +	GG_STATE_READING_VOICE_DATA,	/* czeka na dane voip */
 +	GG_STATE_SENDING_VOICE_ACK,	/* wysyła potwierdzenie voip */
 +	GG_STATE_SENDING_VOICE_REQUEST,	/* wysyła żądanie voip */
 +	GG_STATE_READING_TYPE,		/* czeka na typ połączenia */
 +
 +	/* nowe. bez sensu jest to API. */
 +	GG_STATE_TLS_NEGOTIATION,	/**< Negocjacja połączenia szyfrowanego */
 +
 +	GG_STATE_REQUESTING_ID,		/**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */
 +	GG_STATE_WAITING_FOR_ACCEPT,	/**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */
 +	GG_STATE_WAITING_FOR_INFO,	/**< Oczekiwanie na informacje o połączeniu bezpośrednim */
 +
 +	GG_STATE_READING_ID,		/**< Odebranie identyfikatora połączenia bezpośredniego */
 +	GG_STATE_SENDING_ID,		/**< Wysłano identyfikator połączenia bezpośredniego */
 +	GG_STATE_RESOLVING_GG,		/**< Oczekiwanie na rozwiązanie nazwy serwera Gadu-Gadu */
 +
  	GG_STATE_RESOLVING_RELAY,	/**< Oczekiwanie na rozwiązanie nazwy serwera pośredniczącego */
  	GG_STATE_CONNECTING_RELAY,	/**< Oczekiwanie na połączenie z serwerem pośredniczącym */
 -	GG_STATE_READING_RELAY		/**< Odbieranie danych */ -}; - -/** - * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać - * z deskryptora. Maska bitowa. - * - * \ingroup events - */ -enum gg_check_t { -	GG_CHECK_NONE = 0,		/**< Nie sprawdzaj niczego */ -	GG_CHECK_WRITE = 1,		/**< Sprawdź możliwość zapisu */ -	GG_CHECK_READ = 2		/**< Sprawdź możliwość odczytu */ -}; - -/** - * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione - * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu - * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne - * lub nie mają znaczenia, została usunięta z dokumentacji. - * - * \ingroup login - */ -struct gg_login_params { -	uin_t uin;			/**< Numer Gadu-Gadu */ -	char *password;			/**< Hasło */ -	int async;			/**< Flaga asynchronicznego połączenia (domyślnie nie) */ -	int status;			/**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */ -	char *status_descr;		/**< Początkowy opis użytkownika (domyślnie brak) */ -	uint32_t server_addr;		/**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */ -	uint16_t server_port;		/**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */ -#ifndef DOXYGEN -	uint32_t client_addr;		/**< Adres połączeń bezpośrednich (nieaktualne) */ -	uint16_t client_port;		/**< Port połączeń bezpośrednich (nieaktualne) */ -#endif -	int protocol_version;		/**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */ -	char *client_version;		/**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */ -	int has_audio;			/**< Flaga obsługi połączeń głosowych */ -	int last_sysmsg;		/**< Numer ostatnio odebranej wiadomości systemowej */ -	uint32_t external_addr;		/**< Adres publiczny dla połączeń bezpośrednich (6.x) */ -	uint16_t external_port;		/**< Port publiczny dla połączeń bezpośrednich (6.x) */ -#ifndef DOXYGEN -	int tls;			/**< Flaga połączenia szyfrowanego (nieaktualna) */ -#endif -	int image_size;			/**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */ -#ifndef DOXYGEN -	int era_omnix;			/**< Flaga udawania klienta Era Omnix (nieaktualna) */ -#endif -	int hash_type;			/**< Rodzaj skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyślnie SHA1) */ -	gg_encoding_t encoding;		/**< Rodzaj kodowania używanego w sesji (domyślnie CP1250) */ -	gg_resolver_t resolver;		/**< Sposób rozwiązywania nazw (patrz \ref build-resolver) */ -	int protocol_features;		/**< Opcje protokołu (flagi GG_FEATURE_*). */ -	int status_flags;		/**< Flagi statusu (flagi GG_STATUS_FLAG_*, patrz \ref status). */ - -#ifndef DOXYGEN -	char dummy[1 * sizeof(int)];	/**< \internal Miejsce na kilka kolejnych -					  parametrów, żeby wraz z dodawaniem kolejnych -					  parametrów nie zmieniał się rozmiar struktury */ -#endif - -}; - -#ifdef GG_CONFIG_MIRANDA -struct gg_session *gg_login(const struct gg_login_params *p, SOCKET *gg_sock, int *gg_failno); -#else -struct gg_session *gg_login(const struct gg_login_params *p); -#endif -void gg_free_session(struct gg_session *sess); -void gg_logoff(struct gg_session *sess); -int gg_change_status(struct gg_session *sess, int status); -int gg_change_status_descr(struct gg_session *sess, int status, const char *descr); -int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time); -int gg_change_status_flags(struct gg_session *sess, int flags); -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message); -int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen); -int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message); -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen); -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len); -int gg_ping(struct gg_session *sess); -int gg_userlist_request(struct gg_session *sess, char type, const char *request); -int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32); -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size); -int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length); - -uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len); - -int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type); -gg_resolver_t gg_session_get_resolver(struct gg_session *gs); -int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int)); - -int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type); -gg_resolver_t gg_http_get_resolver(struct gg_http *gh); -int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int)); - -int gg_global_set_resolver(gg_resolver_t type); -gg_resolver_t gg_global_get_resolver(void); -int gg_global_set_custom_resolver(int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int)); - +	GG_STATE_READING_RELAY,		/**< Odbieranie danych */
 +
 +	GG_STATE_DISCONNECTING,		/**< Oczekiwanie na potwierdzenie rozłączenia */
 +};
 +
 +/**
 + * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
 + * z deskryptora. Maska bitowa.
 + *
 + * \ingroup events
 + */
 +enum gg_check_t {
 +	GG_CHECK_NONE = 0,		/**< Nie sprawdzaj niczego */
 +	GG_CHECK_WRITE = 1,		/**< Sprawdź możliwość zapisu */
 +	GG_CHECK_READ = 2		/**< Sprawdź możliwość odczytu */
 +};
 +
 +/**
 + * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione
 + * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu
 + * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne
 + * lub nie mają znaczenia, została usunięta z dokumentacji.
 + *
 + * \ingroup login
 + */
 +struct gg_login_params {
 +	uin_t uin;			/**< Numer Gadu-Gadu */
 +	char *password;			/**< Hasło */
 +	int async;			/**< Flaga asynchronicznego połączenia (domyślnie nie) */
 +	int status;			/**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */
 +	char *status_descr;		/**< Początkowy opis użytkownika (domyślnie brak) */
 +	uint32_t server_addr;		/**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
 +	uint16_t server_port;		/**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
 +#ifndef DOXYGEN
 +	uint32_t client_addr;		/**< Adres połączeń bezpośrednich (nieaktualne) */
 +	uint16_t client_port;		/**< Port połączeń bezpośrednich (nieaktualne) */
 +#endif
 +	int protocol_version;		/**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */
 +	char *client_version;		/**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */
 +	int has_audio;			/**< Flaga obsługi połączeń głosowych */
 +	int last_sysmsg;		/**< Numer ostatnio odebranej wiadomości systemowej */
 +	uint32_t external_addr;		/**< Adres publiczny dla połączeń bezpośrednich (6.x) */
 +	uint16_t external_port;		/**< Port publiczny dla połączeń bezpośrednich (6.x) */
 +#ifndef DOXYGEN
 +	int tls;			/**< Flaga połączenia szyfrowanego (nieaktualna) */
 +#endif
 +	int image_size;			/**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */
 +#ifndef DOXYGEN
 +	int era_omnix;			/**< Flaga udawania klienta Era Omnix (nieaktualna) */
 +#endif
 +	int hash_type;			/**< Rodzaj skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyślnie SHA1) */
 +	gg_encoding_t encoding;		/**< Rodzaj kodowania używanego w sesji (domyślnie CP1250) */
 +	gg_resolver_t resolver;		/**< Sposób rozwiązywania nazw (patrz \ref build-resolver) */
 +	int protocol_features;		/**< Opcje protokołu (flagi GG_FEATURE_*). */
 +	int status_flags;		/**< Flagi statusu (flagi GG_STATUS_FLAG_*, patrz \ref status). */
 +
 +#ifndef DOXYGEN
 +	char dummy[1 * sizeof(int)];	/**< \internal Miejsce na kilka kolejnych
 +					  parametrów, żeby wraz z dodawaniem kolejnych
 +					  parametrów nie zmieniał się rozmiar struktury */
 +#endif
 +
 +};
 +
 +#ifdef GG_CONFIG_MIRANDA
 +struct gg_session *gg_login(const struct gg_login_params *p, SOCKET *gg_sock, int *gg_failno);
 +#else
 +struct gg_session *gg_login(const struct gg_login_params *p);
 +#endif
 +void gg_free_session(struct gg_session *sess);
 +void gg_logoff(struct gg_session *sess);
 +int gg_change_status(struct gg_session *sess, int status);
 +int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
 +int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time);
 +int gg_change_status_flags(struct gg_session *sess, int flags);
 +int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message);
 +int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen);
 +int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message);
 +int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen);
 +int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
 +int gg_ping(struct gg_session *sess);
 +int gg_userlist_request(struct gg_session *sess, char type, const char *request);
 +int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
 +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
 +int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length);
 +
 +uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
 +
 +int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type);
 +gg_resolver_t gg_session_get_resolver(struct gg_session *gs);
 +int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
 +
 +int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type);
 +gg_resolver_t gg_http_get_resolver(struct gg_http *gh);
 +int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
 +
 +int gg_global_set_resolver(gg_resolver_t type);
 +gg_resolver_t gg_global_get_resolver(void);
 +int gg_global_set_custom_resolver(int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
 +
  int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id);
 - -/** - * Rodzaj zdarzenia. - * - * \ingroup events - */ -enum gg_event_t { -	GG_EVENT_NONE = 0,		/**< Nie wydarzyło się nic wartego uwagi */ -	GG_EVENT_MSG,			/**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */ -	GG_EVENT_NOTIFY,		/**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */ -	GG_EVENT_NOTIFY_DESCR,		/**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */ -	GG_EVENT_STATUS,		/**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */ -	GG_EVENT_ACK,			/**< Potwierdzenie doręczenia wiadomości */ -	GG_EVENT_PONG,			/**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */ -	GG_EVENT_CONN_FAILED,		/**< \brief Nie udało się połączyć */ -	GG_EVENT_CONN_SUCCESS,		/**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */ -	GG_EVENT_DISCONNECT,		/**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */ - -	GG_EVENT_DCC_NEW,		/**< Nowe połączenie bezpośrednie (6.x) */ -	GG_EVENT_DCC_ERROR,		/**< Błąd połączenia bezpośredniego (6.x) */ -	GG_EVENT_DCC_DONE,		/**< Zakończono połączenie bezpośrednie (6.x) */ -	GG_EVENT_DCC_CLIENT_ACCEPT,	/**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */ -	GG_EVENT_DCC_CALLBACK,		/**< Zwrotne połączenie bezpośrednie (6.x) */ -	GG_EVENT_DCC_NEED_FILE_INFO,	/**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */ -	GG_EVENT_DCC_NEED_FILE_ACK,	/**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */ -	GG_EVENT_DCC_NEED_VOICE_ACK,	/**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */ -	GG_EVENT_DCC_VOICE_DATA, 	/**< Dane bezpośredniego połączenia głosowego (6.x) */ - -	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/**< Odpowiedź katalogu publicznego */ -	GG_EVENT_PUBDIR50_READ,		/**< Odczytano własne dane z katalogu publicznego */ -	GG_EVENT_PUBDIR50_WRITE,	/**< Zmieniono własne dane w katalogu publicznym */ - -	GG_EVENT_STATUS60,		/**< Zmiana statusu osoby z listy kontaktów */ -	GG_EVENT_NOTIFY60,		/**< Informacja o statusach osób z listy kontaktów. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane.  */ -	GG_EVENT_USERLIST,		/**< Wynik importu lub eksportu listy kontaktów */ -	GG_EVENT_IMAGE_REQUEST,		/**< Żądanie przesłania obrazka z wiadomości */ -	GG_EVENT_IMAGE_REPLY,		/**< Przysłano obrazek z wiadomości */ -	GG_EVENT_DCC_ACK,		/**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */ - -	GG_EVENT_DCC7_NEW,		/**< Nowe połączenie bezpośrednie (7.x) */ -	GG_EVENT_DCC7_ACCEPT,		/**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */ -	GG_EVENT_DCC7_REJECT,		/**< Odrzucono połączenie bezpośrednie (7.x) */ -	GG_EVENT_DCC7_CONNECTED,	/**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */ -	GG_EVENT_DCC7_ERROR,		/**< Błąd połączenia bezpośredniego (7.x) */ -	GG_EVENT_DCC7_DONE,		/**< Zakończono połączenie bezpośrednie (7.x) */ -	GG_EVENT_DCC7_PENDING,		/**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */ - -	GG_EVENT_XML_EVENT,		/**< Otrzymano komunikat systemowy (7.7) */ -	GG_EVENT_DISCONNECT_ACK,	/**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */ -	GG_EVENT_XML_ACTION, -	GG_EVENT_TYPING_NOTIFICATION,	/**< Powiadomienie o pisaniu */ -	GG_EVENT_USER_DATA,		/**< Informacja o kontaktach */ +
 +/**
 + * Rodzaj zdarzenia.
 + *
 + * \ingroup events
 + */
 +enum gg_event_t {
 +	GG_EVENT_NONE = 0,		/**< Nie wydarzyło się nic wartego uwagi */
 +	GG_EVENT_MSG,			/**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */
 +	GG_EVENT_NOTIFY,		/**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
 +	GG_EVENT_NOTIFY_DESCR,		/**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
 +	GG_EVENT_STATUS,		/**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
 +	GG_EVENT_ACK,			/**< Potwierdzenie doręczenia wiadomości */
 +	GG_EVENT_PONG,			/**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */
 +	GG_EVENT_CONN_FAILED,		/**< \brief Nie udało się połączyć */
 +	GG_EVENT_CONN_SUCCESS,		/**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */
 +	GG_EVENT_DISCONNECT,		/**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */
 +
 +	GG_EVENT_DCC_NEW,		/**< Nowe połączenie bezpośrednie (6.x) */
 +	GG_EVENT_DCC_ERROR,		/**< Błąd połączenia bezpośredniego (6.x) */
 +	GG_EVENT_DCC_DONE,		/**< Zakończono połączenie bezpośrednie (6.x) */
 +	GG_EVENT_DCC_CLIENT_ACCEPT,	/**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */
 +	GG_EVENT_DCC_CALLBACK,		/**< Zwrotne połączenie bezpośrednie (6.x) */
 +	GG_EVENT_DCC_NEED_FILE_INFO,	/**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */
 +	GG_EVENT_DCC_NEED_FILE_ACK,	/**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */
 +	GG_EVENT_DCC_NEED_VOICE_ACK,	/**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */
 +	GG_EVENT_DCC_VOICE_DATA, 	/**< Dane bezpośredniego połączenia głosowego (6.x) */
 +
 +	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/**< Odpowiedź katalogu publicznego */
 +	GG_EVENT_PUBDIR50_READ,		/**< Odczytano własne dane z katalogu publicznego */
 +	GG_EVENT_PUBDIR50_WRITE,	/**< Zmieniono własne dane w katalogu publicznym */
 +
 +	GG_EVENT_STATUS60,		/**< Zmiana statusu osoby z listy kontaktów */
 +	GG_EVENT_NOTIFY60,		/**< Informacja o statusach osób z listy kontaktów. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane.  */
 +	GG_EVENT_USERLIST,		/**< Wynik importu lub eksportu listy kontaktów */
 +	GG_EVENT_IMAGE_REQUEST,		/**< Żądanie przesłania obrazka z wiadomości */
 +	GG_EVENT_IMAGE_REPLY,		/**< Przysłano obrazek z wiadomości */
 +	GG_EVENT_DCC_ACK,		/**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */
 +
 +	GG_EVENT_DCC7_NEW,		/**< Nowe połączenie bezpośrednie (7.x) */
 +	GG_EVENT_DCC7_ACCEPT,		/**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */
 +	GG_EVENT_DCC7_REJECT,		/**< Odrzucono połączenie bezpośrednie (7.x) */
 +	GG_EVENT_DCC7_CONNECTED,	/**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */
 +	GG_EVENT_DCC7_ERROR,		/**< Błąd połączenia bezpośredniego (7.x) */
 +	GG_EVENT_DCC7_DONE,		/**< Zakończono połączenie bezpośrednie (7.x) */
 +	GG_EVENT_DCC7_PENDING,		/**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */
 +
 +	GG_EVENT_XML_EVENT,		/**< Otrzymano komunikat systemowy (7.7) */
 +	GG_EVENT_DISCONNECT_ACK,	/**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */
 +	GG_EVENT_TYPING_NOTIFICATION,	/**< Powiadomienie o pisaniu */
 +	GG_EVENT_USER_DATA,		/**< Informacja o kontaktach */
  	GG_EVENT_MULTILOGON_MSG,	/**< Wiadomość wysłana z innej sesji multilogowania */
  	GG_EVENT_MULTILOGON_INFO	/**< Informacja o innych sesjach multilogowania */
 -}; - -#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY - -/** - * Powód nieudanego połączenia. - */ -enum gg_failure_t { -	GG_FAILURE_RESOLVING = 1,	/**< Nie znaleziono serwera */ -	GG_FAILURE_CONNECTING,		/**< Błąd połączenia */ -	GG_FAILURE_INVALID,		/**< Serwer zwrócił nieprawidłowe dane */ -	GG_FAILURE_READING,		/**< Zerwano połączenie podczas odczytu */ -	GG_FAILURE_WRITING,		/**< Zerwano połączenie podczas zapisu */ -	GG_FAILURE_PASSWORD,		/**< Nieprawidłowe hasło */ -	GG_FAILURE_404, 		/**< Nieużywane */ -	GG_FAILURE_TLS,			/**< Błąd negocjacji szyfrowanego połączenia */ -	GG_FAILURE_NEED_EMAIL, 		/**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */ -	GG_FAILURE_INTRUDER,		/**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */ -	GG_FAILURE_UNAVAILABLE		/**< Serwery są wyłączone */ -}; - -/** - * Kod błędu danej operacji. - * - * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie - * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy - * sprawdzić zawartość zmiennej systemowej \c errno. - */ -enum gg_error_t { -	GG_ERROR_RESOLVING = 1,		/**< Nie znaleziono hosta */ -	GG_ERROR_CONNECTING,		/**< Błąd połączenia */ -	GG_ERROR_READING,		/**< Błąd odczytu/odbierania */ -	GG_ERROR_WRITING,		/**< Błąd zapisu/wysyłania */ - -	GG_ERROR_DCC_HANDSHAKE,		/**< Błąd negocjacji */ -	GG_ERROR_DCC_FILE,		/**< Błąd odczytu/zapisu pliku */ -	GG_ERROR_DCC_EOF,		/**< Przedwczesny koniec pliku */ -	GG_ERROR_DCC_NET,		/**< Błąd wysyłania/odbierania */ -	GG_ERROR_DCC_REFUSED, 		/**< Połączenie odrzucone */ - -	GG_ERROR_DCC7_HANDSHAKE,	/**< Błąd negocjacji */ -	GG_ERROR_DCC7_FILE,		/**< Błąd odczytu/zapisu pliku */ -	GG_ERROR_DCC7_EOF,		/**< Przedwczesny koniec pliku */ -	GG_ERROR_DCC7_NET,		/**< Błąd wysyłania/odbierania */ -	GG_ERROR_DCC7_REFUSED, 		/**< Połączenie odrzucone */ +};
 +
 +#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
 +
 +/**
 + * Powód nieudanego połączenia.
 + */
 +enum gg_failure_t {
 +	GG_FAILURE_RESOLVING = 1,	/**< Nie znaleziono serwera */
 +	GG_FAILURE_CONNECTING,		/**< Błąd połączenia */
 +	GG_FAILURE_INVALID,		/**< Serwer zwrócił nieprawidłowe dane */
 +	GG_FAILURE_READING,		/**< Zerwano połączenie podczas odczytu */
 +	GG_FAILURE_WRITING,		/**< Zerwano połączenie podczas zapisu */
 +	GG_FAILURE_PASSWORD,		/**< Nieprawidłowe hasło */
 +	GG_FAILURE_404, 		/**< Nieużywane */
 +	GG_FAILURE_TLS,			/**< Błąd negocjacji szyfrowanego połączenia */
 +	GG_FAILURE_NEED_EMAIL, 		/**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */
 +	GG_FAILURE_INTRUDER,		/**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */
 +	GG_FAILURE_UNAVAILABLE		/**< Serwery są wyłączone */
 +};
 +
 +/**
 + * Kod błędu danej operacji.
 + *
 + * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie
 + * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy
 + * sprawdzić zawartość zmiennej systemowej \c errno.
 + */
 +enum gg_error_t {
 +	GG_ERROR_RESOLVING = 1,		/**< Nie znaleziono hosta */
 +	GG_ERROR_CONNECTING,		/**< Błąd połączenia */
 +	GG_ERROR_READING,		/**< Błąd odczytu/odbierania */
 +	GG_ERROR_WRITING,		/**< Błąd zapisu/wysyłania */
 +
 +	GG_ERROR_DCC_HANDSHAKE,		/**< Błąd negocjacji */
 +	GG_ERROR_DCC_FILE,		/**< Błąd odczytu/zapisu pliku */
 +	GG_ERROR_DCC_EOF,		/**< Przedwczesny koniec pliku */
 +	GG_ERROR_DCC_NET,		/**< Błąd wysyłania/odbierania */
 +	GG_ERROR_DCC_REFUSED, 		/**< Połączenie odrzucone */
 +
 +	GG_ERROR_DCC7_HANDSHAKE,	/**< Błąd negocjacji */
 +	GG_ERROR_DCC7_FILE,		/**< Błąd odczytu/zapisu pliku */
 +	GG_ERROR_DCC7_EOF,		/**< Przedwczesny koniec pliku */
 +	GG_ERROR_DCC7_NET,		/**< Błąd wysyłania/odbierania */
 +	GG_ERROR_DCC7_REFUSED, 		/**< Połączenie odrzucone */
  	GG_ERROR_DCC7_RELAY		/**< Problem z serwerem pośredniczącym */
 -}; - -/** - * Pole zapytania lub odpowiedzi katalogu publicznego. - */ -struct gg_pubdir50_entry { -	int num;	/**< Numer wyniku */ -	char *field;	/**< Nazwa pola */ -	char *value;	/**< Wartość pola */ -} /* GG_DEPRECATED */; - -/** - * Zapytanie lub odpowiedź katalogu publicznego. - * - * Patrz \c gg_pubdir50_t. - */ -struct gg_pubdir50_s { -	int count;	/**< Liczba wyników odpowiedzi */ -	uin_t next;	/**< Numer początkowy następnego zapytania */ -	int type;	/**< Rodzaj zapytania */ -	uint32_t seq;	/**< Numer sekwencyjny */ -	struct gg_pubdir50_entry *entries;	/**< Pola zapytania lub odpowiedzi */ -	int entries_count;	/**< Liczba pól */ -} /* GG_DEPRECATED */; - -/** - * Zapytanie lub odpowiedź katalogu publicznego. - * - * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne - * informacje są dostępne za pomocą funkcji \c gg_pubdir50_* - */ -typedef struct gg_pubdir50_s *gg_pubdir50_t; - -/** +};
 +
 +/**
 + * Pole zapytania lub odpowiedzi katalogu publicznego.
 + */
 +struct gg_pubdir50_entry {
 +	int num;	/**< Numer wyniku */
 +	char *field;	/**< Nazwa pola */
 +	char *value;	/**< Wartość pola */
 +} /* GG_DEPRECATED */;
 +
 +/**
 + * Zapytanie lub odpowiedź katalogu publicznego.
 + *
 + * Patrz \c gg_pubdir50_t.
 + */
 +struct gg_pubdir50_s {
 +	int count;	/**< Liczba wyników odpowiedzi */
 +	uin_t next;	/**< Numer początkowy następnego zapytania */
 +	int type;	/**< Rodzaj zapytania */
 +	uint32_t seq;	/**< Numer sekwencyjny */
 +	struct gg_pubdir50_entry *entries;	/**< Pola zapytania lub odpowiedzi */
 +	int entries_count;	/**< Liczba pól */
 +} /* GG_DEPRECATED */;
 +
 +/**
 + * Zapytanie lub odpowiedź katalogu publicznego.
 + *
 + * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne
 + * informacje są dostępne za pomocą funkcji \c gg_pubdir50_*
 + */
 +typedef struct gg_pubdir50_s *gg_pubdir50_t;
 +
 +/**
   * Opis zdarzeń \c GG_EVENT_MSG i \c GG_EVENT_MULTILOGON_MSG.
 - */ -struct gg_event_msg { -	uin_t sender;		/**< Numer nadawcy/odbiorcy */ -	int msgclass;		/**< Klasa wiadomości */ -	time_t time;		/**< Czas nadania */ -	char *message;	/**< Treść wiadomości */ - -	int recipients_count;	/**< Liczba odbiorców konferencji */ -	uin_t *recipients;	/**< Odbiorcy konferencji */ - -	int formats_length;	/**< Długość informacji o formatowaniu tekstu */ -	void *formats;		/**< Informacje o formatowaniu tekstu */ -	uint32_t seq;		/**< Numer sekwencyjny wiadomości */ - -	char *xhtml_message;	/**< Treść wiadomości w formacie XHTML (może być równe \c NULL, jeśli wiadomość nie zawiera treści XHTML) */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR. - */ -struct gg_event_notify_descr { -	struct gg_notify_reply *notify;	/**< Informacje o liście kontaktów */ -	char *descr;		/**< Opis status */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_STATUS. - */ -struct gg_event_status { -	uin_t uin;		/**< Numer Gadu-Gadu */ -	uint32_t status;	/**< Nowy status */ -	char *descr;		/**< Opis */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_STATUS60. - */ -struct gg_event_status60 { -	uin_t uin;		/**< Numer Gadu-Gadu */ -	int status;		/**< Nowy status */ -	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */ -	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */ -	int version;		/**< Wersja protokołu */ -	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */ -	char *descr;		/**< Opis statusu */ -	time_t time;		/**< Czas powrotu */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60. - */ -struct gg_event_notify60 { -	uin_t uin;		/**< Numer Gadu-Gadu. W ostatnim elemencie jest równy 0, a pozostałe pola są niezainicjowane. */ -	int status;		/**< Nowy status */ -	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */ -	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */ -	int version;		/**< Wersja protokołu */ -	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */ -	char *descr;		/**< Opis statusu */ -	time_t time;		/**< Czas powrotu */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_ACK. - */ -struct gg_event_ack { -	uin_t recipient;	/**< Numer odbiorcy */ -	int status;		/**< Status doręczenia */ -	int seq;		/**< Numer sekwencyjny wiadomości */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_USERLIST. - */ -struct gg_event_userlist { -	char type;		/**< Rodzaj odpowiedzi */ -	char *reply;		/**< Treść odpowiedzi */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA. - */ -struct gg_event_dcc_voice_data { -	uint8_t *data;		/**< Dane dźwiękowe */ -	int length;		/**< Rozmiar danych dźwiękowych */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST. - */ -struct gg_event_image_request { -	uin_t sender;		/**< Nadawca żądania */ -	uint32_t size;		/**< Rozmiar obrazka */ -	uint32_t crc32;		/**< Suma kontrolna CRC32 */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY. - */ -struct gg_event_image_reply { -	uin_t sender;		/**< Nadawca obrazka */ -	uint32_t size;		/**< Rozmiar obrazka */ -	uint32_t crc32;		/**< Suma kontrolna CRC32 */ -	char *filename;		/**< Nazwa pliku */ -	char *image;		/**< Bufor z obrazkiem */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_XML_EVENT. - */ -struct gg_event_xml_event { -	char *data;		/**< Bufor z komunikatem */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_XML_ACTION. - */ -struct gg_event_xml_action { -	char *data;		/**< Bufor z komunikatem */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED. - */ -struct gg_event_dcc7_connected { -	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_DCC7_PENDING. - */ -struct gg_event_dcc7_pending { -	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_DCC7_REJECT. - */ -struct gg_event_dcc7_reject { -	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */ -	int reason;		/**< powód odrzucenia */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT. - */ -struct gg_event_dcc7_accept { -	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */ -	int type;		/**< Sposób połączenia (P2P, przez serwer) */ -	uint32_t remote_ip;	/**< Adres zdalnego klienta */ -	uint16_t remote_port;	/**< Port zdalnego klienta */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_DCC7_DONE. - */ -struct gg_event_dcc7_done { -	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_DCC7_ERROR. - * - * \note Odwrotna kolejność pól ma na celu zachowanie ABI. - */ -struct gg_event_dcc7_error { -	enum gg_error_t error;	/**< Kod błędu */ -	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */ -}; - -/** - * Opis zdarzenia \c GG_EVENT_TYPING_NOTIFICATION. - */ -struct gg_event_typing_notification { -	uin_t uin;		/**< Numer rozmówcy */ -	int length;		/**< Długość tekstu */ -}; - + */
 +struct gg_event_msg {
 +	uin_t sender;		/**< Numer nadawcy/odbiorcy */
 +	int msgclass;		/**< Klasa wiadomości */
 +	time_t time;		/**< Czas nadania */
 +	char *message;	/**< Treść wiadomości */
 +
 +	int recipients_count;	/**< Liczba odbiorców konferencji */
 +	uin_t *recipients;	/**< Odbiorcy konferencji */
 +
 +	int formats_length;	/**< Długość informacji o formatowaniu tekstu */
 +	void *formats;		/**< Informacje o formatowaniu tekstu */
 +	uint32_t seq;		/**< Numer sekwencyjny wiadomości */
 +
 +	char *xhtml_message;	/**< Treść wiadomości w formacie XHTML (może być równe \c NULL, jeśli wiadomość nie zawiera treści XHTML) */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR.
 + */
 +struct gg_event_notify_descr {
 +	struct gg_notify_reply *notify;	/**< Informacje o liście kontaktów */
 +	char *descr;		/**< Opis status */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_STATUS.
 + */
 +struct gg_event_status {
 +	uin_t uin;		/**< Numer Gadu-Gadu */
 +	uint32_t status;	/**< Nowy status */
 +	char *descr;		/**< Opis */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_STATUS60.
 + */
 +struct gg_event_status60 {
 +	uin_t uin;		/**< Numer Gadu-Gadu */
 +	int status;		/**< Nowy status */
 +	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */
 +	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */
 +	int version;		/**< Wersja protokołu */
 +	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
 +	char *descr;		/**< Opis statusu */
 +	time_t time;		/**< Czas powrotu */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
 + */
 +struct gg_event_notify60 {
 +	uin_t uin;		/**< Numer Gadu-Gadu. W ostatnim elemencie jest równy 0, a pozostałe pola są niezainicjowane. */
 +	int status;		/**< Nowy status */
 +	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */
 +	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */
 +	int version;		/**< Wersja protokołu */
 +	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
 +	char *descr;		/**< Opis statusu */
 +	time_t time;		/**< Czas powrotu */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_ACK.
 + */
 +struct gg_event_ack {
 +	uin_t recipient;	/**< Numer odbiorcy */
 +	int status;		/**< Status doręczenia */
 +	int seq;		/**< Numer sekwencyjny wiadomości */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_USERLIST.
 + */
 +struct gg_event_userlist {
 +	char type;		/**< Rodzaj odpowiedzi */
 +	char *reply;		/**< Treść odpowiedzi */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA.
 + */
 +struct gg_event_dcc_voice_data {
 +	uint8_t *data;		/**< Dane dźwiękowe */
 +	int length;		/**< Rozmiar danych dźwiękowych */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
 + */
 +struct gg_event_image_request {
 +	uin_t sender;		/**< Nadawca żądania */
 +	uint32_t size;		/**< Rozmiar obrazka */
 +	uint32_t crc32;		/**< Suma kontrolna CRC32 */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY.
 + */
 +struct gg_event_image_reply {
 +	uin_t sender;		/**< Nadawca obrazka */
 +	uint32_t size;		/**< Rozmiar obrazka */
 +	uint32_t crc32;		/**< Suma kontrolna CRC32 */
 +	char *filename;		/**< Nazwa pliku */
 +	char *image;		/**< Bufor z obrazkiem */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_XML_EVENT.
 + */
 +struct gg_event_xml_event {
 +	char *data;		/**< Bufor z komunikatem */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
 + */
 +struct gg_event_dcc7_connected {
 +	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_DCC7_PENDING.
 + */
 +struct gg_event_dcc7_pending {
 +	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_DCC7_REJECT.
 + */
 +struct gg_event_dcc7_reject {
 +	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
 +	int reason;		/**< powód odrzucenia */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT.
 + */
 +struct gg_event_dcc7_accept {
 +	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
 +	int type;		/**< Sposób połączenia (P2P, przez serwer) */
 +	uint32_t remote_ip;	/**< Adres zdalnego klienta */
 +	uint16_t remote_port;	/**< Port zdalnego klienta */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_DCC7_DONE.
 + */
 +struct gg_event_dcc7_done {
 +	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_DCC7_ERROR.
 + *
 + * \note Odwrotna kolejność pól ma na celu zachowanie ABI.
 + */
 +struct gg_event_dcc7_error {
 +	enum gg_error_t error;	/**< Kod błędu */
 +	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
 +};
 +
 +/**
 + * Opis zdarzenia \c GG_EVENT_TYPING_NOTIFICATION.
 + */
 +struct gg_event_typing_notification {
 +	uin_t uin;		/**< Numer rozmówcy */
 +	int length;		/**< Długość tekstu */
 +};
 +
  /**
   * Atrybut użytkownika.
   */
 @@ -1044,7 +1038,7 @@ struct gg_event_user_data {  	size_t user_count;	/**< Liczba kontaktów */
  	struct gg_event_user_data_user *users;	/**< Lista kontaktów */
  };
 - +
  /**
   * Struktura opisująca sesję multilogowania.
   */
 @@ -1064,1241 +1058,1240 @@ struct gg_event_multilogon_info {  	int count;		/**< Liczba sesji */
  	struct gg_multilogon_session *sessions;	/** Lista sesji */
  };
 - -/** - * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(),  - * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd(). - * - * \ingroup events - */ -union gg_event_union { -	enum gg_failure_t failure;	/**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */ -	struct gg_notify_reply *notify;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY) */ -	struct gg_event_notify_descr notify_descr;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY_DESCR) */ -	struct gg_event_status status;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS) */ -	struct gg_event_status60 status60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS60) */ -	struct gg_event_notify60 *notify60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY60) */ -	struct gg_event_msg msg;	/**< Otrzymano wiadomość (\c GG_EVENT_MSG) */ -	struct gg_event_ack ack;	/**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */ -	struct gg_event_image_request image_request;	/**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */ -	struct gg_event_image_reply image_reply;	/**< Odpowiedź z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */ -	struct gg_event_userlist userlist;	/**< Odpowiedź listy kontaktów (\c GG_EVENT_USERLIST) */ -	gg_pubdir50_t pubdir50;	/**< Odpowiedź katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */ -	struct gg_event_xml_event xml_event;	/**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */ -	struct gg_event_xml_action xml_action;	/**< Zdarzenie XML (\c GG_EVENT_XML_ACTION) */ -	struct gg_dcc *dcc_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC_NEW) */ -	enum gg_error_t dcc_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC_ERROR) */ -	struct gg_event_dcc_voice_data dcc_voice_data;	/**< Dane połączenia głosowego (\c GG_EVENT_DCC_VOICE_DATA) */ -	struct gg_dcc7 *dcc7_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC7_NEW) */ -	enum gg_error_t dcc7_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC7_ERROR) */ -	struct gg_event_dcc7_error dcc7_error_ex;	/**< Błąd połączenia bezpośredniego ze wskaźnikiem na strukturę połączenia (\c GG_EVENT_DCC7_ERROR) */ -	struct gg_event_dcc7_connected dcc7_connected;	/**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */ -	struct gg_event_dcc7_pending dcc7_pending;	/**< Trwa próba połączenia bezpośredniego (\c GG_EVENT_DCC7_PENDING) */ -	struct gg_event_dcc7_reject dcc7_reject;	/**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */ -	struct gg_event_dcc7_accept dcc7_accept;	/**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */ -	struct gg_event_dcc7_done dcc7_done;	/**< Zakończono połączenie bezpośrednie (\c GG_EVENT_DCC7_DONE) */ -	struct gg_event_typing_notification typing_notification;	/**< Powiadomienie o pisaniu (\c GG_EVENT_TYPING_NOTIFICATION) */ +
 +/**
 + * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(), 
 + * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
 + *
 + * \ingroup events
 + */
 +union gg_event_union {
 +	enum gg_failure_t failure;	/**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */
 +	struct gg_notify_reply *notify;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY) */
 +	struct gg_event_notify_descr notify_descr;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY_DESCR) */
 +	struct gg_event_status status;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS) */
 +	struct gg_event_status60 status60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS60) */
 +	struct gg_event_notify60 *notify60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY60) */
 +	struct gg_event_msg msg;	/**< Otrzymano wiadomość (\c GG_EVENT_MSG) */
 +	struct gg_event_ack ack;	/**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */
 +	struct gg_event_image_request image_request;	/**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */
 +	struct gg_event_image_reply image_reply;	/**< Odpowiedź z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */
 +	struct gg_event_userlist userlist;	/**< Odpowiedź listy kontaktów (\c GG_EVENT_USERLIST) */
 +	gg_pubdir50_t pubdir50;	/**< Odpowiedź katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */
 +	struct gg_event_xml_event xml_event;	/**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */
 +	struct gg_dcc *dcc_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC_NEW) */
 +	enum gg_error_t dcc_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC_ERROR) */
 +	struct gg_event_dcc_voice_data dcc_voice_data;	/**< Dane połączenia głosowego (\c GG_EVENT_DCC_VOICE_DATA) */
 +	struct gg_dcc7 *dcc7_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC7_NEW) */
 +	enum gg_error_t dcc7_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC7_ERROR) */
 +	struct gg_event_dcc7_error dcc7_error_ex;	/**< Błąd połączenia bezpośredniego ze wskaźnikiem na strukturę połączenia (\c GG_EVENT_DCC7_ERROR) */
 +	struct gg_event_dcc7_connected dcc7_connected;	/**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */
 +	struct gg_event_dcc7_pending dcc7_pending;	/**< Trwa próba połączenia bezpośredniego (\c GG_EVENT_DCC7_PENDING) */
 +	struct gg_event_dcc7_reject dcc7_reject;	/**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */
 +	struct gg_event_dcc7_accept dcc7_accept;	/**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */
 +	struct gg_event_dcc7_done dcc7_done;	/**< Zakończono połączenie bezpośrednie (\c GG_EVENT_DCC7_DONE) */
 +	struct gg_event_typing_notification typing_notification;	/**< Powiadomienie o pisaniu (\c GG_EVENT_TYPING_NOTIFICATION) */
  	struct gg_event_user_data user_data;	/**< Informacje o kontaktach */
  	struct gg_event_msg multilogon_msg;	/**< Inna sesja wysłała wiadomość (\c GG_EVENT_MULTILOGON_MSG) */
  	struct gg_event_multilogon_info multilogon_info;	/**< Informacja o innych sesjach multilogowania (\c GG_EVENT_MULTILOGON_INFO) */
 -}; - -/** - * Opis zdarzenia. - * - * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd() - * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić - * za pomocą \c gg_event_free(). - * - * \ingroup events - */ -struct gg_event { -	int type;			/**< Rodzaj zdarzenia */ -	union gg_event_union event;	/**< Informacja o zdarzeniu */ -}; - -struct gg_event *gg_watch_fd(struct gg_session *sess); -void gg_event_free(struct gg_event *e); - -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count); -int gg_notify(struct gg_session *sess, uin_t *userlist, int count); -int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type); -int gg_add_notify(struct gg_session *sess, uin_t uin); -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type); -int gg_remove_notify(struct gg_session *sess, uin_t uin); - -struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header); -int gg_http_watch_fd(struct gg_http *h); -void gg_http_stop(struct gg_http *h); -void gg_http_free(struct gg_http *h); - -uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req); -gg_pubdir50_t gg_pubdir50_new(int type); -int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value); -int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq); -const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field); -int gg_pubdir50_type(gg_pubdir50_t res); -int gg_pubdir50_count(gg_pubdir50_t res); -uin_t gg_pubdir50_next(gg_pubdir50_t res); -uint32_t gg_pubdir50_seq(gg_pubdir50_t res); -void gg_pubdir50_free(gg_pubdir50_t res); - -#ifndef DOXYGEN - -#define GG_PUBDIR50_UIN "FmNumber" -#define GG_PUBDIR50_STATUS "FmStatus" -#define GG_PUBDIR50_FIRSTNAME "firstname" -#define GG_PUBDIR50_LASTNAME "lastname" -#define GG_PUBDIR50_NICKNAME "nickname" -#define GG_PUBDIR50_BIRTHYEAR "birthyear" -#define GG_PUBDIR50_CITY "city" -#define GG_PUBDIR50_GENDER "gender" -#define GG_PUBDIR50_GENDER_FEMALE "1" -#define GG_PUBDIR50_GENDER_MALE "2" -#define GG_PUBDIR50_GENDER_SET_FEMALE "2" -#define GG_PUBDIR50_GENDER_SET_MALE "1" -#define GG_PUBDIR50_ACTIVE "ActiveOnly" -#define GG_PUBDIR50_ACTIVE_TRUE "1" -#define GG_PUBDIR50_START "fmstart" -#define GG_PUBDIR50_FAMILYNAME "familyname" -#define GG_PUBDIR50_FAMILYCITY "familycity" - -#else - -/**  - * \ingroup pubdir50 - * - * Rodzaj pola zapytania. - */ -enum { -	GG_PUBDIR50_UIN,	/**< Numer Gadu-Gadu */ -	GG_PUBDIR50_STATUS,	/**< Status (tylko wynik wyszukiwania) */ -	GG_PUBDIR50_FIRSTNAME,	/**< Imię */ -	GG_PUBDIR50_LASTNAME,	/**< Nazwisko */ -	GG_PUBDIR50_NICKNAME,	/**< Pseudonim */ -	GG_PUBDIR50_BIRTHYEAR,	/**< Rok urodzenia lub przedział lat oddzielony spacją */ -	GG_PUBDIR50_CITY,	/**< Miejscowość */ -	GG_PUBDIR50_GENDER,	/**< Płeć */ -	GG_PUBDIR50_ACTIVE,	/**< Osoba dostępna (tylko wyszukiwanie) */ -	GG_PUBDIR50_START,	/**< Numer początkowy wyszukiwania (tylko wyszukiwanie) */ -	GG_PUBDIR50_FAMILYNAME,	/**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */ -	GG_PUBDIR50_FAMILYCITY,	/**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */ -}; - -/** - * \ingroup pubdir50 - * - * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć. - */ -enum { -	GG_PUBDIR50_GENDER_FEMALE,	/**< Kobieta */ -	GG_PUBDIR50_GENDER_MALE,	/**< Mężczyzna */ -}; - -/** - * \ingroup pubdir50 - * - * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie. - */ -enum { -	GG_PUBDIR50_GENDER_SET_FEMALE,	/**< Kobieta */ -	GG_PUBDIR50_GENDER_SET_MALE,	/**< Mężczyzna */ -}; - -/** - * \ingroup pubdir50 - * - * Wartość pola GG_PUBDIR50_ACTIVE. - */ -enum { -	GG_PUBDIR50_ACTIVE_TRUE,	/**< Wyszukaj tylko osoby dostępne */ -}; - -#endif	/* DOXYGEN */ - -/** - * Wynik operacji na katalogu publicznym. - * - * \ingroup http - */ -struct gg_pubdir { -	int success;		/**< Flaga powodzenia operacji */ -	uin_t uin;		/**< Otrzymany numer lub 0 w przypadku błędu */ -}; - -int gg_pubdir_watch_fd(struct gg_http *f); -void gg_pubdir_free(struct gg_http *f); - -/** - * Token autoryzacji niektórych operacji HTTP. - *  - * \ingroup token - */ -struct gg_token { -	int width;		/**< Szerokość obrazka */ -	int height;		/**< Wysokość obrazka */ -	int length;		/**< Liczba znaków w tokenie */ -	char *tokenid;		/**< Identyfikator tokenu */ -}; - -struct gg_http *gg_token(int async); -int gg_token_watch_fd(struct gg_http *h); -void gg_token_free(struct gg_http *h); - -struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async); -#ifndef DOXYGEN -#define gg_register_watch_fd gg_pubdir_watch_fd -#define gg_register_free gg_pubdir_free -#endif - -struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async); -#ifndef DOXYGEN -#define gg_unregister_watch_fd gg_pubdir_watch_fd -#define gg_unregister_free gg_pubdir_free -#endif - -struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async); -#ifndef DOXYGEN -#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd -#define gg_remind_passwd_free gg_pubdir_free -#endif - -struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async); -#ifndef DOXYGEN -#define gg_change_passwd_watch_fd gg_pubdir_watch_fd -#define gg_change_passwd_free gg_pubdir_free -#endif - -extern int gg_dcc_port; -extern unsigned long gg_dcc_ip; - -int gg_dcc_request(struct gg_session *sess, uin_t uin); - -struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); -struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); -struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin); -void gg_dcc_set_type(struct gg_dcc *d, int type); -int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename); -int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename); -int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length); - -#define GG_DCC_VOICE_FRAME_LENGTH 195		/**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */ -#define GG_DCC_VOICE_FRAME_LENGTH_505 326	/**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */ - -struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port); -#ifndef DOXYGEN -#define gg_dcc_socket_free gg_dcc_free -#define gg_dcc_socket_watch_fd gg_dcc_watch_fd -#endif - -struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d); - -void gg_dcc_free(struct gg_dcc *c); - -struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d); -struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash); -struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash); -int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset); -int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason); -int gg_dcc7_abort(struct gg_dcc7 *dcc); -void gg_dcc7_free(struct gg_dcc7 *d); - -extern int gg_debug_level; - -extern void (*gg_debug_handler)(int level, const char *format, va_list ap); -extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap); - -extern FILE *gg_debug_file; - -/** - * \ingroup debug - * @{ - */ -#define GG_DEBUG_NET 1		/**< Rejestracja zdarzeń związanych z siecią */ -#define GG_DEBUG_TRAFFIC 2	/**< Rejestracja ruchu sieciowego */ -#define GG_DEBUG_DUMP 4		/**< Rejestracja zawartości pakietów */ -#define GG_DEBUG_FUNCTION 8	/**< Rejestracja wywołań funkcji */ -#define GG_DEBUG_MISC 16	/**< Rejestracja różnych informacji */ -/** @} */ - -#ifdef GG_DEBUG_DISABLE -#define gg_debug(x, y...) do { } while(0) -#define gg_debug_session(z, x, y...) do { } while(0) -#else -void gg_debug(int level, const char *format, ...); -void gg_debug_session(struct gg_session *sess, int level, const char *format, ...); -#endif - -const char *gg_libgadu_version(void); - -extern int gg_proxy_enabled; -extern char *gg_proxy_host; -extern int gg_proxy_port; -extern char *gg_proxy_username; -extern char *gg_proxy_password; -extern int gg_proxy_http_only; - -extern unsigned long gg_local_ip; - -#define GG_LOGIN_HASH_GG32 0x01	/**< Algorytm Gadu-Gadu */ -#define GG_LOGIN_HASH_SHA1 0x02	/**< Algorytm SHA1 */ - -#ifndef DOXYGEN - -#define GG_PUBDIR50_WRITE 0x01 -#define GG_PUBDIR50_READ 0x02 -#define GG_PUBDIR50_SEARCH 0x03 -#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH -#define GG_PUBDIR50_SEARCH_REPLY 0x05 - -#else - -/** - * \ingroup pubdir50 - *  - * Rodzaj zapytania lub odpowiedzi katalogu publicznego. - */ -enum { -	GG_PUBDIR50_WRITE,	/**< Wysłanie do serwera informacji o sobie */ -	GG_PUBDIR50_READ,	/**< Pobranie z serwera informacji o sobie */ -	GG_PUBDIR50_SEARCH,	/**< Wyszukiwanie w katalogu publicznym */ -	GG_PUBDIR50_SEARCH_REPLY,	/**< Wynik wyszukiwania w katalogu publicznym */ -}; - -#endif	/* DOXYGEN */ - -/** \cond obsolete */ - -#define gg_free_event gg_event_free -#define gg_free_http gg_http_free -#define gg_free_pubdir gg_pubdir_free -#define gg_free_register gg_pubdir_free -#define gg_free_remind_passwd gg_pubdir_free -#define gg_free_dcc gg_dcc_free -#define gg_free_change_passwd gg_pubdir_free - -struct gg_search_request { -	int active; -	unsigned int start; -	char *nickname; -	char *first_name; -	char *last_name; -	char *city; -	int gender; -	int min_birth; -	int max_birth; -	char *email; -	char *phone; -	uin_t uin; -} /* GG_DEPRECATED */; - -struct gg_search { -	int count; -	struct gg_search_result *results; -} GG_DEPRECATED; - -struct gg_search_result { -	uin_t uin; -	char *first_name; -	char *last_name; -	char *nickname; -	int born; -	int gender; -	char *city; -	int active; -} GG_DEPRECATED; - -#define GG_GENDER_NONE 0 -#define GG_GENDER_FEMALE 1 -#define GG_GENDER_MALE 2 - -struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED; -int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED; -void gg_free_search(struct gg_http *f) GG_DEPRECATED; -#define gg_search_free gg_free_search - -const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED; -const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED; -const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED; -const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED; -void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED; - -struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED; -struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED; - -struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED; -struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED; - -struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED; -struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED; - -struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED; -struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED; -struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED; - -struct gg_change_info_request { -	char *first_name; -	char *last_name; -	char *nickname; -	char *email; -	int born; -	int gender; -	char *city; -} /* GG_DEPRECATED */; - -struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED; -void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED; - -struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED; -#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd -#define gg_change_pubdir_free gg_pubdir_free -#define gg_free_change_pubdir gg_pubdir_free - -struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED; -int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED; -void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED; - -struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED; -int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED; -void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED; - -struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED; -int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED; -void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED; - -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED; - -/** \endcond */ - -int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED; - -#ifdef __GNUC__ -char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED; -#else -char *gg_saprintf(const char *format, ...) GG_DEPRECATED; -#endif - -char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED; - -#define gg_alloc_sprintf gg_saprintf - -char *gg_get_line(char **ptr) GG_DEPRECATED; - -SOCKET gg_connect(void *addr, int port, int async) GG_DEPRECATED; -#ifdef GG_CONFIG_MIRANDA +};
 +
 +/**
 + * Opis zdarzenia.
 + *
 + * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd()
 + * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić
 + * za pomocą \c gg_event_free().
 + *
 + * \ingroup events
 + */
 +struct gg_event {
 +	int type;			/**< Rodzaj zdarzenia */
 +	union gg_event_union event;	/**< Informacja o zdarzeniu */
 +};
 +
 +struct gg_event *gg_watch_fd(struct gg_session *sess);
 +void gg_event_free(struct gg_event *e);
 +
 +int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
 +int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
 +int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
 +int gg_add_notify(struct gg_session *sess, uin_t uin);
 +int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
 +int gg_remove_notify(struct gg_session *sess, uin_t uin);
 +
 +struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
 +int gg_http_watch_fd(struct gg_http *h);
 +void gg_http_stop(struct gg_http *h);
 +void gg_http_free(struct gg_http *h);
 +
 +uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
 +gg_pubdir50_t gg_pubdir50_new(int type);
 +int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
 +int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq);
 +const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field);
 +int gg_pubdir50_type(gg_pubdir50_t res);
 +int gg_pubdir50_count(gg_pubdir50_t res);
 +uin_t gg_pubdir50_next(gg_pubdir50_t res);
 +uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
 +void gg_pubdir50_free(gg_pubdir50_t res);
 +
 +#ifndef DOXYGEN
 +
 +#define GG_PUBDIR50_UIN "FmNumber"
 +#define GG_PUBDIR50_STATUS "FmStatus"
 +#define GG_PUBDIR50_FIRSTNAME "firstname"
 +#define GG_PUBDIR50_LASTNAME "lastname"
 +#define GG_PUBDIR50_NICKNAME "nickname"
 +#define GG_PUBDIR50_BIRTHYEAR "birthyear"
 +#define GG_PUBDIR50_CITY "city"
 +#define GG_PUBDIR50_GENDER "gender"
 +#define GG_PUBDIR50_GENDER_FEMALE "1"
 +#define GG_PUBDIR50_GENDER_MALE "2"
 +#define GG_PUBDIR50_GENDER_SET_FEMALE "2"
 +#define GG_PUBDIR50_GENDER_SET_MALE "1"
 +#define GG_PUBDIR50_ACTIVE "ActiveOnly"
 +#define GG_PUBDIR50_ACTIVE_TRUE "1"
 +#define GG_PUBDIR50_START "fmstart"
 +#define GG_PUBDIR50_FAMILYNAME "familyname"
 +#define GG_PUBDIR50_FAMILYCITY "familycity"
 +
 +#else
 +
 +/** 
 + * \ingroup pubdir50
 + *
 + * Rodzaj pola zapytania.
 + */
 +enum {
 +	GG_PUBDIR50_UIN,	/**< Numer Gadu-Gadu */
 +	GG_PUBDIR50_STATUS,	/**< Status (tylko wynik wyszukiwania) */
 +	GG_PUBDIR50_FIRSTNAME,	/**< Imię */
 +	GG_PUBDIR50_LASTNAME,	/**< Nazwisko */
 +	GG_PUBDIR50_NICKNAME,	/**< Pseudonim */
 +	GG_PUBDIR50_BIRTHYEAR,	/**< Rok urodzenia lub przedział lat oddzielony spacją */
 +	GG_PUBDIR50_CITY,	/**< Miejscowość */
 +	GG_PUBDIR50_GENDER,	/**< Płeć */
 +	GG_PUBDIR50_ACTIVE,	/**< Osoba dostępna (tylko wyszukiwanie) */
 +	GG_PUBDIR50_START,	/**< Numer początkowy wyszukiwania (tylko wyszukiwanie) */
 +	GG_PUBDIR50_FAMILYNAME,	/**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */
 +	GG_PUBDIR50_FAMILYCITY,	/**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */
 +};
 +
 +/**
 + * \ingroup pubdir50
 + *
 + * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć.
 + */
 +enum {
 +	GG_PUBDIR50_GENDER_FEMALE,	/**< Kobieta */
 +	GG_PUBDIR50_GENDER_MALE,	/**< Mężczyzna */
 +};
 +
 +/**
 + * \ingroup pubdir50
 + *
 + * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie.
 + */
 +enum {
 +	GG_PUBDIR50_GENDER_SET_FEMALE,	/**< Kobieta */
 +	GG_PUBDIR50_GENDER_SET_MALE,	/**< Mężczyzna */
 +};
 +
 +/**
 + * \ingroup pubdir50
 + *
 + * Wartość pola GG_PUBDIR50_ACTIVE.
 + */
 +enum {
 +	GG_PUBDIR50_ACTIVE_TRUE,	/**< Wyszukaj tylko osoby dostępne */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +/**
 + * Wynik operacji na katalogu publicznym.
 + *
 + * \ingroup http
 + */
 +struct gg_pubdir {
 +	int success;		/**< Flaga powodzenia operacji */
 +	uin_t uin;		/**< Otrzymany numer lub 0 w przypadku błędu */
 +};
 +
 +int gg_pubdir_watch_fd(struct gg_http *f);
 +void gg_pubdir_free(struct gg_http *f);
 +
 +/**
 + * Token autoryzacji niektórych operacji HTTP.
 + * 
 + * \ingroup token
 + */
 +struct gg_token {
 +	int width;		/**< Szerokość obrazka */
 +	int height;		/**< Wysokość obrazka */
 +	int length;		/**< Liczba znaków w tokenie */
 +	char *tokenid;		/**< Identyfikator tokenu */
 +};
 +
 +struct gg_http *gg_token(int async);
 +int gg_token_watch_fd(struct gg_http *h);
 +void gg_token_free(struct gg_http *h);
 +
 +struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
 +#ifndef DOXYGEN
 +#define gg_register_watch_fd gg_pubdir_watch_fd
 +#define gg_register_free gg_pubdir_free
 +#endif
 +
 +struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
 +#ifndef DOXYGEN
 +#define gg_unregister_watch_fd gg_pubdir_watch_fd
 +#define gg_unregister_free gg_pubdir_free
 +#endif
 +
 +struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
 +#ifndef DOXYGEN
 +#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
 +#define gg_remind_passwd_free gg_pubdir_free
 +#endif
 +
 +struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
 +#ifndef DOXYGEN
 +#define gg_change_passwd_watch_fd gg_pubdir_watch_fd
 +#define gg_change_passwd_free gg_pubdir_free
 +#endif
 +
 +extern int gg_dcc_port;
 +extern unsigned long gg_dcc_ip;
 +
 +int gg_dcc_request(struct gg_session *sess, uin_t uin);
 +
 +struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
 +struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
 +struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
 +void gg_dcc_set_type(struct gg_dcc *d, int type);
 +int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename);
 +int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
 +int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
 +
 +#define GG_DCC_VOICE_FRAME_LENGTH 195		/**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */
 +#define GG_DCC_VOICE_FRAME_LENGTH_505 326	/**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */
 +
 +struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
 +#ifndef DOXYGEN
 +#define gg_dcc_socket_free gg_dcc_free
 +#define gg_dcc_socket_watch_fd gg_dcc_watch_fd
 +#endif
 +
 +struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
 +
 +void gg_dcc_free(struct gg_dcc *c);
 +
 +struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
 +struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
 +struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash);
 +int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
 +int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
 +int gg_dcc7_abort(struct gg_dcc7 *dcc);
 +void gg_dcc7_free(struct gg_dcc7 *d);
 +
 +extern int gg_debug_level;
 +
 +extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
 +extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap);
 +
 +extern FILE *gg_debug_file;
 +
 +/**
 + * \ingroup debug
 + * @{
 + */
 +#define GG_DEBUG_NET 1		/**< Rejestracja zdarzeń związanych z siecią */
 +#define GG_DEBUG_TRAFFIC 2	/**< Rejestracja ruchu sieciowego */
 +#define GG_DEBUG_DUMP 4		/**< Rejestracja zawartości pakietów */
 +#define GG_DEBUG_FUNCTION 8	/**< Rejestracja wywołań funkcji */
 +#define GG_DEBUG_MISC 16	/**< Rejestracja różnych informacji */
 +/** @} */
 +
 +#ifdef GG_DEBUG_DISABLE
 +#define gg_debug(x, y...) do { } while(0)
 +#define gg_debug_session(z, x, y...) do { } while(0)
 +#else
 +void gg_debug(int level, const char *format, ...);
 +void gg_debug_session(struct gg_session *sess, int level, const char *format, ...);
 +#endif
 +
 +const char *gg_libgadu_version(void);
 +
 +extern int gg_proxy_enabled;
 +extern char *gg_proxy_host;
 +extern int gg_proxy_port;
 +extern char *gg_proxy_username;
 +extern char *gg_proxy_password;
 +extern int gg_proxy_http_only;
 +
 +extern unsigned long gg_local_ip;
 +
 +#define GG_LOGIN_HASH_GG32 0x01	/**< Algorytm Gadu-Gadu */
 +#define GG_LOGIN_HASH_SHA1 0x02	/**< Algorytm SHA1 */
 +
 +#ifndef DOXYGEN
 +
 +#define GG_PUBDIR50_WRITE 0x01
 +#define GG_PUBDIR50_READ 0x02
 +#define GG_PUBDIR50_SEARCH 0x03
 +#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
 +#define GG_PUBDIR50_SEARCH_REPLY 0x05
 +
 +#else
 +
 +/**
 + * \ingroup pubdir50
 + * 
 + * Rodzaj zapytania lub odpowiedzi katalogu publicznego.
 + */
 +enum {
 +	GG_PUBDIR50_WRITE,	/**< Wysłanie do serwera informacji o sobie */
 +	GG_PUBDIR50_READ,	/**< Pobranie z serwera informacji o sobie */
 +	GG_PUBDIR50_SEARCH,	/**< Wyszukiwanie w katalogu publicznym */
 +	GG_PUBDIR50_SEARCH_REPLY,	/**< Wynik wyszukiwania w katalogu publicznym */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +/** \cond obsolete */
 +
 +#define gg_free_event gg_event_free
 +#define gg_free_http gg_http_free
 +#define gg_free_pubdir gg_pubdir_free
 +#define gg_free_register gg_pubdir_free
 +#define gg_free_remind_passwd gg_pubdir_free
 +#define gg_free_dcc gg_dcc_free
 +#define gg_free_change_passwd gg_pubdir_free
 +
 +struct gg_search_request {
 +	int active;
 +	unsigned int start;
 +	char *nickname;
 +	char *first_name;
 +	char *last_name;
 +	char *city;
 +	int gender;
 +	int min_birth;
 +	int max_birth;
 +	char *email;
 +	char *phone;
 +	uin_t uin;
 +} /* GG_DEPRECATED */;
 +
 +struct gg_search {
 +	int count;
 +	struct gg_search_result *results;
 +} GG_DEPRECATED;
 +
 +struct gg_search_result {
 +	uin_t uin;
 +	char *first_name;
 +	char *last_name;
 +	char *nickname;
 +	int born;
 +	int gender;
 +	char *city;
 +	int active;
 +} GG_DEPRECATED;
 +
 +#define GG_GENDER_NONE 0
 +#define GG_GENDER_FEMALE 1
 +#define GG_GENDER_MALE 2
 +
 +struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED;
 +int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED;
 +void gg_free_search(struct gg_http *f) GG_DEPRECATED;
 +#define gg_search_free gg_free_search
 +
 +const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED;
 +const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED;
 +const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED;
 +const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED;
 +void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED;
 +
 +struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED;
 +struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED;
 +
 +struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED;
 +struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED;
 +
 +struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED;
 +struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED;
 +
 +struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED;
 +struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED;
 +struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
 +
 +struct gg_change_info_request {
 +	char *first_name;
 +	char *last_name;
 +	char *nickname;
 +	char *email;
 +	int born;
 +	int gender;
 +	char *city;
 +} /* GG_DEPRECATED */;
 +
 +struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED;
 +void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED;
 +
 +struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED;
 +#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
 +#define gg_change_pubdir_free gg_pubdir_free
 +#define gg_free_change_pubdir gg_pubdir_free
 +
 +struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED;
 +int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED;
 +void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED;
 +
 +struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED;
 +int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED;
 +void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED;
 +
 +struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED;
 +int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED;
 +void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED;
 +
 +int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED;
 +
 +/** \endcond */
 +
 +int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED;
 +
 +#ifdef __GNUC__
 +char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED;
 +#else
 +char *gg_saprintf(const char *format, ...) GG_DEPRECATED;
 +#endif
 +
 +char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
 +
 +#define gg_alloc_sprintf gg_saprintf
 +
 +char *gg_get_line(char **ptr) GG_DEPRECATED;
 +
 +SOCKET gg_connect(void *addr, int port, int async) GG_DEPRECATED;
 +#ifdef GG_CONFIG_MIRANDA
  SOCKET gg_connect_internal(void *addr, int port, int async, SOCKET *gg_sock);
 -#endif -struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED; -char *gg_read_line(SOCKET sock, char *buf, int length) GG_DEPRECATED; -void gg_chomp(char *line) GG_DEPRECATED; -char *gg_urlencode(const char *str) GG_DEPRECATED; -int gg_http_hash(const char *format, ...) GG_DEPRECATED; -void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED; -int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED; -int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED; -void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED; -int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED; -unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED; -void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED; -uint32_t gg_fix32(uint32_t x); -uint16_t gg_fix16(uint16_t x); -#define fix16 gg_fix16 -#define fix32 gg_fix32 -char *gg_proxy_auth(void) GG_DEPRECATED; -char *gg_base64_encode(const char *buf) GG_DEPRECATED; -char *gg_base64_decode(const char *buf) GG_DEPRECATED; -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED; - -/** - * Kolejka odbieranych obrazków. - */ -struct gg_image_queue { -	uin_t sender;			/**< Nadawca obrazka */ -	uint32_t size;			/**< Rozmiar obrazka */ -	uint32_t crc32;			/**< Suma kontrolna CRC32 */ -	char *filename;			/**< Nazwa pliku */ -	char *image;			/**< Bufor z odebranymi danymi */ -	uint32_t done;			/**< Rozmiar odebranych danych */ - -	struct gg_image_queue *next;	/**< Kolejny element listy */ -} GG_DEPRECATED; - -int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; -int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; -int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; -int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; -int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; -int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; - -#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl" -#define GG_APPMSG_PORT 80 -#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl" -#define GG_PUBDIR_PORT 80 -#define GG_REGISTER_HOST "register.gadu-gadu.pl" -#define GG_REGISTER_PORT 80 -#define GG_REMIND_HOST "retr.gadu-gadu.pl" -#define GG_REMIND_PORT 80 +#endif
 +struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
 +char *gg_read_line(SOCKET sock, char *buf, int length) GG_DEPRECATED;
 +void gg_chomp(char *line) GG_DEPRECATED;
 +char *gg_urlencode(const char *str) GG_DEPRECATED;
 +int gg_http_hash(const char *format, ...) GG_DEPRECATED;
 +void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED;
 +int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED;
 +int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED;
 +void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED;
 +int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED;
 +unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED;
 +void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED;
 +uint32_t gg_fix32(uint32_t x);
 +uint16_t gg_fix16(uint16_t x);
 +#define fix16 gg_fix16
 +#define fix32 gg_fix32
 +char *gg_proxy_auth(void) GG_DEPRECATED;
 +char *gg_base64_encode(const char *buf) GG_DEPRECATED;
 +char *gg_base64_decode(const char *buf) GG_DEPRECATED;
 +int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED;
 +
 +/**
 + * Kolejka odbieranych obrazków.
 + */
 +struct gg_image_queue {
 +	uin_t sender;			/**< Nadawca obrazka */
 +	uint32_t size;			/**< Rozmiar obrazka */
 +	uint32_t crc32;			/**< Suma kontrolna CRC32 */
 +	char *filename;			/**< Nazwa pliku */
 +	char *image;			/**< Bufor z odebranymi danymi */
 +	uint32_t done;			/**< Rozmiar odebranych danych */
 +
 +	struct gg_image_queue *next;	/**< Kolejny element listy */
 +} GG_DEPRECATED;
 +
 +int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
 +int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
 +int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
 +int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
 +int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
 +int gg_dcc7_handle_abort(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
 +
 +#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
 +#define GG_APPMSG_PORT 80
 +#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
 +#define GG_PUBDIR_PORT 80
 +#define GG_REGISTER_HOST "register.gadu-gadu.pl"
 +#define GG_REGISTER_PORT 80
 +#define GG_REMIND_HOST "retr.gadu-gadu.pl"
 +#define GG_REMIND_PORT 80
  #define GG_RELAY_HOST "relay.gadu-gadu.pl"
  #define GG_RELAY_PORT 80
 - -#define GG_DEFAULT_PORT 8074 -#define GG_HTTPS_PORT 443 -#define GG_HTTP_USERAGENT NETLIB_USER_AGENT - -#define GG_DEFAULT_CLIENT_VERSION "10.1.0.11070" -#define GG_DEFAULT_PROTOCOL_VERSION 0x2e -#define GG_DEFAULT_TIMEOUT 30 -#define GG_HAS_AUDIO_MASK 0x40000000 -#define GG_HAS_AUDIO7_MASK 0x20000000 -#define GG_ERA_OMNIX_MASK 0x04000000 -#define GG_LIBGADU_VERSION "1.10.0" - -#ifndef DOXYGEN - -#define GG_FEATURE_MSG77		0x0001 -#define GG_FEATURE_STATUS77		0x0002 -#define GG_FEATURE_UNKNOWN_4		0x0004 -#define GG_FEATURE_UNKNOWN_8		0x0008 -#define GG_FEATURE_DND_FFC		0x0010 -#define GG_FEATURE_IMAGE_DESCR		0x0020 -#define GG_FEATURE_UNKNOWN_40		0x0040 -#define GG_FEATURE_UNKNOWN_80		0x0080 -#define GG_FEATURE_UNKNOWN_100		0x0100 -#define GG_FEATURE_USER_DATA		0x0200 -#define GG_FEATURE_MSG_ACK		0x0400 -#define GG_FEATURE_UNKNOWN_800		0x0800 -#define GG_FEATURE_UNKNOWN_1000		0x1000 -#define GG_FEATURE_TYPING_NOTIFICATION	0x2000 +
 +#define GG_DEFAULT_PORT 8074
 +#define GG_HTTPS_PORT 443
 +#define GG_HTTP_USERAGENT NETLIB_USER_AGENT
 +
 +#define GG_DEFAULT_CLIENT_VERSION "10.1.0.11070"
 +#define GG_DEFAULT_PROTOCOL_VERSION 0x2e
 +#define GG_DEFAULT_TIMEOUT 30
 +#define GG_HAS_AUDIO_MASK 0x40000000
 +#define GG_HAS_AUDIO7_MASK 0x20000000
 +#define GG_ERA_OMNIX_MASK 0x04000000
 +#define GG_LIBGADU_VERSION "1.10.0"
 +
 +#ifndef DOXYGEN
 +
 +#define GG_FEATURE_MSG77		0x0001
 +#define GG_FEATURE_STATUS77		0x0002
 +#define GG_FEATURE_UNKNOWN_4		0x0004
 +#define GG_FEATURE_UNKNOWN_8		0x0008
 +#define GG_FEATURE_DND_FFC		0x0010
 +#define GG_FEATURE_IMAGE_DESCR		0x0020
 +#define GG_FEATURE_UNKNOWN_40		0x0040
 +#define GG_FEATURE_UNKNOWN_80		0x0080
 +#define GG_FEATURE_UNKNOWN_100		0x0100
 +#define GG_FEATURE_USER_DATA		0x0200
 +#define GG_FEATURE_MSG_ACK		0x0400
 +#define GG_FEATURE_UNKNOWN_800		0x0800
 +#define GG_FEATURE_UNKNOWN_1000		0x1000
 +#define GG_FEATURE_TYPING_NOTIFICATION	0x2000
  #define GG_FEATURE_MULTILOGON		0x4000
 - -/* Poniższe makra zostały zachowane dla zgodności API */ -#define GG_FEATURE_MSG80		0 -#define GG_FEATURE_STATUS80		0 -#define GG_FEATURE_STATUS80BETA		0 - -#define GG_FEATURE_ALL			(GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION) - -#else - -/**  - * \ingroup login - * - * Flagi opcji protokołu. - */ -enum { -	GG_FEATURE_MSG77,	/**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */ -	GG_FEATURE_STATUS77,	/**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */ -	GG_FEATURE_DND_FFC,	/**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */ -	GG_FEATURE_IMAGE_DESCR,	/**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */ -}; - - -#endif - -#define GG_DEFAULT_DCC_PORT 1550 - -struct gg_header { -	uint32_t type;			/* typ pakietu */ -	uint32_t length;		/* długość reszty pakietu */ -} GG_PACKED; - -#define GG_WELCOME 0x0001 -#define GG_NEED_EMAIL 0x0014 - -struct gg_welcome { -	uint32_t key;			/* klucz szyfrowania hasła */ -} GG_PACKED; - -#define GG_LOGIN 0x000c - -struct gg_login { -	uint32_t uin;			/* mój numerek */ -	uint32_t hash;			/* hash hasła */ -	uint32_t status;		/* status na dzień dobry */ -	uint32_t version;		/* moja wersja klienta */ -	uint32_t local_ip;		/* mój adres ip */ -	uint16_t local_port;		/* port, na którym słucham */ -} GG_PACKED; - -#define GG_LOGIN_EXT 0x0013 - -struct gg_login_ext { -	uint32_t uin;			/* mój numerek */ -	uint32_t hash;			/* hash hasła */ -	uint32_t status;		/* status na dzień dobry */ -	uint32_t version;		/* moja wersja klienta */ -	uint32_t local_ip;		/* mój adres ip */ -	uint16_t local_port;		/* port, na którym słucham */ -	uint32_t external_ip;		/* zewnętrzny adres ip */ -	uint16_t external_port;		/* zewnętrzny port */ -} GG_PACKED; - -#define GG_LOGIN60 0x0015 - -struct gg_login60 { -	uint32_t uin;			/* mój numerek */ -	uint32_t hash;			/* hash hasła */ -	uint32_t status;		/* status na dzień dobry */ -	uint32_t version;		/* moja wersja klienta */ -	uint8_t dunno1;			/* 0x00 */ -	uint32_t local_ip;		/* mój adres ip */ -	uint16_t local_port;		/* port, na którym słucham */ -	uint32_t external_ip;		/* zewnętrzny adres ip */ -	uint16_t external_port;		/* zewnętrzny port */ -	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */ -	uint8_t dunno2;			/* 0xbe */ -} GG_PACKED; - -#define GG_LOGIN70 0x0019 - -struct gg_login70 { -	uint32_t uin;			/* mój numerek */ -	uint8_t hash_type;		/* rodzaj hashowania hasła */ -	uint8_t hash[64];		/* hash hasła dopełniony zerami */ -	uint32_t status;		/* status na dzień dobry */ -	uint32_t version;		/* moja wersja klienta */ -	uint8_t dunno1;			/* 0x00 */ -	uint32_t local_ip;		/* mój adres ip */ -	uint16_t local_port;		/* port, na którym słucham */ -	uint32_t external_ip;		/* zewnętrzny adres ip (???) */ -	uint16_t external_port;		/* zewnętrzny port (???) */ -	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */ -	uint8_t dunno2;			/* 0xbe */ -} GG_PACKED; - -#define GG_LOGIN_OK 0x0003 - -#define GG_LOGIN_FAILED 0x0009 - -#define GG_PUBDIR50_REQUEST 0x0014 - -struct gg_pubdir50_request { -	uint8_t type;			/* GG_PUBDIR50_* */ -	uint32_t seq;			/* czas wysłania zapytania */ -} GG_PACKED; - -#define GG_PUBDIR50_REPLY 0x000e - -struct gg_pubdir50_reply { -	uint8_t type;			/* GG_PUBDIR50_* */ -	uint32_t seq;			/* czas wysłania zapytania */ -} GG_PACKED; - -#define GG_NEW_STATUS 0x0002 - -#ifndef DOXYGEN - -#define GG_STATUS_NOT_AVAIL 0x0001 -#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 -#define GG_STATUS_FFC 0x0017 -#define GG_STATUS_FFC_DESCR 0x0018 -#define GG_STATUS_AVAIL 0x0002 -#define GG_STATUS_AVAIL_DESCR 0x0004 -#define GG_STATUS_BUSY 0x0003 -#define GG_STATUS_BUSY_DESCR 0x0005 -#define GG_STATUS_DND 0x0021 -#define GG_STATUS_DND_DESCR 0x0022 -#define GG_STATUS_INVISIBLE 0x0014 -#define GG_STATUS_INVISIBLE_DESCR 0x0016 -#define GG_STATUS_BLOCKED 0x0006 - -#define GG_STATUS_IMAGE_MASK 0x0100 -#define GG_STATUS_DESCR_MASK 0x4000 -#define GG_STATUS_FRIENDS_MASK 0x8000 - -#define GG_STATUS_FLAG_UNKNOWN 0x00000001 -#define GG_STATUS_FLAG_VIDEO 0x00000002 -#define GG_STATUS_FLAG_MOBILE 0x00100000 -#define GG_STATUS_FLAG_SPAM 0x00800000 - -#else - -/** - * Rodzaje statusów użytkownika. - * - * \ingroup status - */ -enum { -	GG_STATUS_NOT_AVAIL,		/**< Niedostępny */ -	GG_STATUS_NOT_AVAIL_DESCR,	/**< Niedostępny z opisem */ -	GG_STATUS_FFC,			/**< PoGGadaj ze mną */ -	GG_STATUS_FFC_DESCR,		/**< PoGGadaj ze mną z opisem */ -	GG_STATUS_AVAIL,		/**< Dostępny */ -	GG_STATUS_AVAIL_DESCR,		/**< Dostępny z opisem */ -	GG_STATUS_BUSY,			/**< Zajęty */ -	GG_STATUS_BUSY_DESCR,		/**< Zajęty z opisem */ -	GG_STATUS_DND,			/**< Nie przeszkadzać */ -	GG_STATUS_DND_DESCR,		/**< Nie przeszakdzać z opisem */ -	GG_STATUS_INVISIBLE,		/**< Niewidoczny (tylko własny status) */ -	GG_STATUS_INVISIBLE_DESCR,	/**< Niewidoczny z opisem (tylko własny status) */ -	GG_STATUS_BLOCKED,		/**< Zablokowany (tylko status innych) */ -	GG_STATUS_IMAGE_MASK,		/**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */ -	GG_STATUS_DESCR_MASK,		/**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */ -	GG_STATUS_FRIENDS_MASK,		/**< Flaga bitowa dostępności tylko dla znajomych */ -}; - -/** - * Rodzaje statusów użytkownika. Mapa bitowa. - * - * \ingroup status - */ -enum { -	GG_STATUS_FLAG_UNKNOWN,		/**< Przeznaczenie nieznane, ale występuje zawsze */ -	GG_STATUS_FLAG_VIDEO,		/**< Klient obsługuje wideorozmowy */ -	GG_STATUS_FLAG_MOBILE,		/**< Klient mobilny (ikona telefonu komórkowego) */ -	GG_STATUS_FLAG_SPAM,		/**< Klient chce otrzymywać linki od nieznajomych */ -}; - -#endif	/* DOXYGEN */ - -/** - * \ingroup status - * - * Flaga bitowa dostepnosci informujaca ze mozemy voipowac - */ - -#define GG_STATUS_VOICE_MASK 0x20000	/**< czy ma wlaczone audio (7.7) */ - -/** - * \ingroup status - * - * Maksymalna długośc opisu. - */ -#define GG_STATUS_DESCR_MAXSIZE 255 -#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70 - -#define GG_STATUS_MASK 0xff - -/* GG_S_F() tryb tylko dla znajomych */ -#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0) - -/* GG_S() stan bez uwzględnienia dodatkowych flag */ -#define GG_S(x) ((x) & GG_STATUS_MASK) - - -/* GG_S_FF() chętny do rozmowy */ -#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR) - -/* GG_S_AV() dostępny */ -#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR) - -/* GG_S_AW() zaraz wracam */ -#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR) - -/* GG_S_DD() nie przeszkadzać */ -#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR) - -/* GG_S_NA() niedostępny */ -#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR) - -/* GG_S_I() niewidoczny */ -#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) - - -/* GG_S_A() dostępny lub chętny do rozmowy */ -#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x)) - -/* GG_S_B() zajęty lub nie przeszkadzać */ -#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x)) - - -/* GG_S_D() stan opisowy */ -#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \ -		   GG_S(x) == GG_STATUS_FFC_DESCR || \ -		   GG_S(x) == GG_STATUS_AVAIL_DESCR || \ -		   GG_S(x) == GG_STATUS_BUSY_DESCR || \ -		   GG_S(x) == GG_STATUS_DND_DESCR || \ -		   GG_S(x) == GG_STATUS_INVISIBLE_DESCR) - -/* GG_S_BL() blokowany lub blokujący */ -#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED) - -/** - * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA) - */ -struct gg_new_status { -	uint32_t status;			/**< Nowy status */ -} GG_PACKED; - -#define GG_NOTIFY_FIRST 0x000f -#define GG_NOTIFY_LAST 0x0010 - -#define GG_NOTIFY 0x0010 - -struct gg_notify { -	uint32_t uin;				/* numerek danej osoby */ -	uint8_t dunno1;				/* rodzaj wpisu w liście */ -} GG_PACKED; - -#ifndef DOXYGEN - -#define GG_USER_OFFLINE 0x01 -#define GG_USER_NORMAL 0x03 -#define GG_USER_BLOCKED 0x04 - -#else - -/** - * \ingroup contacts - * - * Rodzaj kontaktu. - */ -enum { -	GG_USER_NORMAL,		/**< Zwykły kontakt */ -	GG_USER_BLOCKED,	/**< Zablokowany */ -	GG_USER_OFFLINE,	/**< Niewidoczny dla kontaktu */ -}; - -#endif	/* DOXYGEN */ - -#define GG_LIST_EMPTY 0x0012 - -#define GG_NOTIFY_REPLY 0x000c	/* tak, to samo co GG_LOGIN */ - -struct gg_notify_reply { -	uint32_t uin;			/* numerek */ -	uint32_t status;		/* status danej osoby */ -	uint32_t remote_ip;		/* adres ip delikwenta */ -	uint16_t remote_port;		/* port, na którym słucha klient */ -	uint32_t version;		/* wersja klienta */ -	uint16_t dunno2;		/* znowu port? */ -} GG_PACKED; - -#define GG_NOTIFY_REPLY60 0x0011 - -struct gg_notify_reply60 { -	uint32_t uin;			/* numerek plus flagi w MSB */ -	uint8_t status;			/* status danej osoby */ -	uint32_t remote_ip;		/* adres ip delikwenta */ -	uint16_t remote_port;		/* port, na którym słucha klient */ -	uint8_t version;		/* wersja klienta */ -	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */ -	uint8_t dunno1;			/* 0x00 */ -} GG_PACKED; - -#define GG_STATUS60 0x000f - -struct gg_status60 { -	uint32_t uin;			/* numerek plus flagi w MSB */ -	uint8_t status;			/* status danej osoby */ -	uint32_t remote_ip;		/* adres ip delikwenta */ -	uint16_t remote_port;		/* port, na którym słucha klient */ -	uint8_t version;		/* wersja klienta */ -	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */ -	uint8_t dunno1;			/* 0x00 */ -} GG_PACKED; - -#define GG_NOTIFY_REPLY77 0x0018 - -struct gg_notify_reply77 { -	uint32_t uin;			/* numerek plus flagi w MSB */ -	uint8_t status;			/* status danej osoby */ -	uint32_t remote_ip;		/* adres ip delikwenta */ -	uint16_t remote_port;		/* port, na którym słucha klient */ -	uint8_t version;		/* wersja klienta */ -	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */ -	uint8_t dunno1;			/* 0x00 */ -	uint32_t dunno2;		/* ? */ -} GG_PACKED; - -#define GG_STATUS77 0x0017 - -struct gg_status77 { -	uint32_t uin;			/* numerek plus flagi w MSB */ -	uint8_t status;			/* status danej osoby */ -	uint32_t remote_ip;		/* adres ip delikwenta */ -	uint16_t remote_port;		/* port, na którym słucha klient */ -	uint8_t version;		/* wersja klienta */ -	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */ -	uint8_t dunno1;			/* 0x00 */ -	uint32_t dunno2;		/* ? */ -} GG_PACKED; - -#define GG_ADD_NOTIFY 0x000d -#define GG_REMOVE_NOTIFY 0x000e - -struct gg_add_remove { -	uint32_t uin;			/* numerek */ -	uint8_t dunno1;			/* bitmapa */ -} GG_PACKED; - -#define GG_STATUS 0x0002 - -struct gg_status { -	uint32_t uin;			/* numerek */ -	uint32_t status;		/* nowy stan */ -} GG_PACKED; - -#define GG_SEND_MSG 0x000b - -#ifndef DOXYGEN - -#define GG_CLASS_QUEUED 0x0001 -#define GG_CLASS_OFFLINE GG_CLASS_QUEUED -#define GG_CLASS_MSG 0x0004 -#define GG_CLASS_CHAT 0x0008 -#define GG_CLASS_CTCP 0x0010 -#define GG_CLASS_ACK 0x0020 -#define GG_CLASS_EXT GG_CLASS_ACK	/**< Dla kompatybilności wstecz */ - -#else - -/** - * Klasy wiadomości. Wartości są maskami bitowymi, które w większości - * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT - * nie ma sensu). - * - * \ingroup messages - */ -enum { -	GG_CLASS_MSG,		/**< Wiadomość ma pojawić się w osobnym oknie */ -	GG_CLASS_CHAT,		/**< Wiadomość ma pojawić się w oknie rozmowy */ -	GG_CLASS_CTCP,		/**< Wiadomość przeznaczona dla klienta Gadu-Gadu */ -	GG_CLASS_ACK,		/**< Klient nie życzy sobie potwierdzenia */ -	GG_CLASS_QUEUED,	/**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */ -}; - -#endif	/* DOXYGEN */ - -/** - * Maksymalna długość wiadomości. - * - * \ingroup messages - */ -#define GG_MSG_MAXSIZE 1989 - -struct gg_send_msg { -	uint32_t recipient; -	uint32_t seq; -	uint32_t msgclass; -} GG_PACKED; - -struct gg_msg_richtext { -	uint8_t flag; -	uint16_t length; -} GG_PACKED; - -/** - * Struktura opisująca formatowanie tekstu. W zależności od wartości pola - * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color - * lub \c gg_msg_richtext_image. - * - * \ingroup messages - */ -struct gg_msg_richtext_format { -	uint16_t position;	/**< Początkowy znak formatowania (liczony od 0) */ -	uint8_t font;		/**< Atrybuty formatowania */ -} GG_PACKED; - -#ifndef DOXYGEN - -#define GG_FONT_BOLD 0x01 -#define GG_FONT_ITALIC 0x02 -#define GG_FONT_UNDERLINE 0x04 -#define GG_FONT_COLOR 0x08 -#define GG_FONT_IMAGE 0x80 - -#else - -/** - * Atrybuty formatowania wiadomości. - * - * \ingroup messages - */ -enum { -	GG_FONT_BOLD, -	GG_FONT_ITALIC, -	GG_FONT_UNDERLINE, -	GG_FONT_COLOR, -	GG_FONT_IMAGE -}; - -#endif	/* DOXYGEN */ - -/** - * Struktura opisującą kolor tekstu dla atrybutu \c GG_FONT_COLOR. - * - * \ingroup messages - */ -struct gg_msg_richtext_color { -	uint8_t red;		/**< Składowa czerwona koloru */ -	uint8_t green;		/**< Składowa zielona koloru */ -	uint8_t blue;		/**< Składowa niebieska koloru */ -} GG_PACKED; - -/** - * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu - * \c GG_FONT_IMAGE. - * - * \ingroup messages - */ -struct gg_msg_richtext_image { -	uint16_t unknown1;	/**< Nieznane pole o wartości 0x0109 */ -	uint32_t size;		/**< Rozmiar obrazka */ -	uint32_t crc32;		/**< Suma kontrolna CRC32 obrazka */ -} GG_PACKED; - -struct gg_msg_recipients { -	uint8_t flag; -	uint32_t count; -} GG_PACKED; - -struct gg_msg_image_request { -	uint8_t flag; -	uint32_t size; -	uint32_t crc32; -} GG_PACKED; - -struct gg_msg_image_reply { -	uint8_t flag; -	uint32_t size; -	uint32_t crc32; -	/* char filename[]; */ -	/* char image[]; */ -} GG_PACKED; - -#define GG_SEND_MSG_ACK 0x0005 - -#ifndef DOXYGEN - -#define GG_ACK_BLOCKED 0x0001 -#define GG_ACK_DELIVERED 0x0002 -#define GG_ACK_QUEUED 0x0003 -#define GG_ACK_MBOXFULL 0x0004 -#define GG_ACK_NOT_DELIVERED 0x0006 - -#else - -/** - * Status doręczenia wiadomości. - * - * \ingroup messages - */ -enum -{ -	GG_ACK_DELIVERED,	/**< Wiadomość dostarczono. */ -	GG_ACK_QUEUED,		/**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */ -	GG_ACK_BLOCKED,		/**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */ -	GG_ACK_MBOXFULL,	/**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */ -	GG_ACK_NOT_DELIVERED	/**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */ -}; - -#endif	/* DOXYGEN */ - -struct gg_send_msg_ack { -	uint32_t status; -	uint32_t recipient; -	uint32_t seq; -} GG_PACKED; - -#define GG_RECV_MSG 0x000a - -struct gg_recv_msg { -	uint32_t sender; -	uint32_t seq; -	uint32_t time; -	uint32_t msgclass; -} GG_PACKED; - -#define GG_PING 0x0008 - -#define GG_PONG 0x0007 - -#define GG_DISCONNECTING 0x000b - -#define GG_USERLIST_REQUEST 0x0016 - -#define GG_XML_EVENT 0x0027 - -#ifndef DOXYGEN - -#define GG_USERLIST_PUT 0x00 -#define GG_USERLIST_PUT_MORE 0x01 -#define GG_USERLIST_GET 0x02 - -#else - -/** - * \ingroup importexport - * - * Rodzaj zapytania. - */ -enum { -	GG_USERLIST_PUT,	/**< Eksport listy kontaktów. */ -	GG_USERLIST_GET,	/**< Import listy kontaktów. */ -}; - -#endif	/* DOXYGEN */ - -struct gg_userlist_request { -	uint8_t type; -} GG_PACKED; - -#define GG_USERLIST_REPLY 0x0010 - -#ifndef DOXYGEN - -#define GG_USERLIST_PUT_REPLY 0x00 -#define GG_USERLIST_PUT_MORE_REPLY 0x02 -#define GG_USERLIST_GET_REPLY 0x06 -#define GG_USERLIST_GET_MORE_REPLY 0x04 - -#else - -/** - * \ingroup importexport - * - * Rodzaj odpowiedzi. - */ -enum { -	GG_USERLIST_PUT_REPLY,	/**< Wyeksportowano listy kontaktów. */ -	GG_USERLIST_GET_REPLY,	/**< Zaimportowano listę kontaktów. */ -}; - -#endif	/* DOXYGEN */ - -struct gg_userlist_reply { -	uint8_t type; -} GG_PACKED; - -struct gg_dcc_tiny_packet { -	uint8_t type;		/* rodzaj pakietu */ -} GG_PACKED; - -struct gg_dcc_small_packet { -	uint32_t type;		/* rodzaj pakietu */ -} GG_PACKED; - -struct gg_dcc_big_packet { -	uint32_t type;		/* rodzaj pakietu */ -	uint32_t dunno1;		/* niewiadoma */ -	uint32_t dunno2;		/* niewiadoma */ -} GG_PACKED; - -/* - * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada. - * nazwy są niepoważne i tymczasowe. - */ -#define GG_DCC_WANT_FILE 0x0003		/* peer chce plik */ -#define GG_DCC_HAVE_FILE 0x0001		/* więc mu damy */ -#define GG_DCC_HAVE_FILEINFO 0x0003	/* niech ma informacje o pliku */ -#define GG_DCC_GIMME_FILE 0x0006	/* peer jest pewny */ -#define GG_DCC_CATCH_FILE 0x0002	/* wysyłamy plik */ - -#define GG_DCC_FILEATTR_READONLY 0x0020 - -#define GG_DCC_TIMEOUT_SEND 1800	/* 30 minut */ -#define GG_DCC_TIMEOUT_GET 1800		/* 30 minut */ -#define GG_DCC_TIMEOUT_FILE_ACK 300	/* 5 minut */ -#define GG_DCC_TIMEOUT_VOICE_ACK 300	/* 5 minut */ - -#define GG_DCC7_INFO 0x1f - -struct gg_dcc7_info { -	uint32_t uin;			/* numer nadawcy */ -	uint32_t type;			/* sposób połączenia */ -	gg_dcc7_id_t id;		/* identyfikator połączenia */ -	char info[GG_DCC7_INFO_LEN];	/* informacje o połączeniu "ip port" */ +
 +/* Poniższe makra zostały zachowane dla zgodności API */
 +#define GG_FEATURE_MSG80		0
 +#define GG_FEATURE_STATUS80		0
 +#define GG_FEATURE_STATUS80BETA		0
 +
 +#define GG_FEATURE_ALL			(GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION)
 +
 +#else
 +
 +/** 
 + * \ingroup login
 + *
 + * Flagi opcji protokołu.
 + */
 +enum {
 +	GG_FEATURE_MSG77,	/**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */
 +	GG_FEATURE_STATUS77,	/**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */
 +	GG_FEATURE_DND_FFC,	/**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */
 +	GG_FEATURE_IMAGE_DESCR,	/**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */
 +};
 +
 +
 +#endif
 +
 +#define GG_DEFAULT_DCC_PORT 1550
 +
 +struct gg_header {
 +	uint32_t type;			/* typ pakietu */
 +	uint32_t length;		/* długość reszty pakietu */
 +} GG_PACKED;
 +
 +#define GG_WELCOME 0x0001
 +#define GG_NEED_EMAIL 0x0014
 +
 +struct gg_welcome {
 +	uint32_t key;			/* klucz szyfrowania hasła */
 +} GG_PACKED;
 +
 +#define GG_LOGIN 0x000c
 +
 +struct gg_login {
 +	uint32_t uin;			/* mój numerek */
 +	uint32_t hash;			/* hash hasła */
 +	uint32_t status;		/* status na dzień dobry */
 +	uint32_t version;		/* moja wersja klienta */
 +	uint32_t local_ip;		/* mój adres ip */
 +	uint16_t local_port;		/* port, na którym słucham */
 +} GG_PACKED;
 +
 +#define GG_LOGIN_EXT 0x0013
 +
 +struct gg_login_ext {
 +	uint32_t uin;			/* mój numerek */
 +	uint32_t hash;			/* hash hasła */
 +	uint32_t status;		/* status na dzień dobry */
 +	uint32_t version;		/* moja wersja klienta */
 +	uint32_t local_ip;		/* mój adres ip */
 +	uint16_t local_port;		/* port, na którym słucham */
 +	uint32_t external_ip;		/* zewnętrzny adres ip */
 +	uint16_t external_port;		/* zewnętrzny port */
 +} GG_PACKED;
 +
 +#define GG_LOGIN60 0x0015
 +
 +struct gg_login60 {
 +	uint32_t uin;			/* mój numerek */
 +	uint32_t hash;			/* hash hasła */
 +	uint32_t status;		/* status na dzień dobry */
 +	uint32_t version;		/* moja wersja klienta */
 +	uint8_t dunno1;			/* 0x00 */
 +	uint32_t local_ip;		/* mój adres ip */
 +	uint16_t local_port;		/* port, na którym słucham */
 +	uint32_t external_ip;		/* zewnętrzny adres ip */
 +	uint16_t external_port;		/* zewnętrzny port */
 +	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 +	uint8_t dunno2;			/* 0xbe */
 +} GG_PACKED;
 +
 +#define GG_LOGIN70 0x0019
 +
 +struct gg_login70 {
 +	uint32_t uin;			/* mój numerek */
 +	uint8_t hash_type;		/* rodzaj hashowania hasła */
 +	uint8_t hash[64];		/* hash hasła dopełniony zerami */
 +	uint32_t status;		/* status na dzień dobry */
 +	uint32_t version;		/* moja wersja klienta */
 +	uint8_t dunno1;			/* 0x00 */
 +	uint32_t local_ip;		/* mój adres ip */
 +	uint16_t local_port;		/* port, na którym słucham */
 +	uint32_t external_ip;		/* zewnętrzny adres ip (???) */
 +	uint16_t external_port;		/* zewnętrzny port (???) */
 +	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 +	uint8_t dunno2;			/* 0xbe */
 +} GG_PACKED;
 +
 +#define GG_LOGIN_OK 0x0003
 +
 +#define GG_LOGIN_FAILED 0x0009
 +
 +#define GG_PUBDIR50_REQUEST 0x0014
 +
 +struct gg_pubdir50_request {
 +	uint8_t type;			/* GG_PUBDIR50_* */
 +	uint32_t seq;			/* czas wysłania zapytania */
 +} GG_PACKED;
 +
 +#define GG_PUBDIR50_REPLY 0x000e
 +
 +struct gg_pubdir50_reply {
 +	uint8_t type;			/* GG_PUBDIR50_* */
 +	uint32_t seq;			/* czas wysłania zapytania */
 +} GG_PACKED;
 +
 +#define GG_NEW_STATUS 0x0002
 +
 +#ifndef DOXYGEN
 +
 +#define GG_STATUS_NOT_AVAIL 0x0001
 +#define GG_STATUS_NOT_AVAIL_DESCR 0x0015
 +#define GG_STATUS_FFC 0x0017
 +#define GG_STATUS_FFC_DESCR 0x0018
 +#define GG_STATUS_AVAIL 0x0002
 +#define GG_STATUS_AVAIL_DESCR 0x0004
 +#define GG_STATUS_BUSY 0x0003
 +#define GG_STATUS_BUSY_DESCR 0x0005
 +#define GG_STATUS_DND 0x0021
 +#define GG_STATUS_DND_DESCR 0x0022
 +#define GG_STATUS_INVISIBLE 0x0014
 +#define GG_STATUS_INVISIBLE_DESCR 0x0016
 +#define GG_STATUS_BLOCKED 0x0006
 +
 +#define GG_STATUS_IMAGE_MASK 0x0100
 +#define GG_STATUS_DESCR_MASK 0x4000
 +#define GG_STATUS_FRIENDS_MASK 0x8000
 +
 +#define GG_STATUS_FLAG_UNKNOWN 0x00000001
 +#define GG_STATUS_FLAG_VIDEO 0x00000002
 +#define GG_STATUS_FLAG_MOBILE 0x00100000
 +#define GG_STATUS_FLAG_SPAM 0x00800000
 +
 +#else
 +
 +/**
 + * Rodzaje statusów użytkownika.
 + *
 + * \ingroup status
 + */
 +enum {
 +	GG_STATUS_NOT_AVAIL,		/**< Niedostępny */
 +	GG_STATUS_NOT_AVAIL_DESCR,	/**< Niedostępny z opisem */
 +	GG_STATUS_FFC,			/**< PoGGadaj ze mną */
 +	GG_STATUS_FFC_DESCR,		/**< PoGGadaj ze mną z opisem */
 +	GG_STATUS_AVAIL,		/**< Dostępny */
 +	GG_STATUS_AVAIL_DESCR,		/**< Dostępny z opisem */
 +	GG_STATUS_BUSY,			/**< Zajęty */
 +	GG_STATUS_BUSY_DESCR,		/**< Zajęty z opisem */
 +	GG_STATUS_DND,			/**< Nie przeszkadzać */
 +	GG_STATUS_DND_DESCR,		/**< Nie przeszakdzać z opisem */
 +	GG_STATUS_INVISIBLE,		/**< Niewidoczny (tylko własny status) */
 +	GG_STATUS_INVISIBLE_DESCR,	/**< Niewidoczny z opisem (tylko własny status) */
 +	GG_STATUS_BLOCKED,		/**< Zablokowany (tylko status innych) */
 +	GG_STATUS_IMAGE_MASK,		/**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
 +	GG_STATUS_DESCR_MASK,		/**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
 +	GG_STATUS_FRIENDS_MASK,		/**< Flaga bitowa dostępności tylko dla znajomych */
 +};
 +
 +/**
 + * Rodzaje statusów użytkownika. Mapa bitowa.
 + *
 + * \ingroup status
 + */
 +enum {
 +	GG_STATUS_FLAG_UNKNOWN,		/**< Przeznaczenie nieznane, ale występuje zawsze */
 +	GG_STATUS_FLAG_VIDEO,		/**< Klient obsługuje wideorozmowy */
 +	GG_STATUS_FLAG_MOBILE,		/**< Klient mobilny (ikona telefonu komórkowego) */
 +	GG_STATUS_FLAG_SPAM,		/**< Klient chce otrzymywać linki od nieznajomych */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +/**
 + * \ingroup status
 + *
 + * Flaga bitowa dostepnosci informujaca ze mozemy voipowac
 + */
 +
 +#define GG_STATUS_VOICE_MASK 0x20000	/**< czy ma wlaczone audio (7.7) */
 +
 +/**
 + * \ingroup status
 + *
 + * Maksymalna długośc opisu.
 + */
 +#define GG_STATUS_DESCR_MAXSIZE 255
 +#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70
 +
 +#define GG_STATUS_MASK 0xff
 +
 +/* GG_S_F() tryb tylko dla znajomych */
 +#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
 +
 +/* GG_S() stan bez uwzględnienia dodatkowych flag */
 +#define GG_S(x) ((x) & GG_STATUS_MASK)
 +
 +
 +/* GG_S_FF() chętny do rozmowy */
 +#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
 +
 +/* GG_S_AV() dostępny */
 +#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
 +
 +/* GG_S_AW() zaraz wracam */
 +#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
 +
 +/* GG_S_DD() nie przeszkadzać */
 +#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR)
 +
 +/* GG_S_NA() niedostępny */
 +#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
 +
 +/* GG_S_I() niewidoczny */
 +#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
 +
 +
 +/* GG_S_A() dostępny lub chętny do rozmowy */
 +#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
 +
 +/* GG_S_B() zajęty lub nie przeszkadzać */
 +#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x))
 +
 +
 +/* GG_S_D() stan opisowy */
 +#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \
 +		   GG_S(x) == GG_STATUS_FFC_DESCR || \
 +		   GG_S(x) == GG_STATUS_AVAIL_DESCR || \
 +		   GG_S(x) == GG_STATUS_BUSY_DESCR || \
 +		   GG_S(x) == GG_STATUS_DND_DESCR || \
 +		   GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
 +
 +/* GG_S_BL() blokowany lub blokujący */
 +#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
 +
 +/**
 + * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA)
 + */
 +struct gg_new_status {
 +	uint32_t status;			/**< Nowy status */
 +} GG_PACKED;
 +
 +#define GG_NOTIFY_FIRST 0x000f
 +#define GG_NOTIFY_LAST 0x0010
 +
 +#define GG_NOTIFY 0x0010
 +
 +struct gg_notify {
 +	uint32_t uin;				/* numerek danej osoby */
 +	uint8_t dunno1;				/* rodzaj wpisu w liście */
 +} GG_PACKED;
 +
 +#ifndef DOXYGEN
 +
 +#define GG_USER_OFFLINE 0x01
 +#define GG_USER_NORMAL 0x03
 +#define GG_USER_BLOCKED 0x04
 +
 +#else
 +
 +/**
 + * \ingroup contacts
 + *
 + * Rodzaj kontaktu.
 + */
 +enum {
 +	GG_USER_NORMAL,		/**< Zwykły kontakt */
 +	GG_USER_BLOCKED,	/**< Zablokowany */
 +	GG_USER_OFFLINE,	/**< Niewidoczny dla kontaktu */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +#define GG_LIST_EMPTY 0x0012
 +
 +#define GG_NOTIFY_REPLY 0x000c	/* tak, to samo co GG_LOGIN */
 +
 +struct gg_notify_reply {
 +	uint32_t uin;			/* numerek */
 +	uint32_t status;		/* status danej osoby */
 +	uint32_t remote_ip;		/* adres ip delikwenta */
 +	uint16_t remote_port;		/* port, na którym słucha klient */
 +	uint32_t version;		/* wersja klienta */
 +	uint16_t dunno2;		/* znowu port? */
 +} GG_PACKED;
 +
 +#define GG_NOTIFY_REPLY60 0x0011
 +
 +struct gg_notify_reply60 {
 +	uint32_t uin;			/* numerek plus flagi w MSB */
 +	uint8_t status;			/* status danej osoby */
 +	uint32_t remote_ip;		/* adres ip delikwenta */
 +	uint16_t remote_port;		/* port, na którym słucha klient */
 +	uint8_t version;		/* wersja klienta */
 +	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 +	uint8_t dunno1;			/* 0x00 */
 +} GG_PACKED;
 +
 +#define GG_STATUS60 0x000f
 +
 +struct gg_status60 {
 +	uint32_t uin;			/* numerek plus flagi w MSB */
 +	uint8_t status;			/* status danej osoby */
 +	uint32_t remote_ip;		/* adres ip delikwenta */
 +	uint16_t remote_port;		/* port, na którym słucha klient */
 +	uint8_t version;		/* wersja klienta */
 +	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 +	uint8_t dunno1;			/* 0x00 */
 +} GG_PACKED;
 +
 +#define GG_NOTIFY_REPLY77 0x0018
 +
 +struct gg_notify_reply77 {
 +	uint32_t uin;			/* numerek plus flagi w MSB */
 +	uint8_t status;			/* status danej osoby */
 +	uint32_t remote_ip;		/* adres ip delikwenta */
 +	uint16_t remote_port;		/* port, na którym słucha klient */
 +	uint8_t version;		/* wersja klienta */
 +	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 +	uint8_t dunno1;			/* 0x00 */
 +	uint32_t dunno2;		/* ? */
 +} GG_PACKED;
 +
 +#define GG_STATUS77 0x0017
 +
 +struct gg_status77 {
 +	uint32_t uin;			/* numerek plus flagi w MSB */
 +	uint8_t status;			/* status danej osoby */
 +	uint32_t remote_ip;		/* adres ip delikwenta */
 +	uint16_t remote_port;		/* port, na którym słucha klient */
 +	uint8_t version;		/* wersja klienta */
 +	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 +	uint8_t dunno1;			/* 0x00 */
 +	uint32_t dunno2;		/* ? */
 +} GG_PACKED;
 +
 +#define GG_ADD_NOTIFY 0x000d
 +#define GG_REMOVE_NOTIFY 0x000e
 +
 +struct gg_add_remove {
 +	uint32_t uin;			/* numerek */
 +	uint8_t dunno1;			/* bitmapa */
 +} GG_PACKED;
 +
 +#define GG_STATUS 0x0002
 +
 +struct gg_status {
 +	uint32_t uin;			/* numerek */
 +	uint32_t status;		/* nowy stan */
 +} GG_PACKED;
 +
 +#define GG_SEND_MSG 0x000b
 +
 +#ifndef DOXYGEN
 +
 +#define GG_CLASS_QUEUED 0x0001
 +#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
 +#define GG_CLASS_MSG 0x0004
 +#define GG_CLASS_CHAT 0x0008
 +#define GG_CLASS_CTCP 0x0010
 +#define GG_CLASS_ACK 0x0020
 +#define GG_CLASS_EXT GG_CLASS_ACK	/**< Dla kompatybilności wstecz */
 +
 +#else
 +
 +/**
 + * Klasy wiadomości. Wartości są maskami bitowymi, które w większości
 + * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT
 + * nie ma sensu).
 + *
 + * \ingroup messages
 + */
 +enum {
 +	GG_CLASS_MSG,		/**< Wiadomość ma pojawić się w osobnym oknie */
 +	GG_CLASS_CHAT,		/**< Wiadomość ma pojawić się w oknie rozmowy */
 +	GG_CLASS_CTCP,		/**< Wiadomość przeznaczona dla klienta Gadu-Gadu */
 +	GG_CLASS_ACK,		/**< Klient nie życzy sobie potwierdzenia */
 +	GG_CLASS_QUEUED,	/**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +/**
 + * Maksymalna długość wiadomości.
 + *
 + * \ingroup messages
 + */
 +#define GG_MSG_MAXSIZE 1989
 +
 +struct gg_send_msg {
 +	uint32_t recipient;
 +	uint32_t seq;
 +	uint32_t msgclass;
 +} GG_PACKED;
 +
 +struct gg_msg_richtext {
 +	uint8_t flag;
 +	uint16_t length;
 +} GG_PACKED;
 +
 +/**
 + * Struktura opisująca formatowanie tekstu. W zależności od wartości pola
 + * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color
 + * lub \c gg_msg_richtext_image.
 + *
 + * \ingroup messages
 + */
 +struct gg_msg_richtext_format {
 +	uint16_t position;	/**< Początkowy znak formatowania (liczony od 0) */
 +	uint8_t font;		/**< Atrybuty formatowania */
 +} GG_PACKED;
 +
 +#ifndef DOXYGEN
 +
 +#define GG_FONT_BOLD 0x01
 +#define GG_FONT_ITALIC 0x02
 +#define GG_FONT_UNDERLINE 0x04
 +#define GG_FONT_COLOR 0x08
 +#define GG_FONT_IMAGE 0x80
 +
 +#else
 +
 +/**
 + * Atrybuty formatowania wiadomości.
 + *
 + * \ingroup messages
 + */
 +enum {
 +	GG_FONT_BOLD,
 +	GG_FONT_ITALIC,
 +	GG_FONT_UNDERLINE,
 +	GG_FONT_COLOR,
 +	GG_FONT_IMAGE
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +/**
 + * Struktura opisującą kolor tekstu dla atrybutu \c GG_FONT_COLOR.
 + *
 + * \ingroup messages
 + */
 +struct gg_msg_richtext_color {
 +	uint8_t red;		/**< Składowa czerwona koloru */
 +	uint8_t green;		/**< Składowa zielona koloru */
 +	uint8_t blue;		/**< Składowa niebieska koloru */
 +} GG_PACKED;
 +
 +/**
 + * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu
 + * \c GG_FONT_IMAGE.
 + *
 + * \ingroup messages
 + */
 +struct gg_msg_richtext_image {
 +	uint16_t unknown1;	/**< Nieznane pole o wartości 0x0109 */
 +	uint32_t size;		/**< Rozmiar obrazka */
 +	uint32_t crc32;		/**< Suma kontrolna CRC32 obrazka */
 +} GG_PACKED;
 +
 +struct gg_msg_recipients {
 +	uint8_t flag;
 +	uint32_t count;
 +} GG_PACKED;
 +
 +struct gg_msg_image_request {
 +	uint8_t flag;
 +	uint32_t size;
 +	uint32_t crc32;
 +} GG_PACKED;
 +
 +struct gg_msg_image_reply {
 +	uint8_t flag;
 +	uint32_t size;
 +	uint32_t crc32;
 +	/* char filename[]; */
 +	/* char image[]; */
 +} GG_PACKED;
 +
 +#define GG_SEND_MSG_ACK 0x0005
 +
 +#ifndef DOXYGEN
 +
 +#define GG_ACK_BLOCKED 0x0001
 +#define GG_ACK_DELIVERED 0x0002
 +#define GG_ACK_QUEUED 0x0003
 +#define GG_ACK_MBOXFULL 0x0004
 +#define GG_ACK_NOT_DELIVERED 0x0006
 +
 +#else
 +
 +/**
 + * Status doręczenia wiadomości.
 + *
 + * \ingroup messages
 + */
 +enum
 +{
 +	GG_ACK_DELIVERED,	/**< Wiadomość dostarczono. */
 +	GG_ACK_QUEUED,		/**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */
 +	GG_ACK_BLOCKED,		/**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */
 +	GG_ACK_MBOXFULL,	/**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */
 +	GG_ACK_NOT_DELIVERED	/**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +struct gg_send_msg_ack {
 +	uint32_t status;
 +	uint32_t recipient;
 +	uint32_t seq;
 +} GG_PACKED;
 +
 +#define GG_RECV_MSG 0x000a
 +
 +struct gg_recv_msg {
 +	uint32_t sender;
 +	uint32_t seq;
 +	uint32_t time;
 +	uint32_t msgclass;
 +} GG_PACKED;
 +
 +#define GG_PING 0x0008
 +
 +#define GG_PONG 0x0007
 +
 +#define GG_DISCONNECTING 0x000b
 +
 +#define GG_USERLIST_REQUEST 0x0016
 +
 +#define GG_XML_EVENT 0x0027
 +
 +#ifndef DOXYGEN
 +
 +#define GG_USERLIST_PUT 0x00
 +#define GG_USERLIST_PUT_MORE 0x01
 +#define GG_USERLIST_GET 0x02
 +
 +#else
 +
 +/**
 + * \ingroup importexport
 + *
 + * Rodzaj zapytania.
 + */
 +enum {
 +	GG_USERLIST_PUT,	/**< Eksport listy kontaktów. */
 +	GG_USERLIST_GET,	/**< Import listy kontaktów. */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +struct gg_userlist_request {
 +	uint8_t type;
 +} GG_PACKED;
 +
 +#define GG_USERLIST_REPLY 0x0010
 +
 +#ifndef DOXYGEN
 +
 +#define GG_USERLIST_PUT_REPLY 0x00
 +#define GG_USERLIST_PUT_MORE_REPLY 0x02
 +#define GG_USERLIST_GET_REPLY 0x06
 +#define GG_USERLIST_GET_MORE_REPLY 0x04
 +
 +#else
 +
 +/**
 + * \ingroup importexport
 + *
 + * Rodzaj odpowiedzi.
 + */
 +enum {
 +	GG_USERLIST_PUT_REPLY,	/**< Wyeksportowano listy kontaktów. */
 +	GG_USERLIST_GET_REPLY,	/**< Zaimportowano listę kontaktów. */
 +};
 +
 +#endif	/* DOXYGEN */
 +
 +struct gg_userlist_reply {
 +	uint8_t type;
 +} GG_PACKED;
 +
 +struct gg_dcc_tiny_packet {
 +	uint8_t type;		/* rodzaj pakietu */
 +} GG_PACKED;
 +
 +struct gg_dcc_small_packet {
 +	uint32_t type;		/* rodzaj pakietu */
 +} GG_PACKED;
 +
 +struct gg_dcc_big_packet {
 +	uint32_t type;		/* rodzaj pakietu */
 +	uint32_t dunno1;		/* niewiadoma */
 +	uint32_t dunno2;		/* niewiadoma */
 +} GG_PACKED;
 +
 +/*
 + * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada.
 + * nazwy są niepoważne i tymczasowe.
 + */
 +#define GG_DCC_WANT_FILE 0x0003		/* peer chce plik */
 +#define GG_DCC_HAVE_FILE 0x0001		/* więc mu damy */
 +#define GG_DCC_HAVE_FILEINFO 0x0003	/* niech ma informacje o pliku */
 +#define GG_DCC_GIMME_FILE 0x0006	/* peer jest pewny */
 +#define GG_DCC_CATCH_FILE 0x0002	/* wysyłamy plik */
 +
 +#define GG_DCC_FILEATTR_READONLY 0x0020
 +
 +#define GG_DCC_TIMEOUT_SEND 1800	/* 30 minut */
 +#define GG_DCC_TIMEOUT_GET 1800		/* 30 minut */
 +#define GG_DCC_TIMEOUT_FILE_ACK 300	/* 5 minut */
 +#define GG_DCC_TIMEOUT_VOICE_ACK 300	/* 5 minut */
 +
 +#define GG_DCC7_INFO 0x1f
 +
 +struct gg_dcc7_info {
 +	uint32_t uin;			/* numer nadawcy */
 +	uint32_t type;			/* sposób połączenia */
 +	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +	char info[GG_DCC7_INFO_LEN];	/* informacje o połączeniu "ip port" */
  	char hash[GG_DCC7_INFO_HASH_LEN];/* skrót "ip" */
 -} GG_PACKED; - -#define GG_DCC7_NEW 0x20 - -struct gg_dcc7_new { -	gg_dcc7_id_t id;		/* identyfikator połączenia */ -	uint32_t uin_from;		/* numer nadawcy */ -	uint32_t uin_to;		/* numer odbiorcy */ -	uint32_t type;			/* rodzaj transmisji */ -	unsigned char filename[GG_DCC7_FILENAME_LEN];	/* nazwa pliku */ -	uint32_t size;			/* rozmiar pliku */ -	uint32_t size_hi;		/* rozmiar pliku (starsze bajty) */ -	unsigned char hash[GG_DCC7_HASH_LEN];	/* hash SHA1 */ -} GG_PACKED; - -#define GG_DCC7_ACCEPT 0x21 - -struct gg_dcc7_accept { -	uint32_t uin;			/* numer przyjmującego połączenie */ -	gg_dcc7_id_t id;		/* identyfikator połączenia */ -	uint32_t offset;		/* offset przy wznawianiu transmisji */ -	uint32_t dunno1;		/* 0x00000000 */ -} GG_PACKED; - -// XXX API -#define GG_DCC7_TYPE_P2P 0x00000001	/**< Połączenie bezpośrednie */ -#define GG_DCC7_TYPE_SERVER 0x00000002	/**< Połączenie przez serwer */ - -#define GG_DCC7_REJECT 0x22 - -struct gg_dcc7_reject { -	uint32_t uin;			/**< Numer odrzucającego połączenie */ -	gg_dcc7_id_t id;		/**< Identyfikator połączenia */ -	uint32_t reason;		/**< Powód rozłączenia */ -} GG_PACKED; - -// XXX API -#define GG_DCC7_REJECT_BUSY 0x00000001	/**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */ -#define GG_DCC7_REJECT_USER 0x00000002	/**< Użytkownik odrzucił połączenie */ -#define GG_DCC7_REJECT_HIDDEN 0x00000003	/* użytkownik ojest ukryty i nie możesz mu wysłać pliku */ -#define GG_DCC7_REJECT_VERSION 0x00000006	/**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */ - -#define GG_DCC7_ID_REQUEST 0x23 - -struct gg_dcc7_id_request { -	uint32_t type;			/**< Rodzaj tranmisji */ -} GG_PACKED; - -// XXX API -#define GG_DCC7_TYPE_VOICE 0x00000001	/**< Transmisja głosu */ -#define GG_DCC7_TYPE_FILE 0x00000004	/**< transmisja pliku */ - -#define GG_DCC7_ID_REPLY 0x23 - -struct gg_dcc7_id_reply { -	uint32_t type;			/** Rodzaj transmisji */ -	gg_dcc7_id_t id;		/** Przyznany identyfikator */ -} GG_PACKED; - -/* -#define GG_DCC7_DUNNO1 0x24 - -struct gg_dcc7_dunno1 { -	// XXX -} GG_PACKED; -*/ - -#define GG_DCC7_ABORT 0x0025
 -
 -struct gg_dcc7_abort {
 +} GG_PACKED;
 +
 +#define GG_DCC7_NEW 0x20
 +
 +struct gg_dcc7_new {
  	gg_dcc7_id_t id;		/* identyfikator połączenia */
  	uint32_t uin_from;		/* numer nadawcy */
  	uint32_t uin_to;		/* numer odbiorcy */
 -} GG_PACKED; - -struct gg_dcc7_aborted { +	uint32_t type;			/* rodzaj transmisji */
 +	unsigned char filename[GG_DCC7_FILENAME_LEN];	/* nazwa pliku */
 +	uint32_t size;			/* rozmiar pliku */
 +	uint32_t size_hi;		/* rozmiar pliku (starsze bajty) */
 +	unsigned char hash[GG_DCC7_HASH_LEN];	/* hash SHA1 */
 +} GG_PACKED;
 +
 +#define GG_DCC7_ACCEPT 0x21
 +
 +struct gg_dcc7_accept {
 +	uint32_t uin;			/* numer przyjmującego połączenie */
  	gg_dcc7_id_t id;		/* identyfikator połączenia */
 -} GG_PACKED; - -#define GG_DCC7_TIMEOUT_CONNECT 10	/* 10 sekund */ -#define GG_DCC7_TIMEOUT_SEND 1800	/* 30 minut */ -#define GG_DCC7_TIMEOUT_GET 1800	/* 30 minut */ -#define GG_DCC7_TIMEOUT_FILE_ACK 300	/* 5 minut */ -#define GG_DCC7_TIMEOUT_VOICE_ACK 300	/* 5 minut */ - -#if defined(__cplusplus) || defined(_WIN32) -#ifdef _WIN32 -#pragma pack(pop) -#endif -#endif - -#endif /* __GG_LIBGADU_H */ - -/* - * Local variables: - * c-indentation-style: k&r - * c-basic-offset: 8 - * indent-tabs-mode: notnil - * End: - * - * vim: shiftwidth=8: - */ +	uint32_t offset;		/* offset przy wznawianiu transmisji */
 +	uint32_t dunno1;		/* 0x00000000 */
 +} GG_PACKED;
 +
 +// XXX API
 +#define GG_DCC7_TYPE_P2P 0x00000001	/**< Połączenie bezpośrednie */
 +#define GG_DCC7_TYPE_SERVER 0x00000002	/**< Połączenie przez serwer */
 +
 +#define GG_DCC7_REJECT 0x22
 +
 +struct gg_dcc7_reject {
 +	uint32_t uin;			/**< Numer odrzucającego połączenie */
 +	gg_dcc7_id_t id;		/**< Identyfikator połączenia */
 +	uint32_t reason;		/**< Powód rozłączenia */
 +} GG_PACKED;
 +
 +// XXX API
 +#define GG_DCC7_REJECT_BUSY 0x00000001	/**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */
 +#define GG_DCC7_REJECT_USER 0x00000002	/**< Użytkownik odrzucił połączenie */
 +#define GG_DCC7_REJECT_HIDDEN 0x00000003	/* użytkownik ojest ukryty i nie możesz mu wysłać pliku */
 +#define GG_DCC7_REJECT_VERSION 0x00000006	/**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */
 +
 +#define GG_DCC7_ID_REQUEST 0x23
 +
 +struct gg_dcc7_id_request {
 +	uint32_t type;			/**< Rodzaj tranmisji */
 +} GG_PACKED;
 +
 +// XXX API
 +#define GG_DCC7_TYPE_VOICE 0x00000001	/**< Transmisja głosu */
 +#define GG_DCC7_TYPE_FILE 0x00000004	/**< transmisja pliku */
 +
 +#define GG_DCC7_ID_REPLY 0x23
 +
 +struct gg_dcc7_id_reply {
 +	uint32_t type;			/** Rodzaj transmisji */
 +	gg_dcc7_id_t id;		/** Przyznany identyfikator */
 +} GG_PACKED;
 +
 +/*
 +#define GG_DCC7_DUNNO1 0x24
 +
 +struct gg_dcc7_dunno1 {
 +	// XXX
 +} GG_PACKED;
 +*/
 +
 +//#define GG_DCC7_ABORT 0x0025
 +//
 +//struct gg_dcc7_abort {
 +//	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +//	uint32_t uin_from;		/* numer nadawcy */
 +//	uint32_t uin_to;		/* numer odbiorcy */
 +//} GG_PACKED;
 +//
 +//struct gg_dcc7_aborted {
 +//	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +//} GG_PACKED;
 +
 +#define GG_DCC7_TIMEOUT_CONNECT 10	/* 10 sekund */
 +#define GG_DCC7_TIMEOUT_SEND 1800	/* 30 minut */
 +#define GG_DCC7_TIMEOUT_GET 1800	/* 30 minut */
 +#define GG_DCC7_TIMEOUT_FILE_ACK 300	/* 5 minut */
 +#define GG_DCC7_TIMEOUT_VOICE_ACK 300	/* 5 minut */
 +
 +#if defined(__cplusplus) || defined(_WIN32)
 +#ifdef _WIN32
 +#pragma pack(pop)
 +#endif
 +#endif
 +
 +#endif /* __GG_LIBGADU_H */
 +
 +/*
 + * Local variables:
 + * c-indentation-style: k&r
 + * c-basic-offset: 8
 + * indent-tabs-mode: notnil
 + * End:
 + *
 + * vim: shiftwidth=8:
 + */
 diff --git a/protocols/Gadu-Gadu/src/libgadu/message.cpp b/protocols/Gadu-Gadu/src/libgadu/message.cpp new file mode 100644 index 0000000000..d0872c32e9 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/message.cpp @@ -0,0 +1,664 @@ +/* + *  (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +/** + * \file message.c + * + * \brief Obsługa wiadomości + *  + * Plik zawiera funkcje dotyczące obsługi "klasy" gg_message_t, które + * w przyszłości zostaną dołączone do API. Obecnie używane są funkcje + * konwersji między tekstem z atrybutami i HTML. + */ + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif + +#ifdef _WIN32 +#include "win32.h" +#endif /* _WIN32 */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <ctype.h> + +#include "message.h" + +#if 0 + +gg_message_t *gg_message_new(void) +{ +	gg_message_t *gm; + +	gm = malloc(sizeof(gg_message_t)); + +	if (gm == NULL) +		return NULL; + +	memset(gm, 0, sizeof(gg_message_t)); + +	gm->msgclass = GG_CLASS_CHAT; +	gm->seq = (uint32_t) -1; + +	return gm; +} + +int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients, size_t recipient_count, char *text, char *html, char *attributes, size_t attributes_length, int auto_convert) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	memset(gm, 0, sizeof(gg_message_t)); +	gm->recipients = recipients; +	gm->recipient_count = recipient_count; +	gm->text = text; +	gm->html = html; +	gm->attributes = attributes; +	gm->attributes_length = attributes_length; +	gm->msgclass = msgclass; +	gm->seq = seq; +	gm->auto_convert = auto_convert; + +	return 0; +} + +void gg_message_free(gg_message_t *gm) +{ +	if (gm == NULL) { +		errno = EINVAL; +		return; +	}	 + +	free(gm->text); +	free(gm->text_converted); +	free(gm->html); +	free(gm->html_converted); +	free(gm->recipients); +	free(gm->attributes); + +	free(gm); +} + +int gg_message_set_auto_convert(gg_message_t *gm, int auto_convert) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	gm->auto_convert = !!auto_convert; + +	if (!gm->auto_convert) { +		free(gm->text_converted); +		free(gm->html_converted); +		gm->text_converted = NULL; +		gm->html_converted = NULL; +	} + +	return 0; +} + +int gg_message_get_auto_convert(gg_message_t *gm) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	return gm->auto_convert; +} + +int gg_message_set_recipients(gg_message_t *gm, const uin_t *recipients, size_t recipient_count) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	if (recipient_count >= INT_MAX / sizeof(uin_t)) { +		errno = EINVAL; +		return -1; +	}	 + +	if ((recipients == NULL) || (recipient_count == 0)) { +		free(gm->recipients); +		gm->recipients = NULL; +		gm->recipient_count = 0; +	} else { +		uin_t *tmp; + +		tmp = realloc(gm->recipients, recipient_count * sizeof(uin_t)); + +		if (tmp == NULL) +			return -1; + +		memcpy(tmp, recipients, recipient_count * sizeof(uin_t)); + +		gm->recipients = tmp; +		gm->recipient_count = recipient_count; +	} +	 +	return 0; +} + +int gg_message_set_recipient(gg_message_t *gm, uin_t recipient) +{ +	return gg_message_set_recipients(gm, &recipient, 1); +} + +int gg_message_get_recipients(gg_message_t *gm, const uin_t **recipients, size_t *recipient_count) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	if (recipients != NULL) +		*recipients = gm->recipients; + +	if (recipient_count != NULL) +		*recipient_count = gm->recipient_count; + +	return 0; +} + +uin_t gg_message_get_recipient(gg_message_t *gm) +{ +	GG_MESSAGE_CHECK(gm, (uin_t) -1); + +	if ((gm->recipients == NULL) || (gm->recipient_count < 1)) { +		// errno = XXX; +		return (uin_t) -1; +	} + +	return gm->recipients[0]; +} + +int gg_message_set_class(gg_message_t *gm, uint32_t msgclass) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	gm->msgclass = msgclass; + +	return 0; +} + +uint32_t gg_message_get_class(gg_message_t *gm) +{ +	GG_MESSAGE_CHECK(gm, (uint32_t) -1); + +	return gm->msgclass; +} + +int gg_message_set_seq(gg_message_t *gm, uint32_t seq) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	gm->seq = seq; + +	return 0; +} + +uint32_t gg_message_get_seq(gg_message_t *gm) +{ +	GG_MESSAGE_CHECK(gm, (uint32_t) -1); + +	return gm->seq; +} + +int gg_message_set_text(gg_message_t *gm, const char *text) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	if (text == NULL) { +		free(gm->text); +		gm->text = NULL; +	} else { +		char *tmp; + +		tmp = strdup(text); + +		if (tmp == NULL) +			return -1; + +		free(gm->text); +		gm->text = tmp; +	} + +	free(gm->html_converted); +	gm->html_converted = NULL; + +	return 0; +} + +const char *gg_message_get_text(gg_message_t *gm) +{ +	GG_MESSAGE_CHECK(gm, NULL); + +	if (gm->text_converted != NULL) +		return gm->text_converted; + +	if (gm->text == NULL && gm->html != NULL && gm->auto_convert) { +		size_t len; + +		free(gm->text_converted); + +		len = gg_message_html_to_text(NULL, gm->html); + +		gm->text_converted = malloc(len + 1); + +		if (gm->text_converted == NULL) +			return NULL; + +		gg_message_html_to_text(gm->text_converted, gm->html); + +		return gm->text_converted; +	} + +	return gm->text; +} + +int gg_message_set_html(gg_message_t *gm, const char *html) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	if (html == NULL) { +		free(gm->html); +		gm->html = NULL; +	} else { +		char *tmp; + +		tmp = strdup(html); + +		if (tmp == NULL) +			return -1; + +		free(gm->html); +		gm->html = tmp; +	} + +	free(gm->text_converted); +	gm->text_converted = NULL; + +	return 0; +} + +const char *gg_message_get_html(gg_message_t *gm) +{ +	GG_MESSAGE_CHECK(gm, NULL); + +	if (gm->html_converted != NULL) +		return gm->html_converted; + +	if (gm->html == NULL && gm->text != NULL && gm->auto_convert) { +		size_t len; + +		free(gm->html_converted); + +		len = gg_message_text_to_html(NULL, gm->text, gm->attributes, gm->attributes_length); + +		gm->html_converted = malloc(len + 1); + +		if (gm->html_converted == NULL) +			return NULL; + +		gg_message_text_to_html(gm->html_converted, gm->text, gm->attributes, gm->attributes_length); + +		return gm->html_converted; +	} + +	return gm->html; +} + +int gg_message_set_attributes(gg_message_t *gm, const char *attributes, size_t length) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	if (length > 0xfffd) { +		// errno = XXX; +		return -1; +	} + +	if ((attributes == NULL) || (length == 0)) { +		free(gm->attributes); +		gm->attributes = NULL; +		gm->attributes_length = 0; +	} else { +		char *tmp; + +		tmp = realloc(gm->attributes, length); + +		if (tmp == NULL) +			return -1; + +		gm->attributes = tmp; +		gm->attributes_length = length; +	} + +	free(gm->html_converted); +	gm->html_converted = NULL; + +	return 0; +} + +int gg_message_get_attributes(gg_message_t *gm, const char **attributes, size_t *attributes_length) +{ +	GG_MESSAGE_CHECK(gm, -1); + +	if (attributes != NULL) +		*attributes = gm->attributes; + +	if (attributes_length != NULL) +		*attributes_length = gm->attributes_length; + +	return 0; +} + +#endif + +/** + * \internal Dodaje tekst na koniec bufora. + *  + * \param dst Wskaźnik na bufor roboczy + * \param pos Wskaźnik na aktualne położenie w buforze roboczym + * \param src Dodawany tekst + * \param len Długość dodawanego tekstu + */ +static void gg_append(char *dst, int *pos, const void *src, int len) +{ +	if (dst != NULL) +		memcpy(&dst[*pos], src, len); + +	*pos += len; +} + +/** + * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML. + * + * \param dst Bufor wynikowy (może być \c NULL) + * \param src Tekst źródłowy w UTF-8 + * \param format Atrybuty tekstu źródłowego + * \param format_len Długość bloku atrybutów tekstu źródłowego + * + * \note Wynikowy tekst nie jest idealnym kodem HTML, ponieważ ma jak + * dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient. + * + * \note Dokleja \c \\0 na końcu bufora wynikowego. + * + * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL). + */ +size_t gg_message_text_to_html(char *dst, const char *src, const char *format, size_t format_len) +{ +	const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">"; +	const int span_len = 75; +	const char img_fmt[] = "<img name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">"; +	const int img_len = 29; +	int char_pos = 0; +	int format_idx = 0; +	unsigned char old_attr = 0; +	const unsigned char *color = (const unsigned char*) "\x00\x00\x00"; +	int len, i; +	const unsigned char *format_ = (const unsigned char*) format; + +	len = 0; + +	/* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc +	 * tak czy inaczej trzeba otworzyć <span>. */ + +	if (src[0] != 0 && (format_idx + 3 > format_len || (format_[format_idx] | (format_[format_idx + 1] << 8)) != 0)) { +		if (dst != NULL) +			sprintf(&dst[len], span_fmt, 0, 0, 0); + +		len += span_len; +	} + +	/* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek +	 * na końcu tekstu. */ + +	for (i = 0; ; i++) { +		/* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */ +		for (;;) { +			unsigned char attr; +			int attr_pos; + +			if (format_idx + 3 > format_len) +				break; + +			attr_pos = format_[format_idx] | (format_[format_idx + 1] << 8); + +			if (attr_pos != char_pos) +				break; + +			attr = format_[format_idx + 2]; + +			/* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */ + +			if (src[i] == 0) +				attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR); + +			format_idx += 3; + +			if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) { +				if (char_pos != 0) { +					if ((old_attr & GG_FONT_UNDERLINE) != 0) +						gg_append(dst, &len, "</u>", 4); + +					if ((old_attr & GG_FONT_ITALIC) != 0) +						gg_append(dst, &len, "</i>", 4); + +					if ((old_attr & GG_FONT_BOLD) != 0) +						gg_append(dst, &len, "</b>", 4); + +					if (src[i] != 0) +						gg_append(dst, &len, "</span>", 7); +				} + +				if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { +					color = &format_[format_idx]; +					format_idx += 3; +				} else { +					color = (unsigned char*) "\x00\x00\x00"; +				} + +				if (src[i] != 0) { +					if (dst != NULL) +						sprintf(&dst[len], span_fmt, color[0], color[1], color[2]); +					len += span_len; +				} +			} else if (char_pos == 0 && src[0] != 0) { +				if (dst != NULL) +					sprintf(&dst[len], span_fmt, 0, 0, 0); +				len += span_len; +			} + +			if ((attr & GG_FONT_BOLD) != 0) +				gg_append(dst, &len, "<b>", 3); + +			if ((attr & GG_FONT_ITALIC) != 0) +				gg_append(dst, &len, "<i>", 3); + +			if ((attr & GG_FONT_UNDERLINE) != 0) +				gg_append(dst, &len, "<u>", 3); + +			if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { +				if (dst != NULL) { +					sprintf(&dst[len], img_fmt, +						format_[format_idx + 9], +						format_[format_idx + 8],  +						format_[format_idx + 7], +						format_[format_idx + 6],  +						format_[format_idx + 5], +						format_[format_idx + 4], +						format_[format_idx + 3], +						format_[format_idx + 2]); +				} + +				len += img_len; +				format_idx += 10; +			} + +			old_attr = attr; +		} + +		/* Doklej znak zachowując htmlowe escapowanie. */ + +		switch (src[i]) { +			case '&': +				gg_append(dst, &len, "&", 5); +				break; +			case '<': +				gg_append(dst, &len, "<", 4); +				break; +			case '>': +				gg_append(dst, &len, ">", 4); +				break; +			case '\'': +				gg_append(dst, &len, "'", 6); +				break; +			case '\"': +				gg_append(dst, &len, """, 6); +				break; +			case '\n': +				gg_append(dst, &len, "<br>", 4); +				break; +			case '\r': +			case 0: +				break; +			default: +				if (dst != NULL) +					dst[len] = src[i]; +				len++; +		} + +		/* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */ + +		if ((src[i] & 0xc0) != 0xc0) +			char_pos++; + +		if (src[i] == 0) +			break; +	} + +	/* Zamknij tagi. */ + +	if ((old_attr & GG_FONT_UNDERLINE) != 0) +		gg_append(dst, &len, "</u>", 4); + +	if ((old_attr & GG_FONT_ITALIC) != 0) +		gg_append(dst, &len, "</i>", 4); + +	if ((old_attr & GG_FONT_BOLD) != 0) +		gg_append(dst, &len, "</b>", 4); + +	if (src[0] != 0) +		gg_append(dst, &len, "</span>", 7); + +	if (dst != NULL) +		dst[len] = 0; + +	return len; +} + +/** + * \internal Zamienia tekst w formacie HTML na czysty tekst. + * + * \param dst Bufor wynikowy (może być \c NULL) + * \param html Tekst źródłowy + * + * \note Dokleja \c \\0 na końcu bufora wynikowego. + * + * \note Funkcja służy do zachowania kompatybilności przy przesyłaniu + * wiadomości HTML do klientów, które tego formatu nie obsługują. Z tego + * powodu funkcja nie zachowuje formatowania, a jedynie usuwa tagi i + * zamienia podstawowe encje na ich odpowiedniki ASCII. + * + * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL). + */ +size_t gg_message_html_to_text(char *dst, const char *html) +{ +	const char *src, *entity, *tag; +	int in_tag, in_entity; +	size_t len; + +	len = 0; +	in_tag = 0; +	tag = NULL; +	in_entity = 0; +	entity = NULL; + +	for (src = html; *src != 0; src++) { +		if (*src == '<') { +			tag = src; +			in_tag = 1; +			continue; +		} + +		if (in_tag && (*src == '>')) { +			if (strncmp(tag, "<br", 3) == 0) { +				if (dst != NULL) +					dst[len] = '\n'; +				len++; +			} +			in_tag = 0; +			continue; +		} + +		if (in_tag) +			continue; + +		if (*src == '&') { +			in_entity = 1; +			entity = src; +			continue; +		} + +		if (in_entity && *src == ';') { +			in_entity = 0; +			if (dst != NULL) { +				if (strncmp(entity, "<", 4) == 0) +					dst[len++] = '<'; +				else if (strncmp(entity, ">", 4) == 0) +					dst[len++] = '>'; +				else if (strncmp(entity, """, 6) == 0) +					dst[len++] = '"'; +				else if (strncmp(entity, "'", 6) == 0) +					dst[len++] = '\''; +				else if (strncmp(entity, "&", 5) == 0) +					dst[len++] = '&'; +				else if (strncmp(entity, " ", 6) == 0) { +					dst[len++] = 0xc2; +					dst[len++] = 0xa0; +				} else +					dst[len++] = '?'; +			} else { +				if (strncmp(entity, " ", 6) == 0) +					len += 2; +				else +					len++; +			} + +			continue; +		} + +		if (in_entity && !(isalnum(*src) || *src == '#')) +			in_entity = 0; + +		if (in_entity) +			continue; + +		if (dst != NULL) +			dst[len] = *src; + +		len++; +	} + +	if (dst != NULL) +		dst[len] = 0; +	 +	return len; +} diff --git a/protocols/Gadu-Gadu/src/libgadu/message.h b/protocols/Gadu-Gadu/src/libgadu/message.h new file mode 100644 index 0000000000..8b4d7f67fa --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/message.h @@ -0,0 +1,56 @@ +/* + *  (C) Copyright 2009 Wojtek Kaniewski <wojtekka@irc.pl> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +#ifndef LIBGADU_MESSAGE_H +#define LIBGADU_MESSAGE_H + +#include <sys/types.h> +//#include <inttypes.h> +#include "libgadu.h" + +#if 0 + +struct gg_message { +	uin_t *recipients; +	size_t recipient_count; +	char *text; +	char *html; +	char *attributes; +	size_t attributes_length; +	uint32_t msgclass; +	uint32_t seq; + +	int auto_convert; +	char *text_converted; +	char *html_converted; +}; + +#define GG_MESSAGE_CHECK(gm, result) \ +	if ((gm) == NULL) { \ +		errno = EINVAL; \ +		return (result); \ +	} + +int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients, size_t recipient_count, char *text, char *xhtml, char *attributes, size_t attributes_length, int auto_convert); + +#endif + +size_t gg_message_html_to_text(char *dst, const char *html); +size_t gg_message_text_to_html(char *dst, const char *utf_msg, const char *format, size_t format_len); + +#endif /* LIBGADU_MESSAGE_H */ diff --git a/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp b/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp index f8fe4dc5de..0f75fd2dd2 100644 --- a/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/obsolete.cpp @@ -1,4 +1,3 @@ -/* coding: UTF-8 */
  /* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */
  /*
 @@ -35,7 +34,6 @@  #include <errno.h>
  #include "libgadu.h"
 -#include "internal.h"
  struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async)
  {
 diff --git a/protocols/Gadu-Gadu/src/libgadu/protocol.h b/protocols/Gadu-Gadu/src/libgadu/protocol.h index 5b4895c260..16bde0d424 100644 --- a/protocols/Gadu-Gadu/src/libgadu/protocol.h +++ b/protocols/Gadu-Gadu/src/libgadu/protocol.h @@ -1,166 +1,165 @@ -/* coding: UTF-8 */ -/* $Id$ */ - -/* - *  (C) Copyright 2009-2010 Jakub Zawadzki <darkjames@darkjames.ath.cx> - *                          Bartłomiej Zimoń <uzi18@o2.pl> - *                          Wojtek Kaniewski <wojtekka@irc.pl> - * - *  This program is free software; you can redistribute it and/or modify - *  it under the terms of the GNU Lesser General Public License Version - *  2.1 as published by the Free Software Foundation. - * - *  This program is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU Lesser General Public License for more details. - * - *  You should have received a copy of the GNU Lesser General Public - *  License along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - *  USA. - */ - -#ifndef LIBGADU_PROTOCOL_H -#define LIBGADU_PROTOCOL_H - -#include "libgadu.h" - -#ifdef _WIN32 -#pragma pack(push, 1) -#endif - -#define GG_LOGIN80BETA 0x0029 - -#define GG_LOGIN80 0x0031 - -#undef GG_FEATURE_STATUS80BETA -#undef GG_FEATURE_MSG80 -#undef GG_FEATURE_STATUS80 -#define GG_FEATURE_STATUS80BETA		0x01 -#define GG_FEATURE_MSG80		0x02 -#define GG_FEATURE_STATUS80 		0x05 - -#define GG8_LANG	"pl" -#define GG8_VERSION	"Gadu-Gadu Client Build " - -struct gg_login80 { -	uint32_t uin;			/* mój numerek */ -	uint8_t language[2];		/* język: GG8_LANG */ -	uint8_t hash_type;		/* rodzaj hashowania hasła */ -	uint8_t hash[64];		/* hash hasła dopełniony zerami */ -	uint32_t status;		/* status na dzień dobry */ -	uint32_t flags;			/* flagi (przeznaczenie nieznane) */ -	uint32_t features;		/* opcje protokołu (GG8_FEATURES) */ -	uint32_t local_ip;		/* mój adres ip */ -	uint16_t local_port;		/* port, na którym słucham */ -	uint32_t external_ip;		/* zewnętrzny adres ip (???) */ -	uint16_t external_port;		/* zewnętrzny port (???) */ -	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */ -	uint8_t dunno2;			/* 0x64 */ -} GG_PACKED; - -#define GG_LOGIN_HASH_TYPE_INVALID 0x0016 - -#define GG_LOGIN80_OK 0x0035 - -/** - * Logowanie powiodło się (pakiet \c GG_LOGIN80_OK) - */ -struct gg_login80_ok { -	uint32_t unknown1;		/* 0x00000001 */ -} GG_PACKED; - -/** - * Logowanie nie powiodło się (pakiet \c GG_LOGIN80_FAILED) - */ -#define GG_LOGIN80_FAILED 0x0043 - -struct gg_login80_failed { -	uint32_t unknown1;		/* 0x00000001 */ -} GG_PACKED; - -#define GG_NEW_STATUS80BETA 0x0028 - -#define GG_NEW_STATUS80 0x0038 - -/** - * Zmiana stanu (pakiet \c GG_NEW_STATUS80) - */ -struct gg_new_status80 { -	uint32_t status;			/**< Nowy status */ -	uint32_t flags;				/**< flagi (nieznane przeznaczenie) */ -	uint32_t description_size;		/**< rozmiar opisu */ -} GG_PACKED; - -#define GG_STATUS80BETA 0x002a -#define GG_NOTIFY_REPLY80BETA 0x002b - -#define GG_STATUS80 0x0036 -#define GG_NOTIFY_REPLY80 0x0037 - -struct gg_notify_reply80 { -	uint32_t uin;		/* numerek plus flagi w najstarszym bajcie */ -	uint32_t status;	/* status danej osoby */ -	uint32_t features;	/* opcje protokołu */ -	uint32_t remote_ip;	/* adres IP bezpośrednich połączeń */ -	uint16_t remote_port;	/* port bezpośrednich połączeń */ -	uint8_t image_size;	/* maksymalny rozmiar obrazków w KB */ -	uint8_t unknown1;	/* 0x00 */ -	uint32_t flags;		/* flagi połączenia */ -	uint32_t descr_len;	/* rozmiar opisu */ -} GG_PACKED; - -#define GG_SEND_MSG80 0x002d - -struct gg_send_msg80 { -	uint32_t recipient; -	uint32_t seq; -	uint32_t msgclass; -	uint32_t offset_plain; -	uint32_t offset_attr; -} GG_PACKED; - -#define GG_RECV_MSG80 0x002e - -struct gg_recv_msg80 { -	uint32_t sender; -	uint32_t seq; -	uint32_t time; -	uint32_t msgclass; -	uint32_t offset_plain; -	uint32_t offset_attr; -} GG_PACKED; - -#define GG_DISCONNECT_ACK 0x000d - -#define GG_RECV_MSG_ACK 0x0046 - -struct gg_recv_msg_ack { -	uint32_t seq; -} GG_PACKED; - -#define GG_USER_DATA 0x0044 - -struct gg_user_data { -	uint32_t type; -	uint32_t user_count; -} GG_PACKED; - -struct gg_user_data_user { -	uint32_t uin; -	uint32_t attr_count; -} GG_PACKED; - -#define GG_TYPING_NOTIFICATION 0x0059 - -struct gg_typing_notification { -	uint16_t length; -	uint32_t uin; -} GG_PACKED; - -#define GG_XML_ACTION 0x002c - +/* $Id$ */
 +
 +/*
 + *  (C) Copyright 2009-2010 Jakub Zawadzki <darkjames@darkjames.ath.cx>
 + *                          Bartłomiej Zimoń <uzi18@o2.pl>
 + *                          Wojtek Kaniewski <wojtekka@irc.pl>
 + *
 + *  This program is free software; you can redistribute it and/or modify
 + *  it under the terms of the GNU Lesser General Public License Version
 + *  2.1 as published by the Free Software Foundation.
 + *
 + *  This program is distributed in the hope that it will be useful,
 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + *  GNU Lesser General Public License for more details.
 + *
 + *  You should have received a copy of the GNU Lesser General Public
 + *  License along with this program; if not, write to the Free Software
 + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
 + *  USA.
 + */
 +
 +#ifndef LIBGADU_PROTOCOL_H
 +#define LIBGADU_PROTOCOL_H
 +
 +#include "libgadu.h"
 +
 +#ifdef _WIN32
 +#pragma pack(push, 1)
 +#endif
 +
 +#define GG_LOGIN80BETA 0x0029
 +
 +#define GG_LOGIN80 0x0031
 +
 +#undef GG_FEATURE_STATUS80BETA
 +#undef GG_FEATURE_MSG80
 +#undef GG_FEATURE_STATUS80
 +#define GG_FEATURE_STATUS80BETA		0x01
 +#define GG_FEATURE_MSG80		0x02
 +#define GG_FEATURE_STATUS80 		0x05
 +
 +#define GG8_LANG	"pl"
 +#define GG8_VERSION	"Gadu-Gadu Client Build "
 +
 +struct gg_login80 {
 +	uint32_t uin;			/* mój numerek */
 +	uint8_t language[2];		/* język: GG8_LANG */
 +	uint8_t hash_type;		/* rodzaj hashowania hasła */
 +	uint8_t hash[64];		/* hash hasła dopełniony zerami */
 +	uint32_t status;		/* status na dzień dobry */
 +	uint32_t flags;			/* flagi (przeznaczenie nieznane) */
 +	uint32_t features;		/* opcje protokołu (GG8_FEATURES) */
 +	uint32_t local_ip;		/* mój adres ip */
 +	uint16_t local_port;		/* port, na którym słucham */
 +	uint32_t external_ip;		/* zewnętrzny adres ip (???) */
 +	uint16_t external_port;		/* zewnętrzny port (???) */
 +	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 +	uint8_t dunno2;			/* 0x64 */
 +} GG_PACKED;
 +
 +#define GG_LOGIN_HASH_TYPE_INVALID 0x0016
 +
 +#define GG_LOGIN80_OK 0x0035
 +
 +/**
 + * Logowanie powiodło się (pakiet \c GG_LOGIN80_OK)
 + */
 +struct gg_login80_ok {
 +	uint32_t unknown1;		/* 0x00000001 */
 +} GG_PACKED;
 +
 +/**
 + * Logowanie nie powiodło się (pakiet \c GG_LOGIN80_FAILED)
 + */
 +#define GG_LOGIN80_FAILED 0x0043
 +
 +struct gg_login80_failed {
 +	uint32_t unknown1;		/* 0x00000001 */
 +} GG_PACKED;
 +
 +#define GG_NEW_STATUS80BETA 0x0028
 +
 +#define GG_NEW_STATUS80 0x0038
 +
 +/**
 + * Zmiana stanu (pakiet \c GG_NEW_STATUS80)
 + */
 +struct gg_new_status80 {
 +	uint32_t status;			/**< Nowy status */
 +	uint32_t flags;				/**< flagi (nieznane przeznaczenie) */
 +	uint32_t description_size;		/**< rozmiar opisu */
 +} GG_PACKED;
 +
 +#define GG_STATUS80BETA 0x002a
 +#define GG_NOTIFY_REPLY80BETA 0x002b
 +
 +#define GG_STATUS80 0x0036
 +#define GG_NOTIFY_REPLY80 0x0037
 +
 +struct gg_notify_reply80 {
 +	uint32_t uin;		/* numerek plus flagi w najstarszym bajcie */
 +	uint32_t status;	/* status danej osoby */
 +	uint32_t features;	/* opcje protokołu */
 +	uint32_t remote_ip;	/* adres IP bezpośrednich połączeń */
 +	uint16_t remote_port;	/* port bezpośrednich połączeń */
 +	uint8_t image_size;	/* maksymalny rozmiar obrazków w KB */
 +	uint8_t unknown1;	/* 0x00 */
 +	uint32_t flags;		/* flagi połączenia */
 +	uint32_t descr_len;	/* rozmiar opisu */
 +} GG_PACKED;
 +
 +#define GG_SEND_MSG80 0x002d
 +
 +struct gg_send_msg80 {
 +	uint32_t recipient;
 +	uint32_t seq;
 +	uint32_t msgclass;
 +	uint32_t offset_plain;
 +	uint32_t offset_attr;
 +} GG_PACKED;
 +
 +#define GG_RECV_MSG80 0x002e
 +
 +struct gg_recv_msg80 {
 +	uint32_t sender;
 +	uint32_t seq;
 +	uint32_t time;
 +	uint32_t msgclass;
 +	uint32_t offset_plain;
 +	uint32_t offset_attr;
 +} GG_PACKED;
 +
 +#define GG_DISCONNECT_ACK 0x000d
 +
 +#define GG_RECV_MSG_ACK 0x0046
 +
 +struct gg_recv_msg_ack {
 +	uint32_t count;
 +} GG_PACKED;
 +
 +#define GG_USER_DATA 0x0044
 +
 +struct gg_user_data {
 +	uint32_t type;
 +	uint32_t user_count;
 +} GG_PACKED;
 +
 +struct gg_user_data_user {
 +	uint32_t uin;
 +	uint32_t attr_count;
 +} GG_PACKED;
 +
 +#define GG_TYPING_NOTIFICATION 0x0059
 +
 +struct gg_typing_notification {
 +	uint16_t length;
 +	uint32_t uin;
 +} GG_PACKED;
 +
 +#define GG_XML_ACTION 0x002c
 +
  #define GG_RECV_OWN_MSG 0x005a
  #define GG_MULTILOGON_INFO 0x005b
 @@ -183,95 +182,119 @@ struct gg_multilogon_info_item {  struct gg_multilogon_disconnect {
  	gg_multilogon_id_t conn_id;
 -} GG_PACKED; - -#define GG_DCC7_VOICE_RETRIES 0x11	/* 17 powtorzen */ - -#define GG_DCC7_RESERVED1		0xdeadc0de -#define GG_DCC7_RESERVED2		0xdeadbeaf - -struct gg_dcc7_voice_auth { -	uint8_t type;			/* 0x00 -> wysylanie ID -					   0x01 -> potwierdzenie ID -					*/ -	gg_dcc7_id_t id;		/* identyfikator połączenia */ -	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */ -	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */ -} GG_PACKED; - -struct gg_dcc7_voice_nodata {	/* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */ -	uint8_t type;			/* 0x02 */ -	gg_dcc7_id_t id;		/* identyfikator połączenia */ -	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */ -	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */ -} GG_PACKED; - -struct gg_dcc7_voice_data { -	uint8_t type;			/* 0x03 */ -	uint32_t did;			/* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */ -	uint32_t len;			/* rozmiar strukturki - 1 (sizeof(type)) */ -	uint32_t packet_id;		/* numerek pakietu */ -	uint32_t datalen;		/* rozmiar danych */ -	/* char data[]; */		/* ramki: albo gsm, albo speex, albo melp, albo inne. */ -} GG_PACKED; - -struct gg_dcc7_voice_init { -	uint8_t type;			/* 0x04 */ -	uint32_t id;			/* nr kroku [0x1 - 0x5] */ -	uint32_t protocol;		/* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */ -	uint32_t len;			/* rozmiar sizeof(protocol)+sizeof(len)+sizeof(data) = 0x08 + sizeof(data) */ -	/* char data[]; */		/* reszta danych */ -} GG_PACKED; - -struct gg_dcc7_voice_init_confirm { -	uint8_t type;			/* 0x05 */ -	uint32_t id;			/* id tego co potwierdzamy [0x1 - 0x5] */ -} GG_PACKED; - -#define GG_DCC7_RELAY_TYPE_SERVER 0x01	/* adres serwera, na który spytać o proxy */ -#define GG_DCC7_RELAY_TYPE_PROXY 0x08	/* adresy proxy, na które sie łączyć */ - -#define GG_DCC7_RELAY_DUNNO1 0x02 - -#define GG_DCC7_RELAY_REQUEST 0x0a - -struct gg_dcc7_relay_req { -	uint32_t magic;			/* 0x0a */ -	uint32_t len;			/* długość całego pakietu */ -	gg_dcc7_id_t id;   		/* identyfikator połączenia */ -	uint16_t type;   		/* typ zapytania */ -	uint16_t dunno1;		/* 0x02 */ -} GG_PACKED; - -#define GG_DCC7_RELAY_REPLY_RCOUNT 0x02 - -#define GG_DCC7_RELAY_REPLY 0x0b - -struct gg_dcc7_relay_reply { -	uint32_t magic;			/* 0x0b */ -	uint32_t len;			/* długość całego pakietu */ -	uint32_t rcount;		/* ilość serwerów */ -} GG_PACKED; - -struct gg_dcc7_relay_reply_server { -	uint32_t addr;		/* adres ip serwera */ -	uint16_t port;		/* port serwera */ -	uint8_t family;		/* rodzina adresów (na końcu?!) AF_INET=2 */ -} GG_PACKED; - -#define GG_DCC7_WELCOME_SERVER 0xc0debabe - -struct gg_dcc7_welcome_server { -	uint32_t magic;			/* 0xc0debabe */ -	gg_dcc7_id_t id;		/* identyfikator połączenia */ -} GG_PACKED; - -struct gg_dcc7_welcome_p2p { -	gg_dcc7_id_t id;		/* identyfikator połączenia */ -} GG_PACKED; - -#ifdef _WIN32 -#pragma pack(pop) -#endif - -#endif /* LIBGADU_PROTOCOL_H */ +} GG_PACKED;
 +
 +#define GG_MSG_CALLBACK 0x02	/**< Żądanie zwrotnego połączenia bezpośredniego */
 +
 +#define GG_MSG_OPTION_CONFERENCE 0x01
 +#define GG_MSG_OPTION_ATTRIBUTES 0x02
 +#define GG_MSG_OPTION_IMAGE_REQUEST 0x04
 +#define GG_MSG_OPTION_IMAGE_REPLY 0x05
 +#define GG_MSG_OPTION_IMAGE_REPLY_MORE 0x06
 +
 +#define GG_DCC7_ABORT 0x0025
 +
 +struct gg_dcc7_abort {
 +	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +	uint32_t uin_from;		/* numer nadawcy */
 +	uint32_t uin_to;		/* numer odbiorcy */
 +} GG_PACKED;
 +
 +#define GG_DCC7_ABORTED 0x0025
 +
 +struct gg_dcc7_aborted {
 +	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +} GG_PACKED;
 +
 +#define GG_DCC7_VOICE_RETRIES 0x11	/* 17 powtorzen */
 +
 +#define GG_DCC7_RESERVED1		0xdeadc0de
 +#define GG_DCC7_RESERVED2		0xdeadbeaf
 +
 +struct gg_dcc7_voice_auth {
 +	uint8_t type;			/* 0x00 -> wysylanie ID
 +					   0x01 -> potwierdzenie ID
 +					*/
 +	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
 +	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
 +} GG_PACKED;
 +
 +struct gg_dcc7_voice_nodata {	/* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */
 +	uint8_t type;			/* 0x02 */
 +	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
 +	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
 +} GG_PACKED;
 +
 +struct gg_dcc7_voice_data {
 +	uint8_t type;			/* 0x03 */
 +	uint32_t did;			/* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */
 +	uint32_t len;			/* rozmiar strukturki - 1 (sizeof(type)) */
 +	uint32_t packet_id;		/* numerek pakietu */
 +	uint32_t datalen;		/* rozmiar danych */
 +	/* char data[]; */		/* ramki: albo gsm, albo speex, albo melp, albo inne. */
 +} GG_PACKED;
 +
 +struct gg_dcc7_voice_init {
 +	uint8_t type;			/* 0x04 */
 +	uint32_t id;			/* nr kroku [0x1 - 0x5] */
 +	uint32_t protocol;		/* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */
 +	uint32_t len;			/* rozmiar sizeof(protocol)+sizeof(len)+sizeof(data) = 0x08 + sizeof(data) */
 +	/* char data[]; */		/* reszta danych */
 +} GG_PACKED;
 +
 +struct gg_dcc7_voice_init_confirm {
 +	uint8_t type;			/* 0x05 */
 +	uint32_t id;			/* id tego co potwierdzamy [0x1 - 0x5] */
 +} GG_PACKED;
 +
 +#define GG_DCC7_RELAY_TYPE_SERVER 0x01	/* adres serwera, na który spytać o proxy */
 +#define GG_DCC7_RELAY_TYPE_PROXY 0x08	/* adresy proxy, na które sie łączyć */
 +
 +#define GG_DCC7_RELAY_DUNNO1 0x02
 +
 +#define GG_DCC7_RELAY_REQUEST 0x0a
 +
 +struct gg_dcc7_relay_req {
 +	uint32_t magic;			/* 0x0a */
 +	uint32_t len;			/* długość całego pakietu */
 +	gg_dcc7_id_t id;   		/* identyfikator połączenia */
 +	uint16_t type;   		/* typ zapytania */
 +	uint16_t dunno1;		/* 0x02 */
 +} GG_PACKED;
 +
 +#define GG_DCC7_RELAY_REPLY_RCOUNT 0x02
 +
 +#define GG_DCC7_RELAY_REPLY 0x0b
 +
 +struct gg_dcc7_relay_reply {
 +	uint32_t magic;			/* 0x0b */
 +	uint32_t len;			/* długość całego pakietu */
 +	uint32_t rcount;		/* ilość serwerów */
 +} GG_PACKED;
 +
 +struct gg_dcc7_relay_reply_server {
 +	uint32_t addr;		/* adres ip serwera */
 +	uint16_t port;		/* port serwera */
 +	uint8_t family;		/* rodzina adresów (na końcu?!) AF_INET=2 */
 +} GG_PACKED;
 +
 +#define GG_DCC7_WELCOME_SERVER 0xc0debabe
 +
 +struct gg_dcc7_welcome_server {
 +	uint32_t magic;			/* 0xc0debabe */
 +	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +} GG_PACKED;
 +
 +struct gg_dcc7_welcome_p2p {
 +	gg_dcc7_id_t id;		/* identyfikator połączenia */
 +} GG_PACKED;
 +
 +#define GG_TIMEOUT_DISCONNECT 5	/**< Maksymalny czas oczekiwania na rozłączenie */
 +
 +#ifdef _WIN32
 +#pragma pack(pop)
 +#endif
 +
 +#endif /* LIBGADU_PROTOCOL_H */
 diff --git a/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp b/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp index 4255192923..96e07c9b53 100644 --- a/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/pubdir50.cpp @@ -1,4 +1,3 @@ -/* coding: UTF-8 */
  /* $Id: pubdir50.c 11370 2010-03-13 16:17:54Z dezred $ */
  /*
 @@ -23,6 +22,9 @@   * \file pubdir50.c
   *
   * \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
 + *
 + * \todo Zoptymalizować konwersję CP1250<->UTF8. Obecnie robiona jest
 + * testowa konwersja, żeby poznać długość tekstu wynikowego.
   */
  #ifndef _WIN64
 @@ -41,6 +43,7 @@  #include "libgadu.h"
  #include "internal.h"
 +#include "encoding.h"
  /**
   * Tworzy nowe zapytanie katalogu publicznego.
 @@ -235,7 +238,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)  		} else {
  			char *tmp;
 -			tmp = gg_utf8_to_cp(req->entries[i].field);
 +			tmp = gg_encoding_convert(req->entries[i].field, sess->encoding, GG_ENCODING_CP1250, -1, -1);
  			if (tmp == NULL)
  				return -1;
 @@ -244,7 +247,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)  			free(tmp);
 -			tmp = gg_utf8_to_cp(req->entries[i].value);
 +			tmp = gg_encoding_convert(req->entries[i].value, sess->encoding, GG_ENCODING_CP1250, -1, -1);
  			if (tmp == NULL)
  				return -1;
 @@ -282,7 +285,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)  		} else {
  			char *tmp;
 -			tmp = gg_utf8_to_cp(req->entries[i].field);
 +			tmp = gg_encoding_convert(req->entries[i].field, sess->encoding, GG_ENCODING_CP1250, -1, -1);
  			if (tmp == NULL) {
  				free(buf);
 @@ -293,7 +296,7 @@ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)  			p += strlen(tmp) + 1;
  			free(tmp);
 -			tmp = gg_utf8_to_cp(req->entries[i].value);
 +			tmp = gg_encoding_convert(req->entries[i].value, sess->encoding, GG_ENCODING_CP1250, -1, -1);
  			if (tmp == NULL) {
  				free(buf);
 @@ -427,7 +430,7 @@ int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, c  			} else {
  				char *tmp;
 -				tmp = gg_cp_to_utf8(value);
 +				tmp = gg_encoding_convert(value, GG_ENCODING_CP1250, sess->encoding, -1, -1);
  				if (tmp == NULL)
  					goto failure;
 diff --git a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp index 9b3ab79147..065169007f 100644 --- a/protocols/Gadu-Gadu/src/libgadu/resolver.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/resolver.cpp @@ -1,6 +1,3 @@ -/* coding: UTF-8 */ -/* $Id$ */ -  /*   *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>   *                          Robert J. Woźny <speedy@ziew.org> @@ -51,6 +48,7 @@  #include "libgadu.h"  #include "resolver.h"  #include "compat.h" +#include "session.h"  /** Sposób rozwiązywania nazw serwerów */  static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; @@ -88,15 +86,17 @@ static void gg_gethostbyname_cleaner(void *data)   * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.   *   * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli - * nie, to zwykłej \c gethostbyname. + * nie, to zwykłej \c gethostbyname. Wynikiem jest tablica adresów zakończona + * wartością INADDR_NONE, którą należy zwolnić po użyciu.   *   * \param hostname Nazwa serwera - * \param addr Wskaźnik na rezultat rozwiązywania nazwy + * \param result Wskaźnik na wskaźnik z tablicą adresów zakończoną INADDR_NONE + * \param count Wskaźnik na zmienną, do ktorej zapisze się liczbę wyników   * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ -int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthread) +int gg_gethostbyname_real(const char *hostname, struct in_addr **result, int *count, int pthread)  {  #ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R  	char *buf = NULL; @@ -104,13 +104,18 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea  	struct hostent he;  	struct hostent *he_ptr = NULL;  	size_t buf_len = 1024; -	int result = -1; +	int res = -1;  	int h_errnop;  	int ret = 0;  #ifdef GG_CONFIG_HAVE_PTHREAD  	int old_state;  #endif +	if (result == NULL) { +		errno = EINVAL; +		return -1; +	} +  #ifdef GG_CONFIG_HAVE_PTHREAD  	pthread_cleanup_push(gg_gethostbyname_cleaner, &buf); @@ -154,9 +159,41 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea  			}  		} -		if (ret == 0 && he_ptr != NULL) { -			memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr)); -			result = 0; +		if (ret == 0 && he_ptr != NULL && he_ptr->h_addr_list[0] != NULL) { +			int i; + +			/* Policz liczbę adresów */ + +			for (i = 0; he_ptr->h_addr_list[i] != NULL; i++) +				; + +			/* Zaalokuj */ + +#ifdef GG_CONFIG_HAVE_PTHREAD +			if (pthread) +				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); +#endif + +			*result = malloc((i + 1) * sizeof(struct in_addr)); + +#ifdef GG_CONFIG_HAVE_PTHREAD +			if (pthread) +				pthread_setcancelstate(old_state, NULL); +#endif + +			if (*result == NULL) +				return -1; + +			/* Kopiuj */ + +			for (i = 0; he_ptr->h_addr_list[i] != NULL; i++) +				memcpy(&((*result)[i]), he_ptr->h_addr_list[i], sizeof(struct in_addr)); + +			(*result)[i].s_addr = INADDR_NONE; + +			*count = i; + +			res = 0;  		}  #ifdef GG_CONFIG_HAVE_PTHREAD @@ -177,26 +214,91 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea  	pthread_cleanup_pop(1);  #endif -	return result; -#else +	return res; +#else /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */  	struct hostent *he; +	int i; + +	if (result == NULL || count == NULL) { +		errno = EINVAL; +		return -1; +	}  	he = gethostbyname(hostname); -	if (he == NULL) +	if (he == NULL || he->h_addr_list[0] == NULL) +		return -1; + +	/* Policz liczbę adresów */ + +	for (i = 0; he->h_addr_list[i] != NULL; i++) +		; + +	/* Zaalokuj */ + +	*result = (in_addr*)malloc((i + 1) * sizeof(struct in_addr)); + +	if (*result == NULL)  		return -1; -	memcpy(addr, he->h_addr, sizeof(struct in_addr)); +	/* Kopiuj */ + +	for (i = 0; he->h_addr_list[i] != NULL; i++) +		memcpy(&((*result)[i]), he->h_addr_list[0], sizeof(struct in_addr)); + +	(*result)[i].s_addr = INADDR_NONE; + +	*count = i;  	return 0;  #endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */  }  /** + * \internal Rozwiązuje nazwę i zapisuje wynik do podanego desktyptora. + * + * \param fd Deskryptor + * \param hostname Nazwa serwera + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_resolver_run(int fd, const char *hostname) +{ +	struct in_addr addr_ip[2], *addr_list; +	int addr_count; +	int res = 0; + +	gg_debug(GG_DEBUG_MISC, "// gg_resolver_run(%d, %s)\n", fd, hostname); + +	if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) { +		if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 1) == -1) { +			addr_list = addr_ip; +			/* addr_ip[0] już zawiera INADDR_NONE */ +		} +	} else { +		addr_list = addr_ip; +		addr_ip[1].s_addr = INADDR_NONE; +		addr_count = 1; +	} + +	gg_debug(GG_DEBUG_MISC, "// gg_resolver_run() count = %d\n", addr_count); + +	if (write(fd, addr_list, (addr_count + 1) * sizeof(struct in_addr)) != (addr_count + 1) * sizeof(struct in_addr)) +		res = -1; + +	if (addr_list != addr_ip) +		free(addr_list); + +	return res; +} + +/**   * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.   *   * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli - * nie, to zwykłej \c gethostbyname. + * nie, to zwykłej \c gethostbyname. Funkcja służy do zachowania zgodności + * ABI i służy do pobierania tylko pierwszego adresu -- pozostałe mogą + * zostać zignorowane przez aplikację.   *   * \param hostname Nazwa serwera   * @@ -204,16 +306,13 @@ int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthrea   */  struct in_addr *gg_gethostbyname(const char *hostname)  { -	struct in_addr *addr; +	struct in_addr *result; +	int count; -	if (!(addr = (in_addr*)malloc(sizeof(struct in_addr)))) +	if (gg_gethostbyname_real(hostname, &result, &count, 0) == -1)  		return NULL; -	if (gg_gethostbyname_real(hostname, addr, 0)) { -		free(addr); -		return NULL; -	} -	return addr; +	return result;  }  /** @@ -243,7 +342,6 @@ struct gg_resolver_fork_data {  static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *hostname)  {  	struct gg_resolver_fork_data *data = NULL; -	struct in_addr addr;  	int new_errno;  	SOCKET pipes[2]; @@ -278,16 +376,9 @@ static int gg_resolver_fork_start(SOCKET *fd, void **priv_data, const char *host  	if (data->pid == 0) {  		gg_sock_close(pipes[0]); -		if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { -			/* W przypadku błędu gg_gethostbyname_real() zwróci -1 -                         * i nie zmieni &addr. Tam jest już INADDR_NONE, -                         * więc nie musimy robić nic więcej. */ -			gg_gethostbyname_real(hostname, &addr, 0); -		} - -		if (gg_sock_write(pipes[1], &addr, sizeof(addr)) != sizeof(addr)) +		if (gg_resolver_run(pipes[1], hostname) == -1)  			exit(1); - +		else  		exit(0);  	} @@ -320,7 +411,7 @@ cleanup:   *                  danych   * \param force Flaga usuwania zasobów przed zakończeniem działania   */ -static void gg_resolver_fork_cleanup(void **priv_data, int force) +void gg_resolver_fork_cleanup(void **priv_data, int force)  {  	struct gg_resolver_fork_data *data; @@ -394,21 +485,13 @@ static void gg_resolver_pthread_cleanup(void **priv_data, int force)  static void *__stdcall gg_resolver_pthread_thread(void *arg)  {  	struct gg_resolver_pthread_data *data = (gg_resolver_pthread_data*)arg; -	struct in_addr addr;  	pthread_detach(pthread_self()); -	if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) { -		/* W przypadku błędu gg_gethostbyname_real() zwróci -1 -                 * i nie zmieni &addr. Tam jest już INADDR_NONE, -                 * więc nie musimy robić nic więcej. */ -		gg_gethostbyname_real(data->hostname, &addr, 1); -	} - -	if (gg_sock_write(data->wfd, &addr, sizeof(addr)) == sizeof(addr)) -		pthread_exit(NULL); -	else  +	if (gg_resolver_run(data->wfd, data->hostname) == -1)  		pthread_exit((void*) -1); +	else +		pthread_exit(NULL);  	return NULL;	/* żeby kompilator nie marudził */  } @@ -480,10 +563,10 @@ static int gg_resolver_pthread_start(SOCKET *fd, void **priv_data, const char *h  	return 0;  cleanup: -	if (data) { +	if (data != NULL)  		free(data->hostname); -		free(data); -	} + +	free(data);  	gg_sock_close(pipes[0]);  	gg_sock_close(pipes[1]); @@ -505,10 +588,7 @@ cleanup:   */  int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)  { -	if (gs == NULL) { -		errno = EINVAL; -		return -1; -	} +	GG_SESSION_CHECK(gs, -1);  	if (type == GG_RESOLVER_DEFAULT) {  		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { @@ -555,10 +635,7 @@ int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)   */  gg_resolver_t gg_session_get_resolver(struct gg_session *gs)  { -	if (gs == NULL) { -		errno = EINVAL; -		return GG_RESOLVER_INVALID; -	} +	GG_SESSION_CHECK(gs, (gg_resolver_t) -1);  	return gs->resolver_type;  } @@ -570,11 +647,32 @@ gg_resolver_t gg_session_get_resolver(struct gg_session *gs)   * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy   * \param resolver_cleanup Funkcja zwalniająca zasoby   * + * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco: + *  - \c "SOCKET *fd" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku + *  - \c "void **priv_data" — wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy + *  - \c "const char *name" — nazwa serwera do rozwiązania + * + * Parametry funkcji zwalniającej zasoby wyglądają następująco: + *  - \c "void **priv_data" — wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu + *  - \c "int force" — flaga mówiąca o tym, że zasoby są zwalniane przed zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji. + * + * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub + * inny deskryptor pozwalający na co najmniej jednostronną komunikację i  + * przekazać go w parametrze \c fd. Po zakończeniu rozwiązywania nazwy, + * powinien wysłać otrzymany adres IP w postaci sieciowej (big-endian) do + * deskryptora. Jeśli rozwiązywanie nazwy się nie powiedzie, należy wysłać + * \c INADDR_NONE. Następnie zostanie wywołana funkcja zwalniająca zasoby + * z parametrem \c force równym \c 0. Gdyby sesja została zakończona przed + * rozwiązaniem nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja + * zwalniająca zasoby zostanie wywołana z parametrem \c force równym \c 1. + *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */  int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int))  { -	if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) { +	GG_SESSION_CHECK(gs, -1); + +	if (resolver_start == NULL || resolver_cleanup == NULL) {  		errno = EINVAL;  		return -1;  	} @@ -729,24 +827,7 @@ gg_resolver_t gg_global_get_resolver(void)   * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy   * \param resolver_cleanup Funkcja zwalniająca zasoby   * - * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco: - *  - \c "SOCKET *fd" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku - *  - \c "void **priv_data" — wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy - *  - \c "const char *name" — nazwa serwera do rozwiązania - * - * Parametry funkcji zwalniającej zasoby wyglądają następująco: - *  - \c "void **priv_data" — wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu - *  - \c "int force" — flaga mówiąca o tym, że zasoby są zwalniane przed zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji. - * - * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub - * inny deskryptor pozwalający na co najmniej jednostronną komunikację i  - * przekazać go w parametrze \c fd. Po zakończeniu rozwiązywania nazwy, - * powinien wysłać otrzymany adres IP w postaci sieciowej (big-endian) do - * deskryptora. Jeśli rozwiązywanie nazwy się nie powiedzie, należy wysłać - * \c INADDR_NONE. Następnie zostanie wywołana funkcja zwalniająca zasoby - * z parametrem \c force równym \c 0. Gdyby sesja została zakończona przed - * rozwiązaniem nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja - * zwalniająca zasoby zostanie wywołana z parametrem \c force równym \c 1. + * Patrz \ref gg_session_set_custom_resolver.   *   * \return 0 jeśli się powiodło, -1 w przypadku błędu   */ diff --git a/protocols/Gadu-Gadu/src/libgadu/resolver.h b/protocols/Gadu-Gadu/src/libgadu/resolver.h index 145c5178a4..481c0e5ea4 100644 --- a/protocols/Gadu-Gadu/src/libgadu/resolver.h +++ b/protocols/Gadu-Gadu/src/libgadu/resolver.h @@ -1,4 +1,3 @@ -/* coding: UTF-8 */
  /* $Id$ */
  /*
 @@ -28,6 +27,6 @@  #include <arpa/inet.h>
  #endif /* _WIN32 */
 -int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread);
 +int gg_gethostbyname_real(const char *hostname, struct in_addr **result, int *count, int pthread);
  #endif /* LIBGADU_RESOLVER_H */
 diff --git a/protocols/Gadu-Gadu/src/libgadu/session.h b/protocols/Gadu-Gadu/src/libgadu/session.h new file mode 100644 index 0000000000..b77f8666e4 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/session.h @@ -0,0 +1,70 @@ +/* + *  (C) Copyright 2008-2010 Wojtek Kaniewski <wojtekka@irc.pl> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU Lesser General Public License Version + *  2.1 as published by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU Lesser General Public License for more details. + * + *  You should have received a copy of the GNU Lesser General Public + *  License along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + *  USA. + */ + +#ifndef LIBGADU_SESSION_H +#define LIBGADU_SESSION_H + +#ifdef GG_CONFIG_HAVE_GNUTLS +#  include <gnutls/gnutls.h> +#endif + +#define GG_SESSION_CHECK(gs, result) \ +	do { \ +		if ((gs) == NULL) { \ +			errno = EINVAL; \ +			return (result); \ +		} \ +	} while (0) + +#define GG_SESSION_CHECK_CONNECTED(gs, result) \ +	do { \ +		GG_SESSION_CHECK(gs, result); \ +		\ +		if (!GG_SESSION_IS_CONNECTED(gs)) { \ +			errno = ENOTCONN; \ +			return (result); \ +		} \ +	} while (0) + +#define GG_SESSION_IS_PROTOCOL_7_7(gs) ((gs)->protocol_version >= 0x2a) +#define GG_SESSION_IS_PROTOCOL_8_0(gs) ((gs)->protocol_version >= 0x2d) + +#define GG_SESSION_IS_IDLE(gs) ((gs)->state == GG_STATE_IDLE) +#define GG_SESSION_IS_CONNECTING(gs) ((gs)->state != GG_STATE_IDLE && (gs)->state != GG_STATE_CONNECTED) +#define GG_SESSION_IS_CONNECTED(gs) ((gs)->state == GG_STATE_CONNECTED) + +#ifdef GG_CONFIG_HAVE_GNUTLS + +typedef struct { +	gnutls_session_t session; +	gnutls_certificate_credentials_t xcred; +} gg_session_gnutls_t; + +#define GG_SESSION_GNUTLS(gs) ((gg_session_gnutls_t*) (gs)->ssl)->session + +#endif /* GG_CONFIG_HAVE_GNUTLS */ + +#ifdef GG_CONFIG_HAVE_OPENSSL + +#define GG_SESSION_OPENSSL(gs) ((SSL*) (gs)->ssl) + +#endif /* GG_CONFIG_HAVE_OPENSSL */ + +int gg_session_handle_packet(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge); + +#endif /* LIBGADU_SESSION_H */ diff --git a/protocols/Gadu-Gadu/src/libgadu/sha1.cpp b/protocols/Gadu-Gadu/src/libgadu/sha1.cpp index b01b77a00c..155aa85dfd 100644 --- a/protocols/Gadu-Gadu/src/libgadu/sha1.cpp +++ b/protocols/Gadu-Gadu/src/libgadu/sha1.cpp @@ -1,5 +1,4 @@ -/* coding: UTF-8 */
 -/* $Id: sha1.c,v 1.4 2007-07-20 23:00:50 wojtekka Exp $ */
 +/* $Id$ */
  /*
   *  (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl>
 @@ -24,7 +23,7 @@  /**
   * \file sha1.c
   *
 - * \brief Funkcje wyznaczania skrĂłtu SHA1
 + * \brief Funkcje wyznaczania skrótu SHA1
   */
  #include <string.h>
 @@ -38,11 +37,11 @@  #include "libgadu.h"
  /**
 - * \internal Liczy skrĂłt SHA1 z ziarna i hasĹa.
 + * \internal Liczy skrót SHA1 z ziarna i hasła.
   *
 - * \param password HasĹo
 + * \param password Hasło
   * \param seed Ziarno
 - * \param result Bufor na wynik funkcji skrĂłtu (20 bajtĂłw)
 + * \param result Bufor na wynik funkcji skrótu (20 bajtów)
   */
  void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
  {
 @@ -56,10 +55,10 @@ void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)  }
  /**
 - * \internal Liczy skrĂłt SHA1 z pliku.
 + * \internal Liczy skrót SHA1 z pliku.
   *
   * \param fd Deskryptor pliku
 - * \param result WskaĹşnik na skrĂłt
 + * \param result WskaĹşnik na skrót
   *
   * \return 0 lub -1
   */
  | 
