xref: /reactos/subsystems/win/basesrv/nls.c (revision 9393fc32)
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     UCHAR SecurityDescriptor[52];
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(&SecurityDescriptor,
275                                           sizeof(SecurityDescriptor),
276                                           0x80000000);
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                                &SecurityDescriptor);
290     Status = NtCreateSection(&SectionHandle,
291                              SECTION_MAP_READ,
292                              &ObjectAttributes,
293                              0,
294                              PAGE_READONLY,
295                              SEC_COMMIT,
296                              FileHandle);
297     NtClose(FileHandle);
298     if (!NT_SUCCESS(Status))
299     {
300         DPRINT1("NLS: Failed to create section! %lx\n", Status);
301         return Status;
302     }
303 
304     /* Open a handle to the calling process */
305     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
306     Status = NtOpenProcess(&ProcessHandle,
307                            PROCESS_DUP_HANDLE,
308                            &ObjectAttributes,
309                            &ApiMessage->Header.ClientId);
310     if (!NT_SUCCESS(Status))
311     {
312         DPRINT1("NLS: Failed to open process! %lx\n", Status);
313         NtClose(SectionHandle);
314         return Status;
315     }
316 
317     /* Duplicate the handle to the section object into it */
318     Status = NtDuplicateObject(NtCurrentProcess(),
319                                SectionHandle,
320                                ProcessHandle,
321                                &NlsMsg->SectionHandle,
322                                0,
323                                0,
324                                3);
325     NtClose(ProcessHandle);
326     return Status;
327 }
328 
329 CSR_API(BaseSrvNlsUpdateCacheCount)
330 {
331     DPRINT1("%s not yet implemented\n", __FUNCTION__);
332     return STATUS_NOT_IMPLEMENTED;
333 }
334 
335 CSR_API(BaseSrvNlsGetUserInfo)
336 {
337     NTSTATUS Status;
338     PBASE_NLS_GET_USER_INFO NlsMsg = &((PBASE_API_MESSAGE)ApiMessage)->Data.NlsGetUserInfo;
339 
340     /* Make sure the buffer is valid and of the right size */
341     if ((CsrValidateMessageBuffer(ApiMessage, &NlsMsg->NlsUserInfo, NlsMsg->Size, sizeof(BYTE))) &&
342         (NlsMsg->Size == sizeof(NLS_USER_INFO)))
343     {
344         /* Acquire the lock to prevent updates while we copy */
345         Status = RtlEnterCriticalSection(&NlsCacheCriticalSection);
346         if (NT_SUCCESS(Status))
347         {
348             /* Do the copy now, then drop the lock */
349             RtlCopyMemory(NlsMsg->NlsUserInfo, pNlsRegUserInfo, NlsMsg->Size);
350             DPRINT1("NLS Data copy complete\n");
351             RtlLeaveCriticalSection(&NlsCacheCriticalSection);
352         }
353     }
354     else
355     {
356         /* The data was invalid, bail out */
357         DPRINT1("NLS: Size of info is invalid: %lx vs %lx\n", NlsMsg->Size, sizeof(NLS_USER_INFO));
358         Status = STATUS_INVALID_PARAMETER;
359     }
360 
361     /* All done */
362     return Status;
363 }
364 
365 /* PUBLIC APIS ****************************************************************/
366 
367 NTSTATUS
368 NTAPI
369 BaseSrvNlsLogon(DWORD Unknown)
370 {
371     DPRINT1("%s(%lu) not yet implemented\n", __FUNCTION__, Unknown);
372     return STATUS_NOT_IMPLEMENTED;
373 }
374 
375 NTSTATUS
376 NTAPI
377 BaseSrvNlsUpdateRegistryCache(DWORD Unknown1,
378                               DWORD Unknown2)
379 {
380     DPRINT1("%s(%lu, %lu) not yet implemented\n", __FUNCTION__, Unknown1, Unknown2);
381     return STATUS_NOT_IMPLEMENTED;
382 }
383 
384 /* EOF */
385