/*
 * 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 <fstream>
//---------------------------------------------------------------------------
#include "User.h"
//---------------------------------------------------------------------------
#include "colUsers.h"
#include "DcCommands.h"
#include "GlobalDataQueue.h"
#include "hashUsrManager.h"
#include "LanguageManager.h"
#include "LuaScriptManager.h"
#include "ProfileManager.h"
#include "ServerManager.h"
#include "SettingManager.h"
#include "utility.h"
#include "UdpDebug.h"
#include "ZlibUtility.h"
//---------------------------------------------------------------------------
#ifdef _WIN32
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
#ifdef _WITH_SQLITE
#include "DB-SQLite.h"
#elif _WITH_POSTGRES
#include "DB-PostgreSQL.h"
#elif _WITH_MYSQL
#include "DB-MySQL.h"
#endif
#include "DeFlood.h"
//---------------------------------------------------------------------------
#ifdef _BUILD_GUI
#include "../gui.win/GuiUtil.h"
#include "../gui.win/MainWindowPageUsersChat.h"
#endif
//---------------------------------------------------------------------------
static const size_t ZMINDATALEN = 128;
static const char * sBadTag = "BAD TAG!"; // 8
static const char * sOtherNoTag = "OTHER (NO TAG)"; // 14
static const char * sUnknownTag = "UNKNOWN TAG"; // 11
static const char * sDefaultNick = "<unknown>"; // 9
//---------------------------------------------------------------------------
DcCommand ActualDcCommand;
//---------------------------------------------------------------------------

static bool UserProcessLines(User * pUser, const uint32_t ui32NewDataStart)
{
	char c = 0;

	char * pBuffer = pUser->m_pRecvBuf;
	char * buffer = pUser->m_pRecvBuf;
	static int g_id = 0;
	++g_id;

	for (uint32_t ui32i = ui32NewDataStart; ui32i < pUser->m_ui32RecvBufDataLen; ++ui32i)
	{
		// look for pipes in the data - process lines one by one
		if (pUser->m_pRecvBuf[ui32i] == '|')
		{
			c = pUser->m_pRecvBuf[ui32i + 1];
			pUser->m_pRecvBuf[ui32i + 1] = '\0';

			const uint32_t ui32CommandLen = (uint32_t)(((pUser->m_pRecvBuf + ui32i) - pBuffer) + 1);
			if (pBuffer[0] == '|')
			{
				//UdpDebug->BroadcastFormat("[SYS] heartbeat from %s (%s).", pUser->Nick, pUser->m_sIP);
				//send(Sck, "|", 1, 0);
			}
			else if (ui32CommandLen <= (pUser->m_ui8State < User::STATE_ADDME ? 1024U : 65536U))
			{
				ActualDcCommand.m_pUser = pUser;
				ActualDcCommand.m_sCommand = pBuffer;
				ActualDcCommand.m_ui32CommandLen = ui32CommandLen;

				DcCommands::m_Ptr->PreProcessData(&ActualDcCommand);
				extern bool g_isUseLog;
				if (pUser->m_is_proxy_user || pUser->m_is_invalid_json || g_isUseLog ||
				        pUser->m_is_bad_len_port || pUser->m_is_bad_port || pUser->m_is_ddos_udp ||
				        pUser->m_is_bad_len_number_myinfo || pUser->m_is_max_int8 || pUser->m_is_max_ip_len)
				{
					pUser->logInvalidUser(g_id, buffer, ui32CommandLen, false);
				}
			}
			else
			{
				pUser->SendFormat("UserProcessLines1", false, "<%s> %s!|", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_CMD_TOO_LONG]);
				pUser->Close();

				UdpDebug::m_Ptr->BroadcastFormat("[SYS] %s (%s): Received command too long. User disconnected.", pUser->m_sNick, pUser->m_sIP);

				return false;
			}

			pUser->m_pRecvBuf[ui32i + 1] = c;
			pBuffer += ui32CommandLen;
			if (pUser->m_ui8State >= User::STATE_CLOSING)
			{
				return true;
			}
		}
		else if (pUser->m_pRecvBuf[ui32i] == '\0')
		{
			// look for NULL character and replace with zero
			pUser->m_pRecvBuf[ui32i] = '0';
			continue;
		}
	}

	pUser->m_ui32RecvBufDataLen -= (uint32_t)(pBuffer - pUser->m_pRecvBuf);

	if (pUser->m_ui32RecvBufDataLen == 0)
	{
		DcCommands::m_Ptr->ProcessCmds(pUser);

		pUser->m_pRecvBuf[0] = '\0';

		return false;
	}
	else if (pUser->m_ui32RecvBufDataLen == 1)
	{
		DcCommands::m_Ptr->ProcessCmds(pUser);

		pUser->m_pRecvBuf[0] = pBuffer[0];
		pUser->m_pRecvBuf[1] = '\0';

		return true;
	}
	else
	{
		if (pUser->m_ui32RecvBufDataLen > (pUser->m_ui8State < User::STATE_ADDME ? 1024U : 65536U))
		{
			// PPK ... we don't want commands longer than 64 kB, drop this user !
			pUser->SendFormat("UserProcessLines2", false, "<%s> %s!|", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_CMD_TOO_LONG]);
			pUser->Close();

			UdpDebug::m_Ptr->BroadcastFormat("[SYS] %s (%s): RecvBuffer overflow. User disconnected.", pUser->m_sNick, pUser->m_sIP);

			return false;
		}

		DcCommands::m_Ptr->ProcessCmds(pUser);

		memmove(pUser->m_pRecvBuf, pBuffer, pUser->m_ui32RecvBufDataLen);
		pUser->m_pRecvBuf[pUser->m_ui32RecvBufDataLen] = '\0';

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

static void UserSetBadTag(User * pUser, char * sDesc, const uint8_t ui8DescLen)
{
	// PPK ... clear all tag related things
	pUser->m_sTagVersion = NULL;
	pUser->m_ui8TagVersionLen = 0;

	pUser->m_sModes[0] = '\0';
	pUser->m_ui32Hubs = pUser->m_ui32Slots = pUser->m_ui32OLimit = pUser->m_ui32LLimit = pUser->m_ui32DLimit = pUser->m_ui32NormalHubs = pUser->m_ui32RegHubs = pUser->m_ui32OpHubs = 0;
	pUser->m_ui32BoolBits |= User::BIT_OLDHUBSTAG;
	pUser->m_ui32BoolBits |= User::BIT_HAVE_BADTAG;

	pUser->m_sDescription = sDesc;
	pUser->m_ui8DescriptionLen = ui8DescLen;

	// PPK ... clear (fake) tag
	pUser->m_sTag = NULL;
	pUser->m_ui8TagLen = 0;

	// PPK ... set bad tag
	pUser->m_sClient = (char *)sBadTag;
	pUser->m_ui8ClientLen = 8;

	// PPK ... send report to udp debug
	UdpDebug::m_Ptr->BroadcastFormat("[SYS] User %s (%s) have bad TAG (%s) ?!?", pUser->m_sNick, pUser->m_sIP, pUser->m_sMyInfoOriginal);
}
//---------------------------------------------------------------------------

static void UserParseMyInfo(User * pUser)
{
	memcpy(ServerManager::m_pGlobalBuffer, pUser->m_sMyInfoOriginal, pUser->m_ui16MyInfoOriginalLen);

	char *sMyINFOParts[] = { NULL, NULL, NULL, NULL, NULL };
	uint16_t iMyINFOPartsLen[] = { 0, 0, 0, 0, 0 };

	unsigned char cPart = 0;

	sMyINFOParts[cPart] = ServerManager::m_pGlobalBuffer + 14 + pUser->m_ui8NickLen; // desription start


	for (uint32_t ui32i = 14 + pUser->m_ui8NickLen; ui32i < pUser->m_ui16MyInfoOriginalLen - 1u; ui32i++)
	{
		if (ServerManager::m_pGlobalBuffer[ui32i] == '$')
		{
			ServerManager::m_pGlobalBuffer[ui32i] = '\0';
			iMyINFOPartsLen[cPart] = (uint16_t)((ServerManager::m_pGlobalBuffer + ui32i) - sMyINFOParts[cPart]);

			// are we on end of myinfo ???
			if (cPart == 4)
				break;

			cPart++;
			sMyINFOParts[cPart] = ServerManager::m_pGlobalBuffer + ui32i + 1;
		}
	}

	// check if we have all myinfo parts, connection and sharesize must have length more than 0 !
	if (sMyINFOParts[0] == NULL || sMyINFOParts[1] == NULL || iMyINFOPartsLen[1] != 1 || sMyINFOParts[2] == NULL || iMyINFOPartsLen[2] == 0 || sMyINFOParts[3] == NULL || sMyINFOParts[4] == NULL || iMyINFOPartsLen[4] == 0)
	{
		pUser->SendFormat("UserParseMyInfo1", false, "<%s> %s!|", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_YOU_MyINFO_IS_CORRUPTED]);

		UdpDebug::m_Ptr->BroadcastFormat("[SYS] User %s (%s) with bad MyINFO (%s) disconnected.", pUser->m_sNick, pUser->m_sIP, pUser->m_sMyInfoOriginal);

		pUser->Close();
		return;
	}

	// connection
	pUser->m_ui8MagicByte = sMyINFOParts[2][iMyINFOPartsLen[2] - 1];
	pUser->m_sConnection = pUser->m_sMyInfoOriginal + (sMyINFOParts[2] - ServerManager::m_pGlobalBuffer);
	pUser->m_ui8ConnectionLen = (uint8_t)(iMyINFOPartsLen[2] - 1);

	// email
	if (iMyINFOPartsLen[3] != 0)
	{
		pUser->m_sEmail = pUser->m_sMyInfoOriginal + (sMyINFOParts[3] - ServerManager::m_pGlobalBuffer);
		pUser->m_ui8EmailLen = (uint8_t)iMyINFOPartsLen[3];
	}

	// share
	// PPK ... check for valid numeric share, kill fakers !
	if (HaveOnlyNumbers(sMyINFOParts[4], iMyINFOPartsLen[4]) == false)
	{
		//UdpDebug::m_Ptr->BroadcastFormat("[SYS] User %s (%s) with non-numeric sharesize disconnected.", pUser->Nick, pUser->IP);
		pUser->Close();
		return;
	}

	if (((pUser->m_ui32BoolBits & User::BIT_HAVE_SHARECOUNTED) == User::BIT_HAVE_SHARECOUNTED) == true)
	{
		ServerManager::m_ui64TotalShare -= pUser->m_ui64SharedSize;
#ifdef _WIN32
		pUser->m_ui64SharedSize = _strtoui64(sMyINFOParts[4], NULL, 10);
#else
		pUser->m_ui64SharedSize = strtoull(sMyINFOParts[4], NULL, 10);
#endif
		ServerManager::m_ui64TotalShare += pUser->m_ui64SharedSize;
	}
	else
	{
#ifdef _WIN32
		pUser->m_ui64SharedSize = _strtoui64(sMyINFOParts[4], NULL, 10);
#else
		pUser->m_ui64SharedSize = strtoull(sMyINFOParts[4], NULL, 10);
#endif
	}

	// Reset all tag infos...
	pUser->m_sModes[0] = '\0';
	pUser->m_ui32Hubs = 0;
	pUser->m_ui32NormalHubs = 0;
	pUser->m_ui32RegHubs = 0;
	pUser->m_ui32OpHubs = 0;
	pUser->m_ui32Slots = 0;
	pUser->m_ui32OLimit = 0;
	pUser->m_ui32LLimit = 0;
	pUser->m_ui32DLimit = 0;

	// description
	if (iMyINFOPartsLen[0] != 0)
	{
		if (sMyINFOParts[0][iMyINFOPartsLen[0] - 1] == '>')
		{
			char *DCTag = strrchr(sMyINFOParts[0], '<');
			if (DCTag == NULL)
			{
				pUser->m_sDescription = pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer);
				pUser->m_ui8DescriptionLen = (uint8_t)iMyINFOPartsLen[0];

				pUser->m_sClient = (char*)sOtherNoTag;
				pUser->m_ui8ClientLen = 14;
				return;
			}

			pUser->m_sTag = pUser->m_sMyInfoOriginal + (DCTag - ServerManager::m_pGlobalBuffer);
			pUser->m_ui8TagLen = (uint8_t)(iMyINFOPartsLen[0] - (DCTag - sMyINFOParts[0]));

			static const uint16_t ui16plusplus = *((uint16_t *)"++");
			if (DCTag[3] == ' ' && *((uint16_t *)(DCTag + 1)) == ui16plusplus)
			{
				pUser->m_ui32SupportBits |= User::SUPPORTBIT_NOHELLO;
			}

			static const uint16_t ui16V = *((uint16_t *)"V:");

			char * sTemp = strchr(DCTag, ' ');

			if (sTemp != NULL && *((uint16_t *)(sTemp + 1)) == ui16V)
			{
				sTemp[0] = '\0';
				pUser->m_sClient = pUser->m_sMyInfoOriginal + ((DCTag + 1) - ServerManager::m_pGlobalBuffer);
				pUser->m_ui8ClientLen = (uint8_t)((sTemp - DCTag) - 1);
			}
			else
			{
				pUser->m_sClient = (char *)sUnknownTag;
				pUser->m_ui8ClientLen = 11;
				pUser->m_sTag = NULL;
				pUser->m_ui8TagLen = 0;
				sMyINFOParts[0][iMyINFOPartsLen[0] - 1] = '>'; // not valid DC Tag, add back > tag ending
				pUser->m_sDescription = pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer);
				pUser->m_ui8DescriptionLen = (uint8_t)iMyINFOPartsLen[0];
				return;
			}

			size_t szTagPattLen = ((sTemp - DCTag) + 1);

			sMyINFOParts[0][iMyINFOPartsLen[0] - 1] = ','; // terminate tag end with ',' for easy tag parsing

			uint32_t reqVals = 0;
			char * sTagPart = DCTag + szTagPattLen;

			for (size_t szi = szTagPattLen; szi < (size_t)(iMyINFOPartsLen[0] - (DCTag - sMyINFOParts[0])); szi++)
			{
				if (DCTag[szi] == ',')
				{
					DCTag[szi] = '\0';
					if (sTagPart[1] != ':')
					{
						UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
						return;
					}

					switch (sTagPart[0])
					{
					case 'V':
						// PPK ... fix for potencial memory leak with fake tag
						if (sTagPart[2] == '\0' || pUser->m_sTagVersion)
						{
							UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
							return;
						}
						pUser->m_sTagVersion = pUser->m_sMyInfoOriginal + ((sTagPart + 2) - ServerManager::m_pGlobalBuffer);
						pUser->m_ui8TagVersionLen = (uint8_t)((DCTag + szi) - (sTagPart + 2));
						reqVals++;
						break;
					case 'M':
						if ((pUser->m_ui32BoolBits & User::BIT_IPV6) == User::BIT_IPV6 && (pUser->m_ui32SupportBits & User::SUPPORTBIT_IP64) == User::SUPPORTBIT_IP64)
						{
							if (sTagPart[2] == '\0' || sTagPart[3] == '\0' || sTagPart[4] != '\0')
							{
								UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
								return;
							}
							pUser->m_sModes[0] = sTagPart[2];
							pUser->m_sModes[1] = sTagPart[3];
							pUser->m_sModes[2] = '\0';

							if (toupper(sTagPart[3]) == 'A')
							{
								pUser->m_ui32BoolBits |= User::BIT_IPV6_ACTIVE;
							}
							else
							{
								pUser->m_ui32BoolBits &= ~User::BIT_IPV6_ACTIVE;
							}
						}
						else
						{
							if (sTagPart[2] == '\0' || sTagPart[3] != '\0')
							{
								UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
								return;
							}
							pUser->m_sModes[0] = sTagPart[2];
							pUser->m_sModes[1] = '\0';
						}

						if (toupper(sTagPart[2]) == 'A')
						{
							pUser->m_ui32BoolBits |= User::BIT_IPV4_ACTIVE;
						}
						else
						{
							pUser->m_ui32BoolBits &= ~User::BIT_IPV4_ACTIVE;
						}

						reqVals++;
						break;
					case 'H':
					{
						if (sTagPart[2] == '\0')
						{
							UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
							return;
						}

						DCTag[szi] = '/';

						char *sHubsParts[] = { NULL, NULL, NULL };
						uint16_t iHubsPartsLen[] = { 0, 0, 0 };

						uint8_t ui8Part = 0;

						sHubsParts[ui8Part] = sTagPart + 2;


						for (uint32_t ui32j = 3; ui32j < (uint32_t)((DCTag + szi + 1) - sTagPart); ui32j++)
						{
							if (sTagPart[ui32j] == '/')
							{
								sTagPart[ui32j] = '\0';
								iHubsPartsLen[ui8Part] = (uint16_t)((sTagPart + ui32j) - sHubsParts[ui8Part]);

								// are we on end of hubs tag part ???
								if (ui8Part == 2)
									break;

								ui8Part++;
								sHubsParts[ui8Part] = sTagPart + ui32j + 1;
							}
						}

						if (sHubsParts[0] != NULL && sHubsParts[1] != NULL && sHubsParts[2] != NULL)
						{
							if (iHubsPartsLen[0] != 0 && iHubsPartsLen[1] != 0 && iHubsPartsLen[2] != 0)
							{
								if (HaveOnlyNumbers(sHubsParts[0], iHubsPartsLen[0]) == false ||
								        HaveOnlyNumbers(sHubsParts[1], iHubsPartsLen[1]) == false ||
								        HaveOnlyNumbers(sHubsParts[2], iHubsPartsLen[2]) == false)
								{
									UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
									return;
								}
								pUser->m_ui32NormalHubs = atoi(sHubsParts[0]);
								pUser->m_ui32RegHubs = atoi(sHubsParts[1]);
								pUser->m_ui32OpHubs = atoi(sHubsParts[2]);
								pUser->m_ui32Hubs = pUser->m_ui32NormalHubs + pUser->m_ui32RegHubs + pUser->m_ui32OpHubs;
								// PPK ... kill LAM3R with fake hubs
								if (pUser->m_ui32Hubs != 0)
								{
									pUser->m_ui32BoolBits &= ~User::BIT_OLDHUBSTAG;
									reqVals++;
									break;
								}
							}
						}
						else if (sHubsParts[1] == DCTag + szi + 1 && sHubsParts[2] == NULL)
						{
							DCTag[szi] = '\0';
							pUser->m_ui32Hubs = atoi(sHubsParts[0]);
							reqVals++;
							pUser->m_ui32BoolBits |= User::BIT_OLDHUBSTAG;
							break;
						}

						pUser->SendFormat("UserParseMyInfo2", false, "<%s> %s!|", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], LanguageManager::m_Ptr->m_sTexts[LAN_FAKE_TAG]);

						pUser->m_sTag[pUser->m_ui8TagLen] = '\0';
						UdpDebug::m_Ptr->BroadcastFormat("[SYS] User %s (%s) with fake Tag disconnected: %s", pUser->m_sNick, pUser->m_sIP, pUser->m_sTag);

						//
						// [-] FlylinkDC++ fix User JuniorSD_R166 (178.68.64.129) with fake Tag disconnected: <FlylinkDC++ V:r503-x64-19807,M ,H:0/0/0,S:15>


						// pUser->Close();
						// return;
						//
						pUser->m_ui32NormalHubs = 1; // [+]FlylinkDC++
						if (pUser->m_ui32Hubs == 0)
						{
							pUser->m_ui32Hubs = 1;
						}
						break;
					}
					case 'S':
					{
						if (sTagPart[2] == '\0')
						{
							UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
							return;
						}
						const uint16_t l_slot_len = uint16_t(strlen(sTagPart + 2));
						if (l_slot_len > 12)
						{
							pUser->m_is_bad_len_number_myinfo = true;
						}
						if (HaveOnlyNumbers(sTagPart + 2, l_slot_len) == false)
						{
							UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
							return;
						}
						pUser->m_ui32Slots = atoi(sTagPart + 2);
						reqVals++;
						break;
					}
					case 'O':
						if (sTagPart[2] == '\0')
						{
							UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
							return;
						}
						if (strlen(sTagPart) > 12)
						{
							pUser->m_is_bad_len_number_myinfo = true;
						}
						pUser->m_ui32OLimit = atoi(sTagPart + 2);
						break;
					case 'B':
					case 'L':
						if (sTagPart[2] == '\0')
						{
							UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
							return;
						}
						if (strlen(sTagPart) > 12)
						{
							pUser->m_is_bad_len_number_myinfo = true;
						}
						pUser->m_ui32LLimit = atoi(sTagPart + 2);
						break;
					case 'D':
						if (sTagPart[2] == '\0')
						{
							UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
							return;
						}
						if (strlen(sTagPart) > 12)
						{
							pUser->m_is_bad_len_number_myinfo = true;
						}
						pUser->m_ui32DLimit = atoi(sTagPart + 2);
						break;
					default:
						//UdpDebug::m_Ptr->BroadcastFormat("[SYS] %s (%s): Extra info in DC tag: %s", pUser->Nick, pUser->m_sIP, sTag);
						break;
					}
					sTagPart = DCTag + szi + 1;
				}
			}

			if (reqVals < 4)
			{
				UserSetBadTag(pUser, pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
				return;
			}
			else
			{
				pUser->m_sDescription = pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer);
				pUser->m_ui8DescriptionLen = (uint8_t)(DCTag - sMyINFOParts[0]);
				return;
			}
		}
		else
		{
			pUser->m_sDescription = pUser->m_sMyInfoOriginal + (sMyINFOParts[0] - ServerManager::m_pGlobalBuffer);
			pUser->m_ui8DescriptionLen = (uint8_t)iMyINFOPartsLen[0];
		}
	}

	pUser->m_sClient = (char *)sOtherNoTag;
	pUser->m_ui8ClientLen = 14;

	pUser->m_sTag = NULL;
	pUser->m_ui8TagLen = 0;

	pUser->m_sTagVersion = NULL;
	pUser->m_ui8TagVersionLen = 0;

	pUser->m_sModes[0] = '\0';
	pUser->m_ui32Hubs = 0;
	pUser->m_ui32NormalHubs = 0;
	pUser->m_ui32RegHubs = 0;
	pUser->m_ui32OpHubs = 0;
	pUser->m_ui32Slots = 0;
	pUser->m_ui32OLimit = 0;
	pUser->m_ui32LLimit = 0;
	pUser->m_ui32DLimit = 0;
}
//---------------------------------------------------------------------------

UserBan::UserBan() : m_sMessage(NULL), m_ui32Len(0), m_ui32NickHash(0)
{
	// ...
}
//---------------------------------------------------------------------------

UserBan::~UserBan()
{
	safe_free(m_sMessage);
}
//---------------------------------------------------------------------------

UserBan * UserBan::CreateUserBan(const char * sMess, const uint32_t ui32MessLen, const uint32_t ui32Hash)
{
	UserBan * pUserBan = new (std::nothrow) UserBan();

	if (pUserBan == NULL)
	{
		AppendDebugLog("%s - [MEM] Cannot allocate new pUserBan in UserBan::CreateUserBan\n");

		return NULL;
	}

	pUserBan->m_sMessage = (char *)malloc(ui32MessLen + 1);
	if (pUserBan->m_sMessage == NULL)
	{
		AppendDebugLogFormat("[MEM] UserBan::CreateUserBan cannot allocate %u bytes for sMessage\n", ui32MessLen + 1);

		delete pUserBan;
		return NULL;
	}

	memcpy(pUserBan->m_sMessage, sMess, ui32MessLen);
	pUserBan->m_sMessage[ui32MessLen] = '\0';

	pUserBan->m_ui32Len = ui32MessLen;
	pUserBan->m_ui32NickHash = ui32Hash;

	return pUserBan;
}
//---------------------------------------------------------------------------

LoginLogout::LoginLogout() : m_ui64LogonTick(0), m_ui64IPv4CheckTick(0), m_pBan(NULL), m_pBuffer(NULL), m_ui32ToCloseLoops(0), m_ui32UserConnectedLen(0)
{
	// ...
}
//---------------------------------------------------------------------------
void LoginLogout::Clean()
{
	safe_delete(m_pBan);
	safe_free(m_pBuffer);
}
LoginLogout::~LoginLogout()
{
	Clean();
}
//---------------------------------------------------------------------------

User::User() : m_ui64SharedSize(0), m_ui64ChangedSharedSizeShort(0), m_ui64ChangedSharedSizeLong(0), m_ui64GetNickListsTick(0), m_ui64MyINFOsTick(0), m_ui64SearchsTick(0),
	m_ui64ChatMsgsTick(0), m_ui64PMsTick(0), m_ui64SameSearchsTick(0), m_ui64SamePMsTick(0), m_ui64SameChatsTick(0), m_ui64LastMyINFOSendTick(0), m_ui64LastNicklist(0), m_ui64ReceivedPmTick(0),
	m_ui64ChatMsgsTick2(0), m_ui64PMsTick2(0), m_ui64SearchsTick2(0), m_ui64MyINFOsTick2(0), m_ui64CTMsTick(0), m_ui64CTMsTick2(0), m_ui64RCTMsTick(0), m_ui64RCTMsTick2(0),
	m_ui64SRsTick(0), m_ui64SRsTick2(0), m_ui64RecvsTick(0), m_ui64RecvsTick2(0), m_ui64ChatIntMsgsTick(0), m_ui64PMsIntTick(0), m_ui64SearchsIntTick(0),
	m_tLoginTime(0),
	m_pCmdToUserStrt(NULL), m_pCmdToUserEnd(NULL), m_pCmdStrt(NULL), m_pCmdEnd(NULL), m_pCmdActive4Search(NULL), m_pCmdActive6Search(NULL), m_pCmdPassiveSearch(NULL),
	m_pPrev(NULL), m_pNext(NULL), m_pHashTablePrev(NULL), m_pHashTableNext(NULL), m_pHashIpTablePrev(NULL), m_pHashIpTableNext(NULL),
	m_sNick((char *)sDefaultNick), m_sMyInfoOriginal(NULL), m_sMyInfoShort(NULL), m_sMyInfoLong(NULL),
	m_sDescription(NULL), m_sTag(NULL), m_sConnection(NULL), m_sEmail(NULL), m_sClient((char *)sOtherNoTag), m_sTagVersion(NULL),
	m_sLastChat(NULL), m_sLastPM(NULL), m_sLastSearch(NULL), m_pSendBuf(NULL), m_pRecvBuf(NULL), m_pSendBufHead(NULL),
	m_sChangedDescriptionShort(NULL), m_sChangedDescriptionLong(NULL), m_sChangedTagShort(NULL), m_sChangedTagLong(NULL),
	m_sChangedConnectionShort(NULL), m_sChangedConnectionLong(NULL), m_sChangedEmailShort(NULL), m_sChangedEmailLong(NULL),
	m_ui32Recvs(0), m_ui32Recvs2(0),
	m_ui32Hubs(0), m_ui32Slots(0), m_ui32OLimit(0), m_ui32LLimit(0), m_ui32DLimit(0), m_ui32NormalHubs(0), m_ui32RegHubs(0), m_ui32OpHubs(0),
	m_ui32SendCalled(0), m_ui32RecvCalled(0), m_ui32ReceivedPmCount(0), m_ui32SR(0), m_ui32DefloodWarnings(0),
	m_ui32BoolBits(0), m_ui32InfoBits(0), m_ui32SupportBits(0),
	m_ui32SendBufLen(0), m_ui32RecvBufLen(0), m_ui32SendBufDataLen(0), m_ui32RecvBufDataLen(0),
	m_ui32NickHash(0), m_i32Profile(-1),
#ifdef _WIN32
	m_Socket(INVALID_SOCKET),
#else
	m_Socket(-1),
#endif
	m_ui16MyInfoOriginalLen(0), m_ui16MyInfoShortLen(0), m_ui16MyInfoLongLen(0), m_ui16GetNickLists(0), m_ui16MyINFOs(0), m_ui16Searchs(0),
	m_ui16ChatMsgs(0), m_ui16PMs(0), m_ui16SameSearchs(0), m_ui16LastSearchLen(0), m_ui16SamePMs(0), m_ui16LastPMLen(0), m_ui16SameChatMsgs(0),
	m_ui16LastChatLen(0), m_ui16LastPmLines(0), m_ui16SameMultiPms(0), m_ui16LastChatLines(0), m_ui16SameMultiChats(0), m_ui16ChatMsgs2(0), m_ui16PMs2(0),
	m_ui16Searchs2(0), m_ui16MyINFOs2(0), m_ui16CTMs(0), m_ui16CTMs2(0), m_ui16RCTMs(0), m_ui16RCTMs2(0), m_ui16SRs(0), m_ui16SRs2(0), m_ui16ChatIntMsgs(0),
	m_ui16PMsInt(0), m_ui16SearchsInt(0), m_ui16IpTableIdx(0),
	m_ui8MagicByte(0),
	m_ui8NickLen(9), m_ui8IpLen(0), m_ui8ConnectionLen(0), m_ui8DescriptionLen(0), m_ui8EmailLen(0), m_ui8TagLen(0), m_ui8ClientLen(14), m_ui8TagVersionLen(0),
	m_ui8Country(246), m_ui8State(User::STATE_SOCKET_ACCEPTED), m_ui8IPv4Len(0),
	m_ui8ChangedDescriptionShortLen(0), m_ui8ChangedDescriptionLongLen(0), m_ui8ChangedTagShortLen(0), m_ui8ChangedTagLongLen(0),
	m_ui8ChangedConnectionShortLen(0), m_ui8ChangedConnectionLongLen(0), m_ui8ChangedEmailShortLen(0), m_ui8ChangedEmailLongLen(0),
	m_last_recv_tick(0),m_is_invalid_json(false), m_is_json_user(false)

#ifdef FLYLINKDC_USE_VERSION
	,m_sVersion(NULL)
#endif
{
	m_ui32BoolBits |= User::BIT_IPV4_ACTIVE;
	m_ui32BoolBits |= User::BIT_OLDHUBSTAG;

	time(&m_tLoginTime);

	memset(&m_ui128IpHash, 0, 16);

	m_sIP[0] = '\0';
	m_sIPv4[0] = '\0';
	m_sModes[0] = '\0';
#ifdef USE_FLYLINKDC_EXT_JSON
	m_user_ext_info = nullptr;
	m_is_invalid_json = false;
	m_is_json_user = false;
#endif
	m_is_bad_len_port = false;
	m_is_bad_len_number_myinfo = false;
	m_is_bad_port = false;
	m_is_ddos_udp = false;
	m_is_proxy_user = false;
	m_is_max_int8 = false;
	m_is_max_ip_len = false;
	GlobalDataQueue::m_Ptr->m_flylinkdc_hub_users.Add({}).Increment();
}
//---------------------------------------------------------------------------

User::~User()
{
	GlobalDataQueue::m_Ptr->m_flylinkdc_hub_users.Add({}).Decrement();
	free(m_pRecvBuf);
	free(m_pSendBuf);
	free(m_sLastChat);
	free(m_sLastPM);
	free(m_sMyInfoShort);
	free(m_sMyInfoLong);
	free(m_sMyInfoOriginal);
#ifdef FLYLINKDC_USE_VERSION
	free(m_sVersion);
#endif
	free(m_sChangedDescriptionShort);
	free(m_sChangedDescriptionLong);
	free(m_sChangedTagShort);
	free(m_sChangedTagLong);
	free(m_sChangedConnectionShort);
	free(m_sChangedConnectionLong);
	free(m_sChangedEmailShort);
	free(m_sChangedEmailLong);

	if (((m_ui32SupportBits & User::SUPPORTBIT_ZPIPE) == User::SUPPORTBIT_ZPIPE) == true)
		DcCommands::m_Ptr->m_ui32StatZPipe--;

	ServerManager::m_ui32Parts++;

#ifdef _BUILD_GUI
	if (::SendMessage(MainWindowPageUsersChat::m_Ptr->m_hWndPageItems[MainWindowPageUsersChat::BTN_SHOW_COMMANDS], BM_GETCHECK, 0, 0) == BST_CHECKED)
	{
		RichEditAppendText(MainWindowPageUsersChat::m_Ptr->m_hWndPageItems[MainWindowPageUsersChat::REDT_CHAT], ("x User removed: " + std::to_string(m_sNick) + " (Socket " + std::to_string(m_Socket) + ")").c_str());
	}
#endif

	if (m_sNick != sDefaultNick)
	{
		free(m_sNick);
	}

	User::DeletePrcsdUsrCmd(m_pCmdActive4Search);
	User::DeletePrcsdUsrCmd(m_pCmdActive6Search);
	User::DeletePrcsdUsrCmd(m_pCmdPassiveSearch);

	PrcsdUsrCmd * cur = NULL,
	              * next = m_pCmdStrt;

	while (next != NULL)
	{
		cur = next;
		next = cur->m_pNext;

		safe_free(cur->m_sCommand);

		delete cur;
	}

	PrcsdToUsrCmd * curto = NULL,
	                * nextto = m_pCmdToUserStrt;

	while (nextto != NULL)
	{
		curto = nextto;
		nextto = curto->m_pNext;

		safe_free(curto->m_sCommand);

		delete curto;
	}


	m_pCmdToUserStrt = nullptr;
	m_pCmdToUserEnd = nullptr;
#ifdef USE_FLYLINKDC_EXT_JSON
	safe_delete(m_user_ext_info);
	m_is_json_user = false;
#endif
}
//---------------------------------------------------------------------------

bool User::MakeLock()
{
	// This code computes the valid Lock string including the Pk= string
	// For maximum speed we just find two random numbers - start and step
	// Step is added each cycle to the start and the ascii 122 boundary is
	// checked. If overflow occurs then the overflowed value is added to the
	// ascii 48 value ("0") and continues.
	// The lock has fixed length 63 bytes

#ifdef _WIN32
#ifdef _BUILD_GUI
#ifndef _M_X64
	static const char sLock[] = "$Lock EXTENDEDPROTOCOL                           win Pk=PtokaX|";
#else
	static const char sLock[] = "$Lock EXTENDEDPROTOCOL                           wg6 Pk=PtokaX|";
#endif
#else
#ifndef _M_X64
	static const char sLock[] = "$Lock EXTENDEDPROTOCOL                           wis Pk=PtokaX|";
#elif _M_ARM
	static const char sLock[] = "$Lock EXTENDEDPROTOCOL                           wsa Pk=PtokaX|";
#else
	static const char sLock[] = "$Lock EXTENDEDPROTOCOL                           ws6 Pk=PtokaX|";
#endif
#endif
#else
	static const char sLock[] = "$Lock EXTENDEDPROTOCOL                           nix Pk=PtokaX|";
#endif
	static const size_t szLockLen = sizeof(sLock) - 1;

	size_t szAllignLen = Allign(m_ui32SendBufDataLen + szLockLen);

	char * pOldBuf = m_pSendBuf;
	m_pSendBuf = (char *)realloc(pOldBuf, szAllignLen);
	if (m_pSendBuf == NULL)
	{
		m_pSendBuf = pOldBuf;
		m_ui32BoolBits |= BIT_ERROR;

		AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes in User::MakeLock\n", szAllignLen);

		return false;
	}
	m_ui32SendBufLen = (uint32_t)(szAllignLen - 1);
	m_pSendBufHead = m_pSendBuf;

	// append data to the buffer
	memcpy(m_pSendBuf, sLock, szLockLen);
	m_ui32SendBufDataLen += szLockLen;
	m_pSendBuf[m_ui32SendBufDataLen] = '\0';

	for (uint8_t ui8i = 22; ui8i < 49; ui8i++)
	{
#ifdef _WIN32
		m_pSendBuf[ui8i] = (char)((rand() % 74) + 48);
#else
		m_pSendBuf[ui8i] = (char)((random() % 74) + 48);
#endif
	}

//  Memo(string(m_pSendBuf, m_ui32SendBufDataLen));

	m_LogInOut.m_pBuffer = (char *)malloc(64);
	if (m_LogInOut.m_pBuffer == NULL)
	{
		AppendDebugLog("%s - [MEM] Cannot allocate 64 bytes for pBuffer in User::MakeLock\n");
		return false;
	}

	memcpy(m_LogInOut.m_pBuffer, m_pSendBuf, szLockLen);
	m_LogInOut.m_pBuffer[szLockLen] = '\0';

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

bool User::DoRecv()
{
	if ((m_ui32BoolBits & BIT_ERROR) == BIT_ERROR || m_ui8State >= STATE_CLOSING)
		return false;

#ifdef _WIN32
	u_long iAvailBytes = 0;
	if (ioctlsocket(m_Socket, FIONREAD, &iAvailBytes) == SOCKET_ERROR)
	{
		int iError = WSAGetLastError();
#else
	int iAvailBytes = 0;
	if (ioctl(m_Socket, FIONREAD, &iAvailBytes) == -1)
	{
#endif
		UdpDebug::m_Ptr->BroadcastFormat("[ERR] %s (%s): ioctlsocket(FIONREAD) error %s (%d). User is being closed.", m_sNick, m_sIP,
#ifdef _WIN32
		                                 WSErrorStr(iError), iError);
#else
		                                 ErrnoStr(errno), errno);
#endif
		m_ui32BoolBits |= BIT_ERROR;
		Close();
		return false;
	}

	// PPK ... check flood ...
	if (iAvailBytes != 0 && ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::NODEFLOODRECV) == false)
	{
		if (SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_ACTION] != 0)
		{
			if (m_ui32Recvs == 0)
			{
				m_ui64RecvsTick = ServerManager::m_ui64ActualTick;
			}

			m_ui32Recvs += iAvailBytes;

			if (DeFloodCheckForDataFlood(this, DEFLOOD_MAX_DOWN, SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_ACTION],
			                             m_ui32Recvs, m_ui64RecvsTick, SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_KB],
			                             (uint32_t)SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_TIME]) == true)
			{
				return false;
			}

			if (m_ui32Recvs != 0)
			{
				m_ui32Recvs -= iAvailBytes;
			}
		}

		if (SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_ACTION2] != 0)
		{
			if (m_ui32Recvs2 == 0)
			{
				m_ui64RecvsTick2 = ServerManager::m_ui64ActualTick;
			}

			m_ui32Recvs2 += iAvailBytes;

			if (DeFloodCheckForDataFlood(this, DEFLOOD_MAX_DOWN, SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_ACTION2],
			                             m_ui32Recvs2, m_ui64RecvsTick2, SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_KB2],
			                             (uint32_t)SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_DOWN_TIME2]) == true)
			{
				return false;
			}

			if (m_ui32Recvs2 != 0)
			{
				m_ui32Recvs2 -= iAvailBytes;
			}
		}
	}

	if (iAvailBytes == 0)
	{
		const int64_t l_delta = ServerManager::m_ui64ActualTick - m_last_recv_tick;
		if (l_delta > 5 || l_delta < 0)
		{
			m_last_recv_tick = ServerManager::m_ui64ActualTick;
			// we need to try recv to catch connection error or closed connection
			iAvailBytes = 16;
		}
		else
		{
#ifdef _WIN32
			//printf("[%5d] Skip revc %d \r\n", GetTickCount(), m_Socket);
#endif
			return true;
		}
	}
	else if (iAvailBytes > 8 * 1024)
	{
		// receive max. 8k bytes to receive buffer
		iAvailBytes = 8 * 1024;
	}

	size_t szAllignLen = 0;

	if (m_ui32RecvBufLen < m_ui32RecvBufDataLen + iAvailBytes)
	{
		szAllignLen = Allign(m_ui32RecvBufDataLen + iAvailBytes);
	}
	else if (m_ui32RecvCalled > 60)
	{
		szAllignLen = Allign(m_ui32RecvBufDataLen + iAvailBytes);
		if (m_ui32RecvBufLen <= szAllignLen)
		{
			szAllignLen = 0;
		}

		m_ui32RecvCalled = 0;
	}

	if (szAllignLen != 0)
	{
		char * pOldBuf = m_pRecvBuf;

		m_pRecvBuf = (char *)realloc(pOldBuf, szAllignLen);
		if (m_pRecvBuf == NULL)
		{
			m_pRecvBuf = pOldBuf;
			m_ui32BoolBits |= BIT_ERROR;
			Close();

			AppendDebugLogFormat("[MEM] Cannot (re)allocate %zu bytes for pRecvBuf in User::DoRecv\n", szAllignLen);

			return false;
		}

		m_ui32RecvBufLen = (uint32_t)(szAllignLen - 1);
	}

	// receive new data to pRecvBuf
	const int recvlen = recv(m_Socket, m_pRecvBuf + m_ui32RecvBufDataLen, m_ui32RecvBufLen - m_ui32RecvBufDataLen, 0);
	GlobalDataQueue::m_Ptr->PrometheusRecvBytes("user",recvlen);
	m_ui32RecvCalled++;

#ifdef _WIN32
//#ifdef _DEBUG
	//printf("[%5d] recv %d recvlen = %d\r\n", GetTickCount(), m_Socket, recvlen);
//#endif
	if (recvlen == SOCKET_ERROR)
	{
		int iError = WSAGetLastError();
		if (iError != WSAEWOULDBLOCK)
		{
#else
	if (recvlen == -1)
	{
		if (errno != EAGAIN)
		{
#endif
			UdpDebug::m_Ptr->BroadcastFormat("[ERR] %s (%s): recv() error %s (%d). User is being closed.", m_sNick, m_sIP,
#ifdef _WIN32
			                                 WSErrorStr(iError), iError);
#else
			                                 ErrnoStr(errno), errno);
#endif
			m_ui32BoolBits |= BIT_ERROR;
			Close();
			return false;
		}
		else
		{
			return false;
		}
	}
	else if (recvlen == 0)    // regular close
	{
#ifdef _WIN32
#ifdef _BUILD_GUI
		if (::SendMessage(MainWindowPageUsersChat::m_Ptr->m_hWndPageItems[MainWindowPageUsersChat::BTN_SHOW_COMMANDS], BM_GETCHECK, 0, 0) == BST_CHECKED)
		{
			int iRet = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "- User has closed the connection: %s (%s)", m_sNick, m_sIP);
			if (iRet > 0)
			{
				RichEditAppendText(MainWindowPageUsersChat::m_Ptr->m_hWndPageItems[MainWindowPageUsersChat::REDT_CHAT], ServerManager::m_pGlobalBuffer);
			}
		}
#endif
#endif

		m_ui32BoolBits |= BIT_ERROR;
		Close();
		return false;
	}

	m_ui32Recvs += recvlen;
	m_ui32Recvs2 += recvlen;
	ServerManager::m_ui64BytesRead += recvlen;
	m_ui32RecvBufDataLen += recvlen;
	m_pRecvBuf[m_ui32RecvBufDataLen] = '\0';
	if (UserProcessLines(this, m_ui32RecvBufDataLen - recvlen) == true)
	{
		return true;
	}

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

void User::SendChar(const char * sText, const size_t szTextLen)
{
	if (m_ui8State >= STATE_CLOSING || szTextLen == 0)
		return;

	if (((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == false || szTextLen < ZMINDATALEN)
	{
		if (PutInSendBuf(sText, szTextLen))
		{
			Try2Send();
		}
	}
	else
	{
		uint32_t iLen = 0;
		char *sData = ZlibUtility::m_Ptr->CreateZPipe(sText, szTextLen, iLen);

		if (iLen == 0)
		{
			if (PutInSendBuf(sText, szTextLen))
			{
				Try2Send();
			}
		}
		else
		{
			ServerManager::m_ui64BytesSentSaved += szTextLen - iLen;
			if (PutInSendBuf(sData, iLen))
			{
				Try2Send();
			}
		}
	}
}
//---------------------------------------------------------------------------
#ifdef _WIN32
static const char g_badChars[] =
{
	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
	31, '<', '>', '\\', '/', '"', '|', '?', '*', 0
};
#else
static const char g_badChars[] =
{
	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
	31, '<', '>', '\\', '/', '"', '|', '?', '*', 0
};
#endif

#ifdef USE_FLYLINKDC_EXT_JSON
void User::SendCharDelayedExtJSON()
{
	if (isSupportExtJSON())
	{
		if (!Users::m_Ptr->m_AllExtJSON.empty())
		{
			SendCharDelayed(Users::m_Ptr->m_AllExtJSON.c_str(), Users::m_Ptr->m_AllExtJSON.size());
#ifdef _WIN32
#ifdef _PtokaX_TESTING_
			//  string l_log = string(Users::m_Ptr->m_AllExtJSON.c_str(), Users::m_Ptr->m_AllExtJSON.size());
			//  l_log += "\r\n";
			//  AppendDebugLog(l_log.c_str());
#endif
#endif
		}
	}
}
#endif // USE_FLYLINKDC_EXT_JSON
//---------------------------------------------------------------------------

void User::logInvalidUser(int p_id, const char * p_buffer, int p_len, bool p_is_send)
{

	return;
#if 0
	std::string l_name = "/home/dc/src/PtokaX/logs/";
	if (p_is_send)
		l_name += "send-";
	else
		l_name += "recv-";
	l_name += m_sIP;
	l_name += "_";
	if (m_sNick)
	{
		std::string l_nick = m_sNick;
		std::string::size_type i = 0;
		while ((i = l_nick.find_first_of(g_badChars, i)) != std::string::npos)
		{
			l_nick[i] = '_';
			i++;
		}
		l_name += l_nick;
	}
	if (m_is_invalid_json)
		l_name += "-error-json";
	if (m_is_proxy_user)
		l_name += "-proxy-user";
	if (m_is_bad_len_port)
		l_name += "-error-len-port";
	if (m_is_bad_len_number_myinfo)
		l_name += "-error-len-MYINFO";
	if (m_is_bad_port)
		l_name += "-error-port";
	if (m_is_ddos_udp)
		l_name += "-error-DDOS-UDP";
	if (m_is_max_int8)
		l_name += "-error-MAX_INT8";
	if (m_is_max_ip_len)
		l_name += "-error-MAX_IP_LEN";

	l_name += ".log";
	std::ofstream l_file_out(l_name, std::ios_base::app);
	if (!l_file_out.is_open())
	{
#ifndef _WIN32
		syslog(LOG_NOTICE, "Error create %s code = %d", l_name.c_str(), errno);
#endif
		printf("Error create %s code = %d\r\n", l_name.c_str(), errno);
	}
	else
	{
		//l_file_out.seekp(0, std::ios_base::end);
		time_t acc_time;
		time(&acc_time);
		struct tm * acc_tm;
		acc_tm = localtime(&acc_time);
		char sBuf[64];
		sBuf[0] = 0;
		strftime(sBuf, 64, "\r\n%c :::::", acc_tm);
		l_file_out << sBuf << " Nick = [ " << m_sNick <<
		           " ] RecvBufDataLen = " << m_ui32RecvBufDataLen <<
		           " RecvBufLen = " << m_ui32RecvBufLen <<
		           " ] SendBufDataLen = " << m_ui32SendBufDataLen <<
		           " SendBufLen = " << m_ui32SendBufLen <<
		           " Command: " << p_id << ": ";
		l_file_out.write(p_buffer, p_len);
		//l_file_out << std::endl;
	}
#endif
}
//---------------------------------------------------------------------------

void User::SendCharDelayed(const char * sText, const size_t szTextLen)
{
	if (m_ui8State >= STATE_CLOSING || szTextLen == 0)
	{
		return;
	}

	if (((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == false || szTextLen < ZMINDATALEN)
	{
		PutInSendBuf(sText, szTextLen);
	}
	else
	{
		uint32_t iLen = 0;
		char *sPipeData = ZlibUtility::m_Ptr->CreateZPipe(sText, szTextLen, iLen);

		if (iLen == 0)
		{
			PutInSendBuf(sText, szTextLen);
		}
		else
		{
			PutInSendBuf(sPipeData, iLen);
			ServerManager::m_ui64BytesSentSaved += szTextLen - iLen;
		}
	}
}
//---------------------------------------------------------------------------

void User::SendTextDelayed(const string &sText)
{
	SendCharDelayed(sText.c_str(), sText.size());
}

void User::SendFormat(const char * sFrom, const bool bDelayed, const char * sFormatMsg, ...)
{
	if (m_ui8State >= STATE_CLOSING)
	{
		return;
	}

	va_list vlArgs;
	va_start(vlArgs, sFormatMsg);

	int iRet = vsnprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, sFormatMsg, vlArgs);

	va_end(vlArgs);

	if (iRet <= 0)
	{
		AppendDebugLogFormat("[ERR] vsnprintf wrong value %d in User::SendFormatDelayed from: %s\n", iRet, sFrom);

		return;
	}

	if (((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == false || (size_t)iRet < ZMINDATALEN)
	{
		if (PutInSendBuf(ServerManager::m_pGlobalBuffer, iRet) == true && bDelayed == false)
		{
			Try2Send();
		}
	}
	else
	{
		uint32_t iLen = 0;
		char *sData = ZlibUtility::m_Ptr->CreateZPipe(ServerManager::m_pGlobalBuffer, iRet, iLen);

		if (iLen == 0)
		{
			if (PutInSendBuf(ServerManager::m_pGlobalBuffer, iRet) == true && bDelayed == false)
			{
				Try2Send();
			}
		}
		else
		{
			if (PutInSendBuf(sData, iLen) == true && bDelayed == false)
			{
				Try2Send();
			}
			ServerManager::m_ui64BytesSentSaved += iRet - iLen;
		}
	}
}
//---------------------------------------------------------------------------

void User::SendFormatCheckPM(const char * sFrom, const char * sOtherNick, const bool bDelayed, const char * sFormatMsg, ...)
{
	if (m_ui8State >= STATE_CLOSING)
	{
		return;
	}

	int iMsgLen = 0;

	if (sOtherNick != NULL)
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "$To: %s From: %s $", m_sNick, sOtherNick);
		if (iMsgLen <= 0)
		{
			AppendDebugLogFormat("[ERR] snprintf wrong value %d in User::SendFormatCheckPM from: %s\n", iMsgLen, sFrom);

			return;
		}
	}

	va_list vlArgs;
	va_start(vlArgs, sFormatMsg);

	int iRet = vsnprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, sFormatMsg, vlArgs);

	va_end(vlArgs);

	if (iRet <= 0)
	{
		AppendDebugLogFormat("[ERR] vsnprintf wrong value %d in User::SendFormatCheckPM from: %s\n", iRet, sFrom);

		return;
	}

	iMsgLen += iRet;

	if (((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == false || (size_t)iMsgLen < ZMINDATALEN)
	{
		if (PutInSendBuf(ServerManager::m_pGlobalBuffer, iMsgLen) == true && bDelayed == false)
		{
			Try2Send();
		}
	}
	else
	{
		uint32_t iLen = 0;
		char *sData = ZlibUtility::m_Ptr->CreateZPipe(ServerManager::m_pGlobalBuffer, iMsgLen, iLen);

		if (iLen == 0)
		{
			if (PutInSendBuf(ServerManager::m_pGlobalBuffer, iMsgLen) == true && bDelayed == false)
			{
				Try2Send();
			}
		}
		else
		{
			if (PutInSendBuf(sData, iLen) == true && bDelayed == false)
			{
				Try2Send();
			}
			ServerManager::m_ui64BytesSentSaved += iMsgLen - iLen;
		}
	}
}
//---------------------------------------------------------------------------

bool User::PutInSendBuf(const char * sText, const size_t szTxtLen)
{
	m_ui32SendCalled++;

	size_t szAllignLen = 0;
#ifdef _DEBUG
	if (strstr(Text, "$ExtJSON ") != NULL)
	{
		int a = 0;
		a++;
	}
#endif

	if (m_ui32SendBufLen < m_ui32SendBufDataLen + szTxtLen)
	{
		if (m_pSendBuf == NULL)
		{
			szAllignLen = Allign(m_ui32SendBufDataLen + szTxtLen);
		}
		else
		{
			if ((size_t)(m_pSendBufHead - m_pSendBuf) > szTxtLen)
			{
				uint32_t offset = (uint32_t)(m_pSendBufHead - m_pSendBuf);
				memmove(m_pSendBuf, m_pSendBufHead, (m_ui32SendBufDataLen - offset));
				m_pSendBufHead = m_pSendBuf;
				m_ui32SendBufDataLen = m_ui32SendBufDataLen - offset;
			}
			else
			{
				szAllignLen = Allign(m_ui32SendBufDataLen + szTxtLen);
				size_t szMaxBufLen = (size_t)(((m_ui32BoolBits & BIT_BIG_SEND_BUFFER) == BIT_BIG_SEND_BUFFER) == true ?
				                              ((Users::m_Ptr->m_ui32MyInfosTagLen > Users::m_Ptr->m_ui32MyInfosLen ? Users::m_Ptr->m_ui32MyInfosTagLen : Users::m_Ptr->m_ui32MyInfosLen) * 2) :
					                              (Users::m_Ptr->m_ui32MyInfosTagLen > Users::m_Ptr->m_ui32MyInfosLen ? Users::m_Ptr->m_ui32MyInfosTagLen : Users::m_Ptr->m_ui32MyInfosLen));
				szMaxBufLen = szMaxBufLen < 262144 ? 262144 : szMaxBufLen;
				if (szAllignLen > szMaxBufLen)
			{
					// does the buffer size reached the maximum
					if (SettingManager::m_Ptr->m_bBools[SETBOOL_KEEP_SLOW_USERS] == false || (m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE)
					{
						// we want to drop the slow user
						m_ui32BoolBits |= BIT_ERROR;
						Close();

						UdpDebug::m_Ptr->BroadcastFormat("[SYS] %s (%s) SendBuffer overflow (AL:%zu[SL:%u|NL:%zu|FL:%zu]/ML:%zu). User disconnected.",
						                                 m_sNick, m_sIP, szAllignLen, m_ui32SendBufDataLen, szTxtLen, m_pSendBufHead-m_pSendBuf, szMaxBufLen);
						return false;
					}
					else
					{
						UdpDebug::m_Ptr->BroadcastFormat("[SYS] %s (%s) SendBuffer overflow (AL:%zu[SL:%u|NL:%zu|FL:%zu]/ML:%zu). Buffer cleared - user stays online.",
						                                 m_sNick, m_sIP, szAllignLen, m_ui32SendBufDataLen, szTxtLen, m_pSendBufHead-m_pSendBuf, szMaxBufLen);
					}

					// we want to keep the slow user online
					// PPK ... i don't want to corrupt last command, get rest of it and add to new buffer ;)
					char *sTemp = (char *)memchr(m_pSendBufHead, '|', m_ui32SendBufDataLen - (m_pSendBufHead - m_pSendBuf));
					if (sTemp != NULL)
					{
						uint32_t iOldSBDataLen = m_ui32SendBufDataLen;

						uint32_t iRestCommandLen = (uint32_t)((sTemp - m_pSendBufHead) + 1);
						if (m_pSendBuf != m_pSendBufHead)
						{
							memmove(m_pSendBuf, m_pSendBufHead, iRestCommandLen);
						}
						m_ui32SendBufDataLen = iRestCommandLen;

						// If is not needed then don't lost all data, try to find some space with removing only few oldest commands
						if (szTxtLen < szMaxBufLen && iOldSBDataLen > (uint32_t)((sTemp + 1) - m_pSendBuf) && (iOldSBDataLen - ((sTemp + 1) - m_pSendBuf)) > (uint32_t)szTxtLen)
						{
							char *sTemp1;
							// try to remove min half of send bufer
							if (iOldSBDataLen > (m_ui32SendBufLen / 2) && (uint32_t)((sTemp + 1 + szTxtLen) - m_pSendBuf) < (m_ui32SendBufLen / 2))
							{
								sTemp1 = (char *)memchr(m_pSendBuf + (m_ui32SendBufLen / 2), '|', iOldSBDataLen - (m_ui32SendBufLen / 2));
							}
							else
							{
								sTemp1 = (char *)memchr(sTemp + 1 + szTxtLen, '|', iOldSBDataLen - ((sTemp + 1 + szTxtLen) - m_pSendBuf));
							}

							if (sTemp1 != NULL)
							{
								iRestCommandLen = (uint32_t)(iOldSBDataLen - ((sTemp1 + 1) - m_pSendBuf));
								memmove(m_pSendBuf + m_ui32SendBufDataLen, sTemp1 + 1, iRestCommandLen);
								m_ui32SendBufDataLen += iRestCommandLen;
							}
						}
					}
					else
					{
						m_pSendBuf[0] = '|';
						m_pSendBuf[1] = '\0';
						m_ui32SendBufDataLen = 1;
					}

					size_t szAllignTxtLen = Allign(szTxtLen + m_ui32SendBufDataLen);

					char * pOldBuf = m_pSendBuf;
					m_pSendBuf = (char *)realloc(pOldBuf, szAllignTxtLen);
					if (m_pSendBuf == NULL)
					{
						m_pSendBuf = pOldBuf;
						m_ui32BoolBits |= BIT_ERROR;
						Close();

						AppendDebugLogFormat("[MEM] Cannot reallocate %zu bytes in User::PutInSendBuf-keepslow\n", szAllignLen);

						return false;
					}
					m_ui32SendBufLen = (uint32_t)(szAllignTxtLen - 1);
					m_pSendBufHead = m_pSendBuf;

					szAllignLen = 0;
				}
				else
				{
					szAllignLen = Allign(m_ui32SendBufDataLen + szTxtLen);
				}
			}
		}
	}
	else if (m_ui32SendCalled > 100)
	{
		szAllignLen = Allign(m_ui32SendBufDataLen + szTxtLen);
		if (m_ui32SendBufLen <= szAllignLen)
		{
			szAllignLen = 0;
		}

		m_ui32SendCalled = 0;
	}

	if (szAllignLen != 0)
	{
		const uint32_t offset = (m_pSendBuf == NULL ? 0 : (uint32_t)(m_pSendBufHead - m_pSendBuf));

		char * pOldBuf = m_pSendBuf;
		m_pSendBuf = (char *)realloc(pOldBuf, szAllignLen);
		if (m_pSendBuf == NULL)
		{
			m_pSendBuf = pOldBuf;
			m_ui32BoolBits |= BIT_ERROR;
			Close();

			AppendDebugLogFormat("[MEM] Cannot (re)allocate %zu bytes for new pSendBuf in User::PutInSendBuf\n", szAllignLen);

			return false;
		}

		m_ui32SendBufLen = (uint32_t)(szAllignLen - 1);
		m_pSendBufHead = m_pSendBuf + offset;
	}

	// append data to the buffer
	memcpy(m_pSendBuf + m_ui32SendBufDataLen, sText, szTxtLen);
	m_ui32SendBufDataLen += (uint32_t)szTxtLen;
	m_pSendBuf[m_ui32SendBufDataLen] = '\0';

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

bool User::Try2Send()
{
	if ((m_ui32BoolBits & BIT_ERROR) == BIT_ERROR || m_ui32SendBufDataLen == 0)
	{
		return false;
	}

	// compute length of unsent data
	int32_t offset = (int32_t)(m_pSendBufHead - m_pSendBuf);
	int32_t len = m_ui32SendBufDataLen - offset;

	if (offset < 0 || len < 0)
	{
		AppendDebugLogFormat("[ERR] Negative send values!\nSendBuf: %p\nPlayHead: %p\nDataLen: %u\n", m_pSendBuf, m_pSendBufHead, m_ui32SendBufDataLen);

		m_ui32BoolBits |= BIT_ERROR;
		Close();

		return false;
	}
#ifdef _DEBUG
	if (strstr(m_pSendBufHead, "$ExtJSON ") != NULL)
	{
		int a = 0;
		a++;
	}
#endif

	const int n = send(m_Socket, m_pSendBufHead, len < 32768 ? len : 32768, 0);

#ifdef _WIN32
	if (n == SOCKET_ERROR)
	{
		int iError = WSAGetLastError();
		if (iError != WSAEWOULDBLOCK)
		{
#else
	if (n == -1)
	{
		if (errno != EAGAIN)
		{
#endif
			UdpDebug::m_Ptr->BroadcastFormat("[ERR] %s (%s): send() error %s (%d). User is being closed.", m_sNick, m_sIP,
#ifdef _WIN32
			                                 WSErrorStr(iError), iError);
#else
			                                 ErrnoStr(errno), errno);
#endif
			m_ui32BoolBits |= BIT_ERROR;
			Close();
			return false;
		}
		else
		{
			return true;
		}
	}

	ServerManager::m_ui64BytesSent += n;
    GlobalDataQueue::m_Ptr->PrometheusSendBytes(__func__,n);
	
	static int g_id = 0;
	extern bool g_isUseLog;
	if (g_isUseLog)
	{
		logInvalidUser(++g_id, m_pSendBufHead, len, true);
	}

	// if buffer is sent then mark it as empty (first byte = 0)
	// else move remaining data on new place and free old buffer
	if (n < len)
	{
		m_pSendBufHead += n;
		return true;
	}
	else
	{
		// PPK ... we need to free memory allocated for big buffer on login (userlist, motd...)
		if (((m_ui32BoolBits & BIT_BIG_SEND_BUFFER) == BIT_BIG_SEND_BUFFER) == true
		        ||
		        m_ui32SendBufLen > 1024
		   )
		{
			if (m_pSendBuf != NULL)
			{
				safe_free(m_pSendBuf);
				m_pSendBufHead = m_pSendBuf;
				m_ui32SendBufLen = 0;
				m_ui32SendBufDataLen = 0;
			}
			m_ui32BoolBits &= ~BIT_BIG_SEND_BUFFER;
		}
		else
		{
			m_pSendBuf[0] = '\0';
			m_pSendBufHead = m_pSendBuf;
			m_ui32SendBufDataLen = 0;
		}
		return false;
	}
}
//---------------------------------------------------------------------------

void User::SetIP(const char * sIP)
{
	strcpy(m_sIP, sIP);
	m_ui8IpLen = (uint8_t)strlen(sIP);
}
//------------------------------------------------------------------------------

void User::SetNick(const char * sNick, const uint8_t ui8NickLen)
{
	if (m_sNick != sDefaultNick && m_sNick != NULL)
	{
		safe_free(m_sNick);
	}

	m_sNick = (char *)malloc(ui8NickLen + 1);
	if (m_sNick == NULL)
	{
		m_sNick = (char *)sDefaultNick;
		m_ui32BoolBits |= BIT_ERROR;
		Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %" PRIu8 " bytes for m_sNick in User::SetNick\n", m_ui8NickLen + 1);

		return;
	}
	memcpy(m_sNick, sNick, ui8NickLen);
	m_sNick[ui8NickLen] = '\0';
	m_ui8NickLen = ui8NickLen;
	m_ui32NickHash = HashNick(m_sNick, m_ui8NickLen);
}
//------------------------------------------------------------------------------
#ifdef USE_FLYLINKDC_EXT_JSON
void User::SetExtJSONOriginal(const char * sNewExtJSON, const uint16_t ui16NewExtJSONLen)
{
	if (m_user_ext_info)
	{
		Users::m_Ptr->DelFromExtJSONInfos(this);
		std::string l_info(sNewExtJSON, ui16NewExtJSONLen);
		m_user_ext_info->SetJSONOriginal(l_info);
		Users::m_Ptr->Add2ExtJSON(this);
	}
}
#endif
//------------------------------------------------------------------------------

void User::SetMyInfoOriginal(const char * sMyInfo, const uint16_t ui16MyInfoLen)
{
	char * sOldMyInfo = m_sMyInfoOriginal;

	const char * sOldDescription = m_sDescription;
	uint8_t ui8OldDescriptionLen = m_ui8DescriptionLen;

	char * sOldTag = m_sTag;
	uint8_t ui8OldTagLen = m_ui8TagLen;

	char * sOldConnection = m_sConnection;
	uint8_t ui8OldConnectionLen = m_ui8ConnectionLen;

	char * sOldEmail = m_sEmail;
	uint8_t ui8OldEmailLen = m_ui8EmailLen;

	uint64_t ui64OldShareSize = m_ui64SharedSize;

	if (m_sMyInfoOriginal != NULL)
	{
		m_sConnection = NULL;
		m_ui8ConnectionLen = 0;

		m_sDescription = NULL;
		m_ui8DescriptionLen = 0;

		m_sEmail = NULL;
		m_ui8EmailLen = 0;

		m_sTag = NULL;
		m_ui8TagLen = 0;

		m_sClient = NULL;
		m_ui8ClientLen = 0;

		m_sTagVersion = NULL;
		m_ui8TagVersionLen = 0;

		m_sMyInfoOriginal = NULL;
	}

	m_sMyInfoOriginal = (char *)malloc(ui16MyInfoLen + 1);
	if (m_sMyInfoOriginal == NULL)
	{
		m_ui32BoolBits |= BIT_ERROR;
		Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %hu bytes for m_sMyInfoOriginal in UserSetMyInfoOriginal\n", ui16MyInfoLen + 1);

		return;
	}
	memcpy(m_sMyInfoOriginal, sMyInfo, ui16MyInfoLen);
	m_sMyInfoOriginal[ui16MyInfoLen] = '\0';
	m_ui16MyInfoOriginalLen = ui16MyInfoLen;

	UserParseMyInfo(this);

	if (ui8OldDescriptionLen != m_ui8DescriptionLen || (m_ui8DescriptionLen > 0 && memcmp(sOldDescription, m_sDescription, m_ui8DescriptionLen) != 0))
	{
		m_ui32InfoBits |= INFOBIT_DESCRIPTION_CHANGED;
	}
	else
	{
		m_ui32InfoBits &= ~INFOBIT_DESCRIPTION_CHANGED;
	}

	if (ui8OldTagLen != m_ui8TagLen || (m_ui8TagLen > 0 && memcmp(sOldTag, m_sTag, m_ui8TagLen) != 0))
	{
		m_ui32InfoBits |= INFOBIT_TAG_CHANGED;
	}
	else
	{
		m_ui32InfoBits &= ~INFOBIT_TAG_CHANGED;
	}

	if (ui8OldConnectionLen != m_ui8ConnectionLen || (m_ui8ConnectionLen > 0 && memcmp(sOldConnection, m_sConnection, m_ui8ConnectionLen) != 0))
	{
		m_ui32InfoBits |= INFOBIT_CONNECTION_CHANGED;
	}
	else
	{
		m_ui32InfoBits &= ~INFOBIT_CONNECTION_CHANGED;
	}

	if (ui8OldEmailLen != m_ui8EmailLen || (m_ui8EmailLen > 0 && memcmp(sOldEmail, m_sEmail, m_ui8EmailLen) != 0))
	{
		m_ui32InfoBits |= INFOBIT_EMAIL_CHANGED;
	}
	else
	{
		m_ui32InfoBits &= ~INFOBIT_EMAIL_CHANGED;
	}

	if (ui64OldShareSize != m_ui64SharedSize)
	{
		m_ui32InfoBits |= INFOBIT_SHARE_CHANGED;
	}
	else
	{
		m_ui32InfoBits &= ~INFOBIT_SHARE_CHANGED;
	}

	safe_free(sOldMyInfo);

	if (((m_ui32InfoBits & INFOBIT_SHARE_SHORT_PERM) == INFOBIT_SHARE_SHORT_PERM) == false)
	{
		m_ui64ChangedSharedSizeShort = m_ui64SharedSize;
	}

	if (((m_ui32InfoBits & INFOBIT_SHARE_LONG_PERM) == INFOBIT_SHARE_LONG_PERM) == false)
	{
		m_ui64ChangedSharedSizeLong = m_ui64SharedSize;
	}

}
//------------------------------------------------------------------------------

static void UserSetMyInfoLong(User * pUser, char * sMyInfoLong, const uint16_t &ui16MyInfoLongLen)
{
	if (pUser->m_sMyInfoLong != NULL)
	{
		if (SettingManager::m_Ptr->m_ui8FullMyINFOOption != 2)
		{
			Users::m_Ptr->DelFromMyInfosTag(pUser);
		}

		safe_free(pUser->m_sMyInfoLong);

	}

	pUser->m_sMyInfoLong = (char *)malloc(ui16MyInfoLongLen + 1);
	if (pUser->m_sMyInfoLong == NULL)
	{
		pUser->m_ui32BoolBits |= User::BIT_ERROR;
		pUser->Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %hu bytes for m_sMyInfoLong in UserSetMyInfoLong\n", ui16MyInfoLongLen + 1);

		return;
	}
	memcpy(pUser->m_sMyInfoLong, sMyInfoLong, ui16MyInfoLongLen);
	pUser->m_sMyInfoLong[ui16MyInfoLongLen] = '\0';
	pUser->m_ui16MyInfoLongLen = ui16MyInfoLongLen;
}
//------------------------------------------------------------------------------

static void UserSetMyInfoShort(User * pUser, char * sMyInfoShort, const uint16_t &ui16MyInfoShortLen)
{
	if (pUser->m_sMyInfoShort != NULL)
	{
		if (SettingManager::m_Ptr->m_ui8FullMyINFOOption != 0)
		{
			Users::m_Ptr->DelFromMyInfos(pUser);
		}

		safe_free(pUser->m_sMyInfoShort);
	}

	pUser->m_sMyInfoShort = (char *)malloc(ui16MyInfoShortLen + 1);
	if (pUser->m_sMyInfoShort == NULL)
	{
		pUser->m_ui32BoolBits |= User::BIT_ERROR;
		pUser->Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %hu bytes for m_sMyInfoShort in UserSetMyInfoShort\n", ui16MyInfoShortLen + 1);

		return;
	}
	memcpy(pUser->m_sMyInfoShort, sMyInfoShort, ui16MyInfoShortLen);
	pUser->m_sMyInfoShort[ui16MyInfoShortLen] = '\0';
	pUser->m_ui16MyInfoShortLen = ui16MyInfoShortLen;
}
//------------------------------------------------------------------------------

void User::SetVersion(const char * /* sVersion*/)
{
#ifdef FLYLINKDC_USE_VERSION
	free(m_sVersion);
	const size_t szLen = strlen(sNewVer);
	m_sVersion = (char *)malloc(szLen + 1);
	if (m_sVersion == NULL)
	{
		m_ui32BoolBits |= BIT_ERROR;
		Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes for m_sVersion in User::SetVersion\n", szLen+1);

		return;
	}
	memcpy(m_sVersion, sNewVer, szLen);
	m_sVersion[szLen] = '\0';
#endif
}
//------------------------------------------------------------------------------

void User::SetLastChat(const char * sData, const size_t szLen)
{
	free(m_sLastChat);

	m_sLastChat = (char *)malloc(szLen + 1);
	if (m_sLastChat == NULL)
	{
		m_ui32BoolBits |= BIT_ERROR;
		Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes for m_sLastChat in User::SetLastChat\n", szLen+1);

		return;
	}
	memcpy(m_sLastChat, sData, szLen);
	m_sLastChat[szLen] = '\0';
	m_ui16SameChatMsgs = 1;
	m_ui64SameChatsTick = ServerManager::m_ui64ActualTick;
	m_ui16LastChatLen = (uint16_t)szLen;
	m_ui16SameMultiChats = 0;
	m_ui16LastChatLines = 0;
}
//------------------------------------------------------------------------------

void User::SetLastPM(const char * sData, const size_t szLen)
{
	free(m_sLastPM);

	m_sLastPM = (char *)malloc(szLen + 1);
	if (m_sLastPM == NULL)
	{
		m_ui32BoolBits |= BIT_ERROR;
		Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes for m_sLastPM in User::SetLastPM\n", szLen+1);

		return;
	}

	memcpy(m_sLastPM, sData, szLen);
	m_sLastPM[szLen] = '\0';
	m_ui16SamePMs = 1;
	m_ui64SamePMsTick = ServerManager::m_ui64ActualTick;
	m_ui16LastPMLen = (uint16_t)szLen;
	m_ui16SameMultiPms = 0;
	m_ui16LastPmLines = 0;
}
//------------------------------------------------------------------------------

void User::SetLastSearch(const char * sData, const size_t szLen)
{
	m_LastSearch = std::string(sData, szLen);
	m_ui16SameSearchs = 1;
	m_ui64SameSearchsTick = ServerManager::m_ui64ActualTick;
}
//------------------------------------------------------------------------------

void User::SetBuffer(const char * sKickMsg, size_t szLen /* = 0*/)
{
	if (szLen == 0)
	{
		szLen = strlen(sKickMsg);
	}

	void * pOldBuf = m_LogInOut.m_pBuffer;

	if (szLen < 512)
	{
		m_LogInOut.m_pBuffer = (char *)realloc(pOldBuf, szLen + 1);
		if (m_LogInOut.m_pBuffer == NULL)
		{
			m_ui32BoolBits |= BIT_ERROR;
			Close();

			AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes for pBuffer in User::SetBuffer\n", szLen+1);

			return;
		}
		memcpy(m_LogInOut.m_pBuffer, sKickMsg, szLen);
		m_LogInOut.m_pBuffer[szLen] = '\0';
	}
	else
	{
		m_LogInOut.m_pBuffer = (char *)realloc(pOldBuf, 512);
		if (m_LogInOut.m_pBuffer == NULL)
		{
			m_ui32BoolBits |= BIT_ERROR;
			Close();

			AppendDebugLog("%s - [MEM] Cannot allocate 512 bytes for pBuffer in User::SetBuffer\n");

			return;
		}
		memcpy(m_LogInOut.m_pBuffer, sKickMsg, 508);
		m_LogInOut.m_pBuffer[510] = '.';
		m_LogInOut.m_pBuffer[509] = '.';
		m_LogInOut.m_pBuffer[508] = '.';
		m_LogInOut.m_pBuffer[511] = '\0';
	}
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void User::FreeBuffer()
{
	safe_free(m_LogInOut.m_pBuffer);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void User::Close(const bool bNoQuit/* = false*/)
{
	m_is_json_user = false;
	if (m_ui8State >= STATE_CLOSING)
	{
		return;
	}

	// Store old state for use in next lines.
	// State must be updated here and now to avoid multiple closes.
	uint8_t ui8OldState = m_ui8State;
	m_ui8State = STATE_CLOSING;

	// nick in hash table ?
	if ((m_ui32BoolBits & BIT_HASHED) == BIT_HASHED)
	{
		HashManager::m_Ptr->Remove(this);
	}

	// nick in nick/op list ?
	if (ui8OldState >= STATE_ADDME_2LOOP)
	{
		Users::m_Ptr->DelFromNickList(m_sNick, (m_ui32BoolBits & BIT_OPERATOR) == BIT_OPERATOR);
		Users::m_Ptr->DelFromUserIP(this);

		// PPK ... fix for QuickList nad ghost...
		// and fixing redirect all too ;)
		// and fix disconnect on send error too =)
		if (bNoQuit == false)
		{
			// alex82 ... HideUser / ������� �����
			// alex82 ... NoQuit / ��������� $Quit ��� �����
			if (((m_ui32InfoBits & INFOBIT_HIDDEN) == INFOBIT_HIDDEN) == false && ((m_ui32InfoBits & INFOBIT_NO_QUIT) == INFOBIT_NO_QUIT) == false)
			{
				int iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "$Quit %s|", m_sNick);
				if (CheckSprintf(iMsgLen, ServerManager::m_szGlobalBufferSize, "User::Close") == true)
				{
					GlobalDataQueue::m_Ptr->AddQueueItem(ServerManager::m_pGlobalBuffer, iMsgLen, NULL, 0, GlobalDataQueue::CMD_QUIT);
				}
			}
			Users::m_Ptr->Add2RecTimes(this);
		}

#ifdef _BUILD_GUI
		if (::SendMessage(MainWindowPageUsersChat::m_Ptr->m_hWndPageItems[MainWindowPageUsersChat::BTN_AUTO_UPDATE_USERLIST], BM_GETCHECK, 0, 0) == BST_CHECKED)
		{
			MainWindowPageUsersChat::m_Ptr->RemoveUser(this);
		}
#endif

		//sqldb->FinalizeVisit(u);
#ifdef FLYLINKDC_USE_DB
#ifdef _WITH_SQLITE
		DBSQLite::m_Ptr->UpdateRecord(this);
#elif _WITH_POSTGRES
		DBPostgreSQL::m_Ptr->UpdateRecord(this);
#elif _WITH_MYSQL
		DBMySQL::m_Ptr->UpdateRecord(this);
#endif
#endif
		if (((m_ui32BoolBits & BIT_HAVE_SHARECOUNTED) == BIT_HAVE_SHARECOUNTED) == true)
		{
			ServerManager::m_ui64TotalShare -= m_ui64SharedSize;
			m_ui32BoolBits &= ~BIT_HAVE_SHARECOUNTED;
		}

		ScriptManager::m_Ptr->UserDisconnected(this);
	}

	// + alex82 ... HideUser / ������� �����
	if (((m_ui32InfoBits & INFOBIT_HIDDEN) == User::INFOBIT_HIDDEN) == false && ui8OldState > STATE_ADDME_2LOOP)
	{
		ServerManager::m_ui32Logged--;
	}

	User::DeletePrcsdUsrCmd(m_pCmdActive4Search);
	User::DeletePrcsdUsrCmd(m_pCmdActive6Search);
	User::DeletePrcsdUsrCmd(m_pCmdPassiveSearch);

	PrcsdUsrCmd * cur = NULL,
	              * next = m_pCmdStrt;

	while (next != NULL)
	{
		cur = next;
		next = cur->m_pNext;

		safe_free(cur->m_sCommand);

		delete cur;
	}

	m_pCmdStrt = NULL;
	m_pCmdEnd = NULL;

	PrcsdToUsrCmd * curto = NULL,
	                * nextto = m_pCmdToUserStrt;

	while (nextto != NULL)
	{
		curto = nextto;
		nextto = curto->m_pNext;

		safe_free(curto->m_sCommand);

		delete curto;
	}


	m_pCmdToUserStrt = NULL;
	m_pCmdToUserEnd = NULL;

	if (m_sMyInfoLong)
	{
		if (SettingManager::m_Ptr->m_ui8FullMyINFOOption != 2)
		{
			Users::m_Ptr->DelFromMyInfosTag(this);
		}
	}

	if (m_sMyInfoShort)
	{
		if (SettingManager::m_Ptr->m_ui8FullMyINFOOption != 0)
		{
			Users::m_Ptr->DelFromMyInfos(this);
		}
	}

#ifdef USE_FLYLINKDC_EXT_JSON
	Users::m_Ptr->DelFromExtJSONInfos(this);
#endif

	if (m_ui32SendBufDataLen == 0 || (m_ui32BoolBits & BIT_ERROR) == BIT_ERROR)
	{
		m_ui8State = STATE_REMME;
	}
	else
	{
		m_LogInOut.m_ui32ToCloseLoops = 100;
	}
}
//---------------------------------------------------------------------------

void User::Add2Userlist()
{
	Users::m_Ptr->Add2NickList(this);
	Users::m_Ptr->Add2UserIP(this);

	switch (SettingManager::m_Ptr->m_ui8FullMyINFOOption)
	{
	case 0:
	{
		GenerateMyInfoLong();
		Users::m_Ptr->Add2MyInfosTag(this);
		return;
	}
	case 1:
	{
		GenerateMyInfoLong();
		Users::m_Ptr->Add2MyInfosTag(this);
		GenerateMyInfoShort();
		Users::m_Ptr->Add2MyInfos(this);
		return;
	}
	case 2:
	{
		GenerateMyInfoShort();
		Users::m_Ptr->Add2MyInfos(this);
		return;
	}
	default:
		break;
	}
}
//------------------------------------------------------------------------------

void User::AddUserList()
{
	m_ui32BoolBits |= BIT_BIG_SEND_BUFFER;
	m_ui64LastNicklist = ServerManager::m_ui64ActualTick;

	if (((m_ui32SupportBits & SUPPORTBIT_NOHELLO) == SUPPORTBIT_NOHELLO) == false)
	{
		if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::ALLOWEDOPCHAT) == false || (SettingManager::m_Ptr->m_bBools[SETBOOL_REG_OP_CHAT] == false ||
		        (SettingManager::m_Ptr->m_bBools[SETBOOL_REG_BOT] == true && SettingManager::m_Ptr->m_bBotsSameNick == true)))
		{
			if (isSupportZpipe() == false)
			{
				SendCharDelayed(Users::m_Ptr->m_pNickList, Users::m_Ptr->m_ui32NickListLen);
			}
			else
			{
				if (Users::m_Ptr->m_ui32ZNickListLen == 0)
				{
					Users::m_Ptr->m_pZNickList = ZlibUtility::m_Ptr->CreateZPipeAlign(Users::m_Ptr->m_pNickList, Users::m_Ptr->m_ui32NickListLen, Users::m_Ptr->m_pZNickList,
					                             Users::m_Ptr->m_ui32ZNickListLen, Users::m_Ptr->m_ui32ZNickListSize);
					if (Users::m_Ptr->m_ui32ZNickListLen == 0)
					{
						SendCharDelayed(Users::m_Ptr->m_pNickList, Users::m_Ptr->m_ui32NickListLen);
					}
					else
					{
						PutInSendBuf(Users::m_Ptr->m_pZNickList, Users::m_Ptr->m_ui32ZNickListLen);
						ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32NickListLen - Users::m_Ptr->m_ui32ZNickListLen;
					}
				}
				else
				{
					PutInSendBuf(Users::m_Ptr->m_pZNickList, Users::m_Ptr->m_ui32ZNickListLen);
					ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32NickListLen - Users::m_Ptr->m_ui32ZNickListLen;
				}
			}
		}
		else
		{
			// PPK ... OpChat bot is now visible only for OPs ;)
			int iLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "%s$$|", SettingManager::m_Ptr->m_sTexts[SETTXT_OP_CHAT_NICK]);
			if (iLen > 0)
			{
				if (Users::m_Ptr->m_ui32NickListSize < Users::m_Ptr->m_ui32NickListLen + iLen)
				{
					char * pOldBuf = Users::m_Ptr->m_pNickList;
					Users::m_Ptr->m_pNickList = (char *)realloc(pOldBuf, Users::m_Ptr->m_ui32NickListSize + NICKLISTSIZE + 1);
					if (Users::m_Ptr->m_pNickList == NULL)
					{
						Users::m_Ptr->m_pNickList = pOldBuf;
						m_ui32BoolBits |= BIT_ERROR;
						Close();

						AppendDebugLogFormat("[MEM] Cannot reallocate %u bytes for m_pNickList in User::AddUserList\n", Users::m_Ptr->m_ui32NickListSize + NICKLISTSIZE + 1);

						return;
					}
					Users::m_Ptr->m_ui32NickListSize += NICKLISTSIZE;
				}

				memcpy(Users::m_Ptr->m_pNickList + Users::m_Ptr->m_ui32NickListLen - 1, ServerManager::m_pGlobalBuffer, iLen);
				Users::m_Ptr->m_pNickList[Users::m_Ptr->m_ui32NickListLen + (iLen - 1)] = '\0';
				SendCharDelayed(Users::m_Ptr->m_pNickList, Users::m_Ptr->m_ui32NickListLen + (iLen - 1));
				Users::m_Ptr->m_pNickList[Users::m_Ptr->m_ui32NickListLen - 1] = '|';
				Users::m_Ptr->m_pNickList[Users::m_Ptr->m_ui32NickListLen] = '\0';
			}
		}
	}

	switch (SettingManager::m_Ptr->m_ui8FullMyINFOOption)
	{
	case 0:
	{
#ifdef USE_FLYLINKDC_EXT_JSON
		// TODO ExtJSON - ZPipe
		SendCharDelayedExtJSON();
#endif
		if (Users::m_Ptr->m_ui32MyInfosTagLen == 0)
		{
			break;
		}

		if (isSupportZpipe() == false)
		{
			SendCharDelayed(Users::m_Ptr->m_pMyInfosTag, Users::m_Ptr->m_ui32MyInfosTagLen);
		}
		else
		{
			if (Users::m_Ptr->m_ui32ZMyInfosTagLen == 0)
			{
				Users::m_Ptr->m_pZMyInfosTag = ZlibUtility::m_Ptr->CreateZPipeAlign(Users::m_Ptr->m_pMyInfosTag, Users::m_Ptr->m_ui32MyInfosTagLen, Users::m_Ptr->m_pZMyInfosTag,
				                               Users::m_Ptr->m_ui32ZMyInfosTagLen, Users::m_Ptr->m_ui32ZMyInfosTagSize);
				if (Users::m_Ptr->m_ui32ZMyInfosTagLen == 0)
				{
					SendCharDelayed(Users::m_Ptr->m_pMyInfosTag, Users::m_Ptr->m_ui32MyInfosTagLen);
				}
				else
				{
					PutInSendBuf(Users::m_Ptr->m_pZMyInfosTag, Users::m_Ptr->m_ui32ZMyInfosTagLen);
					ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosTagLen - Users::m_Ptr->m_ui32ZMyInfosTagLen;
				}
			}
			else
			{
				PutInSendBuf(Users::m_Ptr->m_pZMyInfosTag, Users::m_Ptr->m_ui32ZMyInfosTagLen);
				ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosTagLen - Users::m_Ptr->m_ui32ZMyInfosTagLen;
			}
		}
		break;
	}
	case 1:
	{
#ifdef USE_FLYLINKDC_EXT_JSON
		// TODO ExtJSON - ZPipe
		// ExtJSON Step reconnect - 1
		SendCharDelayedExtJSON();
#endif
		if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::SENDFULLMYINFOS) == false)
		{
			if (Users::m_Ptr->m_ui32MyInfosLen == 0)
			{
				break;
			}

			if (isSupportZpipe() == false)
			{
				SendCharDelayed(Users::m_Ptr->m_pMyInfos, Users::m_Ptr->m_ui32MyInfosLen);
			}
			else
			{
				if (Users::m_Ptr->m_ui32ZMyInfosLen == 0)
				{
					Users::m_Ptr->m_pZMyInfos = ZlibUtility::m_Ptr->CreateZPipeAlign(Users::m_Ptr->m_pMyInfos, Users::m_Ptr->m_ui32MyInfosLen, Users::m_Ptr->m_pZMyInfos,
					                            Users::m_Ptr->m_ui32ZMyInfosLen, Users::m_Ptr->m_ui32ZMyInfosSize);
					if (Users::m_Ptr->m_ui32ZMyInfosLen == 0)
					{
						SendCharDelayed(Users::m_Ptr->m_pMyInfos, Users::m_Ptr->m_ui32MyInfosLen);
					}
					else
					{
						PutInSendBuf(Users::m_Ptr->m_pZMyInfos, Users::m_Ptr->m_ui32ZMyInfosLen);
						ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosLen - Users::m_Ptr->m_ui32ZMyInfosLen;
					}
				}
				else
				{
					PutInSendBuf(Users::m_Ptr->m_pZMyInfos, Users::m_Ptr->m_ui32ZMyInfosLen);
					ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosLen - Users::m_Ptr->m_ui32ZMyInfosLen;
				}
			}
		}
		else
		{

#ifdef USE_FLYLINKDC_EXT_JSON
			// TODO ExtJSON
#endif
			if (Users::m_Ptr->m_ui32MyInfosTagLen == 0)
			{
				break;
			}
			if (isSupportZpipe() == false)
			{
				SendCharDelayed(Users::m_Ptr->m_pMyInfosTag, Users::m_Ptr->m_ui32MyInfosTagLen);
			}
			else
			{
				if (Users::m_Ptr->m_ui32ZMyInfosTagLen == 0)
				{
					Users::m_Ptr->m_pZMyInfosTag = ZlibUtility::m_Ptr->CreateZPipeAlign(Users::m_Ptr->m_pMyInfosTag, Users::m_Ptr->m_ui32MyInfosTagLen, Users::m_Ptr->m_pZMyInfosTag,
					                               Users::m_Ptr->m_ui32ZMyInfosTagLen, Users::m_Ptr->m_ui32ZMyInfosTagSize);
					if (Users::m_Ptr->m_ui32ZMyInfosTagLen == 0)
					{
						SendCharDelayed(Users::m_Ptr->m_pMyInfosTag, Users::m_Ptr->m_ui32MyInfosTagLen);
					}
					else
					{
						PutInSendBuf(Users::m_Ptr->m_pZMyInfosTag, Users::m_Ptr->m_ui32ZMyInfosTagLen);
						ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosTagLen - Users::m_Ptr->m_ui32ZMyInfosTagLen;
					}
				}
				else
				{
					PutInSendBuf(Users::m_Ptr->m_pZMyInfosTag, Users::m_Ptr->m_ui32ZMyInfosTagLen);
					ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosTagLen - Users::m_Ptr->m_ui32ZMyInfosTagLen;
				}
			}
		}
		break;
	}
	case 2:
	{
#ifdef USE_FLYLINKDC_EXT_JSON
		// TODO ExtJSON ZPipe
		SendCharDelayedExtJSON();
#endif
		if (Users::m_Ptr->m_ui32MyInfosLen == 0)
		{
			break;
		}

		if (isSupportZpipe() == false)
		{
			SendCharDelayed(Users::m_Ptr->m_pMyInfos, Users::m_Ptr->m_ui32MyInfosLen);
		}
		else
		{
			if (Users::m_Ptr->m_ui32ZMyInfosLen == 0)
			{
				Users::m_Ptr->m_pZMyInfos = ZlibUtility::m_Ptr->CreateZPipeAlign(Users::m_Ptr->m_pMyInfos, Users::m_Ptr->m_ui32MyInfosLen, Users::m_Ptr->m_pZMyInfos,
				                            Users::m_Ptr->m_ui32ZMyInfosLen, Users::m_Ptr->m_ui32ZMyInfosSize);
				if (Users::m_Ptr->m_ui32ZMyInfosLen == 0)
				{
					SendCharDelayed(Users::m_Ptr->m_pMyInfos, Users::m_Ptr->m_ui32MyInfosLen);
				}
				else
				{
					PutInSendBuf(Users::m_Ptr->m_pZMyInfos, Users::m_Ptr->m_ui32ZMyInfosLen);
					ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosLen - Users::m_Ptr->m_ui32ZMyInfosLen;
				}
			}
			else
			{
				PutInSendBuf(Users::m_Ptr->m_pZMyInfos, Users::m_Ptr->m_ui32ZMyInfosLen);
				ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32MyInfosLen - Users::m_Ptr->m_ui32ZMyInfosLen;
			}
		}
	}
	default:
		break;
	}

	if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::ALLOWEDOPCHAT) == false || (SettingManager::m_Ptr->m_bBools[SETBOOL_REG_OP_CHAT] == false ||
	        (SettingManager::m_Ptr->m_bBools[SETBOOL_REG_BOT] == true && SettingManager::m_Ptr->m_bBotsSameNick == true)))
	{
		if (Users::m_Ptr->m_ui32OpListLen > 9)
		{
			if (isSupportZpipe() == false)
			{
				SendCharDelayed(Users::m_Ptr->m_pOpList, Users::m_Ptr->m_ui32OpListLen);
			}
			else
			{
				if (Users::m_Ptr->m_ui32ZOpListLen == 0)
				{
					Users::m_Ptr->m_pZOpList = ZlibUtility::m_Ptr->CreateZPipeAlign(Users::m_Ptr->m_pOpList, Users::m_Ptr->m_ui32OpListLen, Users::m_Ptr->m_pZOpList,
					                           Users::m_Ptr->m_ui32ZOpListLen, Users::m_Ptr->m_ui32ZOpListSize);
					if (Users::m_Ptr->m_ui32ZOpListLen == 0)
					{
						SendCharDelayed(Users::m_Ptr->m_pOpList, Users::m_Ptr->m_ui32OpListLen);
					}
					else
					{
						PutInSendBuf(Users::m_Ptr->m_pZOpList, Users::m_Ptr->m_ui32ZOpListLen);
						ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32OpListLen - Users::m_Ptr->m_ui32ZOpListLen;
					}
				}
				else
				{
					PutInSendBuf(Users::m_Ptr->m_pZOpList, Users::m_Ptr->m_ui32ZOpListLen);
					ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32OpListLen - Users::m_Ptr->m_ui32ZOpListLen;
				}
			}
		}
	}
	else
	{
		// PPK ... OpChat bot is now visible only for OPs ;)
		SendCharDelayed(SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_OP_CHAT_MYINFO],
		                SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_OP_CHAT_MYINFO]);
		int iLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "%s$$|", SettingManager::m_Ptr->m_sTexts[SETTXT_OP_CHAT_NICK]);
		if (iLen > 0)
		{
			if (Users::m_Ptr->m_ui32OpListSize < Users::m_Ptr->m_ui32OpListLen + iLen)
			{
				char * pOldBuf = Users::m_Ptr->m_pOpList;
				Users::m_Ptr->m_pOpList = (char *)realloc(pOldBuf, Users::m_Ptr->m_ui32OpListSize + OPLISTSIZE + 1);
				if (Users::m_Ptr->m_pOpList == NULL)
				{
					Users::m_Ptr->m_pOpList = pOldBuf;
					m_ui32BoolBits |= BIT_ERROR;
					Close();

					AppendDebugLogFormat("[MEM] Cannot reallocate %u bytes for m_pOpList in User::AddUserList\n", Users::m_Ptr->m_ui32OpListSize + OPLISTSIZE + 1);

					return;
				}
				Users::m_Ptr->m_ui32OpListSize += OPLISTSIZE;
			}

			memcpy(Users::m_Ptr->m_pOpList + Users::m_Ptr->m_ui32OpListLen - 1, ServerManager::m_pGlobalBuffer, iLen);
			Users::m_Ptr->m_pOpList[Users::m_Ptr->m_ui32OpListLen + (iLen - 1)] = '\0';
			SendCharDelayed(Users::m_Ptr->m_pOpList, Users::m_Ptr->m_ui32OpListLen + (iLen - 1));
			Users::m_Ptr->m_pOpList[Users::m_Ptr->m_ui32OpListLen - 1] = '|';
			Users::m_Ptr->m_pOpList[Users::m_Ptr->m_ui32OpListLen] = '\0';
		}
	}

	if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::SENDALLUSERIP) == true && ((m_ui32SupportBits & SUPPORTBIT_USERIP2) == SUPPORTBIT_USERIP2) == true)
	{
		if (Users::m_Ptr->m_ui32UserIPListLen > 9)
		{
			if (isSupportZpipe() == false)
			{
				SendCharDelayed(Users::m_Ptr->m_pUserIPList, Users::m_Ptr->m_ui32UserIPListLen);
			}
			else
			{
				if (Users::m_Ptr->m_ui32ZUserIPListLen == 0)
				{
					Users::m_Ptr->m_pZUserIPList = ZlibUtility::m_Ptr->CreateZPipeAlign(Users::m_Ptr->m_pUserIPList, Users::m_Ptr->m_ui32UserIPListLen, Users::m_Ptr->m_pZUserIPList,
					                               Users::m_Ptr->m_ui32ZUserIPListLen, Users::m_Ptr->m_ui32ZUserIPListSize);
					if (Users::m_Ptr->m_ui32ZUserIPListLen == 0)
					{
						SendCharDelayed(Users::m_Ptr->m_pUserIPList, Users::m_Ptr->m_ui32UserIPListLen);
					}
					else
					{
						PutInSendBuf(Users::m_Ptr->m_pZUserIPList, Users::m_Ptr->m_ui32ZUserIPListLen);
						ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32UserIPListLen - Users::m_Ptr->m_ui32ZUserIPListLen;
					}
				}
				else
				{
					PutInSendBuf(Users::m_Ptr->m_pZUserIPList, Users::m_Ptr->m_ui32ZUserIPListLen);
					ServerManager::m_ui64BytesSentSaved += Users::m_Ptr->m_ui32UserIPListLen - Users::m_Ptr->m_ui32ZUserIPListLen;
				}
			}
		}
	}
}
//---------------------------------------------------------------------------

bool User::GenerateMyInfoLong()   // true == changed
{
	// Prepare myinfo with nick
	int iLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "$MyINFO $ALL %s ", m_sNick);
	if (iLen <= 0)
	{
		return false;
	}

	// Add description
	if (m_ui8ChangedDescriptionLongLen != 0)
	{
		if (m_sChangedDescriptionLong != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedDescriptionLong, m_ui8ChangedDescriptionLongLen);
			iLen += m_ui8ChangedDescriptionLongLen;
		}

		if (((m_ui32InfoBits & INFOBIT_DESCRIPTION_LONG_PERM) == INFOBIT_DESCRIPTION_LONG_PERM) == false)
		{
			User::FreeInfo(m_sChangedDescriptionLong, m_ui8ChangedDescriptionLongLen);
			m_ui8ChangedDescriptionLongLen = 0;
		}
	}
	else if (m_sDescription != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sDescription, (size_t)m_ui8DescriptionLen);
		iLen += m_ui8DescriptionLen;
	}

	// Add tag
	if (m_ui8ChangedTagLongLen != 0)
	{
		if (m_sChangedTagLong != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedTagLong, m_ui8ChangedTagLongLen);
			iLen += m_ui8ChangedTagLongLen;
		}

		if (((m_ui32InfoBits & INFOBIT_TAG_LONG_PERM) == INFOBIT_TAG_LONG_PERM) == false)
		{
			User::FreeInfo(m_sChangedTagLong, m_ui8ChangedTagLongLen);
		}
	}
	else if (m_sTag != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sTag, (size_t)m_ui8TagLen);
		iLen += (int)m_ui8TagLen;
	}

	memcpy(ServerManager::m_pGlobalBuffer + iLen, "$ $", 3);
	iLen += 3;

	// Add connection
	if (m_ui8ChangedConnectionLongLen != 0)
	{
		if (m_sChangedConnectionLong != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedConnectionLong, m_ui8ChangedConnectionLongLen);
			iLen += m_ui8ChangedConnectionLongLen;
		}

		if (((m_ui32InfoBits & INFOBIT_CONNECTION_LONG_PERM) == INFOBIT_CONNECTION_LONG_PERM) == false)
		{
			User::FreeInfo(m_sChangedConnectionLong, m_ui8ChangedConnectionLongLen);
		}
	}
	else if (m_sConnection != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sConnection, m_ui8ConnectionLen);
		iLen += m_ui8ConnectionLen;
	}

	// add magicbyte
	uint8_t ui8Magic = m_ui8MagicByte;

	if (((m_ui32BoolBits & User::SUPPORTBIT_TLS2) == User::SUPPORTBIT_TLS2) == false)
	{
		// should not be set if user not have TLS2 support
		ui8Magic &= ~0x10;
		ui8Magic &= ~0x20;
	}

	if ((m_ui32BoolBits & BIT_IPV4) == BIT_IPV4)
	{
		ui8Magic |= 0x40; // IPv4 support
	}
	else
	{
		ui8Magic &= ~0x40; // IPv4 support
	}

	if ((m_ui32BoolBits & BIT_IPV6) == BIT_IPV6)
	{
		ui8Magic |= 0x80; // IPv6 support
	}
	else
	{
		ui8Magic &= ~0x80; // IPv6 support
	}

	ServerManager::m_pGlobalBuffer[iLen] = ui8Magic;
	ServerManager::m_pGlobalBuffer[iLen + 1] = '$';
	iLen += 2;

	// Add email
	if (m_ui8ChangedEmailLongLen != 0)
	{
		if (m_sChangedEmailLong != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedEmailLong, m_ui8ChangedEmailLongLen);
			iLen += m_ui8ChangedEmailLongLen;
		}

		if (((m_ui32InfoBits & INFOBIT_EMAIL_LONG_PERM) == INFOBIT_EMAIL_LONG_PERM) == false)
		{
			User::FreeInfo(m_sChangedEmailLong, m_ui8ChangedEmailLongLen);
		}
	}
	else if (m_sEmail != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sEmail, (size_t)m_ui8EmailLen);
		iLen += (int)m_ui8EmailLen;
	}

	// Add share and end of myinfo
	int iRet = snprintf(ServerManager::m_pGlobalBuffer + iLen, ServerManager::m_szGlobalBufferSize - iLen, "$%" PRIu64 "$|", m_ui64ChangedSharedSizeLong);

	if (((m_ui32InfoBits & INFOBIT_SHARE_LONG_PERM) == INFOBIT_SHARE_LONG_PERM) == false)
	{
		m_ui64ChangedSharedSizeLong = m_ui64SharedSize;
	}

	if (iRet <= 0)
	{
		return false;
	}
	iLen += iRet;

	if (m_sMyInfoLong != NULL)
	{
		if (m_ui16MyInfoLongLen == (uint16_t)iLen && memcmp(m_sMyInfoLong + 14 + m_ui8NickLen, ServerManager::m_pGlobalBuffer + 14 + m_ui8NickLen, m_ui16MyInfoLongLen - 14 - m_ui8NickLen) == 0)
		{
			return false;
		}
	}

	UserSetMyInfoLong(this, ServerManager::m_pGlobalBuffer, (uint16_t)iLen);

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

bool User::GenerateMyInfoShort()   // true == changed
{
	// Prepare myinfo with nick
	int iLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "$MyINFO $ALL %s ", m_sNick);
	if (iLen <= 0)
	{
		return false;
	}

	// Add mode to start of description if is enabled
	if (SettingManager::m_Ptr->m_bBools[SETBOOL_MODE_TO_DESCRIPTION] == true && m_sModes[0] != 0)
	{
		char * sActualDescription = NULL;

		if (m_ui8ChangedDescriptionShortLen != 0)
		{
			sActualDescription = m_sChangedDescriptionShort;
		}
		else if (SettingManager::m_Ptr->m_bBools[SETBOOL_STRIP_DESCRIPTION] == true)
		{
			sActualDescription = m_sDescription;
		}

		if (sActualDescription == NULL)
		{
			ServerManager::m_pGlobalBuffer[iLen] = m_sModes[0];
			iLen++;
		}
		else if (sActualDescription[0] != m_sModes[0] && sActualDescription[1] != ' ')
		{
			ServerManager::m_pGlobalBuffer[iLen] = m_sModes[0];
			ServerManager::m_pGlobalBuffer[iLen + 1] = ' ';
			iLen += 2;
		}
	}

	// Add description
	if (m_ui8ChangedDescriptionShortLen != 0)
	{
		if (m_sChangedDescriptionShort != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedDescriptionShort, m_ui8ChangedDescriptionShortLen);
			iLen += m_ui8ChangedDescriptionShortLen;
		}

		if (((m_ui32InfoBits & INFOBIT_DESCRIPTION_SHORT_PERM) == INFOBIT_DESCRIPTION_SHORT_PERM) == false)
		{
			User::FreeInfo(m_sChangedDescriptionShort, m_ui8ChangedDescriptionShortLen);
		}
	}
	else if (SettingManager::m_Ptr->m_bBools[SETBOOL_STRIP_DESCRIPTION] == false && m_sDescription != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sDescription, m_ui8DescriptionLen);
		iLen += m_ui8DescriptionLen;
	}

	// Add tag
	if (m_ui8ChangedTagShortLen != 0)
	{
		if (m_sChangedTagShort != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedTagShort, m_ui8ChangedTagShortLen);
			iLen += m_ui8ChangedTagShortLen;
		}

		if (((m_ui32InfoBits & INFOBIT_TAG_SHORT_PERM) == INFOBIT_TAG_SHORT_PERM) == false)
		{
			User::FreeInfo(m_sChangedTagShort, m_ui8ChangedTagShortLen);
		}
	}
	else if (SettingManager::m_Ptr->m_bBools[SETBOOL_STRIP_TAG] == false && m_sTag != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sTag, (size_t)m_ui8TagLen);
		iLen += (int)m_ui8TagLen;
	}

	// Add mode to myinfo if is enabled
	if (SettingManager::m_Ptr->m_bBools[SETBOOL_MODE_TO_MYINFO] == true && m_sModes[0] != 0)
	{
		int iRet = snprintf(ServerManager::m_pGlobalBuffer + iLen, ServerManager::m_szGlobalBufferSize - iLen, "$%c$", m_sModes[0]);
		if (iRet <= 0)
		{
			return false;
		}
		iLen += iRet;
	}
	else
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, "$ $", 3);
		iLen += 3;
	}

	// Add connection
	if (m_ui8ChangedConnectionShortLen != 0)
	{
		if (m_sChangedConnectionShort != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedConnectionShort, m_ui8ChangedConnectionShortLen);
			iLen += m_ui8ChangedConnectionShortLen;
		}

		if (((m_ui32InfoBits & INFOBIT_CONNECTION_SHORT_PERM) == INFOBIT_CONNECTION_SHORT_PERM) == false)
		{
			User::FreeInfo(m_sChangedConnectionShort, m_ui8ChangedConnectionShortLen);
		}
	}
	else if (SettingManager::m_Ptr->m_bBools[SETBOOL_STRIP_CONNECTION] == false && m_sConnection != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sConnection, m_ui8ConnectionLen);
		iLen += m_ui8ConnectionLen;
	}

	// add magicbyte
	uint8_t ui8Magic = m_ui8MagicByte;

	if (((m_ui32BoolBits & User::SUPPORTBIT_TLS2) == User::SUPPORTBIT_TLS2) == false)
	{
		// should not be set if user not have TLS2 support
		ui8Magic &= ~0x10;
		ui8Magic &= ~0x20;
	}

	if ((m_ui32BoolBits & BIT_IPV4) == BIT_IPV4)
	{
		ui8Magic |= 0x40; // IPv4 support
	}
	else
	{
		ui8Magic &= ~0x40; // IPv4 support
	}

	if ((m_ui32BoolBits & BIT_IPV6) == BIT_IPV6)
	{
		ui8Magic |= 0x80; // IPv6 support
	}
	else
	{
		ui8Magic &= ~0x80; // IPv6 support
	}

	ServerManager::m_pGlobalBuffer[iLen] = ui8Magic;
	ServerManager::m_pGlobalBuffer[iLen + 1] = '$';
	iLen += 2;

	// Add email
	if (m_ui8ChangedEmailShortLen != 0)
	{
		if (m_sChangedEmailShort != NULL)
		{
			memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sChangedEmailShort, m_ui8ChangedEmailShortLen);
			iLen += m_ui8ChangedEmailShortLen;
		}

		if (((m_ui32InfoBits & INFOBIT_EMAIL_SHORT_PERM) == INFOBIT_EMAIL_SHORT_PERM) == false)
		{
			User::FreeInfo(m_sChangedEmailShort, m_ui8ChangedEmailShortLen);
		}
	}
	else if (SettingManager::m_Ptr->m_bBools[SETBOOL_STRIP_EMAIL] == false && m_sEmail != NULL)
	{
		memcpy(ServerManager::m_pGlobalBuffer + iLen, m_sEmail, (size_t)m_ui8EmailLen);
		iLen += (int)m_ui8EmailLen;
	}

	// Add share and end of myinfo
	int iRet = snprintf(ServerManager::m_pGlobalBuffer + iLen, ServerManager::m_szGlobalBufferSize - iLen, "$%" PRIu64 "$|", m_ui64ChangedSharedSizeShort);

	if (((m_ui32InfoBits & INFOBIT_SHARE_SHORT_PERM) == INFOBIT_SHARE_SHORT_PERM) == false)
	{
		m_ui64ChangedSharedSizeShort = m_ui64SharedSize;
	}

	if (iRet <= 0)
	{
		return false;
	}
	iLen += iRet;

	if (m_sMyInfoShort != NULL)
	{
		if (m_ui16MyInfoShortLen == (uint16_t)iLen && memcmp(m_sMyInfoShort + 14 + m_ui8NickLen, ServerManager::m_pGlobalBuffer + 14 + m_ui8NickLen, m_ui16MyInfoShortLen - 14 - m_ui8NickLen) == 0)
		{
			return false;
		}
	}

	UserSetMyInfoShort(this, ServerManager::m_pGlobalBuffer, (uint16_t)iLen);

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

void User::HasSuspiciousTag()
{
	if (SettingManager::m_Ptr->m_bBools[SETBOOL_REPORT_SUSPICIOUS_TAG] == true && SettingManager::m_Ptr->m_bBools[SETBOOL_SEND_STATUS_MESSAGES] == true)
	{
		m_sDescription[m_ui8DescriptionLen] = '\0';
		GlobalDataQueue::m_Ptr->StatusMessageFormat("User::HasSuspiciousTag", "<%s> *** %s (%s) %s. %s: %s|", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], m_sNick, m_sIP, LanguageManager::m_Ptr->m_sTexts[LAN_HAS_SUSPICIOUS_TAG_CHECK_HIM],
		        LanguageManager::m_Ptr->m_sTexts[LAN_FULL_DESCRIPTION], m_sDescription);
		m_sDescription[m_ui8DescriptionLen] = '$';
	}
	m_ui32BoolBits &= ~BIT_HAVE_BADTAG;
}
//---------------------------------------------------------------------------

bool User::ProcessRules()
{
	// if share limit enabled, check it
	if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::NOSHARELIMIT) == false)
	{
		if ((SettingManager::m_Ptr->m_ui64MinShare != 0 && m_ui64SharedSize < SettingManager::m_Ptr->m_ui64MinShare) ||
		        (SettingManager::m_Ptr->m_ui64MaxShare != 0 && m_ui64SharedSize > SettingManager::m_Ptr->m_ui64MaxShare))
		{
			SendChar(SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_SHARE_LIMIT_MSG], SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_SHARE_LIMIT_MSG]);
			//UdpDebug::m_Ptr->BroadcastFormat("[SYS] User with low or high share %s (%s) disconnected.", sNick, sIP);
			return false;
		}
	}

	// no Tag? Apply rule
	if (m_sTag == NULL)
	{
		if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::NOTAGCHECK) == false)
		{
			if (SettingManager::m_Ptr->m_i16Shorts[SETSHORT_NO_TAG_OPTION] != 0)
			{
				SendChar(SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_NO_TAG_MSG], SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_NO_TAG_MSG]);
				//UdpDebug::m_Ptr->BroadcastFormat("[SYS] User without Tag %s (%s) redirected.", sNick, sIP);
				return false;
			}
		}
	}
	else
	{
		// min and max slot check
		if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::NOSLOTCHECK) == false)
		{
			// TODO 2 -oPTA -ccheckers: $SR based slots fetching for no_tag users

			if ((SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MIN_SLOTS_LIMIT] != 0 && m_ui32Slots < (uint32_t)SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MIN_SLOTS_LIMIT]) ||
			        (SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_SLOTS_LIMIT] != 0 && m_ui32Slots > (uint32_t)SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_SLOTS_LIMIT]))
			{
				SendChar(SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_SLOTS_LIMIT_MSG], SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_SLOTS_LIMIT_MSG]);
				//UdpDebug::m_Ptr->BroadcastFormat("[SYS] User with bad slots %s (%s) disconnected.", sNick, sIP);
				return false;
			}
		}

		// slots/hub ration check
		if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::NOSLOTHUBRATIO) == false &&
		        SettingManager::m_Ptr->m_i16Shorts[SETSHORT_HUB_SLOT_RATIO_HUBS] != 0 && SettingManager::m_Ptr->m_i16Shorts[SETSHORT_HUB_SLOT_RATIO_SLOTS] != 0)
		{
			const uint32_t slots = m_ui32Slots;
			const uint32_t hubs = m_ui32Hubs > 0 ? m_ui32Hubs : 1;
			if (((double)slots / hubs) < ((double)SettingManager::m_Ptr->m_i16Shorts[SETSHORT_HUB_SLOT_RATIO_SLOTS] / SettingManager::m_Ptr->m_i16Shorts[SETSHORT_HUB_SLOT_RATIO_HUBS]))
			{
				SendChar(SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SLOT_RATIO_MSG], SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_HUB_SLOT_RATIO_MSG]);
				//UdpDebug::m_Ptr->BroadcastFormat("[SYS] User with bad hub/slot ratio %s (%s) disconnected.", sNick, sIP);
				return false;
			}
		}

		// hub checker
		if (ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::NOMAXHUBCHECK) == false && SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_HUBS_LIMIT] != 0)
		{
			if (m_ui32Hubs > (uint32_t)SettingManager::m_Ptr->m_i16Shorts[SETSHORT_MAX_HUBS_LIMIT])
			{
				SendChar(SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_MAX_HUBS_LIMIT_MSG], SettingManager::m_Ptr->m_ui16PreTextsLens[SettingManager::SETPRETXT_MAX_HUBS_LIMIT_MSG]);
				//UdpDebug::m_Ptr->BroadcastFormat("[SYS] User with bad hubs count %s (%s) disconnected.", sNick, sIP);
				return false;
			}
		}
	}

	return true;
}

//------------------------------------------------------------------------------

void User::AddPrcsdCmd(const uint8_t ui8Type, const char * sCommand, const size_t szCommandLen, User * pToUser, const bool bIsPm/* = false*/)
{
	if (ui8Type == PrcsdUsrCmd::CTM_MCTM_RCTM_SR_TO)
	{
		PrcsdToUsrCmd * cur = NULL,
		                * next = m_pCmdToUserStrt;

		while (next != NULL)
		{
			cur = next;
			next = cur->m_pNext;

			if (cur->m_pToUser == pToUser)
			{
				char * pOldBuf = cur->m_sCommand;
				cur->m_sCommand = (char *)realloc(pOldBuf, cur->m_ui32Len + szCommandLen + 1);
				if (cur->m_sCommand == NULL)
				{
					cur->m_sCommand = pOldBuf;
					m_ui32BoolBits |= BIT_ERROR;
					Close();

					AppendDebugLogFormat("[MEM] Cannot reallocate %zu bytes in User::AddPrcsdCmd\n", cur->m_ui32Len+szCommandLen+1);

					return;
				}
				memcpy(cur->m_sCommand + cur->m_ui32Len, sCommand, szCommandLen);
				cur->m_sCommand[cur->m_ui32Len + szCommandLen] = '\0';
				cur->m_ui32Len += (uint32_t)szCommandLen;
				cur->m_ui32PmCount += bIsPm == true ? 1 : 0;
				return;
			}
		}

		PrcsdToUsrCmd * pNewToCmd = new (std::nothrow) PrcsdToUsrCmd;
		if (pNewToCmd == NULL)
		{
			m_ui32BoolBits |= BIT_ERROR;
			Close();

			AppendDebugLog("%s - [MEM] User::AddPrcsdCmd cannot allocate new pNewToCmd\n");

			return;
		}

		pNewToCmd->m_sCommand = (char *)malloc(szCommandLen + 1);
		if (pNewToCmd->m_sCommand == NULL)
		{
			m_ui32BoolBits |= BIT_ERROR;
			Close();

			AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes for sCommand in User::AddPrcsdCmd\n", szCommandLen+1);

			delete pNewToCmd;

			return;
		}

		memcpy(pNewToCmd->m_sCommand, sCommand, szCommandLen);
		pNewToCmd->m_sCommand[szCommandLen] = '\0';

		pNewToCmd->m_ui32Len = (uint32_t)szCommandLen;
		pNewToCmd->m_ui32PmCount = bIsPm == true ? 1 : 0;
		pNewToCmd->m_ui32Loops = 0;
		pNewToCmd->m_pToUser = pToUser;

		pNewToCmd->m_nick = std::string(pToUser->m_sNick, pToUser->m_ui8NickLen);
		pNewToCmd->m_pNext = nullptr;

		if (m_pCmdToUserStrt == NULL)
		{
			m_pCmdToUserStrt = pNewToCmd;
			m_pCmdToUserEnd = pNewToCmd;
		}
		else
		{
			m_pCmdToUserEnd->m_pNext = pNewToCmd;
			m_pCmdToUserEnd = pNewToCmd;
		}

		return;
	}

	PrcsdUsrCmd * pNewcmd = new (std::nothrow) PrcsdUsrCmd;
	if (pNewcmd == NULL)
	{
		m_ui32BoolBits |= BIT_ERROR;
		Close();

		AppendDebugLog("%s - [MEM] User::AddPrcsdCmd cannot allocate new pNewcmd1\n");

		return;
	}

	pNewcmd->m_sCommand = (char *)malloc(szCommandLen + 1);
	if (pNewcmd->m_sCommand == NULL)
	{
		m_ui32BoolBits |= BIT_ERROR;
		Close();

		AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes for sCommand in User::AddPrcsdCmd1\n", szCommandLen+1);

		delete pNewcmd;

		return;
	}

	memcpy(pNewcmd->m_sCommand, sCommand, szCommandLen);
	pNewcmd->m_sCommand[szCommandLen] = '\0';

	pNewcmd->m_ui32Len = (uint32_t)szCommandLen;
	pNewcmd->m_ui8Type = ui8Type;
	pNewcmd->m_pNext = nullptr;
	pNewcmd->m_pPtr = (void *)pToUser; // TODO - ������

	if (m_pCmdStrt == NULL)
	{
		m_pCmdStrt = pNewcmd;
		m_pCmdEnd = pNewcmd;
	}
	else
	{
		m_pCmdEnd->m_pNext = pNewcmd;
		m_pCmdEnd = pNewcmd;
	}
}
//---------------------------------------------------------------------------

void User::AddMeOrIPv4Check()
{
	if (((m_ui32BoolBits & BIT_IPV6) == BIT_IPV6) && ((m_ui32SupportBits & SUPPORTBIT_IPV4) == SUPPORTBIT_IPV4) && ServerManager::m_sHubIP[0] != '\0' && ServerManager::m_bUseIPv4 == true)
	{
		m_ui8State = STATE_IPV4_CHECK;
		m_LogInOut.m_ui64IPv4CheckTick = ServerManager::m_ui64ActualTick;

		SendFormat("AddMeOrIPv4Check", true, "$ConnectToMe %s %s:%hu|", m_sNick, ServerManager::m_sHubIP, SettingManager::m_Ptr->m_ui16PortNumbers[0]);
	}
	else
	{
		m_ui8State = STATE_ADDME;
	}
}
//---------------------------------------------------------------------------

char * User::SetUserInfo(char* sOldData, uint8_t &ui8OldDataLen, const char * sNewData, const size_t szNewDataLen, const char * /* sDataName */)
{
	User::FreeInfo(sOldData, ui8OldDataLen);

	if (szNewDataLen > 0)
	{
		sOldData = (char *)malloc(szNewDataLen + 1);
		if (sOldData == NULL)
		{
			AppendDebugLogFormat("[MEM] Cannot allocate %zu bytes in User::SetUserInfo\n", szNewDataLen+1);
			return sOldData;
		}

		memcpy(sOldData, sNewData, szNewDataLen);
		sOldData[szNewDataLen] = '\0';
		ui8OldDataLen = (uint8_t)szNewDataLen;
	}
	else
	{
		ui8OldDataLen = 1;
	}

	return sOldData;
}
//---------------------------------------------------------------------------

void User::RemFromSendBuf(const char * sData, const uint32_t ui32Len, const uint32_t ui32SendBufLen)
{
	char *match = strstr(m_pSendBuf + ui32SendBufLen, sData);
	if (match != NULL)
	{
		memmove(match, match + ui32Len, m_ui32SendBufDataLen - ((match + (ui32Len)) - m_pSendBuf));
		m_ui32SendBufDataLen -= ui32Len;
		m_pSendBuf[m_ui32SendBufDataLen] = '\0';
	}
}
//------------------------------------------------------------------------------

void User::DeletePrcsdUsrCmd(PrcsdUsrCmd *& pCommand)
{
	if (pCommand != NULL)
	{
		free(pCommand->m_sCommand);
		delete pCommand;
		pCommand = nullptr; // [+] FlylinkDC++
	}
}
//------------------------------------------------------------------------------

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

V1032 The pointer '"++"' is cast to a more strictly aligned pointer type.

V1032 The pointer '"V:"' is cast to a more strictly aligned pointer type.

V1086 A call of the 'memcpy' function will lead to underflow of the buffer 'm_LogInOut.m_pBuffer'.