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