// SoftEther VPN Source Code - Stable Edition Repository // Mayaqua Kernel // // SoftEther VPN Server, Client and Bridge are free software under GPLv2. // // Copyright (c) Daiyuu Nobori. // Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan. // Copyright (c) SoftEther Corporation. // // All Rights Reserved. // // http://www.softether.org/ // // Author: Daiyuu Nobori, Ph.D. // 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 #define CFG_C #include #include #include #include #include #include #include #include // 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; } bool FileCopyExWithEofW(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, b->Size, 0); WriteBufChar(b, 0x1A); 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 // with appending the EOF FileCopyExWithEofW(name, tmp, true); // 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]; bool has_eof = false; // 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) { UINT size; // Read the temporary file o = FileOpenW(tmp, false); if (o != NULL) { // Check the EOF size = FileSize(o); if (size >= 2) { char c; if (FileSeek(o, FILE_BEGIN, size - 1) && FileRead(o, &c, 1) && c == 0x1A && FileSeek(o, FILE_BEGIN, 0)) { // EOF ok has_eof = true; } else { // No EOF: file is corrupted FileClose(o); o = NULL; } } } } 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); if (has_eof) { // Ignore EOF size -= 1; } 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; }