/*
 * PtokaX - hub server for Direct Connect peer to peer network.

 * Copyright (C) 2002-2005  Ptaczek, Ptaczek at PtokaX dot org
 * Copyright (C) 2004-2022  Petr Kozelka, PPK at PtokaX dot org

 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * 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 General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

//---------------------------------------------------------------------------
#include "stdinc.h"
//---------------------------------------------------------------------------
#include "utility.h"
//---------------------------------------------------------------------------
#include "hashBanManager.h"
#include "LanguageManager.h"
#include "ServerManager.h"
#include "SettingManager.h"
#include "UdpDebug.h"
#include "GlobalDataQueue.h"
//---------------------------------------------------------------------------
#ifdef _WIN32
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
#ifdef _BUILD_GUI
#include "../gui.win/GuiUtil.h"
#include "../gui.win/MainWindowPageUsersChat.h"
#endif
//---------------------------------------------------------------------------
#ifndef _WITHOUT_SKEIN
#include <skein.h>
#endif
//---------------------------------------------------------------------------
/*
static const int MAX_PAT_SIZE = 64;
static const int MAX_ALPHABET_SIZE = 255;
*/
//---------------------------------------------------------------------------
extern bool g_isUseSyslog;

void AppendSyslog(const char* sType, const char * sData)
{
#ifndef _WIN32
	if (g_isUseSyslog)
	{
		syslog(LOG_NOTICE, "[%s] %s", sType, sData);
	}
#else
	(void)sType;
	(void)sData;
#endif
}

#ifdef _WIN32
void Cout(const string & sMsg)
{
	if (ServerManager::m_hConsole != NULL)
	{
		WriteConsole(ServerManager::m_hConsole, (sMsg + "\n").c_str(), (DWORD)sMsg.size() + 1, 0, 0);
	}
}
#else
void Cout(const string &/*sMsg*/)
{
	return;
}
#endif
//---------------------------------------------------------------------------
/*
#ifdef _WIN32
    // Boyer-Moore string matching algo.
    // returns :
    // offset in bytes or -1 if no match
    int BMFind(char *text, int N, char *pat, int M) {
       int i, j, DD[MAX_PAT_SIZE], D[MAX_ALPHABET_SIZE];

       // Predzpracovani
       preDD(pat, M, DD);
       preD(pat, M, D);

       // Vyhledavani
       // Searching
       i = 0;
       while (i <= N-M) {
          for(j = M-1; j >= 0  &&  pat[j] == text[j+i]; --j);
          if (j < 0) {
             return i;
             //i += DD[0];
          }
          else
             i += DD[j] > (D[text[j + i]] - M + 1 + j) ? DD[j] : (D[text[j + i]] - M + 1 + j);
       }
       return -1;
    }
//---------------------------------------------------------------------------

    void preD(char *pat, int M, int D[]) {
       int i;
       for(i = 0; i < MAX_ALPHABET_SIZE; ++i)
          D[i] = M;
       for(i = 0; i < M - 1; ++i)
          D[pat[i]] = M - i - 1;
    }
//---------------------------------------------------------------------------

    // Funkce ulozi do suff[i] delku nejdelsiho podretezce,
    // ktery konci na pozici pat[i]
    // a soucasne je priponou pat
    //
    void suffixes(char *pat, int M, int *suff) {
       int f = 0, g, i;

       suff[M - 1] = M;
       g = M - 1;
       for(i = M - 2; i >= 0; --i) {
          if (i > g && suff[i + M - 1 - f] < i - g)
             suff[i] = suff[i + M - 1 - f];
          else {
             if (i < g)
                g = i;
             f = i;
             while (g >= 0 && pat[g] == pat[g + M - 1 - f])
                --g;
             suff[i] = f - g;
          }
       }
    }
//---------------------------------------------------------------------------

    void preDD(char *pat, int M, int DD[]) {
       int i, j, suff[MAX_PAT_SIZE];

       suffixes(pat, M, suff);

       for(i = 0; i < M; ++i)
          DD[i] = M;
       j = 0;
       for(i = M - 1; i >= -1; --i)
          if (suff[i] == i+1  ||  i == -1) // TODO FlylinkDC++ cppcheck! suff[-1] ??
             for( ; j < M-1-i; ++j)
                if (DD[j] == M)
                   DD[j] = M-1-i;
       for(i = 0; i <= M-2; ++i)
          DD[M - 1 - suff[i]] = M-1-i;
    }
#endif
*/
//---------------------------------------------------------------------------

char * Lock2Key(char * sLock)
{
	static const uint8_t ui8LockSize = 46;
	static char cKey[128];
	cKey[0] = '\0';

	// $Lock EXTENDEDPROTOCOL33MTL6@h5AIad^P2UoPv?fZU]ivM6 Pk=PtokaX|

	sLock = sLock + 6; // set begin after $Lock_
	sLock[ui8LockSize] = '\0'; // set end before Pk

	uint8_t v;

	// first make the crypting stuff
	for (uint8_t ui8i = 0; ui8i < ui8LockSize; ui8i++)
	{
		if (ui8i == 0)
		{
			v = (uint8_t)(sLock[0] ^ sLock[45] ^ sLock[44] ^ 5);
		}
		else
		{
			v = sLock[ui8i] ^ sLock[ui8i - 1];
		}

		// swap nibbles (0xF0 = 11110000, 0x0F = 00001111)

		v = (uint8_t)(((v << 4) & 0xF0) | ((v >> 4) & 0x0F));

		switch (v)
		{
		case   0:
			strcat(cKey, "/%DCN000%/");
			break;
		case   5:
			strcat(cKey, "/%DCN005%/");
			break;
		case  36:
			strcat(cKey, "/%DCN036%/");
			break;
		case  96:
			strcat(cKey, "/%DCN096%/");
			break;
		case 124:
			strcat(cKey, "/%DCN124%/");
			break;
		case 126:
			strcat(cKey, "/%DCN126%/");
			break;
		default:
			strncat(cKey, (char *)&v, 1);
			break;
		}
	}
	return cKey;
}
//---------------------------------------------------------------------------

#ifdef _WIN32
char * WSErrorStr(const uint32_t ui32Error)
{
	static char errStrings[][64] =
	{
		{"0"}, {"1"}, {"2"}, {"3"},
		{"WSAEINTR"},
		{"5"}, {"6"}, {"7"}, {"8"},
		{"WSAEBADF"},
		{"10"}, {"11"}, {"12"},
		{"WSAEACCES"},
		{"WSAEFAULT"},
		{"15"}, {"16"}, {"17"}, {"18"}, {"19"}, {"20"}, {"21"},
		{"WSAEINVAL"},
		{"23"},
		{"WSAEMFILE"},
		{"25"}, {"26"}, {"27"}, {"28"}, {"29"}, {"30"}, {"31"}, {"32"}, {"33"}, {"34"},
		{"WSAEWOULDBLOCK"},
		{"WSAEINPROGRESS"},   // This error is returned if any Windows Sockets API function is called while a blocking function is in progress.
		{"WSAEALREADY"},
		{"WSAENOTSOCK"},
		{"WSAEDESTADDRREQ"},
		{"WSAEMSGSIZE"},
		{"WSAEPROTOTYPE"},
		{"WSAENOPROTOOPT"},
		{"WSAEPROTONOSUPPORT"},
		{"WSAESOCKTNOSUPPORT"},
		{"WSAEOPNOTSUPP"},
		{"WSAEPFNOSUPPORT"},
		{"WSAEAFNOSUPPORT"},
		{"WSAEADDRINUSE"},
		{"WSAEADDRNOTAVAIL"},
		{"WSAENETDOWN"}, // This error may be reported at any time if the Windows Sockets implementation detects an underlying failure.
		{"WSAENETUNREACH"},
		{"WSAENETRESET"},
		{"WSAECONNABORTED"},
		{"WSAECONNRESET"},
		{"WSAENOBUFS"},
		{"WSAEISCONN"},
		{"WSAENOTCONN"},
		{"WSAESHUTDOWN"},
		{"WSAETOOMANYREFS"},
		{"WSAETIMEDOUT"},
		{"WSAECONNREFUSED"},
		{"WSAELOOP"},
		{"WSAENAMETOOLONG"},
		{"WSAEHOSTDOWN"},
		{"WSAEHOSTUNREACH"}, // 10065
		{"WSASYSNOTREADY"}, // 10091
		{"WSAVERNOTSUPPORTED"}, // 10092
		{"WSANOTINITIALISED"}, // 10093
		{"WSAHOST_NOT_FOUND"}, // 11001
		{"WSATRY_AGAIN"}, // 11002
		{"WSANO_RECOVERY"}, // 11003
		{"WSANO_DATA"} // 11004
	};

	switch (ui32Error)
	{
	case 10091 :
		return errStrings[66]; // WSASYSNOTREADY
	case 10092 :
		return errStrings[67]; // WSAVERNOTSUPPORTED
	case 10093 :
		return errStrings[68]; // WSANOTINITIALISED
	case 11001 :
		return errStrings[69]; // WSAHOST
	case 11002 :
		return errStrings[70]; // WSATRY
	case 11003 :
		return errStrings[71]; // WSANO
	case 11004 :
		return errStrings[72]; // WSANO
	default :
		return errStrings[ui32Error - 10000];
	}
}
#else
const char * ErrnoStr(const uint32_t ui32Error)
{
	static const char *errStrings[] =
	{
		"UNDEFINED",
		"EADDRINUSE",
		"EADDRNOTAVAIL",
		"ECONNRESET",
		"ETIMEDOUT",
		"ECONNREFUSED",
		"EHOSTUNREACH",
	};

	switch(ui32Error)
	{
	case 98:
		return errStrings[1];
	case 99:
		return errStrings[2];
	case 104:
		return errStrings[3];
	case 110:
		return errStrings[4];
	case 111:
		return errStrings[5];
	case 113:
		return errStrings[6];
	default :
		return errStrings[0];
	}
}
#endif
//---------------------------------------------------------------------------

const char * formatBytes(const uint64_t ui64Bytes)
{
	static const char *unit[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", " ", " ", " ", " ", " ", " ", " "};
	static char sBytes[128];

	if (ui64Bytes < 1024)
	{
		int iLen = snprintf(sBytes, 128, "%" PRIu64 " %s", ui64Bytes & 0xffffffff, unit[0]);
		if (iLen <= 0)
		{
			sBytes[0] = '\0';
		}
	}
	else
	{
		long double ldBytes = (long double)ui64Bytes;
		uint8_t iter = 0;
		for (; ldBytes > 1024; iter++)
			ldBytes /= 1024;

		int iLen = snprintf(sBytes, 128, "%0.2Lf %s", ldBytes, unit[iter]);
		if (iLen <= 0)
		{
			sBytes[0] = '\0';
		}
	}

	return sBytes;
}
//---------------------------------------------------------------------------

const char * formatBytesPerSecond(const uint64_t ui64Bytes)
{
	static const char *secondunit[] = {"B/s", "kB/s", "MB/s", "GB/s", "TB/s", "PB/s", "EB/s", "ZB/s", "YB/s", " ", " ", " ", " ", " ", " ", " "};
	static char sBytes[128];

	if (ui64Bytes < 1024)
	{
		int iLen = snprintf(sBytes, 128, "%" PRIu64 " %s", ui64Bytes & 0xffffffff, secondunit[0]);
		if (iLen <= 0)
		{
			sBytes[0] = '\0';
		}
	}
	else
	{
		long double ldBytes = (long double)ui64Bytes;
		uint8_t iter = 0;
		for (; ldBytes > 1024; iter++)
			ldBytes /= 1024;

		int iLen = snprintf(sBytes, 128, "%0.2Lf %s", ldBytes, secondunit[iter]);
		if (iLen <= 0)
		{
			sBytes[0] = '\0';
		}
	}

	return sBytes;
}
//---------------------------------------------------------------------------

const char * formatTime(uint64_t ui64Rest)
{
	static char time[256];
	time[0] = '\0';

	char buf[128];
	uint8_t i = 0;
	uint64_t n = ui64Rest / 525600;
	ui64Rest -= n * 525600;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%" PRIu64 " %s", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_YEARS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_YEAR_LWR]);
		if (iLen <= 0)
		{
			return time;
		}

		strcat(time, buf);
		i++;
	}

	n = ui64Rest / 43200;
	ui64Rest -= n * 43200;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_MONTHS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_MONTH_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
		i++;
	}

	n = ui64Rest / 1440;
	ui64Rest -= n * 1440;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_DAYS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_DAY_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
		i++;
	}

	n = ui64Rest / 60;
	ui64Rest -= n * 60;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_HOURS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_HOUR_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
		i++;
	}

	if (ui64Rest != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", ui64Rest, LanguageManager::m_Ptr->m_sTexts[LAN_MIN_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}
		strcat(time, buf);
	}

	return time;
}
//---------------------------------------------------------------------------

const char * formatSecTime(uint64_t ui64Rest)
{
	static char time[256];
	time[0] = '\0';

	char buf[128];
	uint8_t i = 0;
	uint64_t n = ui64Rest / 31536000;
	ui64Rest -= n * 31536000;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%" PRIu64 " %s", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_YEARS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_YEAR_LWR]);
		if (iLen <= 0)
		{
			return time;
		}

		strcat(time, buf);
		i++;
	}

	n = ui64Rest / 2592000;
	ui64Rest -= n * 2592000;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_MONTHS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_MONTH_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
		i++;
	}

	n = ui64Rest / 86400;
	ui64Rest -= n * 86400;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_DAYS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_DAY_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
		i++;
	}

	n = ui64Rest / 3600;
	ui64Rest -= n * 3600;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", n, n > 1 ? LanguageManager::m_Ptr->m_sTexts[LAN_HOURS_LWR] : LanguageManager::m_Ptr->m_sTexts[LAN_HOUR_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
		i++;
	}

	n = ui64Rest / 60;
	ui64Rest -= n * 60;

	if (n != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", n, LanguageManager::m_Ptr->m_sTexts[LAN_MIN_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
		i++;
	}

	if (ui64Rest != 0)
	{
		int iLen = snprintf(buf, 128, "%s%" PRIu64 " %s", i > 0 ? " " : "", ui64Rest, LanguageManager::m_Ptr->m_sTexts[LAN_SEC_LWR]);
		if (iLen <= 0)
		{
			time[0] = '\0';
			return time;
		}

		strcat(time, buf);
	}
	return time;
}
//---------------------------------------------------------------------------

char* stristr(const char *str1, const char *str2)
{
	char *cp = (char *)str1;
	if (*str2 == 0)
		return (char *)str1;
	while (*cp != 0)
	{
		char *s1 = cp;
		char *s2 = (char *) str2;
		while (*s1 != 0 && *s2 != 0 && ((*s1 - *s2) == 0 ||
		                                (*s1 - tolower(*s2)) == 0 || (*s1 - toupper(*s2)) == 0))
			s1++, s2++;
		if (*s2 == 0)
		{
			return cp;
		}
		cp++;
	}
	return NULL;
}
//---------------------------------------------------------------------------

char* stristr2(const char *str1, const char *str2)
{
	char *cp = (char *)str1;
	if (*str2 == 0)
		return (char *)str1;
	while (*cp != 0)
	{
		char *s1 = cp;
		char *s2 = (char *) str2;
		while (*s1 != 0 && *s2 != 0 && ((*s1 - *s2) == 0 ||
		                                (tolower(*s1) - *s2) == 0))
			s1++, s2++;
		if (*s2 == 0)
		{
			return cp;
		}
		cp++;
	}
	return NULL;
}
//---------------------------------------------------------------------------

// check IP string.
// false - no ip
// true - valid ip
bool isIP(const char * sIP)
{
	if (ServerManager::m_bUseIPv6 == true && strchr(sIP, '.') == NULL)
	{
		if (strlen(sIP) > 39)
		{
			return false;
		}

		uint8_t ui128IpHash[16];
#if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN_IOT)
		if (win_inet_pton(sIP, ui128IpHash) != 1)
		{
#else
		if (inet_pton(AF_INET6, sIP, ui128IpHash) != 1)
		{
#endif
			return false;
		}
	}
	else
	{
		if (strlen(sIP) > 15)
		{
			return false;
		}

		uint32_t ui32IpHash = inet_addr(sIP);

		if (ui32IpHash == INADDR_NONE)
		{
			return false;
		}
	}

	return true;
}
//---------------------------------------------------------------------------

uint32_t HashNick(const char * sNick, const size_t szNickLen)
{
	unsigned char c = 0;
	uint32_t h = 5381;

	for (size_t szi = 0; szi < szNickLen; szi++)
	{
		c = (unsigned char)tolower(sNick[szi]);
		h += (h << 5);
		h ^= c;
	}

	return (h + 1);
}
//---------------------------------------------------------------------------

bool HashIP(const char * sIP, uint8_t * ui128IpHash)
{
	if (ServerManager::m_bUseIPv6 == true && strchr(sIP, '.') == NULL)
	{
		if (strlen(sIP) > 39)
		{
			return false;
		}

#if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN_IOT)
		if (win_inet_pton(sIP, ui128IpHash) != 1)
		{
#else
		if (inet_pton(AF_INET6, sIP, ui128IpHash) != 1)
		{
#endif
			return false;
		}
	}
	else
	{
		if (strlen(sIP) > 15)
		{
			return false;
		}

		uint32_t ui32IpHash = inet_addr(sIP);

		if (ui32IpHash == INADDR_NONE)
		{
			return false;
		}

		memset(ui128IpHash, 0, 16);

		ui128IpHash[10] = 255;
		ui128IpHash[11] = 255;

		memcpy(ui128IpHash + 12, &ui32IpHash, 4);
	}

	return true;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

uint16_t GetIpTableIdx(const uint8_t * ui128IpHash)
{
	unsigned char c = 0;
	uint32_t h = 5381;

	for (uint8_t ui8i = 0; ui8i < 16; ui8i++)
	{
		c = (unsigned char)ui128IpHash[ui8i];
		h += (h << 5);
		h ^= c;
	}

	h += 1;

	uint16_t ui16Idx = 0;
	memcpy(&ui16Idx, &h, sizeof(uint16_t));

	return ui16Idx;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

int GenerateBanMessage(BanItem * pBan, const time_t &tAccTime)
{
	int iMsgLen = 0;

	if (((pBan->m_ui8Bits & BanManager::PERM) == BanManager::PERM) == true)
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "<%s> %s.", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_SORRY_PERM_BANNED]);

	}
	else
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "<%s> %s: %s.", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_SORRY_TEMP_BANNED], formatSecTime(pBan->m_tTempBanExpire - tAccTime));

	}

	if (iMsgLen <= 0)
	{
		AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateBanMessage1\n", iMsgLen);

		return 0;
	}


	if (SettingManager::m_Ptr->m_bBools[SETBOOL_BAN_MSG_SHOW_IP] == true && pBan->m_sIp[0] != '\0')
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_IP], pBan->m_sIp);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateBanMessage2\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}

	if (SettingManager::m_Ptr->m_bBools[SETBOOL_BAN_MSG_SHOW_NICK] == true && pBan->m_sNick != NULL)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_NICK], pBan->m_sNick);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateBanMessage3\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}

	if (SettingManager::m_Ptr->m_bBools[SETBOOL_BAN_MSG_SHOW_REASON] == true && pBan->m_sReason != NULL)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_REASON], pBan->m_sReason);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateBanMessage4\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}

	if (SettingManager::m_Ptr->m_bBools[SETBOOL_BAN_MSG_SHOW_BY] == true && pBan->m_sBy != NULL)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_BANNED_BY], pBan->m_sBy);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateBanMessage5\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}

	if (SettingManager::m_Ptr->m_sTexts[SETTXT_MSG_TO_ADD_TO_BAN_MSG] != NULL)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s|", SettingManager::m_Ptr->m_sTexts[SETTXT_MSG_TO_ADD_TO_BAN_MSG]);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateBanMessage6\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}
	else
	{
		ServerManager::m_pGlobalBuffer[iMsgLen] = '|';
		iMsgLen++;
		ServerManager::m_pGlobalBuffer[iMsgLen] = '\0';
	}

#ifdef FLYLINKDC_USE_REDIR
	if (((pBan->m_ui8Bits & BanManager::PERM) == BanManager::PERM) == true)
	{
		if (SettingManager::m_Ptr->m_bBools[SETBOOL_PERM_BAN_REDIR] == true && SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_PERM_BAN_REDIR_ADDRESS] != NULL)
		{
			strcpy(ServerManager::m_pGlobalBuffer + iMsgLen, SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_PERM_BAN_REDIR_ADDRESS]);
			iMsgLen += (int)SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_PERM_BAN_REDIR_ADDRESS];
		}
	}
	else
	{
		if (SettingManager::m_Ptr->m_bBools[SETBOOL_TEMP_BAN_REDIR] == true && SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_TEMP_BAN_REDIR_ADDRESS] != NULL)
		{
			strcpy(ServerManager::m_pGlobalBuffer + iMsgLen, SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_TEMP_BAN_REDIR_ADDRESS]);
			iMsgLen += (int)SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_TEMP_BAN_REDIR_ADDRESS];
		}
	}
#endif

	return iMsgLen;
}
//---------------------------------------------------------------------------

int GenerateRangeBanMessage(RangeBanItem * pRangeBan, const time_t &tAccTime)
{
	int iMsgLen = 0;

	if (((pRangeBan->m_ui8Bits & BanManager::PERM) == BanManager::PERM) == true)
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "<%s> %s.", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_SORRY_PERM_BANNED]);
	}
	else
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "<%s> %s: %s", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_SORRY_TEMP_BANNED], formatSecTime(pRangeBan->m_tTempBanExpire - tAccTime));
	}

	if (iMsgLen <= 0)
	{
		AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateRangeBanMessage1\n", iMsgLen);

		return 0;
	}

	if (SettingManager::m_Ptr->m_bBools[SETBOOL_BAN_MSG_SHOW_RANGE] == true)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s-%s", LanguageManager::m_Ptr->m_sTexts[LAN_RANGE], pRangeBan->m_sIpFrom, pRangeBan->m_sIpTo);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateRangeBanMessage2\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}

	if (SettingManager::m_Ptr->m_bBools[SETBOOL_BAN_MSG_SHOW_REASON] == true && pRangeBan->m_sReason != NULL)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_REASON], pRangeBan->m_sReason);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateRangeBanMessage3\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}

	if (SettingManager::m_Ptr->m_bBools[SETBOOL_BAN_MSG_SHOW_BY] == true && pRangeBan->m_sBy != NULL)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_BANNED_BY], pRangeBan->m_sBy);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateRangeBanMessage4\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}

	if (SettingManager::m_Ptr->m_sTexts[SETTXT_MSG_TO_ADD_TO_BAN_MSG] != NULL)
	{
		int iLen = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s|", SettingManager::m_Ptr->m_sTexts[SETTXT_MSG_TO_ADD_TO_BAN_MSG]);
		if (iLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in GenerateRangeBanMessage5\n", iLen);

			return 0;
		}

		iMsgLen += iLen;
	}
	else
	{
		ServerManager::m_pGlobalBuffer[iMsgLen] = '|';
		iMsgLen++;
		ServerManager::m_pGlobalBuffer[iMsgLen] = '\0';
	}

#ifdef FLYLINKDC_USE_REDIR

	if (((pRangeBan->m_ui8Bits & BanManager::PERM) == BanManager::PERM) == true)
	{
		if (SettingManager::m_Ptr->m_bBools[SETBOOL_PERM_BAN_REDIR] == true && SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_PERM_BAN_REDIR_ADDRESS] != NULL)
		{
			strcpy(ServerManager::m_pGlobalBuffer + iMsgLen, SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_PERM_BAN_REDIR_ADDRESS]);
			iMsgLen += (int)SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_PERM_BAN_REDIR_ADDRESS];
		}
	}
	else
	{
		if (SettingManager::m_Ptr->m_bBools[SETBOOL_TEMP_BAN_REDIR] == true && SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_TEMP_BAN_REDIR_ADDRESS] != NULL)
		{
			strcpy(ServerManager::m_pGlobalBuffer + iMsgLen, SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_TEMP_BAN_REDIR_ADDRESS]);
			iMsgLen += (int)SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_TEMP_BAN_REDIR_ADDRESS];
		}
	}
#endif 

	return iMsgLen;
}
//---------------------------------------------------------------------------

bool GenerateTempBanTime(const uint8_t ui8Multiplyer, const uint32_t ui32Time, time_t &tAccTime, time_t &tBanTime)
{
	time(&tAccTime);
	struct tm *tm = localtime(&tAccTime);

	switch (ui8Multiplyer)
	{
	case 'm':
		tm->tm_min += ui32Time;
		break;
	case 'h':
		tm->tm_hour += ui32Time;
		break;
	case 'd':
		tm->tm_mday += ui32Time;
		break;
	case 'w':
		tm->tm_mday += ui32Time * 7;
		break;
	case 'M':
		tm->tm_mon += ui32Time;
		break;
	case 'Y':
		tm->tm_year += ui32Time;
		break;
	default:
		return false;
	}

	tm->tm_isdst = -1;

	tBanTime = mktime(tm);

	if(tBanTime == (time_t)-1)
	{
		return false;
	}

	return true;
}
//---------------------------------------------------------------------------
// + alex82 ... from MOD
bool CheckSprintf(const int iRetVal, const size_t szMax, const char * sMsg)
{
	if (iRetVal > 0)
	{
		if (szMax != 0 && iRetVal >= (int)szMax)
		{
			const std::string sDbgstr = "%s - [ERR] sprintf high value " + std::to_string(iRetVal) + "/" + std::to_string((uint64_t)szMax) + " in " + std::to_string(sMsg) + "\n";
			AppendDebugLog(sDbgstr.c_str()/*, 0*/);
			return false;
		}
	}
	else
	{
		const std::string sDbgstr = "%s - [ERR] sprintf low value " + std::to_string(iRetVal) + " in " + std::to_string(sMsg) + "\n";
		AppendDebugLog(sDbgstr.c_str()/*, 0*/);
		return false;
	}
	return true;
}
//---------------------------------------------------------------------------

bool CheckSprintf1(const int iRetVal, const size_t szLenVal, const size_t szMax, const char * sMsg)
{
	if (iRetVal > 0)
	{
		if (szMax != 0 && szLenVal >= szMax)
		{
			const std::string sDbgstr = "%s - [ERR] sprintf high value " + std::to_string((uint64_t)szLenVal) + "/" + std::to_string((uint64_t)szMax) + " in " + std::to_string(sMsg) + "\n";
			AppendDebugLog(sDbgstr.c_str()/*, 0*/);
			return false;
		}
	}
	else
	{
		const std::string sDbgstr = "%s - [ERR] sprintf low value " + std::to_string(iRetVal) + " in " + std::to_string(sMsg) + "\n";
		AppendDebugLog(sDbgstr.c_str()/*, 0*/);
		return false;
	}
	return true;
}
//---------------------------------------------------------------------------

void AppendLog(const char* sData, const bool bScript/* == false*/)
{
	FILE * fw;
	AppendSyslog("AppendLog", sData); // [+]FlylinkDC++
	if (bScript == false)
	{
#ifdef _WIN32
		fw = fopen((ServerManager::m_sPath + "\\logs\\system.log").c_str(), "a");
#else
		fw = fopen((ServerManager::m_sPath + "/logs/system.log").c_str(), "a");
#endif
	}
	else
	{
#ifdef _WIN32
		fw = fopen((ServerManager::m_sPath + "\\logs\\script.log").c_str(), "a");
#else
		fw = fopen((ServerManager::m_sPath + "/logs/script.log").c_str(), "a");
#endif
	}

	if (fw != NULL)
	{
		time_t acc_time;
		time(&acc_time);

		struct tm * acc_tm;
		acc_tm = localtime(&acc_time);

		char sBuf[64];
		strftime(sBuf, sizeof(sBuf), "%c", acc_tm);

		fprintf(fw, "%s - %s\n", sBuf, sData);
		printf("[log] %s - %s\n", sBuf, sData);

		fclose(fw);
	}
	if(GlobalDataQueue::m_Ptr)
	   GlobalDataQueue::m_Ptr->PrometheusLogBytes("logs",strlen(sData));

	if (UdpDebug::m_Ptr != NULL && bScript == false)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] %s", sData);
	}
}
//---------------------------------------------------------------------------

void AppendDebugLog(const char * sData)
{

	AppendSyslog("AppendDebugLog", sData); // [+]FlylinkDC++

#ifdef _WIN32
	FILE * fw = fopen((ServerManager::m_sPath + "\\logs\\debug.log").c_str(), "a");
#else
	FILE * fw = fopen((ServerManager::m_sPath + "/logs/debug.log").c_str(), "a");
#endif

	if (fw == NULL)
	{
		return;
	}

	time_t acc_time;
	time(&acc_time);

	struct tm * acc_tm;
	acc_tm = localtime(&acc_time);

	char sBuf[64];
	strftime(sBuf, 64, "%c", acc_tm);

	fprintf(fw, sData, sBuf); // "%s - xxx\n"
	printf("[debug-log] %s - %s\n", sBuf, sData);

	fclose(fw);
	if(GlobalDataQueue::m_Ptr)
	   GlobalDataQueue::m_Ptr->PrometheusLogBytes("logsd",strlen(sData));
}
//---------------------------------------------------------------------------

void AppendDebugLogFormat(const char * sFormatMsg, ...)
{
#ifndef _WIN32 // [+]FlylinkDC++
	if (g_isUseSyslog)
	{
		std::string l_str;
		l_str.resize(65535);
		va_list vlArgs;
		va_start(vlArgs, sFormatMsg);
		vsnprintf(&l_str[0], l_str.size(), sFormatMsg, vlArgs);
		va_end(vlArgs);
		syslog(LOG_NOTICE, "[AppendDebugLogFormat] %s", l_str.c_str());
	}
#endif

#ifdef _WIN32
	FILE * fw = fopen((ServerManager::m_sPath + "\\logs\\debug.log").c_str(), "a");
#else
	FILE * fw = fopen((ServerManager::m_sPath + "/logs/debug.log").c_str(), "a");
#endif

	if (fw == NULL)
	{
		return;
	}

	time_t tmAccTime;
	time(&tmAccTime);

	size_t szLen = strftime(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "%c - ", localtime(&tmAccTime));

	if (szLen != 0)
	{
		fwrite(ServerManager::m_pGlobalBuffer, 1, szLen, fw);
	}

	va_list vlArgs;
	va_start(vlArgs, sFormatMsg);

	vfprintf(fw, sFormatMsg, vlArgs);

	va_end(vlArgs);

	fclose(fw);
    if(GlobalDataQueue::m_Ptr)
    	GlobalDataQueue::m_Ptr->PrometheusLogBytes("logf",szLen);
}
//---------------------------------------------------------------------------
#ifdef _WIN32
void GetHeapStats(void * pHeap, DWORD &dwCommitted, DWORD &dwUnCommitted)
{
	PROCESS_HEAP_ENTRY *lpEntry;

	lpEntry = (PROCESS_HEAP_ENTRY *)calloc(1, sizeof(PROCESS_HEAP_ENTRY));
	if (lpEntry == NULL)
	{
		return;
	}

	lpEntry->lpData = NULL;

	while (HeapWalk((HANDLE)pHeap, (PROCESS_HEAP_ENTRY *)lpEntry) != 0)
	{
		if ((lpEntry->wFlags & PROCESS_HEAP_REGION))
		{
			dwCommitted += lpEntry->Region.dwCommittedSize;
			dwUnCommitted += lpEntry->Region.dwUnCommittedSize;
		}
	}

	free(lpEntry);
}
//---------------------------------------------------------------------------

const char * ExtractFileName(const char * sPath)
{
	const char * sName = strrchr(sPath, '\\');

	if (sName != NULL)
	{
		return sName;
	}
	else
	{
		return sPath;
	}
}
#endif
//---------------------------------------------------------------------------

#ifdef _BUILD_GUI
void Memo(const string &sMessage)
{
	RichEditAppendText(MainWindowPageUsersChat::m_Ptr->m_hWndPageItems[MainWindowPageUsersChat::REDT_CHAT], sMessage.c_str());
}
#else
void Memo(const string &/*sMessage*/)
{
	// ...
}
#endif
//---------------------------------------------------------------------------

bool FileExist(const char * sPath)
{
#ifdef _WIN32
	const DWORD code = GetFileAttributes(sPath);
	if (code != INVALID_FILE_ATTRIBUTES && code != FILE_ATTRIBUTE_DIRECTORY)
	{
#else
	struct stat st;
	if (stat(sPath, &st) == 0 && S_ISDIR(st.st_mode) == 0)
	{
#endif
		return true;
	}

	return false;
}
//---------------------------------------------------------------------------

bool DirExist(const char * sPath)
{
#ifdef _WIN32
	const DWORD code = GetFileAttributes(sPath);
	if (code == FILE_ATTRIBUTE_DIRECTORY)
	{
#else
	struct stat st;
	if (stat(sPath, &st) == 0 && S_ISDIR(st.st_mode))
	{
#endif
		return true;
	}

	return false;
}
//---------------------------------------------------------------------------

#ifdef _WIN32
void SetupOsVersion()
{
	OSVERSIONINFOEX ver;
	memset(&ver, 0, sizeof(OSVERSIONINFOEX));
	ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

	if (::GetVersionEx((OSVERSIONINFO*)&ver) == 0)
	{
		ServerManager::m_sOS = "Windows (unknown version)";

		return;
	}

	if (ver.dwMajorVersion == 10)
	{
		if (ver.dwMinorVersion == 0)
		{
			if(ver.dwBuildNumber >= 22000)
			{
				ServerManager::m_sOS = "Windows 11 (Build " + std::to_string((uint32_t)ver.dwBuildNumber) + ")";
			}
			else
			{
				ServerManager::m_sOS = "Windows 10 (Build " + std::to_string((uint32_t)ver.dwBuildNumber) + ")";
			}
			return;
		}
	}
	else if (ver.dwMajorVersion == 6)
	{
		if (ver.dwMinorVersion == 3)
		{
			ServerManager::m_sOS = "Windows 8.1";
			return;
		}
		else if (ver.dwMinorVersion == 2)
		{
			ServerManager::m_sOS = "Windows 8";
			return;
		}
		else if (ver.dwMinorVersion == 1)
		{
			if (ver.wProductType == VER_NT_WORKSTATION)
			{
				ServerManager::m_sOS = "Windows 7";
				return;
			}
			else
			{
				ServerManager::m_sOS = "Windows 2008 R2";
				return;
			}
		}
		else
		{
			if (ver.wProductType == VER_NT_WORKSTATION)
			{
				ServerManager::m_sOS = "Windows Vista";
				return;
			}
			else
			{
				ServerManager::m_sOS = "Windows 2008";
				return;
			}
		}
	}
	else if (ver.dwMajorVersion == 5)
	{
		if (ver.dwMinorVersion == 2)
		{
			if (ver.wProductType != VER_NT_WORKSTATION)
			{
				ServerManager::m_sOS = "Windows 2003";
				return;
			}
			else
			{
				SYSTEM_INFO si;
				memset(&si, 0, sizeof(SYSTEM_INFO));

				::GetNativeSystemInfo(&si);

				if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
				{
					ServerManager::m_sOS = "Windows XP x64";
					return;
				}
			}

			// should not happen, but for sure...
			ServerManager::m_sOS = "Windows 2003/XP64";
			return;
		}
		else if (ver.dwMinorVersion == 1)
		{
			ServerManager::m_sOS = "Windows XP";
			return;
		}
	}

	ServerManager::m_sOS = "Windows (unknown version)";
}
//---------------------------------------------------------------------------

void * LuaAlocator(void * /*pOld*/, void * pData, const size_t /*szOldSize*/, const size_t szNewSize)
{
	if (szNewSize == 0)
	{
		if (pData)
		{
			free(pData);
		}

		return NULL;
	}
	else
	{
		if (pData)
		{
			return realloc(pData, szNewSize);
		}
		else
		{
			return malloc(szNewSize);
		}
	}
}
#endif
//---------------------------------------------------------------------------

#if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN_IOT)
typedef INT (WSAAPI * pInetPton)(INT, PCTSTR, PVOID);
pInetPton MyInetPton = NULL;
typedef PCTSTR(WSAAPI *pInetNtop)(INT, PVOID, PTSTR, size_t);
pInetNtop MyInetNtop = NULL;
#endif
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void CheckForIPv4()
{
#ifdef _WIN32
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	if (sock == INVALID_SOCKET)
	{
		int iError = WSAGetLastError();
		if (iError == WSAEAFNOSUPPORT)
		{
#else
	int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == -1)
	{
		if (errno == EAFNOSUPPORT)
		{
#endif
			ServerManager::m_bUseIPv4 = false;
			return;
		}
	}
	safe_closesocket(sock);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void CheckForIPv6()
{
#ifdef _WIN32
	SOCKET sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);

	if (sock == INVALID_SOCKET)
	{
		int iError = WSAGetLastError();
		if (iError == WSAEAFNOSUPPORT)
		{
#else
	int sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == -1)
	{
		if (errno == EAFNOSUPPORT)
		{
#endif
			ServerManager::m_bUseIPv6 = false;
			return;
		}
	}

#ifdef _WIN32
	DWORD dwIPv6 = 0;
	if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&dwIPv6, sizeof(dwIPv6)) != SOCKET_ERROR)
	{
#else
	int iIPv6 = 0;
	if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &iIPv6, sizeof(iIPv6)) != -1)
	{
#endif
		ServerManager::m_bIPv6DualStack = true;
	}

	safe_closesocket(sock);

#if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN_IOT)
	HINSTANCE hWs2_32 = ::LoadLibrary("Ws2_32.dll");

	MyInetPton = (pInetPton)::GetProcAddress(hWs2_32, "inet_pton");
	MyInetNtop = (pInetNtop)::GetProcAddress(hWs2_32, "inet_ntop");

	::FreeLibrary(hWs2_32);
#endif
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN_IOT)
INT win_inet_pton(PCTSTR pAddrString, PVOID pAddrBuf)
{
	if (MyInetPton != NULL)
	{
		return MyInetPton(AF_INET6, pAddrString, pAddrBuf);
	}
	else
	{
		sockaddr_storage sas_addr;
		socklen_t sas_len = sizeof(sockaddr_storage);
		memset(&sas_addr, 0, sas_len);

		if (::WSAStringToAddressA((LPSTR)pAddrString, AF_INET6, NULL, (struct sockaddr *)&sas_addr, &sas_len) == 0)
		{
			memcpy(pAddrBuf, &((struct sockaddr_in6 *)&sas_addr)->sin6_addr.s6_addr, 16);
			return 1;
		}
		else
		{
			return 0;
		}
	}
}
//---------------------------------------------------------------------------

void win_inet_ntop(PVOID pAddr, PTSTR pStringBuf, size_t szStringBufSize)
{
	if (MyInetNtop != NULL)
	{
		MyInetNtop(AF_INET6, pAddr, pStringBuf, szStringBufSize);
	}
	else
	{
		struct sockaddr_in6 sin6;
		socklen_t sin6_len = sizeof(sockaddr_in6);

		memset(&sin6, 0, sin6_len);
		sin6.sin6_family = AF_INET6;
		memcpy(&sin6.sin6_addr, pAddr, 16);

		::WSAAddressToStringA((struct sockaddr *)&sin6, sin6_len, NULL, pStringBuf, (LPDWORD)&szStringBufSize);
	}
}
//---------------------------------------------------------------------------
#endif

bool GetMacAddress(const char * sIP, char * sMac)
{
#ifdef FLYLINKDC_USE_MAC
#ifdef _WIN32
	uint32_t uiIP = ::inet_addr(sIP);

	MIB_IPNETTABLE * pINT = (MIB_IPNETTABLE *)new (std::nothrow) char[PTOKAX_GLOBAL_BUFF_SIZE]; // TODO
	if (pINT == NULL)
	{
		return false;
	}

	ULONG ulSize = PTOKAX_GLOBAL_BUFF_SIZE;
	DWORD dwRes = ::GetIpNetTable(pINT, &ulSize, TRUE);
	if (dwRes == NO_ERROR)
	{
		for (DWORD dwi = 0; dwi < pINT->dwNumEntries; dwi++)
		{
			if (pINT->table[dwi].dwAddr == uiIP && pINT->table[dwi].dwType != MIB_IPNET_TYPE_INVALID)
			{
				snprintf(sMac, 18, "%02x:%02x:%02x:%02x:%02x:%02x", pINT->table[dwi].bPhysAddr[0], pINT->table[dwi].bPhysAddr[1], pINT->table[dwi].bPhysAddr[2],
				         pINT->table[dwi].bPhysAddr[3], pINT->table[dwi].bPhysAddr[4], pINT->table[dwi].bPhysAddr[5]);
				delete []pINT;
				return true;
			}
		}
	}
	delete []pINT;
#else
	FILE *fp = fopen("/proc/net/arp", "r");
	if (fp != NULL)
	{
		bool bLastCharSpace = true;
		uint8_t ui8NonSpaces = 0;
		uint16_t ui16IpLen = (uint16_t)strlen(sIP);
		char buf[1024];

		while (fgets(buf, 1024, fp) != NULL)
		{
			if (strncmp(buf, sIP, ui16IpLen) == 0 && buf[ui16IpLen] == ' ')
			{
				bLastCharSpace = true;
				ui8NonSpaces = 0;
				while (buf[ui16IpLen] != '\0')
				{
					if (buf[ui16IpLen] == ' ')
					{
						bLastCharSpace = true;
					}
					else
					{
						if (bLastCharSpace == true)
						{
							bLastCharSpace = false;
							ui8NonSpaces++;
						}
					}

					if (ui8NonSpaces == 3)
					{
						strncpy(sMac, buf + ui16IpLen, 17);
						sMac[17] = '\0';
						fclose(fp);
						return true;
					}

					ui16IpLen++;
				}
			}
		}

		fclose(fp);
	}
#endif
#else
	(void)sIP;
	(void)sMac;
#endif // FLYLINKDC_USE_MAC 
	return false;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void CreateGlobalBuffer()
{
	ServerManager::m_szGlobalBufferSize = PTOKAX_GLOBAL_BUFF_SIZE;
	ServerManager::m_pGlobalBuffer = (char *)calloc(ServerManager::m_szGlobalBufferSize, 1);
	if (ServerManager::m_pGlobalBuffer == NULL)
	{
		AppendDebugLog("%s - [MEM] Cannot create ServerManager::m_pGlobalBuffer\n");
		exit(EXIT_FAILURE);
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void DeleteGlobalBuffer()
{
	safe_free(ServerManager::m_pGlobalBuffer);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool CheckAndResizeGlobalBuffer(const size_t szWantedSize)
{
	if (ServerManager::m_szGlobalBufferSize >= szWantedSize)
	{
		return true;
	}

	size_t szOldSize = ServerManager::m_szGlobalBufferSize;
	char * sOldBuf = ServerManager::m_pGlobalBuffer;

	ServerManager::m_szGlobalBufferSize = Allign(szWantedSize);

	ServerManager::m_pGlobalBuffer = (char *)realloc(sOldBuf, ServerManager::m_szGlobalBufferSize);
	if (ServerManager::m_pGlobalBuffer == NULL)
	{
		ServerManager::m_pGlobalBuffer = sOldBuf;

		AppendDebugLogFormat("[MEM] Cannot reallocate %zu bytes in CheckAndResizeGlobalBuffer for ServerManager::m_pGlobalBuffer\n", ServerManager::m_szGlobalBufferSize);

		ServerManager::m_szGlobalBufferSize = szOldSize;
		return false;
	}

	return true;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void ReduceGlobalBuffer()
{
	if (ServerManager::m_szGlobalBufferSize == PTOKAX_GLOBAL_BUFF_SIZE)
	{
		return;
	}

	size_t szOldSize = ServerManager::m_szGlobalBufferSize;
	char * sOldBuf = ServerManager::m_pGlobalBuffer;

	ServerManager::m_szGlobalBufferSize = PTOKAX_GLOBAL_BUFF_SIZE;

	ServerManager::m_pGlobalBuffer = (char *)realloc(sOldBuf, ServerManager::m_szGlobalBufferSize);
	if (ServerManager::m_pGlobalBuffer == NULL)
	{
		ServerManager::m_pGlobalBuffer = sOldBuf;

		AppendDebugLogFormat("[MEM] Cannot reallocate %zu bytes in ReduceGlobalBuffer for ServerManager::m_pGlobalBuffer\n", ServerManager::m_szGlobalBufferSize);

		ServerManager::m_szGlobalBufferSize = szOldSize;
		return;
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool HashPassword(const char * sPassword, const size_t szPassLen, uint8_t * ui8PassHash)
{
#ifndef _WITHOUT_SKEIN
	Skein1024_Ctxt_t ctx1024;

	if (Skein1024_Init(&ctx1024, 512) == SKEIN_SUCCESS)
	{
		if (Skein1024_Update(&ctx1024, (const u08b_t *)sPassword, szPassLen) == SKEIN_SUCCESS)
		{
			if (Skein1024_Final(&ctx1024, ui8PassHash) == SKEIN_SUCCESS)
			{
				return true;
			}
		}
	}
#endif
	return false;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#ifdef _WIN32
static const uint16_t ui16Num = 42; // Answer to the Ultimate Question
static const uint8_t ui8Num = 42; //  of Life, The Universe, and Everything

uint64_t htobe64(const uint64_t & ui64Value)
{
	if (*(uint8_t *)&ui16Num == ui8Num)  // LE
	{
		return _byteswap_uint64(ui64Value);
	}
	else     // BE
	{
		return ui64Value;
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

uint64_t be64toh(const uint64_t & ui64Value)
{
	if (*(uint8_t *)&ui16Num == ui8Num)  // LE
	{
		return _byteswap_uint64(ui64Value);
	}
	else     // BE
	{
		return ui64Value;
	}
}
#endif
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool WantAgain()
{
	printf("Do you want to try it again (Y/n) ? ");
	int iChar = getchar();

	while (getchar() != '\n')
	{
		// boredom...
	};

	if (toupper(iChar) == 'Y')
	{
		return true;
	}

	return false;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool IsPrivateIP(const char * sIP)
{
	if (ServerManager::m_bUseIPv6 == true && strchr(sIP, '.') == NULL)
	{
		Hash128 ui128IpHash;
#if defined(_WIN32) && !defined(_WIN64) && !defined(_WIN_IOT)
		if (win_inet_pton(sIP, ui128IpHash) != 1)
		{
#else
		if (inet_pton(AF_INET6, sIP, ui128IpHash) != 1)
		{
#endif
			return false;
		}

		if (IN6_IS_ADDR_LOOPBACK((in6_addr *)ui128IpHash.data()) || IN6_IS_ADDR_LINKLOCAL((in6_addr *)ui128IpHash.data()) || IN6_IS_ADDR_SITELOCAL((in6_addr *)ui128IpHash.data()))
		{
			return true;
		}
	}
	else
	{
		uint32_t ui32IpHash = inet_addr(sIP);

		if (ui32IpHash == INADDR_NONE)
		{
			return false;
		}

		uint8_t ui32IP[4];
		memcpy(ui32IP, &ui32IpHash, 4);

		if (ui32IP[0] == 10 || ui32IP[0] == 127 || (ui32IP[0] == 169 && ui32IP[1] == 254) || (ui32IP[0] == 172 && ui32IP[1] == 16) || (ui32IP[0] == 192 && ui32IP[1] == 168))
		{
			return true;
		}
	}

	return false;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

V1042 This file is marked with copyleft license, which requires you to open the derived source code.