xref: /reactos/subsystems/win/basesrv/nls.c (revision 84344399)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Base API Server DLL
4  * FILE:            subsystems/win/basesrv/nls.c
5  * PURPOSE:         National Language Support (NLS)
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "basesrv.h"
12 
13 #include <ndk/mmfuncs.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 /* GLOBALS ********************************************************************/
19 
20 RTL_CRITICAL_SECTION NlsCacheCriticalSection;
21 PNLS_USER_INFO pNlsRegUserInfo;
22 
23 BOOLEAN BaseSrvKernel32DelayLoadComplete;
24 HANDLE BaseSrvKernel32DllHandle;
25 UNICODE_STRING BaseSrvKernel32DllPath;
26 
27 POPEN_DATA_FILE pOpenDataFile;
28 PVOID /*PGET_DEFAULT_SORTKEY_SIZE */ pGetDefaultSortkeySize;
29 PVOID /*PGET_LINGUIST_LANG_SIZE*/ pGetLinguistLangSize;
30 PVOID /*PNLS_CONVERT_INTEGER_TO_STRING*/ pNlsConvertIntegerToString;
31 PVOID /*PVALIDATE_LCTYPE*/ pValidateLCType;
32 PVALIDATE_LOCALE pValidateLocale;
33 PGET_NLS_SECTION_NAME pGetNlsSectionName;
34 PVOID /*PGET_USER_DEFAULT_LANGID*/ pGetUserDefaultLangID;
35 PGET_CP_FILE_NAME_FROM_REGISTRY pGetCPFileNameFromRegistry;
36 
37 NTSTATUS
38 (WINAPI *pCreateNlsSecurityDescriptor)(
39     _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor,
40     _In_ SIZE_T DescriptorSize,
41     _In_ ULONG AccessMask);
42 
43 BASESRV_KERNEL_IMPORTS BaseSrvKernel32Imports[10] =
44 {
45     { "OpenDataFile", (PVOID*) &pOpenDataFile },
46     { "GetDefaultSortkeySize", (PVOID*) &pGetDefaultSortkeySize },
47     { "GetLinguistLangSize", (PVOID*) &pGetLinguistLangSize },
48     { "NlsConvertIntegerToString", (PVOID*) &pNlsConvertIntegerToString },
49     { "ValidateLCType", (PVOID*) &pValidateLCType },
50     { "ValidateLocale", (PVOID*) &pValidateLocale },
51     { "GetNlsSectionName", (PVOID*) &pGetNlsSectionName },
52     { "GetUserDefaultLangID", (PVOID*) &pGetUserDefaultLangID },
53     { "GetCPFileNameFromRegistry", (PVOID*) &pGetCPFileNameFromRegistry },
54     { "CreateNlsSecurityDescriptor", (PVOID*) &pCreateNlsSecurityDescriptor },
55 };
56 
57 /* FUNCTIONS *****************************************************************/
58 
59 NTSTATUS
60 NTAPI
61 BaseSrvDelayLoadKernel32(VOID)
62 {
63     NTSTATUS Status;
64     ULONG i;
65     ANSI_STRING ProcedureName;
66 
67     /* Only do this once */
68     if (BaseSrvKernel32DelayLoadComplete) return STATUS_SUCCESS;
69 
70     /* Loop all imports */
71     for (i = 0; i < RTL_NUMBER_OF(BaseSrvKernel32Imports); i++)
72     {
73         /* Only look them up once */
74         if (!*BaseSrvKernel32Imports[i].FunctionPointer)
75         {
76             /* If we haven't loaded the DLL yet, do it now */
77             if (!BaseSrvKernel32DllHandle)
78             {
79                 Status = LdrLoadDll(0,
80                                     0,
81                                     &BaseSrvKernel32DllPath,
82                                     &BaseSrvKernel32DllHandle);
83                 if (!NT_SUCCESS(Status))
84                 {
85                     DPRINT1("Failed to load %wZ\n", &BaseSrvKernel32DllPath);
86                     return Status;
87                 }
88             }
89 
90             /* Get the address of the routine being looked up*/
91             RtlInitAnsiString(&ProcedureName, BaseSrvKernel32Imports[i].FunctionName);
92             Status = LdrGetProcedureAddress(BaseSrvKernel32DllHandle,
93                                             &ProcedureName,
94                                             0,
95                                             BaseSrvKernel32Imports[i].FunctionPointer);
96             DPRINT1("NLS: Found %Z at 0x%p\n",
97                     &ProcedureName,
98                     BaseSrvKernel32Imports[i].FunctionPointer);
99             if (!NT_SUCCESS(Status)) break;
100         }
101     }
102 
103     /* Did we find them all? */
104     if (i == RTL_NUMBER_OF(BaseSrvKernel32Imports))
105     {
106         /* Excellent */
107         BaseSrvKernel32DelayLoadComplete = TRUE;
108         return STATUS_SUCCESS;
109     }
110 
111     /* Nope, fail */
112     return Status;
113 }
114 
115 VOID
116 NTAPI
117 BaseSrvNLSInit(IN PBASE_STATIC_SERVER_DATA StaticData)
118 {
119     /* Initialize the lock */
120     RtlInitializeCriticalSection(&NlsCacheCriticalSection);
121 
122     /* Initialize the data with all F's */
123     pNlsRegUserInfo = &StaticData->NlsUserInfo;
124     RtlFillMemory(&StaticData->NlsUserInfo, sizeof(StaticData->NlsUserInfo), 0xFF);
125 
126     /* Set empty LCID */
127     pNlsRegUserInfo->UserLocaleId = 0;
128 
129     /* Reset the cache update counter */
130     RtlEnterCriticalSection(&NlsCacheCriticalSection);
131     pNlsRegUserInfo->ulCacheUpdateCount = 0;
132     RtlLeaveCriticalSection(&NlsCacheCriticalSection);
133 
134     /* Get the LCID */
135     NtQueryDefaultLocale(0, &pNlsRegUserInfo->UserLocaleId);
136 }
137 
138 NTSTATUS
139 NTAPI
140 BaseSrvNlsConnect(IN PCSR_PROCESS CsrProcess,
141                   IN OUT PVOID  ConnectionInfo,
142                   IN OUT PULONG ConnectionInfoLength)
143 {
144     /* Does nothing */
145     return STATUS_SUCCESS;
146 }
147 
148 /* PUBLIC SERVER APIS *********************************************************/
149 
150 CSR_API(BaseSrvNlsSetUserInfo)
151 {
152     DPRINT1("%s not yet implemented\n", __FUNCTION__);
153     return STATUS_NOT_IMPLEMENTED;
154 }
155 
156 CSR_API(BaseSrvNlsSetMultipleUserInfo)
157 {
158     DPRINT1("%s not yet implemented\n", __FUNCTION__);
159     return STATUS_NOT_IMPLEMENTED;
160 }
161 
162 CSR_API(BaseSrvNlsCreateSection)
163 {
164     NTSTATUS Status;
165     HANDLE SectionHandle, ProcessHandle, FileHandle;
166     ULONG LocaleId;
167     UNICODE_STRING NlsSectionName;
168     PWCHAR NlsFileName;
169     UCHAR SecurityDescriptor[NLS_SECTION_SECURITY_DESCRIPTOR_SIZE];
170     OBJECT_ATTRIBUTES ObjectAttributes;
171     WCHAR FileNameBuffer[32];
172     WCHAR NlsSectionNameBuffer[32];
173     PBASE_NLS_CREATE_SECTION NlsMsg = &((PBASE_API_MESSAGE)ApiMessage)->Data.NlsCreateSection;
174 
175     /* Load kernel32 first and import the NLS routines */
176     Status = BaseSrvDelayLoadKernel32();
177     if (!NT_SUCCESS(Status)) return Status;
178 
179     /* Assume failure */
180     NlsMsg->SectionHandle = NULL;
181 
182     /* Check and validate the locale ID, if one is present */
183     LocaleId = NlsMsg->LocaleId;
184     DPRINT1("NLS: Create Section with LCID: %lx for Type: %d\n", LocaleId, NlsMsg->Type);
185     if (LocaleId)
186     {
187         if (!pValidateLocale(LocaleId)) return STATUS_INVALID_PARAMETER;
188     }
189 
190     /* Check which NLS section is being created */
191     switch (NlsMsg->Type)
192     {
193         /* For each one, set the correct filename and object name */
194         case 1:
195             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionUnicode");
196             NlsFileName = L"unicode.nls";
197             break;
198         case 2:
199             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionLocale");
200             NlsFileName =  L"locale.nls";
201             break;
202         case 3:
203             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCType");
204             NlsFileName = L"ctype.nls";
205             break;
206         case 4:
207             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionSortkey");
208             NlsFileName = L"sortkey.nls";
209             break;
210         case 5:
211             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionSortTbls");
212             NlsFileName = L"sorttbls.nls";
213             break;
214         case 6:
215             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCP437");
216             NlsFileName = L"c_437.nls";
217             break;
218         case 7:
219             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCP1252");
220             NlsFileName = L"c_1252.nls";
221             break;
222         case 8:
223             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionLANG_EXCEPT");
224             NlsFileName = L"l_except.nls";
225             break;
226         case 9:
227             DPRINT1("This type not yet supported\n");
228             return STATUS_NOT_IMPLEMENTED;
229         case 10:
230             DPRINT1("This type not yet supported\n");
231             return STATUS_NOT_IMPLEMENTED;
232         case 11:
233             /* Get the filename for this locale */
234             if (!pGetCPFileNameFromRegistry(NlsMsg->LocaleId,
235                                             FileNameBuffer,
236                                             RTL_NUMBER_OF(FileNameBuffer)))
237             {
238                 DPRINT1("File name query failed\n");
239                 return STATUS_INVALID_PARAMETER;
240             }
241 
242             /* Get the name of the section for this locale */
243             DPRINT1("File name: %S\n", FileNameBuffer);
244             Status = pGetNlsSectionName(NlsMsg->LocaleId,
245                                         10,
246                                         0,
247                                         L"\\NLS\\NlsSectionCP",
248                                         NlsSectionNameBuffer,
249                                         RTL_NUMBER_OF(NlsSectionNameBuffer));
250             if (!NT_SUCCESS(Status))
251             {
252                 DPRINT1("Section name query failed: %lx\n", Status);
253                 return Status;
254             }
255 
256             /* Setup the name and go open it */
257             NlsFileName = FileNameBuffer;
258             DPRINT1("Section name: %S\n", NlsSectionNameBuffer);
259             RtlInitUnicodeString(&NlsSectionName, NlsSectionNameBuffer);
260             break;
261         case 12:
262             RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionGeo");
263             NlsFileName = L"geo.nls";
264             break;
265         default:
266             DPRINT1("NLS: Invalid NLS type!\n");
267             return STATUS_INVALID_PARAMETER;
268     }
269 
270     /* Open the specified NLS file */
271     Status = pOpenDataFile(&FileHandle, NlsFileName);
272     if (Status != STATUS_SUCCESS)
273     {
274         DPRINT1("NLS: Failed to open file: %lx\n", Status);
275         return Status;
276     }
277 
278     /* Create an SD for the section object */
279     Status = pCreateNlsSecurityDescriptor(&SecurityDescriptor,
280                                           sizeof(SecurityDescriptor),
281                                           SECTION_MAP_READ);
282     if (!NT_SUCCESS(Status))
283     {
284         DPRINT1("NLS: CreateNlsSecurityDescriptor FAILED!: %lx\n", Status);
285         NtClose(FileHandle);
286         return Status;
287     }
288 
289     /* Create the section object proper */
290     InitializeObjectAttributes(&ObjectAttributes,
291                                &NlsSectionName,
292                                OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_OPENIF,
293                                NULL,
294                                &SecurityDescriptor);
295     Status = NtCreateSection(&SectionHandle,
296                              SECTION_MAP_READ,
297                              &ObjectAttributes,
298                              0,
299                              PAGE_READONLY,
300                              SEC_COMMIT,
301                              FileHandle);
302     NtClose(FileHandle);
303     if (!NT_SUCCESS(Status))
304     {
305         DPRINT1("NLS: Failed to create section! %lx\n", Status);
306         return Status;
307     }
308 
309     /* Open a handle to the calling process */
310     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
311     Status = NtOpenProcess(&ProcessHandle,
312                            PROCESS_DUP_HANDLE,
313                            &ObjectAttributes,
314                            &ApiMessage->Header.ClientId);
315     if (!NT_SUCCESS(Status))
316     {
317         DPRINT1("NLS: Failed to open process! %lx\n", Status);
318         NtClose(SectionHandle);
319         return Status;
320     }
321 
322     /* Duplicate the handle to the section object into it */
323     Status = NtDuplicateObject(NtCurrentProcess(),
324                                SectionHandle,
325                                ProcessHandle,
326                                &NlsMsg->SectionHandle,
327                                0,
328                                0,
329                                3);
330     NtClose(ProcessHandle);
331     return Status;
332 }
333 
334 CSR_API(BaseSrvNlsUpdateCacheCount)
335 {
336     DPRINT1("%s not yet implemented\n", __FUNCTION__);
337     return STATUS_NOT_IMPLEMENTED;
338 }
339 
340 CSR_API(BaseSrvNlsGetUserInfo)
341 {
342     NTSTATUS Status;
343     PBASE_NLS_GET_USER_INFO NlsMsg = &((PBASE_API_MESSAGE)ApiMessage)->Data.NlsGetUserInfo;
344 
345     /* Make sure the buffer is valid and of the right size */
346     if ((CsrValidateMessageBuffer(ApiMessage, &NlsMsg->NlsUserInfo, NlsMsg->Size, sizeof(BYTE))) &&
347         (NlsMsg->Size == sizeof(NLS_USER_INFO)))
348     {
349         /* Acquire the lock to prevent updates while we copy */
350         Status = RtlEnterCriticalSection(&NlsCacheCriticalSection);
351         if (NT_SUCCESS(Status))
352         {
353             /* Do the copy now, then drop the lock */
354             RtlCopyMemory(NlsMsg->NlsUserInfo, pNlsRegUserInfo, NlsMsg->Size);
355             DPRINT1("NLS Data copy complete\n");
356             RtlLeaveCriticalSection(&NlsCacheCriticalSection);
357         }
358     }
359     else
360     {
361         /* The data was invalid, bail out */
362         DPRINT1("NLS: Size of info is invalid: %lx vs %lx\n", NlsMsg->Size, sizeof(NLS_USER_INFO));
363         Status = STATUS_INVALID_PARAMETER;
364     }
365 
366     /* All done */
367     return Status;
368 }
369 
370 /* PUBLIC APIS ****************************************************************/
371 
372 NTSTATUS
373 NTAPI
374 BaseSrvNlsLogon(DWORD Unknown)
375 {
376     DPRINT1("%s(%lu) not yet implemented\n", __FUNCTION__, Unknown);
377     return STATUS_NOT_IMPLEMENTED;
378 }
379 
380 NTSTATUS
381 NTAPI
382 BaseSrvNlsUpdateRegistryCache(DWORD Unknown1,
383                               DWORD Unknown2)
384 {
385     DPRINT1("%s(%lu, %lu) not yet implemented\n", __FUNCTION__, Unknown1, Unknown2);
386     return STATUS_NOT_IMPLEMENTED;
387 }
388 
389 /* EOF */
390