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

 * 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 "DB-SQLite.h"
#ifdef FLYLINKDC_USE_DB
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "hashRegManager.h"
#include "hashUsrManager.h"
#include "HubCommands.h"
#include "IP2Country.h"
#include "LanguageManager.h"
#include "ProfileManager.h"
#include "ServerManager.h"
#include "SettingManager.h"
#include "TextConverter.h"
#include "UdpDebug.h"
#include "User.h"
#include "utility.h"
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
DBSQLite * DBSQLite::m_Ptr = NULL;
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

DBSQLite::DBSQLite() : m_bConnected(false)
{
	m_pSqliteDB = nullptr;
	if (SettingManager::m_Ptr->m_bBools[SETBOOL_ENABLE_DATABASE] == false)
	{
		return;
	}

#ifdef _WIN32
	int iRet = sqlite3_open((ServerManager::m_sPath + "\\cfg\\users.sqlite").c_str(), &m_pSqliteDB);
#else
	int iRet = sqlite3_open((ServerManager::m_sPath + "/cfg/users.sqlite").c_str(), &m_pSqliteDB);
#endif
	if (iRet != SQLITE_OK)
	{
		m_bConnected = false;
		AppendLog(string("DBSQLite connection failed: ") + sqlite3_errmsg(m_pSqliteDB));
		sqlite3_close(m_pSqliteDB);

		return;
	}

	char * sErrMsg = nullptr;

	iRet = sqlite3_exec(m_pSqliteDB, "PRAGMA synchronous = NORMAL;\r\n"
	                    "PRAGMA journal_mode = WAL;",
	                    NULL, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		m_bConnected = false;
		AppendLog(string("DBSQLite PRAGMA set failed: ") + sErrMsg);
		sqlite3_free(sErrMsg);
		sqlite3_close(m_pSqliteDB);

		return;
	}

	iRet = sqlite3_exec(m_pSqliteDB,
	                    "CREATE TABLE IF NOT EXISTS userinfo ("
	                    "nick VARCHAR(64) NOT NULL,"
	                    "nick_lower VARCHAR(64) NOT NULL,"
	                    "last_updated DATETIME NOT NULL,"
	                    "ip_address VARCHAR(39) NOT NULL,"
	                    "share VARCHAR(24) NOT NULL,"
	                    "description VARCHAR(192),"
	                    "tag VARCHAR(192),"
	                    "connection VARCHAR(32),"
	                    "email VARCHAR(96),"
	                    "message_count INTEGER default 0"
	                    ");", NULL, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		m_bConnected = false;
		AppendLog(string("DBSQLite check/create table failed: ") + sErrMsg);
		sqlite3_free(sErrMsg);
		sqlite3_close(m_pSqliteDB);
		return;
	}
	//
	iRet = sqlite3_exec(m_pSqliteDB, "ALTER TABLE userinfo ADD COLUMN message_count INTEGER default 0;", NULL, NULL, &sErrMsg);
	if (iRet == SQLITE_OK)
	{
		/*
		        if (iRet != SQLITE_OK)
		        {
		            m_bConnected = false;
		            AppendLog(string("DBSQLite update userinfo set message_count = 0 where message_count is null failed: ") + sErrMsg);
		            sqlite3_free(sErrMsg);
		            sqlite3_close(m_pSqliteDB);
		            return;
		        }
		        */
	}
	iRet = sqlite3_exec(m_pSqliteDB, "CREATE INDEX IF NOT EXISTS i_userinfo_message_count ON userinfo(message_count);", NULL, NULL, &sErrMsg);
	if (iRet != SQLITE_OK)
	{
		m_bConnected = false;
		AppendLog(string("DBSQLite CREATE UNIQUE INDEX IF NOT EXISTS i_userinfo_message_count ON userinfo(message_count) failed: ") + sErrMsg);
		sqlite3_free(sErrMsg);
		sqlite3_close(m_pSqliteDB);
		return;
	}

	//
	iRet = sqlite3_exec(m_pSqliteDB, "ALTER TABLE userinfo ADD COLUMN nick_lower VARCHAR(64);", NULL, NULL, &sErrMsg);
	if (iRet == SQLITE_OK)
	{
		iRet = sqlite3_exec(m_pSqliteDB, "update userinfo set nick_lower = LOWER(nick);", NULL, NULL, &sErrMsg);
		if (iRet != SQLITE_OK)
		{
			m_bConnected = false;
			AppendLog(string("DBSQLite update userinfo set nick_lower = LOWER(nick); failed: ") + sErrMsg);
			sqlite3_free(sErrMsg);
			sqlite3_close(m_pSqliteDB);
			return;
		}
	}
	iRet = sqlite3_exec(m_pSqliteDB, "CREATE UNIQUE INDEX IF NOT EXISTS iu_userinfo_nick ON userinfo(nick_lower);", NULL, NULL, &sErrMsg);
	if (iRet != SQLITE_OK)
	{
		m_bConnected = false;
		AppendLog(string("DBSQLite CREATE UNIQUE INDEX IF NOT EXISTS iu_userinfo_nick ON userinfo(nick_lower) failed: ") + sErrMsg);
		sqlite3_free(sErrMsg);
		sqlite3_close(m_pSqliteDB);
		return;
	}

	m_bConnected = true;
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

DBSQLite::~DBSQLite()
{
#ifdef FLYLINKDC_USE_SQLITE_REMOVE_OLD_RECORD
	// When user don't want to save data in database forever then he can set to remove records older than X days.
	if (SettingManager::m_Ptr->m_i16Shorts[SETSHORT_DB_REMOVE_OLD_RECORDS] != 0)
	{
		RemoveOldRecords(SettingManager::m_Ptr->m_i16Shorts[SETSHORT_DB_REMOVE_OLD_RECORDS]);
	}
#endif // FLYLINKDC_USE_SQLITE_REMOVE_OLD_RECORD    
	if (m_bConnected == true)
	{
		sqlite3_close(m_pSqliteDB);
	}

	sqlite3_shutdown();
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void DBSQLite::IncMessageCount(User * pUser)
{
	if (m_bConnected == false)
	{
		return;
	}

	char sNick[65];
	if (TextConverter::m_Ptr->CheckUtf8AndConvert(pUser->m_sNick, pUser->m_ui8NickLen, sNick, 65) == 0)
	{
		return;
	}
	char sSQLCommand[1024];
	sqlite3_snprintf(sizeof(sSQLCommand), sSQLCommand,
	                 "UPDATE userinfo SET message_count = message_count+1 WHERE nick_lower = LOWER(%Q);", sNick);

	char * sErrMsg = nullptr;

	int iRet = sqlite3_exec(m_pSqliteDB, sSQLCommand, NULL, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite update record [IncMessageCount] failed: %s", sErrMsg);
		sqlite3_free(sErrMsg);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Now that important part. Function to update or insert user to database.
void DBSQLite::UpdateRecord(User * pUser)
{
	if (m_bConnected == false)
	{
		return;
	}

	char sNick[65];
	if (TextConverter::m_Ptr->CheckUtf8AndConvert(pUser->m_sNick, pUser->m_ui8NickLen, sNick, 65) == 0)
	{
		return;
	}

	char sShare[24];
	if (snprintf(sShare, 24, "%0.02f GB", (double)pUser->m_ui64SharedSize / 1073741824) <= 0)
	{
		return;
	}

	char sDescription[193];
	sDescription[0] = '\0';

	if (pUser->m_sDescription != NULL)
	{
		TextConverter::m_Ptr->CheckUtf8AndConvert(pUser->m_sDescription, pUser->m_ui8DescriptionLen, sDescription, 193);
	}

	char sTag[193];
	sTag[0] = '\0';

	if (pUser->m_sTag != NULL)
	{
		TextConverter::m_Ptr->CheckUtf8AndConvert(pUser->m_sTag, pUser->m_ui8TagLen, sTag, 193);
	}

	char sConnection[33];
	sConnection[0] = '\0';

	if (pUser->m_sConnection != NULL)
	{
		TextConverter::m_Ptr->CheckUtf8AndConvert(pUser->m_sConnection, pUser->m_ui8ConnectionLen, sConnection, 33);
	}

	char sEmail[97];
	sEmail[0] = '\0';

	if (pUser->m_sEmail != NULL)
	{
		TextConverter::m_Ptr->CheckUtf8AndConvert(pUser->m_sEmail, pUser->m_ui8EmailLen, sEmail, 97);
	}


	char sSQLCommand[1024];
	sqlite3_snprintf(sizeof(sSQLCommand), sSQLCommand,
	                 "UPDATE userinfo SET "
	                 "nick = %Q,"
	                 "last_updated = DATETIME('now')," // last_updated
	                 "ip_address = %Q," // ip
	                 "share = %Q," // share
	                 "description = %Q," // description
	                 "tag = %Q," // tag
	                 "connection = %Q," // connection
	                 "email = %Q" // email
	                 "WHERE nick_lower = LOWER(%Q);", // nick
	                 sNick, pUser->m_sIP, sShare, sDescription, sTag, sConnection, sEmail, sNick
	                );

	char * sErrMsg = NULL;

	int iRet = sqlite3_exec(m_pSqliteDB, sSQLCommand, NULL, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite update record failed: %s", sErrMsg);
		sqlite3_free(sErrMsg);
	}

	iRet = sqlite3_changes(m_pSqliteDB);
	if (iRet != 0)
	{
		return;
	}

	sqlite3_snprintf(1024, sSQLCommand,
	                 "INSERT INTO userinfo (nick, nick_lower, last_updated, ip_address, share, description, tag, connection, email) VALUES ("
	                 "%Q," // nick
	                 "LOWER(%Q)," // nick
	                 "DATETIME('now')," // last_updated
	                 "%Q," // ip
	                 "%Q," // share
	                 "%Q," // description
	                 "%Q," // tag
	                 "%Q," // connection
	                 "%Q" // email
	                 ");",
	                 sNick, sNick, pUser->m_sIP, sShare, sDescription, sTag, sConnection, sEmail
	                );

	iRet = sqlite3_exec(m_pSqliteDB, sSQLCommand, NULL, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite insert record failed: %s", sErrMsg);
		sqlite3_free(sErrMsg);
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

static bool bFirst = true;
static bool bSecond = false;
static char sFirstNick[65];
static char sFirstIP[40];
static int iMsgLen = 0;
static int iAfterHubSecMsgLen = 0;

static int SelectCallBack(void *, int iArgCount, char ** ppArgSTrings, char **)
{
	if (iArgCount != 8)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite SelectCallBack wrong iArgCount: %d", iArgCount);
		return 0;
	}

	if (bFirst == true)
	{
		bFirst = false;
		bSecond = true;

		size_t szLength = strlen(ppArgSTrings[0]);
		memcpy(sFirstNick, ppArgSTrings[0], szLength);
		sFirstNick[szLength] = '\0';

		szLength = strlen(ppArgSTrings[2]);
		memcpy(sFirstIP, ppArgSTrings[2], szLength);
		sFirstIP[szLength] = '\0';

		szLength = strlen(ppArgSTrings[0]);
		if (szLength == 0 || szLength > 64)
		{
			UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid nick length: %zu", szLength);
			return 0;
		}

		int iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_NICK], ppArgSTrings[0]);
		if (iRet <= 0)
		{
			return 0;
		}
		iMsgLen += iRet;

		RegUser * pReg = RegManager::m_Ptr->Find(ppArgSTrings[0], szLength);
		if (pReg != NULL)
		{
			iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_PROFILE], ProfileManager::m_Ptr->m_ppProfilesTable[pReg->m_ui16Profile]->m_sName);
			if (iRet <= 0)
			{
				return 0;
			}
			iMsgLen += iRet;
		}

		// In case when SQL wildcards were used is possible that user is online. Then we don't use data from database, but data that are in server memory.
		User * pOnlineUser = HashManager::m_Ptr->FindUser(ppArgSTrings[0], szLength);
		if (pOnlineUser != NULL)
		{
			iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s ", LanguageManager::m_Ptr->m_sTexts[LAN_STATUS], LanguageManager::m_Ptr->m_sTexts[LAN_ONLINE_FROM]);
			if (iRet <= 0)
			{
				return 0;
			}
			iMsgLen += iRet;

			struct tm *tm = localtime(&pOnlineUser->m_tLoginTime);
			iRet = (int)strftime(ServerManager::m_pGlobalBuffer + iMsgLen, 256, "%c", tm);
			if (iRet <= 0)
			{
				return 0;
			}
			iMsgLen += iRet;

			if (pOnlineUser->m_sIPv4[0] != '\0')
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s / %s\n%s: %0.02f %s", LanguageManager::m_Ptr->m_sTexts[LAN_IP], pOnlineUser->m_sIP, pOnlineUser->m_sIPv4, LanguageManager::m_Ptr->m_sTexts[LAN_SHARE_SIZE], (double)pOnlineUser->m_ui64SharedSize / 1073741824, LanguageManager::m_Ptr->m_sTexts[LAN_GIGA_BYTES]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;
			}
			else
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s\n%s: %0.02f %s", LanguageManager::m_Ptr->m_sTexts[LAN_IP], pOnlineUser->m_sIP, LanguageManager::m_Ptr->m_sTexts[LAN_SHARE_SIZE], (double)pOnlineUser->m_ui64SharedSize / 1073741824, LanguageManager::m_Ptr->m_sTexts[LAN_GIGA_BYTES]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;
			}

			if (pOnlineUser->m_sDescription != NULL)
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: ", LanguageManager::m_Ptr->m_sTexts[LAN_DESCRIPTION]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;
				memcpy(ServerManager::m_pGlobalBuffer + iMsgLen, pOnlineUser->m_sDescription, pOnlineUser->m_ui8DescriptionLen);
				iMsgLen += (int)pOnlineUser->m_ui8DescriptionLen;
			}

			if (pOnlineUser->m_sTag != NULL)
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: ", LanguageManager::m_Ptr->m_sTexts[LAN_TAG]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;
				memcpy(ServerManager::m_pGlobalBuffer + iMsgLen, pOnlineUser->m_sTag, pOnlineUser->m_ui8TagLen);
				iMsgLen += (int)pOnlineUser->m_ui8TagLen;
			}

			if (pOnlineUser->m_sConnection != NULL)
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: ", LanguageManager::m_Ptr->m_sTexts[LAN_CONNECTION]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;
				memcpy(ServerManager::m_pGlobalBuffer + iMsgLen, pOnlineUser->m_sConnection, pOnlineUser->m_ui8ConnectionLen);
				iMsgLen += (int)pOnlineUser->m_ui8ConnectionLen;
			}

			if (pOnlineUser->m_sEmail != NULL)
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: ", LanguageManager::m_Ptr->m_sTexts[LAN_EMAIL]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;
				memcpy(ServerManager::m_pGlobalBuffer + iMsgLen, pOnlineUser->m_sEmail, pOnlineUser->m_ui8EmailLen);
				iMsgLen += (int)pOnlineUser->m_ui8EmailLen;
			}

			if (IpP2Country::m_Ptr->m_ui32Count != 0)
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: ", LanguageManager::m_Ptr->m_sTexts[LAN_COUNTRY]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;
				memcpy(ServerManager::m_pGlobalBuffer + iMsgLen, IpP2Country::m_Ptr->GetCountry(pOnlineUser->m_ui8Country, false), 2);
				iMsgLen += 2;
			}

			return 0;
		}
		else     // User is offline, then we use data from database.
		{
			iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s ", LanguageManager::m_Ptr->m_sTexts[LAN_STATUS], LanguageManager::m_Ptr->m_sTexts[LAN_OFFLINE_FROM]);
			if (iRet <= 0)
			{
				return 0;
			}
			iMsgLen += iRet;
#ifdef _WIN32
			time_t tTime = (time_t)_strtoui64(ppArgSTrings[1], NULL, 10);
#else
			time_t tTime = (time_t)strtoull(ppArgSTrings[1], NULL, 10);
#endif
			struct tm *tm = localtime(&tTime);
			iRet = (int)strftime(ServerManager::m_pGlobalBuffer + iMsgLen, 256, "%c", tm);
			if (iRet <= 0)
			{
				return 0;
			}
			iMsgLen += iRet;

			szLength = strlen(ppArgSTrings[2]);
			if (szLength == 0 || szLength > 39)
			{
				UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid ip length: %zu", szLength);
				return 0;
			}

			szLength = strlen(ppArgSTrings[3]);
			if (szLength == 0 || szLength > 24)
			{
				UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid share length: %zu", szLength);
				return 0;
			}

			char * sIP = ppArgSTrings[2];

			iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_IP], sIP, LanguageManager::m_Ptr->m_sTexts[LAN_SHARE_SIZE], ppArgSTrings[3]);
			if (iRet <= 0)
			{
				return 0;
			}
			iMsgLen += iRet;

			szLength = strlen(ppArgSTrings[4]);
			if (szLength != 0)
			{
				if (szLength > 192)
				{
					UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid description length: %zu", szLength);
					return 0;
				}

					iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_DESCRIPTION], ppArgSTrings[4]);
					if (iRet <= 0)
					{
						return 0;
					}
					iMsgLen += iRet;
				}

			szLength = strlen(ppArgSTrings[5]);
			if (szLength != 0)
			{
				if (szLength > 192)
				{
					UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid tag length: %zu", szLength);
					return 0;
				}

					iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_TAG], ppArgSTrings[5]);
					if (iRet <= 0)
					{
						return 0;
					}
					iMsgLen += iRet;
				}

			szLength = strlen(ppArgSTrings[6]);
			if (szLength != 0)
			{
				if (szLength > 32)
				{
					UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid connection length: %zu", szLength);
					return 0;
				}

					iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_CONNECTION], ppArgSTrings[6]);
					if (iRet <= 0)
					{
						return 0;
					}
					iMsgLen += iRet;
				}

			szLength = strlen(ppArgSTrings[7]);
			if (szLength != 0)
			{
				if (szLength > 96)
				{
					UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid email length: %zu", szLength);
					return 0;
				}

					iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_EMAIL], ppArgSTrings[7]);
					if (iRet <= 0)
					{
						return 0;
					}
					iMsgLen += iRet;
				}

			uint8_t ui128IPHash[16];
			memset(ui128IPHash, 0, 16);

			if (IpP2Country::m_Ptr->m_ui32Count != 0 && HashIP(sIP, ui128IPHash) == true)
			{
				iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: ", LanguageManager::m_Ptr->m_sTexts[LAN_COUNTRY]);
				if (iRet <= 0)
				{
					return 0;
				}
				iMsgLen += iRet;

				memcpy(ServerManager::m_pGlobalBuffer + iMsgLen, IpP2Country::m_Ptr->Find(ui128IPHash, false), 2);
				iMsgLen += 2;
			}

			return 0;
		}
	}
	else if (bSecond == true)
	{
		bSecond = false;

		size_t szLength = strlen(sFirstNick);
		if (szLength == 0 || szLength > 64)
		{
			UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid nick length: %zu", szLength);
			return 0;
		}

		szLength = strlen(sFirstIP);
		if (szLength == 0 || szLength > 39)
		{
			UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid ip length: %zu", szLength);
			return 0;
		}

		int iRet = snprintf(ServerManager::m_pGlobalBuffer + iAfterHubSecMsgLen, ServerManager::m_szGlobalBufferSize - iAfterHubSecMsgLen, "\n%s: %s\t\t%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_NICK], sFirstNick, LanguageManager::m_Ptr->m_sTexts[LAN_IP], sFirstIP);
		if (iRet <= 0)
		{
			return 0;
		}
		iMsgLen = iAfterHubSecMsgLen + iRet;
	}

	size_t szLength = strlen(ppArgSTrings[0]);
	if (szLength == 0 || szLength > 64)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid nick length: %zu", szLength);
		return 0;
	}

	szLength = strlen(ppArgSTrings[2]);
	if (szLength == 0 || szLength > 39)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search returned invalid ip length: %zu", szLength);
		return 0;
	}

	int iRet = snprintf(ServerManager::m_pGlobalBuffer + iMsgLen, ServerManager::m_szGlobalBufferSize - iMsgLen, "\n%s: %s\t\t%s: %s", LanguageManager::m_Ptr->m_sTexts[LAN_NICK], ppArgSTrings[0], LanguageManager::m_Ptr->m_sTexts[LAN_IP], ppArgSTrings[2]);
	if (iRet <= 0)
	{
		return 0;
	}
	iMsgLen += iRet;

	return 0;
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// First of two functions to search data in database. Nick will be probably most used.
bool DBSQLite::SearchNick(ChatCommand * pChatCommand)
{
	if (m_bConnected == false)
	{
		return false;
	}

	char sUtfNick[65];
	if (TextConverter::m_Ptr->CheckUtf8AndConvert(pChatCommand->m_sCommand, (uint8_t)pChatCommand->m_ui32CommandLen, sUtfNick, 65) == 0)
	{
		return false;
	}

	if (pChatCommand->m_bFromPM == true)
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "$To: %s From: %s $<%s> ", pChatCommand->m_pUser->m_sNick, SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC]);
	}
	else
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "<%s> ", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC]);
	}

	if (iMsgLen <= 0)
	{
		return false;
	}

	iAfterHubSecMsgLen = iMsgLen;

	bFirst = true;
	bSecond = false;

	char sSQLCommand[256];
	sqlite3_snprintf(256, sSQLCommand, "SELECT nick, %s, ip_address, share, description, tag, connection, email FROM userinfo WHERE nick_lower LIKE LOWER(%Q) ORDER BY last_updated DESC LIMIT 50;", "strftime('%s', last_updated)", sUtfNick);

	char * sErrMsg = NULL;

	int iRet = sqlite3_exec(m_pSqliteDB, sSQLCommand, SelectCallBack, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search for nick failed: %s", sErrMsg);
		sqlite3_free(sErrMsg);

		return false;
	}

	if (iMsgLen == iAfterHubSecMsgLen)
	{
		return false;
	}
	else
	{
		ServerManager::m_pGlobalBuffer[iMsgLen] = '|';
		ServerManager::m_pGlobalBuffer[iMsgLen + 1] = '\0';

		pChatCommand->m_pUser->SendCharDelayed(ServerManager::m_pGlobalBuffer, iMsgLen + 1);

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

// Second of two fnctions to search data in database. Now using IP.
bool DBSQLite::SearchIP(ChatCommand * pChatCommand)
{
	if (m_bConnected == false)
	{
		return false;
	}

	if (pChatCommand->m_bFromPM == true)
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "$To: %s From: %s $<%s> ", pChatCommand->m_pUser->m_sNick, SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC], SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC]);
	}
	else
	{
		iMsgLen = snprintf(ServerManager::m_pGlobalBuffer, ServerManager::m_szGlobalBufferSize, "<%s> ", SettingManager::m_Ptr->m_sPreTexts[SettingManager::SETPRETXT_HUB_SEC]);
	}

	if (iMsgLen <= 0)
	{
		return false;
	}

	iAfterHubSecMsgLen = iMsgLen;

	bFirst = true;
	bSecond = false;

	char sSQLCommand[256];
	sqlite3_snprintf(256, sSQLCommand, "SELECT nick, %s, ip_address, share, description, tag, connection, email FROM userinfo WHERE ip_address LIKE %Q ORDER BY last_updated DESC LIMIT 50;", "strftime('%s', last_updated)", pChatCommand->m_sCommand);

	char * sErrMsg = NULL;

	int iRet = sqlite3_exec(m_pSqliteDB, sSQLCommand, SelectCallBack, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite search for nick failed: %s", sErrMsg);
		sqlite3_free(sErrMsg);

		return false;
	}

	if (iMsgLen == iAfterHubSecMsgLen)
	{
		return false;
	}
	else
	{
		ServerManager::m_pGlobalBuffer[iMsgLen] = '|';
		ServerManager::m_pGlobalBuffer[iMsgLen + 1] = '\0';

		pChatCommand->m_pUser->SendCharDelayed(ServerManager::m_pGlobalBuffer, iMsgLen + 1);

		return true;
	}
}
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#ifdef FLYLINKDC_USE_SQLITE_REMOVE_OLD_RECORD
// Function to remove X days old records from database.
void DBSQLite::RemoveOldRecords(const uint16_t ui16Days)
{
	if (m_bConnected == false)
	{
		return;
	}

	char sSQLCommand[256];
	if (snprintf(sSQLCommand, 256, "DELETE FROM userinfo WHERE last_updated < DATETIME('now', '-%hu days', 'localtime');", ui16Days) <= 0)
	{
		return;
	}

	char * sErrMsg = NULL;

	int iRet = sqlite3_exec(m_pSqliteDB, sSQLCommand, SelectCallBack, NULL, &sErrMsg);

	if (iRet != SQLITE_OK)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite remove old records failed: %s", sErrMsg);
		sqlite3_free(sErrMsg);
	}

	iRet = sqlite3_changes(m_pSqliteDB);
	if (iRet != 0)
	{
		UdpDebug::m_Ptr->BroadcastFormat("[LOG] DBSQLite removed old records: %d", iRet);
	}
}
#endif // FLYLINKDC_USE_SQLITE_REMOVE_OLD_RECORD
//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#endif // FLYLINKDC_USE_DB

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