// SoftEther VPN Source Code
// Mayaqua Kernel
// 
// SoftEther VPN Server, Client and Bridge are free software under GPLv2.
// 
// Copyright (c) 2012-2016 Daiyuu Nobori.
// Copyright (c) 2012-2016 SoftEther VPN Project, University of Tsukuba, Japan.
// Copyright (c) 2012-2016 SoftEther Corporation.
// 
// All Rights Reserved.
// 
// http://www.softether.org/
// 
// Author: Daiyuu Nobori
// Comments: Tetsuo Sugiyama, Ph.D.
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// version 2 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 version 2
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// THE LICENSE AGREEMENT IS ATTACHED ON THE SOURCE-CODE PACKAGE
// AS "LICENSE.TXT" FILE. READ THE TEXT FILE IN ADVANCE TO USE THE SOFTWARE.
// 
// 
// THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN,
// UNDER JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY,
// MERGE, PUBLISH, DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS
// SOFTWARE, THAT ANY JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS
// SOFTWARE OR ITS CONTENTS, AGAINST US (SOFTETHER PROJECT, SOFTETHER
// CORPORATION, DAIYUU NOBORI OR OTHER SUPPLIERS), OR ANY JURIDICAL
// DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND OF USING, COPYING,
// MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING, AND/OR
// SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
// CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO
// EXCLUSIVE JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO,
// JAPAN. YOU MUST WAIVE ALL DEFENSES OF LACK OF PERSONAL JURISDICTION
// AND FORUM NON CONVENIENS. PROCESS MAY BE SERVED ON EITHER PARTY IN
// THE MANNER AUTHORIZED BY APPLICABLE LAW OR COURT RULE.
// 
// USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS
// YOU HAVE A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY
// CRIMINAL LAWS OR CIVIL RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS
// SOFTWARE IN OTHER COUNTRIES IS COMPLETELY AT YOUR OWN RISK. THE
// SOFTETHER VPN PROJECT HAS DEVELOPED AND DISTRIBUTED THIS SOFTWARE TO
// COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING CIVIL RIGHTS INCLUDING
// PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER COUNTRIES' LAWS OR
// CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES. WE HAVE
// NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
// INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+
// COUNTRIES AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE
// WORLD, WITH DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY
// COUNTRIES' LAWS, REGULATIONS AND CIVIL RIGHTS TO MAKE THE SOFTWARE
// COMPLY WITH ALL COUNTRIES' LAWS BY THE PROJECT. EVEN IF YOU WILL BE
// SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A PUBLIC SERVANT IN YOUR
// COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE LIABLE TO
// RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
// RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT
// JUST A STATEMENT FOR WARNING AND DISCLAIMER.
// 
// 
// SOURCE CODE CONTRIBUTION
// ------------------------
// 
// Your contribution to SoftEther VPN Project is much appreciated.
// Please send patches to us through GitHub.
// Read the SoftEther VPN Patch Acceptance Policy in advance:
// http://www.softether.org/5-download/src/9.patch
// 
// 
// DEAR SECURITY EXPERTS
// ---------------------
// 
// If you find a bug or a security vulnerability please kindly inform us
// about the problem immediately so that we can fix the security problem
// to protect a lot of users around the world as soon as possible.
// 
// Our e-mail address for security reports is:
// softether-vpn-security [at] softether.org
// 
// Please note that the above e-mail address is not a technical support
// inquiry address. If you need technical assistance, please visit
// http://www.softether.org/ and ask your question on the users forum.
// 
// Thank you for your cooperation.
// 
// 
// NO MEMORY OR RESOURCE LEAKS
// ---------------------------
// 
// The memory-leaks and resource-leaks verification under the stress
// test has been passed before release this source code.


// Cfg.c
// Configuration information manipulation module

#include <GlobalConst.h>

#define	CFG_C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <stdarg.h>
#include <time.h>
#include <errno.h>
#include <Mayaqua/Mayaqua.h>

// Create a backup of the configuration file
void BackupCfgWEx(CFG_RW *rw, FOLDER *f, wchar_t *original, UINT revision_number)
{
	wchar_t dirname[MAX_PATH];
	wchar_t filename[MAX_PATH];
	wchar_t fullpath[MAX_PATH];
	wchar_t datestr[MAX_PATH];
	SYSTEMTIME st;
	// Validate arguments
	if (f == NULL || filename == NULL || rw == NULL)
	{
		return;
	}

	// Determine the directory name
	UniFormat(dirname, sizeof(dirname), L"@backup.%s", original[0] == L'@' ? original + 1 : original);

	// Determine the file name
	LocalTime(&st);
	UniFormat(datestr, sizeof(datestr), L"%04u%02u%02u%02u_%s",
		st.wYear, st.wMonth, st.wDay, st.wHour, original[0] == L'@' ? original + 1 : original);

	if (revision_number == INFINITE)
	{
		UniStrCpy(filename, sizeof(filename), datestr);
	}
	else
	{
		UniFormat(filename, sizeof(filename), L"%08u_%s",
			revision_number, original[0] == L'@' ? original + 1 : original);
	}

	// Don't save if the date and time has not been changed
	if (UniStrCmpi(datestr, rw->LastSavedDateStr) == 0)
	{
		return;
	}

	UniStrCpy(rw->LastSavedDateStr, sizeof(rw->LastSavedDateStr), datestr);

	// Check the existence of file name
	if (IsFileExistsW(filename))
	{
		return;
	}

	// Create the directory
	MakeDirW(dirname);

	// Save the file
	UniFormat(fullpath, sizeof(fullpath), L"%s/%s", dirname, filename);
	CfgSaveW(f, fullpath);
}

// Close the configuration file R/W
void FreeCfgRw(CFG_RW *rw)
{
	// Validate arguments
	if (rw == NULL)
	{
		return;
	}

	if (rw->Io != NULL)
	{
		FileClose(rw->Io);
	}

	DeleteLock(rw->lock);
	Free(rw->FileNameW);
	Free(rw->FileName);
	Free(rw);
}

// Writing to the configuration file
UINT SaveCfgRw(CFG_RW *rw, FOLDER *f)
{
	return SaveCfgRwEx(rw, f, INFINITE);
}
UINT SaveCfgRwEx(CFG_RW *rw, FOLDER *f, UINT revision_number)
{
	UINT ret = 0;
	// Validate arguments
	if (rw == NULL || f == NULL)
	{
		return 0;
	}

	Lock(rw->lock);
	{
		if (rw->Io != NULL)
		{
			FileClose(rw->Io);
			rw->Io = NULL;
		}

		if (CfgSaveExW2(rw, f, rw->FileNameW, &ret))
		{
			if (rw->DontBackup == false)
			{
				BackupCfgWEx(rw, f, rw->FileNameW, revision_number);
			}
		}
		else
		{
			ret = 0;
		}

		rw->Io = FileOpenW(rw->FileNameW, false);
	}
	Unlock(rw->lock);

	return ret;
}

// Creating a configuration file R/W
CFG_RW *NewCfgRw(FOLDER **root, char *cfg_name)
{
	return NewCfgRwEx(root, cfg_name, false);
}
CFG_RW *NewCfgRwW(FOLDER **root, wchar_t *cfg_name)
{
	return NewCfgRwExW(root, cfg_name, false);
}
CFG_RW *NewCfgRwEx(FOLDER **root, char *cfg_name, bool dont_backup)
{
	wchar_t *cfg_name_w = CopyStrToUni(cfg_name);
	CFG_RW *ret = NewCfgRwExW(root, cfg_name_w, dont_backup);

	Free(cfg_name_w);

	return ret;
}
CFG_RW *NewCfgRwExW(FOLDER **root, wchar_t *cfg_name, bool dont_backup)
{
	return NewCfgRwEx2W(root, cfg_name, dont_backup, NULL);
}
CFG_RW *NewCfgRwEx2A(FOLDER **root, char *cfg_name_a, bool dont_backup, char *template_name_a)
{
	CFG_RW *ret;
	wchar_t *cfg_name_w = CopyStrToUni(cfg_name_a);
	wchar_t *template_name_w = CopyStrToUni(template_name_a);

	ret = NewCfgRwEx2W(root, cfg_name_w, dont_backup, template_name_w);

	Free(cfg_name_w);
	Free(template_name_w);

	return ret;
}
CFG_RW *NewCfgRwEx2W(FOLDER **root, wchar_t *cfg_name, bool dont_backup, wchar_t *template_name)
{
	CFG_RW *rw;
	FOLDER *f;
	bool loaded_from_template = false;
	// Validate arguments
	if (cfg_name == NULL || root == NULL)
	{
		return NULL;
	}

	f = CfgReadW(cfg_name);
	if (f == NULL)
	{
		// Load from template
		if (UniIsEmptyStr(template_name) == false)
		{
			f = CfgReadW(template_name);
			if (f != NULL)
			{
				loaded_from_template = true;

				goto LABEL_CONTIUNE;
			}
		}

		rw = ZeroMalloc(sizeof(CFG_RW));
		rw->lock = NewLock();
		rw->FileNameW = CopyUniStr(cfg_name);
		rw->FileName = CopyUniToStr(cfg_name);
		rw->Io = FileCreateW(cfg_name);
		*root = NULL;
		rw->DontBackup = dont_backup;

		return rw;
	}

LABEL_CONTIUNE:
	rw = ZeroMalloc(sizeof(CFG_RW));
	rw->FileNameW = CopyUniStr(cfg_name);
	rw->FileName = CopyUniToStr(cfg_name);
	if (loaded_from_template == false)
	{
		rw->Io = FileOpenW(cfg_name, false);
	}
	else
	{
		rw->Io = FileCreateW(cfg_name);
	}
	rw->lock = NewLock();

	*root = f;

	rw->DontBackup = dont_backup;

	return rw;
}

// Copy a file
bool FileCopy(char *src, char *dst)
{
	BUF *b;
	bool ret = false;
	// Validate arguments
	if (src == NULL || dst == NULL)
	{
		return false;
	}

	b = ReadDump(src);
	if (b == NULL)
	{
		return false;
	}

	SeekBuf(b, 0, 0);

	ret = DumpBuf(b, dst);

	FreeBuf(b);

	return ret;
}
bool FileCopyW(wchar_t *src, wchar_t *dst)
{
	return FileCopyExW(src, dst, true);
}
bool FileCopyExW(wchar_t *src, wchar_t *dst, bool read_lock)
{
	BUF *b;
	bool ret = false;
	// Validate arguments
	if (src == NULL || dst == NULL)
	{
		return false;
	}

	b = ReadDumpExW(src, false);
	if (b == NULL)
	{
		return false;
	}

	SeekBuf(b, 0, 0);

	ret = DumpBufW(b, dst);

	FreeBuf(b);

	return ret;
}

// Save the settings to a file
void CfgSave(FOLDER *f, char *name)
{
	CfgSaveEx(NULL, f, name);
}
void CfgSaveW(FOLDER *f, wchar_t *name)
{
	CfgSaveExW(NULL, f, name);
}
bool CfgSaveEx(CFG_RW *rw, FOLDER *f, char *name)
{
	wchar_t *name_w = CopyStrToUni(name);
	bool ret = CfgSaveExW(rw, f, name_w);

	Free(name_w);

	return ret;
}
bool CfgSaveExW(CFG_RW *rw, FOLDER *f, wchar_t *name)
{
	return CfgSaveExW2(rw, f, name, NULL);
}
bool CfgSaveExW2(CFG_RW *rw, FOLDER *f, wchar_t *name, UINT *written_size)
{
	return CfgSaveExW3(rw, f, name, written_size, IsFileExistsW(SAVE_BINARY_FILE_NAME_SWITCH));
}
bool CfgSaveExW3(CFG_RW *rw, FOLDER *f, wchar_t *name, UINT *written_size, bool write_binary)
{
	wchar_t tmp[MAX_SIZE];
	bool text = !write_binary;
	UCHAR hash[SHA1_SIZE];
	BUF *b;
	IO *o;
	bool ret = true;
	UINT dummy_int = 0;
	// Validate arguments
	if (name == NULL || f == NULL)
	{
		return false;
	}
	if (written_size == NULL)
	{
		written_size = &dummy_int;
	}

	// Convert to buffer
	b = CfgFolderToBuf(f, text);
	if (b == NULL)
	{
		return false;
	}
	// Hash the contents
	Hash(hash, b->Buf, b->Size, true);

	// Compare the contents to be written with the content which was written last
	if (rw != NULL)
	{
		if (Cmp(hash, rw->LashHash, SHA1_SIZE) == 0)
		{
			// Contents are not changed
			ret = false;
		}
		else
		{
			Copy(rw->LashHash, hash, SHA1_SIZE);
		}
	}

	if (ret || OS_IS_UNIX(GetOsInfo()->OsType))
	{
		// Generate a temporary file name
		UniFormat(tmp, sizeof(tmp), L"%s.log", name);
		// Copy the file that currently exist to a temporary file
		FileCopyW(name, tmp);

		// Save the new file
		o = FileCreateW(name);
		if (o != NULL)
		{
			if (FileWrite(o, b->Buf, b->Size) == false)
			{
				// File saving failure
				FileClose(o);
				FileDeleteW(name);
				FileRenameW(tmp, name);

				if (rw != NULL)
				{
					Zero(rw->LashHash, sizeof(rw->LashHash));
				}
			}
			else
			{
				// Successful saving file
				FileClose(o);
				// Delete the temporary file
				FileDeleteW(tmp);
			}
		}
		else
		{
			// File saving failure
			FileRenameW(tmp, name);

			if (rw != NULL)
			{
				Zero(rw->LashHash, sizeof(rw->LashHash));
			}
		}
	}

	*written_size = b->Size;

	// Release memory 
	FreeBuf(b);

	return ret;
}

// Read the settings from the file
FOLDER *CfgRead(char *name)
{
	wchar_t *name_w = CopyStrToUni(name);
	FOLDER *ret = CfgReadW(name_w);

	Free(name_w);

	return ret;
}
FOLDER *CfgReadW(wchar_t *name)
{
	wchar_t tmp[MAX_SIZE];
	wchar_t newfile[MAX_SIZE];
	BUF *b;
	IO *o;
	UINT size;
	void *buf;
	FOLDER *f;
	bool delete_new = false;
	bool binary_file = false;
	bool invalid_file = false;
	UCHAR header[8];
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	// Generate a new file name
	UniFormat(newfile, sizeof(newfile), L"%s.new", name);
	// Generate a temporary file name
	UniFormat(tmp, sizeof(tmp), L"%s.log", name);

	// Read the new file if it exists
	o = FileOpenW(newfile, false);
	if (o == NULL)
	{
		// Read the temporary file
		o = FileOpenW(tmp, false);
	}
	else
	{
		delete_new = true;
	}
	if (o == NULL)
	{
		// Read the original file if there is no temporary file
		o = FileOpenW(name, false);
	}
	else
	{
		// Read the original file too if the size of temporary file is 0
		if (FileSize(o) == 0)
		{
			invalid_file = true;
		}

		if (invalid_file)
		{
			FileClose(o);
			o = FileOpenW(name, false);
		}
	}
	if (o == NULL)
	{
		// Failed to read
		return NULL;
	}

	// Read into the buffer
	size = FileSize(o);
	buf = Malloc(size);
	FileRead(o, buf, size);
	b = NewBuf();
	WriteBuf(b, buf, size);
	SeekBuf(b, 0, 0);

	// Close the file
	FileClose(o);

	if (delete_new)
	{
		// Delete the new file
		FileDeleteW(newfile);
	}

	// If the beginning 8 character of the buffer is "SEVPN_DB", it is binary file
	ReadBuf(b, header, sizeof(header));
	if (Cmp(header, TAG_BINARY, 8) == 0)
	{
		UCHAR hash1[SHA1_SIZE], hash2[SHA1_SIZE];
		binary_file = true;

		// Check the hash 
		ReadBuf(b, hash1, sizeof(hash1));

		Hash(hash2, ((UCHAR *)b->Buf) + 8 + SHA1_SIZE, b->Size - 8 - SHA1_SIZE, true);

		if (Cmp(hash1, hash2, SHA1_SIZE) != 0)
		{
			// Corrupted file
			invalid_file = true;
			FreeBuf(b);
			return NULL;
		}
	}

	SeekBuf(b, 0, 0);

	if (binary_file)
	{
		SeekBuf(b, 8 + SHA1_SIZE, 0);
	}

	// Convert the buffer into a folder
	if (binary_file == false)
	{
		// Text mode
		f = CfgBufTextToFolder(b);
	}
	else
	{
		// Binary mode
		f = CfgBufBinToFolder(b);
	}

	// Memory release
	Free(buf);
	FreeBuf(b);

	FileDeleteW(newfile);

	return f;
}

// Test of Cfg
void CfgTest2(FOLDER *f, UINT n)
{
}

void CfgTest()
{
#if	0
	FOLDER *root;
	BUF *b;
	Debug("\nCFG Test Begin\n");

	root = CfgCreateFolder(NULL, TAG_ROOT);
	CfgTest2(root, 5);

	b = CfgFolderToBufText(root);
	//Print("%s\n", b->Buf);
	SeekBuf(b, 0, 0);

	CfgDeleteFolder(root);

	DumpBuf(b, "test1.config");

	root = CfgBufTextToFolder(b);

	FreeBuf(b);

	b = CfgFolderToBufText(root);
//	Print("%s\n", b->Buf);
	DumpBuf(b, "test2.config");
	FreeBuf(b);

	CfgSave(root, "test.txt");

	CfgDeleteFolder(root);

	Debug("\nCFG Test End\n");
#endif
}

// Read one line
char *CfgReadNextLine(BUF *b)
{
	char *tmp;
	char *buf;
	UINT len;
	// Validate arguments
	if (b == NULL)
	{
		return NULL;
	}

	// Examine the number of characters up to the next newline
	tmp = (char *)b->Buf + b->Current;
	if ((b->Size - b->Current) == 0)
	{
		// Read to the end
		return NULL;
	}
	len = 0;
	while (true)
	{
		if (tmp[len] == 13 || tmp[len] == 10)
		{
			if (tmp[len] == 13)
			{
				if (len < (b->Size - b->Current))
				{
					len++;
				}
			}
			break;
		}
		len++;
		if (len >= (b->Size - b->Current))
		{
			break;
		}
	}

	// Read ahead only 'len' bytes
	buf = ZeroMalloc(len + 1);
	ReadBuf(b, buf, len);
	SeekBuf(b, 1, 1);

	if (StrLen(buf) >= 1)
	{
		if (buf[StrLen(buf) - 1] == 13)
		{
			buf[StrLen(buf) - 1] = 0;
		}
	}

	return buf;
}

// Read the text stream
bool CfgReadNextTextBUF(BUF *b, FOLDER *current)
{
	char *buf;
	TOKEN_LIST *token;
	char *name;
	char *string;
	char *data;
	bool ret;
	FOLDER *f;

	// Validate arguments
	if (b == NULL || current == NULL)
	{
		return false;
	}

	ret = true;

	// Read one line
	buf = CfgReadNextLine(b);
	if (buf == NULL)
	{
		return false;
	}

	// Analyze this line
	token = ParseToken(buf, "\t ");
	if (token == NULL)
	{
		Free(buf);
		return false;
	}

	if (token->NumTokens >= 1)
	{
		if (!StrCmpi(token->Token[0], TAG_DECLARE))
		{
			if (token->NumTokens >= 2)
			{
				// declare
				name = CfgUnescape(token->Token[1]);

				// Create a folder
				f = CfgCreateFolder(current, name);

				// Read the next folder
				while (true)
				{
					if (CfgReadNextTextBUF(b, f) == false)
					{
						break;
					}
				}

				Free(name);
			}
		}
		if (!StrCmpi(token->Token[0], "}"))
		{
			// end
			ret = false;
		}
		if (token->NumTokens >= 3)
		{
			name = CfgUnescape(token->Token[1]);
			data = token->Token[2];

			if (!StrCmpi(token->Token[0], TAG_STRING))
			{
				// string
				wchar_t *uni;
				UINT uni_size;
				string = CfgUnescape(data);
				uni_size = CalcUtf8ToUni(string, StrLen(string));
				if (uni_size != 0)
				{
					uni = Malloc(uni_size);
					Utf8ToUni(uni, uni_size, string, StrLen(string));
					CfgAddUniStr(current, name, uni);
					Free(uni);
				}
				Free(string);
			}
			if (!StrCmpi(token->Token[0], TAG_INT))
			{
				// uint
				CfgAddInt(current, name, ToInt(data));
			}
			if (!StrCmpi(token->Token[0], TAG_INT64))
			{
				// uint64
				CfgAddInt64(current, name, ToInt64(data));
			}
			if (!StrCmpi(token->Token[0], TAG_BOOL))
			{
				// bool
				bool b = false;
				if (!StrCmpi(data, TAG_TRUE))
				{
					b = true;
				}
				else if (ToInt(data) != 0)
				{
					b = true;
				}
				CfgAddBool(current, name, b);
			}
			if (!StrCmpi(token->Token[0], TAG_BYTE))
			{
				// byte
				char *unescaped_b64 = CfgUnescape(data);
				void *tmp = Malloc(StrLen(unescaped_b64) * 4 + 64);
				int size = B64_Decode(tmp, unescaped_b64, StrLen(unescaped_b64));
				CfgAddByte(current, name, tmp, size);
				Free(tmp);
				Free(unescaped_b64);
			}

			Free(name);
		}
	}

	// Release of the token
	FreeToken(token);

	Free(buf);

	return ret;
}

// Convert the stream text to a folder
FOLDER *CfgBufTextToFolder(BUF *b)
{
	FOLDER *f, *c;
	// Validate arguments
	if (b == NULL)
	{
		return NULL;
	}

	// Read recursively from the root folder
	c = CfgCreateFolder(NULL, "tmp");

	while (true)
	{
		// Read the text stream
		if (CfgReadNextTextBUF(b, c) == false)
		{
			break;
		}
	}

	// Getting root folder
	f = CfgGetFolder(c, TAG_ROOT);
	if (f == NULL)
	{
		// Root folder is not found
		CfgDeleteFolder(c);
		return NULL;
	}

	// Remove the reference from tmp folder to the root
	Delete(c->Folders, f);
	f->Parent = NULL;

	// Delete the tmp folder
	CfgDeleteFolder(c);

	// Return the root folder
	return f;
}

// Read the next folder
void CfgReadNextFolderBin(BUF *b, FOLDER *parent)
{
	char name[MAX_SIZE];
	FOLDER *f;
	UINT n, i;
	UINT size;
	UCHAR *buf;
	wchar_t *string;
	// Validate arguments
	if (b == NULL || parent == NULL)
	{
		return;
	}

	// Folder name
	ReadBufStr(b, name, sizeof(name));
	f = CfgCreateFolder(parent, name);

	// The number of the subfolder
	n = ReadBufInt(b);
	for (i = 0;i < n;i++)
	{
		// Subfolder
		CfgReadNextFolderBin(b, f);
	}

	// The number of items
	n = ReadBufInt(b);
	for (i = 0;i < n;i++)
	{
		UINT type;

		// Name
		ReadBufStr(b, name, sizeof(name));
		// Type
		type = ReadBufInt(b);

		switch (type)
		{
		case ITEM_TYPE_INT:
			// int
			CfgAddInt(f, name, ReadBufInt(b));
			break;

		case ITEM_TYPE_INT64:
			// int64
			CfgAddInt64(f, name, ReadBufInt64(b));
			break;

		case ITEM_TYPE_BYTE:
			// data
			size = ReadBufInt(b);
			buf = ZeroMalloc(size);
			ReadBuf(b, buf, size);
			CfgAddByte(f, name, buf, size);
			Free(buf);
			break;

		case ITEM_TYPE_STRING:
			// string
			size = ReadBufInt(b);
			buf = ZeroMalloc(size + 1);
			ReadBuf(b, buf, size);
			string = ZeroMalloc(CalcUtf8ToUni(buf, StrLen(buf)) + 4);
			Utf8ToUni(string, 0, buf, StrLen(buf));
			CfgAddUniStr(f, name, string);
			Free(string);
			Free(buf);
			break;

		case ITEM_TYPE_BOOL:
			// bool
			CfgAddBool(f, name, ReadBufInt(b) == 0 ? false : true);
			break;
		}
	}
}

// Convert the binary to folder
FOLDER *CfgBufBinToFolder(BUF *b)
{
	FOLDER *f, *c;
	// Validate arguments
	if (b == NULL)
	{
		return NULL;
	}

	// Create a temporary folder
	c = CfgCreateFolder(NULL, "tmp");

	// Read a binary
	CfgReadNextFolderBin(b, c);

	// Get root folder
	f = CfgGetFolder(c, TAG_ROOT);
	if (f == NULL)
	{
		// Missing
		CfgDeleteFolder(c);
		return NULL;
	}

	Delete(c->Folders, f);
	f->Parent = NULL;

	CfgDeleteFolder(c);

	return f;
}

// Convert the folder to binary
BUF *CfgFolderToBufBin(FOLDER *f)
{
	BUF *b;
	UCHAR hash[SHA1_SIZE];
	// Validate arguments
	if (f == NULL)
	{
		return NULL;
	}

	b = NewBuf();

	// Header
	WriteBuf(b, TAG_BINARY, 8);

	// Hash area
	Zero(hash, sizeof(hash));
	WriteBuf(b, hash, sizeof(hash));

	// Output the root folder (recursive)
	CfgOutputFolderBin(b, f);

	// Hash
	Hash(((UCHAR *)b->Buf) + 8, ((UCHAR *)b->Buf) + 8 + SHA1_SIZE, b->Size - 8 - SHA1_SIZE, true);

	return b;
}

// Convert the folder to a stream text
BUF *CfgFolderToBufText(FOLDER *f)
{
	return CfgFolderToBufTextEx(f, false);
}
BUF *CfgFolderToBufTextEx(FOLDER *f, bool no_banner)
{
	BUF *b;
	// Validate arguments
	if (f == NULL)
	{
		return NULL;
	}

	// Create a stream
	b = NewBuf();

	// Copyright notice
	if (no_banner == false)
	{
		WriteBuf(b, TAG_CPYRIGHT, StrLen(TAG_CPYRIGHT));
	}

	// Output the root folder (recursive)
	CfgOutputFolderText(b, f, 0);

	return b;
}

// Output the folder contents (Enumerate folders)
bool CfgEnumFolderProc(FOLDER *f, void *param)
{
	CFG_ENUM_PARAM *p;
	// Validate arguments
	if (f == NULL || param == NULL)
	{
		return false;
	}

	p = (CFG_ENUM_PARAM *)param;
	// Output the folder contents (recursive)
	CfgOutputFolderText(p->b, f, p->depth);

	return true;
}

// Output the contents of the item (enumeration)
bool CfgEnumItemProc(ITEM *t, void *param)
{
	CFG_ENUM_PARAM *p;
	// Validate arguments
	if (t == NULL || param == NULL)
	{
		return false;
	}

	p = (CFG_ENUM_PARAM *)param;
	CfgAddItemText(p->b, t, p->depth);

	return true;
}

// Output the folder contents (Recursive, binary)
void CfgOutputFolderBin(BUF *b, FOLDER *f)
{
	UINT i;
	// Validate arguments
	if (b == NULL || f == NULL)
	{
		return;
	}

	// Folder name
	WriteBufStr(b, f->Name);

	// The number of the subfolder
	WriteBufInt(b, LIST_NUM(f->Folders));

	// Subfolder
	for (i = 0;i < LIST_NUM(f->Folders);i++)
	{
		FOLDER *sub = LIST_DATA(f->Folders, i);
		CfgOutputFolderBin(b, sub);

		if ((i % 100) == 99)
		{
			YieldCpu();
		}
	}

	// The number of Items
	WriteBufInt(b, LIST_NUM(f->Items));

	// Item
	for (i = 0;i < LIST_NUM(f->Items);i++)
	{
		char *utf8;
		UINT utf8_size;
		ITEM *t = LIST_DATA(f->Items, i);

		// Item Name
		WriteBufStr(b, t->Name);

		// Type
		WriteBufInt(b, t->Type);

		switch (t->Type)
		{
		case ITEM_TYPE_INT:
			// Integer
			WriteBufInt(b, *((UINT *)t->Buf));
			break;

		case ITEM_TYPE_INT64:
			// 64-bit integer
			WriteBufInt64(b, *((UINT64 *)t->Buf));
			break;

		case ITEM_TYPE_BYTE:
			// Data size
			WriteBufInt(b, t->size);
			// Data
			WriteBuf(b, t->Buf, t->size);
			break;

		case ITEM_TYPE_STRING:
			// String
			utf8_size = CalcUniToUtf8((wchar_t *)t->Buf) + 1;
			utf8 = ZeroMalloc(utf8_size);
			UniToUtf8(utf8, utf8_size, (wchar_t *)t->Buf);
			WriteBufInt(b, StrLen(utf8));
			WriteBuf(b, utf8, StrLen(utf8));
			Free(utf8);
			break;

		case ITEM_TYPE_BOOL:
			// Boolean type
			if (*((bool *)t->Buf) == false)
			{
				WriteBufInt(b, 0);
			}
			else
			{
				WriteBufInt(b, 1);
			}
			break;
		}
	}
}

// Output the contents of the folder (Recursive, text)
void CfgOutputFolderText(BUF *b, FOLDER *f, UINT depth)
{
	CFG_ENUM_PARAM p;
	// Validate arguments
	if (b == NULL || f == NULL)
	{
		return;
	}

	// Output starting of the folder
	CfgAddDeclare(b, f->Name, depth);
	depth++;

	Zero(&p, sizeof(CFG_ENUM_PARAM));
	p.depth = depth;
	p.b = b;
	p.f = f;

	// Enumerate the list of items
	CfgEnumItem(f, CfgEnumItemProc, &p);

	if (LIST_NUM(f->Folders) != 0 && LIST_NUM(f->Items) != 0)
	{
		WriteBuf(b, "\r\n", 2);
	}

	// Enumerate the folder list
	CfgEnumFolder(f, CfgEnumFolderProc, &p);
	// Output the end of the folder
	depth--;
	CfgAddEnd(b, depth);

	//WriteBuf(b, "\r\n", 2);
}

// Output contents of the item
void CfgAddItemText(BUF *b, ITEM *t, UINT depth)
{
	char *data;
	char *sub = NULL;
	UINT len;
	UINT size;
	char *utf8;
	UINT utf8_size;
	wchar_t *string;
	// Validate arguments
	if (b == NULL || t == NULL)
	{
		return;
	}

	// Process the data by its type
	data = NULL;
	switch (t->Type)
	{
	case ITEM_TYPE_INT:
		data = Malloc(32);
		ToStr(data, *((UINT *)t->Buf));
		break;

	case ITEM_TYPE_INT64:
		data = Malloc(64);
		ToStr64(data, *((UINT64 *)t->Buf));
		break;

	case ITEM_TYPE_BYTE:
		data = ZeroMalloc(t->size * 4 + 32);
		len = B64_Encode(data, t->Buf, t->size);
		data[len] = 0;
		break;

	case ITEM_TYPE_STRING:
		string = t->Buf;
		utf8_size = CalcUniToUtf8(string);
		utf8_size++;
		utf8 = ZeroMalloc(utf8_size);
		utf8[0] = 0;
		UniToUtf8(utf8, utf8_size, string);
		size = utf8_size;
		data = utf8;
		break;

	case ITEM_TYPE_BOOL:
		size = 32;
		data = Malloc(size);
		if (*((bool *)t->Buf) == false)
		{
			StrCpy(data, size, TAG_FALSE);
		}
		else
		{
			StrCpy(data, size, TAG_TRUE);
		}
		break;
	}
	if (data == NULL)
	{
		return;
	}

	// Output the data line
	CfgAddData(b, t->Type, t->Name, data, sub, depth);

	// Memory release
	Free(data);
	if (sub != NULL)
	{
		Free(sub);
	}
}

// Output the data line
void CfgAddData(BUF *b, UINT type, char *name, char *data, char *sub, UINT depth)
{
	char *tmp;
	char *name2;
	char *data2;
	char *sub2 = NULL;
	UINT tmp_size;
	// Validate arguments
	if (b == NULL || type == 0 || name == NULL || data == NULL)
	{
		return;
	}

	name2 = CfgEscape(name);
	data2 = CfgEscape(data);
	if (sub != NULL)
	{
		sub2 = CfgEscape(sub);
	}

	tmp_size = StrLen(name2) + StrLen(data2) + 2 + 64 + 1;
	tmp = Malloc(tmp_size);

	if (sub2 != NULL)
	{
		StrCpy(tmp, tmp_size, CfgTypeToStr(type));
		StrCat(tmp, tmp_size, " ");
		StrCat(tmp, tmp_size, name2);
		StrCat(tmp, tmp_size, " ");
		StrCat(tmp, tmp_size, data2);
		StrCat(tmp, tmp_size, " ");
		StrCat(tmp, tmp_size, sub2);
	}
	else
	{
		StrCpy(tmp, tmp_size, CfgTypeToStr(type));
		StrCat(tmp, tmp_size, " ");
		StrCat(tmp, tmp_size, name2);
		StrCat(tmp, tmp_size, " ");
		StrCat(tmp, tmp_size, data2);
	}

	Free(name2);
	Free(data2);
	if (sub2 != NULL)
	{
		Free(sub2);
	}
	CfgAddLine(b, tmp, depth);
	Free(tmp);
}

// Convert the data type string to an integer value
UINT CfgStrToType(char *str)
{
	if (!StrCmpi(str, TAG_INT)) return ITEM_TYPE_INT;
	if (!StrCmpi(str, TAG_INT64)) return ITEM_TYPE_INT64;
	if (!StrCmpi(str, TAG_BYTE)) return ITEM_TYPE_BYTE;
	if (!StrCmpi(str, TAG_STRING)) return ITEM_TYPE_STRING;
	if (!StrCmpi(str, TAG_BOOL)) return ITEM_TYPE_BOOL;
	return 0;
}

// Convert the type of data to a string
char *CfgTypeToStr(UINT type)
{
	switch (type)
	{
	case ITEM_TYPE_INT:
		return TAG_INT;
	case ITEM_TYPE_INT64:
		return TAG_INT64;
	case ITEM_TYPE_BYTE:
		return TAG_BYTE;
	case ITEM_TYPE_STRING:
		return TAG_STRING;
	case ITEM_TYPE_BOOL:
		return TAG_BOOL;
	}
	return NULL;
}

// Outputs the End line
void CfgAddEnd(BUF *b, UINT depth)
{
	// Validate arguments
	if (b == NULL)
	{
		return;
	}

	CfgAddLine(b, "}", depth);
//	CfgAddLine(b, TAG_END, depth);
}

// Outputs the Declare lines
void CfgAddDeclare(BUF *b, char *name, UINT depth)
{
	char *tmp;
	char *name2;
	UINT tmp_size;
	// Validate arguments
	if (b == NULL || name == NULL)
	{
		return;
	}

	name2 = CfgEscape(name);

	tmp_size = StrLen(name2) + 2 + StrLen(TAG_DECLARE);
	tmp = Malloc(tmp_size);

	Format(tmp, 0, "%s %s", TAG_DECLARE, name2);
	CfgAddLine(b, tmp, depth);
	CfgAddLine(b, "{", depth);
	Free(tmp);
	Free(name2);
}

// Outputs one line
void CfgAddLine(BUF *b, char *str, UINT depth)
{
	UINT i;
	// Validate arguments
	if (b == NULL)
	{
		return;
	}

	for (i = 0;i < depth;i++)
	{
		WriteBuf(b, "\t", 1);
	}
	WriteBuf(b, str, StrLen(str));
	WriteBuf(b, "\r\n", 2);
}

// Convert the folder to a stream
BUF *CfgFolderToBuf(FOLDER *f, bool textmode)
{
	return CfgFolderToBufEx(f, textmode, false);
}
BUF *CfgFolderToBufEx(FOLDER *f, bool textmode, bool no_banner)
{
	// Validate arguments
	if (f == NULL)
	{
		return NULL;
	}

	if (textmode)
	{
		return CfgFolderToBufTextEx(f, no_banner);
	}
	else
	{
		return CfgFolderToBufBin(f);;
	}
}

// Escape restoration of the string
char *CfgUnescape(char *str)
{
	char *tmp;
	char *ret;
	char tmp2[16];
	UINT len, wp, i;
	UINT code;
	// Validate arguments
	if (str == NULL)
	{
		return NULL;
	}

	len = StrLen(str);
	tmp = ZeroMalloc(len + 1);
	wp = 0;
	if (len == 1 && str[0] == '$')
	{
		// Empty character
		tmp[0] = 0;
	}
	else
	{
		for (i = 0;i < len;i++)
		{
			if (str[i] != '$')
			{
				tmp[wp++] = str[i];
			}
			else
			{
				tmp2[0] = '0';
				tmp2[1] = 'x';
				tmp2[2] = str[i + 1];
				tmp2[3] = str[i + 2];
				i += 2;
				tmp2[4] = 0;
				code = ToInt(tmp2);
				tmp[wp++] = (char)code;
			}
		}
	}
	ret = Malloc(StrLen(tmp) + 1);
	StrCpy(ret, StrLen(tmp) + 1, tmp);
	Free(tmp);
	return ret;
}

// Escape the string
char *CfgEscape(char *str)
{
	char *tmp;
	char *ret;
	char tmp2[16];
	UINT len;
	UINT wp, i;
	// Validate arguments
	if (str == NULL)
	{
		return NULL;
	}

	len = StrLen(str);
	tmp = ZeroMalloc(len * 3 + 2);
	if (len == 0)
	{
		// Empty character
		StrCpy(tmp, (len * 3 + 2), "$");
	}
	else
	{
		// Non null character
		wp = 0;
		for (i = 0;i < len;i++)
		{
			if (CfgCheckCharForName(str[i]))
			{
				tmp[wp++] = str[i];
			}
			else
			{
				tmp[wp++] = '$';
				Format(tmp2, sizeof(tmp2), "%02X", (UINT)str[i]);
				tmp[wp++] = tmp2[0];
				tmp[wp++] = tmp2[1];
			}
		}
	}
	ret = Malloc(StrLen(tmp) + 1);
	StrCpy(ret, 0, tmp);
	Free(tmp);
	return ret;
}

// Check if the character can be used in the name
bool CfgCheckCharForName(char c)
{
	if (c >= 0 && c <= 31)
	{
		return false;
	}
	if (c == ' ' || c == '\t')
	{
		return false;
	}
	if (c == '$')
	{
		return false;
	}
	return true;
}

// Get the string type value
bool CfgGetStr(FOLDER *f, char *name, char *str, UINT size)
{
	wchar_t *tmp;
	UINT tmp_size;
	// Validate arguments
	if (f == NULL || name == NULL || str == NULL)
	{
		return false;
	}

	str[0] = 0;

	// Get unicode string temporarily
	tmp_size = size * 4 + 10; // Just to make sure, a quantity of this amount is secured.
	tmp = Malloc(tmp_size);
	if (CfgGetUniStr(f, name, tmp, tmp_size) == false)
	{
		// Failure
		Free(tmp);
		return false;
	}

	// Copy to the ANSI string
	UniToStr(str, size, tmp);
	Free(tmp);

	return true;
}

// Get the value of the unicode_string type
bool CfgGetUniStr(FOLDER *f, char *name, wchar_t *str, UINT size)
{
	ITEM *t;
	// Validate arguments
	if (f == NULL || name == NULL || str == NULL)
	{
		return false;
	}

	str[0] = 0;

	t = CfgFindItem(f, name);
	if (t == NULL)
	{
		return false;
	}
	if (t->Type != ITEM_TYPE_STRING)
	{
		return false;
	}
	UniStrCpy(str, size, t->Buf);
	return true;
}

// Check for the existence of a folder
bool CfgIsFolder(FOLDER *f, char *name)
{
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return false;
	}

	return (CfgGetFolder(f, name) == NULL) ? false : true;
}

// Check for the existence of item
bool CfgIsItem(FOLDER *f, char *name)
{
	ITEM *t;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return false;
	}

	t = CfgFindItem(f, name);
	if (t == NULL)
	{
		return false;
	}

	return true;
}

// Get the byte[] type as a BUF
BUF *CfgGetBuf(FOLDER *f, char *name)
{
	ITEM *t;
	BUF *b;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return NULL;
	}

	t = CfgFindItem(f, name);
	if (t == NULL)
	{
		return NULL;
	}

	b = NewBuf();
	WriteBuf(b, t->Buf, t->size);
	SeekBuf(b, 0, 0);

	return b;
}

// Get the value of type byte[]
UINT CfgGetByte(FOLDER *f, char *name, void *buf, UINT size)
{
	ITEM *t;
	// Validate arguments
	if (f == NULL || name == NULL || buf == NULL)
	{
		return 0;
	}

	t = CfgFindItem(f, name);
	if (t == NULL)
	{
		return 0;
	}
	if (t->Type != ITEM_TYPE_BYTE)
	{
		return 0;
	}
	if (t->size <= size)
	{
		Copy(buf, t->Buf, t->size);
		return t->size;
	}
	else
	{
		Copy(buf, t->Buf, size);
		return t->size;
	}
}

// Get the value of type int64
UINT64 CfgGetInt64(FOLDER *f, char *name)
{
	ITEM *t;
	UINT64 *ret;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return 0;
	}

	t = CfgFindItem(f, name);
	if (t == NULL)
	{
		return 0;
	}
	if (t->Type != ITEM_TYPE_INT64)
	{
		return 0;
	}
	if (t->size != sizeof(UINT64))
	{
		return 0;
	}

	ret = (UINT64 *)t->Buf;
	return *ret;
}

// Get the value of the bool type
bool CfgGetBool(FOLDER *f, char *name)
{
	ITEM *t;
	bool *ret;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return 0;
	}

	t = CfgFindItem(f, name);
	if (t == NULL)
	{
		return 0;
	}
	if (t->Type != ITEM_TYPE_BOOL)
	{
		return 0;
	}
	if (t->size != sizeof(bool))
	{
		return 0;
	}

	ret = (bool *)t->Buf;
	if (*ret == false)
	{
		return false;
	}
	else
	{
		return true;
	}
}

// Get the value of the int type
UINT CfgGetInt(FOLDER *f, char *name)
{
	ITEM *t;
	UINT *ret;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return 0;
	}

	t = CfgFindItem(f, name);
	if (t == NULL)
	{
		return 0;
	}
	if (t->Type != ITEM_TYPE_INT)
	{
		return 0;
	}
	if (t->size != sizeof(UINT))
	{
		return 0;
	}

	ret = (UINT *)t->Buf;
	return *ret;
}

// Search for an item
ITEM *CfgFindItem(FOLDER *parent, char *name)
{
	ITEM *t, tt;
	// Validate arguments
	if (parent == NULL || name == NULL)
	{
		return NULL;
	}

	tt.Name = ZeroMalloc(StrLen(name) + 1);
	StrCpy(tt.Name, 0, name);
	t = Search(parent->Items, &tt);
	Free(tt.Name);

	return t;
}

// Get a folder
FOLDER *CfgGetFolder(FOLDER *parent, char *name)
{
	return CfgFindFolder(parent, name);
}

// Search a folder
FOLDER *CfgFindFolder(FOLDER *parent, char *name)
{
	FOLDER *f, ff;
	// Validate arguments
	if (parent == NULL || name == NULL)
	{
		return NULL;
	}

	ff.Name = ZeroMalloc(StrLen(name) + 1);
	StrCpy(ff.Name, 0, name);
	f = Search(parent->Folders, &ff);
	Free(ff.Name);

	return f;
}

// Adding a string type
ITEM *CfgAddStr(FOLDER *f, char *name, char *str)
{
	wchar_t *tmp;
	UINT tmp_size;
	ITEM *t;
	// Validate arguments
	if (f == NULL || name == NULL || str == NULL)
	{
		return NULL;
	}

	// Convert to a Unicode string
	tmp_size = CalcStrToUni(str);
	if (tmp_size == 0)
	{
		return NULL;
	}
	tmp = Malloc(tmp_size);
	StrToUni(tmp, tmp_size, str);
	t = CfgAddUniStr(f, name, tmp);
	Free(tmp);

	return t;
}

// Add unicode_string type
ITEM *CfgAddUniStr(FOLDER *f, char *name, wchar_t *str)
{
	// Validate arguments
	if (f == NULL || name == NULL || str == NULL)
	{
		return NULL;
	}

	return CfgCreateItem(f, name, ITEM_TYPE_STRING, str, UniStrSize(str));
}

// Add a binary
ITEM *CfgAddBuf(FOLDER *f, char *name, BUF *b)
{
	// Validate arguments
	if (f == NULL || name == NULL || b == NULL)
	{
		return NULL;
	}
	return CfgAddByte(f, name, b->Buf, b->Size);
}

// Add byte type
ITEM *CfgAddByte(FOLDER *f, char *name, void *buf, UINT size)
{
	// Validate arguments
	if (f == NULL || name == NULL || buf == NULL)
	{
		return NULL;
	}
	return CfgCreateItem(f, name, ITEM_TYPE_BYTE, buf, size);
}

// Add a 64-bit integer type
ITEM *CfgAddInt64(FOLDER *f, char *name, UINT64 i)
{
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return NULL;
	}
	return CfgCreateItem(f, name, ITEM_TYPE_INT64, &i, sizeof(UINT64));
}

// Get an IP address type
bool CfgGetIp(FOLDER *f, char *name, struct IP *ip)
{
	char tmp[MAX_SIZE];
	// Validate arguments
	if (f == NULL || name == NULL || ip == NULL)
	{
		return false;
	}

	Zero(ip, sizeof(IP));

	if (CfgGetStr(f, name, tmp, sizeof(tmp)) == false)
	{
		return false;
	}

	if (StrToIP(ip, tmp) == false)
	{
		return false;
	}

	return true;
}
UINT CfgGetIp32(FOLDER *f, char *name)
{
	IP p;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return 0;
	}

	if (CfgGetIp(f, name, &p) == false)
	{
		return 0;
	}

	return IPToUINT(&p);
}
bool CfgGetIp6Addr(FOLDER *f, char *name, IPV6_ADDR *addr)
{
	IP ip;
	// Validate arguments
	Zero(addr, sizeof(IPV6_ADDR));
	if (f == NULL || name == NULL || addr == NULL)
	{
		return false;
	}

	if (CfgGetIp(f, name, &ip) == false)
	{
		return false;
	}

	if (IsIP6(&ip) == false)
	{
		return false;
	}

	if (IPToIPv6Addr(addr, &ip) == false)
	{
		return false;
	}

	return true;
}

// Add an IP address type
ITEM *CfgAddIp(FOLDER *f, char *name, struct IP *ip)
{
	char tmp[MAX_SIZE];
	// Validate arguments
	if (f == NULL || name == NULL || ip == NULL)
	{
		return NULL;
	}

	IPToStr(tmp, sizeof(tmp), ip);

	return CfgAddStr(f, name, tmp);
}
ITEM *CfgAddIp32(FOLDER *f, char *name, UINT ip)
{
	IP p;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return NULL;
	}

	UINTToIP(&p, ip);

	return CfgAddIp(f, name, &p);
}
ITEM *CfgAddIp6Addr(FOLDER *f, char *name, IPV6_ADDR *addr)
{
	IP ip;
	// Validate arguments
	if (f == NULL || name == NULL || addr == NULL)
	{
		return NULL;
	}

	IPv6AddrToIP(&ip, addr);

	return CfgAddIp(f, name, &ip);
}

// Add an integer type
ITEM *CfgAddInt(FOLDER *f, char *name, UINT i)
{
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return NULL;
	}
	return CfgCreateItem(f, name, ITEM_TYPE_INT, &i, sizeof(UINT));
}

// Adding a bool type
ITEM *CfgAddBool(FOLDER *f, char *name, bool b)
{
	bool v;
	// Validate arguments
	if (f == NULL || name == NULL)
	{
		return NULL;
	}

	v = b ? 1 : 0;
	return CfgCreateItem(f, name, ITEM_TYPE_BOOL, &b, sizeof(bool));
}

// Comparison function of the item names
int CmpItemName(void *p1, void *p2)
{
	ITEM *f1, *f2;
	if (p1 == NULL || p2 == NULL)
	{
		return 0;
	}
	f1 = *(ITEM **)p1;
	f2 = *(ITEM **)p2;
	if (f1 == NULL || f2 == NULL)
	{
		return 0;
	}
	return StrCmpi(f1->Name, f2->Name);
}

// Comparison function of the folder names
int CmpFolderName(void *p1, void *p2)
{
	FOLDER *f1, *f2;
	if (p1 == NULL || p2 == NULL)
	{
		return 0;
	}
	f1 = *(FOLDER **)p1;
	f2 = *(FOLDER **)p2;
	if (f1 == NULL || f2 == NULL)
	{
		return 0;
	}
	return StrCmpi(f1->Name, f2->Name);
}

// Enumeration of items
void CfgEnumItem(FOLDER *f, ENUM_ITEM proc, void *param)
{
	UINT i;
	// Validate arguments
	if (f == NULL || proc == NULL)
	{
		return;
	}
	
	for (i = 0;i < LIST_NUM(f->Items);i++)
	{
		ITEM *tt = LIST_DATA(f->Items, i);
		if (proc(tt, param) == false)
		{
			break;
		}
	}
}

// Enumerate the folders and store it in the token list
TOKEN_LIST *CfgEnumFolderToTokenList(FOLDER *f)
{
	TOKEN_LIST *t, *ret;
	UINT i;
	// Validate arguments
	if (f == NULL)
	{
		return NULL;
	}

	t = ZeroMalloc(sizeof(TOKEN_LIST));
	t->NumTokens = LIST_NUM(f->Folders);
	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);

	for (i = 0;i < t->NumTokens;i++)
	{
		FOLDER *ff = LIST_DATA(f->Folders, i);
		t->Token[i] = CopyStr(ff->Name);
	}

	ret = UniqueToken(t);
	FreeToken(t);

	return ret;
}

// Enumerate items and store these to the token list
TOKEN_LIST *CfgEnumItemToTokenList(FOLDER *f)
{
	TOKEN_LIST *t, *ret;
	UINT i;
	// Validate arguments
	if (f == NULL)
	{
		return NULL;
	}

	t = ZeroMalloc(sizeof(TOKEN_LIST));
	t->NumTokens = LIST_NUM(f->Items);
	t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);

	for (i = 0;i < t->NumTokens;i++)
	{
		FOLDER *ff = LIST_DATA(f->Items, i);
		t->Token[i] = CopyStr(ff->Name);
	}

	ret = UniqueToken(t);
	FreeToken(t);

	return ret;
}

// Folder enumeration
void CfgEnumFolder(FOLDER *f, ENUM_FOLDER proc, void *param)
{
	UINT i;
	// Validate arguments
	if (f == NULL || proc == NULL)
	{
		return;
	}
	
	for (i = 0;i < LIST_NUM(f->Folders);i++)
	{
		FOLDER *ff = LIST_DATA(f->Folders, i);
		if (proc(ff, param) == false)
		{
			break;
		}

		if ((i % 100) == 99)
		{
			YieldCpu();
		}
	}
}

// Create an item
ITEM *CfgCreateItem(FOLDER *parent, char *name, UINT type, void *buf, UINT size)
{
	UINT name_size;
	ITEM *t;
#ifdef	CHECK_CFG_NAME_EXISTS
	ITEM tt;
#endif	// CHECK_CFG_NAME_EXISTS
	// Validate arguments
	if (parent == NULL || name == NULL || type == 0 || buf == NULL)
	{
		return NULL;
	}

	name_size = StrLen(name) + 1;

#ifdef	CHECK_CFG_NAME_EXISTS

	// Check whether there are any items with the same name already
	tt.Name = ZeroMalloc(name_size);
	StrCpy(tt.Name, 0, name);
	t = Search(parent->Items, &tt);
	Free(tt.Name);
	if (t != NULL)
	{
		// Duplicated
		return NULL;
	}

#endif	// CHECK_CFG_NAME_EXISTS

	t = ZeroMalloc(sizeof(ITEM));
	t->Buf = Malloc(size);
	Copy(t->Buf, buf, size);
	t->Name = ZeroMalloc(name_size);
	StrCpy(t->Name, 0, name);
	t->Type = type;
	t->size = size;
	t->Parent = parent;
	
	// Add to the parent list 
	Insert(parent->Items, t);

	return t;
}

// Delete the item
void CfgDeleteItem(ITEM *t)
{
	// Validate arguments
	if (t == NULL)
	{
		return;
	}

	// Remove from the parent list
	Delete(t->Parent->Items, t);

	// Memory release
	Free(t->Buf);
	Free(t->Name);
	Free(t);
}


// Delete the folder
void CfgDeleteFolder(FOLDER *f)
{
	FOLDER **ff;
	ITEM **tt;
	UINT num, i;
	// Validate arguments
	if (f == NULL)
	{
		return;
	}

	// Remove all subfolders
	num = LIST_NUM(f->Folders);
	ff = Malloc(sizeof(FOLDER *) * num);
	Copy(ff, f->Folders->p, sizeof(FOLDER *) * num);
	for (i = 0;i < num;i++)
	{
		CfgDeleteFolder(ff[i]);
	}
	Free(ff);

	// Remove all items
	num = LIST_NUM(f->Items);
	tt = Malloc(sizeof(ITEM *) * num);
	Copy(tt, f->Items->p, sizeof(ITEM *) * num);
	for (i = 0;i < num;i++)
	{
		CfgDeleteItem(tt[i]);
	}
	Free(tt);

	// Memory release
	Free(f->Name);
	// Remove from the parent list
	if (f->Parent != NULL)
	{
		Delete(f->Parent->Folders, f);
	}
	// Release the list
	ReleaseList(f->Folders);
	ReleaseList(f->Items);

	// Release of the memory of the body
	Free(f);
}

// Creating a root
FOLDER *CfgCreateRoot()
{
	return CfgCreateFolder(NULL, TAG_ROOT);
}

// Create a folder
FOLDER *CfgCreateFolder(FOLDER *parent, char *name)
{
	UINT size;
	FOLDER *f;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	size = StrLen(name) + 1;

#ifdef	CHECK_CFG_NAME_EXISTS

	// Check the name in the parent list
	if (parent != NULL)
	{
		FOLDER ff;
		ff.Name = ZeroMalloc(size);
		StrCpy(ff.Name, 0, name);
		f = Search(parent->Folders, &ff);
		Free(ff.Name);
		if (f != NULL)
		{
			// Folder with the same name already exists
			return NULL;
		}
	}

#endif	// CHECK_CFG_NAME_EXISTS

	f = ZeroMalloc(sizeof(FOLDER));
	f->Items = NewListFast(CmpItemName);
	f->Folders = NewListFast(CmpFolderName);
	f->Name = ZeroMalloc(size);
	StrCpy(f->Name, 0, name);
	f->Parent = parent;

	// Add to parentlist
	if (f->Parent != NULL)
	{
		Insert(f->Parent->Folders, f);
	}
	return f;
}



// Developed by SoftEther VPN Project at University of Tsukuba in Japan.
// Department of Computer Science has dozens of overly-enthusiastic geeks.
// Join us: http://www.tsukuba.ac.jp/english/admission/
