1 /*
2  * PROJECT:     ReactOS Applications Manager
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Config parser
5  * COPYRIGHT:   Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org)
6  *              Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com)
7  *              Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org)
8  *              Copyright 2021 Mark Jansen <mark.jansen@reactos.org>
9  */
10 #include "rapps.h"
11 #include <debug.h>
12 
13 struct CLocaleSections
14 {
15     CStringW Locale;
16     CStringW LocaleNeutral;
17     CStringW Section;
18 };
19 
20 struct CSectionNames
21 {
22     CLocaleSections ArchSpecific;
23     CLocaleSections ArchNeutral;
24 };
25 static CSectionNames g_Names;
26 
27 static CStringW
28 GetINIFullPath(const CStringW &FileName)
29 {
30     CStringW szDir;
31     CStringW szBuffer;
32 
33     GetStorageDirectory(szDir);
34     szBuffer.Format(L"%ls\\rapps\\%ls", szDir.GetString(), FileName.GetString());
35 
36     return szBuffer;
37 }
38 
39 CConfigParser::CConfigParser(const CStringW &FileName) : szConfigPath(GetINIFullPath(FileName))
40 {
41     CacheINI();
42 }
43 
44 void
45 CConfigParser::ReadSection(CStringW &Buffer, const CStringW &Section, BOOL isArch)
46 {
47     DWORD len = 512;
48     DWORD result;
49 
50     do
51     {
52         len *= 2;
53 
54         result = GetPrivateProfileSectionW(Section, Buffer.GetBuffer(len), len, szConfigPath);
55         Buffer.ReleaseBuffer(result);
56     } while (result == len - 2);
57 
58     len = 0;
59     while (len < result)
60     {
61         // Explicitly use the null terminator!
62         CString tmp = Buffer.GetBuffer() + len;
63         if (tmp.GetLength() > 0)
64         {
65             len += tmp.GetLength() + 1;
66 
67             int idx = tmp.Find('=');
68             if (idx >= 0)
69             {
70                 CString key = tmp.Left(idx);
71 
72 #ifndef _M_IX86
73                 // On non-x86 architecture we need the architecture specific URL
74                 if (!isArch && key == "URLDownload")
75                 {
76                     continue;
77                 }
78 #endif
79 
80                 // Is this key already present from a more specific translation?
81                 if (m_Keys.FindKey(key) >= 0)
82                 {
83                     continue;
84                 }
85 
86                 CString value = tmp.Mid(idx + 1);
87                 m_Keys.Add(key, value);
88             }
89             else
90             {
91                 DPRINT1("ERROR: invalid key/value pair: '%S'\n", tmp.GetString());
92             }
93         }
94         else
95         {
96             break;
97         }
98     }
99 }
100 
101 VOID
102 CConfigParser::CacheINI()
103 {
104     // Cache section names
105     if (g_Names.ArchSpecific.Locale.IsEmpty())
106     {
107         CString szLocaleID;
108         const INT cchLocaleSize = 5;
109 
110         GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE, szLocaleID.GetBuffer(cchLocaleSize), cchLocaleSize);
111         szLocaleID.ReleaseBuffer();
112         CString INISectionLocale = L"Section." + szLocaleID;
113 
114         g_Names.ArchSpecific.Locale = INISectionLocale + L"." CurrentArchitecture;
115         g_Names.ArchNeutral.Locale = INISectionLocale;
116 
117         // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part
118         if (szLocaleID.GetLength() >= 2)
119         {
120             g_Names.ArchSpecific.LocaleNeutral = L"Section." + szLocaleID.Right(2) + L"." CurrentArchitecture;
121             g_Names.ArchNeutral.LocaleNeutral = L"Section." + szLocaleID.Right(2);
122         }
123 
124         g_Names.ArchSpecific.Section = L"Section." CurrentArchitecture;
125         g_Names.ArchNeutral.Section = L"Section";
126     }
127 
128     // Use a shared buffer so that we don't have to re-allocate it every time
129     CStringW Buffer;
130 
131     ReadSection(Buffer, g_Names.ArchSpecific.Locale, TRUE);
132     if (!g_Names.ArchSpecific.LocaleNeutral.IsEmpty())
133     {
134         ReadSection(Buffer, g_Names.ArchSpecific.LocaleNeutral, TRUE);
135     }
136     ReadSection(Buffer, g_Names.ArchSpecific.Section, TRUE);
137 
138     ReadSection(Buffer, g_Names.ArchNeutral.Locale, FALSE);
139     if (!g_Names.ArchNeutral.LocaleNeutral.IsEmpty())
140     {
141         ReadSection(Buffer, g_Names.ArchNeutral.LocaleNeutral, FALSE);
142     }
143     ReadSection(Buffer, g_Names.ArchNeutral.Section, FALSE);
144 }
145 
146 BOOL
147 CConfigParser::GetString(const CStringW &KeyName, CStringW &ResultString)
148 {
149     int nIndex = m_Keys.FindKey(KeyName);
150     if (nIndex >= 0)
151     {
152         ResultString = m_Keys.GetValueAt(nIndex);
153         return TRUE;
154     }
155 
156     ResultString.Empty();
157     return FALSE;
158 }
159 
160 BOOL
161 CConfigParser::GetInt(const CStringW &KeyName, INT &iResult)
162 {
163     CStringW Buffer;
164 
165     iResult = 0;
166 
167     // grab the text version of our entry
168     if (!GetString(KeyName, Buffer))
169         return FALSE;
170 
171     if (Buffer.IsEmpty())
172         return FALSE;
173 
174     // convert it to an actual integer
175     iResult = StrToIntW(Buffer.GetString());
176 
177     // we only care about values > 0
178     return (iResult > 0);
179 }
180