/*
 * 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 "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 = { NULL, NULL, 0 };
//---------------------------------------------------------------------------

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

    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';

            uint32_t ui32CommandLen = (uint32_t)(((pUser->m_pRecvBuf+ui32i)-pBuffer)+1);
            if(pBuffer[0] == '|') {
                //UdpDebug->BroadcastFormat("[SYS] heartbeat from %s (%s).", pUser->Nick, pUser->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);
        	} 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);

							pUser->Close();
                            return;
                        }
                        case 'S':
                            if(sTagPart[2] == '\0') {
                                UserSetBadTag(pUser, pUser->m_sMyInfoOriginal+(sMyINFOParts[0]-ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
                                return;
                            }
                            if(HaveOnlyNumbers(sTagPart+2, (uint16_t)strlen(sTagPart+2)) == 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;
                            }
							pUser->m_ui32OLimit = atoi(sTagPart+2);
                            break;
                        case 'B':
                            if(sTagPart[2] == '\0') {
                                UserSetBadTag(pUser, pUser->m_sMyInfoOriginal+(sMyINFOParts[0]-ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
                                return;
                            }
							pUser->m_ui32LLimit = atoi(sTagPart+2);
                            break;
                        case 'L':
                            if(sTagPart[2] == '\0') {
                                UserSetBadTag(pUser, pUser->m_sMyInfoOriginal+(sMyINFOParts[0]-ServerManager::m_pGlobalBuffer), (uint8_t)iMyINFOPartsLen[0]);
                                return;
                            }
							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;
                            }
							pUser->m_ui32DLimit = atoi(sTagPart+2);
                            break;
                        default:
                            //UdpDebug::m_Ptr->BroadcastFormat("[SYS] %s (%s): Extra info in DC tag: %s", pUser->Nick, pUser->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() {
#ifdef _WIN32
    if(m_sMessage != NULL && HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sMessage) == 0) {
        AppendDebugLog("%s - [MEM] Cannot deallocate m_sMessage in UserBan::~UserBan\n");
    }
#else
	free(m_sMessage);
#endif
}
//---------------------------------------------------------------------------

UserBan * UserBan::CreateUserBan(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;
    }

#ifdef _WIN32
    pUserBan->m_sMessage = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, ui32MessLen+1);
#else
	pUserBan->m_sMessage = (char *)malloc(ui32MessLen+1);
#endif
    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) {
    // ...
}
//---------------------------------------------------------------------------

LoginLogout::~LoginLogout() {
    delete m_pBan;

#ifdef _WIN32
    if(m_pBuffer != NULL) {
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_pBuffer) == 0) {
            AppendDebugLog("%s - [MEM] Cannot deallocate m_pBuffer in LoginLogout::~LoginLogout\n");
        }
    }
#else
	free(m_pBuffer);
#endif
}
//---------------------------------------------------------------------------

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_pLogInOut(NULL),
	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_sVersion(NULL), 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_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';
}
//---------------------------------------------------------------------------

User::~User() {
#ifdef _WIN32
	if(m_pRecvBuf != NULL) {
		if(HeapFree(ServerManager::m_hRecvHeap, HEAP_NO_SERIALIZE, (void *)m_pRecvBuf) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_pRecvBuf in User::~User\n");
        }
    }
#else
	free(m_pRecvBuf);
#endif

#ifdef _WIN32
	if(m_pSendBuf != NULL) {
		if(HeapFree(ServerManager::m_hSendHeap, HEAP_NO_SERIALIZE, (void *)m_pSendBuf) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_pSendBuf in User::~User\n");
        }
    }
#else
	free(m_pSendBuf);
#endif

#ifdef _WIN32
	if(m_sLastChat != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sLastChat) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sLastChat in User::~User\n");
        }
    }
#else
	free(m_sLastChat);
#endif

#ifdef _WIN32
	if(m_sLastPM != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sLastPM) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sLastPM in User::~User\n");
        }
    }
#else
	free(m_sLastPM);
#endif

#ifdef _WIN32
	if(m_sLastSearch != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sLastSearch) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sLastSearch in User::~User\n");
        }
    }
#else
	free(m_sLastSearch);
#endif

#ifdef _WIN32
	if(m_sMyInfoShort != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sMyInfoShort) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sMyInfoShort in User::~User\n");
        }
    }
#else
	free(m_sMyInfoShort);
#endif

#ifdef _WIN32
	if(m_sMyInfoLong != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sMyInfoLong) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sMyInfoLong in User::~User\n");
        }
    }
#else
	free(m_sMyInfoLong);
#endif

#ifdef _WIN32
	if(m_sMyInfoOriginal != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sMyInfoOriginal) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sMyInfoOriginal in User::~User\n");
        }
    }
#else
	free(m_sMyInfoOriginal);
#endif

#ifdef _WIN32
	if(m_sVersion != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sVersion) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sVersion in User::~User\n");
        }
    }
#else
	free(m_sVersion);
#endif

#ifdef _WIN32
	if(m_sChangedDescriptionShort != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedDescriptionShort) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedDescriptionShort in User::~User\n");
        }
    }
#else
	free(m_sChangedDescriptionShort);
#endif

#ifdef _WIN32
	if(m_sChangedDescriptionLong != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedDescriptionLong) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedDescriptionLong in User::~User\n");
        }
    }
#else
	free(m_sChangedDescriptionLong);
#endif

#ifdef _WIN32
	if(m_sChangedTagShort != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedTagShort) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedTagShort in User::~User\n");
        }
    }
#else
	free(m_sChangedTagShort);
#endif

#ifdef _WIN32
	if(m_sChangedTagLong != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedTagLong) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedTagLong in User::~User\n");
        }
    }
#else
	free(m_sChangedTagLong);
#endif

#ifdef _WIN32
	if(m_sChangedConnectionShort != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedConnectionShort) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedConnectionShort in User::~User\n");
        }
    }
#else
	free(m_sChangedConnectionShort);
#endif

#ifdef _WIN32
	if(m_sChangedConnectionLong != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedConnectionLong) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedConnectionLong in User::~User\n");
        }
    }
#else
	free(m_sChangedConnectionLong);
#endif

#ifdef _WIN32
	if(m_sChangedEmailShort != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedEmailShort) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedEmailShort in User::~User\n");
        }
    }
#else
	free(m_sChangedEmailShort);
#endif

#ifdef _WIN32
	if(m_sChangedEmailLong != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sChangedEmailLong) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sChangedEmailLong in User::~User\n");
        }
    }
#else
	free(m_sChangedEmailLong);
#endif

	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: " + string(m_sNick, m_ui8NickLen) + " (Socket " + string(m_Socket) + ")").c_str());
    }
#endif

	if(m_sNick != sDefaultNick) {
#ifdef _WIN32
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sNick) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sNick in User::~User\n");
		}
#else
		free(m_sNick);
#endif
	}
        
	delete m_pLogInOut;
    
	if(m_pCmdActive4Search != NULL) {
        User::DeletePrcsdUsrCmd(m_pCmdActive4Search);
    }

	if(m_pCmdActive6Search != NULL) {
        User::DeletePrcsdUsrCmd(m_pCmdActive6Search);
    }

	if(m_pCmdPassiveSearch != NULL) {
        User::DeletePrcsdUsrCmd(m_pCmdPassiveSearch);
    }
                
	PrcsdUsrCmd * cur = NULL,
        * next = m_pCmdStrt;
        
    while(next != NULL) {
        cur = next;
        next = cur->m_pNext;

#ifdef _WIN32
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)cur->m_sCommand) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate cur->m_sCommand in User::~User\n");
        }
#else
		free(cur->m_sCommand);
#endif
        delete cur;
	}

	PrcsdToUsrCmd * curto = NULL,
        * nextto = m_pCmdToUserStrt;
                    
    while(nextto != NULL) {
        curto = nextto;
        nextto = curto->m_pNext;

#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)curto->m_sCommand) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate curto->m_sCommand in User::~User\n");
        }
#else
		free(curto->m_sCommand);
#endif

#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)curto->m_sToNick) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate curto->m_sToNick in User::~User\n");
        }
#else
		free(curto->m_sToNick);
#endif

        delete curto;
	}
}
//---------------------------------------------------------------------------

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 = Allign1024(m_ui32SendBufDataLen+szLockLen);

	char * pOldBuf = m_pSendBuf;
#ifdef _WIN32
    if(m_pSendBuf == NULL) {
		m_pSendBuf = (char *)HeapAlloc(ServerManager::m_hSendHeap, HEAP_NO_SERIALIZE, szAllignLen);
    } else {
		m_pSendBuf = (char *)HeapReAlloc(ServerManager::m_hSendHeap, HEAP_NO_SERIALIZE, (void *)pOldBuf, szAllignLen);
	}
#else
	m_pSendBuf = (char *)realloc(pOldBuf, szAllignLen);
#endif
    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(pSendBuf, ui32SendBufDataLen));

#ifdef _WIN32
	m_pLogInOut->m_pBuffer = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, 64);
#else
	m_pLogInOut->m_pBuffer = (char *)malloc(64);
#endif
    if(m_pLogInOut->m_pBuffer == NULL) {
		AppendDebugLog("%s - [MEM] Cannot allocate 64 bytes for pBuffer in User::MakeLock\n");
		return false;
    }
    
    memcpy(m_pLogInOut->m_pBuffer, m_pSendBuf, szLockLen);
	m_pLogInOut->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) {
		// we need to try recv to catch connection error or closed connection
        iAvailBytes = 16;
    } else if(iAvailBytes > 16384) {
        // receive max. 16384 bytes to receive buffer
        iAvailBytes = 16384;
    }

    size_t szAllignLen = 0;

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

		m_ui32RecvCalled = 0;
    }

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

#ifdef _WIN32
        if(m_pRecvBuf == NULL) {
			m_pRecvBuf = (char *)HeapAlloc(ServerManager::m_hRecvHeap, HEAP_NO_SERIALIZE, szAllignLen);
        } else {
			m_pRecvBuf = (char *)HeapReAlloc(ServerManager::m_hRecvHeap, HEAP_NO_SERIALIZE, (void *)pOldBuf, szAllignLen);
        }
#else
		m_pRecvBuf = (char *)realloc(pOldBuf, szAllignLen);
#endif
		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
	int recvlen = recv(m_Socket, m_pRecvBuf+m_ui32RecvBufDataLen, m_ui32RecvBufLen-m_ui32RecvBufDataLen, 0);
	m_ui32RecvCalled++;

#ifdef _WIN32
    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();
            }
        }
    }
}
//---------------------------------------------------------------------------

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::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;

    if(m_ui32SendBufLen < m_ui32SendBufDataLen+szTxtLen) {
        if(m_pSendBuf == NULL) {
            szAllignLen = Allign1024(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 = Allign1024(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 = Allign1024(szTxtLen+m_ui32SendBufDataLen);

                    char * pOldBuf = m_pSendBuf;
#ifdef _WIN32
					m_pSendBuf = (char *)HeapReAlloc(ServerManager::m_hSendHeap, HEAP_NO_SERIALIZE, (void *)pOldBuf, szAllignTxtLen);
#else
					m_pSendBuf = (char *)realloc(pOldBuf, szAllignTxtLen);
#endif
                    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 = Allign1024(m_ui32SendBufDataLen+szTxtLen);
                }
        	}
        }
    } else if(m_ui32SendCalled > 100) {
        szAllignLen = Allign1024(m_ui32SendBufDataLen+szTxtLen);
        if(m_ui32SendBufLen <= szAllignLen) {
            szAllignLen = 0;
        }

		m_ui32SendCalled = 0;
    }

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

        char * pOldBuf = m_pSendBuf;
#ifdef _WIN32
        if(m_pSendBuf == NULL) {
			m_pSendBuf = (char *)HeapAlloc(ServerManager::m_hSendHeap, HEAP_NO_SERIALIZE, szAllignLen);
        } else {
			m_pSendBuf = (char *)HeapReAlloc(ServerManager::m_hSendHeap, HEAP_NO_SERIALIZE, (void *)pOldBuf, szAllignLen);
        }
#else
		m_pSendBuf = (char *)realloc(pOldBuf, szAllignLen);
#endif
        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;
    }

    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;

	// 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) {
            if(m_pSendBuf != NULL) {
#ifdef _WIN32
               if(HeapFree(ServerManager::m_hSendHeap, HEAP_NO_SERIALIZE, (void *)m_pSendBuf) == 0) {
					AppendDebugLog("%s - [MEM] Cannot deallocate pSendBuf in User::Try2Send\n");
                }
#else
				free(m_pSendBuf);
#endif
				m_pSendBuf = NULL;
				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(char * sIP) {
    strcpy(m_sIP, sIP);
    m_ui8IpLen = (uint8_t)strlen(sIP);
}
//------------------------------------------------------------------------------

void User::SetNick(char * sNick, const uint8_t ui8NickLen) {
	if(m_sNick != sDefaultNick && m_sNick != NULL) {
#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sNick) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sNick in User::SetNick\n");
        }
#else
		free(m_sNick);
#endif
    }

#ifdef _WIN32
    m_sNick = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, ui8NickLen+1);
#else
	m_sNick = (char *)malloc(ui8NickLen+1);
#endif
    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);
}
//------------------------------------------------------------------------------

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

    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;
    }

#ifdef _WIN32
    m_sMyInfoOriginal = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, ui16MyInfoLen+1);
#else
	m_sMyInfoOriginal = (char *)malloc(ui16MyInfoLen+1);
#endif
    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;
    }

    if(sOldMyInfo != NULL) {
#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)sOldMyInfo) == 0) {
            AppendDebugLog("%s - [MEM] Cannot deallocate m_sOldMyInfo in UserSetMyInfoOriginal\n");
        }
#else
        free(sOldMyInfo);
#endif
    }

    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);
        }

#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)pUser->m_sMyInfoLong) == 0) {
            AppendDebugLog("%s - [MEM] Cannot deallocate pUser->m_sMyInfoLong in UserSetMyInfoLong\n");
        }
#else
        free(pUser->m_sMyInfoLong);
#endif
    }

#ifdef _WIN32
    pUser->m_sMyInfoLong = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, ui16MyInfoLongLen+1);
#else
	pUser->m_sMyInfoLong = (char *)malloc(ui16MyInfoLongLen+1);
#endif
    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);
        }

#ifdef _WIN32    	    
    	if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)pUser->m_sMyInfoShort) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate pUser->m_sMyInfoShort in UserSetMyInfoShort\n");
        }
#else
		free(pUser->m_sMyInfoShort);
#endif
    }

#ifdef _WIN32
    pUser->m_sMyInfoShort = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, ui16MyInfoShortLen+1);
#else
	pUser->m_sMyInfoShort = (char *)malloc(ui16MyInfoShortLen+1);
#endif
    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(char * sVersion) {
#ifdef _WIN32
	if(m_sVersion) {
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sVersion) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sVersion in User::SetVersion\n");
        }
    }
#else
	free(m_sVersion);
#endif

    size_t szLen = strlen(sVersion);
#ifdef _WIN32
    m_sVersion = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szLen+1);
#else
	m_sVersion = (char *)malloc(szLen+1);
#endif
    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, sVersion, szLen);
    m_sVersion[szLen] = '\0';
}
//------------------------------------------------------------------------------

void User::SetLastChat(char * sData, const size_t szLen) {
#ifdef _WIN32
    if(m_sLastChat != NULL) {
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sLastChat) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sLastChat in User::SetLastChat\n");
        }
    }
#else
	free(m_sLastChat);
#endif

#ifdef _WIN32
    m_sLastChat = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szLen+1);
#else
	m_sLastChat = (char *)malloc(szLen+1);
#endif
    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(char * sData, const size_t szLen) {
#ifdef _WIN32
    if(m_sLastPM != NULL) {
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sLastPM) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sLastPM in User::SetLastPM\n");
        }
    }
#else
	free(m_sLastPM);
#endif

#ifdef _WIN32
    m_sLastPM = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szLen+1);
#else
	m_sLastPM = (char *)malloc(szLen+1);
#endif
    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(char * sData, const size_t szLen) {
#ifdef _WIN32
    if(m_sLastSearch != NULL) {
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_sLastSearch) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate m_sLastSearch in User::SetLastSearch\n");
        }
    }
#else
	free(m_sLastSearch);
#endif

#ifdef _WIN32
    m_sLastSearch = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szLen+1);
#else
	m_sLastSearch = (char *)malloc(szLen+1);
#endif
    if(m_sLastSearch == NULL) {
        m_ui32BoolBits |= BIT_ERROR;
        Close();

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

        return;
    }   
    memcpy(m_sLastSearch, sData, szLen);
    m_sLastSearch[szLen] = '\0';
    m_ui16SameSearchs = 1;
    m_ui64SameSearchsTick = ServerManager::m_ui64ActualTick;
    m_ui16LastSearchLen = (uint16_t)szLen;
}
//------------------------------------------------------------------------------

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

    if(m_pLogInOut == NULL) {
		m_pLogInOut = new (std::nothrow) LoginLogout();
        if(m_pLogInOut == NULL) {
			m_ui32BoolBits |= BIT_ERROR;
    		Close();

    		AppendDebugLog("%s - [MEM] Cannot allocate new pLogInOut in User::SetBuffer\n");
    		return;
        }
    }

	void * pOldBuf = m_pLogInOut->m_pBuffer;

    if(szLen < 512) {
#ifdef _WIN32
		if(m_pLogInOut->m_pBuffer == NULL) {
			m_pLogInOut->m_pBuffer = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szLen+1);
		} else {
			m_pLogInOut->m_pBuffer = (char *)HeapReAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, pOldBuf, szLen+1);
		}
#else
		m_pLogInOut->m_pBuffer = (char *)realloc(pOldBuf, szLen+1);
#endif
        if(m_pLogInOut->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_pLogInOut->m_pBuffer, sKickMsg, szLen);
		m_pLogInOut->m_pBuffer[szLen] = '\0';
    } else {
#ifdef _WIN32
		if(m_pLogInOut->m_pBuffer == NULL) {
			m_pLogInOut->m_pBuffer = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, 512);
		} else {
			m_pLogInOut->m_pBuffer = (char *)HeapReAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, pOldBuf, 512);
		}
#else
		m_pLogInOut->m_pBuffer = (char *)realloc(pOldBuf, 512);
#endif
        if(m_pLogInOut->m_pBuffer == NULL) {
			m_ui32BoolBits |= BIT_ERROR;
            Close();

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

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

void User::FreeBuffer() {
    if(m_pLogInOut->m_pBuffer != NULL) {
#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)m_pLogInOut->m_pBuffer) == 0) {
            AppendDebugLog("%s - [MEM] Cannot deallocate pLogInOut->pBuffer in User::FreeBuffer\n");
        }
#else
        free(m_pLogInOut->m_pBuffer);
#endif
		m_pLogInOut->m_pBuffer = NULL;
    }
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void User::Close(const bool bNoQuit/* = 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) {         
            int iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "$Quit %s|", m_sNick); 
            if(iMsgLen > 0) {
                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 _WITH_SQLITE
		DBSQLite::m_Ptr->UpdateRecord(this);
#elif _WITH_POSTGRES
		DBPostgreSQL::m_Ptr->UpdateRecord(this);
#elif _WITH_MYSQL
		DBMySQL::m_Ptr->UpdateRecord(this);
#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);
	}

    if(ui8OldState > STATE_ADDME_2LOOP) {
        ServerManager::m_ui32Logged--;
    }

    if(m_pCmdActive4Search != NULL) {
        User::DeletePrcsdUsrCmd(m_pCmdActive4Search);
		m_pCmdActive4Search = NULL;
    }

    if(m_pCmdActive6Search != NULL) {
        User::DeletePrcsdUsrCmd(m_pCmdActive6Search);
		m_pCmdActive6Search = NULL;
    }

    if(m_pCmdPassiveSearch != NULL) {
        User::DeletePrcsdUsrCmd(m_pCmdPassiveSearch);
		m_pCmdPassiveSearch = NULL;
    }
                        
    PrcsdUsrCmd * cur = NULL,
        * next = m_pCmdStrt;
                        
    while(next != NULL) {
        cur = next;
        next = cur->m_pNext;

#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)cur->m_sCommand) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate cur->m_sCommand in User::Close\n");
        }
#else
		free(cur->m_sCommand);
#endif
        cur->m_sCommand = NULL;

        delete cur;
	}
    
	m_pCmdStrt = NULL;
	m_pCmdEnd = NULL;
    
    PrcsdToUsrCmd * curto = NULL,
        * nextto = m_pCmdToUserStrt;
                        
    while(nextto != NULL) {
        curto = nextto;
        nextto = curto->m_pNext;

#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)curto->m_sCommand) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate curto->m_sCommand in User::Close\n");
        }
#else
		free(curto->m_sCommand);
#endif
        curto->m_sCommand = NULL;

#ifdef _WIN32
        if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)curto->m_sToNick) == 0) {
			AppendDebugLog("%s - [MEM] Cannot deallocate curto->m_sToNick in User::Close\n");
        }
#else
		free(curto->m_sToNick);
#endif
        curto->m_sToNick = NULL;

        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);
        }
    }

    if(m_ui32SendBufDataLen == 0 || (m_ui32BoolBits & BIT_ERROR) == BIT_ERROR) {
		m_ui8State = STATE_REMME;
    } else {
        if(m_pLogInOut == NULL) {
			m_pLogInOut = new (std::nothrow) LoginLogout();
            if(m_pLogInOut == NULL) {
				m_ui8State = STATE_REMME;
        		AppendDebugLog("%s - [MEM] Cannot allocate new pLogInOut in User::Close\n");
        		return;
            }
        }

		m_pLogInOut->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(((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == 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->CreateZPipe(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, Allign16K);
                    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;
#ifdef _WIN32
                    Users::m_Ptr->m_pNickList = (char *)HeapReAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)pOldBuf, Users::m_Ptr->m_ui32NickListSize+NICKLISTSIZE+1);
#else
					Users::m_Ptr->m_pNickList = (char *)realloc(pOldBuf, Users::m_Ptr->m_ui32NickListSize+NICKLISTSIZE+1);
#endif
                    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: {
            if(Users::m_Ptr->m_ui32MyInfosTagLen == 0) {
                break;
            }

            if(((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == 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->CreateZPipe(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, Allign128K);
                    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: {
    		if(ProfileManager::m_Ptr->IsAllowed(this, ProfileManager::SENDFULLMYINFOS) == false) {
                if(Users::m_Ptr->m_ui32MyInfosLen == 0) {
                    break;
                }

                if(((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == 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->CreateZPipe(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, Allign128K);
                        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 {
                if(Users::m_Ptr->m_ui32MyInfosTagLen == 0) {
                    break;
                }

                if(((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == 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->CreateZPipe(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, Allign128K);
                        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: {
            if(Users::m_Ptr->m_ui32MyInfosLen == 0) {
                break;
            }

            if(((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == 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->CreateZPipe(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, Allign128K);
                    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(((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == 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->CreateZPipe(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, Allign16K);
                    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;
#ifdef _WIN32
                Users::m_Ptr->m_pOpList = (char *)HeapReAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)pOldBuf, Users::m_Ptr->m_ui32OpListSize+OPLISTSIZE+1);
#else
				Users::m_Ptr->m_pOpList = (char *)realloc(pOldBuf, Users::m_Ptr->m_ui32OpListSize+OPLISTSIZE+1);
#endif
                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(((m_ui32SupportBits & SUPPORTBIT_ZPIPE) == SUPPORTBIT_ZPIPE) == 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->CreateZPipe(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, Allign16K);
                    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) {
            if(m_sChangedDescriptionLong != NULL) {
                User::FreeInfo(m_sChangedDescriptionLong, "sChangedDescriptionLong");
				m_sChangedDescriptionLong = NULL;
            }
			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) {
            if(m_sChangedTagLong != NULL) {
                User::FreeInfo(m_sChangedTagLong, "sChangedTagLong");
				m_sChangedTagLong = NULL;
            }
			m_ui8ChangedTagLongLen = 0;
        }
    } 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) {
            if(m_sChangedConnectionLong != NULL) {
                User::FreeInfo(m_sChangedConnectionLong, "sChangedConnectionLong");
				m_sChangedConnectionLong = NULL;
            }
			m_ui8ChangedConnectionLongLen = 0;
        }
    } 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) {
            if(m_sChangedEmailLong != NULL) {
                User::FreeInfo(m_sChangedEmailLong, "sChangedEmailLong");
				m_sChangedEmailLong = NULL;
            }
			m_ui8ChangedEmailLongLen = 0;
        }
    } 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) {
            if(m_sChangedDescriptionShort != NULL) {
                User::FreeInfo(m_sChangedDescriptionShort, "sChangedDescriptionShort");
				m_sChangedDescriptionShort = NULL;
            }
			m_ui8ChangedDescriptionShortLen = 0;
        }
    } 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) {
            if(m_sChangedTagShort != NULL) {
                User::FreeInfo(m_sChangedTagShort, "sChangedTagShort");
				m_sChangedTagShort = NULL;
            }
			m_ui8ChangedTagShortLen = 0;
        }
    } 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) {
            if(m_sChangedConnectionShort != NULL) {
                User::FreeInfo(m_sChangedConnectionShort, "sChangedConnectionShort");
				m_sChangedConnectionShort = NULL;
            }
			m_ui8ChangedConnectionShortLen = 0;
        }
    } 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) {
            if(m_sChangedEmailShort != NULL) {
                User::FreeInfo(m_sChangedEmailShort, "sChangedEmailShort");
				m_sChangedEmailShort = NULL;
            }
			m_ui8ChangedEmailShortLen = 0;
        }
    } 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;
}
//---------------------------------------------------------------------------

#ifdef _WIN32
void User::FreeInfo(char * sInfo, const char * sName) {
	if(sInfo != NULL) {
		if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)sInfo) == 0) {
			AppendDebugLogFormat("[MEM] Cannot deallocate %s in User::FreeInfo\n", sName);
        }
    }
#else
void User::FreeInfo(char * sInfo, const char */* sName*/) {
	free(sInfo);
#endif
}
//------------------------------------------------------------------------------

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) {
            uint32_t slots = m_ui32Slots;
            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, 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;
#ifdef _WIN32
                cur->m_sCommand = (char *)HeapReAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)pOldBuf, cur->m_ui32Len+szCommandLen+1);
#else
				cur->m_sCommand = (char *)realloc(pOldBuf, cur->m_ui32Len+szCommandLen+1);
#endif
                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;
        }

#ifdef _WIN32
        pNewToCmd->m_sCommand = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szCommandLen+1);
#else
		pNewToCmd->m_sCommand = (char *)malloc(szCommandLen+1);
#endif
        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;
        
#ifdef _WIN32
        pNewToCmd->m_sToNick = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, pToUser->m_ui8NickLen+1);
#else
		pNewToCmd->m_sToNick = (char *)malloc(pToUser->m_ui8NickLen+1);
#endif
        if(pNewToCmd->m_sToNick == NULL) {
			m_ui32BoolBits |= BIT_ERROR;
            Close();

			AppendDebugLogFormat("[MEM] Cannot allocate %" PRIu8 " bytes for ToNick in User::AddPrcsdCmd\n", pToUser->m_ui8NickLen+1);

#ifdef _WIN32
            if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)pNewToCmd->m_sCommand) == 0) {
                AppendDebugLog("%s - [MEM] Cannot deallocate pNewToCmd->sCommand in User::AddPrcsdCmd\n");
			}
#else
            free(pNewToCmd->m_sCommand);
#endif

            delete pNewToCmd;

            return;
        }   

        memcpy(pNewToCmd->m_sToNick, pToUser->m_sNick, pToUser->m_ui8NickLen);
        pNewToCmd->m_sToNick[pToUser->m_ui8NickLen] = '\0';
        
        pNewToCmd->m_ui32ToNickLen = pToUser->m_ui8NickLen;
        pNewToCmd->m_pNext = NULL;
               
        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;
    }

#ifdef _WIN32
    pNewcmd->m_sCommand = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szCommandLen+1);
#else
	pNewcmd->m_sCommand = (char *)malloc(szCommandLen+1);
#endif
    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 = NULL;
    pNewcmd->m_pPtr = (void *)pToUser;

    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_pLogInOut->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, char * sNewData, const size_t szNewDataLen, const char * sDataName) {
    if(sOldData != NULL) {
        User::FreeInfo(sOldData, sDataName);
        sOldData = NULL;
        ui8OldDataLen = 0;
    }

    if(szNewDataLen > 0) {
#ifdef _WIN32
        sOldData = (char *)HeapAlloc(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, szNewDataLen+1);
#else
        sOldData = (char *)malloc(szNewDataLen+1);
#endif
        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) {
#ifdef _WIN32
    if(HeapFree(ServerManager::m_hPtokaXHeap, HEAP_NO_SERIALIZE, (void *)pCommand->m_sCommand) == 0) {
        AppendDebugLog("%s - [MEM] Cannot deallocate pCommand->m_sCommand in User::DeletePrcsdUsrCmd\n");
    }
#else
    free(pCommand->m_sCommand);
#endif
    delete pCommand;
}
