mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
384 lines
13 KiB
C
384 lines
13 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Base API Server DLL
|
|
* FILE: subsystems/win/basesrv/nls.c
|
|
* PURPOSE: National Language Support (NLS)
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "basesrv.h"
|
|
|
|
#include <ndk/mmfuncs.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
RTL_CRITICAL_SECTION NlsCacheCriticalSection;
|
|
PNLS_USER_INFO pNlsRegUserInfo;
|
|
|
|
BOOLEAN BaseSrvKernel32DelayLoadComplete;
|
|
HANDLE BaseSrvKernel32DllHandle;
|
|
UNICODE_STRING BaseSrvKernel32DllPath;
|
|
|
|
POPEN_DATA_FILE pOpenDataFile;
|
|
PVOID /*PGET_DEFAULT_SORTKEY_SIZE */ pGetDefaultSortkeySize;
|
|
PVOID /*PGET_LINGUIST_LANG_SIZE*/ pGetLinguistLangSize;
|
|
PVOID /*PNLS_CONVERT_INTEGER_TO_STRING*/ pNlsConvertIntegerToString;
|
|
PVOID /*PVALIDATE_LCTYPE*/ pValidateLCType;
|
|
PVALIDATE_LOCALE pValidateLocale;
|
|
PGET_NLS_SECTION_NAME pGetNlsSectionName;
|
|
PVOID /*PGET_USER_DEFAULT_LANGID*/ pGetUserDefaultLangID;
|
|
PGET_CP_FILE_NAME_FROM_REGISTRY pGetCPFileNameFromRegistry;
|
|
PCREATE_NLS_SECURTY_DESCRIPTOR pCreateNlsSecurityDescriptor;
|
|
|
|
BASESRV_KERNEL_IMPORTS BaseSrvKernel32Imports[10] =
|
|
{
|
|
{ "OpenDataFile", (PVOID*) &pOpenDataFile },
|
|
{ "GetDefaultSortkeySize", (PVOID*) &pGetDefaultSortkeySize },
|
|
{ "GetLinguistLangSize", (PVOID*) &pGetLinguistLangSize },
|
|
{ "NlsConvertIntegerToString", (PVOID*) &pNlsConvertIntegerToString },
|
|
{ "ValidateLCType", (PVOID*) &pValidateLCType },
|
|
{ "ValidateLocale", (PVOID*) &pValidateLocale },
|
|
{ "GetNlsSectionName", (PVOID*) &pGetNlsSectionName },
|
|
{ "GetUserDefaultLangID", (PVOID*) &pGetUserDefaultLangID },
|
|
{ "GetCPFileNameFromRegistry", (PVOID*) &pGetCPFileNameFromRegistry },
|
|
{ "CreateNlsSecurityDescriptor", (PVOID*) &pCreateNlsSecurityDescriptor },
|
|
};
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
BaseSrvDelayLoadKernel32(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
ANSI_STRING ProcedureName;
|
|
|
|
/* Only do this once */
|
|
if (BaseSrvKernel32DelayLoadComplete) return STATUS_SUCCESS;
|
|
|
|
/* Loop all imports */
|
|
for (i = 0; i < RTL_NUMBER_OF(BaseSrvKernel32Imports); i++)
|
|
{
|
|
/* Only look them up once */
|
|
if (!*BaseSrvKernel32Imports[i].FunctionPointer)
|
|
{
|
|
/* If we haven't loaded the DLL yet, do it now */
|
|
if (!BaseSrvKernel32DllHandle)
|
|
{
|
|
Status = LdrLoadDll(0,
|
|
0,
|
|
&BaseSrvKernel32DllPath,
|
|
&BaseSrvKernel32DllHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to load %wZ\n", &BaseSrvKernel32DllPath);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/* Get the address of the routine being looked up*/
|
|
RtlInitAnsiString(&ProcedureName, BaseSrvKernel32Imports[i].FunctionName);
|
|
Status = LdrGetProcedureAddress(BaseSrvKernel32DllHandle,
|
|
&ProcedureName,
|
|
0,
|
|
BaseSrvKernel32Imports[i].FunctionPointer);
|
|
DPRINT1("NLS: Found %Z at 0x%p\n",
|
|
&ProcedureName,
|
|
BaseSrvKernel32Imports[i].FunctionPointer);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
}
|
|
}
|
|
|
|
/* Did we find them all? */
|
|
if (i == RTL_NUMBER_OF(BaseSrvKernel32Imports))
|
|
{
|
|
/* Excellent */
|
|
BaseSrvKernel32DelayLoadComplete = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Nope, fail */
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
BaseSrvNLSInit(IN PBASE_STATIC_SERVER_DATA StaticData)
|
|
{
|
|
/* Initialize the lock */
|
|
RtlInitializeCriticalSection(&NlsCacheCriticalSection);
|
|
|
|
/* Initialize the data with all F's */
|
|
pNlsRegUserInfo = &StaticData->NlsUserInfo;
|
|
RtlFillMemory(&StaticData->NlsUserInfo, sizeof(StaticData->NlsUserInfo), 0xFF);
|
|
|
|
/* Set empty LCID */
|
|
pNlsRegUserInfo->UserLocaleId = 0;
|
|
|
|
/* Reset the cache update counter */
|
|
RtlEnterCriticalSection(&NlsCacheCriticalSection);
|
|
pNlsRegUserInfo->ulCacheUpdateCount = 0;
|
|
RtlLeaveCriticalSection(&NlsCacheCriticalSection);
|
|
|
|
/* Get the LCID */
|
|
NtQueryDefaultLocale(0, &pNlsRegUserInfo->UserLocaleId);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
BaseSrvNlsConnect(IN PCSR_PROCESS CsrProcess,
|
|
IN OUT PVOID ConnectionInfo,
|
|
IN OUT PULONG ConnectionInfoLength)
|
|
{
|
|
/* Does nothing */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* PUBLIC SERVER APIS *********************************************************/
|
|
|
|
CSR_API(BaseSrvNlsSetUserInfo)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(BaseSrvNlsSetMultipleUserInfo)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(BaseSrvNlsCreateSection)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE SectionHandle, ProcessHandle, FileHandle;
|
|
ULONG LocaleId;
|
|
UNICODE_STRING NlsSectionName;
|
|
PWCHAR NlsFileName;
|
|
UCHAR SecurityDescriptor[52];
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
WCHAR FileNameBuffer[32];
|
|
WCHAR NlsSectionNameBuffer[32];
|
|
PBASE_NLS_CREATE_SECTION NlsMsg = &((PBASE_API_MESSAGE)ApiMessage)->Data.NlsCreateSection;
|
|
|
|
/* Load kernel32 first and import the NLS routines */
|
|
Status = BaseSrvDelayLoadKernel32();
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Assume failure */
|
|
NlsMsg->SectionHandle = NULL;
|
|
|
|
/* Check and validate the locale ID, if one is present */
|
|
LocaleId = NlsMsg->LocaleId;
|
|
DPRINT1("NLS: Create Section with LCID: %lx for Type: %d\n", LocaleId, NlsMsg->Type);
|
|
if (LocaleId)
|
|
{
|
|
if (!pValidateLocale(LocaleId)) return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check which NLS section is being created */
|
|
switch (NlsMsg->Type)
|
|
{
|
|
/* For each one, set the correct filename and object name */
|
|
case 1:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionUnicode");
|
|
NlsFileName = L"unicode.nls";
|
|
break;
|
|
case 2:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionLocale");
|
|
NlsFileName = L"locale.nls";
|
|
break;
|
|
case 3:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCType");
|
|
NlsFileName = L"ctype.nls";
|
|
break;
|
|
case 4:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionSortkey");
|
|
NlsFileName = L"sortkey.nls";
|
|
break;
|
|
case 5:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionSortTbls");
|
|
NlsFileName = L"sorttbls.nls";
|
|
break;
|
|
case 6:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCP437");
|
|
NlsFileName = L"c_437.nls";
|
|
break;
|
|
case 7:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionCP1252");
|
|
NlsFileName = L"c_1252.nls";
|
|
break;
|
|
case 8:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionLANG_EXCEPT");
|
|
NlsFileName = L"l_except.nls";
|
|
break;
|
|
case 9:
|
|
DPRINT1("This type not yet supported\n");
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
case 10:
|
|
DPRINT1("This type not yet supported\n");
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
case 11:
|
|
/* Get the filename for this locale */
|
|
if (!pGetCPFileNameFromRegistry(NlsMsg->LocaleId,
|
|
FileNameBuffer,
|
|
RTL_NUMBER_OF(FileNameBuffer)))
|
|
{
|
|
DPRINT1("File name query failed\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Get the name of the section for this locale */
|
|
DPRINT1("File name: %S\n", FileNameBuffer);
|
|
Status = pGetNlsSectionName(NlsMsg->LocaleId,
|
|
10,
|
|
0,
|
|
L"\\NLS\\NlsSectionCP",
|
|
NlsSectionNameBuffer,
|
|
RTL_NUMBER_OF(NlsSectionNameBuffer));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Section name query failed: %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Setup the name and go open it */
|
|
NlsFileName = FileNameBuffer;
|
|
DPRINT1("Section name: %S\n", NlsSectionNameBuffer);
|
|
RtlInitUnicodeString(&NlsSectionName, NlsSectionNameBuffer);
|
|
break;
|
|
case 12:
|
|
RtlInitUnicodeString(&NlsSectionName, L"\\NLS\\NlsSectionGeo");
|
|
NlsFileName = L"geo.nls";
|
|
break;
|
|
default:
|
|
DPRINT1("NLS: Invalid NLS type!\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Open the specified NLS file */
|
|
Status = pOpenDataFile(&FileHandle, NlsFileName);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
DPRINT1("NLS: Failed to open file: %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Create an SD for the section object */
|
|
Status = pCreateNlsSecurityDescriptor(&SecurityDescriptor,
|
|
sizeof(SecurityDescriptor),
|
|
0x80000000);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NLS: CreateNlsSecurityDescriptor FAILED!: %lx\n", Status);
|
|
NtClose(FileHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Create the section object proper */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&NlsSectionName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT | OBJ_OPENIF,
|
|
NULL,
|
|
&SecurityDescriptor);
|
|
Status = NtCreateSection(&SectionHandle,
|
|
SECTION_MAP_READ,
|
|
&ObjectAttributes,
|
|
0,
|
|
PAGE_READONLY,
|
|
SEC_COMMIT,
|
|
FileHandle);
|
|
NtClose(FileHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NLS: Failed to create section! %lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Open a handle to the calling process */
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
|
|
Status = NtOpenProcess(&ProcessHandle,
|
|
PROCESS_DUP_HANDLE,
|
|
&ObjectAttributes,
|
|
&ApiMessage->Header.ClientId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NLS: Failed to open process! %lx\n", Status);
|
|
NtClose(SectionHandle);
|
|
return Status;
|
|
}
|
|
|
|
/* Duplicate the handle to the section object into it */
|
|
Status = NtDuplicateObject(NtCurrentProcess(),
|
|
SectionHandle,
|
|
ProcessHandle,
|
|
&NlsMsg->SectionHandle,
|
|
0,
|
|
0,
|
|
3);
|
|
NtClose(ProcessHandle);
|
|
return Status;
|
|
}
|
|
|
|
CSR_API(BaseSrvNlsUpdateCacheCount)
|
|
{
|
|
DPRINT1("%s not yet implemented\n", __FUNCTION__);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
CSR_API(BaseSrvNlsGetUserInfo)
|
|
{
|
|
NTSTATUS Status;
|
|
PBASE_NLS_GET_USER_INFO NlsMsg = &((PBASE_API_MESSAGE)ApiMessage)->Data.NlsGetUserInfo;
|
|
|
|
/* Make sure the buffer is valid and of the right size */
|
|
if ((CsrValidateMessageBuffer(ApiMessage, &NlsMsg->NlsUserInfo, NlsMsg->Size, TRUE)) &&
|
|
(NlsMsg->Size == sizeof(NLS_USER_INFO)))
|
|
{
|
|
/* Acquire the lock to prevent updates while we copy */
|
|
Status = RtlEnterCriticalSection(&NlsCacheCriticalSection);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Do the copy now, then drop the lock */
|
|
RtlCopyMemory(NlsMsg->NlsUserInfo, pNlsRegUserInfo, NlsMsg->Size);
|
|
DPRINT1("NLS Data copy complete\n");
|
|
RtlLeaveCriticalSection(&NlsCacheCriticalSection);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The data was invalid, bail out */
|
|
DPRINT1("NLS: Size of info is invalid: %lx vs %lx\n", NlsMsg->Size, sizeof(NLS_USER_INFO));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* All done */
|
|
return Status;
|
|
}
|
|
|
|
/* PUBLIC APIS ****************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
BaseSrvNlsLogon(DWORD Unknown)
|
|
{
|
|
DPRINT1("%s(%lu) not yet implemented\n", __FUNCTION__, Unknown);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
BaseSrvNlsUpdateRegistryCache(DWORD Unknown1,
|
|
DWORD Unknown2)
|
|
{
|
|
DPRINT1("%s(%lu, %lu) not yet implemented\n", __FUNCTION__, Unknown1, Unknown2);
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* EOF */
|