1 // SoftEther VPN Source Code - Stable Edition Repository
2 // Mayaqua Kernel
3 //
4 // SoftEther VPN Server, Client and Bridge are free software under the Apache License, Version 2.0.
5 //
6 // Copyright (c) Daiyuu Nobori.
7 // Copyright (c) SoftEther VPN Project, University of Tsukuba, Japan.
8 // Copyright (c) SoftEther Corporation.
9 // Copyright (c) all contributors on SoftEther VPN project in GitHub.
10 //
11 // All Rights Reserved.
12 //
13 // http://www.softether.org/
14 //
15 // This stable branch is officially managed by Daiyuu Nobori, the owner of SoftEther VPN Project.
16 // Pull requests should be sent to the Developer Edition Master Repository on https://github.com/SoftEtherVPN/SoftEtherVPN
17 //
18 // License: The Apache License, Version 2.0
19 // https://www.apache.org/licenses/LICENSE-2.0
20 //
21 // DISCLAIMER
22 // ==========
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 // SOFTWARE.
31 //
32 // THIS SOFTWARE IS DEVELOPED IN JAPAN, AND DISTRIBUTED FROM JAPAN, UNDER
33 // JAPANESE LAWS. YOU MUST AGREE IN ADVANCE TO USE, COPY, MODIFY, MERGE, PUBLISH,
34 // DISTRIBUTE, SUBLICENSE, AND/OR SELL COPIES OF THIS SOFTWARE, THAT ANY
35 // JURIDICAL DISPUTES WHICH ARE CONCERNED TO THIS SOFTWARE OR ITS CONTENTS,
36 // AGAINST US (SOFTETHER PROJECT, SOFTETHER CORPORATION, DAIYUU NOBORI OR OTHER
37 // SUPPLIERS), OR ANY JURIDICAL DISPUTES AGAINST US WHICH ARE CAUSED BY ANY KIND
38 // OF USING, COPYING, MODIFYING, MERGING, PUBLISHING, DISTRIBUTING, SUBLICENSING,
39 // AND/OR SELLING COPIES OF THIS SOFTWARE SHALL BE REGARDED AS BE CONSTRUED AND
40 // CONTROLLED BY JAPANESE LAWS, AND YOU MUST FURTHER CONSENT TO EXCLUSIVE
41 // JURISDICTION AND VENUE IN THE COURTS SITTING IN TOKYO, JAPAN. YOU MUST WAIVE
42 // ALL DEFENSES OF LACK OF PERSONAL JURISDICTION AND FORUM NON CONVENIENS.
43 // PROCESS MAY BE SERVED ON EITHER PARTY IN THE MANNER AUTHORIZED BY APPLICABLE
44 // LAW OR COURT RULE.
45 //
46 // USE ONLY IN JAPAN. DO NOT USE THIS SOFTWARE IN ANOTHER COUNTRY UNLESS YOU HAVE
47 // A CONFIRMATION THAT THIS SOFTWARE DOES NOT VIOLATE ANY CRIMINAL LAWS OR CIVIL
48 // RIGHTS IN THAT PARTICULAR COUNTRY. USING THIS SOFTWARE IN OTHER COUNTRIES IS
49 // COMPLETELY AT YOUR OWN RISK. THE SOFTETHER VPN PROJECT HAS DEVELOPED AND
50 // DISTRIBUTED THIS SOFTWARE TO COMPLY ONLY WITH THE JAPANESE LAWS AND EXISTING
51 // CIVIL RIGHTS INCLUDING PATENTS WHICH ARE SUBJECTS APPLY IN JAPAN. OTHER
52 // COUNTRIES' LAWS OR CIVIL RIGHTS ARE NONE OF OUR CONCERNS NOR RESPONSIBILITIES.
53 // WE HAVE NEVER INVESTIGATED ANY CRIMINAL REGULATIONS, CIVIL LAWS OR
54 // INTELLECTUAL PROPERTY RIGHTS INCLUDING PATENTS IN ANY OF OTHER 200+ COUNTRIES
55 // AND TERRITORIES. BY NATURE, THERE ARE 200+ REGIONS IN THE WORLD, WITH
56 // DIFFERENT LAWS. IT IS IMPOSSIBLE TO VERIFY EVERY COUNTRIES' LAWS, REGULATIONS
57 // AND CIVIL RIGHTS TO MAKE THE SOFTWARE COMPLY WITH ALL COUNTRIES' LAWS BY THE
58 // PROJECT. EVEN IF YOU WILL BE SUED BY A PRIVATE ENTITY OR BE DAMAGED BY A
59 // PUBLIC SERVANT IN YOUR COUNTRY, THE DEVELOPERS OF THIS SOFTWARE WILL NEVER BE
60 // LIABLE TO RECOVER OR COMPENSATE SUCH DAMAGES, CRIMINAL OR CIVIL
61 // RESPONSIBILITIES. NOTE THAT THIS LINE IS NOT LICENSE RESTRICTION BUT JUST A
62 // STATEMENT FOR WARNING AND DISCLAIMER.
63 //
64 // READ AND UNDERSTAND THE 'WARNING.TXT' FILE BEFORE USING THIS SOFTWARE.
65 // SOME SOFTWARE PROGRAMS FROM THIRD PARTIES ARE INCLUDED ON THIS SOFTWARE WITH
66 // LICENSE CONDITIONS WHICH ARE DESCRIBED ON THE 'THIRD_PARTY.TXT' FILE.
67 //
68 //
69 // SOURCE CODE CONTRIBUTION
70 // ------------------------
71 //
72 // Your contribution to SoftEther VPN Project is much appreciated.
73 // Please send patches to us through GitHub.
74 // Read the SoftEther VPN Patch Acceptance Policy in advance:
75 // http://www.softether.org/5-download/src/9.patch
76 //
77 //
78 // DEAR SECURITY EXPERTS
79 // ---------------------
80 //
81 // If you find a bug or a security vulnerability please kindly inform us
82 // about the problem immediately so that we can fix the security problem
83 // to protect a lot of users around the world as soon as possible.
84 //
85 // Our e-mail address for security reports is:
86 // softether-vpn-security [at] softether.org
87 //
88 // Please note that the above e-mail address is not a technical support
89 // inquiry address. If you need technical assistance, please visit
90 // http://www.softether.org/ and ask your question on the users forum.
91 //
92 // Thank you for your cooperation.
93 //
94 //
95 // NO MEMORY OR RESOURCE LEAKS
96 // ---------------------------
97 //
98 // The memory-leaks and resource-leaks verification under the stress
99 // test has been passed before release this source code.
100
101
102 // Table.c
103 // Read and management routines for string table
104
105 #include <GlobalConst.h>
106
107 #include <stdio.h>
108 #include <stdlib.h>
109 #include <string.h>
110 #include <wchar.h>
111 #include <stdarg.h>
112 #include <time.h>
113 #include <errno.h>
114 #include <Mayaqua/Mayaqua.h>
115
116 // List of TABLE
117 static LIST *TableList = NULL;
118 static wchar_t old_table_name[MAX_SIZE] = {0}; // Old table name
119 static LANGLIST current_lang = {0};
120 static LANGLIST current_os_lang = {0};
121
122 // Initialization of string table routine
InitTable()123 void InitTable()
124 {
125 LIST *o;
126 char tmp[MAX_SIZE];
127 LANGLIST *e = NULL;
128 LANGLIST *os_lang = NULL;
129 char table_name[MAX_SIZE];
130 if (MayaquaIsMinimalMode())
131 {
132 // Not to load in case of minimum mode
133 return;
134 }
135
136 o = LoadLangList();
137 if (o == NULL)
138 {
139 LABEL_FATAL_ERROR:
140 Alert("Fatal Error: The file \"hamcore.se2\" is missing or broken.\r\nPlease check hamcore.se2.\r\n\r\n(First, reboot the computer. If this problem occurs again, please reinstall VPN software files.)", NULL);
141 exit(-1);
142 return;
143 }
144
145 // Read the lang.config
146 if (LoadLangConfigCurrentDir(tmp, sizeof(tmp)))
147 {
148 e = GetBestLangByName(o, tmp);
149 }
150
151 os_lang = GetBestLangForCurrentEnvironment(o);
152
153 if (e == NULL)
154 {
155 e = os_lang;
156 }
157
158 if (e == NULL)
159 {
160 goto LABEL_FATAL_ERROR;
161 }
162
163 SaveLangConfigCurrentDir(e->Name);
164
165 Copy(¤t_lang, e, sizeof(LANGLIST));
166 Copy(¤t_os_lang, os_lang, sizeof(LANGLIST));
167
168 current_lang.LangList = current_lang.LcidList = NULL;
169 current_os_lang.LangList = current_os_lang.LcidList = NULL;
170
171 // Read the corresponding string table
172 Format(table_name, sizeof(table_name), "|strtable_%s.stb", current_lang.Name);
173 if (LoadTable(table_name) == false)
174 {
175 goto LABEL_FATAL_ERROR;
176 }
177
178 FreeLangList(o);
179 }
180
181 // Get the language of the current OS
GetCurrentOsLang(LANGLIST * e)182 void GetCurrentOsLang(LANGLIST *e)
183 {
184 // Validate arguments
185 if (e == NULL)
186 {
187 return;
188 }
189
190 Copy(e, ¤t_os_lang, sizeof(LANGLIST));
191 }
192
193 // Get the language ID of the current OS
GetCurrentOsLangId()194 UINT GetCurrentOsLangId()
195 {
196 LANGLIST e;
197
198 Zero(&e, sizeof(e));
199
200 GetCurrentOsLang(&e);
201
202 return e.Id;
203 }
204
205 // Get the current language
GetCurrentLang(LANGLIST * e)206 void GetCurrentLang(LANGLIST *e)
207 {
208 // Validate arguments
209 if (e == NULL)
210 {
211 return;
212 }
213
214 Copy(e, ¤t_lang, sizeof(LANGLIST));
215 }
216
217 // Get the current language ID
GetCurrentLangId()218 UINT GetCurrentLangId()
219 {
220 LANGLIST e;
221
222 Zero(&e, sizeof(e));
223
224 GetCurrentLang(&e);
225
226 return e.Id;
227 }
228
229 // Write to the lang.config file in the current directory
SaveLangConfigCurrentDir(char * str)230 bool SaveLangConfigCurrentDir(char *str)
231 {
232 // Validate arguments
233 if (str == NULL)
234 {
235 return false;
236 }
237
238 return SaveLangConfig(LANG_CONFIG_FILENAME, str);
239 }
240
241 // Write to the lang.config file
SaveLangConfig(wchar_t * filename,char * str)242 bool SaveLangConfig(wchar_t *filename, char *str)
243 {
244 BUF *b;
245 LIST *o;
246 UINT i;
247 bool ret;
248 // Validate arguments
249 if (filename == NULL)
250 {
251 return false;
252 }
253
254 // Read the template
255 b = ReadDump(LANG_CONFIG_TEMPLETE);
256 if (b == NULL)
257 {
258 return false;
259 }
260
261 SeekBuf(b, b->Size, 0);
262
263 o = LoadLangList();
264 if (o != NULL)
265 {
266 wchar_t tmp[MAX_SIZE];
267
268 AppendBufStr(b, "# Available Language IDs are:\r\n");
269
270 for (i = 0;i < LIST_NUM(o);i++)
271 {
272 LANGLIST *e = LIST_DATA(o, i);
273
274 UniFormat(tmp, sizeof(tmp), L"# %S: %s (%s)\r\n",
275 e->Name, e->TitleEnglish, e->TitleLocal);
276
277 AppendBufUtf8(b, tmp);
278 }
279
280 AppendBufStr(b, "\r\n\r\n# Specify a Language ID here.\r\n");
281 AppendBufStr(b, str);
282 AppendBufStr(b, "\r\n\r\n");
283
284 FreeLangList(o);
285 }
286
287 ret = DumpBufWIfNecessary(b, filename);
288
289 FreeBuf(b);
290
291 return ret;
292 }
293
294 // Read the lang.config file in the current directory
LoadLangConfigCurrentDir(char * str,UINT str_size)295 bool LoadLangConfigCurrentDir(char *str, UINT str_size)
296 {
297 // Validate arguments
298 if (str == NULL)
299 {
300 return false;
301 }
302
303 return LoadLangConfig(LANG_CONFIG_FILENAME, str, str_size);
304 }
305
306 // Read the lang.config file
LoadLangConfig(wchar_t * filename,char * str,UINT str_size)307 bool LoadLangConfig(wchar_t *filename, char *str, UINT str_size)
308 {
309 BUF *b;
310 bool ret = false;
311 // Validate arguments
312 if (filename == NULL || str == NULL)
313 {
314 return false;
315 }
316
317 b = ReadDumpW(filename);
318 if (b == NULL)
319 {
320 return false;
321 }
322
323 while (true)
324 {
325 char *line = CfgReadNextLine(b);
326
327 if (line == NULL)
328 {
329 break;
330 }
331
332 Trim(line);
333
334 if (IsEmptyStr(line) == false)
335 {
336 if (StartWith(line, "#") == false && StartWith(line, "//") == false && StartWith(line, ";") == false &&
337 InStr(line, "#") == false)
338 {
339 StrCpy(str, str_size, line);
340 ret = true;
341 }
342 }
343
344 Free(line);
345 }
346
347 FreeBuf(b);
348
349 return ret;
350 }
351
352 // Choose the language from the ID
GetLangById(LIST * o,UINT id)353 LANGLIST *GetLangById(LIST *o, UINT id)
354 {
355 UINT i;
356 // Validate arguments
357 if (o == NULL)
358 {
359 return NULL;
360 }
361
362 for (i = 0;i < LIST_NUM(o);i++)
363 {
364 LANGLIST *e = LIST_DATA(o, i);
365
366 if (e->Id == id)
367 {
368 return e;
369 }
370 }
371
372 return NULL;
373 }
374
375 // Choice the best language for the current environment
GetBestLangForCurrentEnvironment(LIST * o)376 LANGLIST *GetBestLangForCurrentEnvironment(LIST *o)
377 {
378 LANGLIST *ret = NULL;
379 // Validate arguments
380 if (o == NULL)
381 {
382 return NULL;
383 }
384
385 #ifdef OS_WIN32
386 ret = GetBestLangByLcid(o, MsGetUserLocaleId());
387 #else // OS_WIN32
388 if (true)
389 {
390 char lang[MAX_SIZE];
391
392 if (GetEnv("LANG", lang, sizeof(lang)))
393 {
394 ret = GetBestLangByLangStr(o, lang);
395 }
396 else
397 {
398 ret = GetBestLangByLangStr(o, "C");
399 }
400 }
401 #endif // OS_WIN32
402
403 return ret;
404 }
405
406 // Search for the best language from LANG string of UNIX
GetBestLangByLangStr(LIST * o,char * str)407 LANGLIST *GetBestLangByLangStr(LIST *o, char *str)
408 {
409 UINT i;
410 LANGLIST *ret;
411 // Validate arguments
412 if (o == NULL)
413 {
414 return NULL;
415 }
416
417 for (i = 0;i < LIST_NUM(o);i++)
418 {
419 LANGLIST *e = LIST_DATA(o, i);
420 UINT j;
421
422 for (j = 0;j < LIST_NUM(e->LangList);j++)
423 {
424 char *v = LIST_DATA(e->LangList, j);
425
426 if (StrCmpi(v, str) == 0)
427 {
428 return e;
429 }
430 }
431 }
432
433 for (i = 0;i < LIST_NUM(o);i++)
434 {
435 LANGLIST *e = LIST_DATA(o, i);
436 UINT j;
437
438 for (j = 0;j < LIST_NUM(e->LangList);j++)
439 {
440 char *v = LIST_DATA(e->LangList, j);
441
442 if (StartWith(str, v) || StartWith(v, str))
443 {
444 return e;
445 }
446 }
447 }
448
449 ret = GetBestLangByName(o, "en");
450
451 return ret;
452 }
453
454 // Search for the best language from LCID
GetBestLangByLcid(LIST * o,UINT lcid)455 LANGLIST *GetBestLangByLcid(LIST *o, UINT lcid)
456 {
457 LANGLIST *ret;
458 UINT i;
459 // Validate arguments
460 if (o == NULL)
461 {
462 return NULL;
463 }
464
465 for (i = 0;i < LIST_NUM(o);i++)
466 {
467 LANGLIST *e = LIST_DATA(o, i);
468
469 if (IsIntInList(e->LcidList, lcid))
470 {
471 return e;
472 }
473 }
474
475 ret = GetBestLangByName(o, "en");
476
477 return ret;
478 }
479
480 // Search for the best language from the name
GetBestLangByName(LIST * o,char * name)481 LANGLIST *GetBestLangByName(LIST *o, char *name)
482 {
483 UINT i;
484 LANGLIST *ret = NULL;
485 // Validate arguments
486 if (o == NULL)
487 {
488 return NULL;
489 }
490
491 for (i = 0;i < LIST_NUM(o);i++)
492 {
493 LANGLIST *e = LIST_DATA(o, i);
494
495 if (StrCmpi(e->Name, name) == 0)
496 {
497 ret = e;
498 break;
499 }
500 }
501
502 if (ret != NULL)
503 {
504 return ret;
505 }
506
507 for (i = 0;i < LIST_NUM(o);i++)
508 {
509 LANGLIST *e = LIST_DATA(o, i);
510
511 if (StartWith(e->Name, name) || StartWith(name, e->Name))
512 {
513 ret = e;
514 break;
515 }
516 }
517
518 if (ret != NULL)
519 {
520 return ret;
521 }
522
523 return ret;
524 }
525
526 // Release the language list
FreeLangList(LIST * o)527 void FreeLangList(LIST *o)
528 {
529 UINT i;
530 // Validate arguments
531 if (o == NULL)
532 {
533 return;
534 }
535
536 for (i = 0;i < LIST_NUM(o);i++)
537 {
538 LANGLIST *e = LIST_DATA(o, i);
539
540 FreeStrList(e->LangList);
541 ReleaseIntList(e->LcidList);
542
543 Free(e);
544 }
545
546 ReleaseList(o);
547 }
548
549 // Read the language list
LoadLangList()550 LIST *LoadLangList()
551 {
552 LIST *o = NewListFast(NULL);
553 char *filename = LANGLIST_FILENAME;
554 BUF *b;
555
556 #ifdef OS_WIN32
557 if (MsIsWine())
558 {
559 filename = LANGLIST_FILENAME_WINE;
560 }
561 #endif // OS_WIN32
562
563 b = ReadDump(filename);
564 if (b == NULL)
565 {
566 return NULL;
567 }
568
569 while (true)
570 {
571 char *line = CfgReadNextLine(b);
572
573 if (line == NULL)
574 {
575 break;
576 }
577
578 Trim(line);
579
580 if (IsEmptyStr(line) == false && StartWith(line, "#") == false)
581 {
582 TOKEN_LIST *t = ParseToken(line, "\t ");
583 if (t != NULL)
584 {
585 if (t->NumTokens == 6)
586 {
587 LANGLIST *e = ZeroMalloc(sizeof(LANGLIST));
588 TOKEN_LIST *t2;
589
590 e->Id = ToInt(t->Token[0]);
591 StrCpy(e->Name, sizeof(e->Name), t->Token[1]);
592 Utf8ToUni(e->TitleEnglish, sizeof(e->TitleEnglish), t->Token[2], StrLen(t->Token[2]));
593 Utf8ToUni(e->TitleLocal, sizeof(e->TitleLocal), t->Token[3], StrLen(t->Token[3]));
594
595 UniReplaceStrEx(e->TitleEnglish, sizeof(e->TitleEnglish), e->TitleEnglish,
596 L"_", L" ", true);
597
598 UniReplaceStrEx(e->TitleLocal, sizeof(e->TitleLocal), e->TitleLocal,
599 L"_", L" ", true);
600
601 e->LcidList = NewIntList(false);
602
603 t2 = ParseToken(t->Token[4], ",");
604 if (t2 != NULL)
605 {
606 UINT i;
607
608 for (i = 0;i < t2->NumTokens;i++)
609 {
610 UINT id = ToInt(t2->Token[i]);
611
612 AddIntDistinct(e->LcidList, id);
613 }
614
615 FreeToken(t2);
616 }
617
618 e->LangList = NewListFast(NULL);
619
620 t2 = ParseToken(t->Token[5], ",");
621 if (t2 != NULL)
622 {
623 UINT i;
624
625 for (i = 0;i < t2->NumTokens;i++)
626 {
627 Add(e->LangList, CopyStr(t2->Token[i]));
628 }
629
630 FreeToken(t2);
631 }
632
633 Add(o, e);
634 }
635
636 FreeToken(t);
637 }
638 }
639
640 Free(line);
641 }
642
643 FreeBuf(b);
644
645 return o;
646 }
647
648 // Get an error string in Unicode
GetUniErrorStr(UINT err)649 wchar_t *GetUniErrorStr(UINT err)
650 {
651 wchar_t *ret;
652 char name[MAX_SIZE];
653 Format(name, sizeof(name), "ERR_%u", err);
654
655 ret = GetTableUniStr(name);
656 if (UniStrLen(ret) != 0)
657 {
658 return ret;
659 }
660 else
661 {
662 return _UU("ERR_UNKNOWN");
663 }
664 }
665
666 // Get an error string
GetErrorStr(UINT err)667 char *GetErrorStr(UINT err)
668 {
669 char *ret;
670 char name[MAX_SIZE];
671 Format(name, sizeof(name), "ERR_%u", err);
672
673 ret = GetTableStr(name);
674 if (StrLen(ret) != 0)
675 {
676 return ret;
677 }
678 else
679 {
680 return _SS("ERR_UNKNOWN");
681 }
682 }
683
684 // Load the integer value from the table
GetTableInt(char * name)685 UINT GetTableInt(char *name)
686 {
687 char *str;
688 // Validate arguments
689 if (name == NULL)
690 {
691 return 0;
692 }
693
694 str = GetTableStr(name);
695 return ToInt(str);
696 }
697
698 // Load a Unicode string from the table
GetTableUniStr(char * name)699 wchar_t *GetTableUniStr(char *name)
700 {
701 TABLE *t;
702 // Validate arguments
703 if (name == NULL)
704 {
705 // Debug("%s: ************\n", name);
706 return L"";
707 }
708
709 // Search
710 t = FindTable(name);
711 if (t == NULL)
712 {
713 //Debug("%s: UNICODE STRING NOT FOUND\n", name);
714 return L"";
715 }
716
717 return t->unistr;
718 }
719
720 // Load the string from the table
GetTableStr(char * name)721 char *GetTableStr(char *name)
722 {
723 TABLE *t;
724 // Validate arguments
725 if (name == NULL)
726 {
727 return "";
728 }
729
730 #ifdef OS_WIN32
731 if (StrCmpi(name, "DEFAULT_FONT") == 0)
732 {
733 if (_II("LANG") == 2)
734 {
735 UINT os_type = GetOsType();
736 if (OS_IS_WINDOWS_9X(os_type) ||
737 GET_KETA(os_type, 100) <= 4)
738 {
739 // Use the SimSun font in Windows 9x, Windows NT 4.0, Windows 2000, Windows XP, and Windows Server 2003
740 return "SimSun";
741 }
742 }
743 }
744 #endif // OS_WIN32
745
746 // Search
747 t = FindTable(name);
748 if (t == NULL)
749 {
750 //Debug("%s: ANSI STRING NOT FOUND\n", name);
751 return "";
752 }
753
754 return t->str;
755 }
756
757 // Get the string name that begins with the specified name
GetTableNameStartWith(char * str)758 TOKEN_LIST *GetTableNameStartWith(char *str)
759 {
760 UINT i;
761 UINT len;
762 LIST *o;
763 TOKEN_LIST *t;
764 char tmp[MAX_SIZE];
765 // Validate arguments
766 if (str == NULL)
767 {
768 return NullToken();
769 }
770
771 StrCpy(tmp, sizeof(tmp), str);
772 StrUpper(tmp);
773
774 len = StrLen(tmp);
775
776 o = NewListFast(NULL);
777
778 for (i = 0;i < LIST_NUM(TableList);i++)
779 {
780 TABLE *t = LIST_DATA(TableList, i);
781 UINT len2 = StrLen(t->name);
782
783 if (len2 >= len)
784 {
785 if (Cmp(t->name, tmp, len) == 0)
786 {
787 Insert(o, CopyStr(t->name));
788 }
789 }
790 }
791
792 t = ZeroMalloc(sizeof(TOKEN_LIST));
793 t->NumTokens = LIST_NUM(o);
794 t->Token = ZeroMalloc(sizeof(char *) * t->NumTokens);
795
796 for (i = 0;i < t->NumTokens;i++)
797 {
798 t->Token[i] = LIST_DATA(o, i);
799 }
800
801 ReleaseList(o);
802
803 return t;
804 }
805
806 // Search the table
FindTable(char * name)807 TABLE *FindTable(char *name)
808 {
809 TABLE *t, tt;
810 // Validate arguments
811 if (name == NULL || TableList == NULL)
812 {
813 return NULL;
814 }
815
816 tt.name = CopyStr(name);
817 t = Search(TableList, &tt);
818 Free(tt.name);
819
820 return t;
821 }
822
823 // A function that compares the table name
CmpTableName(void * p1,void * p2)824 int CmpTableName(void *p1, void *p2)
825 {
826 TABLE *t1, *t2;
827 if (p1 == NULL || p2 == NULL)
828 {
829 return 0;
830 }
831 t1 = *(TABLE **)p1;
832 t2 = *(TABLE **)p2;
833 if (t1 == NULL || t2 == NULL)
834 {
835 return 0;
836 }
837
838 return StrCmpi(t1->name, t2->name);
839 }
840
841 // Interpret a line
ParseTableLine(char * line,char * prefix,UINT prefix_size,LIST * replace_list)842 TABLE *ParseTableLine(char *line, char *prefix, UINT prefix_size, LIST *replace_list)
843 {
844 UINT i, len;
845 UINT len_name;
846 UINT string_start;
847 char *name;
848 char *name2;
849 UINT name2_size;
850 wchar_t *unistr;
851 char *str;
852 UINT unistr_size, str_size;
853 TABLE *t;
854 // Validate arguments
855 if (line == NULL || prefix == NULL)
856 {
857 return NULL;
858 }
859 TrimLeft(line);
860
861 // No line
862 len = StrLen(line);
863 if (len == 0)
864 {
865 return NULL;
866 }
867
868 // Comment
869 if (line[0] == '#' || (line[0] == '/' && line[1] == '/'))
870 {
871 return NULL;
872 }
873
874 // Search to the end position of the name
875 len_name = 0;
876 for (i = 0;;i++)
877 {
878 if (line[i] == 0)
879 {
880 // There is only one token
881 return NULL;
882 }
883 if (line[i] == ' ' || line[i] == '\t')
884 {
885 break;
886 }
887 len_name++;
888 }
889
890 name = Malloc(len_name + 1);
891 StrCpy(name, len_name + 1, line);
892
893 string_start = len_name;
894 for (i = len_name;i < len;i++)
895 {
896 if (line[i] != ' ' && line[i] != '\t')
897 {
898 break;
899 }
900 string_start++;
901 }
902 if (i == len)
903 {
904 Free(name);
905 return NULL;
906 }
907
908 // Unescape
909 UnescapeStr(&line[string_start]);
910
911 // Convert to Unicode
912 unistr_size = CalcUtf8ToUni(&line[string_start], StrLen(&line[string_start]));
913 if (unistr_size == 0)
914 {
915 Free(name);
916 return NULL;
917 }
918 unistr = Malloc(unistr_size);
919 Utf8ToUni(unistr, unistr_size, &line[string_start], StrLen(&line[string_start]));
920
921 if (UniInChar(unistr, L'$'))
922 {
923 // Replace the replacement string
924 wchar_t *tmp;
925 UINT tmp_size = (UniStrSize(unistr) + 1024) * 2;
926 UINT i;
927
928 tmp = Malloc(tmp_size);
929
930 UniStrCpy(tmp, tmp_size, unistr);
931
932 for (i = 0; i < LIST_NUM(replace_list);i++)
933 {
934 TABLE *r = LIST_DATA(replace_list, i);
935
936 UniReplaceStrEx(tmp, tmp_size, tmp, (wchar_t *)r->name, r->unistr, false);
937 }
938
939 Free(unistr);
940
941 unistr = CopyUniStr(tmp);
942
943 Free(tmp);
944 }
945
946 // Convert to ANSI
947 str_size = CalcUniToStr(unistr);
948 if (str_size == 0)
949 {
950 str_size = 1;
951 str = Malloc(1);
952 str[0] = 0;
953 }
954 else
955 {
956 str = Malloc(str_size);
957 UniToStr(str, str_size, unistr);
958 }
959
960 if (StrCmpi(name, "PREFIX") == 0)
961 {
962 // Prefix is specified
963 StrCpy(prefix, prefix_size, str);
964 Trim(prefix);
965
966 if (StrCmpi(prefix, "$") == 0 || StrCmpi(prefix, "NULL") == 0)
967 {
968 prefix[0] = 0;
969 }
970
971 Free(name);
972 Free(str);
973 Free(unistr);
974
975 return NULL;
976 }
977
978 name2_size = StrLen(name) + StrLen(prefix) + 2;
979 name2 = ZeroMalloc(name2_size);
980
981 if (prefix[0] != 0)
982 {
983 StrCat(name2, name2_size, prefix);
984 StrCat(name2, name2_size, "@");
985 }
986
987 StrCat(name2, name2_size, name);
988
989 Free(name);
990
991 // Create a TABLE
992 t = Malloc(sizeof(TABLE));
993 StrUpper(name2);
994 t->name = name2;
995 t->str = str;
996 t->unistr = unistr;
997
998 return t;
999 }
1000
1001 // Unescape the string
UnescapeStr(char * src)1002 void UnescapeStr(char *src)
1003 {
1004 UINT i, len, wp;
1005 char *tmp;
1006 // Validate arguments
1007 if (src == NULL)
1008 {
1009 return;
1010 }
1011
1012 len = StrLen(src);
1013 tmp = Malloc(len + 1);
1014 wp = 0;
1015 for (i = 0;i < len;i++)
1016 {
1017 if (src[i] == '\\')
1018 {
1019 i++;
1020 switch (src[i])
1021 {
1022 case 0:
1023 goto FINISH;
1024 case '\\':
1025 tmp[wp++] = '\\';
1026 break;
1027 case ' ':
1028 tmp[wp++] = ' ';
1029 break;
1030 case 'n':
1031 case 'N':
1032 tmp[wp++] = '\n';
1033 break;
1034 case 'r':
1035 case 'R':
1036 tmp[wp++] = '\r';
1037 break;
1038 case 't':
1039 case 'T':
1040 tmp[wp++] = '\t';
1041 break;
1042 }
1043 }
1044 else
1045 {
1046 tmp[wp++] = src[i];
1047 }
1048 }
1049 FINISH:
1050 tmp[wp++] = 0;
1051 StrCpy(src, 0, tmp);
1052 Free(tmp);
1053 }
1054
1055 // Release the table
FreeTable()1056 void FreeTable()
1057 {
1058 UINT i, num;
1059 TABLE **tables;
1060 if (TableList == NULL)
1061 {
1062 return;
1063 }
1064
1065 TrackingDisable();
1066
1067 num = LIST_NUM(TableList);
1068 tables = ToArray(TableList);
1069 for (i = 0;i < num;i++)
1070 {
1071 TABLE *t = tables[i];
1072 Free(t->name);
1073 Free(t->str);
1074 Free(t->unistr);
1075 Free(t);
1076 }
1077 ReleaseList(TableList);
1078 TableList = NULL;
1079 Free(tables);
1080
1081 Zero(old_table_name, sizeof(old_table_name));
1082
1083 TrackingEnable();
1084 }
1085
1086 // Read a string table from the buffer
LoadTableFromBuf(BUF * b)1087 bool LoadTableFromBuf(BUF *b)
1088 {
1089 char *tmp;
1090 char prefix[MAX_SIZE];
1091 LIST *replace_list = NULL;
1092 UINT i;
1093 // Validate arguments
1094 if (b == NULL)
1095 {
1096 return false;
1097 }
1098
1099 // If the table already exists, delete it
1100 FreeTable();
1101
1102 // Create a list
1103 TableList = NewList(CmpTableName);
1104
1105 Zero(prefix, sizeof(prefix));
1106
1107 replace_list = NewListFast(NULL);
1108
1109 // Read the contents of the buffer line by line
1110 while (true)
1111 {
1112 TABLE *t;
1113 bool ok = true;
1114
1115 tmp = CfgReadNextLine(b);
1116 if (tmp == NULL)
1117 {
1118 break;
1119 }
1120
1121 if (tmp[0] == '$')
1122 {
1123 char key[128];
1124 char value[MAX_SIZE];
1125 if (GetKeyAndValue(tmp, key, sizeof(key), value, sizeof(value), " \t"))
1126 {
1127 if (StartWith(key, "$") && EndWith(key, "$") && StrLen(key) >= 3)
1128 {
1129 TABLE *t;
1130 wchar_t univalue[MAX_SIZE];
1131 wchar_t uniname[MAX_SIZE];
1132
1133 t = ZeroMalloc(sizeof(TABLE));
1134
1135 Zero(univalue, sizeof(univalue));
1136 Utf8ToUni(univalue, sizeof(univalue), value, StrLen(value));
1137
1138 StrToUni(uniname, sizeof(uniname), key);
1139
1140 t->name = (char *)CopyUniStr(uniname);
1141 t->unistr = CopyUniStr(univalue);
1142
1143 Add(replace_list, t);
1144
1145 // Found a replacement definition
1146 ok = false;
1147 }
1148 }
1149 }
1150
1151 if (ok)
1152 {
1153 t = ParseTableLine(tmp, prefix, sizeof(prefix), replace_list);
1154 if (t != NULL)
1155 {
1156 // Register
1157 Insert(TableList, t);
1158 }
1159 }
1160
1161 Free(tmp);
1162 }
1163
1164 for (i = 0;i < LIST_NUM(replace_list);i++)
1165 {
1166 TABLE *t = LIST_DATA(replace_list, i);
1167
1168 Free(t->name);
1169 Free(t->str);
1170 Free(t->unistr);
1171
1172 Free(t);
1173 }
1174
1175 ReleaseList(replace_list);
1176
1177 return true;
1178 }
1179
1180 // Generate the Unicode string cache file name
GenerateUnicodeCacheFileName(wchar_t * name,UINT size,wchar_t * strfilename,UINT strfilesize,UCHAR * filehash)1181 void GenerateUnicodeCacheFileName(wchar_t *name, UINT size, wchar_t *strfilename, UINT strfilesize, UCHAR *filehash)
1182 {
1183 wchar_t tmp[MAX_SIZE];
1184 wchar_t hashstr[64];
1185 wchar_t hashtemp[MAX_SIZE];
1186 wchar_t exe[MAX_SIZE];
1187 UCHAR hash[SHA1_SIZE];
1188 // Validate arguments
1189 if (name == NULL || strfilename == NULL || filehash == NULL)
1190 {
1191 return;
1192 }
1193
1194 GetExeDirW(exe, sizeof(exe));
1195 UniStrCpy(hashtemp, sizeof(hashtemp), strfilename);
1196 BinToStrW(tmp, sizeof(tmp), filehash, MD5_SIZE);
1197 UniStrCat(hashtemp, sizeof(hashtemp), tmp);
1198 UniStrCat(hashtemp, sizeof(hashtemp), exe);
1199 UniStrLower(hashtemp);
1200
1201 Hash(hash, hashtemp, UniStrLen(hashtemp) * sizeof(wchar_t), true);
1202 BinToStrW(hashstr, sizeof(hashstr), hash, 4);
1203 UniFormat(tmp, sizeof(tmp), UNICODE_CACHE_FILE, hashstr);
1204 UniStrLower(tmp);
1205
1206 #ifndef OS_WIN32
1207 UniStrCpy(exe, sizeof(exe), L"/tmp");
1208 #else // OS_WIN32
1209 StrToUni(exe, sizeof(exe), MsGetTempDir());
1210 #endif // OS_WIN32
1211
1212 UniFormat(name, size, L"%s/%s", exe, tmp);
1213 NormalizePathW(name, size, name);
1214 }
1215
1216 // Save the Unicode cache
SaveUnicodeCache(wchar_t * strfilename,UINT strfilesize,UCHAR * hash)1217 void SaveUnicodeCache(wchar_t *strfilename, UINT strfilesize, UCHAR *hash)
1218 {
1219 UNICODE_CACHE c;
1220 BUF *b;
1221 UINT i;
1222 IO *io;
1223 wchar_t name[MAX_PATH];
1224 UCHAR binhash[MD5_SIZE];
1225 // Validate arguments
1226 if (strfilename == NULL || hash == NULL)
1227 {
1228 return;
1229 }
1230
1231 Zero(&c, sizeof(c));
1232 UniToStr(c.StrFileName, sizeof(c.StrFileName), strfilename);
1233 c.StrFileSize = strfilesize;
1234 GetMachineName(c.MachineName, sizeof(c.MachineName));
1235 c.OsType = GetOsInfo()->OsType;
1236 Copy(c.hash, hash, MD5_SIZE);
1237
1238 #ifdef OS_UNIX
1239 GetCurrentCharSet(c.CharSet, sizeof(c.CharSet));
1240 #else // OS_UNIX
1241 {
1242 UINT id = MsGetThreadLocale();
1243 Copy(c.CharSet, &id, sizeof(id));
1244 }
1245 #endif // OS_UNIX
1246
1247 b = NewBuf();
1248 WriteBuf(b, &c, sizeof(c));
1249
1250 WriteBufInt(b, LIST_NUM(TableList));
1251 for (i = 0;i < LIST_NUM(TableList);i++)
1252 {
1253 TABLE *t = LIST_DATA(TableList, i);
1254 WriteBufInt(b, StrLen(t->name));
1255 WriteBuf(b, t->name, StrLen(t->name));
1256 WriteBufInt(b, StrLen(t->str));
1257 WriteBuf(b, t->str, StrLen(t->str));
1258 WriteBufInt(b, UniStrLen(t->unistr));
1259 WriteBuf(b, t->unistr, UniStrLen(t->unistr) * sizeof(wchar_t));
1260 }
1261
1262 Hash(binhash, b->Buf, b->Size, false);
1263 WriteBuf(b, binhash, MD5_SIZE);
1264
1265 GenerateUnicodeCacheFileName(name, sizeof(name), strfilename, strfilesize, hash);
1266
1267 io = FileCreateW(name);
1268 if (io != NULL)
1269 {
1270 SeekBuf(b, 0, 0);
1271 BufToFile(io, b);
1272 FileClose(io);
1273 }
1274
1275 FreeBuf(b);
1276 }
1277
1278 // Reading the Unicode cache
LoadUnicodeCache(wchar_t * strfilename,UINT strfilesize,UCHAR * hash)1279 bool LoadUnicodeCache(wchar_t *strfilename, UINT strfilesize, UCHAR *hash)
1280 {
1281 UNICODE_CACHE c, t;
1282 BUF *b;
1283 UINT i, num;
1284 IO *io;
1285 wchar_t name[MAX_PATH];
1286 UCHAR binhash[MD5_SIZE];
1287 UCHAR binhash_2[MD5_SIZE];
1288 // Validate arguments
1289 if (strfilename == NULL || hash == NULL)
1290 {
1291 return false;
1292 }
1293
1294 GenerateUnicodeCacheFileName(name, sizeof(name), strfilename, strfilesize, hash);
1295
1296 io = FileOpenW(name, false);
1297 if (io == NULL)
1298 {
1299 return false;
1300 }
1301
1302 b = FileToBuf(io);
1303 if (b == NULL)
1304 {
1305 FileClose(io);
1306 return false;
1307 }
1308
1309 SeekBuf(b, 0, 0);
1310 FileClose(io);
1311
1312 Hash(binhash, b->Buf, b->Size >= MD5_SIZE ? (b->Size - MD5_SIZE) : 0, false);
1313 Copy(binhash_2, ((UCHAR *)b->Buf) + (b->Size >= MD5_SIZE ? (b->Size - MD5_SIZE) : 0), MD5_SIZE);
1314 if (Cmp(binhash, binhash_2, MD5_SIZE) != 0)
1315 {
1316 FreeBuf(b);
1317 return false;
1318 }
1319
1320 Zero(&c, sizeof(c));
1321 UniToStr(c.StrFileName, sizeof(c.StrFileName), strfilename);
1322 c.StrFileSize = strfilesize;
1323 DisableNetworkNameCache();
1324 GetMachineName(c.MachineName, sizeof(c.MachineName));
1325 EnableNetworkNameCache();
1326 c.OsType = GetOsInfo()->OsType;
1327 Copy(c.hash, hash, MD5_SIZE);
1328
1329 #ifdef OS_UNIX
1330 GetCurrentCharSet(c.CharSet, sizeof(c.CharSet));
1331 #else // OS_UNIX
1332 {
1333 UINT id = MsGetThreadLocale();
1334 Copy(c.CharSet, &id, sizeof(id));
1335 }
1336 #endif // OS_UNIX
1337
1338 Zero(&t, sizeof(t));
1339 ReadBuf(b, &t, sizeof(t));
1340
1341 if (Cmp(&c, &t, sizeof(UNICODE_CACHE)) != 0)
1342 {
1343 FreeBuf(b);
1344 return false;
1345 }
1346
1347 num = ReadBufInt(b);
1348
1349 FreeTable();
1350 TableList = NewList(CmpTableName);
1351
1352 for (i = 0;i < num;i++)
1353 {
1354 UINT len;
1355 TABLE *t = ZeroMalloc(sizeof(TABLE));
1356
1357 len = ReadBufInt(b);
1358 t->name = ZeroMalloc(len + 1);
1359 ReadBuf(b, t->name, len);
1360
1361 len = ReadBufInt(b);
1362 t->str = ZeroMalloc(len + 1);
1363 ReadBuf(b, t->str, len);
1364
1365 len = ReadBufInt(b);
1366 t->unistr = ZeroMalloc((len + 1) * sizeof(wchar_t));
1367 ReadBuf(b, t->unistr, len * sizeof(wchar_t));
1368
1369 Add(TableList, t);
1370 }
1371
1372 FreeBuf(b);
1373
1374 Sort(TableList);
1375
1376 return true;
1377 }
1378
1379 // Read the string table
LoadTableMain(wchar_t * filename)1380 bool LoadTableMain(wchar_t *filename)
1381 {
1382 BUF *b;
1383 UINT64 t1, t2;
1384 UCHAR hash[MD5_SIZE];
1385 // Validate arguments
1386 if (filename == NULL)
1387 {
1388 return false;
1389 }
1390
1391 if (MayaquaIsMinimalMode())
1392 {
1393 return true;
1394 }
1395
1396 if (UniStrCmpi(old_table_name, filename) == 0)
1397 {
1398 // Already loaded
1399 return true;
1400 }
1401
1402 t1 = Tick64();
1403
1404 // Open the file
1405 b = ReadDumpW(filename);
1406 if (b == NULL)
1407 {
1408 char tmp[MAX_SIZE];
1409 StrCpy(tmp, sizeof(tmp), "Error: Can't read string tables (file not found).\r\nPlease check hamcore.se2.\r\n\r\n(First, reboot the computer. If this problem occurs again, please reinstall VPN software files.)");
1410 Alert(tmp, NULL);
1411 exit(-1);
1412 return false;
1413 }
1414
1415 Hash(hash, b->Buf, b->Size, false);
1416
1417 if (LoadUnicodeCache(filename, b->Size, hash) == false)
1418 {
1419 if (LoadTableFromBuf(b) == false)
1420 {
1421 FreeBuf(b);
1422 return false;
1423 }
1424
1425 SaveUnicodeCache(filename, b->Size, hash);
1426
1427 //Debug("Unicode Source: strtable.stb\n");
1428 }
1429 else
1430 {
1431 //Debug("Unicode Source: unicode_cache\n");
1432 }
1433
1434 FreeBuf(b);
1435
1436 SetLocale(_UU("DEFAULE_LOCALE"));
1437
1438 UniStrCpy(old_table_name, sizeof(old_table_name), filename);
1439
1440 t2 = Tick64();
1441
1442 if (StrCmpi(_SS("STRTABLE_ID"), STRTABLE_ID) != 0)
1443 {
1444 char tmp[MAX_SIZE];
1445 Format(tmp, sizeof(tmp), "Error: Can't read string tables (invalid version: '%s'!='%s').\r\nPlease check hamcore.se2.\r\n\r\n(First, reboot the computer. If this problem occurs again, please reinstall VPN software files.)",
1446 _SS("STRTABLE_ID"), STRTABLE_ID);
1447 Alert(tmp, NULL);
1448 exit(-1);
1449 return false;
1450 }
1451
1452 //Debug("Unicode File Read Cost: %u (%u Lines)\n", (UINT)(t2 - t1), LIST_NUM(TableList));
1453
1454 return true;
1455 }
LoadTable(char * filename)1456 bool LoadTable(char *filename)
1457 {
1458 wchar_t *filename_a = CopyStrToUni(filename);
1459 bool ret = LoadTableW(filename_a);
1460
1461 Free(filename_a);
1462
1463 return ret;
1464 }
LoadTableW(wchar_t * filename)1465 bool LoadTableW(wchar_t *filename)
1466 {
1467 bool ret;
1468 BUF *b;
1469 wchar_t replace_name[MAX_PATH];
1470
1471 Zero(replace_name, sizeof(replace_name));
1472
1473 TrackingDisable();
1474
1475 b = ReadDump("@table_name.txt");
1476 if (b != NULL)
1477 {
1478 char *s = CfgReadNextLine(b);
1479 if (s != NULL)
1480 {
1481 if (IsEmptyStr(s) == false)
1482 {
1483 StrToUni(replace_name, sizeof(replace_name), s);
1484 filename = replace_name;
1485 }
1486
1487 Free(s);
1488 }
1489 FreeBuf(b);
1490 }
1491
1492 ret = LoadTableMain(filename);
1493
1494 TrackingEnable();
1495
1496 return ret;
1497 }
1498
1499
1500