From fdceea406e62d278d82b1cf69311a537b6fa4b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Mon, 15 Apr 2013 19:46:07 +0000 Subject: [PATCH] [REACTOS] Here is all the deprecated code: the old CSR as well as the new one which wasn't included; the ReactOS-specific win32csr and the csr module of win32k. svn path=/trunk/; revision=58771 --- reactos/deprecated/csr.c | 91 + reactos/deprecated/csr.h | 20 + reactos/deprecated/csr/CMakeLists.txt | 16 + reactos/deprecated/csr/csr.rc | 4 + reactos/deprecated/csr/csrsrv/CMakeLists.txt | 30 + reactos/deprecated/csr/csrsrv/api.c | 1650 ++++++++++++ reactos/deprecated/csr/csrsrv/csrsrv.rc | 5 + reactos/deprecated/csr/csrsrv/csrsrv2.spec | 35 + reactos/deprecated/csr/csrsrv/init.c | 988 +++++++ reactos/deprecated/csr/csrsrv/process.c | 1342 ++++++++++ reactos/deprecated/csr/csrsrv/server.c | 687 +++++ reactos/deprecated/csr/csrsrv/session.c | 407 +++ reactos/deprecated/csr/csrsrv/srv.h | 395 +++ reactos/deprecated/csr/csrsrv/status.h | 111 + reactos/deprecated/csr/csrsrv/thread.c | 1056 ++++++++ reactos/deprecated/csr/csrsrv/wait.c | 449 ++++ reactos/deprecated/csr/main.c | 97 + reactos/deprecated/csrsrv/CMakeLists.txt | 31 + reactos/deprecated/csrsrv/api/process.c | 229 ++ reactos/deprecated/csrsrv/api/user.c | 41 + reactos/deprecated/csrsrv/api/wapi.c | 1400 ++++++++++ reactos/deprecated/csrsrv/csrsrv.rc | 5 + reactos/deprecated/csrsrv/csrsrv.spec | 36 + reactos/deprecated/csrsrv/include/api.h | 467 ++++ reactos/deprecated/csrsrv/include/csrplugin.h | 51 + reactos/deprecated/csrsrv/init.c | 1144 ++++++++ reactos/deprecated/csrsrv/procsup.c | 1240 +++++++++ reactos/deprecated/csrsrv/server.c | 698 +++++ reactos/deprecated/csrsrv/session.c | 568 ++++ reactos/deprecated/csrsrv/srv.h | 54 + reactos/deprecated/csrsrv/thredsup.c | 819 ++++++ reactos/deprecated/csrsrv/wait.c | 449 ++++ reactos/deprecated/win32csr/CMakeLists.txt | 36 + reactos/deprecated/win32csr/alias.c | 552 ++++ reactos/deprecated/win32csr/coninput.c | 651 +++++ reactos/deprecated/win32csr/conio.h | 237 ++ reactos/deprecated/win32csr/conoutput.c | 1457 ++++++++++ reactos/deprecated/win32csr/console.c | 832 ++++++ reactos/deprecated/win32csr/desktopbg.c | 27 + reactos/deprecated/win32csr/desktopbg.h | 20 + reactos/deprecated/win32csr/dllmain.c | 372 +++ reactos/deprecated/win32csr/exitros.c | 912 +++++++ reactos/deprecated/win32csr/file.c | 586 ++++ reactos/deprecated/win32csr/file.h | 28 + reactos/deprecated/win32csr/guiconsole.c | 2362 +++++++++++++++++ reactos/deprecated/win32csr/guiconsole.h | 18 + reactos/deprecated/win32csr/handle.c | 381 +++ reactos/deprecated/win32csr/harderror.c | 578 ++++ reactos/deprecated/win32csr/lang/bg-BG.rc | 64 + reactos/deprecated/win32csr/lang/cs-CZ.rc | 63 + reactos/deprecated/win32csr/lang/de-DE.rc | 58 + reactos/deprecated/win32csr/lang/el-GR.rc | 58 + reactos/deprecated/win32csr/lang/en-US.rc | 58 + reactos/deprecated/win32csr/lang/es-ES.rc | 63 + reactos/deprecated/win32csr/lang/fr-FR.rc | 60 + reactos/deprecated/win32csr/lang/he-IL.rc | 58 + reactos/deprecated/win32csr/lang/id-ID.rc | 58 + reactos/deprecated/win32csr/lang/it-IT.rc | 67 + reactos/deprecated/win32csr/lang/ja-JP.rc | 58 + reactos/deprecated/win32csr/lang/no-NO.rc | 58 + reactos/deprecated/win32csr/lang/pl-PL.rc | 66 + reactos/deprecated/win32csr/lang/pt-BR.rc | 60 + reactos/deprecated/win32csr/lang/ro-RO.rc | 65 + reactos/deprecated/win32csr/lang/ru-RU.rc | 58 + reactos/deprecated/win32csr/lang/sk-SK.rc | 63 + reactos/deprecated/win32csr/lang/sv-SE.rc | 65 + reactos/deprecated/win32csr/lang/tr-TR.rc | 78 + reactos/deprecated/win32csr/lang/uk-UA.rc | 66 + reactos/deprecated/win32csr/lang/zh-CN.rc | 58 + reactos/deprecated/win32csr/lang/zh-TW.rc | 58 + reactos/deprecated/win32csr/lineinput.c | 597 +++++ reactos/deprecated/win32csr/res/terminal.ico | Bin 0 -> 236566 bytes reactos/deprecated/win32csr/resource.h | 52 + reactos/deprecated/win32csr/rsrc.rc | 73 + reactos/deprecated/win32csr/tuiconsole.c | 487 ++++ reactos/deprecated/win32csr/tuiconsole.h | 14 + reactos/deprecated/win32csr/w32csr.h | 46 + reactos/deprecated/win32csr/win32csr.h | 78 + reactos/deprecated/win32csr/win32csr.rc | 12 + reactos/deprecated/win32csr/win32csr.spec | 1 + 80 files changed, 26384 insertions(+) create mode 100644 reactos/deprecated/csr.c create mode 100644 reactos/deprecated/csr.h create mode 100644 reactos/deprecated/csr/CMakeLists.txt create mode 100644 reactos/deprecated/csr/csr.rc create mode 100644 reactos/deprecated/csr/csrsrv/CMakeLists.txt create mode 100644 reactos/deprecated/csr/csrsrv/api.c create mode 100644 reactos/deprecated/csr/csrsrv/csrsrv.rc create mode 100644 reactos/deprecated/csr/csrsrv/csrsrv2.spec create mode 100644 reactos/deprecated/csr/csrsrv/init.c create mode 100644 reactos/deprecated/csr/csrsrv/process.c create mode 100644 reactos/deprecated/csr/csrsrv/server.c create mode 100644 reactos/deprecated/csr/csrsrv/session.c create mode 100644 reactos/deprecated/csr/csrsrv/srv.h create mode 100644 reactos/deprecated/csr/csrsrv/status.h create mode 100644 reactos/deprecated/csr/csrsrv/thread.c create mode 100644 reactos/deprecated/csr/csrsrv/wait.c create mode 100644 reactos/deprecated/csr/main.c create mode 100644 reactos/deprecated/csrsrv/CMakeLists.txt create mode 100644 reactos/deprecated/csrsrv/api/process.c create mode 100644 reactos/deprecated/csrsrv/api/user.c create mode 100644 reactos/deprecated/csrsrv/api/wapi.c create mode 100644 reactos/deprecated/csrsrv/csrsrv.rc create mode 100644 reactos/deprecated/csrsrv/csrsrv.spec create mode 100644 reactos/deprecated/csrsrv/include/api.h create mode 100644 reactos/deprecated/csrsrv/include/csrplugin.h create mode 100644 reactos/deprecated/csrsrv/init.c create mode 100644 reactos/deprecated/csrsrv/procsup.c create mode 100644 reactos/deprecated/csrsrv/server.c create mode 100644 reactos/deprecated/csrsrv/session.c create mode 100644 reactos/deprecated/csrsrv/srv.h create mode 100644 reactos/deprecated/csrsrv/thredsup.c create mode 100644 reactos/deprecated/csrsrv/wait.c create mode 100644 reactos/deprecated/win32csr/CMakeLists.txt create mode 100644 reactos/deprecated/win32csr/alias.c create mode 100644 reactos/deprecated/win32csr/coninput.c create mode 100644 reactos/deprecated/win32csr/conio.h create mode 100644 reactos/deprecated/win32csr/conoutput.c create mode 100644 reactos/deprecated/win32csr/console.c create mode 100644 reactos/deprecated/win32csr/desktopbg.c create mode 100644 reactos/deprecated/win32csr/desktopbg.h create mode 100644 reactos/deprecated/win32csr/dllmain.c create mode 100644 reactos/deprecated/win32csr/exitros.c create mode 100644 reactos/deprecated/win32csr/file.c create mode 100644 reactos/deprecated/win32csr/file.h create mode 100644 reactos/deprecated/win32csr/guiconsole.c create mode 100644 reactos/deprecated/win32csr/guiconsole.h create mode 100644 reactos/deprecated/win32csr/handle.c create mode 100644 reactos/deprecated/win32csr/harderror.c create mode 100644 reactos/deprecated/win32csr/lang/bg-BG.rc create mode 100644 reactos/deprecated/win32csr/lang/cs-CZ.rc create mode 100644 reactos/deprecated/win32csr/lang/de-DE.rc create mode 100644 reactos/deprecated/win32csr/lang/el-GR.rc create mode 100644 reactos/deprecated/win32csr/lang/en-US.rc create mode 100644 reactos/deprecated/win32csr/lang/es-ES.rc create mode 100644 reactos/deprecated/win32csr/lang/fr-FR.rc create mode 100644 reactos/deprecated/win32csr/lang/he-IL.rc create mode 100644 reactos/deprecated/win32csr/lang/id-ID.rc create mode 100644 reactos/deprecated/win32csr/lang/it-IT.rc create mode 100644 reactos/deprecated/win32csr/lang/ja-JP.rc create mode 100644 reactos/deprecated/win32csr/lang/no-NO.rc create mode 100644 reactos/deprecated/win32csr/lang/pl-PL.rc create mode 100644 reactos/deprecated/win32csr/lang/pt-BR.rc create mode 100644 reactos/deprecated/win32csr/lang/ro-RO.rc create mode 100644 reactos/deprecated/win32csr/lang/ru-RU.rc create mode 100644 reactos/deprecated/win32csr/lang/sk-SK.rc create mode 100644 reactos/deprecated/win32csr/lang/sv-SE.rc create mode 100644 reactos/deprecated/win32csr/lang/tr-TR.rc create mode 100644 reactos/deprecated/win32csr/lang/uk-UA.rc create mode 100644 reactos/deprecated/win32csr/lang/zh-CN.rc create mode 100644 reactos/deprecated/win32csr/lang/zh-TW.rc create mode 100644 reactos/deprecated/win32csr/lineinput.c create mode 100644 reactos/deprecated/win32csr/res/terminal.ico create mode 100644 reactos/deprecated/win32csr/resource.h create mode 100644 reactos/deprecated/win32csr/rsrc.rc create mode 100644 reactos/deprecated/win32csr/tuiconsole.c create mode 100644 reactos/deprecated/win32csr/tuiconsole.h create mode 100644 reactos/deprecated/win32csr/w32csr.h create mode 100644 reactos/deprecated/win32csr/win32csr.h create mode 100644 reactos/deprecated/win32csr/win32csr.rc create mode 100644 reactos/deprecated/win32csr/win32csr.spec diff --git a/reactos/deprecated/csr.c b/reactos/deprecated/csr.c new file mode 100644 index 00000000000..2b622e846a5 --- /dev/null +++ b/reactos/deprecated/csr.c @@ -0,0 +1,91 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * PURPOSE: Interface to csrss + * FILE: subsys/win32k/ntuser/csr.c + * PROGRAMER: Ge van Geldorp (ge@gse.nl) + */ + +#include + +static HANDLE WindowsApiPort = NULL; +PEPROCESS CsrProcess = NULL; + +NTSTATUS FASTCALL +CsrInit(void) +{ + NTSTATUS Status; + UNICODE_STRING PortName; + ULONG ConnectInfoLength; + SECURITY_QUALITY_OF_SERVICE Qos; + + RtlInitUnicodeString(&PortName, L"\\Windows\\ApiPort"); + ConnectInfoLength = 0; + Qos.Length = sizeof(Qos); + Qos.ImpersonationLevel = SecurityDelegation; + Qos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + Qos.EffectiveOnly = FALSE; + + Status = ZwConnectPort(&WindowsApiPort, + &PortName, + &Qos, + NULL, + NULL, + NULL, + NULL, + &ConnectInfoLength); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + CsrProcess = PsGetCurrentProcess(); + + return STATUS_SUCCESS; +} + + +NTSTATUS FASTCALL +co_CsrNotify(PCSR_API_MESSAGE Request) +{ + NTSTATUS Status; + PEPROCESS OldProcess; + + if (NULL == CsrProcess) + { + return STATUS_INVALID_PORT_HANDLE; + } + + Request->Header.u2.ZeroInit = 0; + Request->Header.u1.s1.DataLength = sizeof(CSR_API_MESSAGE) - sizeof(PORT_MESSAGE); + Request->Header.u1.s1.TotalLength = sizeof(CSR_API_MESSAGE); + + /* Switch to the process in which the WindowsApiPort handle is valid */ + OldProcess = PsGetCurrentProcess(); + if (CsrProcess != OldProcess) + { + KeAttachProcess(&CsrProcess->Pcb); + } + + UserLeaveCo(); + + Status = ZwRequestWaitReplyPort(WindowsApiPort, + &Request->Header, + &Request->Header); + + UserEnterCo(); + + if (CsrProcess != OldProcess) + { + KeDetachProcess(); + } + + if (NT_SUCCESS(Status)) + { + Status = Request->Status; + } + + return Status; +} + +/* EOF */ diff --git a/reactos/deprecated/csr.h b/reactos/deprecated/csr.h new file mode 100644 index 00000000000..5256c03b651 --- /dev/null +++ b/reactos/deprecated/csr.h @@ -0,0 +1,20 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * PURPOSE: Interface to csrss + * FILE: subsys/win32k/include/csr.h + * PROGRAMER: Ge van Geldorp (ge@gse.nl) + */ + +#pragma once + +extern PEPROCESS CsrProcess; + +NTSTATUS FASTCALL CsrInit(void); +NTSTATUS FASTCALL co_CsrNotify(PCSR_API_MESSAGE Request); +NTSTATUS FASTCALL CsrCloseHandle(HANDLE Handle); +NTSTATUS WINAPI CsrInsertObject(HANDLE ObjectHandle, + ACCESS_MASK DesiredAccess, + PHANDLE Handle); + +/* EOF */ diff --git a/reactos/deprecated/csr/CMakeLists.txt b/reactos/deprecated/csr/CMakeLists.txt new file mode 100644 index 00000000000..29ccac1a8da --- /dev/null +++ b/reactos/deprecated/csr/CMakeLists.txt @@ -0,0 +1,16 @@ + +include_directories( + include + ${REACTOS_SOURCE_DIR}/include/reactos/subsys + ${REACTOS_SOURCE_DIR}/include/reactos/drivers) + +add_executable(csrss2 main.c csr.rc) + +set_module_type(csrss2 nativecui) +target_link_libraries(csrss2 nt) +add_importlibs(csrss2 ntdll csrsrv2) +add_dependencies(csrss2 psdk bugcodes) +add_cd_file(TARGET csrss2 DESTINATION reactos/system32 FOR all) + +add_subdirectory(csrsrv) + diff --git a/reactos/deprecated/csr/csr.rc b/reactos/deprecated/csr/csr.rc new file mode 100644 index 00000000000..c5d32a1013d --- /dev/null +++ b/reactos/deprecated/csr/csr.rc @@ -0,0 +1,4 @@ +#define REACTOS_STR_FILE_DESCRIPTION "Client/Server Runtime Process\0" +#define REACTOS_STR_INTERNAL_NAME "csrss\0" +#define REACTOS_STR_ORIGINAL_FILENAME "csrss.exe\0" +#include diff --git a/reactos/deprecated/csr/csrsrv/CMakeLists.txt b/reactos/deprecated/csr/csrsrv/CMakeLists.txt new file mode 100644 index 00000000000..8c48af51816 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/CMakeLists.txt @@ -0,0 +1,30 @@ + +include_directories(${REACTOS_SOURCE_DIR}/subsystems/win32/csrss/include) +include_directories(${REACTOS_SOURCE_DIR}/include/reactos/subsys) + +spec2def(csrsrv2.dll csrsrv2.spec ADD_IMPORTLIB) + +list(APPEND SOURCE + api.c + init.c + process.c + server.c + session.c + thread.c + wait.c + csrsrv.rc + ${CMAKE_CURRENT_BINARY_DIR}/csrsrv2.def) + +add_library(csrsrv2 SHARED ${SOURCE}) + +target_link_libraries(csrsrv2 ${PSEH_LIB} smlib) + +set_module_type(csrsrv2 nativedll) + +add_importlibs(csrsrv2 ntdll) + +add_pch(csrsrv2 srv.h) + +add_dependencies(csrsrv2 psdk bugcodes) +add_cd_file(TARGET csrsrv2 DESTINATION reactos/system32 FOR all) + diff --git a/reactos/deprecated/csr/csrsrv/api.c b/reactos/deprecated/csr/csrsrv/api.c new file mode 100644 index 00000000000..e7257095f14 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/api.c @@ -0,0 +1,1650 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrsrv/api.c + * PURPOSE: CSR Server DLL API LPC Implementation + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ +BOOLEAN (*CsrClientThreadSetup)(VOID) = NULL; +ULONG CsrMaxApiRequestThreads; +UNICODE_STRING CsrSbApiPortName; +UNICODE_STRING CsrApiPortName; +HANDLE CsrSbApiPort; +HANDLE CsrApiPort; +PCSR_THREAD CsrSbApiRequestThreadPtr; +volatile LONG CsrpStaticThreadCount; +volatile LONG CsrpDynamicThreadTotal; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name CsrpCheckRequestThreads + * + * The CsrpCheckRequestThreads routine checks if there are no more threads + * to handle CSR API Requests, and creates a new thread if possible, to + * avoid starvation. + * + * @param None. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * if a new thread couldn't be created. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrpCheckRequestThreads(VOID) +{ + HANDLE hThread; + CLIENT_ID ClientId; + NTSTATUS Status; + + /* Decrease the count, and see if we're out */ + if (!(_InterlockedDecrement(&CsrpStaticThreadCount))) + { + /* Check if we've still got space for a Dynamic Thread */ + if (CsrpDynamicThreadTotal < CsrMaxApiRequestThreads) + { + /* Create a new dynamic thread */ + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + TRUE, + 0, + 0, + 0, + (PVOID)CsrApiRequestThread, + NULL, + &hThread, + &ClientId); + /* Check success */ + if (NT_SUCCESS(Status)) + { + /* Increase the thread counts */ + _InterlockedIncrement(&CsrpStaticThreadCount); + _InterlockedIncrement(&CsrpDynamicThreadTotal); + + /* Add a new server thread */ + if (CsrAddStaticServerThread(hThread, + &ClientId, + CsrThreadIsServerThread)) + { + /* Activate it */ + NtResumeThread(hThread,NULL); + } + else + { + /* Failed to create a new static thread */ + _InterlockedDecrement(&CsrpStaticThreadCount); + _InterlockedDecrement(&CsrpDynamicThreadTotal); + + /* Terminate it */ + NtTerminateThread(hThread,0); + NtClose(hThread); + + /* Return */ + return STATUS_UNSUCCESSFUL; + } + } + } + } + + /* Success */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSbApiPortInitialize + * + * The CsrSbApiPortInitialize routine initializes the LPC Port used for + * communications with the Session Manager (SM) and initializes the static + * thread that will handle connection requests and APIs. + * + * @param None + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSbApiPortInitialize(VOID) +{ + ULONG Size; + PSECURITY_DESCRIPTOR PortSd; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE hRequestThread; + CLIENT_ID ClientId; + + /* Calculate how much space we'll need for the Port Name */ + Size = CsrDirectoryName.Length + sizeof(SB_PORT_NAME) + sizeof(WCHAR); + + /* Create the buffer for it */ + CsrSbApiPortName.Buffer = RtlAllocateHeap(CsrHeap, 0, Size); + if (!CsrSbApiPortName.Buffer) return STATUS_NO_MEMORY; + + /* Setup the rest of the empty string */ + CsrSbApiPortName.Length = 0; + CsrSbApiPortName.MaximumLength = (USHORT)Size; + + /* Now append the full port name */ + RtlAppendUnicodeStringToString(&CsrSbApiPortName, &CsrDirectoryName); + RtlAppendUnicodeToString(&CsrSbApiPortName, UNICODE_PATH_SEP); + RtlAppendUnicodeToString(&CsrSbApiPortName, SB_PORT_NAME); + if (CsrDebug & 2) DPRINT1("CSRSS: Creating %wZ port and associated thread\n", &CsrSbApiPortName); + + /* Create Security Descriptor for this Port */ + Status = CsrCreateLocalSystemSD(&PortSd); + if (!NT_SUCCESS(Status)) return Status; + + /* Initialize the Attributes */ + InitializeObjectAttributes(&ObjectAttributes, + &CsrSbApiPortName, + 0, + NULL, + PortSd); + + /* Create the Port Object */ + Status = NtCreatePort(&CsrSbApiPort, + &ObjectAttributes, + sizeof(SB_CONNECTION_INFO), + sizeof(SB_API_MSG), + 32 * sizeof(SB_API_MSG)); + if (PortSd) RtlFreeHeap(CsrHeap, 0, PortSd); + + if (NT_SUCCESS(Status)) + { + /* Create the Thread to handle the API Requests */ + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + TRUE, + 0, + 0, + 0, + (PVOID)CsrSbApiRequestThread, + NULL, + &hRequestThread, + &ClientId); + if (NT_SUCCESS(Status)) + { + /* Add it as a Static Server Thread */ + CsrSbApiRequestThreadPtr = CsrAddStaticServerThread(hRequestThread, + &ClientId, + 0); + + /* Activate it */ + Status = NtResumeThread(hRequestThread, NULL); + } + } + + return Status; +} + +/*++ + * @name CsrApiPortInitialize + * + * The CsrApiPortInitialize routine initializes the LPC Port used for + * communications with the Client/Server Runtime (CSR) and initializes the + * static thread that will handle connection requests and APIs. + * + * @param None + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrApiPortInitialize(VOID) +{ + ULONG Size; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE hRequestEvent, hThread; + CLIENT_ID ClientId; + PLIST_ENTRY ListHead, NextEntry; + PCSR_THREAD ServerThread; + + /* Calculate how much space we'll need for the Port Name */ + Size = CsrDirectoryName.Length + sizeof(CSR_PORT_NAME) + sizeof(WCHAR); + + /* Create the buffer for it */ + CsrApiPortName.Buffer = RtlAllocateHeap(CsrHeap, 0, Size); + if (!CsrApiPortName.Buffer) return STATUS_NO_MEMORY; + + /* Setup the rest of the empty string */ + CsrApiPortName.Length = 0; + CsrApiPortName.MaximumLength = (USHORT)Size; + RtlAppendUnicodeStringToString(&CsrApiPortName, &CsrDirectoryName); + RtlAppendUnicodeToString(&CsrApiPortName, UNICODE_PATH_SEP); + RtlAppendUnicodeToString(&CsrApiPortName, CSR_PORT_NAME); + if (CsrDebug & 1) + { + DPRINT1("CSRSS: Creating %wZ port and associated threads\n", &CsrApiPortName); + DPRINT1("CSRSS: sizeof( CONNECTINFO ) == %ld sizeof( API_MSG ) == %ld\n", + sizeof(CSR_CONNECTION_INFO), sizeof(CSR_API_MESSAGE)); + } + + /* FIXME: Create a Security Descriptor */ + + /* Initialize the Attributes */ + InitializeObjectAttributes(&ObjectAttributes, + &CsrApiPortName, + 0, + NULL, + NULL /* FIXME*/); + + /* Create the Port Object */ + Status = NtCreatePort(&CsrApiPort, + &ObjectAttributes, + sizeof(CSR_CONNECTION_INFO), + sizeof(CSR_API_MESSAGE), + 16 * PAGE_SIZE); + if (NT_SUCCESS(Status)) + { + /* Create the event the Port Thread will use */ + Status = NtCreateEvent(&hRequestEvent, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE); + if (NT_SUCCESS(Status)) + { + /* Create the Request Thread */ + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + TRUE, + 0, + 0, + 0, + (PVOID)CsrApiRequestThread, + (PVOID)hRequestEvent, + &hThread, + &ClientId); + if (NT_SUCCESS(Status)) + { + /* Add this as a static thread to CSRSRV */ + CsrAddStaticServerThread(hThread, &ClientId, CsrThreadIsServerThread); + + /* Get the Thread List Pointers */ + ListHead = &CsrRootProcess->ThreadList; + NextEntry = ListHead->Flink; + + /* Start looping the list */ + while (NextEntry != ListHead) + { + /* Get the Thread */ + ServerThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link); + + /* Start it up */ + Status = NtResumeThread(ServerThread->ThreadHandle, NULL); + + /* Is this a Server Thread? */ + if (ServerThread->Flags & CsrThreadIsServerThread) + { + /* If so, then wait for it to initialize */ + Status = NtWaitForSingleObject(hRequestEvent, FALSE, NULL); + ASSERT(NT_SUCCESS(Status)); + } + + /* Next thread */ + NextEntry = NextEntry->Flink; + } + + /* We don't need this anymore */ + NtClose(hRequestEvent); + } + } + } + + /* Return */ + return Status; +} + +/*++ + * @name CsrApiRequestThread + * + * The CsrApiRequestThread routine handles incoming messages or connection + * requests on the CSR API LPC Port. + * + * @param Parameter + * System-default user-defined parameter. Unused. + * + * @return The thread exit code, if the thread is terminated. + * + * @remarks Before listening on the port, the routine will first attempt + * to connect to the user subsystem. + * + *--*/ +NTSTATUS +NTAPI +CsrApiRequestThread(IN PVOID Parameter) +{ + PTEB Teb = NtCurrentTeb(); + LARGE_INTEGER TimeOut; + PCSR_THREAD CurrentThread, CsrThread; + NTSTATUS Status; + PCSR_API_MESSAGE ReplyMsg; + CSR_API_MESSAGE ReceiveMsg; + PCSR_PROCESS CsrProcess; + PHARDERROR_MSG HardErrorMsg; + PVOID PortContext; + PCSR_SERVER_DLL ServerDll; + PCLIENT_DIED_MSG ClientDiedMsg; + PDBGKM_MSG DebugMessage; + ULONG ServerId, ApiId, Reply, MessageType, i; + HANDLE ReplyPort; + + /* Setup LPC loop port and message */ + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + + /* Connect to user32 */ + while (!CsrConnectToUser()) + { + /* Set up the timeout for the connect (30 seconds) */ + TimeOut.QuadPart = -30 * 1000 * 1000 * 10; + + /* Keep trying until we get a response */ + Teb->Win32ClientInfo[0] = 0; + NtDelayExecution(FALSE, &TimeOut); + } + + /* Get our thread */ + CurrentThread = Teb->CsrClientThread; + + /* If we got an event... */ + if (Parameter) + { + /* Set it, to let stuff waiting on us load */ + Status = NtSetEvent((HANDLE)Parameter, NULL); + ASSERT(NT_SUCCESS(Status)); + + /* Increase the Thread Counts */ + _InterlockedIncrement(&CsrpStaticThreadCount); + _InterlockedIncrement(&CsrpDynamicThreadTotal); + } + + /* Now start the loop */ + while (TRUE) + { + /* Make sure the real CID is set */ + Teb->RealClientId = Teb->ClientId; + + /* Debug check */ + if (Teb->CountOfOwnedCriticalSections) + { + DPRINT1("CSRSRV: FATAL ERROR. CsrThread is Idle while holding %lu critical sections\n", + Teb->CountOfOwnedCriticalSections); + DPRINT1("CSRSRV: Last Receive Message %lx ReplyMessage %lx\n", + &ReceiveMsg, ReplyMsg); + DbgBreakPoint(); + } + + /* Wait for a message to come through */ + Status = NtReplyWaitReceivePort(ReplyPort, + &PortContext, + &ReplyMsg->Header, + &ReceiveMsg.Header); + + /* Check if we didn't get success */ + if (Status != STATUS_SUCCESS) + { + /* Was it a failure or another success code? */ + if (!NT_SUCCESS(Status)) + { + /* Check for specific status cases */ + if ((Status != STATUS_INVALID_CID) && + (Status != STATUS_UNSUCCESSFUL) && + ((Status == STATUS_INVALID_HANDLE) || (ReplyPort == CsrApiPort))) + { + /* Notify the debugger */ + DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status); + DPRINT1("CSRSS: ReplyPortHandle %lx CsrApiPort %lx\n", ReplyPort, CsrApiPort); + } + + /* We failed big time, so start out fresh */ + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + continue; + } + else + { + /* A bizare "success" code, just try again */ + DPRINT1("NtReplyWaitReceivePort returned \"success\" status 0x%x\n", Status); + continue; + } + } + + /* Use whatever Client ID we got */ + Teb->RealClientId = ReceiveMsg.Header.ClientId; + + /* Get the Message Type */ + MessageType = ReceiveMsg.Header.u2.s2.Type; + + /* Handle connection requests */ + if (MessageType == LPC_CONNECTION_REQUEST) + { + /* Handle the Connection Request */ + CsrApiHandleConnectionRequest(&ReceiveMsg); + ReplyPort = CsrApiPort; + ReplyMsg = NULL; + continue; + } + + /* It's some other kind of request. Get the lock for the lookup */ + CsrAcquireProcessLock(); + + /* Now do the lookup to get the CSR_THREAD */ + CsrThread = CsrLocateThreadByClientId(&CsrProcess, + &ReceiveMsg.Header.ClientId); + + /* Did we find a thread? */ + if (!CsrThread) + { + /* This wasn't a CSR Thread, release lock */ + CsrReleaseProcessLock(); + + /* If this was an exception, handle it */ + if (MessageType == LPC_EXCEPTION) + { + ReplyMsg = &ReceiveMsg; + ReplyPort = CsrApiPort; + ReplyMsg->Status = DBG_CONTINUE; + } + else if (MessageType == LPC_PORT_CLOSED || + MessageType == LPC_CLIENT_DIED) + { + /* The Client or Port are gone, loop again */ + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + } + else if (MessageType == LPC_ERROR_EVENT) + { + /* If it's a hard error, handle this too */ + HardErrorMsg = (PHARDERROR_MSG)&ReceiveMsg; + + /* Default it to unhandled */ + HardErrorMsg->Response = ResponseNotHandled; + + /* Check if there are free api threads */ + CsrpCheckRequestThreads(); + if (CsrpStaticThreadCount) + { + /* Loop every Server DLL */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Check if it's valid and if it has a Hard Error Callback */ + if ((ServerDll) && (ServerDll->HardErrorCallback)) + { + /* Call it */ + ServerDll->HardErrorCallback(NULL, HardErrorMsg); + + /* If it's handled, get out of here */ + if (HardErrorMsg->Response != ResponseNotHandled) break; + } + } + } + + /* Increase the thread count */ + _InterlockedIncrement(&CsrpStaticThreadCount); + + /* If the response was 0xFFFFFFFF, we'll ignore it */ + if (HardErrorMsg->Response == 0xFFFFFFFF) + { + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + } + else + { + ReplyMsg = &ReceiveMsg; + } + } + else if (MessageType == LPC_REQUEST) + { + /* This is an API Message coming from a non-CSR Thread */ + ReplyMsg = &ReceiveMsg; + ReplyPort = CsrApiPort; + ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION; + } + else if (MessageType == LPC_DATAGRAM) + { + /* This is an API call, get the Server ID */ + ServerId = CSR_SERVER_ID_FROM_OPCODE(ReceiveMsg.Opcode); + + /* Make sure that the ID is within limits, and the Server DLL loaded */ + ServerDll = NULL; + if ((ServerId >= CSR_SERVER_DLL_MAX) || + (!(ServerDll = CsrLoadedServerDll[ServerId]))) + { + /* We are beyond the Maximum Server ID */ + DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n", + ServerId, ServerDll); + DbgBreakPoint(); + ReplyPort = CsrApiPort; + ReplyMsg = NULL; + continue; + } + + /* Get the API ID */ + ApiId = CSR_API_ID_FROM_OPCODE(ReceiveMsg.Opcode); + + /* Normalize it with our Base ID */ + ApiId -= ServerDll->ApiBase; + + /* Make sure that the ID is within limits, and the entry exists */ + if (ApiId >= ServerDll->HighestApiSupported) + { + /* We are beyond the Maximum API ID, or it doesn't exist */ + DPRINT1("CSRSS: %lx is invalid ApiTableIndex for %Z\n", + CSR_API_ID_FROM_OPCODE(ReceiveMsg.Opcode), + &ServerDll->Name); + ReplyPort = CsrApiPort; + ReplyMsg = NULL; + continue; + } + + if (CsrDebug & 2) + { + DPRINT1("[%02x] CSRSS: [%02x,%02x] - %s Api called from %08x\n", + Teb->ClientId.UniqueThread, + ReceiveMsg.Header.ClientId.UniqueProcess, + ReceiveMsg.Header.ClientId.UniqueThread, + ServerDll->NameTable[ApiId], + NULL); + } + + /* Assume success */ + ReceiveMsg.Status = STATUS_SUCCESS; + + /* Validation complete, start SEH */ + _SEH2_TRY + { + /* Make sure we have enough threads */ + CsrpCheckRequestThreads(); + + /* Call the API and get the result */ + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + ServerDll->DispatchTable[ApiId](&ReceiveMsg, &Reply); + + /* Increase the static thread count */ + _InterlockedIncrement(&CsrpStaticThreadCount); + } + _SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation())) + { + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + } + _SEH2_END; + } + else + { + /* Some other ignored message type */ + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + } + + /* Keep going */ + continue; + } + + /* We have a valid thread, was this an LPC Request? */ + if (MessageType != LPC_REQUEST) + { + /* It's not an API, check if the client died */ + if (MessageType == LPC_CLIENT_DIED) + { + /* Get the information and check if it matches our thread */ + ClientDiedMsg = (PCLIENT_DIED_MSG)&ReceiveMsg; + if (ClientDiedMsg->CreateTime.QuadPart == CsrThread->CreateTime.QuadPart) + { + /* Reference the thread */ + CsrLockedReferenceThread(CsrThread); + + /* Destroy the thread in the API Message */ + CsrDestroyThread(&ReceiveMsg.Header.ClientId); + + /* Check if the thread was actually ourselves */ + if (CsrProcess->ThreadCount == 1) + { + /* Kill the process manually here */ + CsrDestroyProcess(&CsrThread->ClientId, 0); + } + + /* Remove our extra reference */ + CsrLockedDereferenceThread(CsrThread); + } + + /* Release the lock and keep looping */ + CsrReleaseProcessLock(); + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + continue; + } + + /* Reference the thread and release the lock */ + CsrLockedReferenceThread(CsrThread); + CsrReleaseProcessLock(); + + /* Check if this was an exception */ + if (MessageType == LPC_EXCEPTION) + { + /* Kill the process */ + NtTerminateProcess(CsrProcess->ProcessHandle, STATUS_ABANDONED); + + /* Destroy it from CSR */ + CsrDestroyProcess(&ReceiveMsg.Header.ClientId, STATUS_ABANDONED); + + /* Return a Debug Message */ + DebugMessage = (PDBGKM_MSG)&ReceiveMsg; + DebugMessage->ReturnedStatus = DBG_CONTINUE; + ReplyMsg = &ReceiveMsg; + ReplyPort = CsrApiPort; + + /* Remove our extra reference */ + CsrDereferenceThread(CsrThread); + } + else if (MessageType == LPC_ERROR_EVENT) + { + /* If it's a hard error, handle this too */ + HardErrorMsg = (PHARDERROR_MSG)&ReceiveMsg; + + /* Default it to unhandled */ + HardErrorMsg->Response = ResponseNotHandled; + + /* Check if there are free api threads */ + CsrpCheckRequestThreads(); + if (CsrpStaticThreadCount) + { + /* Loop every Server DLL */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Check if it's valid and if it has a Hard Error Callback */ + if ((ServerDll) && (ServerDll->HardErrorCallback)) + { + /* Call it */ + ServerDll->HardErrorCallback(CsrThread, HardErrorMsg); + + /* If it's handled, get out of here */ + if (HardErrorMsg->Response != ResponseNotHandled) break; + } + } + } + + /* Increase the thread count */ + _InterlockedIncrement(&CsrpStaticThreadCount); + + /* If the response was 0xFFFFFFFF, we'll ignore it */ + if (HardErrorMsg->Response == 0xFFFFFFFF) + { + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + } + else + { + CsrDereferenceThread(CsrThread); + ReplyMsg = &ReceiveMsg; + ReplyPort = CsrApiPort; + } + } + else + { + /* Something else */ + CsrDereferenceThread(CsrThread); + ReplyMsg = NULL; + } + + /* Keep looping */ + continue; + } + + /* We got an API Request */ + CsrLockedReferenceThread(CsrThread); + CsrReleaseProcessLock(); + + /* This is an API call, get the Server ID */ + ServerId = CSR_SERVER_ID_FROM_OPCODE(ReceiveMsg.Opcode); + + /* Make sure that the ID is within limits, and the Server DLL loaded */ + ServerDll = NULL; + if ((ServerId >= CSR_SERVER_DLL_MAX) || + (!(ServerDll = CsrLoadedServerDll[ServerId]))) + { + /* We are beyond the Maximum Server ID */ + DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n", + ServerId, ServerDll); + DbgBreakPoint(); + + ReplyPort = CsrApiPort; + ReplyMsg = &ReceiveMsg; + ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION; + CsrDereferenceThread(CsrThread); + continue; + } + + /* Get the API ID */ + ApiId = CSR_API_ID_FROM_OPCODE(ReceiveMsg.Opcode); + + /* Normalize it with our Base ID */ + ApiId -= ServerDll->ApiBase; + + /* Make sure that the ID is within limits, and the entry exists */ + if (ApiId >= ServerDll->HighestApiSupported) + { + /* We are beyond the Maximum API ID, or it doesn't exist */ + DPRINT1("CSRSS: %lx is invalid ApiTableIndex for %Z\n", + CSR_API_ID_FROM_OPCODE(ReceiveMsg.Opcode), + &ServerDll->Name); + + ReplyPort = CsrApiPort; + ReplyMsg = &ReceiveMsg; + ReplyMsg->Status = STATUS_ILLEGAL_FUNCTION; + CsrDereferenceThread(CsrThread); + continue; + } + + if (CsrDebug & 2) + { + DPRINT1("[%02x] CSRSS: [%02x,%02x] - %s Api called from %08x\n", + Teb->ClientId.UniqueThread, + ReceiveMsg.Header.ClientId.UniqueProcess, + ReceiveMsg.Header.ClientId.UniqueThread, + ServerDll->NameTable[ApiId], + CsrThread); + } + + /* Assume success */ + ReplyMsg = &ReceiveMsg; + ReceiveMsg.Status = STATUS_SUCCESS; + + /* Now we reply to a particular client */ + ReplyPort = CsrThread->Process->ClientPort; + + /* Check if there's a capture buffer */ + if (ReceiveMsg.CsrCaptureData) + { + /* Capture the arguments */ + if (!CsrCaptureArguments(CsrThread, &ReceiveMsg)) + { + /* Ignore this message if we failed to get the arguments */ + CsrDereferenceThread(CsrThread); + continue; + } + } + + /* Validation complete, start SEH */ + _SEH2_TRY + { + /* Make sure we have enough threads */ + CsrpCheckRequestThreads(); + + Teb->CsrClientThread = CsrThread; + + /* Call the API and get the result */ + Reply = 0; + ServerDll->DispatchTable[ApiId](&ReceiveMsg, &Reply); + + /* Increase the static thread count */ + _InterlockedIncrement(&CsrpStaticThreadCount); + + Teb->CsrClientThread = CurrentThread; + + if (Reply == 3) + { + ReplyMsg = NULL; + if (ReceiveMsg.CsrCaptureData) + { + CsrReleaseCapturedArguments(&ReceiveMsg); + } + CsrDereferenceThread(CsrThread); + ReplyPort = CsrApiPort; + } + else if (Reply == 2) + { + NtReplyPort(ReplyPort, &ReplyMsg->Header); + ReplyPort = CsrApiPort; + ReplyMsg = NULL; + CsrDereferenceThread(CsrThread); + } + else if (Reply == 1) + { + ReplyPort = CsrApiPort; + ReplyMsg = NULL; + } + else + { + if (ReceiveMsg.CsrCaptureData) + { + CsrReleaseCapturedArguments(&ReceiveMsg); + } + CsrDereferenceThread(CsrThread); + } + } + _SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation())) + { + ReplyMsg = NULL; + ReplyPort = CsrApiPort; + } + _SEH2_END; + } + + /* We're out of the loop for some reason, terminate! */ + NtTerminateThread(NtCurrentThread(), Status); + return Status; +} + +/*++ + * @name CsrApiHandleConnectionRequest + * + * The CsrApiHandleConnectionRequest routine handles and accepts a new + * connection request to the CSR API LPC Port. + * + * @param ApiMessage + * Pointer to the incoming CSR API Message which contains the + * connection request. + * + * @return STATUS_SUCCESS in case of success, or status code which caused + * the routine to error. + * + * @remarks This routine is responsible for attaching the Shared Section to + * new clients connecting to CSR. + * + *--*/ +NTSTATUS +NTAPI +CsrApiHandleConnectionRequest(IN PCSR_API_MESSAGE ApiMessage) +{ + PCSR_THREAD CsrThread = NULL; + PCSR_PROCESS CsrProcess = NULL; + NTSTATUS Status = STATUS_SUCCESS; + PCSR_CONNECTION_INFO ConnectInfo = &ApiMessage->ConnectionInfo; + BOOLEAN AllowConnection = FALSE; + REMOTE_PORT_VIEW RemotePortView; + HANDLE hPort; + + /* Acquire the Process Lock */ + CsrAcquireProcessLock(); + + /* Lookup the CSR Thread */ + CsrThread = CsrLocateThreadByClientId(NULL, &ApiMessage->Header.ClientId); + + /* Check if we have a thread */ + if (CsrThread) + { + /* Get the Process */ + CsrProcess = CsrThread->Process; + + /* Make sure we have a Process as well */ + if (CsrProcess) + { + /* Reference the Process */ + CsrLockedReferenceProcess(CsrThread->Process); + + /* Release the lock */ + CsrReleaseProcessLock(); + + /* Duplicate the Object Directory */ + Status = NtDuplicateObject(NtCurrentProcess(), + CsrObjectDirectory, + CsrProcess->ProcessHandle, + &ConnectInfo->ObjectDirectory, + 0, + 0, + DUPLICATE_SAME_ACCESS | + DUPLICATE_SAME_ATTRIBUTES); + + /* Acquire the lock */ + CsrAcquireProcessLock(); + + /* Check for success */ + if (NT_SUCCESS(Status)) + { + /* Attach the Shared Section */ + Status = CsrSrvAttachSharedSection(CsrProcess, ConnectInfo); + + /* Check how this went */ + if (NT_SUCCESS(Status)) AllowConnection = TRUE; + } + + /* Dereference the project */ + CsrLockedDereferenceProcess(CsrProcess); + } + } + + /* Release the lock */ + CsrReleaseProcessLock(); + + /* Setup the Port View Structure */ + RemotePortView.Length = sizeof(REMOTE_PORT_VIEW); + RemotePortView.ViewSize = 0; + RemotePortView.ViewBase = NULL; + + /* Save the Process ID */ + ConnectInfo->ProcessId = NtCurrentTeb()->ClientId.UniqueProcess; + + /* Accept the Connection */ + Status = NtAcceptConnectPort(&hPort, + AllowConnection ? UlongToPtr(CsrProcess->SequenceNumber) : 0, + &ApiMessage->Header, + AllowConnection, + NULL, + &RemotePortView); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtAcceptConnectPort - failed. Status == %X\n", Status); + } + else if (AllowConnection) + { + if (CsrDebug & 2) + { + DPRINT1("CSRSS: ClientId: %lx.%lx has ClientView: Base=%p, Size=%lx\n", + ApiMessage->Header.ClientId.UniqueProcess, + ApiMessage->Header.ClientId.UniqueThread, + RemotePortView.ViewBase, + RemotePortView.ViewSize); + } + + /* Set some Port Data in the Process */ + CsrProcess->ClientPort = hPort; + CsrProcess->ClientViewBase = (ULONG_PTR)RemotePortView.ViewBase; + CsrProcess->ClientViewBounds = (ULONG_PTR)((ULONG_PTR)RemotePortView.ViewBase + + (ULONG_PTR)RemotePortView.ViewSize); + + /* Complete the connection */ + Status = NtCompleteConnectPort(hPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCompleteConnectPort - failed. Status == %X\n", Status); + } + } + else + { + DPRINT1("CSRSS: Rejecting Connection Request from ClientId: %lx.%lx\n", + ApiMessage->Header.ClientId.UniqueProcess, + ApiMessage->Header.ClientId.UniqueThread); + } + + /* Return status to caller */ + return Status; +} + +/*++ + * @name CsrSbApiRequestThread + * + * The CsrSbApiRequestThread routine handles incoming messages or connection + * requests on the SM API LPC Port. + * + * @param Parameter + * System-default user-defined parameter. Unused. + * + * @return The thread exit code, if the thread is terminated. + * + * @remarks Before listening on the port, the routine will first attempt + * to connect to the user subsystem. + * + *--*/ +VOID +NTAPI +CsrSbApiRequestThread(IN PVOID Parameter) +{ + NTSTATUS Status; + SB_API_MSG ReceiveMsg; + PSB_API_MSG ReplyMsg = NULL; + PVOID PortContext; + ULONG MessageType; + + /* Start the loop */ + while (TRUE) + { + /* Wait for a message to come in */ + Status = NtReplyWaitReceivePort(CsrSbApiPort, + &PortContext, + &ReplyMsg->h, + &ReceiveMsg.h); + + /* Check if we didn't get success */ + if (Status != STATUS_SUCCESS) + { + /* If we only got a warning, keep going */ + if (NT_SUCCESS(Status)) continue; + + /* We failed big time, so start out fresh */ + ReplyMsg = NULL; + DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status); + continue; + } + + /* Save the message type */ + MessageType = ReceiveMsg.h.u2.s2.Type; + + /* Check if this is a connection request */ + if (MessageType == LPC_CONNECTION_REQUEST) + { + /* Handle connection request */ + CsrSbApiHandleConnectionRequest(&ReceiveMsg); + + /* Start over */ + ReplyMsg = NULL; + continue; + } + + /* Check if the port died */ + if (MessageType == LPC_PORT_CLOSED) + { + /* Close the handle if we have one */ + if (PortContext) NtClose((HANDLE)PortContext); + + /* Client died, start over */ + ReplyMsg = NULL; + continue; + } + else if (MessageType == LPC_CLIENT_DIED) + { + /* Client died, start over */ + ReplyMsg = NULL; + continue; + } + + /* + * It's an API Message, check if it's within limits. If it's not, the + * NT Behaviour is to set this to the Maximum API. + */ + if (ReceiveMsg.ApiNumber > SbpMaxApiNumber) + { + ReceiveMsg.ApiNumber = SbpMaxApiNumber; + DPRINT1("CSRSS: %lx is invalid Sb ApiNumber\n", ReceiveMsg.ApiNumber); + } + + /* Reuse the message */ + ReplyMsg = &ReceiveMsg; + + /* Make sure that the message is supported */ + if (ReceiveMsg.ApiNumber < SbpMaxApiNumber) + { + /* Call the API */ + if (!CsrServerSbApiDispatch[ReceiveMsg.ApiNumber](&ReceiveMsg)) + { + /* It failed, so return nothing */ + ReplyMsg = NULL; + } + } + else + { + /* We don't support this API Number */ + ReplyMsg->ReturnValue = STATUS_NOT_IMPLEMENTED; + } + } +} + +/*++ + * @name CsrSbApiHandleConnectionRequest + * + * The CsrSbApiHandleConnectionRequest routine handles and accepts a new + * connection request to the SM API LPC Port. + * + * @param ApiMessage + * Pointer to the incoming CSR API Message which contains the + * connection request. + * + * @return STATUS_SUCCESS in case of success, or status code which caused + * the routine to error. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message) +{ + NTSTATUS Status; + REMOTE_PORT_VIEW RemotePortView; + HANDLE hPort; + + /* Set the Port View Structure Length */ + RemotePortView.Length = sizeof(REMOTE_PORT_VIEW); + + /* Accept the connection */ + Status = NtAcceptConnectPort(&hPort, + NULL, + (PPORT_MESSAGE)Message, + TRUE, + NULL, + &RemotePortView); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: Sb Accept Connection failed %lx\n", Status); + return Status; + } + + /* Complete the Connection */ + Status = NtCompleteConnectPort(hPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: Sb Complete Connection failed %lx\n",Status); + } + + /* Return status */ + return Status; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + +/*++ + * @name CsrCallServerFromServer + * @implemented NT4 + * + * The CsrCallServerFromServer routine calls a CSR API from within a server. + * It avoids using LPC messages since the request isn't coming from a client. + * + * @param ReceiveMsg + * Pointer to the CSR API Message to send to the server. + * + * @param ReplyMsg + * Pointer to the CSR API Message to receive from the server. + * + * @return STATUS_SUCCESS in case of success, STATUS_ILLEGAL_FUNCTION + * if the opcode is invalid, or STATUS_ACCESS_VIOLATION if there + * was a problem executing the API. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCallServerFromServer(PCSR_API_MESSAGE ReceiveMsg, + PCSR_API_MESSAGE ReplyMsg) +{ + ULONG ServerId; + PCSR_SERVER_DLL ServerDll; + ULONG ApiId; + ULONG Reply; + NTSTATUS Status; + + /* Get the Server ID */ + ServerId = CSR_SERVER_ID_FROM_OPCODE(ReceiveMsg->Opcode); + + /* Make sure that the ID is within limits, and the Server DLL loaded */ + if ((ServerId >= CSR_SERVER_DLL_MAX) || + (!(ServerDll = CsrLoadedServerDll[ServerId]))) + { + /* We are beyond the Maximum Server ID */ + DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n", ServerId, ServerDll); + ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION; + return STATUS_ILLEGAL_FUNCTION; + } + else + { + /* Get the API ID */ + ApiId = CSR_API_ID_FROM_OPCODE(ReceiveMsg->Opcode); + + /* Normalize it with our Base ID */ + ApiId -= ServerDll->ApiBase; + + /* Make sure that the ID is within limits, and the entry exists */ + if ((ApiId >= ServerDll->HighestApiSupported) || + ((ServerDll->ValidTable) && !(ServerDll->ValidTable[ApiId]))) + { + /* We are beyond the Maximum API ID, or it doesn't exist */ + DPRINT1("CSRSS: %lx (%s) is invalid ApiTableIndex for %Z or is an " + "invalid API to call from the server.\n", + ServerDll->ValidTable[ApiId], + ((ServerDll->NameTable) && (ServerDll->NameTable[ApiId])) ? + ServerDll->NameTable[ApiId] : "*** UNKNOWN ***", &ServerDll->Name); + DbgBreakPoint(); + ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION; + return STATUS_ILLEGAL_FUNCTION; + } + } + + if (CsrDebug & 2) + { + DPRINT1("CSRSS: %s Api Request received from server process\n", + ServerDll->NameTable[ApiId]); + } + + /* Validation complete, start SEH */ + _SEH2_TRY + { + /* Call the API and get the result */ + Status = ServerDll->DispatchTable[ApiId](ReceiveMsg, &Reply); + + /* Return the result, no matter what it is */ + ReplyMsg->Status = Status; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* If we got an exception, return access violation */ + ReplyMsg->Status = STATUS_ACCESS_VIOLATION; + } + _SEH2_END; + + /* Return success */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrConnectToUser + * @implemented NT4 + * + * The CsrConnectToUser connects to the User subsystem. + * + * @param None + * + * @return A pointer to the CSR Thread + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrConnectToUser(VOID) +{ + NTSTATUS Status; + ANSI_STRING DllName; + UNICODE_STRING TempName; + HANDLE hUser32; + STRING StartupName; + PTEB Teb = NtCurrentTeb(); + PCSR_THREAD CsrThread; + BOOLEAN Connected; + + /* Check if we didn't already find it */ + if (!CsrClientThreadSetup) + { + /* Get the DLL Handle for user32.dll */ + RtlInitAnsiString(&DllName, "user32"); + RtlAnsiStringToUnicodeString(&TempName, &DllName, TRUE); + Status = LdrGetDllHandle(NULL, + NULL, + &TempName, + &hUser32); + RtlFreeUnicodeString(&TempName); + + /* If we got teh handle, get the Client Thread Startup Entrypoint */ + if (NT_SUCCESS(Status)) + { + RtlInitAnsiString(&StartupName,"ClientThreadSetup"); + Status = LdrGetProcedureAddress(hUser32, + &StartupName, + 0, + (PVOID)&CsrClientThreadSetup); + } + } + + /* Connect to user32 */ + _SEH2_TRY + { + Connected = CsrClientThreadSetup(); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Connected = FALSE; + } _SEH2_END; + + if (!Connected) + { + DPRINT1("CSRSS: CsrConnectToUser failed\n"); + return NULL; + } + + /* Save pointer to this thread in TEB */ + CsrAcquireProcessLock(); + CsrThread = CsrLocateThreadInProcess(NULL, &Teb->ClientId); + CsrReleaseProcessLock(); + if (CsrThread) Teb->CsrClientThread = CsrThread; + + /* Return it */ + return CsrThread; +} + +/*++ + * @name CsrQueryApiPort + * @implemented NT4 + * + * The CsrQueryApiPort routine returns a handle to the CSR API LPC port. + * + * @param None. + * + * @return A handle to the port. + * + * @remarks None. + * + *--*/ +HANDLE +NTAPI +CsrQueryApiPort(VOID) +{ + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + return CsrApiPort; +} + +/*++ + * @name CsrCaptureArguments + * @implemented NT5.1 + * + * The CsrCaptureArguments routine validates a CSR Capture Buffer and + * re-captures it into a server CSR Capture Buffer. + * + * @param CsrThread + * Pointer to the CSR Thread performing the validation. + * + * @param ApiMessage + * Pointer to the CSR API Message containing the Capture Buffer + * that needs to be validated. + * + * @return TRUE if validation succeeded, FALSE otherwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrCaptureArguments(IN PCSR_THREAD CsrThread, + IN PCSR_API_MESSAGE ApiMessage) +{ + PCSR_CAPTURE_BUFFER LocalCaptureBuffer = NULL, RemoteCaptureBuffer = NULL; + ULONG LocalLength = 0, PointerCount = 0; + SIZE_T BufferDistance = 0; + ULONG_PTR **PointerOffsets = NULL, *CurrentPointer = NULL; + + /* Use SEH to make sure this is valid */ + _SEH2_TRY + { + /* Get the buffer we got from whoever called NTDLL */ + LocalCaptureBuffer = ApiMessage->CsrCaptureData; + LocalLength = LocalCaptureBuffer->Size; + + /* Now check if the buffer is inside our mapped section */ + if (((ULONG_PTR)LocalCaptureBuffer < CsrThread->Process->ClientViewBase) || + (((ULONG_PTR)LocalCaptureBuffer + LocalLength) >= CsrThread->Process->ClientViewBounds)) + { + /* Return failure */ + DPRINT1("*** CSRSS: CaptureBuffer outside of ClientView\n"); + ApiMessage->Status = STATUS_INVALID_PARAMETER; + _SEH2_YIELD(return FALSE); + } + + /* Check if the Length is valid */ + if (((LocalCaptureBuffer->PointerCount * 4 + sizeof(CSR_CAPTURE_BUFFER)) > + LocalLength) ||(LocalLength > MAXWORD)) + { + /* Return failure */ + DPRINT1("*** CSRSS: CaptureBuffer %p has bad length\n", LocalCaptureBuffer); + DbgBreakPoint(); + ApiMessage->Status = STATUS_INVALID_PARAMETER; + _SEH2_YIELD(return FALSE); + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Return failure */ + ApiMessage->Status = STATUS_INVALID_PARAMETER; + _SEH2_YIELD(return FALSE); + } _SEH2_END; + + /* We validated the incoming buffer, now allocate the remote one */ + RemoteCaptureBuffer = RtlAllocateHeap(CsrHeap, 0, LocalLength); + if (!RemoteCaptureBuffer) + { + /* We're out of memory */ + ApiMessage->Status = STATUS_NO_MEMORY; + return FALSE; + } + + /* Copy the client's buffer */ + RtlMoveMemory(RemoteCaptureBuffer, LocalCaptureBuffer, LocalLength); + + /* Copy the length */ + RemoteCaptureBuffer->Size = LocalLength; + + /* Calculate the difference between our buffer and the client's */ + BufferDistance = (ULONG_PTR)RemoteCaptureBuffer - (ULONG_PTR)LocalCaptureBuffer; + + /* Save the pointer count and offset pointer */ + PointerCount = RemoteCaptureBuffer->PointerCount; + PointerOffsets = (ULONG_PTR**)(RemoteCaptureBuffer + 1); + + /* Start the loop */ + while (PointerCount) + { + /* Get the current pointer */ + if ((CurrentPointer = *PointerOffsets++)) + { + /* Add it to the CSR Message structure */ + CurrentPointer += (ULONG_PTR)ApiMessage; + + /* Validate the bounds of the current pointer */ + if ((*CurrentPointer >= CsrThread->Process->ClientViewBase) && + (*CurrentPointer < CsrThread->Process->ClientViewBounds)) + { + /* Modify the pointer to take into account its new position */ + *CurrentPointer += BufferDistance; + } + else + { + /* Invalid pointer, fail */ + DPRINT1("*** CSRSS: CaptureBuffer MessagePointer outside of ClientView\n"); + DbgBreakPoint(); + ApiMessage->Status = STATUS_INVALID_PARAMETER; + } + } + + /* Move to the next Pointer */ + PointerCount--; + } + + /* Check if we got success */ + if (ApiMessage->Status != STATUS_SUCCESS) + { + /* Failure. Free the buffer and return*/ + RtlFreeHeap(CsrHeap, 0, RemoteCaptureBuffer); + return FALSE; + } + else + { + /* Success, save the previous buffer */ + RemoteCaptureBuffer->PreviousCaptureBuffer = LocalCaptureBuffer; + ApiMessage->CsrCaptureData = RemoteCaptureBuffer; + } + + /* Success */ + return TRUE; +} + +/*++ + * @name CsrReleaseCapturedArguments + * @implemented NT5.1 + * + * The CsrReleaseCapturedArguments routine releases a Capture Buffer + * that was previously captured with CsrCaptureArguments. + * + * @param ApiMessage + * Pointer to the CSR API Message containing the Capture Buffer + * that needs to be released. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage) +{ + PCSR_CAPTURE_BUFFER RemoteCaptureBuffer, LocalCaptureBuffer; + SIZE_T BufferDistance; + ULONG PointerCount; + ULONG_PTR **PointerOffsets, *CurrentPointer; + + /* Get the capture buffers */ + RemoteCaptureBuffer = ApiMessage->CsrCaptureData; + LocalCaptureBuffer = RemoteCaptureBuffer->PreviousCaptureBuffer; + + /* Free the previous one */ + RemoteCaptureBuffer->PreviousCaptureBuffer = NULL; + + /* Find out the difference between the two buffers */ + BufferDistance = (ULONG_PTR)LocalCaptureBuffer - (ULONG_PTR)RemoteCaptureBuffer; + + /* Save the pointer count and offset pointer */ + PointerCount = RemoteCaptureBuffer->PointerCount; + PointerOffsets = (ULONG_PTR**)(RemoteCaptureBuffer + 1); + + /* Start the loop */ + while (PointerCount) + { + /* Get the current pointer */ + CurrentPointer = *PointerOffsets++; + if (CurrentPointer) + { + /* Add it to the CSR Message structure */ + CurrentPointer += (ULONG_PTR)ApiMessage; + + /* Modify the pointer to take into account its new position */ + *CurrentPointer += BufferDistance; + } + + /* Move to the next Pointer */ + PointerCount--; + } + + /* Copy the data back */ + RtlMoveMemory(LocalCaptureBuffer, RemoteCaptureBuffer, RemoteCaptureBuffer->Size); + + /* Free our allocated buffer */ + RtlFreeHeap(CsrHeap, 0, RemoteCaptureBuffer); +} + +/*++ + * @name CsrValidateMessageBuffer + * @implemented NT5.1 + * + * The CsrValidateMessageBuffer routine validates a captured message buffer + * present in the CSR Api Message + * + * @param ApiMessage + * Pointer to the CSR API Message containing the CSR Capture Buffer. + * + * @param Buffer + * Pointer to the message buffer to validate. + * + * @param ArgumentSize + * Size of the message to check. + * + * @param ArgumentCount + * Number of messages to check. + * + * @return TRUE if validation suceeded, FALSE otherwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrValidateMessageBuffer(IN PCSR_API_MESSAGE ApiMessage, + IN PVOID *Buffer, + IN ULONG ArgumentSize, + IN ULONG ArgumentCount) +{ + PCSR_CAPTURE_BUFFER CaptureBuffer = ApiMessage->CsrCaptureData; + SIZE_T BufferDistance; + ULONG PointerCount, i; + ULONG_PTR **PointerOffsets, *CurrentPointer; + + /* Make sure there are some arguments */ + if (!ArgumentCount) return FALSE; + + /* Check if didn't get a buffer and there aren't any arguments to check */ + if (!(*Buffer) && (!(ArgumentCount * ArgumentSize))) return TRUE; + + /* Check if we have no capture buffer */ + if (!CaptureBuffer) + { + /* In this case, check only the Process ID */ + if (NtCurrentTeb()->ClientId.UniqueProcess == + ApiMessage->Header.ClientId.UniqueProcess) + { + /* There is a match, validation succeeded */ + return TRUE; + } + } + else + { + /* Make sure that there is still space left in the buffer */ + if ((CaptureBuffer->Size - (ULONG_PTR)*Buffer + (ULONG_PTR)CaptureBuffer) < + (ArgumentCount * ArgumentSize)) + { + /* Find out the difference between the two buffers */ + BufferDistance = (ULONG_PTR)Buffer - (ULONG_PTR)ApiMessage; + + /* Save the pointer count */ + PointerCount = CaptureBuffer->PointerCount; + PointerOffsets = (ULONG_PTR**)(CaptureBuffer + 1); + + /* Start the loop */ + for (i = 0; i < PointerCount; i++) + { + /* Get the current pointer */ + CurrentPointer = *PointerOffsets++; + + /* Check if its' equal to the difference */ + if (*CurrentPointer == BufferDistance) return TRUE; + } + } + } + + /* Failure */ + DbgPrint("CSRSRV: Bad message buffer %p\n", ApiMessage); + DbgBreakPoint(); + return FALSE; +} + +/*++ + * @name CsrValidateMessageString + * @implemented NT5.1 + * + * The CsrValidateMessageString validates a captured Wide-Character String + * present in a CSR API Message. + * + * @param ApiMessage + * Pointer to the CSR API Message containing the CSR Capture Buffer. + * + * @param MessageString + * Pointer to the buffer containing the string to validate. + * + * @return TRUE if validation suceeded, FALSE otherwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrValidateMessageString(IN PCSR_API_MESSAGE ApiMessage, + IN LPWSTR *MessageString) +{ + DPRINT1("CSRSRV: %s called\n", __FUNCTION__); + return FALSE; +} + +/* EOF */ diff --git a/reactos/deprecated/csr/csrsrv/csrsrv.rc b/reactos/deprecated/csr/csrsrv/csrsrv.rc new file mode 100644 index 00000000000..658072d09b8 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/csrsrv.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS CSR Core Server\0" +#define REACTOS_STR_INTERNAL_NAME "csrsrv\0" +#define REACTOS_STR_ORIGINAL_FILENAME "csrsrv.dll\0" +#include diff --git a/reactos/deprecated/csr/csrsrv/csrsrv2.spec b/reactos/deprecated/csr/csrsrv/csrsrv2.spec new file mode 100644 index 00000000000..e3794208fe9 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/csrsrv2.spec @@ -0,0 +1,35 @@ +@ stdcall CsrAddStaticServerThread(ptr ptr long) +@ stdcall CsrCallServerFromServer(ptr ptr) +@ stdcall CsrConnectToUser() +@ stdcall CsrCreateProcess(ptr ptr ptr ptr long ptr) +@ stdcall CsrCreateRemoteThread(ptr ptr) +@ stdcall CsrCreateThread(ptr ptr ptr long) +@ stdcall CsrCreateWait(ptr ptr ptr ptr ptr ptr) +@ stdcall CsrDebugProcess(ptr) +@ stdcall CsrDebugProcessStop(ptr) +@ stdcall CsrDereferenceProcess(ptr) +@ stdcall CsrDereferenceThread(ptr) +@ stdcall CsrDereferenceWait(ptr) +@ stdcall CsrDestroyProcess(ptr long) +@ stdcall CsrDestroyThread(ptr) +@ stdcall CsrExecServerThread(ptr long) +@ stdcall CsrGetProcessLuid(ptr ptr) +@ stdcall CsrImpersonateClient(ptr) +@ stdcall CsrLockProcessByClientId(ptr ptr) +@ stdcall CsrLockThreadByClientId(ptr ptr) +@ stdcall CsrMoveSatisfiedWait(ptr ptr) +@ stdcall CsrNotifyWait(ptr long ptr ptr) +@ stdcall CsrPopulateDosDevices() +@ stdcall CsrQueryApiPort() +@ stdcall CsrReferenceThread(ptr) +@ stdcall CsrRevertToSelf() +@ stdcall CsrServerInitialization(long ptr) +@ stdcall CsrSetBackgroundPriority(ptr) +@ stdcall CsrSetCallingSpooler(long) +@ stdcall CsrSetForegroundPriority(ptr) +@ stdcall CsrShutdownProcesses(ptr long) +@ stdcall CsrUnhandledExceptionFilter(ptr) +@ stdcall CsrUnlockProcess(ptr) +@ stdcall CsrUnlockThread(ptr) +@ stdcall CsrValidateMessageBuffer(ptr ptr long long) +@ stdcall CsrValidateMessageString(ptr ptr) diff --git a/reactos/deprecated/csr/csrsrv/init.c b/reactos/deprecated/csr/csrsrv/init.c new file mode 100644 index 00000000000..eee4f3d28d6 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/init.c @@ -0,0 +1,988 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrsrv/init.c + * PURPOSE: CSR Server DLL Initialization + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +HANDLE CsrObjectDirectory; +ULONG SessionId; +BOOLEAN CsrProfileControl; +UNICODE_STRING CsrDirectoryName; +HANDLE CsrHeap; +HANDLE BNOLinksDirectory; +HANDLE SessionObjectDirectory; +HANDLE DosDevicesDirectory; +HANDLE CsrInitializationEvent; +SYSTEM_BASIC_INFORMATION CsrNtSysInfo; +ULONG CsrDebug; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name CsrParseServerCommandLine + * + * The CsrParseServerCommandLine routine parses the CSRSS command-line in the + * registry and performs operations for each entry found. + * + * @param ArgumentCount + * Number of arguments on the command line. + * + * @param Arguments + * Array of arguments. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +FASTCALL +CsrParseServerCommandLine(IN ULONG ArgumentCount, + IN PCHAR Arguments[]) +{ + NTSTATUS Status; + PCHAR ParameterName = NULL, ParameterValue = NULL, EntryPoint, ServerString; + ULONG i, DllIndex; + ANSI_STRING AnsiString; + OBJECT_ATTRIBUTES ObjectAttributes; + + /* Set the Defaults */ + CsrTotalPerProcessDataLength = 0; + CsrObjectDirectory = 0; + CsrMaxApiRequestThreads = 16; + + /* Save our Session ID, and create a Directory for it */ + SessionId = NtCurrentPeb()->SessionId; + Status = CsrCreateSessionObjectDirectory(SessionId); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: CsrCreateSessionObjectDirectory failed (%lx)\n", + Status); + + /* It's not fatal if the session ID isn't zero */ + if (SessionId) return Status; + ASSERT(NT_SUCCESS(Status)); + } + + /* Loop through every argument */ + for (i = 1; i < ArgumentCount; i++) + { + /* Split Name and Value */ + ParameterName = Arguments[i]; + ParameterValue = NULL; + ParameterValue = strchr(ParameterName, '='); + if (ParameterValue) *ParameterValue++ = ANSI_NULL; + DPRINT1("Name=%s, Value=%s\n", ParameterName, ParameterValue); + + /* Check for Object Directory */ + if (!_stricmp(ParameterName, "ObjectDirectory")) + { + /* Check if a session ID is specified */ + if (SessionId) + { + DPRINT1("Sessions not yet implemented\n"); + ASSERT(SessionId); + } + + /* Initialize the directory name */ + RtlInitAnsiString(&AnsiString, ParameterValue); + Status = RtlAnsiStringToUnicodeString(&CsrDirectoryName, + &AnsiString, + TRUE); + ASSERT(NT_SUCCESS(Status) || SessionId != 0); + if (!NT_SUCCESS(Status)) return Status; + + /* Create it */ + InitializeObjectAttributes(&ObjectAttributes, + &CsrDirectoryName, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, + NULL, + NULL); + Status = NtCreateDirectoryObject(&CsrObjectDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) return Status; + + /* Secure it */ + Status = CsrSetDirectorySecurity(CsrObjectDirectory); + if (!NT_SUCCESS(Status)) return Status; + } + else if (!_stricmp(ParameterName, "SubSystemType")) + { + /* Ignored */ + } + else if (!_stricmp(ParameterName, "MaxRequestThreads")) + { + Status = RtlCharToInteger(ParameterValue, + 0, + &CsrMaxApiRequestThreads); + } + else if (!_stricmp(ParameterName, "RequestThreads")) + { + /* Ignored */ + Status = STATUS_SUCCESS; + } + else if (!_stricmp(ParameterName, "ProfileControl")) + { + /* Ignored */ + } + else if (!_stricmp(ParameterName, "SharedSection")) + { + /* Craete the Section */ + Status = CsrSrvCreateSharedSection(ParameterValue); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: *** Invalid syntax for %s=%s (Status == %X)\n", + ParameterName, ParameterValue, Status); + return Status; + } + + /* Load us */ + Status = CsrLoadServerDll("CSRSS", NULL, CSR_SRV_SERVER); + } + else if (!_stricmp(ParameterName, "ServerDLL")) + { + /* Loop the command line */ + EntryPoint = NULL; + Status = STATUS_INVALID_PARAMETER; + ServerString = ParameterValue; + while (*ServerString) + { + /* Check for the Entry Point */ + if ((*ServerString == ':') && (!EntryPoint)) + { + /* Found it. Add a nullchar and save it */ + *ServerString++ = ANSI_NULL; + EntryPoint = ServerString; + } + + /* Check for the Dll Index */ + if (*ServerString++ == ',') break; + } + + /* Did we find something to load? */ + if (!*ServerString) + { + DPRINT1("CSRSS: *** Invalid syntax for ServerDll=%s (Status == %X)\n", + ParameterValue, Status); + return Status; + } + + /* Convert it to a ULONG */ + Status = RtlCharToInteger(ServerString, 10, &DllIndex); + + /* Add a null char if it was valid */ + if (NT_SUCCESS(Status)) ServerString[-1] = ANSI_NULL; + + /* Load it */ + if (CsrDebug & 1) DPRINT1("CSRSS: Loading ServerDll=%s:%s\n", ParameterValue, EntryPoint); + Status = CsrLoadServerDll(ParameterValue, EntryPoint, DllIndex); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: *** Failed loading ServerDll=%s (Status == 0x%x)\n", + ParameterValue, Status); + return Status; + } + } + else if (!_stricmp(ParameterName, "Windows")) + { + /* Ignored */ + } + else + { + /* Invalid parameter on the command line */ + Status = STATUS_INVALID_PARAMETER; + } + } + + /* Return status */ + return Status; +} + +/*++ + * @name CsrCreateLocalSystemSD + * + * The CsrCreateLocalSystemSD routine creates a Security Descriptor for + * the local account with PORT_ALL_ACCESS. + * + * @param LocalSystemSd + * Pointer to a pointer to the security descriptor to create. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateLocalSystemSD(OUT PSECURITY_DESCRIPTOR *LocalSystemSd) +{ + SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY}; + PSID SystemSid; + ULONG Length; + PSECURITY_DESCRIPTOR SystemSd; + PACL Dacl; + NTSTATUS Status; + + /* Initialize the System SID */ + RtlAllocateAndInitializeSid(&NtSidAuthority, 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &SystemSid); + + /* Get the length of the SID */ + Length = RtlLengthSid(SystemSid) + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + + /* Allocate a buffer for the Security Descriptor, with SID and DACL */ + SystemSd = RtlAllocateHeap(CsrHeap, 0, SECURITY_DESCRIPTOR_MIN_LENGTH + Length); + + /* Set the pointer to the DACL */ + Dacl = (PACL)((ULONG_PTR)SystemSd + SECURITY_DESCRIPTOR_MIN_LENGTH); + + /* Now create the SD itself */ + Status = RtlCreateSecurityDescriptor(SystemSd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + RtlFreeHeap(CsrHeap, 0, SystemSd); + return Status; + } + + /* Create the DACL for it*/ + RtlCreateAcl(Dacl, Length, ACL_REVISION2); + + /* Create the ACE */ + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, PORT_ALL_ACCESS, SystemSid); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + RtlFreeHeap(CsrHeap, 0, SystemSd); + return Status; + } + + /* Clear the DACL in the SD */ + Status = RtlSetDaclSecurityDescriptor(SystemSd, TRUE, Dacl, FALSE); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + RtlFreeHeap(CsrHeap, 0, SystemSd); + return Status; + } + + /* Free the SID and return*/ + RtlFreeSid(SystemSid); + *LocalSystemSd = SystemSd; + return Status; +} + +/*++ + * @name GetDosDevicesProtection + * + * The GetDosDevicesProtection creates a security descriptor for the DOS Devices + * Object Directory. + * + * @param DosDevicesSd + * Pointer to the Security Descriptor to return. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks Depending on the DOS Devices Protection Mode (set in the registry), + * regular users may or may not have full access to the directory. + * + *--*/ +NTSTATUS +NTAPI +GetDosDevicesProtection(OUT PSECURITY_DESCRIPTOR DosDevicesSd) +{ + SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; + SID_IDENTIFIER_AUTHORITY CreatorAuthority = {SECURITY_CREATOR_SID_AUTHORITY}; + SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY}; + PSID WorldSid, CreatorSid, AdminSid, SystemSid; + UCHAR KeyValueBuffer[0x40]; + PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo; + UNICODE_STRING KeyName; + ULONG ProtectionMode = 0; + OBJECT_ATTRIBUTES ObjectAttributes; + PACL Dacl; + PACCESS_ALLOWED_ACE Ace; + HANDLE hKey; + NTSTATUS Status; + ULONG ResultLength, SidLength, AclLength; + + /* Create the SD */ + Status = RtlCreateSecurityDescriptor(DosDevicesSd, SECURITY_DESCRIPTOR_REVISION); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the System SID */ + Status = RtlAllocateAndInitializeSid(&NtSidAuthority, 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &SystemSid); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the World SID */ + Status = RtlAllocateAndInitializeSid(&WorldAuthority, 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &WorldSid); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the Admin SID */ + Status = RtlAllocateAndInitializeSid(&NtSidAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &AdminSid); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the Creator SID */ + Status = RtlAllocateAndInitializeSid(&CreatorAuthority, 1, + SECURITY_CREATOR_OWNER_RID, + 0, 0, 0, 0, 0, 0, 0, + &CreatorSid); + ASSERT(NT_SUCCESS(Status)); + + /* Open the Session Manager Key */ + RtlInitUnicodeString(&KeyName, SM_REG_KEY); + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); + if (NT_SUCCESS(Status)) + { + /* Read the key value */ + RtlInitUnicodeString(&KeyName, L"ProtectionMode"); + Status = NtQueryValueKey(hKey, + &KeyName, + KeyValuePartialInformation, + KeyValueBuffer, + sizeof(KeyValueBuffer), + &ResultLength); + + /* Make sure it's what we expect it to be */ + KeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer; + if ((NT_SUCCESS(Status)) && (KeyValuePartialInfo->Type == REG_DWORD) && + (*(PULONG)KeyValuePartialInfo->Data)) + { + /* Save the Protection Mode */ + ProtectionMode = *(PULONG)KeyValuePartialInfo->Data; + } + + /* Close the handle */ + NtClose(hKey); + } + + /* Check the Protection Mode */ + if (ProtectionMode & 3) + { + /* Calculate SID Lengths */ + SidLength = RtlLengthSid(CreatorSid) + RtlLengthSid(SystemSid) + + RtlLengthSid(AdminSid); + AclLength = sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) + SidLength; + + /* Allocate memory for the DACL */ + Dacl = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, AclLength); + ASSERT(Dacl != NULL); + + /* Build the ACL and add 3 ACEs */ + Status = RtlCreateAcl(Dacl, AclLength, ACL_REVISION2); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, SystemSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, AdminSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, CreatorSid); + ASSERT(NT_SUCCESS(Status)); + + /* Edit the ACEs to make them inheritable */ + Status = RtlGetAce(Dacl, 0, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + Status = RtlGetAce(Dacl, 1, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + Status = RtlGetAce(Dacl, 2, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE; + + /* Set this DACL with the SD */ + Status = RtlSetDaclSecurityDescriptor(DosDevicesSd, TRUE, Dacl, FALSE); + ASSERT(NT_SUCCESS(Status)); + goto Quickie; + } + else + { + /* Calculate SID Lengths */ + SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(SystemSid); + AclLength = sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) + SidLength; + + /* Allocate memory for the DACL */ + Dacl = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, AclLength); + ASSERT(Dacl != NULL); + + /* Build the ACL and add 3 ACEs */ + Status = RtlCreateAcl(Dacl, AclLength, ACL_REVISION2); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE, WorldSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, SystemSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, WorldSid); + ASSERT(NT_SUCCESS(Status)); + + /* Edit the last ACE to make it inheritable */ + Status = RtlGetAce(Dacl, 2, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE; + + /* Set this DACL with the SD */ + Status = RtlSetDaclSecurityDescriptor(DosDevicesSd, TRUE, Dacl, FALSE); + ASSERT(NT_SUCCESS(Status)); + goto Quickie; + } + +/* FIXME: failure cases! Fail: */ + /* Free the memory */ + RtlFreeHeap(CsrHeap, 0, Dacl); + +/* FIXME: semi-failure cases! Quickie: */ +Quickie: + /* Free the SIDs */ + RtlFreeSid(SystemSid); + RtlFreeSid(WorldSid); + RtlFreeSid(AdminSid); + RtlFreeSid(CreatorSid); + + /* Return */ + return Status; +} + +/*++ + * @name FreeDosDevicesProtection + * + * The FreeDosDevicesProtection frees the security descriptor that was created + * by GetDosDevicesProtection + * + * @param DosDevicesSd + * Pointer to the security descriptor to free. + + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +FreeDosDevicesProtection(IN PSECURITY_DESCRIPTOR DosDevicesSd) +{ + PACL Dacl; + BOOLEAN Present, Default; + NTSTATUS Status; + + /* Get the DACL corresponding to this SD */ + Status = RtlGetDaclSecurityDescriptor(DosDevicesSd, &Present, &Dacl, &Default); + ASSERT(NT_SUCCESS(Status)); + ASSERT(Present); + ASSERT(Dacl != NULL); + + /* Free it */ + if ((NT_SUCCESS(Status)) && (Dacl)) RtlFreeHeap(CsrHeap, 0, Dacl); +} + +/*++ + * @name CsrCreateSessionObjectDirectory + * + * The CsrCreateSessionObjectDirectory routine creates the BaseNamedObjects, + * Session and Dos Devices directories for the specified session. + * + * @param Session + * Session ID for which to create the directories. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateSessionObjectDirectory(IN ULONG Session) +{ + WCHAR SessionBuffer[512], BnoBuffer[512]; + UNICODE_STRING SessionString, BnoString; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE BnoHandle; + SECURITY_DESCRIPTOR DosDevicesSd; + NTSTATUS Status; + + /* Generate the Session BNOLINKS Directory name */ + swprintf(SessionBuffer, L"%ws\\BNOLINKS", SESSION_ROOT); + RtlInitUnicodeString(&SessionString, SessionBuffer); + + /* Create it */ + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtCreateDirectoryObject(&BNOLinksDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateDirectoryObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + return Status; + } + + /* Now add the Session ID */ + swprintf(SessionBuffer, L"%ld", Session); + RtlInitUnicodeString(&SessionString, SessionBuffer); + + /* Check if this is the first Session */ + if (Session) + { + /* Not the first, so the name will be slighly more complex */ + swprintf(BnoBuffer, L"%ws\\%ld\\BaseNamedObjects", SESSION_ROOT, Session); + RtlInitUnicodeString(&BnoString, BnoBuffer); + } + else + { + /* Use the direct name */ + RtlInitUnicodeString(&BnoString, L"\\BaseNamedObjects"); + } + + /* Create the symlink */ + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE, + BNOLinksDirectory, + NULL); + Status = NtCreateSymbolicLinkObject(&BnoHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes, + &BnoString); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateSymbolicLinkObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + return Status; + } + + /* Create the \DosDevices Security Descriptor */ + Status = GetDosDevicesProtection(&DosDevicesSd); + if (!NT_SUCCESS(Status)) return Status; + + /* Now create a directory for this session */ + swprintf(SessionBuffer, L"%ws\\%ld", SESSION_ROOT, Session); + RtlInitUnicodeString(&SessionString, SessionBuffer); + + /* Create the directory */ + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE, + 0, + &DosDevicesSd); + Status = NtCreateDirectoryObject(&SessionObjectDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateDirectoryObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + FreeDosDevicesProtection(&DosDevicesSd); + return Status; + } + + /* Next, create a directory for this session's DOS Devices */ + RtlInitUnicodeString(&SessionString, L"DosDevices"); + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_CASE_INSENSITIVE, + SessionObjectDirectory, + &DosDevicesSd); + Status = NtCreateDirectoryObject(&DosDevicesDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateDirectoryObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + } + + /* Release the Security Descriptor */ + FreeDosDevicesProtection(&DosDevicesSd); + + /* Return */ + return Status; +} + +/*++ + * @name CsrSetProcessSecurity + * + * The CsrSetProcessSecurity routine protects access to the CSRSS process + * from unauthorized tampering. + * + * @param None. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSetProcessSecurity(VOID) +{ + NTSTATUS Status; + HANDLE hToken, hProcess = NtCurrentProcess(); + ULONG ReturnLength, Length; + PTOKEN_USER TokenInfo = NULL; + PSECURITY_DESCRIPTOR ProcSd = NULL; + PACL Dacl; + PSID UserSid; + + /* Open our token */ + Status = NtOpenProcessToken(hProcess, TOKEN_QUERY, &hToken); + if (!NT_SUCCESS(Status)) goto Quickie; + + /* Get the Token User Length */ + NtQueryInformationToken(hToken, TokenUser, NULL, 0, &Length); + + /* Allocate space for it */ + TokenInfo = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, Length); + if (!TokenInfo) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Now query the data */ + Status = NtQueryInformationToken(hToken, TokenUser, TokenInfo, Length, &Length); + NtClose(hToken); + if (!NT_SUCCESS(Status)) goto Quickie; + + /* Now check the SID Length */ + UserSid = TokenInfo->User.Sid; + ReturnLength = RtlLengthSid(UserSid) + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + + /* Allocate a buffer for the Security Descriptor, with SID and DACL */ + ProcSd = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, SECURITY_DESCRIPTOR_MIN_LENGTH + Length); + if (!ProcSd) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Set the pointer to the DACL */ + Dacl = (PACL)((ULONG_PTR)ProcSd + SECURITY_DESCRIPTOR_MIN_LENGTH); + + /* Now create the SD itself */ + Status = RtlCreateSecurityDescriptor(ProcSd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) goto Quickie; + + /* Create the DACL for it*/ + RtlCreateAcl(Dacl, Length, ACL_REVISION2); + + /* Create the ACE */ + Status = RtlAddAccessAllowedAce(Dacl, + ACL_REVISION, + PROCESS_VM_READ | PROCESS_VM_WRITE | + PROCESS_VM_OPERATION | PROCESS_DUP_HANDLE | + PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME | + PROCESS_QUERY_INFORMATION | READ_CONTROL, + UserSid); + if (!NT_SUCCESS(Status)) goto Quickie; + + /* Clear the DACL in the SD */ + Status = RtlSetDaclSecurityDescriptor(ProcSd, TRUE, Dacl, FALSE); + if (!NT_SUCCESS(Status)) goto Quickie; + + /* Write the SD into the Process */ + Status = NtSetSecurityObject(hProcess, DACL_SECURITY_INFORMATION, ProcSd); + + /* Free the memory and return */ +Quickie: + if (ProcSd) RtlFreeHeap(CsrHeap, 0, ProcSd); + RtlFreeHeap(CsrHeap, 0, TokenInfo); + return Status; +} + +/*++ + * @name CsrSetDirectorySecurity + * + * The CsrSetDirectorySecurity routine sets the security descriptor for the + * specified Object Directory. + * + * @param ObjectDirectory + * Handle fo the Object Directory to protect. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSetDirectorySecurity(IN HANDLE ObjectDirectory) +{ + /* FIXME: Implement */ + return STATUS_SUCCESS; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + +/*++ + * @name CsrServerInitialization + * @implemented NT4 + * + * The CsrServerInitialization routine is the native (not Server) entrypoint + * of this Server DLL. It serves as the entrypoint for csrss. + * + * @param ArgumentCount + * Number of arguments on the command line. + * + * @param Arguments + * Array of arguments from the command line. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrServerInitialization(IN ULONG ArgumentCount, + IN PCHAR Arguments[]) +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i = 0; + PVOID ProcessData; + PCSR_SERVER_DLL ServerDll; + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + + /* Create the Init Event */ + Status = NtCreateEvent(&CsrInitializationEvent, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtCreateEvent failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Cache System Basic Information so we don't always request it */ + Status = NtQuerySystemInformation(SystemBasicInformation, + &CsrNtSysInfo, + sizeof(SYSTEM_BASIC_INFORMATION), + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtQuerySystemInformation failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Save our Heap */ + CsrHeap = RtlGetProcessHeap(); + + /* Set our Security Descriptor to protect the process */ + Status = CsrSetProcessSecurity(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrSetProcessSecurity failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Set up Session Support */ + Status = CsrInitializeNtSessionList(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrInitializeSessions failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Set up Process Support */ + Status = CsrInitializeProcessStructure(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrInitializeProcessStructure failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Parse the command line */ + Status = CsrParseServerCommandLine(ArgumentCount, Arguments); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrParseServerCommandLine failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* All Server DLLs are now loaded, allocate a heap for the Root Process */ + ProcessData = RtlAllocateHeap(CsrHeap, + HEAP_ZERO_MEMORY, + CsrTotalPerProcessDataLength); + if (!ProcessData) + { + DPRINT1("CSRSRV:%s: RtlAllocateHeap failed (Status=%08lx)\n", + __FUNCTION__, STATUS_NO_MEMORY); + return STATUS_NO_MEMORY; + } + + /* + * Our Root Process was never officially initalized, so write the data + * for each Server DLL manually. + */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the current Server */ + ServerDll = CsrLoadedServerDll[i]; + + /* Is it loaded, and does it have per process data? */ + if ((ServerDll) && (ServerDll->SizeOfProcessData)) + { + /* It does, give it part of our allocated heap */ + CsrRootProcess->ServerData[i] = ProcessData; + + /* Move to the next heap position */ + ProcessData = (PVOID)((ULONG_PTR)ProcessData + + ServerDll->SizeOfProcessData); + } + else + { + /* Nothing for this Server DLL */ + CsrRootProcess->ServerData[i] = NULL; + } + } + + /* Now initialize the Root Process manually as well */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the current Server */ + ServerDll = CsrLoadedServerDll[i]; + + /* Is it loaded, and does it a callback for new processes? */ + if ((ServerDll) && (ServerDll->NewProcessCallback)) + { + /* Call the callback */ + ServerDll->NewProcessCallback(NULL, CsrRootProcess); + } + } + + /* Now initialize our API Port */ + Status = CsrApiPortInitialize(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrApiPortInitialize failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Initialize the API Port for SM communication */ + Status = CsrSbApiPortInitialize(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrSbApiPortInitialize failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* We're all set! Connect to SM! */ + Status = SmConnectToSm(&CsrSbApiPortName, + CsrSbApiPort, + IMAGE_SUBSYSTEM_WINDOWS_GUI, + &CsrSmApiPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: SmConnectToSm failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Finito! Signal the event */ + Status = NtSetEvent(CsrInitializationEvent, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtSetEvent failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Close the event handle now */ + NtClose(CsrInitializationEvent); + + /* Have us handle Hard Errors */ + Status = NtSetDefaultHardErrorPort(CsrApiPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtSetDefaultHardErrorPort failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Return status */ + return Status; +} + +/*++ + * @name CsrPopulateDosDevices + * @unimplemented NT5.1 + * + * The CsrPopulateDosDevices routine uses the DOS Device Map from the Kernel + * to populate the Dos Devices Object Directory for the session. + * + * @param None. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrPopulateDosDevices(VOID) +{ + DPRINT1("Deprecated API\n"); + return; +} + +BOOL +NTAPI +DllMain(IN HANDLE hDll, + IN DWORD dwReason, + IN LPVOID lpReserved) +{ + /* We don't do much */ + UNREFERENCED_PARAMETER(hDll); + UNREFERENCED_PARAMETER(dwReason); + UNREFERENCED_PARAMETER(lpReserved); + return TRUE; +} + +/* EOF */ diff --git a/reactos/deprecated/csr/csrsrv/process.c b/reactos/deprecated/csr/csrsrv/process.c new file mode 100644 index 00000000000..9bf455db7c4 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/process.c @@ -0,0 +1,1342 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrsrv/process.c + * PURPOSE: CSR Server DLL Process Implementation + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +PCSR_PROCESS CsrRootProcess = NULL; +RTL_CRITICAL_SECTION CsrProcessLock; +ULONG CsrProcessSequenceCount = 5; +ULONG CsrTotalPerProcessDataLength; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name ProtectHandle + * @implemented NT5.2 + * + * The ProtectHandle routine protects an object handle against closure. + * + * @return TRUE or FALSE. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +ProtectHandle(IN HANDLE ObjectHandle) +{ + NTSTATUS Status; + OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo; + + /* Query current state */ + Status = NtQueryObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo), + NULL); + if (NT_SUCCESS(Status)) + { + /* Enable protect from close */ + HandleInfo.ProtectFromClose = TRUE; + Status = NtSetInformationObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo)); + if (NT_SUCCESS(Status)) return TRUE; + } + + /* We failed to or set the state */ + return FALSE; +} + +/*++ + * @name UnProtectHandle + * @implemented NT5.2 + * + * The UnProtectHandle routine unprotects an object handle against closure. + * + * @return TRUE or FALSE. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +UnProtectHandle(IN HANDLE ObjectHandle) +{ + NTSTATUS Status; + OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo; + + /* Query current state */ + Status = NtQueryObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo), + NULL); + if (NT_SUCCESS(Status)) + { + /* Disable protect from close */ + HandleInfo.ProtectFromClose = FALSE; + Status = NtSetInformationObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo)); + if (NT_SUCCESS(Status)) return TRUE; + } + + /* We failed to or set the state */ + return FALSE; +} + +/*++ + * @name CsrAllocateProcess + * @implemented NT4 + * + * The CsrAllocateProcess routine allocates a new CSR Process object. + * + * @return Pointer to the newly allocated CSR Process. + * + * @remarks None. + * + *--*/ +PCSR_PROCESS +NTAPI +CsrAllocateProcess(VOID) +{ + PCSR_PROCESS CsrProcess; + ULONG TotalSize; + + /* Calculate the amount of memory this should take */ + TotalSize = sizeof(CSR_PROCESS) + + (CSR_SERVER_DLL_MAX * sizeof(PVOID)) + + CsrTotalPerProcessDataLength; + + /* Allocate a Process */ + CsrProcess = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, TotalSize); + if (!CsrProcess) return NULL; + + /* Handle the Sequence Number and protect against overflow */ + CsrProcess->SequenceNumber = CsrProcessSequenceCount++; + if (CsrProcessSequenceCount < 5) CsrProcessSequenceCount = 5; + + /* Increase the reference count */ + CsrProcess->ReferenceCount++; + + /* Initialize the Thread List */ + InitializeListHead(&CsrProcess->ThreadList); + + /* Return the Process */ + return CsrProcess; +} + +/*++ + * @name CsrServerInitialization + * @implemented NT4 + * + * The CsrInitializeProcessStructure routine sets up support for CSR Processes + * and CSR Threads. + * + * @param None. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrInitializeProcessStructure(VOID) +{ + NTSTATUS Status; + ULONG i; + + /* Initialize the Lock */ + Status = RtlInitializeCriticalSection(&CsrProcessLock); + if (!NT_SUCCESS(Status)) return Status; + + /* Set up the Root Process */ + CsrRootProcess = CsrAllocateProcess(); + if (!CsrRootProcess) return STATUS_NO_MEMORY; + + /* Set up the minimal information for it */ + InitializeListHead(&CsrRootProcess->ListLink); + CsrRootProcess->ProcessHandle = (HANDLE)-1; + CsrRootProcess->ClientId = NtCurrentTeb()->ClientId; + + /* Initialize the Thread Hash List */ + for (i = 0; i < 256; i++) InitializeListHead(&CsrThreadHashTable[i]); + + /* Initialize the Wait Lock */ + return RtlInitializeCriticalSection(&CsrWaitListsLock); +} + +/*++ + * @name CsrDeallocateProcess + * + * The CsrDeallocateProcess frees the memory associated with a CSR Process. + * + * @param CsrProcess + * Pointer to the CSR Process to be freed. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Process has been cleanly + * dereferenced and killed. + * + *--*/ +VOID +NTAPI +CsrDeallocateProcess(IN PCSR_PROCESS CsrProcess) +{ + /* Free the process object from the heap */ + RtlFreeHeap(CsrHeap, 0, CsrProcess); +} + +/*++ + * @name CsrInsertProcess + * + * The CsrInsertProcess routine inserts a CSR Process into the Process List + * and notifies Server DLLs of the creation of a new CSR Process. + * + * @param Parent + * Optional pointer to the CSR Process creating this CSR Process. + * + * @param CurrentProcess + * Optional pointer to the current CSR Process. + * + * @param CsrProcess + * Pointer to the CSR Process which is to be inserted. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrInsertProcess(IN PCSR_PROCESS Parent OPTIONAL, + IN PCSR_PROCESS CurrentProcess OPTIONAL, + IN PCSR_PROCESS CsrProcess) +{ + PCSR_SERVER_DLL ServerDll; + ULONG i; + ASSERT(ProcessStructureListLocked()); + + /* Set the parent */ + CsrProcess->Parent = Parent; + + /* Insert it into the Root List */ + InsertTailList(&CsrRootProcess->ListLink, &CsrProcess->ListLink); + + /* Notify the Server DLLs */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the current Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Make sure it's valid and that it has callback */ + if ((ServerDll) && (ServerDll->NewProcessCallback)) + { + ServerDll->NewProcessCallback(CurrentProcess, CsrProcess); + } + } +} + +/*++ + * @name CsrLockedDereferenceProcess + * + * The CsrLockedDereferenceProcess dereferences a CSR Process while the + * Process Lock is already being held. + * + * @param CsrProcess + * Pointer to the CSR Process to be dereferenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedDereferenceProcess(PCSR_PROCESS CsrProcess) +{ + LONG LockCount; + + /* Decrease reference count */ + LockCount = --CsrProcess->ReferenceCount; + ASSERT(LockCount >= 0); + if (!LockCount) + { + /* Call the generic cleanup code */ + CsrProcessRefcountZero(CsrProcess); + CsrAcquireProcessLock(); + } +} + +/*++ + * @name CsrLockedReferenceProcess + * + * The CsrLockedReferenceProcess refences a CSR Process while the + * Process Lock is already being held. + * + * @param CsrProcess + * Pointer to the CSR Process to be referenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedReferenceProcess(IN PCSR_PROCESS CsrProcess) +{ + /* Increment the reference count */ + ++CsrProcess->ReferenceCount; +} + +/*++ + * @name CsrRemoveProcess + * + * The CsrRemoveProcess function undoes a CsrInsertProcess operation and + * removes the CSR Process from the Process List and notifies Server DLLs + * of this removal. + * + * @param CsrProcess + * Pointer to the CSR Process to remove. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrRemoveProcess(IN PCSR_PROCESS CsrProcess) +{ + PCSR_SERVER_DLL ServerDll; + ULONG i; + ASSERT(ProcessStructureListLocked()); + + /* Remove us from the Process List */ + RemoveEntryList(&CsrProcess->ListLink); + + /* Release the lock */ + CsrReleaseProcessLock(); + + /* Loop every Server DLL */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Check if it's valid and if it has a Disconnect Callback */ + if ((ServerDll) && (ServerDll->DisconnectCallback)) + { + /* Call it */ + ServerDll->DisconnectCallback(CsrProcess); + } + } +} + +/*++ + * @name CsrProcessRefcountZero + * + * The CsrProcessRefcountZero routine is executed when a CSR Process has lost + * all its active references. It removes and de-allocates the CSR Process. + * + * @param CsrProcess + * Pointer to the CSR Process that is to be deleted. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Process has lost all + * its references. + * + * This routine is called with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrProcessRefcountZero(IN PCSR_PROCESS CsrProcess) +{ + /* Remove the Process from the list */ + CsrRemoveProcess(CsrProcess); + + /* Check if there's a session */ + if (CsrProcess->NtSession) + { + /* Dereference the Session */ + CsrDereferenceNtSession(CsrProcess->NtSession, 0); + } + + /* Close the Client Port if there is one */ + if (CsrProcess->ClientPort) NtClose(CsrProcess->ClientPort); + + /* Close the process handle */ + NtClose(CsrProcess->ProcessHandle); + + /* Free the Proces Object */ + CsrDeallocateProcess(CsrProcess); +} + +/*++ + * @name CsrpSetToNormalPriority + * + * The CsrpSetToNormalPriority routine sets the current NT Process' + * priority to the normal priority for CSR Processes. + * + * @param None. + * + * @return None. + * + * @remarks The "Normal" Priority corresponds to the Normal Forground + * Priority (9) plus a boost of 4. + * + *--*/ +VOID +NTAPI +CsrpSetToNormalPriority(VOID) +{ + KPRIORITY BasePriority = (8 + 1) + 4; + + /* Set the Priority */ + NtSetInformationProcess(NtCurrentProcess(), + ProcessBasePriority, + &BasePriority, + sizeof(KPRIORITY)); +} + +/*++ + * @name CsrpSetToShutdownPriority + * + * The CsrpSetToShutdownPriority routine sets the current NT Process' + * priority to the boosted priority for CSR Processes doing shutdown. + * Additonally, it acquires the Shutdown Privilege required for shutdown. + * + * @param None. + * + * @return None. + * + * @remarks The "Shutdown" Priority corresponds to the Normal Forground + * Priority (9) plus a boost of 6. + * + *--*/ +VOID +NTAPI +CsrpSetToShutdownPriority(VOID) +{ + KPRIORITY SetBasePriority = (8 + 1) + 6; + BOOLEAN Old; + + /* Get the shutdown privilege */ + if (NT_SUCCESS(RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, + TRUE, + FALSE, + &Old))) + { + /* Set the Priority */ + NtSetInformationProcess(NtCurrentProcess(), + ProcessBasePriority, + &SetBasePriority, + sizeof(KPRIORITY)); + } +} + +/*++ + * @name FindProcessForShutdown + * + * The FindProcessForShutdown routine returns a CSR Process which is ready + * to be shutdown, and sets the appropriate shutdown flags for it. + * + * @param CallerLuid + * Pointer to the LUID of the CSR Process calling this routine. + * + * @return Pointer to a CSR Process which is ready to be shutdown. + * + * @remarks None. + * + *--*/ +PCSR_PROCESS +NTAPI +FindProcessForShutdown(IN PLUID CallerLuid) +{ + PLIST_ENTRY NextEntry; + LUID ProcessLuid; + NTSTATUS Status; + LUID SystemLuid = SYSTEM_LUID; + PCSR_PROCESS CsrProcess; + PCSR_THREAD CsrThread; + PCSR_PROCESS ReturnCsrProcess = NULL; + ULONG Level = 0; + + /* Set the List Pointers */ + NextEntry = CsrRootProcess->ListLink.Flink; + while (NextEntry != &CsrRootProcess->ListLink) + { + /* Get the process */ + CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink); + + /* Move to the next entry */ + NextEntry = NextEntry->Flink; + + /* Skip this process if it's already been processed*/ + if (CsrProcess->Flags & CsrProcessSkipShutdown) continue; + + /* Get the LUID of this Process */ + Status = CsrGetProcessLuid(CsrProcess->ProcessHandle, &ProcessLuid); + + /* Check if we didn't get access to the LUID */ + if (Status == STATUS_ACCESS_DENIED) + { + /* Check if we have any threads */ + if (CsrProcess->ThreadCount) + { + /* Impersonate one of the threads and retry */ + CsrThread = CONTAINING_RECORD(CsrProcess->ThreadList.Flink, + CSR_THREAD, + Link); + CsrImpersonateClient(CsrThread); + Status = CsrGetProcessLuid(NULL, &ProcessLuid); + CsrRevertToSelf(); + } + } + + if (!NT_SUCCESS(Status)) + { + /* We didn't have access, so skip it */ + CsrProcess->Flags |= CsrProcessSkipShutdown; + continue; + } + + /* Check if this is the System LUID */ + if (RtlEqualLuid(&ProcessLuid, &SystemLuid)) + { + /* Mark this process */ + CsrProcess->ShutdownFlags |= CsrShutdownSystem; + } + else if (!RtlEqualLuid(&ProcessLuid, CallerLuid)) + { + /* Our LUID doesn't match with the caller's */ + CsrProcess->ShutdownFlags |= CsrShutdownOther; + } + + /* Check if we're past the previous level */ + if ((CsrProcess->ShutdownLevel > Level) || !(ReturnCsrProcess)) + { + /* Update the level */ + Level = CsrProcess->ShutdownLevel; + + /* Set the final process */ + ReturnCsrProcess = CsrProcess; + } + } + + /* Check if we found a process */ + if (ReturnCsrProcess) + { + /* Skip this one next time */ + ReturnCsrProcess->Flags |= CsrProcessSkipShutdown; + } + + return ReturnCsrProcess; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/*++ + * @name CsrCreateProcess + * @implemented NT4 + * + * Do nothing for 500ms. + * + * @param ArgumentCount + * Description of the parameter. Wrapped to more lines on ~70th + * column. + * + * @param Arguments + * Description of the parameter. Wrapped to more lines on ~70th + * column. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateProcess(IN HANDLE hProcess, + IN HANDLE hThread, + IN PCLIENT_ID ClientId, + IN PCSR_NT_SESSION NtSession, + IN ULONG Flags, + IN PCLIENT_ID DebugCid) +{ + PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread; + CLIENT_ID CurrentCid; + PCSR_PROCESS CurrentProcess; + PVOID ProcessData; + ULONG i; + PCSR_PROCESS CsrProcess; + NTSTATUS Status; + PCSR_THREAD CsrThread; + KERNEL_USER_TIMES KernelTimes; + + /* Get the current CID and lock Processes */ + CurrentCid = CurrentThread->ClientId; + CsrAcquireProcessLock(); + + /* Get the current CSR Thread */ + CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, &CurrentCid); + if (!CurrentThread) + { + /* We've failed to locate the thread */ + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Allocate a new Process Object */ + CsrProcess = CsrAllocateProcess(); + if (!CsrProcess) + { + /* Couldn't allocate Process */ + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Inherit the Process Data */ + CurrentProcess = CurrentThread->Process; + ProcessData = &CurrentProcess->ServerData[CSR_SERVER_DLL_MAX]; + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Check if the DLL is Loaded and has Per Process Data */ + if (CsrLoadedServerDll[i] && CsrLoadedServerDll[i]->SizeOfProcessData) + { + /* Set the pointer */ + CsrProcess->ServerData[i] = ProcessData; + + /* Copy the Data */ + RtlMoveMemory(ProcessData, + CurrentProcess->ServerData[i], + CsrLoadedServerDll[i]->SizeOfProcessData); + + /* Update next data pointer */ + ProcessData = (PVOID)((ULONG_PTR)ProcessData + + CsrLoadedServerDll[i]->SizeOfProcessData); + } + else + { + /* No data for this Server */ + CsrProcess->ServerData[i] = NULL; + } + } + + /* Set the Exception port to us */ + Status = NtSetInformationProcess(hProcess, + ProcessExceptionPort, + &CsrApiPort, + sizeof(HANDLE)); + if (!NT_SUCCESS(Status)) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* If Check if CreateProcess got CREATE_NEW_PROCESS_GROUP */ + if (!(Flags & CsrProcessCreateNewGroup)) + { + /* Create new data */ + CsrProcess->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess); + CsrProcess->ProcessGroupSequence = CsrProcess->SequenceNumber; + } + else + { + /* Copy it from the current process */ + CsrProcess->ProcessGroupId = CurrentProcess->ProcessGroupId; + CsrProcess->ProcessGroupSequence = CurrentProcess->ProcessGroupSequence; + } + + /* Check if this is a console process */ + if (Flags & CsrProcessIsConsoleApp) CsrProcess->Flags |= CsrProcessIsConsoleApp; + + /* Mask out non-debug flags */ + Flags &= ~(CsrProcessIsConsoleApp | CsrProcessCreateNewGroup | CsrProcessPriorityFlags); + + /* Check if every process will be debugged */ + if (!(Flags) && (CurrentProcess->DebugFlags & CsrDebugProcessChildren)) + { + /* Pass it on to the current process */ + CsrProcess->DebugFlags = CsrDebugProcessChildren; + CsrProcess->DebugCid = CurrentProcess->DebugCid; + } + + /* Check if Debugging was used on this process */ + if ((Flags & (CsrDebugOnlyThisProcess | CsrDebugProcessChildren)) && (DebugCid)) + { + /* Save the debug flag used */ + CsrProcess->DebugFlags = Flags; + + /* Save the CID */ + CsrProcess->DebugCid = *DebugCid; + } + + /* Check if we debugging is enabled */ + if (CsrProcess->DebugFlags) + { + /* Set the Debug Port to us */ + Status = NtSetInformationProcess(hProcess, + ProcessDebugPort, + &CsrApiPort, + sizeof(HANDLE)); + ASSERT(NT_SUCCESS(Status)); + if (!NT_SUCCESS(Status)) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + } + + /* Get the Thread Create Time */ + Status = NtQueryInformationThread(hThread, + ThreadTimes, + (PVOID)&KernelTimes, + sizeof(KernelTimes), + NULL); + if (!NT_SUCCESS(Status)) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Allocate a CSR Thread Structure */ + CsrThread = CsrAllocateThread(CsrProcess); + if (!CsrThread) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Save the data we have */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = *ClientId; + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->Flags = 0; + + /* Insert the Thread into the Process */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Reference the session */ + CsrReferenceNtSession(NtSession); + CsrProcess->NtSession = NtSession; + + /* Setup Process Data */ + CsrProcess->ClientId = *ClientId; + CsrProcess->ProcessHandle = hProcess; + CsrProcess->ShutdownLevel = 0x280; + + /* Set the Priority to Background */ + CsrSetBackgroundPriority(CsrProcess); + + /* Insert the Process */ + CsrInsertProcess(NULL, CurrentProcess, CsrProcess); + + /* Release lock and return */ + CsrReleaseProcessLock(); + return Status; +} + +/*++ + * @name CsrDebugProcess + * @implemented NT4 + * + * The CsrDebugProcess routine is deprecated in NT 5.1 and higher. It is + * exported only for compatibility with older CSR Server DLLs. + * + * @param CsrProcess + * Deprecated. + * + * @return Deprecated + * + * @remarks Deprecated. + * + *--*/ +NTSTATUS +NTAPI +CsrDebugProcess(IN PCSR_PROCESS CsrProcess) +{ + /* CSR does not handle debugging anymore */ + DPRINT("CSRSRV: %s(%08lx) called\n", __FUNCTION__, CsrProcess); + return STATUS_UNSUCCESSFUL; +} + +/*++ + * @name CsrServerInitialization + * @implemented NT4 + * + * The CsrDebugProcessStop routine is deprecated in NT 5.1 and higher. It is + * exported only for compatibility with older CSR Server DLLs. + * + * @param CsrProcess + * Deprecated. + * + * @return Deprecated + * + * @remarks Deprecated. + * + *--*/ +NTSTATUS +NTAPI +CsrDebugProcessStop(IN PCSR_PROCESS CsrProcess) +{ + /* CSR does not handle debugging anymore */ + DPRINT("CSRSRV: %s(%08lx) called\n", __FUNCTION__, CsrProcess); + return STATUS_UNSUCCESSFUL; +} + +/*++ + * @name CsrDereferenceProcess + * @implemented NT4 + * + * The CsrDereferenceProcess routine removes a reference from a CSR Process. + * + * @param CsrThread + * Pointer to the CSR Process to dereference. + * + * @return None. + * + * @remarks If the reference count has reached zero (ie: the CSR Process has + * no more active references), it will be deleted. + * + *--*/ +VOID +NTAPI +CsrDereferenceProcess(IN PCSR_PROCESS CsrProcess) +{ + LONG LockCount; + + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Decrease reference count */ + LockCount = --CsrProcess->ReferenceCount; + ASSERT(LockCount >= 0); + if (!LockCount) + { + /* Call the generic cleanup code */ + CsrProcessRefcountZero(CsrProcess); + } + else + { + /* Just release the lock */ + CsrReleaseProcessLock(); + } +} + +/*++ + * @name CsrDestroyProcess + * @implemented NT4 + * + * The CsrDestroyProcess routine destroys the CSR Process corresponding to + * a given Client ID. + * + * @param Cid + * Pointer to the Client ID Structure corresponding to the CSR + * Process which is about to be destroyed. + * + * @param ExitStatus + * Unused. + * + * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING + * if the CSR Process is already terminating. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrDestroyProcess(IN PCLIENT_ID Cid, + IN NTSTATUS ExitStatus) +{ + PCSR_THREAD CsrThread; + PCSR_PROCESS CsrProcess; + CLIENT_ID ClientId = *Cid; + PLIST_ENTRY NextEntry; + + /* Acquire lock */ + CsrAcquireProcessLock(); + + /* Find the thread */ + CsrThread = CsrLocateThreadByClientId(&CsrProcess, &ClientId); + + /* Make sure we got one back, and that it's not already gone */ + if (!(CsrThread) || (CsrProcess->Flags & CsrProcessTerminating)) + { + /* Release the lock and return failure */ + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Set the terminated flag */ + CsrProcess->Flags |= CsrProcessTerminating; + + /* Get the List Pointers */ + NextEntry = CsrProcess->ThreadList.Flink; + while (NextEntry != &CsrProcess->ThreadList) + { + /* Get the current thread entry */ + CsrThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link); + + /* Make sure the thread isn't already dead */ + if (CsrThread->Flags & CsrThreadTerminated) + { + NextEntry = NextEntry->Flink; + continue; + } + + /* Set the Terminated flag */ + CsrThread->Flags |= CsrThreadTerminated; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Do we have an active wait block? */ + if (CsrThread->WaitBlock) + { + /* Notify waiters of termination */ + CsrNotifyWaitBlock(CsrThread->WaitBlock, + NULL, + NULL, + NULL, + CsrProcessTerminating, + TRUE); + } + + /* Release the Wait Lock */ + CsrReleaseWaitLock(); + + /* Dereference the thread */ + CsrLockedDereferenceThread(CsrThread); + NextEntry = CsrProcess->ThreadList.Flink; + } + + /* Release the Process Lock and return success */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrGetProcessLuid + * @implemented NT4 + * + * Do nothing for 500ms. + * + * @param hProcess + * Optional handle to the process whose LUID should be returned. + * + * @param Luid + * Pointer to a LUID Pointer which will receive the CSR Process' LUID + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks If hProcess is not supplied, then the current thread's token will + * be used. If that too is missing, then the current process' token + * will be used. + * + *--*/ +NTSTATUS +NTAPI +CsrGetProcessLuid(HANDLE hProcess OPTIONAL, + PLUID Luid) +{ + HANDLE hToken = NULL; + NTSTATUS Status; + ULONG Length; + PTOKEN_STATISTICS TokenStats; + + /* Check if we have a handle to a CSR Process */ + if (!hProcess) + { + /* We don't, so try opening the Thread's Token */ + Status = NtOpenThreadToken(NtCurrentThread(), + TOKEN_QUERY, + FALSE, + &hToken); + + /* Check for success */ + if (!NT_SUCCESS(Status)) + { + /* If we got some other failure, then return and quit */ + if (Status != STATUS_NO_TOKEN) return Status; + + /* We don't have a Thread Token, use a Process Token */ + hProcess = NtCurrentProcess(); + hToken = NULL; + } + } + + /* Check if we have a token by now */ + if (!hToken) + { + /* No token yet, so open the Process Token */ + Status = NtOpenProcessToken(hProcess, + TOKEN_QUERY, + &hToken); + if (!NT_SUCCESS(Status)) return Status; + } + + /* Now get the size we'll need for the Token Information */ + Status = NtQueryInformationToken(hToken, + TokenStatistics, + NULL, + 0, + &Length); + + /* Allocate memory for the Token Info */ + TokenStats = RtlAllocateHeap(CsrHeap, 0, Length); + if (!TokenStats) + { + /* Fail and close the token */ + NtClose(hToken); + return STATUS_NO_MEMORY; + } + + /* Now query the information */ + Status = NtQueryInformationToken(hToken, + TokenStatistics, + TokenStats, + Length, + &Length); + + /* Close the handle */ + NtClose(hToken); + + /* Check for success to return the LUID */ + if (NT_SUCCESS(Status)) *Luid = TokenStats->AuthenticationId; + + /* Free the query information */ + RtlFreeHeap(CsrHeap, 0, TokenStats); + + /* Return the Status */ + return Status; +} + +/*++ + * @name CsrLockProcessByClientId + * @implemented NT4 + * + * The CsrLockProcessByClientId routine locks the CSR Process corresponding + * to the given Process ID and optionally returns it. + * + * @param Pid + * Process ID corresponding to the CSR Process which will be locked. + * + * @param CsrProcess + * Optional pointer to a CSR Process pointer which will hold the + * CSR Process corresponding to the given Process ID. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks Locking a CSR Process is defined as acquiring an extra + * reference to it and returning with the Process Lock held. + * + *--*/ +NTSTATUS +NTAPI +CsrLockProcessByClientId(IN HANDLE Pid, + OUT PCSR_PROCESS *CsrProcess) +{ + PLIST_ENTRY NextEntry; + PCSR_PROCESS CurrentProcess = NULL; + NTSTATUS Status; + + /* Acquire the lock */ + CsrAcquireProcessLock(); + + /* Assume failure */ + ASSERT(CsrProcess != NULL); + *CsrProcess = NULL; + + /* Setup the List Pointers */ + NextEntry = &CsrRootProcess->ListLink; + do + { + /* Get the Process */ + CurrentProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink); + + /* Check for PID Match */ + if (CurrentProcess->ClientId.UniqueProcess == Pid) + { + Status = STATUS_SUCCESS; + break; + } + + /* Next entry */ + NextEntry = NextEntry->Flink; + } while (NextEntry != &CsrRootProcess->ListLink); + + /* Check if we didn't find it in the list */ + if (!NT_SUCCESS(Status)) + { + /* Nothing found, release the lock */ + CsrReleaseProcessLock(); + } + else + { + /* Lock the found process and return it */ + CsrLockedReferenceProcess(CurrentProcess); + *CsrProcess = CurrentProcess; + } + + /* Return the result */ + return Status; +} + +/*++ + * @name CsrSetForegroundPriority + * @implemented NT4 + * + * The CsrSetForegroundPriority routine sets the priority for the given CSR + * Process as a Foreground priority. + * + * @param CsrProcess + * Pointer to the CSR Process whose priority will be modified. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrSetForegroundPriority(IN PCSR_PROCESS CsrProcess) +{ + PROCESS_PRIORITY_CLASS PriorityClass; + + /* Set the Foreground bit on */ + PriorityClass.Foreground = TRUE; + + /* Set the new Priority */ + NtSetInformationProcess(CsrProcess->ProcessHandle, + ProcessPriorityClass, + &PriorityClass, + sizeof(PriorityClass)); +} + +/*++ + * @name CsrSetBackgroundPriority + * @implemented NT4 + * + * The CsrSetBackgroundPriority routine sets the priority for the given CSR + * Process as a Background priority. + * + * @param CsrProcess + * Pointer to the CSR Process whose priority will be modified. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrSetBackgroundPriority(IN PCSR_PROCESS CsrProcess) +{ + PROCESS_PRIORITY_CLASS PriorityClass; + + /* Set the Foreground bit off */ + PriorityClass.Foreground = FALSE; + + /* Set the new Priority */ + NtSetInformationProcess(CsrProcess->ProcessHandle, + ProcessPriorityClass, + &PriorityClass, + sizeof(PriorityClass)); +} + +/*++ + * @name CsrShutdownProcesses + * @implemented NT4 + * + * The CsrShutdownProcesses routine shuts down every CSR Process possible + * and calls each Server DLL's shutdown notification. + * + * @param CallerLuid + * Pointer to the LUID of the CSR Process that is ordering the + * shutdown. + * + * @param Flags + * Flags to send to the shutdown notification routine. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrShutdownProcesses(IN PLUID CallerLuid, + IN ULONG Flags) +{ + PLIST_ENTRY NextEntry; + PCSR_PROCESS CsrProcess; + NTSTATUS Status; + BOOLEAN FirstTry; + ULONG i; + PCSR_SERVER_DLL ServerDll; + ULONG Result = 0; /* Intentionally invalid enumeratee to silence compiler warning */ + + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Add shutdown flag */ + CsrRootProcess->ShutdownFlags |= CsrShutdownSystem; + + /* Get the list pointers */ + NextEntry = CsrRootProcess->ListLink.Flink; + while (NextEntry != &CsrRootProcess->ListLink) + { + /* Get the Process */ + CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink); + + /* Remove the skip flag, set shutdown flags to 0*/ + CsrProcess->Flags &= ~CsrProcessSkipShutdown; + CsrProcess->ShutdownFlags = 0; + + /* Move to the next */ + NextEntry = NextEntry->Flink; + } + + /* Set shudown Priority */ + CsrpSetToShutdownPriority(); + + /* Start looping */ + while (TRUE) + { + /* Find the next process to shutdown */ + CsrProcess = FindProcessForShutdown(CallerLuid); + if (!CsrProcess) break; + + /* Increase reference to process */ + CsrProcess->ReferenceCount++; + + FirstTry = TRUE; + while (TRUE) + { + /* Loop all the servers */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the current server */ + ServerDll = CsrLoadedServerDll[i]; + if ((ServerDll) && (ServerDll->ShutdownProcessCallback)) + { + /* Release the lock, make the callback, and acquire it back */ + CsrReleaseProcessLock(); + Result = ServerDll->ShutdownProcessCallback(CsrProcess, + Flags, + FirstTry); + CsrAcquireProcessLock(); + + /* Check the result */ + if (Result == CsrShutdownCsrProcess) + { + /* The callback unlocked the process */ + break; + } + else if (Result == CsrShutdownCancelled) + { + /* Check if this was a forced shutdown */ + if (Flags & EWX_FORCE) + { + DPRINT1("Process %x cancelled forced shutdown (Dll = %d)\n", + CsrProcess->ClientId.UniqueProcess, i); + DbgBreakPoint(); + } + + /* Shutdown was cancelled, unlock and exit */ + CsrReleaseProcessLock(); + Status = STATUS_CANCELLED; + goto Quickie; + } + } + } + + /* No matches during the first try, so loop again */ + if ((FirstTry) && (Result == CsrShutdownNonCsrProcess)) + { + FirstTry = FALSE; + continue; + } + + /* Second try, break out */ + break; + } + + /* We've reached the final loop here, so dereference */ + if (i == CSR_SERVER_DLL_MAX) CsrLockedDereferenceProcess(CsrProcess); + } + + /* Success path */ + CsrReleaseProcessLock(); + Status = STATUS_SUCCESS; + +Quickie: + /* Return to normal priority */ + CsrpSetToNormalPriority(); + return Status; +} + +/*++ + * @name CsrUnlockProcess + * @implemented NT4 + * + * The CsrUnlockProcess undoes a previous CsrLockProcessByClientId operation. + * + * @param CsrProcess + * Pointer to a previously locked CSR Process. + * + * @return STATUS_SUCCESS. + * + * @remarks This routine must be called with the Process Lock held. + * + *--*/ +NTSTATUS +NTAPI +CsrUnlockProcess(IN PCSR_PROCESS CsrProcess) +{ + /* Dereference the process */ + CsrLockedDereferenceProcess(CsrProcess); + + /* Release the lock and return */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/deprecated/csr/csrsrv/server.c b/reactos/deprecated/csr/csrsrv/server.c new file mode 100644 index 00000000000..12100055981 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/server.c @@ -0,0 +1,687 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrsrv/server.c + * PURPOSE: CSR Server DLL Server Functions + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +PCSR_API_ROUTINE CsrServerApiDispatchTable[5] = +{ + CsrSrvClientConnect, + CsrSrvUnusedFunction, + CsrSrvUnusedFunction, + CsrSrvIdentifyAlertableThread, + CsrSrvSetPriorityClass +}; + +BOOLEAN CsrServerApiServerValidTable[5] = +{ + TRUE, + FALSE, + TRUE, + TRUE, + TRUE +}; + +PCHAR CsrServerApiNameTable[5] = +{ + "ClientConnect", + "ThreadConnect", + "ProfileControl", + "IdentifyAlertableThread", + "SetPriorityClass" +}; + +PCSR_SERVER_DLL CsrLoadedServerDll[CSR_SERVER_DLL_MAX]; +PVOID CsrSrvSharedSectionHeap; +PVOID CsrSrvSharedSectionBase; +PVOID *CsrSrvSharedStaticServerData; +ULONG CsrSrvSharedSectionSize; +HANDLE CsrSrvSharedSection; + +/* PRIVATE FUNCTIONS**********************************************************/ + +/*++ + * @name CsrLoadServerDll + * @implemented NT4 + * + * The CsrLoadServerDll routine loads a CSR Server DLL and calls its entrypoint + * + * @param DllString + * Pointer to the CSR Server DLL to load and call. + * + * @param EntryPoint + * Pointer to the name of the server's initialization function. If + * this parameter is NULL, the default ServerDllInitialize will be + * assumed. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrLoadServerDll(IN PCHAR DllString, + IN PCHAR EntryPoint OPTIONAL, + IN ULONG ServerId) +{ + NTSTATUS Status; + ANSI_STRING DllName; + UNICODE_STRING TempString, ErrorString; + ULONG_PTR Parameters[2]; + HANDLE hServerDll = NULL; + ULONG Size; + PCSR_SERVER_DLL ServerDll; + STRING EntryPointString; + PCSR_SERVER_DLL_INIT_CALLBACK ServerDllInitProcedure; + ULONG Response; + + /* Check if it's beyond the maximum we support */ + if (ServerId >= CSR_SERVER_DLL_MAX) return STATUS_TOO_MANY_NAMES; + + /* Check if it's already been loaded */ + if (CsrLoadedServerDll[ServerId]) return STATUS_INVALID_PARAMETER; + + /* Convert the name to Unicode */ + ASSERT(DllString != NULL); + RtlInitAnsiString(&DllName, DllString); + Status = RtlAnsiStringToUnicodeString(&TempString, &DllName, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + /* If we are loading ourselves, don't actually load us */ + if (ServerId != CSR_SRV_SERVER) + { + /* Load the DLL */ + Status = LdrLoadDll(NULL, 0, &TempString, &hServerDll); + if (!NT_SUCCESS(Status)) + { + /* Setup error parameters */ + Parameters[0] = (ULONG_PTR)&TempString; + Parameters[1] = (ULONG_PTR)&ErrorString; + RtlInitUnicodeString(&ErrorString, L"Default Load Path"); + + /* Send a hard error */ + NtRaiseHardError(Status, + 2, + 3, + Parameters, + OptionOk, + &Response); + } + + /* Get rid of the string */ + RtlFreeUnicodeString(&TempString); + if (!NT_SUCCESS(Status)) return Status; + } + + /* Allocate a CSR DLL Object */ + Size = sizeof(CSR_SERVER_DLL) + DllName.MaximumLength; + ServerDll = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, Size); + if (!ServerDll) + { + if (hServerDll) LdrUnloadDll(hServerDll); + return STATUS_NO_MEMORY; + } + + /* Set up the Object */ + ServerDll->Length = Size; + ServerDll->SharedSection = CsrSrvSharedSectionHeap; + ServerDll->Event = CsrInitializationEvent; + ServerDll->Name.Length = DllName.Length; + ServerDll->Name.MaximumLength = DllName.MaximumLength; + ServerDll->Name.Buffer = (PCHAR)(ServerDll + 1); + if (DllName.Length) + { + strncpy(ServerDll->Name.Buffer, DllName.Buffer, DllName.Length); + } + ServerDll->ServerId = ServerId; + ServerDll->ServerHandle = hServerDll; + + /* Now get the entrypoint */ + if (hServerDll) + { + /* Initialize a string for the entrypoint, or use the default */ + RtlInitAnsiString(&EntryPointString, + !(EntryPoint) ? "ServerDllInitialization" : + EntryPoint); + + /* Get a pointer to it */ + Status = LdrGetProcedureAddress(hServerDll, + &EntryPointString, + 0, + (PVOID)&ServerDllInitProcedure); + } + else + { + /* No handle, so we are loading ourselves */ + ServerDllInitProcedure = CsrServerDllInitialization; + Status = STATUS_SUCCESS; + } + + /* Check if we got the pointer, and call it */ + if (NT_SUCCESS(Status)) + { + /* Get the result from the Server DLL */ + Status = ServerDllInitProcedure(ServerDll); + + /* Check for Success */ + if (NT_SUCCESS(Status)) + { + /* + * Add this Server's Per-Process Data Size to the total that each + * process will need. + */ + CsrTotalPerProcessDataLength += ServerDll->SizeOfProcessData; + + /* Save the pointer in our list */ + CsrLoadedServerDll[ServerDll->ServerId] = ServerDll; + + /* Does it use our generic heap? */ + if (ServerDll->SharedSection != CsrSrvSharedSectionHeap) + { + /* No, save the pointer to its shared section in our list */ + CsrSrvSharedStaticServerData[ServerDll->ServerId] = ServerDll->SharedSection; + } + } + else + { + /* Use shared failure code */ + goto LoadFailed; + } + } + else + { +LoadFailed: + /* Server Init failed, unload it */ + if (hServerDll) LdrUnloadDll(hServerDll); + + /* Delete the Object */ + RtlFreeHeap(CsrHeap, 0, ServerDll); + } + + /* Return to caller */ + return Status; +} + +/*++ + * @name CsrServerDllInitialization + * @implemented NT4 + * + * The CsrServerDllInitialization is the initialization routine for + * the this Server DLL. + * + * @param LoadedServerDll + * Pointer to the CSR Server DLL structure representing this Server DLL. + * + * @return STATUS_SUCCESS. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrServerDllInitialization(IN PCSR_SERVER_DLL LoadedServerDll) +{ + /* Setup the DLL Object */ + LoadedServerDll->ApiBase = 0; + LoadedServerDll->HighestApiSupported = 5; + LoadedServerDll->DispatchTable = CsrServerApiDispatchTable; + LoadedServerDll->ValidTable = CsrServerApiServerValidTable; + LoadedServerDll->NameTable = CsrServerApiNameTable; + LoadedServerDll->SizeOfProcessData = 0; + LoadedServerDll->ConnectCallback = NULL; + LoadedServerDll->DisconnectCallback = NULL; + + /* All done */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvClientConnect + * + * The CsrSrvClientConnect CSR API handles a new connection to a server DLL. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Optional reply to this request. + * + * @return STATUS_SUCCESS in case of success, STATUS_INVALID_PARAMETER + * or STATUS_TOO_MANY_NAMES in case of failure. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvClientConnect(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply OPTIONAL) +{ + NTSTATUS Status; + PCSR_CLIENT_CONNECT ClientConnect; + PCSR_SERVER_DLL ServerDll; + PCSR_PROCESS CurrentProcess = ((PCSR_THREAD)NtCurrentTeb()->CsrClientThread)->Process; + + /* Load the Message, set default reply */ + ClientConnect = (PCSR_CLIENT_CONNECT)&ApiMessage->CsrClientConnect; + *Reply = 0; + + /* Validate the ServerID */ + if (ClientConnect->ServerId >= CSR_SERVER_DLL_MAX) + { + return STATUS_TOO_MANY_NAMES; + } + else if (!CsrLoadedServerDll[ClientConnect->ServerId]) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validate the Message Buffer */ + if (!(CsrValidateMessageBuffer(ApiMessage, + ClientConnect->ConnectionInfo, + ClientConnect->ConnectionInfoSize, + 1))) + { + /* Fail due to buffer overflow or other invalid buffer */ + return STATUS_INVALID_PARAMETER; + } + + /* Load the Server DLL */ + ServerDll = CsrLoadedServerDll[ClientConnect->ServerId]; + + /* Check if it has a Connect Callback */ + if (ServerDll->ConnectCallback) + { + /* Call the callback */ + Status = ServerDll->ConnectCallback(CurrentProcess, + ClientConnect->ConnectionInfo, + &ClientConnect->ConnectionInfoSize); + } + else + { + /* Assume success */ + Status = STATUS_SUCCESS; + } + + /* Return status */ + return Status; +} + +/*++ + * @name CsrSrvCreateSharedSection + * + * The CsrSrvCreateSharedSection creates the Shared Section that all CSR Server + * DLLs and Clients can use to share data. + * + * @param ParameterValue + * Specially formatted string from our registry command-line which + * specifies various arguments for the shared section. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvCreateSharedSection(IN PCHAR ParameterValue) +{ + PCHAR SizeValue = ParameterValue; + ULONG Size; + NTSTATUS Status; + LARGE_INTEGER SectionSize; + ULONG ViewSize = 0; + PPEB Peb = NtCurrentPeb(); + + /* If there's no parameter, fail */ + if (!ParameterValue) return STATUS_INVALID_PARAMETER; + + /* Find the first comma, and null terminate */ + while (*SizeValue) + { + if (*SizeValue == ',') + { + *SizeValue++ = ANSI_NULL; + break; + } + else + { + SizeValue++; + } + } + + /* Make sure it's valid */ + if (!*SizeValue) return STATUS_INVALID_PARAMETER; + + /* Convert it to an integer */ + Status = RtlCharToInteger(SizeValue, 0, &Size); + if (!NT_SUCCESS(Status)) return Status; + + /* Multiply by 1024 entries and round to page size */ + CsrSrvSharedSectionSize = ROUND_UP(Size * 1024, CsrNtSysInfo.PageSize); + + /* Create the Secion */ + SectionSize.LowPart = CsrSrvSharedSectionSize; + SectionSize.HighPart = 0; + Status = NtCreateSection(&CsrSrvSharedSection, + SECTION_ALL_ACCESS, + NULL, + &SectionSize, + PAGE_EXECUTE_READWRITE, + SEC_BASED | SEC_RESERVE, + NULL); + if (!NT_SUCCESS(Status)) return Status; + + /* Map the section */ + Status = NtMapViewOfSection(CsrSrvSharedSection, + NtCurrentProcess(), + &CsrSrvSharedSectionBase, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + MEM_TOP_DOWN, + PAGE_EXECUTE_READWRITE); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + NtClose(CsrSrvSharedSection); + return Status; + } + + /* FIXME: Write the value to registry */ + + /* The Heap is the same place as the Base */ + CsrSrvSharedSectionHeap = CsrSrvSharedSectionBase; + + /* Create the heap */ + if (!(RtlCreateHeap(HEAP_ZERO_MEMORY | HEAP_CLASS_7, + CsrSrvSharedSectionHeap, + CsrSrvSharedSectionSize, + PAGE_SIZE, + 0, + 0))) + { + /* Failure, unmap section and return */ + NtUnmapViewOfSection(NtCurrentProcess(), CsrSrvSharedSectionBase); + NtClose(CsrSrvSharedSection); + return STATUS_NO_MEMORY; + } + + /* Now allocate space from the heap for the Shared Data */ + CsrSrvSharedStaticServerData = RtlAllocateHeap(CsrSrvSharedSectionHeap, + 0, + CSR_SERVER_DLL_MAX * + sizeof(PVOID)); + if (!CsrSrvSharedStaticServerData) return STATUS_NO_MEMORY; + + /* Write the values to the PEB */ + Peb->ReadOnlySharedMemoryBase = CsrSrvSharedSectionBase; + Peb->ReadOnlySharedMemoryHeap = CsrSrvSharedSectionHeap; + Peb->ReadOnlyStaticServerData = CsrSrvSharedStaticServerData; + + /* Return */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvAttachSharedSection + * + * The CsrSrvAttachSharedSection maps the CSR Shared Section into a new + * CSR Process' address space, and returns the pointers to the section + * through the Connection Info structure. + * + * @param CsrProcess + * Pointer to the CSR Process that is attempting a connection. + * + * @param ConnectInfo + * Pointer to the CSR Connection Info structure for the incoming + * connection. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvAttachSharedSection(IN PCSR_PROCESS CsrProcess OPTIONAL, + OUT PCSR_CONNECTION_INFO ConnectInfo) +{ + NTSTATUS Status; + ULONG ViewSize = 0; + + /* Check if we have a process */ + if (CsrProcess) + { + /* Map the sectio into this process */ + Status = NtMapViewOfSection(CsrSrvSharedSection, + CsrProcess->ProcessHandle, + &CsrSrvSharedSectionBase, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + SEC_NO_CHANGE, + PAGE_EXECUTE_READ); + if (!NT_SUCCESS(Status)) return Status; + } + + /* Write the values in the Connection Info structure */ + ConnectInfo->SharedSectionBase = CsrSrvSharedSectionBase; + ConnectInfo->SharedSectionHeap = CsrSrvSharedSectionHeap; + ConnectInfo->SharedSectionData = CsrSrvSharedStaticServerData; + + /* Return success */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvIdentifyAlertableThread + * @implemented NT4 + * + * The CsrSrvIdentifyAlertableThread CSR API marks a CSR Thread as alertable. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Pointer to an optional reply to this request. + * + * @return STATUS_SUCCESS. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvIdentifyAlertableThread(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply) +{ + PCSR_THREAD CsrThread = NtCurrentTeb()->CsrClientThread; + + /* Set the alertable flag */ + CsrThread->Flags |= CsrThreadAltertable; + + /* Return success */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvSetPriorityClass + * @implemented NT4 + * + * The CsrSrvSetPriorityClass CSR API is deprecated. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Pointer to an optional reply to this request. + * + * @return STATUS_SUCCESS. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvSetPriorityClass(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply) +{ + /* Deprecated */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvUnusedFunction + * @implemented NT4 + * + * The CsrSrvUnusedFunction CSR API is a stub for deprecated APIs. + * + * The CsrSrvSetPriorityClass CSR API is deprecated. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Pointer to an optional reply to this request. + * + * @return STATUS_INVALID_PARAMETER. + * + * @remarks CsrSrvSetPriorityClass does not use this stub because it must + * return success. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvUnusedFunction(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply) +{ + /* Deprecated */ + return STATUS_INVALID_PARAMETER; +} + +/* PUBLIC FUNCTIONS***********************************************************/ + +/*++ + * @name CsrSetCallingSpooler + * @implemented NT4 + * + * the CsrSetCallingSpooler routine is deprecated. + * + * @param Reserved + * Deprecated + * + * @return None. + * + * @remarks This routine was used in archaic versions of NT for Printer Drivers. + * + *--*/ +VOID +NTAPI +CsrSetCallingSpooler(ULONG Reserved) +{ + /* Deprecated */ + return; +} + +/*++ + * @name CsrUnhandledExceptionFilter + * @implemented NT5 + * + * The CsrUnhandledExceptionFilter routine handles all exceptions + * within SEH-protected blocks. + * + * @param ExceptionPointers + * System-defined Argument. + * + * @return EXCEPTION_EXECUTE_HANDLER. + * + * @remarks None. + * + *--*/ +LONG +NTAPI +CsrUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo) +{ + SYSTEM_KERNEL_DEBUGGER_INFORMATION DebuggerInfo; + EXCEPTION_DISPOSITION Result = EXCEPTION_EXECUTE_HANDLER; + BOOLEAN OldValue; + NTSTATUS Status; + UNICODE_STRING ErrorSource; + ULONG_PTR ErrorParameters[4]; + ULONG Response; + + /* Check if a debugger is installed */ + Status = NtQuerySystemInformation(SystemKernelDebuggerInformation, + &DebuggerInfo, + sizeof(DebuggerInfo), + NULL); + + /* Check if this is Session 0, and the Debugger is Enabled */ + if ((NtCurrentPeb()->SessionId) && (NT_SUCCESS(Status)) && + (DebuggerInfo.KernelDebuggerEnabled)) + { + /* Call the Unhandled Exception Filter */ + if ((Result = RtlUnhandledExceptionFilter(ExceptionInfo)) != + EXCEPTION_CONTINUE_EXECUTION) + { + /* We're going to raise an error. Get Shutdown Privilege first */ + Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, + TRUE, + TRUE, + &OldValue); + + /* Use the Process token if that failed */ + if (Status == STATUS_NO_TOKEN) + { + Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, + TRUE, + FALSE, + &OldValue); + } + + /* Initialize our Name String */ + RtlInitUnicodeString(&ErrorSource, L"Windows SubSystem"); + + /* Set the parameters */ + ErrorParameters[0] = (ULONG_PTR)&ErrorSource; + ErrorParameters[1] = ExceptionInfo->ExceptionRecord->ExceptionCode; + ErrorParameters[2] = (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress; + ErrorParameters[3] = (ULONG_PTR)ExceptionInfo->ContextRecord; + + /* Bugcheck */ + Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, + 4, + 1, + ErrorParameters, + OptionShutdownSystem, + &Response); + } + + /* Just terminate us */ + NtTerminateProcess(NtCurrentProcess(), + ExceptionInfo->ExceptionRecord->ExceptionCode); + } + + return Result; +} + +/* EOF */ diff --git a/reactos/deprecated/csr/csrsrv/session.c b/reactos/deprecated/csr/csrsrv/session.c new file mode 100644 index 00000000000..2ec08a66a4f --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/session.c @@ -0,0 +1,407 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrsrv/session.c + * PURPOSE: CSR Server DLL Session Implementation + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +RTL_CRITICAL_SECTION CsrNtSessionLock; +LIST_ENTRY CsrNtSessionList; +HANDLE CsrSmApiPort; + +PSB_API_ROUTINE CsrServerSbApiDispatch[5] = +{ + CsrSbCreateSession, + CsrSbTerminateSession, + CsrSbForeignSessionComplete, + CsrSbCreateProcess, + NULL +}; + +PCHAR CsrServerSbApiName[5] = +{ + "SbCreateSession", + "SbTerminateSession", + "SbForeignSessionComplete", + "SbCreateProcess", + "Unknown Csr Sb Api Number" +}; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name CsrInitializeNtSessionList + * + * The CsrInitializeNtSessionList routine sets up support for CSR Sessions. + * + * @param None + * + * @return None + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrInitializeNtSessionList(VOID) +{ + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + + /* Initialize the Session List */ + InitializeListHead(&CsrNtSessionList); + + /* Initialize the Session Lock */ + return RtlInitializeCriticalSection(&CsrNtSessionLock); +} + +/*++ + * @name CsrAllocateNtSession + * + * The CsrAllocateNtSession routine allocates a new CSR NT Session. + * + * @param SessionId + * Session ID of the CSR NT Session to allocate. + * + * @return Pointer to the newly allocated CSR NT Session. + * + * @remarks None. + * + *--*/ +PCSR_NT_SESSION +NTAPI +CsrAllocateNtSession(IN ULONG SessionId) +{ + PCSR_NT_SESSION NtSession; + + /* Allocate an NT Session Object */ + NtSession = RtlAllocateHeap(CsrHeap, 0, sizeof(CSR_NT_SESSION)); + if (NtSession) + { + /* Setup the Session Object */ + NtSession->SessionId = SessionId; + NtSession->ReferenceCount = 1; + + /* Insert it into the Session List */ + CsrAcquireNtSessionLock(); + InsertHeadList(&CsrNtSessionList, &NtSession->SessionLink); + CsrReleaseNtSessionLock(); + } + else + { + ASSERT(NtSession != NULL); + } + + /* Return the Session (or NULL) */ + return NtSession; +} + +/*++ + * @name CsrReferenceNtSession + * + * The CsrReferenceNtSession increases the reference count of a CSR NT Session. + * + * @param Session + * Pointer to the CSR NT Session to reference. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrReferenceNtSession(IN PCSR_NT_SESSION Session) +{ + /* Acquire the lock */ + CsrAcquireNtSessionLock(); + + /* Sanity checks */ + ASSERT(!IsListEmpty(&Session->SessionLink)); + ASSERT(Session->SessionId != 0); + ASSERT(Session->ReferenceCount != 0); + + /* Increase the reference count */ + Session->ReferenceCount++; + + /* Release the lock */ + CsrReleaseNtSessionLock(); +} + +/*++ + * @name CsrDereferenceNtSession + * + * The CsrDereferenceNtSession decreases the reference count of a + * CSR NT Session. + * + * @param Session + * Pointer to the CSR NT Session to reference. + * + * @param ExitStatus + * If this is the last reference to the session, this argument + * specifies the exit status. + * + * @return None. + * + * @remarks CsrDereferenceNtSession will complete the session if + * the last reference to it has been closed. + * + *--*/ +VOID +NTAPI +CsrDereferenceNtSession(IN PCSR_NT_SESSION Session, + IN NTSTATUS ExitStatus) +{ + /* Acquire the lock */ + CsrAcquireNtSessionLock(); + + /* Sanity checks */ + ASSERT(!IsListEmpty(&Session->SessionLink)); + ASSERT(Session->SessionId != 0); + ASSERT(Session->ReferenceCount != 0); + + /* Dereference the Session Object */ + if (!(--Session->ReferenceCount)) + { + /* Remove it from the list */ + RemoveEntryList(&Session->SessionLink); + + /* Release the lock */ + CsrReleaseNtSessionLock(); + + /* Tell SM that we're done here */ + SmSessionComplete(CsrSmApiPort, Session->SessionId, ExitStatus); + + /* Free the Session Object */ + RtlFreeHeap(CsrHeap, 0, Session); + } + else + { + /* Release the lock, the Session is still active */ + CsrReleaseNtSessionLock(); + } +} + + +/* SESSION MANAGER FUNCTIONS**************************************************/ + +/*++ + * @name CsrSbCreateSession + * + * The CsrSbCreateSession API is called by the Session Manager whenever a new + * session is created. + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE othwerwise. + * + * @remarks The CsrSbCreateSession routine will initialize a new CSR NT + * Session and allocate a new CSR Process for the subsystem process. + * + *--*/ +BOOLEAN +NTAPI +CsrSbCreateSession(IN PSB_API_MSG ApiMessage) +{ + PSB_CREATE_SESSION_MSG CreateSession = &ApiMessage->CreateSession; + HANDLE hProcess, hThread; + PCSR_PROCESS CsrProcess; + NTSTATUS Status; + KERNEL_USER_TIMES KernelTimes; + PCSR_THREAD CsrThread; + PVOID ProcessData; + ULONG i; + + /* Save the Process and Thread Handles */ + hProcess = CreateSession->ProcessInfo.ProcessHandle; + hThread = CreateSession->ProcessInfo.ThreadHandle; + + /* Lock the Processes */ + CsrAcquireProcessLock(); + + /* Allocate a new process */ + CsrProcess = CsrAllocateProcess(); + if (!CsrProcess) + { + /* Fail */ + ApiMessage->ReturnValue = STATUS_NO_MEMORY; + CsrReleaseProcessLock(); + return TRUE; + } + + /* Set the exception port */ + Status = NtSetInformationProcess(hProcess, + ProcessExceptionPort, + &CsrApiPort, + sizeof(HANDLE)); + + /* Check for success */ + if (!NT_SUCCESS(Status)) + { + /* Fail the request */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + + /* Strange as it seems, NTSTATUSes are actually returned */ + return (BOOLEAN)STATUS_NO_MEMORY; + } + + /* Get the Create Time */ + Status = NtQueryInformationThread(hThread, + ThreadTimes, + &KernelTimes, + sizeof(KERNEL_USER_TIMES), + NULL); + + /* Check for success */ + if (!NT_SUCCESS(Status)) + { + /* Fail the request */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + + /* Strange as it seems, NTSTATUSes are actually returned */ + return (BOOLEAN)Status; + } + + /* Allocate a new Thread */ + CsrThread = CsrAllocateThread(CsrProcess); + if (!CsrThread) + { + /* Fail the request */ + CsrDeallocateProcess(CsrProcess); + ApiMessage->ReturnValue = STATUS_NO_MEMORY; + CsrReleaseProcessLock(); + return TRUE; + } + + /* Setup the Thread Object */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = CreateSession->ProcessInfo.ClientId; + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->Flags = 0; + + /* Insert it into the Process List */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Setup Process Data */ + CsrProcess->ClientId = CreateSession->ProcessInfo.ClientId; + CsrProcess->ProcessHandle = hProcess; + CsrProcess->NtSession = CsrAllocateNtSession(CreateSession->SessionId); + + /* Set the Process Priority */ + CsrSetBackgroundPriority(CsrProcess); + + /* Get the first data location */ + ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX]; + + /* Loop every DLL */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Check if the DLL is loaded and has Process Data */ + if (CsrLoadedServerDll[i] && CsrLoadedServerDll[i]->SizeOfProcessData) + { + /* Write the pointer to the data */ + CsrProcess->ServerData[i] = ProcessData; + + /* Move to the next data location */ + ProcessData = (PVOID)((ULONG_PTR)ProcessData + + CsrLoadedServerDll[i]->SizeOfProcessData); + } + else + { + /* Nothing for this Process */ + CsrProcess->ServerData[i] = NULL; + } + } + + /* Insert the Process */ + CsrInsertProcess(NULL, NULL, CsrProcess); + + /* Activate the Thread */ + ApiMessage->ReturnValue = NtResumeThread(hThread, NULL); + + /* Release lock and return */ + CsrReleaseProcessLock(); + return TRUE; +} + +/*++ + * @name CsrSbForeignSessionComplete + * + * The CsrSbForeignSessionComplete API is called by the Session Manager + * whenever a foreign session is completed (ie: terminated). + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE othwerwise. + * + * @remarks The CsrSbForeignSessionComplete API is not yet implemented. + * + *--*/ +BOOLEAN +NTAPI +CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage) +{ + /* Deprecated/Unimplemented in NT */ + ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; + return TRUE; +} + +/*++ + * @name CsrSbTerminateSession + * + * The CsrSbTerminateSession API is called by the Session Manager + * whenever a foreign session should be destroyed. + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE othwerwise. + * + * @remarks The CsrSbTerminateSession API is not yet implemented. + * + *--*/ +BOOLEAN +NTAPI +CsrSbTerminateSession(IN PSB_API_MSG ApiMessage) +{ + ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; + return TRUE; +} + +/*++ + * @name CsrSbCreateProcess + * + * The CsrSbCreateProcess API is called by the Session Manager + * whenever a foreign session is created and a new process should be started. + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE othwerwise. + * + * @remarks The CsrSbCreateProcess API is not yet implemented. + * + *--*/ +BOOLEAN +NTAPI +CsrSbCreateProcess(IN PSB_API_MSG ApiMessage) +{ + ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; + return TRUE; +} + +/* EOF */ diff --git a/reactos/deprecated/csr/csrsrv/srv.h b/reactos/deprecated/csr/csrsrv/srv.h new file mode 100644 index 00000000000..48a9f0f1345 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/srv.h @@ -0,0 +1,395 @@ +#ifndef _SRV_H +#define _SRV_H + +/* PSDK/NDK Headers */ +#define NTOS_MODE_USER +#include +#define WIN32_NO_STATUS +#include +#include +#include + +/* CSR Header */ +#include +#include + +/* PSEH for SEH Support */ +#include + +/* DEFINES *******************************************************************/ + +#define CSR_SERVER_DLL_MAX 4 + +#define CsrAcquireProcessLock() \ + RtlEnterCriticalSection(&CsrProcessLock); + +#define CsrReleaseProcessLock() \ + RtlLeaveCriticalSection(&CsrProcessLock); + +#define CsrAcquireWaitLock() \ + RtlEnterCriticalSection(&CsrWaitListsLock); + +#define CsrReleaseWaitLock() \ + RtlLeaveCriticalSection(&CsrWaitListsLock); + +#define CsrAcquireNtSessionLock() \ + RtlEnterCriticalSection(&CsrNtSessionLock) + +#define CsrReleaseNtSessionLock() \ + RtlLeaveCriticalSection(&CsrNtSessionLock) + +#define CsrHashThread(t) \ + (HandleToUlong(t)&(256 - 1)) + +#define ProcessStructureListLocked() \ + (CsrProcessLock.OwningThread == NtCurrentTeb()->ClientId.UniqueThread) + +#define SM_REG_KEY \ + L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" + +#define SESSION_ROOT L"\\Sessions" +#define GLOBAL_ROOT L"\\GLOBAL??" +#define SYMLINK_NAME L"SymbolicLink" +#define SB_PORT_NAME L"SbAbiPort" +#define CSR_PORT_NAME L"ApiPort" +#define UNICODE_PATH_SEP L"\\" + +#define ROUND_UP(n, align) ROUND_DOWN(((ULONG)n) + (align) - 1, (align)) +#define ROUND_DOWN(n, align) (((ULONG)n) & ~((align) - 1l)) + +/* DATA **********************************************************************/ + +extern ULONG CsrTotalPerProcessDataLength; +extern ULONG CsrMaxApiRequestThreads; +extern PCSR_SERVER_DLL CsrLoadedServerDll[CSR_SERVER_DLL_MAX]; +extern PCSR_PROCESS CsrRootProcess; +extern UNICODE_STRING CsrSbApiPortName; +extern UNICODE_STRING CsrApiPortName; +extern HANDLE CsrSbApiPort; +extern HANDLE CsrSmApiPort; +extern HANDLE CsrApiPort; +extern HANDLE CsrHeap; +extern RTL_CRITICAL_SECTION CsrProcessLock; +extern RTL_CRITICAL_SECTION CsrWaitListsLock; +extern LIST_ENTRY CsrThreadHashTable[256]; +extern HANDLE CsrInitializationEvent; +extern SYSTEM_BASIC_INFORMATION CsrNtSysInfo; +extern UNICODE_STRING CsrDirectoryName; +extern HANDLE CsrObjectDirectory; +extern PSB_API_ROUTINE CsrServerSbApiDispatch[5]; +extern ULONG CsrDebug; + +/* FUNCTIONS *****************************************************************/ + +/* FIXME: Public APIs should go in the CSR Server Include */ +BOOLEAN +NTAPI +CsrCaptureArguments( + IN PCSR_THREAD CsrThread, + IN PCSR_API_MESSAGE ApiMessage +); + +NTSTATUS +NTAPI +CsrLoadServerDll( + IN PCHAR DllString, + IN PCHAR EntryPoint, + IN ULONG ServerId +); + +NTSTATUS +NTAPI +CsrServerInitialization( + ULONG ArgumentCount, + PCHAR Arguments[] +); + +BOOLEAN +NTAPI +UnProtectHandle(IN HANDLE ObjectHandle); + +VOID +NTAPI +CsrLockedReferenceProcess(IN PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrLockedReferenceThread(IN PCSR_THREAD CsrThread); + +NTSTATUS +NTAPI +CsrCreateSessionObjectDirectory(IN ULONG SessionId); + +NTSTATUS +NTAPI +CsrCreateObjectDirectory(IN PCHAR ObjectDirectory); + +NTSTATUS +NTAPI +CsrSrvCreateSharedSection(IN PCHAR ParameterValue); + +NTSTATUS +NTAPI +CsrInitializeNtSessionList(VOID); + +NTSTATUS +NTAPI +CsrInitializeProcessStructure(VOID); + +NTSTATUS +NTAPI +CsrApiPortInitialize(VOID); + +NTSTATUS +NTAPI +CsrSbApiPortInitialize(VOID); + +BOOLEAN +NTAPI +CsrSbCreateSession(IN PSB_API_MSG ApiMessage); + +BOOLEAN +NTAPI +CsrSbTerminateSession(IN PSB_API_MSG ApiMessage); + +BOOLEAN +NTAPI +CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage); + +BOOLEAN +NTAPI +CsrSbCreateProcess(IN PSB_API_MSG ApiMessage); + +PCSR_PROCESS +NTAPI +CsrAllocateProcess(VOID); + +PCSR_THREAD +NTAPI +CsrAllocateThread(IN PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrInsertThread( + IN PCSR_PROCESS Process, + IN PCSR_THREAD Thread +); + +VOID +NTAPI +CsrSetBackgroundPriority(IN PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrDeallocateProcess(IN PCSR_PROCESS CsrProcess); + +NTSTATUS +NTAPI +CsrGetProcessLuid( + HANDLE hProcess, + PLUID Luid +); + +BOOLEAN +NTAPI +CsrImpersonateClient(IN PCSR_THREAD CsrThread); + +BOOLEAN +NTAPI +CsrRevertToSelf(VOID); + +PCSR_THREAD +NTAPI +CsrLocateThreadByClientId( + OUT PCSR_PROCESS *Process, + IN PCLIENT_ID ClientId +); + +VOID +NTAPI +CsrDereferenceNtSession( + IN PCSR_NT_SESSION Session, + NTSTATUS ExitStatus +); + +VOID +NTAPI +CsrReferenceNtSession(PCSR_NT_SESSION Session); + +VOID +NTAPI +CsrLockedDereferenceThread(PCSR_THREAD CsrThread); + +VOID +NTAPI +CsrLockedDereferenceProcess(PCSR_PROCESS CsrProcess); + +NTSTATUS +NTAPI +CsrLockProcessByClientId( + IN HANDLE Pid, + OUT PCSR_PROCESS *CsrProcess OPTIONAL +); + +NTSTATUS +NTAPI +CsrUnlockProcess(PCSR_PROCESS CsrProcess); + +NTSTATUS +NTAPI +CsrSrvClientConnect( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrSrvUnusedFunction( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrSrvIdentifyAlertableThread( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrSrvSetPriorityClass( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrServerDllInitialization(IN PCSR_SERVER_DLL LoadedServerDll); + +VOID +NTAPI +CsrDereferenceThread(PCSR_THREAD CsrThread); + +VOID +NTAPI +CsrSbApiRequestThread(IN PVOID Parameter); + +NTSTATUS +NTAPI +CsrApiRequestThread(IN PVOID Parameter); + +BOOLEAN +NTAPI +ProtectHandle(IN HANDLE ObjectHandle); + +PCSR_THREAD +NTAPI +CsrAddStaticServerThread( + IN HANDLE hThread, + IN PCLIENT_ID ClientId, + IN ULONG ThreadFlags +); + +PCSR_THREAD +NTAPI +CsrConnectToUser(VOID); + +PCSR_THREAD +NTAPI +CsrLocateThreadInProcess( + IN PCSR_PROCESS CsrProcess OPTIONAL, + IN PCLIENT_ID Cid +); + +NTSTATUS +NTAPI +CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message); + +NTSTATUS +NTAPI +CsrApiHandleConnectionRequest(IN PCSR_API_MESSAGE ApiMessage); + +NTSTATUS +NTAPI +CsrSrvAttachSharedSection( + IN PCSR_PROCESS CsrProcess OPTIONAL, + OUT PCSR_CONNECTION_INFO ConnectInfo +); + +VOID +NTAPI +CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage); + +BOOLEAN +NTAPI +CsrNotifyWaitBlock( + IN PCSR_WAIT_BLOCK WaitBlock, + IN PLIST_ENTRY WaitList, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags, + IN BOOLEAN DereferenceThread +); + +VOID +NTAPI +CsrDereferenceProcess(PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrInsertProcess( + IN PCSR_PROCESS Parent OPTIONAL, + IN PCSR_PROCESS CurrentProcess OPTIONAL, + IN PCSR_PROCESS CsrProcess +); + +NTSTATUS +NTAPI +CsrPopulateDosDevicesDirectory( + IN HANDLE DosDevicesDirectory, + IN PPROCESS_DEVICEMAP_INFORMATION DeviceMap +); + +BOOLEAN +NTAPI +CsrValidateMessageBuffer( + IN PCSR_API_MESSAGE ApiMessage, + IN PVOID *Buffer, + IN ULONG ArgumentSize, + IN ULONG ArgumentCount +); + +NTSTATUS +NTAPI +CsrCreateLocalSystemSD(OUT PSECURITY_DESCRIPTOR *LocalSystemSd); + +NTSTATUS +NTAPI +CsrDestroyThread(IN PCLIENT_ID Cid); + +NTSTATUS +NTAPI +CsrDestroyProcess( + IN PCLIENT_ID Cid, + IN NTSTATUS ExitStatus +); + +LONG +NTAPI +CsrUnhandledExceptionFilter( + IN PEXCEPTION_POINTERS ExceptionInfo +); + +VOID +NTAPI +CsrProcessRefcountZero(IN PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrThreadRefcountZero(IN PCSR_THREAD CsrThread); + +NTSTATUS +NTAPI +CsrSetDirectorySecurity(IN HANDLE ObjectDirectory); +#endif diff --git a/reactos/deprecated/csr/csrsrv/status.h b/reactos/deprecated/csr/csrsrv/status.h new file mode 100644 index 00000000000..e619443abd3 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/status.h @@ -0,0 +1,111 @@ +/* + * CSRSRV Status + */ + +/* Organization + * + * api.c - Handles the LPC Reply/Request Threads which wait on Sb and Csr APIs. + * Also in charge of creating those threads and setting up the ports. + * Finally, it provides external APIs for validating the API buffers + * and doing server-to-server API calls. + * + * init.c - Handles initialization of CSRSRV, including command-line parsing, + * loading the Server DLLs, creating the Session Directories, setting + * up the DosDevices Object Directory, and initializing each component. + * + * process.c - Handles all internal functions dealing with the CSR Process Object, + * including de/allocation, de/referencing, un/locking, prority, and + * lookups. Also handles all external APIs which touch the CSR Process Object. + * + * server.c - Handles all internal functions related to loading and managing Server + * DLLs, as well as the routines handling the Shared Static Memory Section. + * Holds the API Dispatch/Valid/Name Tables and the public CSR_SERVER API + * interface. Also home of the SEH handler. + * + * session.c - Handles all internal functions dealing with the CSR Session Object, + * including de/allocation, de/referencing, and un/locking. Holds the SB API + * Dispatch/Name Tables and the public CsrSv API Interface for commmunication + * with the Session Manager. + * + * thread.c - Handles all internal functions dealing with the CSR Thread Object, + * including de/allocation, de/referencing, un/locking, impersonation, and + * lookups. Also handles all external APIs which touch the CSR Thread Object. + * + * wait.c - Handles all internal functions dealing with the CSR Wait Object, + * including de/allocation, de/referencing and un/locking. Also implements + * the external Wait API for creating, removing and/or notifying waits. + */ + +/* Exported APIs, their location, and their status + * CsrAddStaticServerThread 753E679E 1 - server.c - IMPLEMENTED + * CsrCallServerFromServer 753E4FD9 2 - api.c - IMPLEMENTED + * CsrConnectToUser 753E4E48 3 - api.c - IMPLEMENTED + * CsrCreateProcess 753E6FD3 4 - process.c - IMPLEMENTED + * CsrCreateRemoteThread 753E73BD 5 - thread.c - IMPLEMENTED + * CsrCreateThread 753E72DA 6 - thread.c - IMPLEMENTED + * CsrCreateWait 753E770E 7 - wait.c - IMPLEMENTED + * CsrDebugProcess 753E7682 8 - process.c - IMPLEMENTED + * CsrDebugProcessStop 753E768A 9 - process.c - IMPLEMENTED + * CsrDereferenceProcess 753E6281 10 - process.c - IMPLEMENTED + * CsrDereferenceThread 753E6964 11 - thread.c - IMPLEMENTED + * CsrDereferenceWait 753E7886 12 - wait.c - IMPLEMENTED + * CsrDestroyProcess 753E7225 13 - process.c - IMPLEMENTED + * CsrDestroyThread 753E7478 14 - thread.c - IMPLEMENTED + * CsrExecServerThread 753E6841 15 - thread.c - IMPLEMENTED + * CsrGetProcessLuid 753E632F 16 - process.c - IMPLEMENTED + * CsrImpersonateClient 753E60F8 17 - thread.c - IMPLEMENTED + * CsrLockProcessByClientId 753E668F 18 - process.c - IMPLEMENTED + * CsrLockThreadByClientId 753E6719 19 - thread.c - IMPLEMENTED + * CsrMoveSatisfiedWait 753E7909 20 - wait.c - IMPLEMENTED + * CsrNotifyWait 753E782F 21 - wait.c - IMPLEMENTED + * CsrPopulateDosDevices 753E37A5 22 - init.c - IMPLEMENTED + * CsrQueryApiPort 753E4E42 23 - api.c - IMPLEMENTED + * CsrReferenceThread 753E61E5 24 - thread.c - IMPLEMENTED + * CsrRevertToSelf 753E615A 25 - thread.c - IMPLEMENTED + * CsrServerInitialization 753E3D75 26 - server.c - IMPLEMENTED + * CsrSetBackgroundPriority 753E5E87 27 - process.c - IMPLEMENTED + * CsrSetCallingSpooler 753E6425 28 - server.c - IMPLEMENTED + * CsrSetForegroundPriority 753E5E67 29 - process.c - IMPLEMENTED + * CsrShutdownProcesses 753E7547 30 - process.c - IMPLEMENTED + * CsrUnhandledExceptionFilter 753E3FE3 31 - server.c - IMPLEMENTED + * CsrUnlockProcess 753E66FD 32 - process.c - IMPLEMENTED + * CsrUnlockThread 753E7503 33 - thread.c - IMPLEMENTED + * CsrValidateMessageBuffer 753E528D 34 - api.c - IMPLEMENTED + * CsrValidateMessageString 753E5323 35 - api.c - UNIMPLEMENTED + */ + +/* Public CSR API Interface Status (server.c) + * CsrSrvClientConnect - IMPLEMENTED + * CsrSrvUnusedFunction - IMPLEMENTED + * CsrSrvIdentifyAlertableThread - IMPLEMENTED + * CsrSrvSetPriorityClass - IMPLEMENTED + */ + +/* Public SB API Interface Status (session.c) + * CsrSbCreateSession - IMPLEMENTED + * CsrSbForeignSessionComplete - IMPLEMENTED + * CsrSbTerminateSession - UNIMPLEMENTED + * CsrSbCreateProcess - UNIMPLEMENTED + */ + +/* What's missing: + * + * - SMSS needs to be partly re-written to match some things done here. + * Among other things, SmConnectToSm, SmCompleteSession and the other + * Sm* Exported APIs have to be properly implemented, as well as the + * callback calling and SM LPC APIs. [DONE!] + * + * - NTDLL needs to get the Csr* routines properly implemented. [DONE!] + * + * - KERNEL32, USER32 need to register with their servers properly. + * Additionally, user32 needs to have ClientThreadStartup implemented + * properly and do the syscall NtUserInitialize (I think) which also + * needs to be implemented in win32k.sys. All this should be less then + * 100 lines of code. [KERNEL32 50% DONE, USER32 NOT DONE] + * + * - The skeleton code for winsrv and basesrv which connects with CSR/CSRSRV + * needs to be written. [NOT DONE] + * + * - The kernel's LPC implementation needs to be made compatible. [DONE!] + */ + diff --git a/reactos/deprecated/csr/csrsrv/thread.c b/reactos/deprecated/csr/csrsrv/thread.c new file mode 100644 index 00000000000..c03f61d82b0 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/thread.c @@ -0,0 +1,1056 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrsrv/thread.c + * PURPOSE: CSR Server DLL Thread Implementation + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +LIST_ENTRY CsrThreadHashTable[256]; +SECURITY_QUALITY_OF_SERVICE CsrSecurityQos = +{ + sizeof(SECURITY_QUALITY_OF_SERVICE), + SecurityImpersonation, + SECURITY_STATIC_TRACKING, + FALSE +}; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name CsrAllocateThread + * + * The CsrAllocateThread routine allocates a new CSR Thread object. + * + * @param CsrProcess + * Pointer to the CSR Process which will contain this CSR Thread. + * + * @return Pointer to the newly allocated CSR Thread. + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrAllocateThread(IN PCSR_PROCESS CsrProcess) +{ + PCSR_THREAD CsrThread; + + /* Allocate the structure */ + CsrThread = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, sizeof(CSR_THREAD)); + if (!CsrThread) return NULL; + + /* Reference the Thread and Process */ + CsrThread->ReferenceCount++; + CsrProcess->ReferenceCount++; + + /* Set the Parent Process */ + CsrThread->Process = CsrProcess; + + /* Return Thread */ + return CsrThread; +} + +/*++ + * @name CsrLocateThreadByClientId + * + * The CsrLocateThreadByClientId routine locates the CSR Thread and, + * optionally, its parent CSR Process, corresponding to a Client ID. + * + * @param Process + * Optional pointer to a CSR Process pointer which will contain + * the CSR Thread's parent. + * + * @param ClientId + * Pointer to a Client ID structure containing the Unique Thread ID + * to look up. + * + * @return Pointer to the CSR Thread corresponding to this CID, or NULL if + * none was found. + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrLocateThreadByClientId(OUT PCSR_PROCESS *Process OPTIONAL, + IN PCLIENT_ID ClientId) +{ + ULONG i; + PLIST_ENTRY NextEntry; + PCSR_THREAD FoundThread; + ASSERT(ProcessStructureListLocked()); + + /* Hash the Thread */ + i = CsrHashThread(ClientId->UniqueThread); + + /* Set the list pointers */ + NextEntry = CsrThreadHashTable[i].Flink; + + /* Star the loop */ + while (NextEntry != &CsrThreadHashTable[i]) + { + /* Get the thread */ + FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, HashLinks); + + /* Compare the CID */ + if (*(PULONGLONG)&FoundThread->ClientId == *(PULONGLONG)ClientId) + { + /* Match found, return the process */ + *Process = FoundThread->Process; + + /* Return thread too */ + return FoundThread; + } + + /* Next */ + NextEntry = NextEntry->Flink; + } + + /* Nothing found */ + return NULL; +} + +/*++ + * @name CsrLocateThreadInProcess + * + * The CsrLocateThreadInProcess routine locates the CSR Thread + * corresponding to a Client ID inside a specific CSR Process. + * + * @param Process + * Optional pointer to the CSR Process which contains the CSR Thread + * that will be looked up. + * + * @param ClientId + * Pointer to a Client ID structure containing the Unique Thread ID + * to look up. + * + * @return Pointer to the CSR Thread corresponding to this CID, or NULL if + * none was found. + * + * @remarks If the CsrProcess argument is NULL, the lookup will be done inside + * CsrRootProcess. + * + *--*/ +PCSR_THREAD +NTAPI +CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL, + IN PCLIENT_ID Cid) +{ + PLIST_ENTRY NextEntry; + PCSR_THREAD FoundThread = NULL; + + /* Use the Root Process if none was specified */ + if (!CsrProcess) CsrProcess = CsrRootProcess; + + /* Save the List pointers */ + NextEntry = CsrProcess->ThreadList.Flink; + + /* Start the Loop */ + while (NextEntry != &CsrProcess->ThreadList) + { + /* Get Thread Entry */ + FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link); + + /* Check for TID Match */ + if (FoundThread->ClientId.UniqueThread == Cid->UniqueThread) break; + + /* Next entry */ + NextEntry = NextEntry->Flink; + } + + /* Return what we found */ + return FoundThread; +} + +/*++ + * @name CsrInsertThread + * + * The CsrInsertThread routine inserts a CSR Thread into its parent's + * Thread List and into the Thread Hash Table. + * + * @param Process + * Pointer to the CSR Process containing this CSR Thread. + * + * @param Thread + * Pointer to the CSR Thread to be inserted. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrInsertThread(IN PCSR_PROCESS Process, + IN PCSR_THREAD Thread) +{ + ULONG i; + ASSERT(ProcessStructureListLocked()); + + /* Insert it into the Regular List */ + InsertTailList(&Process->ThreadList, &Thread->Link); + + /* Increase Thread Count */ + Process->ThreadCount++; + + /* Hash the Thread */ + i = CsrHashThread(Thread->ClientId.UniqueThread); + + /* Insert it there too */ + InsertHeadList(&CsrThreadHashTable[i], &Thread->HashLinks); +} + +/*++ + * @name CsrDeallocateThread + * + * The CsrDeallocateThread frees the memory associated with a CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread to be freed. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Thread has been cleanly + * dereferenced and killed. + * + *--*/ +VOID +NTAPI +CsrDeallocateThread(IN PCSR_THREAD CsrThread) +{ + /* Free the process object from the heap */ + ASSERT(CsrThread->WaitBlock == NULL); + RtlFreeHeap(CsrHeap, 0, CsrThread); +} + +/*++ + * @name CsrLockedReferenceThread + * + * The CsrLockedReferenceThread refences a CSR Thread while the + * Process Lock is already being held. + * + * @param CsrThread + * Pointer to the CSR Thread to be referenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedReferenceThread(IN PCSR_THREAD CsrThread) +{ + /* Increment the reference count */ + ++CsrThread->ReferenceCount; +} + +/*++ + * @name CsrLockedDereferenceThread + * + * The CsrLockedDereferenceThread derefences a CSR Thread while the + * Process Lock is already being held. + * + * @param CsrThread + * Pointer to the CSR Thread to be dereferenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedDereferenceThread(IN PCSR_THREAD CsrThread) +{ + LONG LockCount; + + /* Decrease reference count */ + LockCount = --CsrThread->ReferenceCount; + ASSERT(LockCount >= 0); + if (!LockCount) + { + /* Call the generic cleanup code */ + CsrThreadRefcountZero(CsrThread); + CsrAcquireProcessLock(); + } +} + +/*++ + * @name CsrRemoveThread + * + * The CsrRemoveThread function undoes a CsrInsertThread operation and + * removes the CSR Thread from the the Hash Table and Thread List. + * + * @param CsrThread + * Pointer to the CSR Thread to remove. + * + * @return None. + * + * @remarks If this CSR Thread is the last one inside a CSR Process, the + * parent will be dereferenced and the CsrProcessLastThreadTerminated + * flag will be set. + * + * After executing this routine, the CSR Thread will have the + * CsrThreadInTermination flag set. + * + *--*/ +VOID +NTAPI +CsrRemoveThread(IN PCSR_THREAD CsrThread) +{ + ASSERT(ProcessStructureListLocked()); + + /* Remove it from the List */ + RemoveEntryList(&CsrThread->Link); + + /* Decreate the thread count of the process */ + --CsrThread->Process->ThreadCount; + + /* Remove it from the Hash List as well */ + if (CsrThread->HashLinks.Flink) RemoveEntryList(&CsrThread->HashLinks); + + /* Check if this is the last Thread */ + if (!CsrThread->Process->ThreadCount) + { + /* Check if it's not already been marked for deletion */ + if (!(CsrThread->Process->Flags & CsrProcessLastThreadTerminated)) + { + /* Let everyone know this process is about to lose the thread */ + CsrThread->Process->Flags |= CsrProcessLastThreadTerminated; + + /* Reference the Process */ + CsrLockedDereferenceProcess(CsrThread->Process); + } + } + + /* Mark the thread for deletion */ + CsrThread->Flags |= CsrThreadInTermination; +} + +/*++ + * @name CsrThreadRefcountZero + * + * The CsrThreadRefcountZero routine is executed when a CSR Thread has lost + * all its active references. It removes and de-allocates the CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread that is to be deleted. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Thread has lost all + * its references. + * + * This routine is called with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrThreadRefcountZero(IN PCSR_THREAD CsrThread) +{ + PCSR_PROCESS CsrProcess = CsrThread->Process; + NTSTATUS Status; + + /* Remove this thread */ + CsrRemoveThread(CsrThread); + + /* Release the Process Lock */ + CsrReleaseProcessLock(); + + /* Close the NT Thread Handle */ + UnProtectHandle(CsrThread->ThreadHandle); + Status = NtClose(CsrThread->ThreadHandle); + ASSERT(NT_SUCCESS(Status)); + + /* De-allocate the CSR Thread Object */ + CsrDeallocateThread(CsrThread); + + /* Remove a reference from the process */ + CsrDereferenceProcess(CsrProcess); +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/*++ + * @name CsrAddStaticServerThread + * @implemented NT4 + * + * The CsrAddStaticServerThread routine adds a new CSR Thread to the + * CSR Server Process (CsrRootProcess). + * + * @param hThread + * Handle to an existing NT Thread to which to associate this + * CSR Thread. + * + * @param ClientId + * Pointer to the Client ID structure of the NT Thread to associate + * with this CSR Thread. + * + * @param ThreadFlags + * Initial CSR Thread Flags to associate to this CSR Thread. Usually + * CsrThreadIsServerThread. + * + * @return Pointer to the newly allocated CSR Thread. + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrAddStaticServerThread(IN HANDLE hThread, + IN PCLIENT_ID ClientId, + IN ULONG ThreadFlags) +{ + PCSR_THREAD CsrThread; + + /* Get the Lock */ + CsrAcquireProcessLock(); + + /* Allocate the Server Thread */ + CsrThread = CsrAllocateThread(CsrRootProcess); + if (CsrThread) + { + /* Setup the Object */ + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->ClientId = *ClientId; + CsrThread->Flags = ThreadFlags; + + /* Insert it into the Thread List */ + InsertTailList(&CsrRootProcess->ThreadList, &CsrThread->Link); + + /* Increment the thread count */ + CsrRootProcess->ThreadCount++; + } + else + { + DPRINT1("CsrAddStaticServerThread: alloc failed for thread 0x%x\n", hThread); + } + + /* Release the Process Lock and return */ + CsrReleaseProcessLock(); + return CsrThread; +} + +/*++ + * @name CsrCreateRemoteThread + * @implemented NT4 + * + * The CsrCreateRemoteThread routine creates a CSR Thread object for + * an NT Thread which is not part of the current NT Process. + * + * @param hThread + * Handle to an existing NT Thread to which to associate this + * CSR Thread. + * + * @param ClientId + * Pointer to the Client ID structure of the NT Thread to associate + * with this CSR Thread. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateRemoteThread(IN HANDLE hThread, + IN PCLIENT_ID ClientId) +{ + NTSTATUS Status; + HANDLE ThreadHandle; + PCSR_THREAD CsrThread; + PCSR_PROCESS CsrProcess; + KERNEL_USER_TIMES KernelTimes; + + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + + /* Get the Thread Create Time */ + Status = NtQueryInformationThread(hThread, + ThreadTimes, + &KernelTimes, + sizeof(KernelTimes), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + /* Lock the Owner Process */ + Status = CsrLockProcessByClientId(&ClientId->UniqueProcess, &CsrProcess); + + /* Make sure the thread didn't terminate */ + if (KernelTimes.ExitTime.QuadPart) + { + /* Unlock the process and return */ + CsrUnlockProcess(CsrProcess); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Allocate a CSR Thread Structure */ + CsrThread = CsrAllocateThread(CsrProcess); + if (!CsrThread) + { + DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__); + CsrUnlockProcess(CsrProcess); + return STATUS_NO_MEMORY; + } + + /* Duplicate the Thread Handle */ + Status = NtDuplicateObject(NtCurrentProcess(), + hThread, + NtCurrentProcess(), + &ThreadHandle, + 0, + 0, + DUPLICATE_SAME_ACCESS); + /* Allow failure */ + if (!NT_SUCCESS(Status)) ThreadHandle = hThread; + + /* Save the data we have */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = *ClientId; + CsrThread->ThreadHandle = ThreadHandle; + ProtectHandle(ThreadHandle); + CsrThread->Flags = 0; + + /* Insert the Thread into the Process */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Release the lock and return */ + CsrUnlockProcess(CsrProcess); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrCreateThread + * @implemented NT4 + * + * The CsrCreateThread routine creates a CSR Thread object for an NT Thread. + * + * @param CsrProcess + * Pointer to the CSR Process which will contain the CSR Thread. + * + * @param hThread + * Handle to an existing NT Thread to which to associate this + * CSR Thread. + * + * @param ClientId + * Pointer to the Client ID structure of the NT Thread to associate + * with this CSR Thread. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateThread(IN PCSR_PROCESS CsrProcess, + IN HANDLE hThread, + IN PCLIENT_ID ClientId, + IN BOOLEAN HaveClient) +{ + NTSTATUS Status; + PCSR_THREAD CsrThread, CurrentThread; + PCSR_PROCESS CurrentProcess; + CLIENT_ID CurrentCid; + KERNEL_USER_TIMES KernelTimes; + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + + if (HaveClient) + { + /* Get the current thread and CID */ + CurrentThread = NtCurrentTeb()->CsrClientThread; + CurrentCid = CurrentThread->ClientId; + + /* Acquire the Process Lock */ + CsrAcquireProcessLock(); + + /* Get the current Process and make sure the Thread is valid with this CID */ + CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, &CurrentCid); + if (!CurrentThread) + { + DPRINT1("CSRSRV:%s: invalid thread!\n", __FUNCTION__); + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + } + else + { + /* Acquire the Process Lock */ + CsrAcquireProcessLock(); + } + + /* Get the Thread Create Time */ + Status = NtQueryInformationThread(hThread, + ThreadTimes, + (PVOID)&KernelTimes, + sizeof(KernelTimes), + NULL); + if (!NT_SUCCESS(Status)) + { + CsrReleaseProcessLock(); + return Status; + } + + /* Allocate a CSR Thread Structure */ + CsrThread = CsrAllocateThread(CsrProcess); + if (!CsrThread) + { + DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Save the data we have */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = *ClientId; + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->Flags = 0; + + /* Insert the Thread into the Process */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Release the lock and return */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrDereferenceThread + * @implemented NT4 + * + * The CsrDereferenceThread routine removes a reference from a CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread to dereference. + * + * @return None. + * + * @remarks If the reference count has reached zero (ie: the CSR Thread has + * no more active references), it will be deleted. + * + *--*/ +VOID +NTAPI +CsrDereferenceThread(IN PCSR_THREAD CsrThread) +{ + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Decrease reference count */ + ASSERT(CsrThread->ReferenceCount > 0); + if (!(--CsrThread->ReferenceCount)) + { + /* Call the generic cleanup code */ + CsrThreadRefcountZero(CsrThread); + } + else + { + /* Just release the lock */ + CsrReleaseProcessLock(); + } +} + +/*++ + * @name CsrExecServerThread + * @implemented NT4 + * + * The CsrExecServerThread routine creates an NT Thread and then + * initializes a CSR Thread for it. + * + * @param ThreadHandler + * Pointer to the thread's startup routine. + * + * @param Flags + * Initial CSR Thread Flags to set to the CSR Thread. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks This routine is similar to CsrAddStaticServerThread, but it + * also creates an NT Thread instead of expecting one to already + * exist. + * + *--*/ +NTSTATUS +NTAPI +CsrExecServerThread(IN PVOID ThreadHandler, + IN ULONG Flags) +{ + PCSR_THREAD CsrThread; + HANDLE hThread; + CLIENT_ID ClientId; + NTSTATUS Status; + + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Allocate a CSR Thread in the Root Process */ + ASSERT(CsrRootProcess != NULL); + CsrThread = CsrAllocateThread(CsrRootProcess); + if (!CsrThread) + { + /* Fail */ + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Create the Thread */ + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + FALSE, + 0, + 0, + 0, + ThreadHandler, + NULL, + &hThread, + &ClientId); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + CsrDeallocateThread(CsrThread); + CsrReleaseProcessLock(); + return Status; + } + + /* Setup the Thread Object */ + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->ClientId = ClientId; + CsrThread->Flags = Flags; + + /* Insert it into the Thread List */ + InsertHeadList(&CsrRootProcess->ThreadList, &CsrThread->Link); + + /* Increase the thread count */ + CsrRootProcess->ThreadCount++; + + /* Return */ + CsrReleaseProcessLock(); + return Status; +} + +/*++ + * @name CsrDestroyThread + * @implemented NT4 + * + * The CsrDestroyThread routine destroys the CSR Thread corresponding to + * a given Thread ID. + * + * @param Cid + * Pointer to the Client ID Structure corresponding to the CSR + * Thread which is about to be destroyed. + * + * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING + * if the CSR Thread is already terminating. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrDestroyThread(IN PCLIENT_ID Cid) +{ + CLIENT_ID ClientId = *Cid; + PCSR_THREAD CsrThread; + PCSR_PROCESS CsrProcess; + + /* Acquire lock */ + CsrAcquireProcessLock(); + + /* Find the thread */ + CsrThread = CsrLocateThreadByClientId(&CsrProcess, + &ClientId); + + /* Make sure we got one back, and that it's not already gone */ + if (!CsrThread || CsrThread->Flags & CsrThreadTerminated) + { + /* Release the lock and return failure */ + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Set the terminated flag */ + CsrThread->Flags |= CsrThreadTerminated; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Do we have an active wait block? */ + if (CsrThread->WaitBlock) + { + /* Notify waiters of termination */ + CsrNotifyWaitBlock(CsrThread->WaitBlock, + NULL, + NULL, + NULL, + CsrProcessTerminating, + TRUE); + } + + /* Release the Wait Lock */ + CsrReleaseWaitLock(); + + /* Dereference the thread */ + CsrLockedDereferenceThread(CsrThread); + + /* Release the Process Lock and return success */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrImpersonateClient + * @implemented NT4 + * + * The CsrImpersonateClient will impersonate the given CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread to impersonate. + * + * @return TRUE if impersionation suceeded, false otherwise. + * + * @remarks Impersonation can be recursive. + * + *--*/ +BOOLEAN +NTAPI +CsrImpersonateClient(IN PCSR_THREAD CsrThread) +{ + NTSTATUS Status; + PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread; + + /* Use the current thread if none given */ + if (!CsrThread) CsrThread = CurrentThread; + + /* Still no thread, something is wrong */ + if (!CsrThread) return FALSE; + + /* Make the call */ + Status = NtImpersonateThread(NtCurrentThread(), + CsrThread->ThreadHandle, + &CsrSecurityQos); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: Can't impersonate client thread - Status = %lx\n", Status); + if (Status != STATUS_BAD_IMPERSONATION_LEVEL) DbgBreakPoint(); + return FALSE; + } + + /* Increase the impersonation count for the current thread and return */ + if (CurrentThread) ++CurrentThread->ImpersonationCount; + return TRUE; +} + +/*++ + * @name CsrRevertToSelf + * @implemented NT4 + * + * The CsrRevertToSelf routine will attempt to remove an active impersonation. + * + * @param None. + * + * @return TRUE if the reversion was succesful, false otherwise. + * + * @remarks Impersonation can be recursive; as such, the impersonation token + * will only be deleted once the CSR Thread's impersonaton count + * has reached zero. + * + *--*/ +BOOLEAN +NTAPI +CsrRevertToSelf(VOID) +{ + NTSTATUS Status; + PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread; + HANDLE ImpersonationToken = NULL; + + /* Check if we have a Current Thread */ + if (CurrentThread) + { + /* Make sure impersonation is on */ + if (!CurrentThread->ImpersonationCount) + { + DPRINT1("CSRSS: CsrRevertToSelf called while not impersonating\n"); + DbgBreakPoint(); + return FALSE; + } + else if (--CurrentThread->ImpersonationCount > 0) + { + /* Success; impersonation count decreased but still not zero */ + return TRUE; + } + } + + /* Impersonation has been totally removed, revert to ourselves */ + Status = NtSetInformationThread(NtCurrentThread(), + ThreadImpersonationToken, + &ImpersonationToken, + sizeof(HANDLE)); + + /* Return TRUE or FALSE */ + ASSERT(NT_SUCCESS(Status)); + return NT_SUCCESS(Status); +} + +/*++ + * @name CsrLockThreadByClientId + * @implemented NT4 + * + * The CsrLockThreadByClientId routine locks the CSR Thread corresponding + * to the given Thread ID and optionally returns it. + * + * @param Tid + * Thread ID corresponding to the CSR Thread which will be locked. + * + * @param CsrThread + * Optional pointer to a CSR Thread pointer which will hold the + * CSR Thread corresponding to the given Thread ID. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * othwerwise. + * + * @remarks Locking a CSR Thread is defined as acquiring an extra + * reference to it and returning with the Process Lock held. + * + *--*/ +NTSTATUS +NTAPI +CsrLockThreadByClientId(IN HANDLE Tid, + OUT PCSR_THREAD *CsrThread) +{ + PLIST_ENTRY NextEntry; + PCSR_THREAD CurrentThread = NULL; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + ULONG i; + + /* Acquire the lock */ + CsrAcquireProcessLock(); + + /* Assume failure */ + ASSERT(CsrThread != NULL); + *CsrThread = NULL; + + /* Convert to Hash */ + i = CsrHashThread(Tid); + + /* Setup the List Pointers */ + NextEntry = CsrThreadHashTable[i].Flink; + + /* Start Loop */ + while (NextEntry != &CsrThreadHashTable[i]) + { + /* Get the Process */ + CurrentThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, HashLinks); + + /* Check for PID Match */ + if ((CurrentThread->ClientId.UniqueThread == Tid) && + !(CurrentThread->Flags & CsrThreadTerminated)) + { + /* Get out of here */ + break; + } + + /* Next entry */ + NextEntry = NextEntry->Flink; + } + + /* Nothing found if we got back to the list */ + if (NextEntry == &CsrThreadHashTable[i]) CurrentThread = NULL; + + /* Did the loop find something? */ + if (CurrentThread) + { + /* Reference the found thread */ + Status = STATUS_SUCCESS; + CurrentThread->ReferenceCount++; + *CsrThread = CurrentThread; + } + else + { + /* Nothing found, release the lock */ + Status = STATUS_UNSUCCESSFUL; + CsrReleaseProcessLock(); + } + + /* Return the status */ + return Status; +} + +/*++ + * @name CsrReferenceThread + * @implemented NT4 + * + * The CsrReferenceThread routine increases the active reference count of + * a CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread whose reference count will be increased. + * + * @return None. + * + * @remarks Do not use this routine if the Process Lock is already held. + * + *--*/ +VOID +NTAPI +CsrReferenceThread(PCSR_THREAD CsrThread) +{ + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Sanity checks */ + ASSERT(CsrThread->Flags & CsrThreadTerminated); // CSR_THREAD_DESTROYED in ASSERT + ASSERT(CsrThread->ReferenceCount != 0); + + /* Increment reference count */ + CsrThread->ReferenceCount++; + + /* Release the lock */ + CsrReleaseProcessLock(); +} + +/*++ + * @name CsrUnlockThread + * @implemented NT4 + * + * The CsrUnlockThread undoes a previous CsrLockThreadByClientId operation. + * + * @param CsrThread + * Pointer to a previously locked CSR Thread. + * + * @return STATUS_SUCCESS. + * + * @remarks This routine must be called with the Process Lock held. + * + *--*/ +NTSTATUS +NTAPI +CsrUnlockThread(PCSR_THREAD CsrThread) +{ + /* Dereference the Thread */ + ASSERT(ProcessStructureListLocked()); + CsrLockedDereferenceThread(CsrThread); + + /* Release the lock and return */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/deprecated/csr/csrsrv/wait.c b/reactos/deprecated/csr/csrsrv/wait.c new file mode 100644 index 00000000000..00d0bdf3108 --- /dev/null +++ b/reactos/deprecated/csr/csrsrv/wait.c @@ -0,0 +1,449 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrsrv/wait.c + * PURPOSE: CSR Server DLL Wait Implementation + * PROGRAMMERS: Emanuele Aliberti + * Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +RTL_CRITICAL_SECTION CsrWaitListsLock; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name CsrInitializeWait + * + * The CsrInitializeWait routine initializes a CSR Wait Object. + * + * @param WaitFunction + * Pointer to the function that will handle this wait. + * + * @param CsrWaitThread + * Pointer to the CSR Thread that will perform the wait. + * + * @param WaitApiMessage + * Pointer to the CSR API Message associated to this wait. + * + * @param WaitContext + * Pointer to a user-defined parameter associated to this wait. + * + * @param NewWaitBlock + * Pointed to the initialized CSR Wait Block for this wait. + * + * @return TRUE in case of success, FALSE othwerwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrInitializeWait(IN CSR_WAIT_FUNCTION WaitFunction, + IN PCSR_THREAD CsrWaitThread, + IN OUT PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + OUT PCSR_WAIT_BLOCK *NewWaitBlock) +{ + ULONG Size; + PCSR_WAIT_BLOCK WaitBlock; + + /* Calculate the size of the wait block */ + Size = sizeof(CSR_WAIT_BLOCK) - + sizeof(WaitBlock->WaitApiMessage) + + WaitApiMessage->Header.u1.s1.TotalLength; + + /* Allocate the Wait Block */ + WaitBlock = RtlAllocateHeap(CsrHeap, 0, Size); + if (!WaitBlock) + { + /* Fail */ + WaitApiMessage->Status = STATUS_NO_MEMORY; + return FALSE; + } + + /* Initialize it */ + WaitBlock->Size = Size; + WaitBlock->WaitThread = CsrWaitThread; + WaitBlock->WaitContext = WaitContext; + WaitBlock->WaitFunction = WaitFunction; + WaitBlock->UserWaitList.Flink = NULL; + WaitBlock->UserWaitList.Blink = NULL; + WaitBlock->WaitList = WaitBlock->UserWaitList; + + /* Copy the message */ + RtlMoveMemory(&WaitBlock->WaitApiMessage, + WaitApiMessage, + WaitApiMessage->Header.u1.s1.TotalLength); + + /* Return the block */ + *NewWaitBlock = WaitBlock; + return TRUE; +} + +/*++ + * @name CsrNotifyWaitBlock + * + * The CsrNotifyWaitBlock routine calls the wait function for a registered + * CSR Wait Block, and replies to the attached CSR API Message, if any. + * + * @param WaitBlock + * Pointer to the CSR Wait Block + * + * @param WaitList + * Pointer to the wait list for this wait. + * + * @param WaitArgument[1-2] + * User-defined values to pass to the wait function. + * + * @param WaitFlags + * Wait flags for this wait. + * + * @param DereferenceThread + * Specifies whether the CSR Thread should be dereferenced at the + * end of this wait. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks After a wait block is notified, the wait function becomes invalid. + * + *--*/ +BOOLEAN +NTAPI +CsrNotifyWaitBlock(IN PCSR_WAIT_BLOCK WaitBlock, + IN PLIST_ENTRY WaitList, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags, + IN BOOLEAN DereferenceThread) +{ + /* Call the wait function */ + if ((WaitBlock->WaitFunction)(WaitList, + WaitBlock->WaitThread, + &WaitBlock->WaitApiMessage, + WaitBlock->WaitContext, + WaitArgument1, + WaitArgument2, + WaitFlags)) + { + /* The wait is done, clear the block */ + WaitBlock->WaitThread->WaitBlock = NULL; + + /* Check for captured arguments */ + if (WaitBlock->WaitApiMessage.CsrCaptureData) + { + /* Release them */ + CsrReleaseCapturedArguments(&WaitBlock->WaitApiMessage); + } + + /* Reply to the port */ + NtReplyPort(WaitBlock->WaitThread->Process->ClientPort, + (PPORT_MESSAGE)&WaitBlock->WaitApiMessage); + + /* Check if we should dereference the thread */ + if (DereferenceThread) + { + /* Remove it from the Wait List */ + if (WaitBlock->WaitList.Flink) + { + RemoveEntryList(&WaitBlock->WaitList); + } + + /* Remove it from the User Wait List */ + if (WaitBlock->UserWaitList.Flink) + { + RemoveEntryList(&WaitBlock->UserWaitList); + } + + /* Dereference teh thread */ + CsrDereferenceThread(WaitBlock->WaitThread); + + /* Free the wait block */ + RtlFreeHeap(CsrHeap, 0, WaitBlock); + } + else + { + /* The wait is complete, but the thread is being kept alive */ + WaitBlock->WaitFunction = NULL; + } + + /* The wait suceeded */ + return TRUE; + } + + /* The wait failed */ + return FALSE; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + +/*++ + * @name CsrCreateWait + * @implemented NT4 + * + * The CsrCreateWait routine creates a CSR Wait. + * + * @param WaitList + * Pointer to a list entry of the waits to associate. + * + * @param WaitFunction + * Pointer to the function that will handle this wait. + * + * @param CsrWaitThread + * Pointer to the CSR Thread that will perform the wait. + * + * @param WaitApiMessage + * Pointer to the CSR API Message associated to this wait. + * + * @param WaitContext + * Pointer to a user-defined parameter associated to this wait. + * + * @param UserWaitList + * Pointer to a list entry of the user-defined waits to associate. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrCreateWait(IN PLIST_ENTRY WaitList, + IN CSR_WAIT_FUNCTION WaitFunction, + IN PCSR_THREAD CsrWaitThread, + IN OUT PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + IN PLIST_ENTRY UserWaitList OPTIONAL) +{ + PCSR_WAIT_BLOCK WaitBlock; + + /* Initialize the wait */ + if (!CsrInitializeWait(WaitFunction, + CsrWaitThread, + WaitApiMessage, + WaitContext, + &WaitBlock)) + { + return FALSE; + } + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Make sure the thread wasn't destroyed */ + if (CsrWaitThread->Flags & CsrThreadTerminated) + { + /* Fail the wait */ + RtlFreeHeap(CsrHeap, 0, WaitBlock); + CsrReleaseWaitLock(); + return FALSE; + } + + /* Insert the wait in the queue */ + InsertTailList(WaitList, &WaitBlock->WaitList); + + /* Insert the User Wait too, if one was given */ + if (UserWaitList) InsertTailList(UserWaitList, &WaitBlock->UserWaitList); + + /* Return */ + CsrReleaseWaitLock(); + return TRUE; +} + +/*++ + * @name CsrDereferenceWait + * @implemented NT4 + * + * The CsrDereferenceWait routine derefences a CSR Wait Block. + * + * @param WaitList + * Pointer to the Wait List associated to the wait. + + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrDereferenceWait(IN PLIST_ENTRY WaitList) +{ + PLIST_ENTRY NextEntry; + PCSR_WAIT_BLOCK WaitBlock; + + /* Acquire the Process and Wait Locks */ + CsrAcquireProcessLock(); + CsrAcquireWaitLock(); + + /* Set the list pointers */ + NextEntry = WaitList->Flink; + + /* Start the loop */ + while (NextEntry != WaitList) + { + /* Get the wait block */ + WaitBlock = CONTAINING_RECORD(NextEntry, CSR_WAIT_BLOCK, WaitList); + + /* Move to the next entry */ + NextEntry = NextEntry->Flink; + + /* Check if there's no Wait Routine */ + if (!WaitBlock->WaitFunction) + { + /* Remove it from the Wait List */ + if (WaitBlock->WaitList.Flink) + { + RemoveEntryList(&WaitBlock->WaitList); + } + + /* Remove it from the User Wait List */ + if (WaitBlock->UserWaitList.Flink) + { + RemoveEntryList(&WaitBlock->UserWaitList); + } + + /* Dereference the thread waiting on it */ + CsrDereferenceThread(WaitBlock->WaitThread); + + /* Free the block */ + RtlFreeHeap(CsrHeap, 0, WaitBlock); + } + } + + /* Release the locks */ + CsrReleaseWaitLock(); + CsrReleaseProcessLock(); +} + +/*++ + * @name CsrMoveSatisfiedWait + * @implemented NT5 + * + * The CsrMoveSatisfiedWait routine moves satisfied waits from a wait list + * to another list entry. + * + * @param NewEntry + * Pointer to a list entry where the satisfied waits will be added. + * + * @param WaitList + * Pointer to a list entry to analyze for satisfied waits. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrMoveSatisfiedWait(IN PLIST_ENTRY NewEntry, + IN PLIST_ENTRY WaitList) +{ + PLIST_ENTRY NextEntry; + PCSR_WAIT_BLOCK WaitBlock; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Set the List pointers */ + NextEntry = WaitList->Flink; + + /* Start looping */ + while (NextEntry != WaitList) + { + /* Get the Wait block */ + WaitBlock = CONTAINING_RECORD(NextEntry, CSR_WAIT_BLOCK, WaitList); + + /* Go to the next entry */ + NextEntry = NextEntry->Flink; + + /* Check if there is a Wait Callback */ + if (WaitBlock->WaitFunction) + { + /* Remove it from the Wait Block Queue */ + RemoveEntryList(&WaitBlock->WaitList); + + /* Insert the new entry */ + InsertTailList(&WaitBlock->WaitList, NewEntry); + } + } + + /* Release the wait lock */ + CsrReleaseWaitLock(); +} + +/*++ + * @name CsrNotifyWait + * @implemented NT4 + * + * The CsrNotifyWait notifies a CSR Wait Block. + * + * @param WaitList + * Pointer to the list entry for this wait. + * + * @param WaitType + * Type of the wait to perform, either WaitAny or WaitAll. + * + * @param WaitArgument[1-2] + * User-defined argument to pass on to the wait function. + * + * @return TRUE in case of success, FALSE othwerwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrNotifyWait(IN PLIST_ENTRY WaitList, + IN ULONG WaitType, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2) +{ + PLIST_ENTRY NextEntry; + PCSR_WAIT_BLOCK WaitBlock; + BOOLEAN NotifySuccess = FALSE; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Set the List pointers */ + NextEntry = WaitList->Flink; + + /* Start looping */ + while (NextEntry != WaitList) + { + /* Get the Wait block */ + WaitBlock = CONTAINING_RECORD(NextEntry, CSR_WAIT_BLOCK, WaitList); + + /* Go to the next entry */ + NextEntry = NextEntry->Flink; + + /* Check if there is a Wait Callback */ + if (WaitBlock->WaitFunction) + { + /* Notify the Waiter */ + NotifySuccess |= CsrNotifyWaitBlock(WaitBlock, + WaitList, + WaitArgument1, + WaitArgument2, + 0, + FALSE); + + /* We've already done a wait, so leave unless this is a Wait All */ + if (WaitType != WaitAll) break; + } + } + + /* Release the wait lock and return */ + CsrReleaseWaitLock(); + return NotifySuccess; +} + +/* EOF */ diff --git a/reactos/deprecated/csr/main.c b/reactos/deprecated/csr/main.c new file mode 100644 index 00000000000..301ff50084e --- /dev/null +++ b/reactos/deprecated/csr/main.c @@ -0,0 +1,97 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsys/csr/csrss.c + * PURPOSE: CSR Executable + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#define WIN32_NO_STATUS +#include +#define NTOS_MODE_USER +#include +#include + +#define NDEBUG +#include + +/* PRIVATE FUNCTIONS *********************************************************/ + +VOID +NTAPI +CsrpSetDefaultProcessHardErrorMode (VOID) +{ + ULONG DefaultHardErrorMode = 0; + + /* Disable hard errors */ + NtSetInformationProcess(NtCurrentProcess(), + ProcessDefaultHardErrorMode, + &DefaultHardErrorMode, + sizeof(DefaultHardErrorMode)); +} + +/* + * Note: Standard entrypoint for Native C Programs. + * The OS backend (NtProcessStartup) which calls this routine is + * implemented in a CRT-like static library (much like mainCRTStartup). + * Do NOT manually add the NtProcessStartup entrypoint or anything else. + */ +int +_cdecl +_main(int argc, + char *argv[], + char *envp[], + int DebugFlag) +{ + KPRIORITY BasePriority = (8 + 1) + 4; + NTSTATUS Status; + ULONG Response; + UNREFERENCED_PARAMETER(envp); + UNREFERENCED_PARAMETER(DebugFlag); + + /* Set the Priority */ + NtSetInformationProcess(NtCurrentProcess(), + ProcessBasePriority, + &BasePriority, + sizeof(KPRIORITY)); + + /* Give us IOPL so that we can access the VGA registers */ + Status = NtSetInformationProcess(NtCurrentProcess(), + ProcessUserModeIOPL, + NULL, + 0); + if (!NT_SUCCESS(Status)) + { + /* Raise a hard error */ + DPRINT1("CSRSS: Could not raise IOPL: %x\n", Status); + Status = NtRaiseHardError(STATUS_IO_PRIVILEGE_FAILED, + 0, + 0, + NULL, + OptionOk, + &Response); + } + + /* Initialize CSR through CSRSRV */ + Status = CsrServerInitialization(argc, argv); + if (!NT_SUCCESS(Status)) + { + /* Kill us */ + DPRINT1("CSRSS: CsrServerInitialization failed:% lx\n", Status); + NtTerminateProcess(NtCurrentProcess(), Status); + } + + /* Disable errors */ + CsrpSetDefaultProcessHardErrorMode(); + + /* If this is Session 0, make sure killing us bugchecks the system */ + if (!NtCurrentPeb()->SessionId) RtlSetProcessIsCritical(TRUE, NULL, FALSE); + + /* Kill this thread. CSRSRV keeps us going */ + NtTerminateThread(NtCurrentThread(), Status); + return 0; +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/CMakeLists.txt b/reactos/deprecated/csrsrv/CMakeLists.txt new file mode 100644 index 00000000000..0fabebe4bc0 --- /dev/null +++ b/reactos/deprecated/csrsrv/CMakeLists.txt @@ -0,0 +1,31 @@ + +include_directories(${REACTOS_SOURCE_DIR}/subsystems/win32/csrss/include) +include_directories(${REACTOS_SOURCE_DIR}/include/reactos/subsys) + +spec2def(csrsrv.dll csrsrv.spec ADD_IMPORTLIB) + +list(APPEND SOURCE + api/process.c + api/user.c + api/wapi.c + procsup.c + thredsup.c + init.c + wait.c + session.c + server.c + ${CMAKE_CURRENT_BINARY_DIR}/csrsrv.def) + +add_library(csrsrv SHARED ${SOURCE}) + +target_link_libraries(csrsrv ${PSEH_LIB} smlib) + +set_module_type(csrsrv nativedll) + +add_importlibs(csrsrv ntdll smdll) + +add_pch(csrsrv srv.h) + +add_dependencies(csrsrv psdk bugcodes) +add_cd_file(TARGET csrsrv DESTINATION reactos/system32 FOR all) + diff --git a/reactos/deprecated/csrsrv/api/process.c b/reactos/deprecated/csrsrv/api/process.c new file mode 100644 index 00000000000..2493481ce99 --- /dev/null +++ b/reactos/deprecated/csrsrv/api/process.c @@ -0,0 +1,229 @@ +/* + * subsystems/win32/csrss/csrsrv/api/process.c + * + * "\windows\ApiPort" port process management functions + * + * ReactOS Operating System + */ + +/* INCLUDES ******************************************************************/ + +#include + +#define NDEBUG +#include + +extern NTSTATUS CallProcessCreated(PCSR_PROCESS, PCSR_PROCESS); + +/* GLOBALS *******************************************************************/ + +/* FUNCTIONS *****************************************************************/ + +/********************************************************************** + * CSRSS API + *********************************************************************/ + +CSR_API(CsrSrvCreateProcess) +{ + NTSTATUS Status; + HANDLE ProcessHandle, ThreadHandle; + PCSR_THREAD CsrThread; + PCSR_PROCESS NewProcessData; + ULONG Flags, VdmPower = 0, DebugFlags = 0; + + /* Get the current client thread */ + CsrThread = NtCurrentTeb()->CsrClientThread; + ASSERT(CsrThread != NULL); + + /* Extract the flags out of the process handle */ + Flags = (ULONG_PTR)Request->Data.CreateProcessRequest.ProcessHandle & 3; + Request->Data.CreateProcessRequest.ProcessHandle = (HANDLE)((ULONG_PTR)Request->Data.CreateProcessRequest.ProcessHandle & ~3); + + /* Duplicate the process handle */ + Status = NtDuplicateObject(CsrThread->Process->ProcessHandle, + Request->Data.CreateProcessRequest.ProcessHandle, + NtCurrentProcess(), + &ProcessHandle, + 0, + 0, + DUPLICATE_SAME_ACCESS); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to duplicate process handle\n"); + return Status; + } + + /* Duplicate the thread handle */ + Status = NtDuplicateObject(CsrThread->Process->ProcessHandle, + Request->Data.CreateProcessRequest.ThreadHandle, + NtCurrentProcess(), + &ThreadHandle, + 0, + 0, + DUPLICATE_SAME_ACCESS); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to duplicate process handle\n"); + NtClose(ProcessHandle); + return Status; + } + + /* See if this is a VDM process */ + if (VdmPower) + { + /* Request VDM powers */ + Status = NtSetInformationProcess(ProcessHandle, + ProcessWx86Information, + &VdmPower, + sizeof(VdmPower)); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to get VDM powers\n"); + NtClose(ProcessHandle); + NtClose(ThreadHandle); + return Status; + } + } + + /* Convert some flags. FIXME: More need conversion */ + if (Request->Data.CreateProcessRequest.CreationFlags & CREATE_NEW_PROCESS_GROUP) + { + DebugFlags |= CsrProcessCreateNewGroup; + } + + /* FIXME: SxS Stuff */ + + /* Call CSRSRV to create the CSR_PROCESS structure and the first CSR_THREAD */ + Status = CsrCreateProcess(ProcessHandle, + ThreadHandle, + &Request->Data.CreateProcessRequest.ClientId, + CsrThread->Process->NtSession, + DebugFlags, + NULL); + if (Status == STATUS_THREAD_IS_TERMINATING) + { + DPRINT1("Thread already dead\n"); + return Status; + } + + /* Check for other failures */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create process/thread structures: %lx\n", Status); + return Status; + } + + /* FIXME: Should notify user32 */ + + /* FIXME: VDM vodoo */ + + /* ReactOS Compatibility */ + Status = CsrLockProcessByClientId(Request->Data.CreateProcessRequest.ClientId.UniqueProcess, &NewProcessData); + ASSERT(Status == STATUS_SUCCESS); + if (!(Request->Data.CreateProcessRequest.CreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS))) + { + NewProcessData->ParentConsole = ProcessData->Console; + NewProcessData->bInheritHandles = Request->Data.CreateProcessRequest.bInheritHandles; + } + RtlInitializeCriticalSection(&NewProcessData->HandleTableLock); + CallProcessCreated(ProcessData, NewProcessData); + CsrUnlockProcess(NewProcessData); + + /* Return the result of this operation */ + return Status; +} + +CSR_API(CsrSrvCreateThread) +{ + PCSR_THREAD CurrentThread; + HANDLE ThreadHandle; + NTSTATUS Status; + PCSR_PROCESS CsrProcess; + + /* Get the current CSR thread */ + CurrentThread = NtCurrentTeb()->CsrClientThread; + if (!CurrentThread) + { + DPRINT1("Server Thread TID: [%lx.%lx]\n", + Request->Data.CreateThreadRequest.ClientId.UniqueProcess, + Request->Data.CreateThreadRequest.ClientId.UniqueThread); + return STATUS_SUCCESS; // server-to-server + } + + /* Get the CSR Process for this request */ + CsrProcess = CurrentThread->Process; + if (CsrProcess->ClientId.UniqueProcess != + Request->Data.CreateThreadRequest.ClientId.UniqueProcess) + { + /* This is a remote thread request -- is it within the server itself? */ + if (Request->Data.CreateThreadRequest.ClientId.UniqueProcess == NtCurrentTeb()->ClientId.UniqueProcess) + { + /* Accept this without any further work */ + return STATUS_SUCCESS; + } + + /* Get the real CSR Process for the remote thread's process */ + Status = CsrLockProcessByClientId(Request->Data.CreateThreadRequest.ClientId.UniqueProcess, + &CsrProcess); + if (!NT_SUCCESS(Status)) return Status; + } + + /* Duplicate the thread handle so we can own it */ + Status = NtDuplicateObject(CurrentThread->Process->ProcessHandle, + Request->Data.CreateThreadRequest.ThreadHandle, + NtCurrentProcess(), + &ThreadHandle, + 0, + 0, + DUPLICATE_SAME_ACCESS); + if (NT_SUCCESS(Status)) + { + /* Call CSRSRV to tell it about the new thread */ + Status = CsrCreateThread(CsrProcess, + ThreadHandle, + &Request->Data.CreateThreadRequest.ClientId); + } + + /* Unlock the process and return */ + if (CsrProcess != CurrentThread->Process) CsrUnlockProcess(CsrProcess); + return Status; +} + +CSR_API(CsrTerminateProcess) +{ + PCSR_THREAD CsrThread = NtCurrentTeb()->CsrClientThread; + ASSERT(CsrThread != NULL); + + /* Set magic flag so we don't reply this message back */ + Request->Type = 0xBABE; + + /* Remove the CSR_THREADs and CSR_PROCESS */ + return CsrDestroyProcess(&CsrThread->ClientId, + (NTSTATUS)Request->Data.TerminateProcessRequest.uExitCode); +} + +CSR_API(CsrConnectProcess) +{ + + return(STATUS_SUCCESS); +} + +CSR_API(CsrGetShutdownParameters) +{ + + Request->Data.GetShutdownParametersRequest.Level = ProcessData->ShutdownLevel; + Request->Data.GetShutdownParametersRequest.Flags = ProcessData->ShutdownFlags; + + return(STATUS_SUCCESS); +} + +CSR_API(CsrSetShutdownParameters) +{ + + ProcessData->ShutdownLevel = Request->Data.SetShutdownParametersRequest.Level; + ProcessData->ShutdownFlags = Request->Data.SetShutdownParametersRequest.Flags; + + return(STATUS_SUCCESS); +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/api/user.c b/reactos/deprecated/csrsrv/api/user.c new file mode 100644 index 00000000000..9ce9da0174d --- /dev/null +++ b/reactos/deprecated/csrsrv/api/user.c @@ -0,0 +1,41 @@ +/* + * subsystems/win32/csrss/csrsrv/api/user.c + * + * User functions + * + * ReactOS Operating System + * + * PROGRAMMER: Eric Kohl + */ + +/* INCLUDES ******************************************************************/ + +#include + +#define NDEBUG +#include + +/* GLOBALS *******************************************************************/ + +static BOOLEAN ServicesProcessIdValid = FALSE; +static ULONG_PTR ServicesProcessId; + + +/* FUNCTIONS *****************************************************************/ + +CSR_API(CsrRegisterServicesProcess) +{ + if (ServicesProcessIdValid == TRUE) + { + /* Only accept a single call */ + return STATUS_INVALID_PARAMETER; + } + else + { + ServicesProcessId = (ULONG_PTR)Request->Data.RegisterServicesProcessRequest.ProcessId; + ServicesProcessIdValid = TRUE; + return STATUS_SUCCESS; + } +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/api/wapi.c b/reactos/deprecated/csrsrv/api/wapi.c new file mode 100644 index 00000000000..af4f764a9e6 --- /dev/null +++ b/reactos/deprecated/csrsrv/api/wapi.c @@ -0,0 +1,1400 @@ +/* + * subsystems/win32/csrss/csrsrv/api/wapi.c + * + * CSRSS port message processing + * + * ReactOS Operating System + * + */ + +/* INCLUDES ******************************************************************/ + +#include + +#define NDEBUG +#include + +/* GLOBALS *******************************************************************/ + +static unsigned ApiDefinitionsCount = 0; +static PCSRSS_API_DEFINITION ApiDefinitions = NULL; +UNICODE_STRING CsrApiPortName; +volatile LONG CsrpStaticThreadCount; +volatile LONG CsrpDynamicThreadTotal; +extern ULONG CsrMaxApiRequestThreads; + +/* FUNCTIONS *****************************************************************/ + +NTSTATUS FASTCALL +CsrApiRegisterDefinitions(PCSRSS_API_DEFINITION NewDefinitions) +{ + unsigned NewCount; + PCSRSS_API_DEFINITION Scan; + PCSRSS_API_DEFINITION New; + + DPRINT("CSR: %s called\n", __FUNCTION__); + + NewCount = 0; + for (Scan = NewDefinitions; 0 != Scan->Handler; Scan++) + { + NewCount++; + } + + New = RtlAllocateHeap(CsrHeap, 0, + (ApiDefinitionsCount + NewCount) + * sizeof(CSRSS_API_DEFINITION)); + if (NULL == New) + { + DPRINT1("Unable to allocate memory\n"); + return STATUS_NO_MEMORY; + } + if (0 != ApiDefinitionsCount) + { + RtlCopyMemory(New, ApiDefinitions, + ApiDefinitionsCount * sizeof(CSRSS_API_DEFINITION)); + RtlFreeHeap(CsrHeap, 0, ApiDefinitions); + } + RtlCopyMemory(New + ApiDefinitionsCount, NewDefinitions, + NewCount * sizeof(CSRSS_API_DEFINITION)); + ApiDefinitions = New; + ApiDefinitionsCount += NewCount; + + return STATUS_SUCCESS; +} + +VOID +FASTCALL +CsrApiCallHandler(PCSR_PROCESS ProcessData, + PCSR_API_MESSAGE Request) +{ + unsigned DefIndex; + ULONG Type; + + DPRINT("CSR: Calling handler for type: %x.\n", Request->Type); + Type = Request->Type & 0xFFFF; /* FIXME: USE MACRO */ + DPRINT("CSR: API Number: %x ServerID: %x\n",Type, Request->Type >> 16); + + /* FIXME: Extract DefIndex instead of looping */ + for (DefIndex = 0; DefIndex < ApiDefinitionsCount; DefIndex++) + { + if (ApiDefinitions[DefIndex].Type == Type) + { + if (Request->Header.u1.s1.DataLength < ApiDefinitions[DefIndex].MinRequestSize) + { + DPRINT1("Request type %d min request size %d actual %d\n", + Type, ApiDefinitions[DefIndex].MinRequestSize, + Request->Header.u1.s1.DataLength); + Request->Status = STATUS_INVALID_PARAMETER; + } + else + { + Request->Status = (ApiDefinitions[DefIndex].Handler)(ProcessData, Request); + } + return; + } + } + DPRINT1("CSR: Unknown request type 0x%x\n", Request->Type); + Request->Status = STATUS_INVALID_SYSTEM_SERVICE; +} + +VOID +CallHardError(IN PCSR_THREAD ThreadData, + IN PHARDERROR_MSG HardErrorMessage); + +static +VOID +NTAPI +CsrHandleHardError(IN PCSR_THREAD ThreadData, + IN OUT PHARDERROR_MSG Message) +{ + DPRINT1("CSR: received hard error %lx\n", Message->Status); + + /* Call the hard error handler in win32csr */ + CallHardError(ThreadData, Message); +} + +/*++ + * @name CsrCallServerFromServer + * @implemented NT4 + * + * The CsrCallServerFromServer routine calls a CSR API from within a server. + * It avoids using LPC messages since the request isn't coming from a client. + * + * @param ReceiveMsg + * Pointer to the CSR API Message to send to the server. + * + * @param ReplyMsg + * Pointer to the CSR API Message to receive from the server. + * + * @return STATUS_SUCCESS in case of success, STATUS_ILLEGAL_FUNCTION + * if the opcode is invalid, or STATUS_ACCESS_VIOLATION if there + * was a problem executing the API. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCallServerFromServer(PCSR_API_MESSAGE ReceiveMsg, + PCSR_API_MESSAGE ReplyMsg) +{ +#if 0 // real code + ULONG ServerId; + PCSR_SERVER_DLL ServerDll; + ULONG ApiId; + ULONG Reply; + NTSTATUS Status; + + /* Get the Server ID */ + ServerId = CSR_SERVER_ID_FROM_OPCODE(ReceiveMsg->Opcode); + + /* Make sure that the ID is within limits, and the Server DLL loaded */ + if ((ServerId >= CSR_SERVER_DLL_MAX) || + (!(ServerDll = CsrLoadedServerDll[ServerId]))) + { + /* We are beyond the Maximum Server ID */ + DPRINT1("CSRSS: %lx is invalid ServerDllIndex (%08x)\n", ServerId, ServerDll); + ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION; + return STATUS_ILLEGAL_FUNCTION; + } + else + { + /* Get the API ID */ + ApiId = CSR_API_ID_FROM_OPCODE(ReceiveMsg->Opcode); + + /* Normalize it with our Base ID */ + ApiId -= ServerDll->ApiBase; + + /* Make sure that the ID is within limits, and the entry exists */ + if ((ApiId >= ServerDll->HighestApiSupported) || + ((ServerDll->ValidTable) && !(ServerDll->ValidTable[ApiId]))) + { + /* We are beyond the Maximum API ID, or it doesn't exist */ + DPRINT1("CSRSS: %lx (%s) is invalid ApiTableIndex for %Z or is an " + "invalid API to call from the server.\n", + ServerDll->ValidTable[ApiId], + ((ServerDll->NameTable) && (ServerDll->NameTable[ApiId])) ? + ServerDll->NameTable[ApiId] : "*** UNKNOWN ***", &ServerDll->Name); + DbgBreakPoint(); + ReplyMsg->Status = (ULONG)STATUS_ILLEGAL_FUNCTION; + return STATUS_ILLEGAL_FUNCTION; + } + } + + if (CsrDebug & 2) + { + DPRINT1("CSRSS: %s Api Request received from server process\n", + ServerDll->NameTable[ApiId]); + } + + /* Validation complete, start SEH */ + _SEH2_TRY + { + /* Call the API and get the result */ + Status = (ServerDll->DispatchTable[ApiId])(ReceiveMsg, &Reply); + + /* Return the result, no matter what it is */ + ReplyMsg->Status = Status; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* If we got an exception, return access violation */ + ReplyMsg->Status = STATUS_ACCESS_VIOLATION; + } + _SEH2_END; + + /* Return success */ + return STATUS_SUCCESS; + +#else // Hacky reactos code + + PCSR_PROCESS ProcessData; + + /* Get the Process Data */ + CsrLockProcessByClientId(&ReceiveMsg->Header.ClientId.UniqueProcess, &ProcessData); + if (!ProcessData) + { + DPRINT1("Message: Unable to find data for process 0x%x\n", + ReceiveMsg->Header.ClientId.UniqueProcess); + return STATUS_NOT_SUPPORTED; + } + + /* Validation complete, start SEH */ + _SEH2_TRY + { + /* Call the API and get the result */ + CsrApiCallHandler(ProcessData, ReplyMsg); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* If we got an exception, return access violation */ + ReplyMsg->Status = STATUS_ACCESS_VIOLATION; + } + _SEH2_END; + + /* Release the process reference */ + CsrUnlockProcess(ProcessData); + + /* Return success */ + return STATUS_SUCCESS; +#endif +} + +/*++ + * @name CsrApiPortInitialize + * + * The CsrApiPortInitialize routine initializes the LPC Port used for + * communications with the Client/Server Runtime (CSR) and initializes the + * static thread that will handle connection requests and APIs. + * + * @param None + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrApiPortInitialize(VOID) +{ + ULONG Size; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE hRequestEvent, hThread; + CLIENT_ID ClientId; + PLIST_ENTRY ListHead, NextEntry; + PCSR_THREAD ServerThread; + + /* Calculate how much space we'll need for the Port Name */ + Size = CsrDirectoryName.Length + sizeof(CSR_PORT_NAME) + sizeof(WCHAR); + + /* Create the buffer for it */ + CsrApiPortName.Buffer = RtlAllocateHeap(CsrHeap, 0, Size); + if (!CsrApiPortName.Buffer) return STATUS_NO_MEMORY; + + /* Setup the rest of the empty string */ + CsrApiPortName.Length = 0; + CsrApiPortName.MaximumLength = (USHORT)Size; + RtlAppendUnicodeStringToString(&CsrApiPortName, &CsrDirectoryName); + RtlAppendUnicodeToString(&CsrApiPortName, UNICODE_PATH_SEP); + RtlAppendUnicodeToString(&CsrApiPortName, CSR_PORT_NAME); + if (CsrDebug & 1) + { + DPRINT1("CSRSS: Creating %wZ port and associated threads\n", &CsrApiPortName); + DPRINT1("CSRSS: sizeof( CONNECTINFO ) == %ld sizeof( API_MSG ) == %ld\n", + sizeof(CSR_CONNECTION_INFO), sizeof(CSR_API_MESSAGE)); + } + + /* FIXME: Create a Security Descriptor */ + + /* Initialize the Attributes */ + InitializeObjectAttributes(&ObjectAttributes, + &CsrApiPortName, + 0, + NULL, + NULL /* FIXME*/); + + /* Create the Port Object */ + Status = NtCreatePort(&CsrApiPort, + &ObjectAttributes, + LPC_MAX_DATA_LENGTH, // hack ; sizeof(CSR_CONNECTION_INFO), + LPC_MAX_MESSAGE_LENGTH, // hack ; sizeof(CSR_API_MESSAGE), + 16 * PAGE_SIZE); + if (NT_SUCCESS(Status)) + { + /* Create the event the Port Thread will use */ + Status = NtCreateEvent(&hRequestEvent, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE); + if (NT_SUCCESS(Status)) + { + /* Create the Request Thread */ + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + TRUE, + 0, + 0, + 0, + (PVOID)ClientConnectionThread,//CsrApiRequestThread, + (PVOID)hRequestEvent, + &hThread, + &ClientId); + if (NT_SUCCESS(Status)) + { + /* Add this as a static thread to CSRSRV */ + CsrAddStaticServerThread(hThread, &ClientId, CsrThreadIsServerThread); + + /* Get the Thread List Pointers */ + ListHead = &CsrRootProcess->ThreadList; + NextEntry = ListHead->Flink; + + /* Start looping the list */ + while (NextEntry != ListHead) + { + /* Get the Thread */ + ServerThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link); + + /* Start it up */ + Status = NtResumeThread(ServerThread->ThreadHandle, NULL); + + /* Is this a Server Thread? */ + if (ServerThread->Flags & CsrThreadIsServerThread) + { + /* If so, then wait for it to initialize */ + Status = NtWaitForSingleObject(hRequestEvent, FALSE, NULL); + ASSERT(NT_SUCCESS(Status)); + } + + /* Next thread */ + NextEntry = NextEntry->Flink; + } + + /* We don't need this anymore */ + NtClose(hRequestEvent); + } + } + } + + /* Return */ + return Status; +} + +PBASE_STATIC_SERVER_DATA BaseStaticServerData; + +NTSTATUS +NTAPI +CreateBaseAcls(OUT PACL* Dacl, + OUT PACL* RestrictedDacl) +{ + PSID SystemSid, WorldSid, RestrictedSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY}; + SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; + NTSTATUS Status; + UCHAR KeyValueBuffer[0x40]; + PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo; + UNICODE_STRING KeyName; + ULONG ProtectionMode = 0; + ULONG AclLength, ResultLength; + HANDLE hKey; + OBJECT_ATTRIBUTES ObjectAttributes; + + /* Open the Session Manager Key */ + RtlInitUnicodeString(&KeyName, SM_REG_KEY); + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); + if (NT_SUCCESS(Status)) + { + /* Read the key value */ + RtlInitUnicodeString(&KeyName, L"ProtectionMode"); + Status = NtQueryValueKey(hKey, + &KeyName, + KeyValuePartialInformation, + KeyValueBuffer, + sizeof(KeyValueBuffer), + &ResultLength); + + /* Make sure it's what we expect it to be */ + KeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer; + if ((NT_SUCCESS(Status)) && (KeyValuePartialInfo->Type == REG_DWORD) && + (*(PULONG)KeyValuePartialInfo->Data)) + { + /* Save the Protection Mode */ + ProtectionMode = *(PULONG)KeyValuePartialInfo->Data; + } + + /* Close the handle */ + NtClose(hKey); + } + + /* Allocate the System SID */ + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 1, SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &SystemSid); + ASSERT(NT_SUCCESS(Status)); + + /* Allocate the World SID */ + Status = RtlAllocateAndInitializeSid(&WorldAuthority, + 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &WorldSid); + ASSERT(NT_SUCCESS(Status)); + + /* Allocate the restricted SID */ + Status = RtlAllocateAndInitializeSid(&NtAuthority, + 1, SECURITY_RESTRICTED_CODE_RID, + 0, 0, 0, 0, 0, 0, 0, + &RestrictedSid); + ASSERT(NT_SUCCESS(Status)); + + /* Allocate one ACL with 3 ACEs each for one SID */ + AclLength = sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(SystemSid) + + RtlLengthSid(RestrictedSid) + + RtlLengthSid(WorldSid); + *Dacl = RtlAllocateHeap(CsrHeap, 0, AclLength); + ASSERT(*Dacl != NULL); + + /* Set the correct header fields */ + Status = RtlCreateAcl(*Dacl, AclLength, ACL_REVISION2); + ASSERT(NT_SUCCESS(Status)); + + /* Give the appropriate rights to each SID */ + /* FIXME: Should check SessionId/ProtectionMode */ + Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_QUERY | DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY | READ_CONTROL, WorldSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_ALL_ACCESS, SystemSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(*Dacl, ACL_REVISION2, DIRECTORY_TRAVERSE, RestrictedSid); + ASSERT(NT_SUCCESS(Status)); + + /* Now allocate the restricted DACL */ + *RestrictedDacl = RtlAllocateHeap(CsrHeap, 0, AclLength); + ASSERT(*RestrictedDacl != NULL); + + /* Initialize it */ + Status = RtlCreateAcl(*RestrictedDacl, AclLength, ACL_REVISION2); + ASSERT(NT_SUCCESS(Status)); + + /* And add the same ACEs as before */ + /* FIXME: Not really fully correct */ + Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_QUERY | DIRECTORY_TRAVERSE | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY | READ_CONTROL, WorldSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_ALL_ACCESS, SystemSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(*RestrictedDacl, ACL_REVISION2, DIRECTORY_TRAVERSE, RestrictedSid); + ASSERT(NT_SUCCESS(Status)); + + /* The SIDs are captured, can free them now */ + RtlFreeHeap(CsrHeap, 0, SystemSid); + RtlFreeHeap(CsrHeap, 0, WorldSid); + RtlFreeHeap(CsrHeap, 0, RestrictedSid); + return Status; +} + +VOID +WINAPI +BasepFakeStaticServerData(VOID) +{ + NTSTATUS Status; + WCHAR Buffer[MAX_PATH]; + PWCHAR HeapBuffer; + UNICODE_STRING SystemRootString; + UNICODE_STRING UnexpandedSystemRootString = RTL_CONSTANT_STRING(L"%SystemRoot%"); + UNICODE_STRING BaseSrvCSDString; + UNICODE_STRING BaseSrvWindowsDirectory; + UNICODE_STRING BaseSrvWindowsSystemDirectory; + UNICODE_STRING BnoString; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG SessionId; + HANDLE BaseSrvNamedObjectDirectory; + HANDLE BaseSrvRestrictedObjectDirectory; + PACL BnoDacl, BnoRestrictedDacl; + PSECURITY_DESCRIPTOR BnoSd; + HANDLE SymHandle; + UNICODE_STRING DirectoryName, SymlinkName; + ULONG LuidEnabled; + RTL_QUERY_REGISTRY_TABLE BaseServerRegistryConfigurationTable[2] = + { + { + NULL, + RTL_QUERY_REGISTRY_DIRECT, + L"CSDVersion", + &BaseSrvCSDString + }, + {0} + }; + + /* Get the session ID */ + SessionId = NtCurrentPeb()->SessionId; + + /* Get the Windows directory */ + RtlInitEmptyUnicodeString(&SystemRootString, Buffer, sizeof(Buffer)); + Status = RtlExpandEnvironmentStrings_U(NULL, + &UnexpandedSystemRootString, + &SystemRootString, + NULL); + ASSERT(NT_SUCCESS(Status)); + + /* Create the base directory */ + Buffer[SystemRootString.Length / sizeof(WCHAR)] = UNICODE_NULL; + Status = RtlCreateUnicodeString(&BaseSrvWindowsDirectory, + SystemRootString.Buffer); + ASSERT(NT_SUCCESS(Status)); + + /* Create the system directory */ + wcscat(SystemRootString.Buffer, L"\\system32"); + Status = RtlCreateUnicodeString(&BaseSrvWindowsSystemDirectory, + SystemRootString.Buffer); + ASSERT(NT_SUCCESS(Status)); + + /* FIXME: Check Session ID */ + wcscpy(Buffer, L"\\BaseNamedObjects"); + RtlInitUnicodeString(&BnoString, Buffer); + + /* Allocate the server data */ + BaseStaticServerData = RtlAllocateHeap(CsrSrvSharedSectionHeap, + HEAP_ZERO_MEMORY, + sizeof(BASE_STATIC_SERVER_DATA)); + ASSERT(BaseStaticServerData != NULL); + + /* Process timezone information */ + BaseStaticServerData->TermsrvClientTimeZoneId = TIME_ZONE_ID_INVALID; + BaseStaticServerData->TermsrvClientTimeZoneChangeNum = 0; + Status = NtQuerySystemInformation(SystemTimeOfDayInformation, + &BaseStaticServerData->TimeOfDay, + sizeof(BaseStaticServerData->TimeOfDay), + NULL); + ASSERT(NT_SUCCESS(Status)); + + /* Make a shared heap copy of the Windows directory */ + BaseStaticServerData->WindowsDirectory = BaseSrvWindowsDirectory; + HeapBuffer = RtlAllocateHeap(CsrSrvSharedSectionHeap, + 0, + BaseSrvWindowsDirectory.MaximumLength); + ASSERT(HeapBuffer); + RtlCopyMemory(HeapBuffer, + BaseStaticServerData->WindowsDirectory.Buffer, + BaseSrvWindowsDirectory.MaximumLength); + BaseStaticServerData->WindowsDirectory.Buffer = HeapBuffer; + + /* Make a shared heap copy of the System directory */ + BaseStaticServerData->WindowsSystemDirectory = BaseSrvWindowsSystemDirectory; + HeapBuffer = RtlAllocateHeap(CsrSrvSharedSectionHeap, + 0, + BaseSrvWindowsSystemDirectory.MaximumLength); + ASSERT(HeapBuffer); + RtlCopyMemory(HeapBuffer, + BaseStaticServerData->WindowsSystemDirectory.Buffer, + BaseSrvWindowsSystemDirectory.MaximumLength); + BaseStaticServerData->WindowsSystemDirectory.Buffer = HeapBuffer; + + /* This string is not used */ + RtlInitEmptyUnicodeString(&BaseStaticServerData->WindowsSys32x86Directory, + NULL, + 0); + + /* Make a shared heap copy of the BNO directory */ + BaseStaticServerData->NamedObjectDirectory = BnoString; + BaseStaticServerData->NamedObjectDirectory.MaximumLength = BnoString.Length + + sizeof(UNICODE_NULL); + HeapBuffer = RtlAllocateHeap(CsrSrvSharedSectionHeap, + 0, + BaseStaticServerData->NamedObjectDirectory.MaximumLength); + ASSERT(HeapBuffer); + RtlCopyMemory(HeapBuffer, + BaseStaticServerData->NamedObjectDirectory.Buffer, + BaseStaticServerData->NamedObjectDirectory.MaximumLength); + BaseStaticServerData->NamedObjectDirectory.Buffer = HeapBuffer; + + /* + * Confirmed that in Windows, CSDNumber and RCNumber are actually Length + * and MaximumLength of the CSD String, since the same UNICODE_STRING is + * being queried twice, the first time as a ULONG! + * + * Somehow, in Windows this doesn't cause a buffer overflow, but it might + * in ReactOS, so this code is disabled until someone figures out WTF. + */ + BaseStaticServerData->CSDNumber = 0; + BaseStaticServerData->RCNumber = 0; + + /* Initialize the CSD string and query its value from the registry */ + RtlInitEmptyUnicodeString(&BaseSrvCSDString, Buffer, sizeof(Buffer)); + Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, + L"", + BaseServerRegistryConfigurationTable, + NULL, + NULL); + if (NT_SUCCESS(Status)) + { + /* Copy into the shared buffer */ + wcsncpy(BaseStaticServerData->CSDVersion, + BaseSrvCSDString.Buffer, + BaseSrvCSDString.Length / sizeof(WCHAR)); + } + else + { + /* NULL-terminate to indicate nothing is there */ + BaseStaticServerData->CSDVersion[0] = UNICODE_NULL; + } + + /* Cache the system information */ + Status = NtQuerySystemInformation(SystemBasicInformation, + &BaseStaticServerData->SysInfo, + sizeof(BaseStaticServerData->SysInfo), + NULL); + ASSERT(NT_SUCCESS(Status)); + + /* FIXME: Should query the registry for these */ + BaseStaticServerData->DefaultSeparateVDM = FALSE; + BaseStaticServerData->IsWowTaskReady = FALSE; + + /* Allocate a security descriptor and create it */ + BnoSd = RtlAllocateHeap(CsrHeap, 0, 1024); + ASSERT(BnoSd); + Status = RtlCreateSecurityDescriptor(BnoSd, SECURITY_DESCRIPTOR_REVISION); + ASSERT(NT_SUCCESS(Status)); + + /* Create the BNO and \Restricted DACLs */ + Status = CreateBaseAcls(&BnoDacl, &BnoRestrictedDacl); + ASSERT(NT_SUCCESS(Status)); + + /* Set the BNO DACL as active for now */ + Status = RtlSetDaclSecurityDescriptor(BnoSd, TRUE, BnoDacl, FALSE); + ASSERT(NT_SUCCESS(Status)); + + /* Create the BNO directory */ + RtlInitUnicodeString(&BnoString, L"\\BaseNamedObjects"); + InitializeObjectAttributes(&ObjectAttributes, + &BnoString, + OBJ_OPENIF | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, + NULL, + BnoSd); + Status = NtCreateDirectoryObject(&BaseSrvNamedObjectDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + ASSERT(NT_SUCCESS(Status)); + + /* Check if we are session 0 */ + if (!SessionId) + { + /* Mark this as a session 0 directory */ + Status = NtSetInformationObject(BaseSrvNamedObjectDirectory, + ObjectSessionInformation, + NULL, + 0); + ASSERT(NT_SUCCESS(Status)); + } + + /* Check if LUID device maps are enabled */ + Status = NtQueryInformationProcess(NtCurrentProcess(), + ProcessLUIDDeviceMapsEnabled, + &LuidEnabled, + sizeof(LuidEnabled), + NULL); + ASSERT(NT_SUCCESS(Status)); + BaseStaticServerData->LUIDDeviceMapsEnabled = LuidEnabled; + if (!BaseStaticServerData->LUIDDeviceMapsEnabled) + { + /* Make Global point back to BNO */ + RtlInitUnicodeString(&DirectoryName, L"Global"); + RtlInitUnicodeString(&SymlinkName, L"\\BaseNamedObjects"); + InitializeObjectAttributes(&ObjectAttributes, + &DirectoryName, + OBJ_OPENIF | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, + BaseSrvNamedObjectDirectory, + BnoSd); + Status = NtCreateSymbolicLinkObject(&SymHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes, + &SymlinkName); + if ((NT_SUCCESS(Status)) && !(SessionId)) NtClose(SymHandle); + + /* Make local point back to \Sessions\x\BNO */ + RtlInitUnicodeString(&DirectoryName, L"Local"); + ASSERT(SessionId == 0); + InitializeObjectAttributes(&ObjectAttributes, + &DirectoryName, + OBJ_OPENIF | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, + BaseSrvNamedObjectDirectory, + BnoSd); + Status = NtCreateSymbolicLinkObject(&SymHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes, + &SymlinkName); + if ((NT_SUCCESS(Status)) && !(SessionId)) NtClose(SymHandle); + + /* Make Session point back to BNOLINKS */ + RtlInitUnicodeString(&DirectoryName, L"Session"); + RtlInitUnicodeString(&SymlinkName, L"\\Sessions\\BNOLINKS"); + InitializeObjectAttributes(&ObjectAttributes, + &DirectoryName, + OBJ_OPENIF | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, + BaseSrvNamedObjectDirectory, + BnoSd); + Status = NtCreateSymbolicLinkObject(&SymHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes, + &SymlinkName); + if ((NT_SUCCESS(Status)) && !(SessionId)) NtClose(SymHandle); + + /* Create the BNO\Restricted directory and set the restricted DACL */ + RtlInitUnicodeString(&DirectoryName, L"Restricted"); + Status = RtlSetDaclSecurityDescriptor(BnoSd, TRUE, BnoRestrictedDacl, FALSE); + ASSERT(NT_SUCCESS(Status)); + InitializeObjectAttributes(&ObjectAttributes, + &DirectoryName, + OBJ_OPENIF | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, + BaseSrvNamedObjectDirectory, + BnoSd); + Status = NtCreateDirectoryObject(&BaseSrvRestrictedObjectDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + ASSERT(NT_SUCCESS(Status)); + } + + /* Finally, set the pointer */ + CsrSrvSharedStaticServerData[CSR_CONSOLE] = BaseStaticServerData; +} + +NTSTATUS WINAPI +CsrpHandleConnectionRequest(PPORT_MESSAGE Request) +{ + NTSTATUS Status; + HANDLE ServerPort = NULL;//, ServerThread = NULL; + PCSR_PROCESS ProcessData = NULL; + REMOTE_PORT_VIEW RemotePortView; +// CLIENT_ID ClientId; + BOOLEAN AllowConnection = FALSE; + PCSR_CONNECTION_INFO ConnectInfo; + ServerPort = NULL; + + DPRINT("CSR: %s: Handling: %p\n", __FUNCTION__, Request); + + ConnectInfo = (PCSR_CONNECTION_INFO)(Request + 1); + + /* Save the process ID */ + RtlZeroMemory(ConnectInfo, sizeof(CSR_CONNECTION_INFO)); + + CsrLockProcessByClientId(Request->ClientId.UniqueProcess, &ProcessData); + if (!ProcessData) + { + DPRINT1("CSRSRV: Unknown process: %lx. Will be rejecting connection\n", + Request->ClientId.UniqueProcess); + } + + if ((ProcessData) && (ProcessData != CsrRootProcess)) + { + /* Attach the Shared Section */ + Status = CsrSrvAttachSharedSection(ProcessData, ConnectInfo); + if (NT_SUCCESS(Status)) + { + DPRINT("Connection ok\n"); + AllowConnection = TRUE; + } + else + { + DPRINT1("Shared section map failed: %lx\n", Status); + } + } + else if (ProcessData == CsrRootProcess) + { + AllowConnection = TRUE; + } + + /* Release the process */ + if (ProcessData) CsrUnlockProcess(ProcessData); + + /* Setup the Port View Structure */ + RemotePortView.Length = sizeof(REMOTE_PORT_VIEW); + RemotePortView.ViewSize = 0; + RemotePortView.ViewBase = NULL; + + /* Save the Process ID */ + ConnectInfo->ProcessId = NtCurrentTeb()->ClientId.UniqueProcess; + + Status = NtAcceptConnectPort(&ServerPort, + AllowConnection ? UlongToPtr(ProcessData->SequenceNumber) : 0, + Request, + AllowConnection, + NULL, + &RemotePortView); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtAcceptConnectPort - failed. Status == %X\n", Status); + } + else if (AllowConnection) + { + if (CsrDebug & 2) + { + DPRINT1("CSRSS: ClientId: %lx.%lx has ClientView: Base=%p, Size=%lx\n", + Request->ClientId.UniqueProcess, + Request->ClientId.UniqueThread, + RemotePortView.ViewBase, + RemotePortView.ViewSize); + } + + /* Set some Port Data in the Process */ + ProcessData->ClientPort = ServerPort; + ProcessData->ClientViewBase = (ULONG_PTR)RemotePortView.ViewBase; + ProcessData->ClientViewBounds = (ULONG_PTR)((ULONG_PTR)RemotePortView.ViewBase + + (ULONG_PTR)RemotePortView.ViewSize); + + /* Complete the connection */ + Status = NtCompleteConnectPort(ServerPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCompleteConnectPort - failed. Status == %X\n", Status); + } + } + else + { + DPRINT1("CSRSS: Rejecting Connection Request from ClientId: %lx.%lx\n", + Request->ClientId.UniqueProcess, + Request->ClientId.UniqueThread); + } + + return Status; +} + +/*++ + * @name CsrConnectToUser + * @implemented NT4 + * + * The CsrConnectToUser connects to the User subsystem. + * + * @param None + * + * @return A pointer to the CSR Thread + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrConnectToUser(VOID) +{ + PTEB Teb = NtCurrentTeb(); + PCSR_THREAD CsrThread; +#if 0 + NTSTATUS Status; + ANSI_STRING DllName; + UNICODE_STRING TempName; + HANDLE hUser32; + STRING StartupName; + + /* Check if we didn't already find it */ + if (!CsrClientThreadSetup) + { + /* Get the DLL Handle for user32.dll */ + RtlInitAnsiString(&DllName, "user32"); + RtlAnsiStringToUnicodeString(&TempName, &DllName, TRUE); + Status = LdrGetDllHandle(NULL, + NULL, + &TempName, + &hUser32); + RtlFreeUnicodeString(&TempName); + + /* If we got teh handle, get the Client Thread Startup Entrypoint */ + if (NT_SUCCESS(Status)) + { + RtlInitAnsiString(&StartupName,"ClientThreadSetup"); + Status = LdrGetProcedureAddress(hUser32, + &StartupName, + 0, + (PVOID)&CsrClientThreadSetup); + } + } + + /* Connect to user32 */ + CsrClientThreadSetup(); +#endif + /* Save pointer to this thread in TEB */ + CsrThread = CsrLocateThreadInProcess(NULL, &Teb->ClientId); + if (CsrThread) Teb->CsrClientThread = CsrThread; + + /* Return it */ + return CsrThread; +} + +/*++ + * @name CsrpCheckRequestThreads + * + * The CsrpCheckRequestThreads routine checks if there are no more threads + * to handle CSR API Requests, and creates a new thread if possible, to + * avoid starvation. + * + * @param None. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * if a new thread couldn't be created. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrpCheckRequestThreads(VOID) +{ + HANDLE hThread; + CLIENT_ID ClientId; + NTSTATUS Status; + + /* Decrease the count, and see if we're out */ + if (!(_InterlockedDecrement(&CsrpStaticThreadCount))) + { + /* Check if we've still got space for a Dynamic Thread */ + if (CsrpDynamicThreadTotal < CsrMaxApiRequestThreads) + { + /* Create a new dynamic thread */ + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + TRUE, + 0, + 0, + 0, + (PVOID)ClientConnectionThread,//CsrApiRequestThread, + NULL, + &hThread, + &ClientId); + /* Check success */ + if (NT_SUCCESS(Status)) + { + /* Increase the thread counts */ + _InterlockedIncrement(&CsrpStaticThreadCount); + _InterlockedIncrement(&CsrpDynamicThreadTotal); + + /* Add a new server thread */ + if (CsrAddStaticServerThread(hThread, + &ClientId, + CsrThreadIsServerThread)) + { + /* Activate it */ + NtResumeThread(hThread, NULL); + } + else + { + /* Failed to create a new static thread */ + _InterlockedDecrement(&CsrpStaticThreadCount); + _InterlockedDecrement(&CsrpDynamicThreadTotal); + + /* Terminate it */ + DPRINT1("Failing\n"); + NtTerminateThread(hThread, 0); + NtClose(hThread); + + /* Return */ + return STATUS_UNSUCCESSFUL; + } + } + } + } + + /* Success */ + return STATUS_SUCCESS; +} + +VOID +WINAPI +ClientConnectionThread(IN PVOID Parameter) +{ + PTEB Teb = NtCurrentTeb(); + LARGE_INTEGER TimeOut; + NTSTATUS Status; + BYTE RawRequest[LPC_MAX_DATA_LENGTH]; + PCSR_API_MESSAGE Request = (PCSR_API_MESSAGE)RawRequest; + PCSR_API_MESSAGE Reply; + PCSR_PROCESS CsrProcess; + PCSR_THREAD ServerThread, CsrThread; + ULONG MessageType; + HANDLE ReplyPort; + PDBGKM_MSG DebugMessage; + PHARDERROR_MSG HardErrorMsg; + PCLIENT_DIED_MSG ClientDiedMsg; + DPRINT("CSR: %s called\n", __FUNCTION__); + + /* Setup LPC loop port and message */ + Reply = NULL; + ReplyPort = CsrApiPort; + + /* Connect to user32 */ + while (!CsrConnectToUser()) + { + /* Set up the timeout for the connect (30 seconds) */ + TimeOut.QuadPart = -30 * 1000 * 1000 * 10; + + /* Keep trying until we get a response */ + Teb->Win32ClientInfo[0] = 0; + NtDelayExecution(FALSE, &TimeOut); + } + + /* Get our thread */ + ServerThread = Teb->CsrClientThread; + + /* If we got an event... */ + if (Parameter) + { + /* Set it, to let stuff waiting on us load */ + Status = NtSetEvent((HANDLE)Parameter, NULL); + ASSERT(NT_SUCCESS(Status)); + + /* Increase the Thread Counts */ + _InterlockedIncrement(&CsrpStaticThreadCount); + _InterlockedIncrement(&CsrpDynamicThreadTotal); + } + + /* Now start the loop */ + while (TRUE) + { + /* Make sure the real CID is set */ + Teb->RealClientId = Teb->ClientId; + + /* Debug check */ + if (Teb->CountOfOwnedCriticalSections) + { + DPRINT1("CSRSRV: FATAL ERROR. CsrThread is Idle while holding %lu critical sections\n", + Teb->CountOfOwnedCriticalSections); + DPRINT1("CSRSRV: Last Receive Message %lx ReplyMessage %lx\n", + Request, Reply); + DbgBreakPoint(); + } + + /* Send the reply and wait for a new request */ + DPRINT("Replying to: %lx (%lx)\n", ReplyPort, CsrApiPort); + Status = NtReplyWaitReceivePort(ReplyPort, + 0, + &Reply->Header, + &Request->Header); + /* Check if we didn't get success */ + if (Status != STATUS_SUCCESS) + { + /* Was it a failure or another success code? */ + if (!NT_SUCCESS(Status)) + { + /* Check for specific status cases */ + if ((Status != STATUS_INVALID_CID) && + (Status != STATUS_UNSUCCESSFUL) && + ((Status == STATUS_INVALID_HANDLE) || (ReplyPort == CsrApiPort))) + { + /* Notify the debugger */ + DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status); + DPRINT1("CSRSS: ReplyPortHandle %lx CsrApiPort %lx\n", ReplyPort, CsrApiPort); + } + + /* We failed big time, so start out fresh */ + Reply = NULL; + ReplyPort = CsrApiPort; + DPRINT1("failed: %lx\n", Status); + continue; + } + else + { + /* A bizare "success" code, just try again */ + DPRINT1("NtReplyWaitReceivePort returned \"success\" status 0x%x\n", Status); + continue; + } + } + + /* Use whatever Client ID we got */ + Teb->RealClientId = Request->Header.ClientId; + + /* Get the Message Type */ + MessageType = Request->Header.u2.s2.Type; + + /* Handle connection requests */ + if (MessageType == LPC_CONNECTION_REQUEST) + { + /* Handle the Connection Request */ + DPRINT("Accepting new connection\n"); + CsrpHandleConnectionRequest((PPORT_MESSAGE)Request); + Reply = NULL; + ReplyPort = CsrApiPort; + continue; + } + + /* It's some other kind of request. Get the lock for the lookup */ + CsrAcquireProcessLock(); + + /* Now do the lookup to get the CSR_THREAD */ + CsrThread = CsrLocateThreadByClientId(&CsrProcess, + &Request->Header.ClientId); + + /* Did we find a thread? */ + if (!CsrThread) + { + /* This wasn't a CSR Thread, release lock */ + CsrReleaseProcessLock(); + + /* If this was an exception, handle it */ + if (MessageType == LPC_EXCEPTION) + { + DPRINT1("Exception from unknown thread, just continue\n"); + Reply = Request; + ReplyPort = CsrApiPort; + Reply->Status = DBG_CONTINUE; + } + else if (MessageType == LPC_PORT_CLOSED || + MessageType == LPC_CLIENT_DIED) + { + /* The Client or Port are gone, loop again */ + DPRINT("Death from unknown thread, just continue\n"); + Reply = NULL; + ReplyPort = CsrApiPort; + } + else if (MessageType == LPC_ERROR_EVENT) + { + /* If it's a hard error, handle this too */ + DPRINT1("Hard error from unknown thread, call handlers\n"); +HandleHardError: + HardErrorMsg = (PHARDERROR_MSG)Request; + + /* Default it to unhandled */ + HardErrorMsg->Response = ResponseNotHandled; + + /* Check if there are free api threads */ + CsrpCheckRequestThreads(); + if (CsrpStaticThreadCount) + { + CsrHandleHardError(CsrThread, (PHARDERROR_MSG)Request); + } + + /* If the response was 0xFFFFFFFF, we'll ignore it */ + if (HardErrorMsg->Response == 0xFFFFFFFF) + { + Reply = NULL; + ReplyPort = CsrApiPort; + } + else + { + if (CsrThread) CsrDereferenceThread(CsrThread); + Reply = Request; + ReplyPort = CsrApiPort; + } + } + else if (MessageType == LPC_REQUEST) + { + /* This is an API Message coming from a non-CSR Thread */ + DPRINT1("No thread found for request %lx and clientID %lx.%lx\n", + Request->Type & 0xFFFF, + Request->Header.ClientId.UniqueProcess, + Request->Header.ClientId.UniqueThread); + Reply = Request; + ReplyPort = CsrApiPort; + Reply->Status = STATUS_ILLEGAL_FUNCTION; + } + else if (MessageType == LPC_DATAGRAM) + { + DPRINT1("Kernel datagram: not yet supported\n"); + Reply = NULL; + ReplyPort = CsrApiPort; + } + else + { + /* Some other ignored message type */ + Reply = NULL; + ReplyPort = CsrApiPort; + } + + /* Keep going */ + continue; + } + + /* We have a valid thread, was this an LPC Request? */ + if (MessageType != LPC_REQUEST) + { + /* It's not an API, check if the client died */ + if (MessageType == LPC_CLIENT_DIED) + { + /* Get the information and check if it matches our thread */ + ClientDiedMsg = (PCLIENT_DIED_MSG)Request; + if (ClientDiedMsg->CreateTime.QuadPart == CsrThread->CreateTime.QuadPart) + { + /* Reference the thread */ + CsrLockedReferenceThread(CsrThread); + + /* Destroy the thread in the API Message */ + CsrDestroyThread(&Request->Header.ClientId); + + /* Check if the thread was actually ourselves */ + if (CsrProcess->ThreadCount == 1) + { + /* Kill the process manually here */ + DPRINT1("Last thread\n"); + CsrDestroyProcess(&CsrThread->ClientId, 0); + } + + /* Remove our extra reference */ + CsrLockedDereferenceThread(CsrThread); + } + + /* Release the lock and keep looping */ + CsrReleaseProcessLock(); + Reply = NULL; + ReplyPort = CsrApiPort; + continue; + } + + /* Reference the thread and release the lock */ + CsrLockedReferenceThread(CsrThread); + CsrReleaseProcessLock(); + + /* If this was an exception, handle it */ + if (MessageType == LPC_EXCEPTION) + { + /* Kill the process */ + DPRINT1("Exception in %lx.%lx. Killing...\n", + Request->Header.ClientId.UniqueProcess, + Request->Header.ClientId.UniqueThread); + NtTerminateProcess(CsrProcess->ProcessHandle, STATUS_ABANDONED); + + /* Destroy it from CSR */ + CsrDestroyProcess(&Request->Header.ClientId, STATUS_ABANDONED); + + /* Return a Debug Message */ + DebugMessage = (PDBGKM_MSG)Request; + DebugMessage->ReturnedStatus = DBG_CONTINUE; + Reply = Request; + ReplyPort = CsrApiPort; + + /* Remove our extra reference */ + CsrDereferenceThread(CsrThread); + } + else if (MessageType == LPC_ERROR_EVENT) + { + DPRINT1("Hard error from known CSR thread... handling\n"); + goto HandleHardError; + } + else + { + /* Something else */ + DPRINT1("Unhandled message type: %lx\n", MessageType); + CsrDereferenceThread(CsrThread); + Reply = NULL; + } + + /* Keep looping */ + continue; + } + + /* We got an API Request */ + CsrLockedReferenceThread(CsrThread); + CsrReleaseProcessLock(); + + /* Assume success */ + Reply = Request; + Request->Status = STATUS_SUCCESS; + + /* Now we reply to a particular client */ + ReplyPort = CsrThread->Process->ClientPort; + + DPRINT("CSR: Got CSR API: %x [Message Origin: %x]\n", + Request->Type, + Request->Header.ClientId.UniqueThread); + + /* Validation complete, start SEH */ + _SEH2_TRY + { + /* Make sure we have enough threads */ + CsrpCheckRequestThreads(); + + /* Set the client thread pointer */ + NtCurrentTeb()->CsrClientThread = CsrThread; + + /* Call the Handler */ + CsrApiCallHandler(CsrThread->Process, Request); + + /* Increase the static thread count */ + _InterlockedIncrement(&CsrpStaticThreadCount); + + /* Restore the server thread */ + NtCurrentTeb()->CsrClientThread = ServerThread; + + /* Check if this is a dead client now */ + if (Request->Type == 0xBABE) + { + /* Reply to the death message */ + NtReplyPort(ReplyPort, &Reply->Header); + + /* Reply back to the API port now */ + ReplyPort = CsrApiPort; + Reply = NULL; + + /* Drop the reference */ + CsrDereferenceThread(CsrThread); + } + else + { + /* Drop the reference */ + CsrDereferenceThread(CsrThread); + } + } + _SEH2_EXCEPT(CsrUnhandledExceptionFilter(_SEH2_GetExceptionInformation())) + { + Reply = NULL; + ReplyPort = CsrApiPort; + } + _SEH2_END; + } + + /* Close the port and exit the thread */ + // NtClose(ServerPort); + + DPRINT1("CSR: %s done\n", __FUNCTION__); + /* We're out of the loop for some reason, terminate! */ + NtTerminateThread(NtCurrentThread(), Status); + //return Status; +} + +/*++ + * @name CsrReleaseCapturedArguments + * @implemented NT5.1 + * + * The CsrReleaseCapturedArguments routine releases a Capture Buffer + * that was previously captured with CsrCaptureArguments. + * + * @param ApiMessage + * Pointer to the CSR API Message containing the Capture Buffer + * that needs to be released. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage) +{ + PCSR_CAPTURE_BUFFER RemoteCaptureBuffer, LocalCaptureBuffer; + SIZE_T BufferDistance; + ULONG PointerCount; + ULONG_PTR **PointerOffsets, *CurrentPointer; + + /* Get the capture buffers */ + RemoteCaptureBuffer = ApiMessage->CsrCaptureData; + LocalCaptureBuffer = RemoteCaptureBuffer->PreviousCaptureBuffer; + + /* Free the previous one */ + RemoteCaptureBuffer->PreviousCaptureBuffer = NULL; + + /* Find out the difference between the two buffers */ + BufferDistance = (ULONG_PTR)LocalCaptureBuffer - (ULONG_PTR)RemoteCaptureBuffer; + + /* Save the pointer count and offset pointer */ + PointerCount = RemoteCaptureBuffer->PointerCount; + PointerOffsets = (ULONG_PTR**)(RemoteCaptureBuffer + 1); + + /* Start the loop */ + while (PointerCount) + { + /* Get the current pointer */ + CurrentPointer = *PointerOffsets++; + if (CurrentPointer) + { + /* Add it to the CSR Message structure */ + CurrentPointer += (ULONG_PTR)ApiMessage; + + /* Modify the pointer to take into account its new position */ + *CurrentPointer += BufferDistance; + } + + /* Move to the next Pointer */ + PointerCount--; + } + + /* Copy the data back */ + RtlMoveMemory(LocalCaptureBuffer, RemoteCaptureBuffer, RemoteCaptureBuffer->Size); + + /* Free our allocated buffer */ + RtlFreeHeap(CsrHeap, 0, RemoteCaptureBuffer); +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/csrsrv.rc b/reactos/deprecated/csrsrv/csrsrv.rc new file mode 100644 index 00000000000..fbe30d31c97 --- /dev/null +++ b/reactos/deprecated/csrsrv/csrsrv.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Client/Server Runtime SubSystem Process\0" +#define REACTOS_STR_INTERNAL_NAME "csrsrv\0" +#define REACTOS_STR_ORIGINAL_FILENAME "csrsrv.dll\0" +#include diff --git a/reactos/deprecated/csrsrv/csrsrv.spec b/reactos/deprecated/csrsrv/csrsrv.spec new file mode 100644 index 00000000000..19f442849c5 --- /dev/null +++ b/reactos/deprecated/csrsrv/csrsrv.spec @@ -0,0 +1,36 @@ +@ stdcall CsrAddStaticServerThread(ptr ptr long) +@ stdcall CsrCallServerFromServer(ptr ptr) +;@ stdcall CsrConnectToUser() +;@ stdcall CsrCreateProcess(ptr ptr ptr ptr long ptr) +@ stdcall CsrCreateRemoteThread(ptr ptr) +@ stdcall CsrCreateThread(ptr ptr ptr) +;@ stdcall CsrCreateWait(ptr ptr ptr ptr ptr ptr) +;@ stdcall CsrDebugProcess(ptr) +;@ stdcall CsrDebugProcessStop(ptr) +;@ stdcall CsrDereferenceProcess(ptr) +;@ stdcall CsrDereferenceThread(ptr) +;@ stdcall CsrDereferenceWait(ptr) +;@ stdcall CsrDestroyProcess(ptr long) +;@ stdcall CsrDestroyThread(ptr) +@ stdcall CsrEnumProcesses(ptr ptr) ; Temporary hack +;@ stdcall CsrExecServerThread(ptr long) +@ stdcall CsrGetProcessLuid(ptr ptr) +@ stdcall CsrImpersonateClient(ptr) +@ stdcall CsrLockProcessByClientId(ptr ptr) +;@ stdcall CsrLockThreadByClientId(ptr ptr) +;@ stdcall CsrMoveSatisfiedWait(ptr ptr) +;@ stdcall CsrNotifyWait(ptr long ptr ptr) +;@ stdcall CsrPopulateDosDevices() +;@ stdcall CsrQueryApiPort() +;@ stdcall CsrReferenceThread(ptr) +@ stdcall CsrRevertToSelf() +@ stdcall CsrServerInitialization(long ptr) +;@ stdcall CsrSetBackgroundPriority(ptr) +;@ stdcall CsrSetCallingSpooler(long) +;@ stdcall CsrSetForegroundPriority(ptr) +;@ stdcall CsrShutdownProcesses(ptr long) +;@ stdcall CsrUnhandledExceptionFilter(ptr) +@ stdcall CsrUnlockProcess(ptr) +;@ stdcall CsrUnlockThread(ptr) +;@ stdcall CsrValidateMessageBuffer(ptr ptr long long) +;@ stdcall CsrValidateMessageString(ptr ptr) diff --git a/reactos/deprecated/csrsrv/include/api.h b/reactos/deprecated/csrsrv/include/api.h new file mode 100644 index 00000000000..6a71ee05a6d --- /dev/null +++ b/reactos/deprecated/csrsrv/include/api.h @@ -0,0 +1,467 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/include/api.h + * PURPOSE: CSRSS API interface + */ + +#pragma once + +#define NTOS_MODE_USER +#include +#include + +#include + +#define CSR_SRV_SERVER 0 +#define CSR_SERVER_DLL_MAX 4 +#define LOCK RtlEnterCriticalSection(&ProcessDataLock) +#define UNLOCK RtlLeaveCriticalSection(&ProcessDataLock) +#define CsrAcquireProcessLock() LOCK +#define CsrReleaseProcessLock() UNLOCK +#define ProcessStructureListLocked() \ + (ProcessDataLock.OwningThread == NtCurrentTeb()->ClientId.UniqueThread) + +#define CsrAcquireWaitLock() \ + RtlEnterCriticalSection(&CsrWaitListsLock); + +#define CsrReleaseWaitLock() \ + RtlLeaveCriticalSection(&CsrWaitListsLock); + +#define CsrAcquireNtSessionLock() \ + RtlEnterCriticalSection(&CsrNtSessionLock); + +#define CsrReleaseNtSessionLock() \ + RtlLeaveCriticalSection(&CsrNtSessionLock); + +typedef enum _CSR_THREAD_FLAGS +{ + CsrThreadAltertable = 0x1, + CsrThreadInTermination = 0x2, + CsrThreadTerminated = 0x4, + CsrThreadIsServerThread = 0x10 +} CSR_THREAD_FLAGS, *PCSR_THREAD_FLAGS; + +typedef enum _SHUTDOWN_RESULT +{ + CsrShutdownCsrProcess = 1, + CsrShutdownNonCsrProcess, + CsrShutdownCancelled +} SHUTDOWN_RESULT, *PSHUTDOWN_RESULT; + +typedef enum _CSR_SHUTDOWN_FLAGS +{ + CsrShutdownSystem = 4, + CsrShutdownOther = 8 +} CSR_SHUTDOWN_FLAGS, *PCSR_SHUTDOWN_FLAGS; + +typedef enum _CSR_DEBUG_FLAGS +{ + CsrDebugOnlyThisProcess = 1, + CsrDebugProcessChildren = 2 +} CSR_PROCESS_DEBUG_FLAGS, *PCSR_PROCESS_DEBUG_FLAGS; + +typedef enum _CSR_PROCESS_FLAGS +{ + CsrProcessTerminating = 0x1, + CsrProcessSkipShutdown = 0x2, + CsrProcessNormalPriority = 0x10, + CsrProcessIdlePriority = 0x20, + CsrProcessHighPriority = 0x40, + CsrProcessRealtimePriority = 0x80, + CsrProcessCreateNewGroup = 0x100, + CsrProcessTerminated = 0x200, + CsrProcessLastThreadTerminated = 0x400, + CsrProcessIsConsoleApp = 0x800 +} CSR_PROCESS_FLAGS, *PCSR_PROCESS_FLAGS; + +#define CsrProcessPriorityFlags (CsrProcessNormalPriority | \ + CsrProcessIdlePriority | \ + CsrProcessHighPriority | \ + CsrProcessRealtimePriority) + +typedef struct _CSRSS_CON_PROCESS_DATA +{ + HANDLE ConsoleEvent; + struct tagCSRSS_CONSOLE *Console; + struct tagCSRSS_CONSOLE *ParentConsole; + BOOL bInheritHandles; + RTL_CRITICAL_SECTION HandleTableLock; + ULONG HandleTableSize; + struct _CSRSS_HANDLE *HandleTable; + PCONTROLDISPATCHER CtrlDispatcher; + LIST_ENTRY ConsoleLink; +} CSRSS_CON_PROCESS_DATA, *PCSRSS_CON_PROCESS_DATA; + +typedef struct _CSR_NT_SESSION +{ + ULONG ReferenceCount; + LIST_ENTRY SessionLink; + ULONG SessionId; +} CSR_NT_SESSION, *PCSR_NT_SESSION; + +typedef struct _CSR_PROCESS +{ + CLIENT_ID ClientId; + LIST_ENTRY ListLink; + LIST_ENTRY ThreadList; + struct _CSR_PROCESS *Parent; + PCSR_NT_SESSION NtSession; + ULONG ExpectedVersion; + HANDLE ClientPort; + ULONG_PTR ClientViewBase; + ULONG_PTR ClientViewBounds; + HANDLE ProcessHandle; + ULONG SequenceNumber; + ULONG Flags; + ULONG DebugFlags; + CLIENT_ID DebugCid; + ULONG ReferenceCount; + ULONG ProcessGroupId; + ULONG ProcessGroupSequence; + ULONG fVDM; + ULONG ThreadCount; + ULONG PriorityClass; + ULONG Reserved; + ULONG ShutdownLevel; + ULONG ShutdownFlags; +// PVOID ServerData[ANYSIZE_ARRAY]; + CSRSS_CON_PROCESS_DATA; +} CSR_PROCESS, *PCSR_PROCESS; + +typedef struct _CSR_THREAD +{ + LARGE_INTEGER CreateTime; + LIST_ENTRY Link; + LIST_ENTRY HashLinks; + CLIENT_ID ClientId; + PCSR_PROCESS Process; + struct _CSR_WAIT_BLOCK *WaitBlock; + HANDLE ThreadHandle; + ULONG Flags; + ULONG ReferenceCount; + ULONG ImpersonationCount; +} CSR_THREAD, *PCSR_THREAD; + +typedef +BOOLEAN +(*CSR_WAIT_FUNCTION)( + IN PLIST_ENTRY WaitList, + IN PCSR_THREAD WaitThread, + IN PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags +); + +typedef struct _CSR_WAIT_BLOCK +{ + ULONG Size; + LIST_ENTRY WaitList; + LIST_ENTRY UserWaitList; + PVOID WaitContext; + PCSR_THREAD WaitThread; + CSR_WAIT_FUNCTION WaitFunction; + CSR_API_MESSAGE WaitApiMessage; +} CSR_WAIT_BLOCK, *PCSR_WAIT_BLOCK; + +typedef +NTSTATUS +(NTAPI *PCSR_CONNECT_CALLBACK)( + IN PCSR_PROCESS CsrProcess, + IN OUT PVOID ConnectionInfo, + IN OUT PULONG ConnectionInfoLength +); + +typedef +VOID +(NTAPI *PCSR_DISCONNECT_CALLBACK)(IN PCSR_PROCESS CsrProcess); + +typedef +NTSTATUS +(NTAPI *PCSR_NEWPROCESS_CALLBACK)( + IN PCSR_PROCESS Parent, + IN PCSR_PROCESS CsrProcess +); + +typedef +VOID +(NTAPI *PCSR_HARDERROR_CALLBACK)( + IN PCSR_THREAD CsrThread, + IN PHARDERROR_MSG HardErrorMessage +); + +typedef +ULONG +(NTAPI *PCSR_SHUTDOWNPROCESS_CALLBACK)( + IN PCSR_PROCESS CsrProcess, + IN ULONG Flags, + IN BOOLEAN FirstPhase +); + +typedef +NTSTATUS +(NTAPI *PCSR_API_ROUTINE)( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +typedef struct _CSR_SERVER_DLL +{ + ULONG Length; + HANDLE Event; + ANSI_STRING Name; + HANDLE ServerHandle; + ULONG ServerId; + ULONG Unknown; + ULONG ApiBase; + ULONG HighestApiSupported; + PCSR_API_ROUTINE *DispatchTable; + PBOOLEAN ValidTable; + PCHAR *NameTable; + ULONG SizeOfProcessData; + PCSR_CONNECT_CALLBACK ConnectCallback; + PCSR_DISCONNECT_CALLBACK DisconnectCallback; + PCSR_HARDERROR_CALLBACK HardErrorCallback; + PVOID SharedSection; + PCSR_NEWPROCESS_CALLBACK NewProcessCallback; + PCSR_SHUTDOWNPROCESS_CALLBACK ShutdownProcessCallback; + ULONG Unknown2[3]; +} CSR_SERVER_DLL, *PCSR_SERVER_DLL; + +typedef +NTSTATUS +(NTAPI *PCSR_SERVER_DLL_INIT_CALLBACK)(IN PCSR_SERVER_DLL ServerDll); + + +typedef NTSTATUS (WINAPI *CSRSS_API_PROC)(PCSR_PROCESS ProcessData, + PCSR_API_MESSAGE Request); + +typedef struct _CSRSS_API_DEFINITION +{ + ULONG Type; + ULONG MinRequestSize; + CSRSS_API_PROC Handler; +} CSRSS_API_DEFINITION, *PCSRSS_API_DEFINITION; + +#define CSRSS_DEFINE_API(Func, Handler) \ + { Func, sizeof(CSRSS_##Func), Handler } + +typedef struct _CSRSS_LISTEN_DATA +{ + HANDLE ApiPortHandle; + ULONG ApiDefinitionsCount; + PCSRSS_API_DEFINITION *ApiDefinitions; +} CSRSS_LISTEN_DATA, *PCSRSS_LISTEN_DATA; + +#define CSR_API(n) NTSTATUS WINAPI n ( \ + PCSR_PROCESS ProcessData, \ + PCSR_API_MESSAGE Request) + +/* init.c */ +extern HANDLE hBootstrapOk; +NTSTATUS NTAPI CsrServerInitialization(ULONG ArgumentCount, PCHAR Arguments[]); + +/* api/process.c */ +CSR_API(CsrConnectProcess); +CSR_API(CsrSrvCreateProcess); +CSR_API(CsrTerminateProcess); +CSR_API(CsrSrvCreateThread); +CSR_API(CsrGetShutdownParameters); +CSR_API(CsrSetShutdownParameters); + +VOID +NTAPI +CsrSetBackgroundPriority(IN PCSR_PROCESS CsrProcess); + +PCSR_THREAD +NTAPI +CsrAllocateThread(IN PCSR_PROCESS CsrProcess); + +PCSR_PROCESS +NTAPI +CsrAllocateProcess(VOID); + +VOID +NTAPI +CsrDeallocateProcess(IN PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrRemoveProcess(IN PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrDereferenceThread(IN PCSR_THREAD CsrThread); + +VOID +NTAPI +CsrInsertProcess(IN PCSR_PROCESS Parent OPTIONAL, + IN PCSR_PROCESS CurrentProcess OPTIONAL, + IN PCSR_PROCESS CsrProcess); + + +/* api/wapi.c */ +NTSTATUS FASTCALL CsrApiRegisterDefinitions(PCSRSS_API_DEFINITION NewDefinitions); +VOID FASTCALL CsrApiCallHandler(PCSR_PROCESS ProcessData, + PCSR_API_MESSAGE Request); +VOID WINAPI CsrSbApiRequestThread (PVOID PortHandle); +VOID NTAPI ClientConnectionThread(HANDLE ServerPort); + +VOID +NTAPI +CsrReleaseCapturedArguments(IN PCSR_API_MESSAGE ApiMessage); + +extern HANDLE CsrApiPort; +extern HANDLE CsrSmApiPort; +extern HANDLE CsrSbApiPort; +extern LIST_ENTRY CsrThreadHashTable[256]; +extern PCSR_PROCESS CsrRootProcess; +extern RTL_CRITICAL_SECTION ProcessDataLock, CsrWaitListsLock; +extern UNICODE_STRING CsrDirectoryName; +extern ULONG CsrDebug; +extern ULONG CsrTotalPerProcessDataLength; +extern SYSTEM_BASIC_INFORMATION CsrNtSysInfo; +extern PVOID CsrSrvSharedSectionHeap; +extern PVOID *CsrSrvSharedStaticServerData; +extern HANDLE CsrInitializationEvent; +extern PCSR_SERVER_DLL CsrLoadedServerDll[CSR_SERVER_DLL_MAX]; +extern ULONG CsrMaxApiRequestThreads; + +NTSTATUS +NTAPI +CsrApiPortInitialize(VOID); + +NTSTATUS +NTAPI +CsrCreateProcess(IN HANDLE hProcess, + IN HANDLE hThread, + IN PCLIENT_ID ClientId, + IN PCSR_NT_SESSION NtSession, + IN ULONG Flags, + IN PCLIENT_ID DebugCid); + +BOOLEAN +NTAPI +ProtectHandle(IN HANDLE ObjectHandle); + +VOID +NTAPI +CsrInsertThread(IN PCSR_PROCESS Process, +IN PCSR_THREAD Thread); + +VOID +NTAPI +CsrLockedReferenceThread(IN PCSR_THREAD CsrThread); + +/* api/process.c */ +typedef NTSTATUS (WINAPI *CSRSS_ENUM_PROCESS_PROC)(PCSR_PROCESS ProcessData, + PVOID Context); +NTSTATUS WINAPI CsrInitializeProcessStructure(VOID); + +NTSTATUS WINAPI CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc, PVOID Context); +PCSR_THREAD NTAPI CsrAddStaticServerThread(IN HANDLE hThread, IN PCLIENT_ID ClientId, IN ULONG ThreadFlags); +PCSR_THREAD NTAPI CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL, IN PCLIENT_ID Cid); +PCSR_THREAD NTAPI CsrLocateThreadByClientId(OUT PCSR_PROCESS *Process OPTIONAL, IN PCLIENT_ID ClientId); +NTSTATUS NTAPI CsrLockProcessByClientId(IN HANDLE Pid, OUT PCSR_PROCESS *CsrProcess OPTIONAL); +NTSTATUS NTAPI CsrCreateThread(IN PCSR_PROCESS CsrProcess, IN HANDLE hThread, IN PCLIENT_ID ClientId); +NTSTATUS NTAPI CsrUnlockProcess(IN PCSR_PROCESS CsrProcess); + +//hack +VOID NTAPI CsrThreadRefcountZero(IN PCSR_THREAD CsrThread); + +NTSTATUS +NTAPI +CsrInitializeNtSessionList(VOID); + +NTSTATUS +NTAPI +CsrSrvAttachSharedSection(IN PCSR_PROCESS CsrProcess OPTIONAL, +OUT PCSR_CONNECTION_INFO ConnectInfo); + +NTSTATUS +NTAPI +CsrSrvCreateSharedSection(IN PCHAR ParameterValue); + +NTSTATUS +NTAPI +CsrSrvClientConnect( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrSrvUnusedFunction( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrSrvIdentifyAlertableThread( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrSrvSetPriorityClass( + IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply +); + +NTSTATUS +NTAPI +CsrDestroyProcess(IN PCLIENT_ID Cid, +IN NTSTATUS ExitStatus); + +NTSTATUS +NTAPI +CsrDestroyThread(IN PCLIENT_ID Cid); + +VOID +NTAPI +CsrLockedDereferenceThread(IN PCSR_THREAD CsrThread); + +BOOLEAN +NTAPI +CsrNotifyWaitBlock(IN PCSR_WAIT_BLOCK WaitBlock, + IN PLIST_ENTRY WaitList, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags, + IN BOOLEAN DereferenceThread); + +VOID +NTAPI +CsrReferenceNtSession(IN PCSR_NT_SESSION Session); + +LONG +NTAPI +CsrUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo); + +VOID +NTAPI +CsrDereferenceNtSession(IN PCSR_NT_SESSION Session, +IN NTSTATUS ExitStatus); + +VOID +NTAPI +CsrLockedDereferenceProcess(PCSR_PROCESS CsrProcess); + +VOID +NTAPI +CsrDereferenceProcess(IN PCSR_PROCESS CsrProcess); + +NTSTATUS +NTAPI +CsrLoadServerDll(IN PCHAR DllString, + IN PCHAR EntryPoint OPTIONAL, + IN ULONG ServerId); + +/* api/user.c */ +CSR_API(CsrRegisterServicesProcess); + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/include/csrplugin.h b/reactos/deprecated/csrsrv/include/csrplugin.h new file mode 100644 index 00000000000..885ea8af929 --- /dev/null +++ b/reactos/deprecated/csrsrv/include/csrplugin.h @@ -0,0 +1,51 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/include/csrplugin.h + * PURPOSE: CSRSS plugin interface + */ + +/* + * CSRSS is a native application and can only implicitly link against native + * DLLs. Since e.g. user32.dll and gdi32.dll are win32 DLLs and not native + * DLLs it is not possible to call functions in those DLLs directly from + * CSRSS. + * However, it is possible to explicitly load a non-native DLL. Such a DLL + * can then in turn be implicitly linked against other DLLs in its own + * subsystem. + */ + +#pragma once + +#include "api.h" + +typedef NTSTATUS (WINAPI *CSRSS_ENUM_PROCESSES_PROC)(CSRSS_ENUM_PROCESS_PROC EnumProc, + PVOID Context); + +typedef struct tagCSRSS_EXPORTED_FUNCS +{ + CSRSS_ENUM_PROCESSES_PROC CsrEnumProcessesProc; +} CSRSS_EXPORTED_FUNCS, *PCSRSS_EXPORTED_FUNCS; + +typedef BOOL (WINAPI *CSRPLUGIN_INIT_COMPLETE_PROC)(void); + +typedef VOID (WINAPI *CSRPLUGIN_HARDERROR_PROC)(IN PCSR_THREAD ThreadData, + IN PHARDERROR_MSG HardErrorMessage); + +typedef NTSTATUS (WINAPI *CSRPLUGIN_PROCESS_INHERIT_PROC)(IN PCSR_PROCESS SourceProcessData, + IN PCSR_PROCESS TargetProcessData); + +typedef ULONG (WINAPI *CSRPLUGIN_PROCESS_DELETED_PROC)(IN PCSR_PROCESS ProcessData, IN ULONG Unused, IN BOOLEAN Flag); + +typedef struct tagCSRSS_SERVER_PROCS +{ + CSRPLUGIN_INIT_COMPLETE_PROC InitCompleteProc; + CSRPLUGIN_HARDERROR_PROC HardErrorProc; + CSRPLUGIN_PROCESS_INHERIT_PROC ProcessInheritProc; + CSRPLUGIN_PROCESS_DELETED_PROC ProcessDeletedProc; +} CSRPLUGIN_SERVER_PROCS, *PCSRPLUGIN_SERVER_PROCS; + +typedef BOOL (WINAPI *CSRPLUGIN_INITIALIZE_PROC)(PCSRSS_API_DEFINITION *ApiDefinitions, + PCSRPLUGIN_SERVER_PROCS ServerProcs); + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/init.c b/reactos/deprecated/csrsrv/init.c new file mode 100644 index 00000000000..45a44336cc1 --- /dev/null +++ b/reactos/deprecated/csrsrv/init.c @@ -0,0 +1,1144 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsystems/win32/csrss/csrsrv/init.c + * PURPOSE: CSR Server DLL Initialization + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + * ReactOS Portable Systems Group + */ + +/* INCLUDES *******************************************************************/ + +#include "srv.h" +#define NDEBUG +#include + +/* DATA ***********************************************************************/ + +HANDLE CsrHeap = (HANDLE) 0; +HANDLE CsrObjectDirectory = (HANDLE) 0; +UNICODE_STRING CsrDirectoryName; +UNICODE_STRING CsrSbApiPortName; +HANDLE CsrSbApiPort = 0; +PCSR_THREAD CsrSbApiRequestThreadPtr; +HANDLE CsrSmApiPort; +HANDLE hSbApiPort = (HANDLE) 0; +HANDLE CsrApiPort = (HANDLE) 0; +ULONG CsrDebug = 0;//0xFFFFFFFF; +ULONG CsrMaxApiRequestThreads; +ULONG CsrTotalPerProcessDataLength; +ULONG SessionId; +HANDLE BNOLinksDirectory; +HANDLE SessionObjectDirectory; +HANDLE DosDevicesDirectory; +HANDLE CsrInitializationEvent; +SYSTEM_BASIC_INFORMATION CsrNtSysInfo; + +/* PRIVATE FUNCTIONS **********************************************************/ + +VOID +CallHardError(IN PCSR_THREAD ThreadData, + IN PHARDERROR_MSG HardErrorMessage) +{ + unsigned i; + PCSR_SERVER_DLL ServerDll; + + DPRINT("CSR: %s called\n", __FUNCTION__); + + /* Notify the Server DLLs */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the current Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Make sure it's valid and that it has callback */ + if ((ServerDll) && (ServerDll->HardErrorCallback)) + { + ServerDll->HardErrorCallback(ThreadData, HardErrorMessage); + } + } +} + +NTSTATUS +CallProcessCreated(IN PCSR_PROCESS SourceProcessData, + IN PCSR_PROCESS TargetProcessData) +{ + NTSTATUS Status = STATUS_SUCCESS; + unsigned i; + PCSR_SERVER_DLL ServerDll; + + DPRINT("CSR: %s called\n", __FUNCTION__); + + /* Notify the Server DLLs */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the current Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Make sure it's valid and that it has callback */ + if ((ServerDll) && (ServerDll->NewProcessCallback)) + { + Status = ServerDll->NewProcessCallback(SourceProcessData, TargetProcessData); + } + } + + return Status; +} + +CSRSS_API_DEFINITION NativeDefinitions[] = + { + CSRSS_DEFINE_API(CREATE_PROCESS, CsrSrvCreateProcess), + CSRSS_DEFINE_API(CREATE_THREAD, CsrSrvCreateThread), + CSRSS_DEFINE_API(TERMINATE_PROCESS, CsrTerminateProcess), + CSRSS_DEFINE_API(CONNECT_PROCESS, CsrConnectProcess), + CSRSS_DEFINE_API(REGISTER_SERVICES_PROCESS, CsrRegisterServicesProcess), + CSRSS_DEFINE_API(GET_SHUTDOWN_PARAMETERS, CsrGetShutdownParameters), + CSRSS_DEFINE_API(SET_SHUTDOWN_PARAMETERS, CsrSetShutdownParameters), + { 0, 0, NULL } + }; + +/* === INIT ROUTINES === */ + +VOID +WINAPI +BasepFakeStaticServerData(VOID); + +/*++ + * @name CsrSetProcessSecurity + * + * The CsrSetProcessSecurity routine protects access to the CSRSS process + * from unauthorized tampering. + * + * @param None. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSetProcessSecurity(VOID) +{ + NTSTATUS Status; + HANDLE hToken, hProcess = NtCurrentProcess(); + ULONG Length; + PTOKEN_USER TokenInfo = NULL; + PSECURITY_DESCRIPTOR ProcSd = NULL; + PACL Dacl; + PSID UserSid; + + /* Open our token */ + Status = NtOpenProcessToken(hProcess, TOKEN_QUERY, &hToken); + if (!NT_SUCCESS(Status)) goto Quickie; + + /* Get the Token User Length */ + NtQueryInformationToken(hToken, TokenUser, NULL, 0, &Length); + + /* Allocate space for it */ + TokenInfo = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, Length); + if (!TokenInfo) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Now query the data */ + Status = NtQueryInformationToken(hToken, TokenUser, TokenInfo, Length, &Length); + NtClose(hToken); + if (!NT_SUCCESS(Status)) goto Quickie; + + /* Now check the SID Length */ + UserSid = TokenInfo->User.Sid; + Length = RtlLengthSid(UserSid) + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + + /* Allocate a buffer for the Security Descriptor, with SID and DACL */ + ProcSd = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, SECURITY_DESCRIPTOR_MIN_LENGTH + Length); + if (!ProcSd) + { + Status = STATUS_NO_MEMORY; + goto Quickie; + } + + /* Set the pointer to the DACL */ + Dacl = (PACL)((ULONG_PTR)ProcSd + SECURITY_DESCRIPTOR_MIN_LENGTH); + + /* Now create the SD itself */ + Status = RtlCreateSecurityDescriptor(ProcSd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: SD creation failed - status = %lx\n", Status); + goto Quickie; + } + + /* Create the DACL for it*/ + Status = RtlCreateAcl(Dacl, Length, ACL_REVISION2); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: DACL creation failed - status = %lx\n", Status); + goto Quickie; + } + + /* Create the ACE */ + Status = RtlAddAccessAllowedAce(Dacl, + ACL_REVISION, + PROCESS_VM_READ | PROCESS_VM_WRITE | + PROCESS_VM_OPERATION | PROCESS_DUP_HANDLE | + PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME | + PROCESS_QUERY_INFORMATION | READ_CONTROL, + UserSid); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: ACE creation failed - status = %lx\n", Status); + goto Quickie; + } + + /* Clear the DACL in the SD */ + Status = RtlSetDaclSecurityDescriptor(ProcSd, TRUE, Dacl, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: set DACL failed - status = %lx\n", Status); + goto Quickie; + } + + /* Write the SD into the Process */ + Status = NtSetSecurityObject(hProcess, DACL_SECURITY_INFORMATION, ProcSd); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: set process DACL failed - status = %lx\n", Status); + goto Quickie; + } + + /* Free the memory and return */ +Quickie: + if (ProcSd) RtlFreeHeap(CsrHeap, 0, ProcSd); + RtlFreeHeap(CsrHeap, 0, TokenInfo); + return Status; +} + +/*++ + * @name CsrSetDirectorySecurity + * + * The CsrSetDirectorySecurity routine sets the security descriptor for the + * specified Object Directory. + * + * @param ObjectDirectory + * Handle fo the Object Directory to protect. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSetDirectorySecurity(IN HANDLE ObjectDirectory) +{ + /* FIXME: Implement */ + return STATUS_SUCCESS; +} + +/*++ + * @name GetDosDevicesProtection + * + * The GetDosDevicesProtection creates a security descriptor for the DOS Devices + * Object Directory. + * + * @param DosDevicesSd + * Pointer to the Security Descriptor to return. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks Depending on the DOS Devices Protection Mode (set in the registry), + * regular users may or may not have full access to the directory. + * + *--*/ +NTSTATUS +NTAPI +GetDosDevicesProtection(OUT PSECURITY_DESCRIPTOR DosDevicesSd) +{ + SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; + SID_IDENTIFIER_AUTHORITY CreatorAuthority = {SECURITY_CREATOR_SID_AUTHORITY}; + SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY}; + PSID WorldSid, CreatorSid, AdminSid, SystemSid; + UCHAR KeyValueBuffer[0x40]; + PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo; + UNICODE_STRING KeyName; + ULONG ProtectionMode = 0; + OBJECT_ATTRIBUTES ObjectAttributes; + PACL Dacl; + PACCESS_ALLOWED_ACE Ace; + HANDLE hKey; + NTSTATUS Status; + ULONG ResultLength, SidLength, AclLength; + + /* Create the SD */ + Status = RtlCreateSecurityDescriptor(DosDevicesSd, SECURITY_DESCRIPTOR_REVISION); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the System SID */ + Status = RtlAllocateAndInitializeSid(&NtSidAuthority, 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &SystemSid); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the World SID */ + Status = RtlAllocateAndInitializeSid(&WorldAuthority, 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &WorldSid); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the Admin SID */ + Status = RtlAllocateAndInitializeSid(&NtSidAuthority, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &AdminSid); + ASSERT(NT_SUCCESS(Status)); + + /* Initialize the Creator SID */ + Status = RtlAllocateAndInitializeSid(&CreatorAuthority, 1, + SECURITY_CREATOR_OWNER_RID, + 0, 0, 0, 0, 0, 0, 0, + &CreatorSid); + ASSERT(NT_SUCCESS(Status)); + + /* Open the Session Manager Key */ + RtlInitUnicodeString(&KeyName, SM_REG_KEY); + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes); + if (NT_SUCCESS(Status)) + { + /* Read the key value */ + RtlInitUnicodeString(&KeyName, L"ProtectionMode"); + Status = NtQueryValueKey(hKey, + &KeyName, + KeyValuePartialInformation, + KeyValueBuffer, + sizeof(KeyValueBuffer), + &ResultLength); + + /* Make sure it's what we expect it to be */ + KeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer; + if ((NT_SUCCESS(Status)) && (KeyValuePartialInfo->Type == REG_DWORD) && + (*(PULONG)KeyValuePartialInfo->Data)) + { + /* Save the Protection Mode */ + ProtectionMode = *(PULONG)KeyValuePartialInfo->Data; + } + + /* Close the handle */ + NtClose(hKey); + } + + /* Check the Protection Mode */ + if (ProtectionMode & 3) + { + /* Calculate SID Lengths */ + SidLength = RtlLengthSid(CreatorSid) + RtlLengthSid(SystemSid) + + RtlLengthSid(AdminSid); + AclLength = sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) + SidLength; + + /* Allocate memory for the DACL */ + Dacl = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, AclLength); + ASSERT(Dacl != NULL); + + /* Build the ACL and add 3 ACEs */ + Status = RtlCreateAcl(Dacl, AclLength, ACL_REVISION2); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, SystemSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, AdminSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, CreatorSid); + ASSERT(NT_SUCCESS(Status)); + + /* Edit the ACEs to make them inheritable */ + Status = RtlGetAce(Dacl, 0, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + Status = RtlGetAce(Dacl, 1, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + Status = RtlGetAce(Dacl, 2, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE; + + /* Set this DACL with the SD */ + Status = RtlSetDaclSecurityDescriptor(DosDevicesSd, TRUE, Dacl, FALSE); + ASSERT(NT_SUCCESS(Status)); + goto Quickie; + } + else + { + /* Calculate SID Lengths */ + SidLength = RtlLengthSid(WorldSid) + RtlLengthSid(SystemSid); + AclLength = sizeof(ACL) + 3 * sizeof(ACCESS_ALLOWED_ACE) + SidLength; + + /* Allocate memory for the DACL */ + Dacl = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, AclLength); + ASSERT(Dacl != NULL); + + /* Build the ACL and add 3 ACEs */ + Status = RtlCreateAcl(Dacl, AclLength, ACL_REVISION2); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE, WorldSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, SystemSid); + ASSERT(NT_SUCCESS(Status)); + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, WorldSid); + ASSERT(NT_SUCCESS(Status)); + + /* Edit the last ACE to make it inheritable */ + Status = RtlGetAce(Dacl, 2, (PVOID*)&Ace); + ASSERT(NT_SUCCESS(Status)); + Ace->Header.AceFlags |= OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE; + + /* Set this DACL with the SD */ + Status = RtlSetDaclSecurityDescriptor(DosDevicesSd, TRUE, Dacl, FALSE); + ASSERT(NT_SUCCESS(Status)); + goto Quickie; + } + +/* FIXME: failure cases! Fail: */ + /* Free the memory */ + RtlFreeHeap(CsrHeap, 0, Dacl); + +/* FIXME: semi-failure cases! Quickie: */ +Quickie: + /* Free the SIDs */ + RtlFreeSid(SystemSid); + RtlFreeSid(WorldSid); + RtlFreeSid(AdminSid); + RtlFreeSid(CreatorSid); + + /* Return */ + return Status; +} + +/*++ + * @name FreeDosDevicesProtection + * + * The FreeDosDevicesProtection frees the security descriptor that was created + * by GetDosDevicesProtection + * + * @param DosDevicesSd + * Pointer to the security descriptor to free. + + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +FreeDosDevicesProtection(IN PSECURITY_DESCRIPTOR DosDevicesSd) +{ + PACL Dacl; + BOOLEAN Present, Default; + NTSTATUS Status; + + /* Get the DACL corresponding to this SD */ + Status = RtlGetDaclSecurityDescriptor(DosDevicesSd, &Present, &Dacl, &Default); + ASSERT(NT_SUCCESS(Status)); + ASSERT(Present); + ASSERT(Dacl != NULL); + + /* Free it */ + if ((NT_SUCCESS(Status)) && (Dacl)) RtlFreeHeap(CsrHeap, 0, Dacl); +} + +/*++ + * @name CsrCreateSessionObjectDirectory + * + * The CsrCreateSessionObjectDirectory routine creates the BaseNamedObjects, + * Session and Dos Devices directories for the specified session. + * + * @param Session + * Session ID for which to create the directories. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateSessionObjectDirectory(IN ULONG Session) +{ + WCHAR SessionBuffer[512], BnoBuffer[512]; + UNICODE_STRING SessionString, BnoString; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE BnoHandle; + SECURITY_DESCRIPTOR DosDevicesSd; + NTSTATUS Status; + + /* Generate the Session BNOLINKS Directory name */ + swprintf(SessionBuffer, L"%ws\\BNOLINKS", SESSION_ROOT); + RtlInitUnicodeString(&SessionString, SessionBuffer); + + /* Create it */ + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtCreateDirectoryObject(&BNOLinksDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateDirectoryObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + return Status; + } + + /* Now add the Session ID */ + swprintf(SessionBuffer, L"%ld", Session); + RtlInitUnicodeString(&SessionString, SessionBuffer); + + /* Check if this is the first Session */ + if (Session) + { + /* Not the first, so the name will be slighly more complex */ + swprintf(BnoBuffer, L"%ws\\%ld\\BaseNamedObjects", SESSION_ROOT, Session); + RtlInitUnicodeString(&BnoString, BnoBuffer); + } + else + { + /* Use the direct name */ + RtlInitUnicodeString(&BnoString, L"\\BaseNamedObjects"); + } + + /* Create the symlink */ + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE, + BNOLinksDirectory, + NULL); + Status = NtCreateSymbolicLinkObject(&BnoHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes, + &BnoString); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateSymbolicLinkObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + return Status; + } + + /* Create the \DosDevices Security Descriptor */ + Status = GetDosDevicesProtection(&DosDevicesSd); + if (!NT_SUCCESS(Status)) return Status; + + /* Now create a directory for this session */ + swprintf(SessionBuffer, L"%ws\\%ld", SESSION_ROOT, Session); + RtlInitUnicodeString(&SessionString, SessionBuffer); + + /* Create the directory */ + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE, + 0, + &DosDevicesSd); + Status = NtCreateDirectoryObject(&SessionObjectDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateDirectoryObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + FreeDosDevicesProtection(&DosDevicesSd); + return Status; + } + + /* Next, create a directory for this session's DOS Devices */ + RtlInitUnicodeString(&SessionString, L"DosDevices"); + InitializeObjectAttributes(&ObjectAttributes, + &SessionString, + OBJ_CASE_INSENSITIVE, + SessionObjectDirectory, + &DosDevicesSd); + Status = NtCreateDirectoryObject(&DosDevicesDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: NtCreateDirectoryObject failed in " + "CsrCreateSessionObjectDirectory - status = %lx\n", Status); + } + + /* Release the Security Descriptor */ + FreeDosDevicesProtection(&DosDevicesSd); + + /* Return */ + return Status; +} + +/*++ + * @name CsrParseServerCommandLine + * + * The CsrParseServerCommandLine routine parses the CSRSS command-line in the + * registry and performs operations for each entry found. + * + * @param ArgumentCount + * Number of arguments on the command line. + * + * @param Arguments + * Array of arguments. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +FASTCALL +CsrParseServerCommandLine(IN ULONG ArgumentCount, + IN PCHAR Arguments[]) +{ + NTSTATUS Status; + PCHAR ParameterName = NULL, ParameterValue = NULL, EntryPoint, ServerString; + ULONG i, DllIndex; + ANSI_STRING AnsiString; + OBJECT_ATTRIBUTES ObjectAttributes; + + /* Set the Defaults */ + CsrTotalPerProcessDataLength = 0; + CsrObjectDirectory = NULL; + CsrMaxApiRequestThreads = 16; + + /* Save our Session ID, and create a Directory for it */ + SessionId = NtCurrentPeb()->SessionId; + Status = CsrCreateSessionObjectDirectory(SessionId); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: CsrCreateSessionObjectDirectory failed (%lx)\n", + Status); + + /* It's not fatal if the session ID isn't zero */ + if (SessionId) return Status; + ASSERT(NT_SUCCESS(Status)); + } + + /* Loop through every argument */ + for (i = 1; i < ArgumentCount; i++) + { + /* Split Name and Value */ + ParameterName = Arguments[i]; + ParameterValue = NULL; + ParameterValue = strchr(ParameterName, '='); + if (ParameterValue) *ParameterValue++ = ANSI_NULL; + + /* Check for Object Directory */ + if (!_stricmp(ParameterName, "ObjectDirectory")) + { + /* Check if a session ID is specified */ + if (SessionId) + { + DPRINT1("Sessions not yet implemented\n"); + ASSERT(SessionId); + } + + /* Initialize the directory name */ + RtlInitAnsiString(&AnsiString, ParameterValue); + Status = RtlAnsiStringToUnicodeString(&CsrDirectoryName, + &AnsiString, + TRUE); + ASSERT(NT_SUCCESS(Status) || SessionId != 0); + if (!NT_SUCCESS(Status)) return Status; + + /* Create it */ + InitializeObjectAttributes(&ObjectAttributes, + &CsrDirectoryName, + OBJ_OPENIF | OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, + NULL, + NULL); + Status = NtCreateDirectoryObject(&CsrObjectDirectory, + DIRECTORY_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) return Status; + + /* Secure it */ + Status = CsrSetDirectorySecurity(CsrObjectDirectory); + if (!NT_SUCCESS(Status)) return Status; + } + else if (!_stricmp(ParameterName, "SubSystemType")) + { + /* Ignored */ + } + else if (!_stricmp(ParameterName, "MaxRequestThreads")) + { + Status = RtlCharToInteger(ParameterValue, + 0, + &CsrMaxApiRequestThreads); + } + else if (!_stricmp(ParameterName, "RequestThreads")) + { + /* Ignored */ + Status = STATUS_SUCCESS; + } + else if (!_stricmp(ParameterName, "ProfileControl")) + { + /* Ignored */ + } + else if (!_stricmp(ParameterName, "SharedSection")) + { + /* Create the Section */ + Status = CsrSrvCreateSharedSection(ParameterValue); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: *** Invalid syntax for %s=%s (Status == %X)\n", + ParameterName, ParameterValue, Status); + return Status; + } + + /* Load us */ + Status = CsrLoadServerDll("CSRSS", NULL, CSR_SRV_SERVER); + } + else if (!_stricmp(ParameterName, "ServerDLL")) + { + /* Loop the command line */ + EntryPoint = NULL; + Status = STATUS_INVALID_PARAMETER; + ServerString = ParameterValue; + while (*ServerString) + { + /* Check for the Entry Point */ + if ((*ServerString == ':') && (!EntryPoint)) + { + /* Found it. Add a nullchar and save it */ + *ServerString++ = ANSI_NULL; + EntryPoint = ServerString; + } + + /* Check for the Dll Index */ + if (*ServerString++ == ',') break; + } + + /* Did we find something to load? */ + if (!*ServerString) + { + DPRINT1("CSRSS: *** Invalid syntax for ServerDll=%s (Status == %X)\n", + ParameterValue, Status); + return Status; + } + + /* Convert it to a ULONG */ + Status = RtlCharToInteger(ServerString, 10, &DllIndex); + + /* Add a null char if it was valid */ + if (NT_SUCCESS(Status)) ServerString[-1] = ANSI_NULL; + + /* Load it */ + if (CsrDebug & 1) DPRINT1("CSRSS: Loading ServerDll=%s:%s\n", ParameterValue, EntryPoint); + + /* Hackito ergo sum */ + Status = STATUS_SUCCESS; + if (strstr(ParameterValue, "basesrv")) + { + DPRINT1("Fake basesrv init\n"); + BasepFakeStaticServerData(); + } +// else +// { +// Status = CsrLoadServerDll(ParameterValue, EntryPoint, 2); +// } + // Status = CsrLoadServerDll(ParameterValue, EntryPoint, DllIndex); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: *** Failed loading ServerDll=%s (Status == 0x%x)\n", + ParameterValue, Status); + return Status; + } + } + else if (!_stricmp(ParameterName, "Windows")) + { + /* Ignored */ + } + else + { + /* Invalid parameter on the command line */ + Status = STATUS_INVALID_PARAMETER; + } + } + + /* Return status */ + return Status; +} + +/*++ + * @name CsrCreateLocalSystemSD + * + * The CsrCreateLocalSystemSD routine creates a Security Descriptor for + * the local account with PORT_ALL_ACCESS. + * + * @param LocalSystemSd + * Pointer to a pointer to the security descriptor to create. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateLocalSystemSD(OUT PSECURITY_DESCRIPTOR *LocalSystemSd) +{ + SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY}; + PSID SystemSid; + ULONG Length; + PSECURITY_DESCRIPTOR SystemSd; + PACL Dacl; + NTSTATUS Status; + + /* Initialize the System SID */ + RtlAllocateAndInitializeSid(&NtSidAuthority, 1, + SECURITY_LOCAL_SYSTEM_RID, + 0, 0, 0, 0, 0, 0, 0, + &SystemSid); + + /* Get the length of the SID */ + Length = RtlLengthSid(SystemSid) + sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); + + /* Allocate a buffer for the Security Descriptor, with SID and DACL */ + SystemSd = RtlAllocateHeap(CsrHeap, 0, SECURITY_DESCRIPTOR_MIN_LENGTH + Length); + + /* Set the pointer to the DACL */ + Dacl = (PACL)((ULONG_PTR)SystemSd + SECURITY_DESCRIPTOR_MIN_LENGTH); + + /* Now create the SD itself */ + Status = RtlCreateSecurityDescriptor(SystemSd, SECURITY_DESCRIPTOR_REVISION); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + RtlFreeHeap(CsrHeap, 0, SystemSd); + return Status; + } + + /* Create the DACL for it */ + RtlCreateAcl(Dacl, Length, ACL_REVISION2); + + /* Create the ACE */ + Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, PORT_ALL_ACCESS, SystemSid); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + RtlFreeHeap(CsrHeap, 0, SystemSd); + return Status; + } + + /* Clear the DACL in the SD */ + Status = RtlSetDaclSecurityDescriptor(SystemSd, TRUE, Dacl, FALSE); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + RtlFreeHeap(CsrHeap, 0, SystemSd); + return Status; + } + + /* Free the SID and return*/ + RtlFreeSid(SystemSid); + *LocalSystemSd = SystemSd; + return Status; +} + +/*++ + * @name CsrSbApiPortInitialize + * + * The CsrSbApiPortInitialize routine initializes the LPC Port used for + * communications with the Session Manager (SM) and initializes the static + * thread that will handle connection requests and APIs. + * + * @param None + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSbApiPortInitialize(VOID) +{ + ULONG Size; + PSECURITY_DESCRIPTOR PortSd; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE hRequestThread; + CLIENT_ID ClientId; + + /* Calculate how much space we'll need for the Port Name */ + Size = CsrDirectoryName.Length + sizeof(SB_PORT_NAME) + sizeof(WCHAR); + + /* Create the buffer for it */ + CsrSbApiPortName.Buffer = RtlAllocateHeap(CsrHeap, 0, Size); + if (!CsrSbApiPortName.Buffer) return STATUS_NO_MEMORY; + + /* Setup the rest of the empty string */ + CsrSbApiPortName.Length = 0; + CsrSbApiPortName.MaximumLength = (USHORT)Size; + + /* Now append the full port name */ + RtlAppendUnicodeStringToString(&CsrSbApiPortName, &CsrDirectoryName); + RtlAppendUnicodeToString(&CsrSbApiPortName, UNICODE_PATH_SEP); + RtlAppendUnicodeToString(&CsrSbApiPortName, SB_PORT_NAME); + if (CsrDebug & 2) DPRINT1("CSRSS: Creating %wZ port and associated thread\n", &CsrSbApiPortName); + + /* Create Security Descriptor for this Port */ + Status = CsrCreateLocalSystemSD(&PortSd); + if (!NT_SUCCESS(Status)) return Status; + + /* Initialize the Attributes */ + InitializeObjectAttributes(&ObjectAttributes, + &CsrSbApiPortName, + 0, + NULL, + PortSd); + + /* Create the Port Object */ + Status = NtCreatePort(&CsrSbApiPort, + &ObjectAttributes, + sizeof(SB_CONNECTION_INFO), + sizeof(SB_API_MSG), + 32 * sizeof(SB_API_MSG)); + if (PortSd) RtlFreeHeap(CsrHeap, 0, PortSd); + + if (NT_SUCCESS(Status)) + { + /* Create the Thread to handle the API Requests */ + Status = RtlCreateUserThread(NtCurrentProcess(), + NULL, + TRUE, + 0, + 0, + 0, + (PVOID)CsrSbApiRequestThread, + NULL, + &hRequestThread, + &ClientId); + if (NT_SUCCESS(Status)) + { + /* Add it as a Static Server Thread */ + CsrSbApiRequestThreadPtr = CsrAddStaticServerThread(hRequestThread, + &ClientId, + 0); + + /* Activate it */ + Status = NtResumeThread(hRequestThread, NULL); + } + } + + return Status; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +/*++ + * @name CsrServerInitialization + * @implemented NT4 + * + * The CsrServerInitialization routine is the native (not Server) entrypoint + * of this Server DLL. It serves as the entrypoint for csrss. + * + * @param ArgumentCount + * Number of arguments on the command line. + * + * @param Arguments + * Array of arguments from the command line. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrServerInitialization(IN ULONG ArgumentCount, + IN PCHAR Arguments[]) +{ + NTSTATUS Status = STATUS_SUCCESS; + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + + /* Create the Init Event */ + Status = NtCreateEvent(&CsrInitializationEvent, + EVENT_ALL_ACCESS, + NULL, + SynchronizationEvent, + FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtCreateEvent failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Cache System Basic Information so we don't always request it */ + Status = NtQuerySystemInformation(SystemBasicInformation, + &CsrNtSysInfo, + sizeof(SYSTEM_BASIC_INFORMATION), + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtQuerySystemInformation failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Save our Heap */ + CsrHeap = RtlGetProcessHeap(); + + /* Set our Security Descriptor to protect the process */ + Status = CsrSetProcessSecurity(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrSetProcessSecurity failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Set up Session Support */ + Status = CsrInitializeNtSessionList(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrInitializeSessions failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Set up Process Support */ + Status = CsrInitializeProcessStructure(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrInitializeProcessStructure failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Parse the command line */ + Status = CsrParseServerCommandLine(ArgumentCount, Arguments); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrParseServerCommandLine failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + Status = CsrApiRegisterDefinitions(NativeDefinitions); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV failed in %s with status %lx\n", "CsrApiRegisterDefinitions", Status); + } + + /* Now initialize our API Port */ + Status = CsrApiPortInitialize(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrApiPortInitialize failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Initialize Win32csr */ + Status = CsrLoadServerDll("win32csr", "Win32CsrInitialization", 2); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV failed in %s with status %lx\n", "CsrLoadServerDll", Status); + } + + /* Initialize the API Port for SM communication */ + Status = CsrSbApiPortInitialize(); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: CsrSbApiPortInitialize failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* We're all set! Connect to SM! */ + Status = SmConnectToSm(&CsrSbApiPortName, + CsrSbApiPort, + IMAGE_SUBSYSTEM_WINDOWS_GUI, + &CsrSmApiPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: SmConnectToSm failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Finito! Signal the event */ + Status = NtSetEvent(CsrInitializationEvent, NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtSetEvent failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Close the event handle now */ + NtClose(CsrInitializationEvent); + + /* Have us handle Hard Errors */ + Status = NtSetDefaultHardErrorPort(CsrApiPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSRV:%s: NtSetDefaultHardErrorPort failed (Status=%08lx)\n", + __FUNCTION__, Status); + return Status; + } + + /* Return status */ + return Status; +} + +/*++ + * @name CsrPopulateDosDevices + * @unimplemented NT5.1 + * + * The CsrPopulateDosDevices routine uses the DOS Device Map from the Kernel + * to populate the Dos Devices Object Directory for the session. + * + * @param None. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrPopulateDosDevices(VOID) +{ + DPRINT1("Deprecated API\n"); + return; +} + +BOOL +NTAPI +DllMain(IN HANDLE hDll, + IN DWORD dwReason, + IN LPVOID lpReserved) +{ + /* We don't do much */ + UNREFERENCED_PARAMETER(hDll); + UNREFERENCED_PARAMETER(dwReason); + UNREFERENCED_PARAMETER(lpReserved); + return TRUE; +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/procsup.c b/reactos/deprecated/csrsrv/procsup.c new file mode 100644 index 00000000000..228597050c2 --- /dev/null +++ b/reactos/deprecated/csrsrv/procsup.c @@ -0,0 +1,1240 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsystems/win32/csrss/csrsrv/procsup.c + * PURPOSE: CSR Process Management + * PROGRAMMERS: ReactOS Portable Systems Group + * Alex Ionescu + */ + +/* INCLUDES *******************************************************************/ + +#include + +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +RTL_CRITICAL_SECTION ProcessDataLock; +PCSR_PROCESS CsrRootProcess = NULL; +SECURITY_QUALITY_OF_SERVICE CsrSecurityQos = +{ + sizeof(SECURITY_QUALITY_OF_SERVICE), + SecurityImpersonation, + SECURITY_STATIC_TRACKING, + FALSE +}; +ULONG CsrProcessSequenceCount = 5; +extern ULONG CsrTotalPerProcessDataLength; + +/* FUNCTIONS ******************************************************************/ + +VOID +NTAPI +CsrSetToNormalPriority(VOID) +{ + KPRIORITY BasePriority = (8 + 1) + 4; + + /* Set the Priority */ + NtSetInformationProcess(NtCurrentProcess(), + ProcessBasePriority, + &BasePriority, + sizeof(KPRIORITY)); +} + +VOID +NTAPI +CsrSetToShutdownPriority(VOID) +{ + KPRIORITY SetBasePriority = (8 + 1) + 6; + BOOLEAN Old; + + /* Get the shutdown privilege */ + if (NT_SUCCESS(RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, + TRUE, + FALSE, + &Old))) + { + /* Set the Priority */ + NtSetInformationProcess(NtCurrentProcess(), + ProcessBasePriority, + &SetBasePriority, + sizeof(KPRIORITY)); + } +} + +/*++ + * @name CsrGetProcessLuid + * @implemented NT4 + * + * Do nothing for 500ms. + * + * @param hProcess + * Optional handle to the process whose LUID should be returned. + * + * @param Luid + * Pointer to a LUID Pointer which will receive the CSR Process' LUID + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks If hProcess is not supplied, then the current thread's token will + * be used. If that too is missing, then the current process' token + * will be used. + * + *--*/ +NTSTATUS +NTAPI +CsrGetProcessLuid(HANDLE hProcess OPTIONAL, + PLUID Luid) +{ + HANDLE hToken = NULL; + NTSTATUS Status; + ULONG Length; + PTOKEN_STATISTICS TokenStats; + + /* Check if we have a handle to a CSR Process */ + if (!hProcess) + { + /* We don't, so try opening the Thread's Token */ + Status = NtOpenThreadToken(NtCurrentThread(), + TOKEN_QUERY, + FALSE, + &hToken); + + /* Check for success */ + if (!NT_SUCCESS(Status)) + { + /* If we got some other failure, then return and quit */ + if (Status != STATUS_NO_TOKEN) return Status; + + /* We don't have a Thread Token, use a Process Token */ + hProcess = NtCurrentProcess(); + hToken = NULL; + } + } + + /* Check if we have a token by now */ + if (!hToken) + { + /* No token yet, so open the Process Token */ + Status = NtOpenProcessToken(hProcess, + TOKEN_QUERY, + &hToken); + if (!NT_SUCCESS(Status)) + { + /* Still no token, return the error */ + return Status; + } + } + + /* Now get the size we'll need for the Token Information */ + Status = NtQueryInformationToken(hToken, + TokenStatistics, + NULL, + 0, + &Length); + + /* Allocate memory for the Token Info */ + if (!(TokenStats = RtlAllocateHeap(CsrHeap, 0, Length))) + { + /* Fail and close the token */ + NtClose(hToken); + return STATUS_NO_MEMORY; + } + + /* Now query the information */ + Status = NtQueryInformationToken(hToken, + TokenStatistics, + TokenStats, + Length, + &Length); + + /* Close the handle */ + NtClose(hToken); + + /* Check for success */ + if (NT_SUCCESS(Status)) + { + /* Return the LUID */ + *Luid = TokenStats->AuthenticationId; + } + + /* Free the query information */ + RtlFreeHeap(CsrHeap, 0, TokenStats); + + /* Return the Status */ + return Status; +} + +/*++ + * @name CsrImpersonateClient + * @implemented NT4 + * + * The CsrImpersonateClient will impersonate the given CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread to impersonate. + * + * @return TRUE if impersionation suceeded, false otherwise. + * + * @remarks Impersonation can be recursive. + * + *--*/ +BOOLEAN +NTAPI +CsrImpersonateClient(IN PCSR_THREAD CsrThread) +{ + NTSTATUS Status; + PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread; + + /* Use the current thread if none given */ + if (!CsrThread) CsrThread = CurrentThread; + + /* Still no thread, something is wrong */ + if (!CsrThread) + { + /* Failure */ + return FALSE; + } + + /* Make the call */ + Status = NtImpersonateThread(NtCurrentThread(), + CsrThread->ThreadHandle, + &CsrSecurityQos); + + if (!NT_SUCCESS(Status)) + { + /* Failure */ +/* + DPRINT1("CSRSS: Can't impersonate client thread - Status = %lx\n", Status); + if (Status != STATUS_BAD_IMPERSONATION_LEVEL) DbgBreakPoint(); +*/ + return FALSE; + } + + /* Increase the impersonation count for the current thread */ + if (CurrentThread) ++CurrentThread->ImpersonationCount; + + /* Return Success */ + return TRUE; +} + +/*++ + * @name CsrRevertToSelf + * @implemented NT4 + * + * The CsrRevertToSelf routine will attempt to remove an active impersonation. + * + * @param None. + * + * @return TRUE if the reversion was succesful, false otherwise. + * + * @remarks Impersonation can be recursive; as such, the impersonation token + * will only be deleted once the CSR Thread's impersonaton count + * has reached zero. + * + *--*/ +BOOLEAN +NTAPI +CsrRevertToSelf(VOID) +{ + NTSTATUS Status; + PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread; + HANDLE ImpersonationToken = NULL; + + /* Check if we have a Current Thread */ + if (CurrentThread) + { + /* Make sure impersonation is on */ + if (!CurrentThread->ImpersonationCount) + { + // DPRINT1("CSRSS: CsrRevertToSelf called while not impersonating\n"); + return FALSE; + } + else if (--CurrentThread->ImpersonationCount > 0) + { + /* Success; impersonation count decreased but still not zero */ + return TRUE; + } + } + + /* Impersonation has been totally removed, revert to ourselves */ + Status = NtSetInformationThread(NtCurrentThread(), + ThreadImpersonationToken, + &ImpersonationToken, + sizeof(HANDLE)); + + /* Return TRUE or FALSE */ + return NT_SUCCESS(Status); +} + +/*++ + * @name FindProcessForShutdown + * + * The FindProcessForShutdown routine returns a CSR Process which is ready + * to be shutdown, and sets the appropriate shutdown flags for it. + * + * @param CallerLuid + * Pointer to the LUID of the CSR Process calling this routine. + * + * @return Pointer to a CSR Process which is ready to be shutdown. + * + * @remarks None. + * + *--*/ +PCSR_PROCESS +NTAPI +FindProcessForShutdown(IN PLUID CallerLuid) +{ + PCSR_PROCESS CsrProcess, ReturnCsrProcess = NULL; + // PCSR_THREAD CsrThread; + NTSTATUS Status; + ULONG Level = 0; + LUID ProcessLuid; + LUID SystemLuid = SYSTEM_LUID; + // BOOLEAN IsSystemLuid = FALSE, IsOurLuid = FALSE; + PLIST_ENTRY NextEntry; + + /* Set the List Pointers */ + NextEntry = CsrRootProcess->ListLink.Flink; + while (NextEntry != &CsrRootProcess->ListLink) + { + /* Get the process */ + CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink); + + /* Move to the next entry */ + NextEntry = NextEntry->Flink; + + /* Skip this process if it's already been processed */ + if (CsrProcess->Flags & CsrProcessSkipShutdown) continue; + + /* Get the LUID of this Process */ + Status = CsrGetProcessLuid(CsrProcess->ProcessHandle, &ProcessLuid); + + /* Check if we didn't get access to the LUID */ + if (Status == STATUS_ACCESS_DENIED) + { + /* FIXME:Check if we have any threads */ +/* + if (CsrProcess->ThreadCount) + { + /\* Impersonate one of the threads and retry *\/ + CsrThread = CONTAINING_RECORD(CsrProcess->ThreadList.Flink, + CSR_THREAD, + Link); + CsrImpersonateClient(CsrThread); + Status = CsrGetProcessLuid(NULL, &ProcessLuid); + CsrRevertToSelf(); + } +*/ + } + + if (!NT_SUCCESS(Status)) + { + /* We didn't have access, so skip it */ + CsrProcess->Flags |= CsrProcessSkipShutdown; + continue; + } + + /* Check if this is the System LUID */ + if ((/*IsSystemLuid =*/ RtlEqualLuid(&ProcessLuid, &SystemLuid))) + { + /* Mark this process */ + CsrProcess->ShutdownFlags |= CsrShutdownSystem; + } + else if (!(/*IsOurLuid =*/ RtlEqualLuid(&ProcessLuid, CallerLuid))) + { + /* Our LUID doesn't match with the caller's */ + CsrProcess->ShutdownFlags |= CsrShutdownOther; + } + + /* Check if we're past the previous level */ + if (CsrProcess->ShutdownLevel > Level /* || !ReturnCsrProcess */) + { + /* Update the level */ + Level = CsrProcess->ShutdownLevel; + + /* Set the final process */ + ReturnCsrProcess = CsrProcess; + } + } + + /* Check if we found a process */ + if (ReturnCsrProcess) + { + /* Skip this one next time */ + ReturnCsrProcess->Flags |= CsrProcessSkipShutdown; + } + + return ReturnCsrProcess; +} + +/* This is really "CsrShutdownProcess", mostly */ +NTSTATUS +WINAPI +CsrEnumProcesses(IN CSRSS_ENUM_PROCESS_PROC EnumProc, + IN PVOID Context) +{ + PVOID* RealContext = (PVOID*)Context; + PLUID CallerLuid = RealContext[0]; + PCSR_PROCESS CsrProcess = NULL; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + BOOLEAN FirstTry; + PLIST_ENTRY NextEntry; + ULONG Result = 0; + + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Get the list pointers */ + NextEntry = CsrRootProcess->ListLink.Flink; + while (NextEntry != &CsrRootProcess->ListLink) + { + /* Get the Process */ + CsrProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink); + + /* Remove the skip flag, set shutdown flags to 0*/ + CsrProcess->Flags &= ~CsrProcessSkipShutdown; + CsrProcess->ShutdownFlags = 0; + + /* Move to the next */ + NextEntry = NextEntry->Flink; + } + + /* Set shudown Priority */ + CsrSetToShutdownPriority(); + + /* Loop all processes */ + //DPRINT1("Enumerating for LUID: %lx %lx\n", CallerLuid->HighPart, CallerLuid->LowPart); + + /* Start looping */ + while (TRUE) + { + /* Find the next process to shutdown */ + FirstTry = TRUE; + if (!(CsrProcess = FindProcessForShutdown(CallerLuid))) + { + /* Done, quit */ + CsrReleaseProcessLock(); + Status = STATUS_SUCCESS; + goto Quickie; + } + +LoopAgain: + /* Release the lock, make the callback, and acquire it back */ + //DPRINT1("Found process: %lx\n", CsrProcess->ClientId.UniqueProcess); + CsrReleaseProcessLock(); + Result = (ULONG)EnumProc(CsrProcess, (PVOID)((ULONG_PTR)Context | FirstTry)); + CsrAcquireProcessLock(); + + /* Check the result */ + //DPRINT1("Result: %d\n", Result); + if (Result == CsrShutdownCsrProcess) + { + /* The callback unlocked the process */ + break; + } + else if (Result == CsrShutdownNonCsrProcess) + { + /* A non-CSR process, the callback didn't touch it */ + //continue; + } + else if (Result == CsrShutdownCancelled) + { + /* Shutdown was cancelled, unlock and exit */ + CsrReleaseProcessLock(); + Status = STATUS_CANCELLED; + goto Quickie; + } + + /* No matches during the first try, so loop again */ + if (FirstTry && Result == CsrShutdownNonCsrProcess) + { + FirstTry = FALSE; + goto LoopAgain; + } + } + +Quickie: + /* Return to normal priority */ + CsrSetToNormalPriority(); + return Status; +} + +/*++ + * @name CsrProcessRefcountZero + * + * The CsrProcessRefcountZero routine is executed when a CSR Process has lost + * all its active references. It removes and de-allocates the CSR Process. + * + * @param CsrProcess + * Pointer to the CSR Process that is to be deleted. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Process has lost all + * its references. + * + * This routine is called with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrProcessRefcountZero(IN PCSR_PROCESS CsrProcess) +{ + ASSERT(ProcessStructureListLocked()); + + /* Remove the Process from the list */ + CsrRemoveProcess(CsrProcess); + + /* Check if there's a session */ + if (CsrProcess->NtSession) + { + /* Dereference the Session */ + CsrDereferenceNtSession(CsrProcess->NtSession, 0); + } + + /* Close the Client Port if there is one */ + if (CsrProcess->ClientPort) NtClose(CsrProcess->ClientPort); + + /* Close the process handle */ + NtClose(CsrProcess->ProcessHandle); + + /* Free the Proces Object */ + CsrDeallocateProcess(CsrProcess); +} + +/*++ + * @name CsrLockedDereferenceProcess + * + * The CsrLockedDereferenceProcess dereferences a CSR Process while the + * Process Lock is already being held. + * + * @param CsrProcess + * Pointer to the CSR Process to be dereferenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedDereferenceProcess(PCSR_PROCESS CsrProcess) +{ + LONG LockCount; + + /* Decrease reference count */ + LockCount = --CsrProcess->ReferenceCount; + ASSERT(LockCount >= 0); + if (!LockCount) + { + /* Call the generic cleanup code */ + DPRINT1("Should kill process: %p\n", CsrProcess); + CsrProcessRefcountZero(CsrProcess); + CsrAcquireProcessLock(); + } +} + +/*++ + * @name CsrDereferenceProcess + * @implemented NT4 + * + * The CsrDereferenceProcess routine removes a reference from a CSR Process. + * + * @param CsrThread + * Pointer to the CSR Process to dereference. + * + * @return None. + * + * @remarks If the reference count has reached zero (ie: the CSR Process has + * no more active references), it will be deleted. + * + *--*/ +VOID +NTAPI +CsrDereferenceProcess(IN PCSR_PROCESS CsrProcess) +{ + LONG LockCount; + + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Decrease reference count */ + LockCount = --CsrProcess->ReferenceCount; + ASSERT(LockCount >= 0); + if (!LockCount) + { + /* Call the generic cleanup code */ + CsrProcessRefcountZero(CsrProcess); + } + else + { + /* Just release the lock */ + CsrReleaseProcessLock(); + } +} + +/*++ + * @name CsrDestroyProcess + * @implemented NT4 + * + * The CsrDestroyProcess routine destroys the CSR Process corresponding to + * a given Client ID. + * + * @param Cid + * Pointer to the Client ID Structure corresponding to the CSR + * Process which is about to be destroyed. + * + * @param ExitStatus + * Unused. + * + * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING + * if the CSR Process is already terminating. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrDestroyProcess(IN PCLIENT_ID Cid, + IN NTSTATUS ExitStatus) +{ + PCSR_THREAD CsrThread; + PCSR_PROCESS CsrProcess; + CLIENT_ID ClientId = *Cid; + PLIST_ENTRY NextEntry; + + /* Acquire lock */ + CsrAcquireProcessLock(); + + /* Find the thread */ + CsrThread = CsrLocateThreadByClientId(&CsrProcess, &ClientId); + + /* Make sure we got one back, and that it's not already gone */ + if (!(CsrThread) || (CsrProcess->Flags & CsrProcessTerminating)) + { + /* Release the lock and return failure */ + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Set the terminated flag */ + CsrProcess->Flags |= CsrProcessTerminating; + + /* Get the List Pointers */ + NextEntry = CsrProcess->ThreadList.Flink; + while (NextEntry != &CsrProcess->ThreadList) + { + /* Get the current thread entry */ + CsrThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link); + + /* Make sure the thread isn't already dead */ + if (CsrThread->Flags & CsrThreadTerminated) + { + NextEntry = NextEntry->Flink; + continue; + } + + /* Set the Terminated flag */ + CsrThread->Flags |= CsrThreadTerminated; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Do we have an active wait block? */ + if (CsrThread->WaitBlock) + { + /* Notify waiters of termination */ + CsrNotifyWaitBlock(CsrThread->WaitBlock, + NULL, + NULL, + NULL, + CsrProcessTerminating, + TRUE); + } + + /* Release the Wait Lock */ + CsrReleaseWaitLock(); + + /* Dereference the thread */ + CsrLockedDereferenceThread(CsrThread); + NextEntry = CsrProcess->ThreadList.Flink; + } + + /* Release the Process Lock and return success */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrCreateProcess + * @implemented NT4 + * + * Do nothing for 500ms. + * + * @param ArgumentCount + * Description of the parameter. Wrapped to more lines on ~70th + * column. + * + * @param Arguments + * Description of the parameter. Wrapped to more lines on ~70th + * column. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateProcess(IN HANDLE hProcess, + IN HANDLE hThread, + IN PCLIENT_ID ClientId, + IN PCSR_NT_SESSION NtSession, + IN ULONG Flags, + IN PCLIENT_ID DebugCid) +{ + PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread; + CLIENT_ID CurrentCid; + PCSR_PROCESS CurrentProcess; +// PVOID ProcessData; +// ULONG i; + PCSR_PROCESS CsrProcess; + NTSTATUS Status; + PCSR_THREAD CsrThread; + KERNEL_USER_TIMES KernelTimes; + + /* Get the current CID and lock Processes */ + CurrentCid = CurrentThread->ClientId; + CsrAcquireProcessLock(); + + /* Get the current CSR Thread */ + CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, &CurrentCid); + if (!CurrentThread) + { + /* We've failed to locate the thread */ + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Allocate a new Process Object */ + CsrProcess = CsrAllocateProcess(); + if (!CsrProcess) + { + /* Couldn't allocate Process */ + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + +#if 0 + /* Inherit the Process Data */ + CurrentProcess = CurrentThread->Process; + ProcessData = &CurrentProcess->ServerData[CSR_SERVER_DLL_MAX]; + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Check if the DLL is Loaded and has Per Process Data */ + if ((CsrLoadedServerDll[i]) && (CsrLoadedServerDll[i]->SizeOfProcessData)) + { + /* Set the pointer */ + CsrProcess->ServerData[i] = ProcessData; + + /* Copy the Data */ + RtlMoveMemory(ProcessData, + CurrentProcess->ServerData[i], + CsrLoadedServerDll[i]->SizeOfProcessData); + + /* Update next data pointer */ + ProcessData = (PVOID)((ULONG_PTR)ProcessData + + CsrLoadedServerDll[i]->SizeOfProcessData); + } + else + { + /* No data for this Server */ + CsrProcess->ServerData[i] = NULL; + } + } +#endif + + /* Set the Exception port to us */ + Status = NtSetInformationProcess(hProcess, + ProcessExceptionPort, + &CsrApiPort, + sizeof(HANDLE)); + if (!NT_SUCCESS(Status)) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* If Check if CreateProcess got CREATE_NEW_PROCESS_GROUP */ + if (!(Flags & CsrProcessCreateNewGroup)) + { + /* Create new data */ + CsrProcess->ProcessGroupId = HandleToUlong(ClientId->UniqueProcess); + CsrProcess->ProcessGroupSequence = CsrProcess->SequenceNumber; + } + else + { + /* Copy it from the current process */ + CsrProcess->ProcessGroupId = CurrentProcess->ProcessGroupId; + CsrProcess->ProcessGroupSequence = CurrentProcess->ProcessGroupSequence; + } + + /* Check if this is a console process */ + if (Flags & CsrProcessIsConsoleApp) CsrProcess->Flags |= CsrProcessIsConsoleApp; + + /* Mask out non-debug flags */ + Flags &= ~(CsrProcessIsConsoleApp | CsrProcessCreateNewGroup | CsrProcessPriorityFlags); + + /* Check if every process will be debugged */ + if (!(Flags) && (CurrentProcess->DebugFlags & CsrDebugProcessChildren)) + { + /* Pass it on to the current process */ + CsrProcess->DebugFlags = CsrDebugProcessChildren; + CsrProcess->DebugCid = CurrentProcess->DebugCid; + } + + /* Check if Debugging was used on this process */ + if ((Flags & (CsrDebugOnlyThisProcess | CsrDebugProcessChildren)) && (DebugCid)) + { + /* Save the debug flag used */ + CsrProcess->DebugFlags = Flags; + + /* Save the CID */ + CsrProcess->DebugCid = *DebugCid; + } + + /* Check if we debugging is enabled */ + if (CsrProcess->DebugFlags) + { + /* Set the Debug Port to us */ + Status = NtSetInformationProcess(hProcess, + ProcessDebugPort, + &CsrApiPort, + sizeof(HANDLE)); + ASSERT(NT_SUCCESS(Status)); + if (!NT_SUCCESS(Status)) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + } + + /* Get the Thread Create Time */ + Status = NtQueryInformationThread(hThread, + ThreadTimes, + (PVOID)&KernelTimes, + sizeof(KernelTimes), + NULL); + if (!NT_SUCCESS(Status)) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Allocate a CSR Thread Structure */ + CsrThread = CsrAllocateThread(CsrProcess); + if (!CsrThread) + { + /* Failed */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Save the data we have */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = *ClientId; + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->Flags = 0; + + /* Insert the Thread into the Process */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Reference the session */ + CsrReferenceNtSession(NtSession); + CsrProcess->NtSession = NtSession; + + /* Setup Process Data */ + CsrProcess->ClientId = *ClientId; + CsrProcess->ProcessHandle = hProcess; + CsrProcess->ShutdownLevel = 0x280; + + /* Set the Priority to Background */ + CsrSetBackgroundPriority(CsrProcess); + + /* Insert the Process */ + CsrInsertProcess(NULL, CurrentProcess, CsrProcess); + + /* Release lock and return */ + CsrReleaseProcessLock(); + return Status; +} + +/*++ + * @name CsrUnlockProcess + * @implemented NT4 + * + * The CsrUnlockProcess undoes a previous CsrLockProcessByClientId operation. + * + * @param CsrProcess + * Pointer to a previously locked CSR Process. + * + * @return STATUS_SUCCESS. + * + * @remarks This routine must be called with the Process Lock held. + * + *--*/ +NTSTATUS +NTAPI +CsrUnlockProcess(IN PCSR_PROCESS CsrProcess) +{ + /* Dereference the process */ + CsrLockedDereferenceProcess(CsrProcess); + + /* Release the lock and return */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSetBackgroundPriority + * @implemented NT4 + * + * The CsrSetBackgroundPriority routine sets the priority for the given CSR + * Process as a Background priority. + * + * @param CsrProcess + * Pointer to the CSR Process whose priority will be modified. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrSetBackgroundPriority(IN PCSR_PROCESS CsrProcess) +{ + PROCESS_PRIORITY_CLASS PriorityClass; + + /* Set the Foreground bit off */ + PriorityClass.Foreground = FALSE; + + /* Set the new Priority */ + NtSetInformationProcess(CsrProcess->ProcessHandle, + ProcessPriorityClass, + &PriorityClass, + sizeof(PriorityClass)); +} + +/*++ + * @name CsrAllocateProcess + * @implemented NT4 + * + * The CsrAllocateProcess routine allocates a new CSR Process object. + * + * @return Pointer to the newly allocated CSR Process. + * + * @remarks None. + * + *--*/ +PCSR_PROCESS +NTAPI +CsrAllocateProcess(VOID) +{ + PCSR_PROCESS CsrProcess; + ULONG TotalSize; + + /* Calculate the amount of memory this should take */ + TotalSize = sizeof(CSR_PROCESS) + + (CSR_SERVER_DLL_MAX * sizeof(PVOID)) + + CsrTotalPerProcessDataLength; + + /* Allocate a Process */ + CsrProcess = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, TotalSize); + if (!CsrProcess) return NULL; + + /* Handle the Sequence Number and protect against overflow */ + CsrProcess->SequenceNumber = CsrProcessSequenceCount++; + if (CsrProcessSequenceCount < 5) CsrProcessSequenceCount = 5; + + /* Increase the reference count */ + CsrProcess->ReferenceCount++; + + /* Initialize the Thread List */ + InitializeListHead(&CsrProcess->ThreadList); + + /* Return the Process */ + return CsrProcess; +} + +/*++ + * @name CsrLockedReferenceProcess + * + * The CsrLockedReferenceProcess references a CSR Process while the + * Process Lock is already being held. + * + * @param CsrProcess + * Pointer to the CSR Process to be referenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedReferenceProcess(IN PCSR_PROCESS CsrProcess) +{ + /* Increment the reference count */ + ++CsrProcess->ReferenceCount; +} + +/*++ + * @name CsrInitializeProcessStructure + * @implemented NT4 + * + * The CsrInitializeProcessStructure routine sets up support for CSR Processes + * and CSR Threads. + * + * @param None. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrInitializeProcessStructure(VOID) +{ + NTSTATUS Status; + ULONG i; + + /* Initialize the Lock */ + Status = RtlInitializeCriticalSection(&ProcessDataLock); + if (!NT_SUCCESS(Status)) return Status; + + /* Set up the Root Process */ + CsrRootProcess = CsrAllocateProcess(); + if (!CsrRootProcess) return STATUS_NO_MEMORY; + + /* Set up the minimal information for it */ + InitializeListHead(&CsrRootProcess->ListLink); + CsrRootProcess->ProcessHandle = (HANDLE)-1; + CsrRootProcess->ClientId = NtCurrentTeb()->ClientId; + + /* Initialize the Thread Hash List */ + for (i = 0; i < 256; i++) InitializeListHead(&CsrThreadHashTable[i]); + + /* Initialize the Wait Lock */ + return RtlInitializeCriticalSection(&CsrWaitListsLock); +} + +/*++ + * @name CsrDeallocateProcess + * + * The CsrDeallocateProcess frees the memory associated with a CSR Process. + * + * @param CsrProcess + * Pointer to the CSR Process to be freed. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Process has been cleanly + * dereferenced and killed. + * + *--*/ +VOID +NTAPI +CsrDeallocateProcess(IN PCSR_PROCESS CsrProcess) +{ + /* Free the process object from the heap */ + RtlFreeHeap(CsrHeap, 0, CsrProcess); +} + +/*++ + * @name CsrRemoveProcess + * + * The CsrRemoveProcess function undoes a CsrInsertProcess operation and + * removes the CSR Process from the Process List and notifies Server DLLs + * of this removal. + * + * @param CsrProcess + * Pointer to the CSR Process to remove. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrRemoveProcess(IN PCSR_PROCESS CsrProcess) +{ + PCSR_SERVER_DLL ServerDll; + ULONG i; + ASSERT(ProcessStructureListLocked()); + + /* Remove us from the Process List */ + RemoveEntryList(&CsrProcess->ListLink); + + /* Release the lock */ + CsrReleaseProcessLock(); + + /* Loop every Server DLL */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Check if it's valid and if it has a Disconnect Callback */ + if ((ServerDll) && (ServerDll->DisconnectCallback)) + { + /* Call it */ + ServerDll->DisconnectCallback(CsrProcess); + } + } +} + +/*++ + * @name CsrInsertProcess + * + * The CsrInsertProcess routine inserts a CSR Process into the Process List + * and notifies Server DLLs of the creation of a new CSR Process. + * + * @param Parent + * Optional pointer to the CSR Process creating this CSR Process. + * + * @param CurrentProcess + * Optional pointer to the current CSR Process. + * + * @param CsrProcess + * Pointer to the CSR Process which is to be inserted. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrInsertProcess(IN PCSR_PROCESS Parent OPTIONAL, + IN PCSR_PROCESS CurrentProcess OPTIONAL, + IN PCSR_PROCESS CsrProcess) +{ +#if 0 + PCSR_SERVER_DLL ServerDll; + ULONG i; +#endif + ASSERT(ProcessStructureListLocked()); + + /* Set the parent */ + CsrProcess->Parent = Parent; + + /* Insert it into the Root List */ + InsertTailList(&CsrRootProcess->ListLink, &CsrProcess->ListLink); +#if 0 + /* Notify the Server DLLs */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Get the current Server DLL */ + ServerDll = CsrLoadedServerDll[i]; + + /* Make sure it's valid and that it has callback */ + if ((ServerDll) && (ServerDll->NewProcessCallback)) + { + ServerDll->NewProcessCallback(CurrentProcess, CsrProcess); + } + } +#endif +} + +/*++ + * @name CsrLockProcessByClientId + * @implemented NT4 + * + * The CsrLockProcessByClientId routine locks the CSR Process corresponding + * to the given Process ID and optionally returns it. + * + * @param Pid + * Process ID corresponding to the CSR Process which will be locked. + * + * @param CsrProcess + * Optional pointer to a CSR Process pointer which will hold the + * CSR Process corresponding to the given Process ID. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks Locking a CSR Process is defined as acquiring an extra + * reference to it and returning with the Process Lock held. + * + *--*/ +NTSTATUS +NTAPI +CsrLockProcessByClientId(IN HANDLE Pid, + OUT PCSR_PROCESS *CsrProcess) +{ + PLIST_ENTRY NextEntry; + PCSR_PROCESS CurrentProcess = NULL; + NTSTATUS Status; + + /* Acquire the lock */ + CsrAcquireProcessLock(); + + /* Assume failure */ + ASSERT(CsrProcess != NULL); + *CsrProcess = NULL; + + /* Setup the List Pointers */ + NextEntry = &CsrRootProcess->ListLink; + do + { + /* Get the Process */ + CurrentProcess = CONTAINING_RECORD(NextEntry, CSR_PROCESS, ListLink); + + /* Check for PID Match */ + if (CurrentProcess->ClientId.UniqueProcess == Pid) + { + Status = STATUS_SUCCESS; + break; + } + + /* Next entry */ + NextEntry = NextEntry->Flink; + } while (NextEntry != &CsrRootProcess->ListLink); + + /* Check if we didn't find it in the list */ + if (!NT_SUCCESS(Status)) + { + /* Nothing found, release the lock */ + CsrReleaseProcessLock(); + } + else + { + /* Lock the found process and return it */ + CsrLockedReferenceProcess(CurrentProcess); + *CsrProcess = CurrentProcess; + } + + /* Return the result */ + return Status; +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/server.c b/reactos/deprecated/csrsrv/server.c new file mode 100644 index 00000000000..1221edc127a --- /dev/null +++ b/reactos/deprecated/csrsrv/server.c @@ -0,0 +1,698 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsystems/win32/csrss/csrsrv/server.c + * PURPOSE: CSR Server DLL Server Functions + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +PCSR_API_ROUTINE CsrServerApiDispatchTable[5] = +{ + CsrSrvClientConnect, + CsrSrvUnusedFunction, + CsrSrvUnusedFunction, + CsrSrvIdentifyAlertableThread, + CsrSrvSetPriorityClass +}; + +BOOLEAN CsrServerApiServerValidTable[5] = +{ + TRUE, + FALSE, + TRUE, + TRUE, + TRUE +}; + +PCHAR CsrServerApiNameTable[5] = +{ + "ClientConnect", + "ThreadConnect", + "ProfileControl", + "IdentifyAlertableThread", + "SetPriorityClass" +}; + +PCSR_SERVER_DLL CsrLoadedServerDll[CSR_SERVER_DLL_MAX]; +PVOID CsrSrvSharedSectionHeap; +PVOID CsrSrvSharedSectionBase; +PVOID *CsrSrvSharedStaticServerData; +ULONG CsrSrvSharedSectionSize; +HANDLE CsrSrvSharedSection; + +/* PRIVATE FUNCTIONS**********************************************************/ + +/*++ + * @name CsrServerDllInitialization + * @implemented NT4 + * + * The CsrServerDllInitialization is the initialization routine for + * the this Server DLL. + * + * @param LoadedServerDll + * Pointer to the CSR Server DLL structure representing this Server DLL. + * + * @return STATUS_SUCCESS. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrServerDllInitialization(IN PCSR_SERVER_DLL LoadedServerDll) +{ + /* Setup the DLL Object */ + LoadedServerDll->ApiBase = 0; + LoadedServerDll->HighestApiSupported = CsrpMaxApiNumber; + LoadedServerDll->DispatchTable = CsrServerApiDispatchTable; + LoadedServerDll->ValidTable = CsrServerApiServerValidTable; + LoadedServerDll->NameTable = CsrServerApiNameTable; + LoadedServerDll->SizeOfProcessData = 0; + LoadedServerDll->ConnectCallback = NULL; + LoadedServerDll->DisconnectCallback = NULL; + + /* All done */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrLoadServerDll + * @implemented NT4 + * + * The CsrLoadServerDll routine loads a CSR Server DLL and calls its entrypoint + * + * @param DllString + * Pointer to the CSR Server DLL to load and call. + * + * @param EntryPoint + * Pointer to the name of the server's initialization function. If + * this parameter is NULL, the default ServerDllInitialize will be + * assumed. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrLoadServerDll(IN PCHAR DllString, + IN PCHAR EntryPoint OPTIONAL, + IN ULONG ServerId) +{ + NTSTATUS Status; + ANSI_STRING DllName; + UNICODE_STRING TempString, ErrorString; + ULONG_PTR Parameters[2]; + HANDLE hServerDll = NULL; + ULONG Size; + PCSR_SERVER_DLL ServerDll; + STRING EntryPointString; + PCSR_SERVER_DLL_INIT_CALLBACK ServerDllInitProcedure; + ULONG Response; + + /* Check if it's beyond the maximum we support */ + if (ServerId >= CSR_SERVER_DLL_MAX) return STATUS_TOO_MANY_NAMES; + + /* Check if it's already been loaded */ + if (CsrLoadedServerDll[ServerId]) return STATUS_INVALID_PARAMETER; + + /* Convert the name to Unicode */ + ASSERT(DllString != NULL); + RtlInitAnsiString(&DllName, DllString); + Status = RtlAnsiStringToUnicodeString(&TempString, &DllName, TRUE); + if (!NT_SUCCESS(Status)) return Status; + + /* If we are loading ourselves, don't actually load us */ + if (ServerId != CSR_SRV_SERVER) + { + /* Load the DLL */ + Status = LdrLoadDll(NULL, 0, &TempString, &hServerDll); + if (!NT_SUCCESS(Status)) + { + /* Setup error parameters */ + Parameters[0] = (ULONG_PTR)&TempString; + Parameters[1] = (ULONG_PTR)&ErrorString; + RtlInitUnicodeString(&ErrorString, L"Default Load Path"); + + /* Send a hard error */ + NtRaiseHardError(Status, + 2, + 3, + Parameters, + OptionOk, + &Response); + } + + /* Get rid of the string */ + RtlFreeUnicodeString(&TempString); + if (!NT_SUCCESS(Status)) return Status; + } + + /* Allocate a CSR DLL Object */ + Size = sizeof(CSR_SERVER_DLL) + DllName.MaximumLength; + ServerDll = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, Size); + if (!ServerDll) + { + if (hServerDll) LdrUnloadDll(hServerDll); + return STATUS_NO_MEMORY; + } + + /* Set up the Object */ + ServerDll->Length = Size; + ServerDll->SharedSection = CsrSrvSharedSectionHeap; + ServerDll->Event = CsrInitializationEvent; + ServerDll->Name.Length = DllName.Length; + ServerDll->Name.MaximumLength = DllName.MaximumLength; + ServerDll->Name.Buffer = (PCHAR)(ServerDll + 1); + if (DllName.Length) + { + strncpy(ServerDll->Name.Buffer, DllName.Buffer, DllName.Length); + } + ServerDll->ServerId = ServerId; + ServerDll->ServerHandle = hServerDll; + + /* Now get the entrypoint */ + if (hServerDll) + { + /* Initialize a string for the entrypoint, or use the default */ + RtlInitAnsiString(&EntryPointString, + !(EntryPoint) ? "ServerDllInitialization" : + EntryPoint); + + /* Get a pointer to it */ + Status = LdrGetProcedureAddress(hServerDll, + &EntryPointString, + 0, + (PVOID)&ServerDllInitProcedure); + } + else + { + /* No handle, so we are loading ourselves */ + ServerDllInitProcedure = CsrServerDllInitialization; + Status = STATUS_SUCCESS; + } + + /* Check if we got the pointer, and call it */ + if (NT_SUCCESS(Status)) + { + /* Get the result from the Server DLL */ + Status = ServerDllInitProcedure(ServerDll); + + /* Check for Success */ + if (NT_SUCCESS(Status)) + { + /* + * Add this Server's Per-Process Data Size to the total that each + * process will need. + */ + CsrTotalPerProcessDataLength += ServerDll->SizeOfProcessData; + + /* Save the pointer in our list */ + CsrLoadedServerDll[ServerDll->ServerId] = ServerDll; + + /* Does it use our generic heap? */ + if (ServerDll->SharedSection != CsrSrvSharedSectionHeap) + { + /* No, save the pointer to its shared section in our list */ + CsrSrvSharedStaticServerData[ServerDll->ServerId] = ServerDll->SharedSection; + } + + /* ReactOS Specific hax */ + if (ServerDll->HighestApiSupported == 0xDEADBABE) + { + Status = CsrApiRegisterDefinitions((PVOID)ServerDll->DispatchTable); + } + } + else + { + /* Use shared failure code */ + goto LoadFailed; + } + } + else + { +LoadFailed: + /* Server Init failed, unload it */ + if (hServerDll) LdrUnloadDll(hServerDll); + + /* Delete the Object */ + RtlFreeHeap(CsrHeap, 0, ServerDll); + } + + /* Return to caller */ + return Status; +} + +/*++ + * @name CsrSrvClientConnect + * + * The CsrSrvClientConnect CSR API handles a new connection to a server DLL. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Optional reply to this request. + * + * @return STATUS_SUCCESS in case of success, STATUS_INVALID_PARAMETER + * or STATUS_TOO_MANY_NAMES in case of failure. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvClientConnect(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply OPTIONAL) +{ + /* Hack */ + return STATUS_SUCCESS; +#if 0 + NTSTATUS Status; + PCSR_CLIENT_CONNECT ClientConnect; + PCSR_SERVER_DLL ServerDll; + PCSR_PROCESS CurrentProcess = ((PCSR_THREAD)NtCurrentTeb()->CsrClientThread)->Process; + + /* Load the Message, set default reply */ + ClientConnect = (PCSR_CLIENT_CONNECT)&ApiMessage->CsrClientConnect; + *Reply = 0; + + /* Validate the ServerID */ + if (ClientConnect->ServerId >= CSR_SERVER_DLL_MAX) + { + return STATUS_TOO_MANY_NAMES; + } + else if (!CsrLoadedServerDll[ClientConnect->ServerId]) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validate the Message Buffer */ + if (!(CsrValidateMessageBuffer(ApiMessage, + ClientConnect->ConnectionInfo, + ClientConnect->ConnectionInfoSize, + 1))) + { + /* Fail due to buffer overflow or other invalid buffer */ + return STATUS_INVALID_PARAMETER; + } + + /* Load the Server DLL */ + ServerDll = CsrLoadedServerDll[ClientConnect->ServerId]; + + /* Check if it has a Connect Callback */ + if (ServerDll->ConnectCallback) + { + /* Call the callback */ + Status = ServerDll->ConnectCallback(CurrentProcess, + ClientConnect->ConnectionInfo, + &ClientConnect->ConnectionInfoSize); + } + else + { + /* Assume success */ + Status = STATUS_SUCCESS; + } + + /* Return status */ + return Status; +#endif +} + +/*++ + * @name CsrSrvCreateSharedSection + * + * The CsrSrvCreateSharedSection creates the Shared Section that all CSR Server + * DLLs and Clients can use to share data. + * + * @param ParameterValue + * Specially formatted string from our registry command-line which + * specifies various arguments for the shared section. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvCreateSharedSection(IN PCHAR ParameterValue) +{ + PCHAR SizeValue = ParameterValue; + ULONG Size; + NTSTATUS Status; + LARGE_INTEGER SectionSize; + ULONG ViewSize = 0; + PPEB Peb = NtCurrentPeb(); + + /* If there's no parameter, fail */ + if (!ParameterValue) return STATUS_INVALID_PARAMETER; + + /* Find the first comma, and null terminate */ + while (*SizeValue) + { + if (*SizeValue == ',') + { + *SizeValue++ = ANSI_NULL; + break; + } + else + { + SizeValue++; + } + } + + /* Make sure it's valid */ + if (!*SizeValue) return STATUS_INVALID_PARAMETER; + + /* Convert it to an integer */ + Status = RtlCharToInteger(SizeValue, 0, &Size); + if (!NT_SUCCESS(Status)) return Status; + + /* Multiply by 1024 entries and round to page size */ + CsrSrvSharedSectionSize = ROUND_UP(Size * 1024, CsrNtSysInfo.PageSize); + + /* Create the Secion */ + SectionSize.LowPart = CsrSrvSharedSectionSize; + SectionSize.HighPart = 0; + Status = NtCreateSection(&CsrSrvSharedSection, + SECTION_ALL_ACCESS, + NULL, + &SectionSize, + PAGE_EXECUTE_READWRITE, + SEC_BASED | SEC_RESERVE, + NULL); + if (!NT_SUCCESS(Status)) return Status; + + /* Map the section */ + Status = NtMapViewOfSection(CsrSrvSharedSection, + NtCurrentProcess(), + &CsrSrvSharedSectionBase, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + MEM_TOP_DOWN, + PAGE_EXECUTE_READWRITE); + if (!NT_SUCCESS(Status)) + { + /* Fail */ + NtClose(CsrSrvSharedSection); + return Status; + } + + /* FIXME: Write the value to registry */ + + /* The Heap is the same place as the Base */ + CsrSrvSharedSectionHeap = CsrSrvSharedSectionBase; + + /* Create the heap */ + if (!(RtlCreateHeap(HEAP_ZERO_MEMORY | HEAP_CLASS_7, + CsrSrvSharedSectionHeap, + CsrSrvSharedSectionSize, + PAGE_SIZE, + 0, + 0))) + { + /* Failure, unmap section and return */ + NtUnmapViewOfSection(NtCurrentProcess(), CsrSrvSharedSectionBase); + NtClose(CsrSrvSharedSection); + return STATUS_NO_MEMORY; + } + + /* Now allocate space from the heap for the Shared Data */ + CsrSrvSharedStaticServerData = RtlAllocateHeap(CsrSrvSharedSectionHeap, + 0, + CSR_SERVER_DLL_MAX * + sizeof(PVOID)); + if (!CsrSrvSharedStaticServerData) return STATUS_NO_MEMORY; + + /* Write the values to the PEB */ + Peb->ReadOnlySharedMemoryBase = CsrSrvSharedSectionBase; + Peb->ReadOnlySharedMemoryHeap = CsrSrvSharedSectionHeap; + Peb->ReadOnlyStaticServerData = CsrSrvSharedStaticServerData; + + /* Return */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvAttachSharedSection + * + * The CsrSrvAttachSharedSection maps the CSR Shared Section into a new + * CSR Process' address space, and returns the pointers to the section + * through the Connection Info structure. + * + * @param CsrProcess + * Pointer to the CSR Process that is attempting a connection. + * + * @param ConnectInfo + * Pointer to the CSR Connection Info structure for the incoming + * connection. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvAttachSharedSection(IN PCSR_PROCESS CsrProcess OPTIONAL, + OUT PCSR_CONNECTION_INFO ConnectInfo) +{ + NTSTATUS Status; + ULONG ViewSize = 0; + + /* Check if we have a process */ + if (CsrProcess) + { + /* Map the section into this process */ + DPRINT("CSR Process Handle: %p. CSR Process: %p\n", CsrProcess->ProcessHandle, CsrProcess); + Status = NtMapViewOfSection(CsrSrvSharedSection, + CsrProcess->ProcessHandle, + &CsrSrvSharedSectionBase, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + SEC_NO_CHANGE, + PAGE_EXECUTE_READ); + if (!NT_SUCCESS(Status)) return Status; + } + + /* Write the values in the Connection Info structure */ + ConnectInfo->SharedSectionBase = CsrSrvSharedSectionBase; + ConnectInfo->SharedSectionHeap = CsrSrvSharedSectionHeap; + ConnectInfo->SharedSectionData = CsrSrvSharedStaticServerData; + + /* Return success */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvIdentifyAlertableThread + * @implemented NT4 + * + * The CsrSrvIdentifyAlertableThread CSR API marks a CSR Thread as alertable. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Pointer to an optional reply to this request. + * + * @return STATUS_SUCCESS. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvIdentifyAlertableThread(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply) +{ + PCSR_THREAD CsrThread = NtCurrentTeb()->CsrClientThread; + + /* Set the alertable flag */ + CsrThread->Flags |= CsrThreadAltertable; + + /* Return success */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvSetPriorityClass + * @implemented NT4 + * + * The CsrSrvSetPriorityClass CSR API is deprecated. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Pointer to an optional reply to this request. + * + * @return STATUS_SUCCESS. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvSetPriorityClass(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply) +{ + /* Deprecated */ + return STATUS_SUCCESS; +} + +/*++ + * @name CsrSrvUnusedFunction + * @implemented NT4 + * + * The CsrSrvUnusedFunction CSR API is a stub for deprecated APIs. + * + * The CsrSrvSetPriorityClass CSR API is deprecated. + * + * @param ApiMessage + * Pointer to the CSR API Message for this request. + * + * @param Reply + * Pointer to an optional reply to this request. + * + * @return STATUS_INVALID_PARAMETER. + * + * @remarks CsrSrvSetPriorityClass does not use this stub because it must + * return success. + * + *--*/ +NTSTATUS +NTAPI +CsrSrvUnusedFunction(IN OUT PCSR_API_MESSAGE ApiMessage, + IN OUT PULONG Reply) +{ + /* Deprecated */ + return STATUS_INVALID_PARAMETER; +} + +/* PUBLIC FUNCTIONS***********************************************************/ + +/*++ + * @name CsrSetCallingSpooler + * @implemented NT4 + * + * the CsrSetCallingSpooler routine is deprecated. + * + * @param Reserved + * Deprecated + * + * @return None. + * + * @remarks This routine was used in archaic versions of NT for Printer Drivers. + * + *--*/ +VOID +NTAPI +CsrSetCallingSpooler(ULONG Reserved) +{ + /* Deprecated */ + return; +} + +/*++ + * @name CsrUnhandledExceptionFilter + * @implemented NT5 + * + * The CsrUnhandledExceptionFilter routine handles all exceptions + * within SEH-protected blocks. + * + * @param ExceptionPointers + * System-defined Argument. + * + * @return EXCEPTION_EXECUTE_HANDLER. + * + * @remarks None. + * + *--*/ +LONG +NTAPI +CsrUnhandledExceptionFilter(IN PEXCEPTION_POINTERS ExceptionInfo) +{ + SYSTEM_KERNEL_DEBUGGER_INFORMATION DebuggerInfo; + EXCEPTION_DISPOSITION Result = EXCEPTION_EXECUTE_HANDLER; + BOOLEAN OldValue; + NTSTATUS Status; + UNICODE_STRING ErrorSource; + ULONG_PTR ErrorParameters[4]; + ULONG Response; + + /* Check if a debugger is installed */ + Status = NtQuerySystemInformation(SystemKernelDebuggerInformation, + &DebuggerInfo, + sizeof(DebuggerInfo), + NULL); + + /* Check if this is Session 0, and the Debugger is Enabled */ + if ((NtCurrentPeb()->SessionId) && (NT_SUCCESS(Status)) && + (DebuggerInfo.KernelDebuggerEnabled)) + { + /* Call the Unhandled Exception Filter */ + if ((Result = RtlUnhandledExceptionFilter(ExceptionInfo)) != + EXCEPTION_CONTINUE_EXECUTION) + { + /* We're going to raise an error. Get Shutdown Privilege first */ + Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, + TRUE, + TRUE, + &OldValue); + + /* Use the Process token if that failed */ + if (Status == STATUS_NO_TOKEN) + { + Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, + TRUE, + FALSE, + &OldValue); + } + + /* Initialize our Name String */ + RtlInitUnicodeString(&ErrorSource, L"Windows SubSystem"); + + /* Set the parameters */ + ErrorParameters[0] = (ULONG_PTR)&ErrorSource; + ErrorParameters[1] = ExceptionInfo->ExceptionRecord->ExceptionCode; + ErrorParameters[2] = (ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress; + ErrorParameters[3] = (ULONG_PTR)ExceptionInfo->ContextRecord; + + /* Bugcheck */ + Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, + 4, + 1, + ErrorParameters, + OptionShutdownSystem, + &Response); + } + + /* Just terminate us */ + NtTerminateProcess(NtCurrentProcess(), + ExceptionInfo->ExceptionRecord->ExceptionCode); + } + + return Result; +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/session.c b/reactos/deprecated/csrsrv/session.c new file mode 100644 index 00000000000..361b3c3b9f2 --- /dev/null +++ b/reactos/deprecated/csrsrv/session.c @@ -0,0 +1,568 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsystems/win32/csrss/csrsrv/session.c + * PURPOSE: CSR Server DLL Session Implementation + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +RTL_CRITICAL_SECTION CsrNtSessionLock; +LIST_ENTRY CsrNtSessionList; + +PCHAR CsrServerSbApiName[5] = +{ + "SbCreateSession", + "SbTerminateSession", + "SbForeignSessionComplete", + "SbCreateProcess", + "Unknown Csr Sb Api Number" +}; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name CsrInitializeNtSessionList + * + * The CsrInitializeNtSessionList routine sets up support for CSR Sessions. + * + * @param None + * + * @return None + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrInitializeNtSessionList(VOID) +{ + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + + /* Initialize the Session List */ + InitializeListHead(&CsrNtSessionList); + + /* Initialize the Session Lock */ + return RtlInitializeCriticalSection(&CsrNtSessionLock); +} + +/*++ + * @name CsrAllocateNtSession + * + * The CsrAllocateNtSession routine allocates a new CSR NT Session. + * + * @param SessionId + * Session ID of the CSR NT Session to allocate. + * + * @return Pointer to the newly allocated CSR NT Session. + * + * @remarks None. + * + *--*/ +PCSR_NT_SESSION +NTAPI +CsrAllocateNtSession(IN ULONG SessionId) +{ + PCSR_NT_SESSION NtSession; + + /* Allocate an NT Session Object */ + NtSession = RtlAllocateHeap(CsrHeap, 0, sizeof(CSR_NT_SESSION)); + if (NtSession) + { + /* Setup the Session Object */ + NtSession->SessionId = SessionId; + NtSession->ReferenceCount = 1; + + /* Insert it into the Session List */ + CsrAcquireNtSessionLock(); + InsertHeadList(&CsrNtSessionList, &NtSession->SessionLink); + CsrReleaseNtSessionLock(); + } + else + { + ASSERT(NtSession != NULL); + } + + /* Return the Session (or NULL) */ + return NtSession; +} + +/*++ + * @name CsrReferenceNtSession + * + * The CsrReferenceNtSession increases the reference count of a CSR NT Session. + * + * @param Session + * Pointer to the CSR NT Session to reference. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrReferenceNtSession(IN PCSR_NT_SESSION Session) +{ + /* Acquire the lock */ + CsrAcquireNtSessionLock(); + + /* Sanity checks */ + ASSERT(!IsListEmpty(&Session->SessionLink)); + ASSERT(Session->SessionId != 0); + ASSERT(Session->ReferenceCount != 0); + + /* Increase the reference count */ + Session->ReferenceCount++; + + /* Release the lock */ + CsrReleaseNtSessionLock(); +} + +/*++ + * @name CsrDereferenceNtSession + * + * The CsrDereferenceNtSession decreases the reference count of a + * CSR NT Session. + * + * @param Session + * Pointer to the CSR NT Session to reference. + * + * @param ExitStatus + * If this is the last reference to the session, this argument + * specifies the exit status. + * + * @return None. + * + * @remarks CsrDereferenceNtSession will complete the session if + * the last reference to it has been closed. + * + *--*/ +VOID +NTAPI +CsrDereferenceNtSession(IN PCSR_NT_SESSION Session, + IN NTSTATUS ExitStatus) +{ + /* Acquire the lock */ + CsrAcquireNtSessionLock(); + + /* Sanity checks */ + ASSERT(!IsListEmpty(&Session->SessionLink)); + ASSERT(Session->SessionId != 0); + ASSERT(Session->ReferenceCount != 0); + + /* Dereference the Session Object */ + if (!(--Session->ReferenceCount)) + { + /* Remove it from the list */ + RemoveEntryList(&Session->SessionLink); + + /* Release the lock */ + CsrReleaseNtSessionLock(); + + /* Tell SM that we're done here */ + SmSessionComplete(CsrSmApiPort, Session->SessionId, ExitStatus); + + /* Free the Session Object */ + RtlFreeHeap(CsrHeap, 0, Session); + } + else + { + /* Release the lock, the Session is still active */ + CsrReleaseNtSessionLock(); + } +} + +/* SESSION MANAGER FUNCTIONS**************************************************/ + +/*++ + * @name CsrSbCreateSession + * + * The CsrSbCreateSession API is called by the Session Manager whenever a new + * session is created. + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks The CsrSbCreateSession routine will initialize a new CSR NT + * Session and allocate a new CSR Process for the subsystem process. + * + *--*/ +BOOLEAN +NTAPI +CsrSbCreateSession(IN PSB_API_MSG ApiMessage) +{ + PSB_CREATE_SESSION_MSG CreateSession = &ApiMessage->CreateSession; + HANDLE hProcess, hThread; + PCSR_PROCESS CsrProcess; + NTSTATUS Status; + KERNEL_USER_TIMES KernelTimes; + PCSR_THREAD CsrThread; + //PVOID ProcessData; + //ULONG i; + + /* Save the Process and Thread Handles */ + hProcess = CreateSession->ProcessInfo.ProcessHandle; + hThread = CreateSession->ProcessInfo.ThreadHandle; + + /* Lock the Processes */ + CsrAcquireProcessLock(); + + /* Allocate a new process */ + CsrProcess = CsrAllocateProcess(); + if (!CsrProcess) + { + /* Fail */ + ApiMessage->ReturnValue = STATUS_NO_MEMORY; + CsrReleaseProcessLock(); + return TRUE; + } + + /* Set the exception port */ + Status = NtSetInformationProcess(hProcess, + ProcessExceptionPort, + &CsrApiPort, + sizeof(HANDLE)); + + /* Check for success */ + if (!NT_SUCCESS(Status)) + { + /* Fail the request */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + + /* Strange as it seems, NTSTATUSes are actually returned */ + return (BOOLEAN)STATUS_NO_MEMORY; + } + + /* Get the Create Time */ + Status = NtQueryInformationThread(hThread, + ThreadTimes, + &KernelTimes, + sizeof(KERNEL_USER_TIMES), + NULL); + + /* Check for success */ + if (!NT_SUCCESS(Status)) + { + /* Fail the request */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + + /* Strange as it seems, NTSTATUSes are actually returned */ + return (BOOLEAN)Status; + } + + /* Allocate a new Thread */ + CsrThread = CsrAllocateThread(CsrProcess); + if (!CsrThread) + { + /* Fail the request */ + CsrDeallocateProcess(CsrProcess); + CsrReleaseProcessLock(); + + ApiMessage->ReturnValue = STATUS_NO_MEMORY; + return TRUE; + } + + /* Setup the Thread Object */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = CreateSession->ProcessInfo.ClientId; + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->Flags = 0; + + /* Insert it into the Process List */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Setup Process Data */ + CsrProcess->ClientId = CreateSession->ProcessInfo.ClientId; + CsrProcess->ProcessHandle = hProcess; + CsrProcess->NtSession = CsrAllocateNtSession(CreateSession->SessionId); + + /* Set the Process Priority */ + CsrSetBackgroundPriority(CsrProcess); +#if 0 + /* Get the first data location */ + ProcessData = &CsrProcess->ServerData[CSR_SERVER_DLL_MAX]; + + /* Loop every DLL */ + for (i = 0; i < CSR_SERVER_DLL_MAX; i++) + { + /* Check if the DLL is loaded and has Process Data */ + if (CsrLoadedServerDll[i] && CsrLoadedServerDll[i]->SizeOfProcessData) + { + /* Write the pointer to the data */ + CsrProcess->ServerData[i] = ProcessData; + + /* Move to the next data location */ + ProcessData = (PVOID)((ULONG_PTR)ProcessData + + CsrLoadedServerDll[i]->SizeOfProcessData); + } + else + { + /* Nothing for this Process */ + CsrProcess->ServerData[i] = NULL; + } + } +#else + /* HACKZ: should go in BaseSrv part of CreateCallback done in Insert below */ + RtlInitializeCriticalSection(&CsrProcess->HandleTableLock); +#endif + /* Insert the Process */ + CsrInsertProcess(NULL, NULL, CsrProcess); + + /* Activate the Thread */ + ApiMessage->ReturnValue = NtResumeThread(hThread, NULL); + + /* Release lock and return */ + CsrReleaseProcessLock(); + return TRUE; +} + +/*++ + * @name CsrSbForeignSessionComplete + * + * The CsrSbForeignSessionComplete API is called by the Session Manager + * whenever a foreign session is completed (ie: terminated). + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks The CsrSbForeignSessionComplete API is not yet implemented. + * + *--*/ +BOOLEAN +NTAPI +CsrSbForeignSessionComplete(IN PSB_API_MSG ApiMessage) +{ + /* Deprecated/Unimplemented in NT */ + ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; + return TRUE; +} + +/*++ + * @name CsrSbTerminateSession + * + * The CsrSbTerminateSession API is called by the Session Manager + * whenever a foreign session should be destroyed. + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks The CsrSbTerminateSession API is not yet implemented. + * + *--*/ +BOOLEAN +NTAPI +CsrSbTerminateSession(IN PSB_API_MSG ApiMessage) +{ + ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; + return TRUE; +} + +/*++ + * @name CsrSbCreateProcess + * + * The CsrSbCreateProcess API is called by the Session Manager + * whenever a foreign session is created and a new process should be started. + * + * @param ApiMessage + * Pointer to the Session Manager API Message. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks The CsrSbCreateProcess API is not yet implemented. + * + *--*/ +BOOLEAN +NTAPI +CsrSbCreateProcess(IN PSB_API_MSG ApiMessage) +{ + ApiMessage->ReturnValue = STATUS_NOT_IMPLEMENTED; + return TRUE; +} + +PSB_API_ROUTINE CsrServerSbApiDispatch[5] = +{ + CsrSbCreateSession, + CsrSbTerminateSession, + CsrSbForeignSessionComplete, + CsrSbCreateProcess, + NULL +}; + +/*++ + * @name CsrSbApiHandleConnectionRequest + * + * The CsrSbApiHandleConnectionRequest routine handles and accepts a new + * connection request to the SM API LPC Port. + * + * @param ApiMessage + * Pointer to the incoming CSR API Message which contains the + * connection request. + * + * @return STATUS_SUCCESS in case of success, or status code which caused + * the routine to error. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrSbApiHandleConnectionRequest(IN PSB_API_MSG Message) +{ + NTSTATUS Status; + REMOTE_PORT_VIEW RemotePortView; + HANDLE hPort; + + /* Set the Port View Structure Length */ + RemotePortView.Length = sizeof(REMOTE_PORT_VIEW); + + /* Accept the connection */ + Status = NtAcceptConnectPort(&hPort, + NULL, + (PPORT_MESSAGE)Message, + TRUE, + NULL, + &RemotePortView); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: Sb Accept Connection failed %lx\n", Status); + return Status; + } + + /* Complete the Connection */ + Status = NtCompleteConnectPort(hPort); + if (!NT_SUCCESS(Status)) + { + DPRINT1("CSRSS: Sb Complete Connection failed %lx\n",Status); + } + + /* Return status */ + return Status; +} + +/*++ + * @name CsrSbApiRequestThread + * + * The CsrSbApiRequestThread routine handles incoming messages or connection + * requests on the SM API LPC Port. + * + * @param Parameter + * System-default user-defined parameter. Unused. + * + * @return The thread exit code, if the thread is terminated. + * + * @remarks Before listening on the port, the routine will first attempt + * to connect to the user subsystem. + * + *--*/ +VOID +NTAPI +CsrSbApiRequestThread(IN PVOID Parameter) +{ + NTSTATUS Status; + SB_API_MSG ReceiveMsg; + PSB_API_MSG ReplyMsg = NULL; + PVOID PortContext; + ULONG MessageType; + + /* Start the loop */ + while (TRUE) + { + /* Wait for a message to come in */ + Status = NtReplyWaitReceivePort(CsrSbApiPort, + &PortContext, + &ReplyMsg->h, + &ReceiveMsg.h); + + /* Check if we didn't get success */ + if (Status != STATUS_SUCCESS) + { + /* If we only got a warning, keep going */ + if (NT_SUCCESS(Status)) continue; + + /* We failed big time, so start out fresh */ + ReplyMsg = NULL; + DPRINT1("CSRSS: ReceivePort failed - Status == %X\n", Status); + continue; + } + + /* Save the message type */ + MessageType = ReceiveMsg.h.u2.s2.Type; + + /* Check if this is a connection request */ + if (MessageType == LPC_CONNECTION_REQUEST) + { + /* Handle connection request */ + CsrSbApiHandleConnectionRequest(&ReceiveMsg); + + /* Start over */ + ReplyMsg = NULL; + continue; + } + + /* Check if the port died */ + if (MessageType == LPC_PORT_CLOSED) + { + /* Close the handle if we have one */ + if (PortContext) NtClose((HANDLE)PortContext); + + /* Client died, start over */ + ReplyMsg = NULL; + continue; + } + else if (MessageType == LPC_CLIENT_DIED) + { + /* Client died, start over */ + ReplyMsg = NULL; + continue; + } + + /* + * It's an API Message, check if it's within limits. If it's not, the + * NT Behaviour is to set this to the Maximum API. + */ + if (ReceiveMsg.ApiNumber > SbpMaxApiNumber) + { + ReceiveMsg.ApiNumber = SbpMaxApiNumber; + DPRINT1("CSRSS: %lx is invalid Sb ApiNumber\n", ReceiveMsg.ApiNumber); + } + + /* Reuse the message */ + ReplyMsg = &ReceiveMsg; + + /* Make sure that the message is supported */ + if (ReceiveMsg.ApiNumber < SbpMaxApiNumber) + { + /* Call the API */ + if (!CsrServerSbApiDispatch[ReceiveMsg.ApiNumber](&ReceiveMsg)) + { + /* It failed, so return nothing */ + ReplyMsg = NULL; + } + } + else + { + /* We don't support this API Number */ + ReplyMsg->ReturnValue = STATUS_NOT_IMPLEMENTED; + } + } +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/srv.h b/reactos/deprecated/csrsrv/srv.h new file mode 100644 index 00000000000..c0d44177525 --- /dev/null +++ b/reactos/deprecated/csrsrv/srv.h @@ -0,0 +1,54 @@ +#ifndef _SRV_H +#define _SRV_H + +/* PSDK/NDK Headers */ +#define NTOS_MODE_USER +#include +#define WIN32_NO_STATUS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CSR Header */ +//#include + +/* PSEH for SEH Support */ +#include + +/* Subsystem Manager Header */ +#include +#include + +/* Internal CSRSS Headers */ +#include +#include + +extern HANDLE CsrHeap; + +#define SM_REG_KEY \ + L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" + +#define SESSION_ROOT L"\\Sessions" +#define GLOBAL_ROOT L"\\GLOBAL??" +#define SYMLINK_NAME L"SymbolicLink" +#define SB_PORT_NAME L"SbAbiPort" +#define CSR_PORT_NAME L"ApiPort" +#define UNICODE_PATH_SEP L"\\" + +/* Defines */ +#define ROUND_UP(n, align) ROUND_DOWN(((ULONG)n) + (align) - 1, (align)) +#define ROUND_DOWN(n, align) (((ULONG)n) & ~((align) - 1l)) + +#endif diff --git a/reactos/deprecated/csrsrv/thredsup.c b/reactos/deprecated/csrsrv/thredsup.c new file mode 100644 index 00000000000..18cfca15961 --- /dev/null +++ b/reactos/deprecated/csrsrv/thredsup.c @@ -0,0 +1,819 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsystems/win32/csrss/csrsrv/thredsup.c + * PURPOSE: CSR Server DLL Thread Implementation + * PROGRAMMERS: ReactOS Portable Systems Group + * Alex Ionescu + */ + +/* INCLUDES *******************************************************************/ + +#include + +#define NDEBUG +#include + +#define CsrHashThread(t) \ + (HandleToUlong(t)&(256 - 1)) + +/* GLOBALS ********************************************************************/ + +LIST_ENTRY CsrThreadHashTable[256]; + +/* FUNCTIONS ******************************************************************/ + +/*++ + * @name ProtectHandle + * @implemented NT5.2 + * + * The ProtectHandle routine protects an object handle against closure. + * + * @return TRUE or FALSE. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +ProtectHandle(IN HANDLE ObjectHandle) +{ + NTSTATUS Status; + OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo; + + /* Query current state */ + Status = NtQueryObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo), + NULL); + if (NT_SUCCESS(Status)) + { + /* Enable protect from close */ + HandleInfo.ProtectFromClose = TRUE; + Status = NtSetInformationObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo)); + if (NT_SUCCESS(Status)) return TRUE; + } + + /* We failed to or set the state */ + return FALSE; +} + +/*++ + * @name UnProtectHandle + * @implemented NT5.2 + * + * The UnProtectHandle routine unprotects an object handle against closure. + * + * @return TRUE or FALSE. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +UnProtectHandle(IN HANDLE ObjectHandle) +{ + NTSTATUS Status; + OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleInfo; + + /* Query current state */ + Status = NtQueryObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo), + NULL); + if (NT_SUCCESS(Status)) + { + /* Disable protect from close */ + HandleInfo.ProtectFromClose = FALSE; + Status = NtSetInformationObject(ObjectHandle, + ObjectHandleFlagInformation, + &HandleInfo, + sizeof(HandleInfo)); + if (NT_SUCCESS(Status)) return TRUE; + } + + /* We failed to or set the state */ + return FALSE; +} + +/*++ + * @name CsrAllocateThread + * + * The CsrAllocateThread routine allocates a new CSR Thread object. + * + * @param CsrProcess + * Pointer to the CSR Process which will contain this CSR Thread. + * + * @return Pointer to the newly allocated CSR Thread. + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrAllocateThread(IN PCSR_PROCESS CsrProcess) +{ + PCSR_THREAD CsrThread; + + /* Allocate the structure */ + CsrThread = RtlAllocateHeap(CsrHeap, HEAP_ZERO_MEMORY, sizeof(CSR_THREAD)); + if (!CsrThread) return(NULL); + + /* Reference the Thread and Process */ + CsrThread->ReferenceCount++; + CsrProcess->ReferenceCount++; + + /* Set the Parent Process */ + CsrThread->Process = CsrProcess; + + /* Return Thread */ + return CsrThread; +} + +/*++ + * @name CsrLockedReferenceThread + * + * The CsrLockedReferenceThread references a CSR Thread while the + * Process Lock is already being held. + * + * @param CsrThread + * Pointer to the CSR Thread to be referenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedReferenceThread(IN PCSR_THREAD CsrThread) +{ + /* Increment the reference count */ + ++CsrThread->ReferenceCount; +} + +/*++ + * @name CsrLocateThreadByClientId + * + * The CsrLocateThreadByClientId routine locates the CSR Thread and, + * optionally, its parent CSR Process, corresponding to a Client ID. + * + * @param Process + * Optional pointer to a CSR Process pointer which will contain + * the CSR Thread's parent. + * + * @param ClientId + * Pointer to a Client ID structure containing the Unique Thread ID + * to look up. + * + * @return Pointer to the CSR Thread corresponding to this CID, or NULL if + * none was found. + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrLocateThreadByClientId(OUT PCSR_PROCESS *Process OPTIONAL, + IN PCLIENT_ID ClientId) +{ + ULONG i; + PLIST_ENTRY ListHead, NextEntry; + PCSR_THREAD FoundThread; + // ASSERT(ProcessStructureListLocked()); + + /* Hash the Thread */ + i = CsrHashThread(ClientId->UniqueThread); + + /* Set the list pointers */ + ListHead = &CsrThreadHashTable[i]; + NextEntry = ListHead->Flink; + + /* Star the loop */ + while (NextEntry != ListHead) + { + /* Get the thread */ + FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, HashLinks); + + /* Compare the CID */ + if (FoundThread->ClientId.UniqueThread == ClientId->UniqueThread) + { + /* Match found, return the process */ + *Process = FoundThread->Process; + + /* Return thread too */ +// DPRINT1("Found: %p %p\n", FoundThread, FoundThread->Process); + return FoundThread; + } + + /* Next */ + NextEntry = NextEntry->Flink; + } + + /* Nothing found */ + return NULL; +} + +/*++ + * @name CsrLocateThreadInProcess + * + * The CsrLocateThreadInProcess routine locates the CSR Thread + * corresponding to a Client ID inside a specific CSR Process. + * + * @param Process + * Optional pointer to the CSR Process which contains the CSR Thread + * that will be looked up. + * + * @param ClientId + * Pointer to a Client ID structure containing the Unique Thread ID + * to look up. + * + * @return Pointer to the CSR Thread corresponding to this CID, or NULL if + * none was found. + * + * @remarks If the CsrProcess argument is NULL, the lookup will be done inside + * CsrRootProcess. + * + *--*/ +PCSR_THREAD +NTAPI +CsrLocateThreadInProcess(IN PCSR_PROCESS CsrProcess OPTIONAL, + IN PCLIENT_ID Cid) +{ + PLIST_ENTRY ListHead, NextEntry; + PCSR_THREAD FoundThread = NULL; + + /* Use the Root Process if none was specified */ + if (!CsrProcess) CsrProcess = CsrRootProcess; + + /* Save the List pointers */ +// DPRINT1("Searching in: %p %d\n", CsrProcess, CsrProcess->ThreadCount); + ListHead = &CsrProcess->ThreadList; + NextEntry = ListHead->Flink; + + /* Start the Loop */ + while (NextEntry != ListHead) + { + /* Get Thread Entry */ + FoundThread = CONTAINING_RECORD(NextEntry, CSR_THREAD, Link); + + /* Check for TID Match */ + if (FoundThread->ClientId.UniqueThread == Cid->UniqueThread) break; + + /* Next entry */ + NextEntry = NextEntry->Flink; + } + + /* Return what we found */ +// DPRINT1("Found: %p\n", FoundThread); + return FoundThread; +} + +/*++ + * @name CsrInsertThread + * + * The CsrInsertThread routine inserts a CSR Thread into its parent's + * Thread List and into the Thread Hash Table. + * + * @param Process + * Pointer to the CSR Process containing this CSR Thread. + * + * @param Thread + * Pointer to the CSR Thread to be inserted. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrInsertThread(IN PCSR_PROCESS Process, + IN PCSR_THREAD Thread) +{ + ULONG i; + // ASSERT(ProcessStructureListLocked()); + + /* Insert it into the Regular List */ + InsertTailList(&Process->ThreadList, &Thread->Link); + + /* Increase Thread Count */ + Process->ThreadCount++; + + /* Hash the Thread */ + i = CsrHashThread(Thread->ClientId.UniqueThread); +// DPRINT1("TID %lx HASH: %lx\n", Thread->ClientId.UniqueThread, i); + + /* Insert it there too */ + InsertHeadList(&CsrThreadHashTable[i], &Thread->HashLinks); +} + +/*++ + * @name CsrDeallocateThread + * + * The CsrDeallocateThread frees the memory associated with a CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread to be freed. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Thread has been cleanly + * dereferenced and killed. + * + *--*/ +VOID +NTAPI +CsrDeallocateThread(IN PCSR_THREAD CsrThread) +{ + /* Free the process object from the heap */ + // ASSERT(CsrThread->WaitBlock == NULL); + RtlFreeHeap(CsrHeap, 0, CsrThread); +} + +/*++ + * @name CsrRemoveThread + * + * The CsrRemoveThread function undoes a CsrInsertThread operation and + * removes the CSR Thread from the the Hash Table and Thread List. + * + * @param CsrThread + * Pointer to the CSR Thread to remove. + * + * @return None. + * + * @remarks If this CSR Thread is the last one inside a CSR Process, the + * parent will be dereferenced and the CsrProcessLastThreadTerminated + * flag will be set. + * + * After executing this routine, the CSR Thread will have the + * CsrThreadInTermination flag set. + * + *--*/ +VOID +NTAPI +CsrRemoveThread(IN PCSR_THREAD CsrThread) +{ + ASSERT(ProcessStructureListLocked()); + + /* Remove it from the List */ + RemoveEntryList(&CsrThread->Link); + + /* Decreate the thread count of the process */ + CsrThread->Process->ThreadCount--; + + /* Remove it from the Hash List as well */ + if (CsrThread->HashLinks.Flink) RemoveEntryList(&CsrThread->HashLinks); + + /* Check if this is the last Thread */ + if (!CsrThread->Process->ThreadCount) + { + /* Check if it's not already been marked for deletion */ + if (!(CsrThread->Process->Flags & CsrProcessLastThreadTerminated)) + { + /* Let everyone know this process is about to lose the thread */ + CsrThread->Process->Flags |= CsrProcessLastThreadTerminated; + + /* Reference the Process */ + CsrLockedDereferenceProcess(CsrThread->Process); + } + } + + /* Mark the thread for deletion */ + CsrThread->Flags |= CsrThreadInTermination; +} + +/*++ + * @name CsrCreateRemoteThread + * @implemented NT4 + * + * The CsrCreateRemoteThread routine creates a CSR Thread object for + * an NT Thread which is not part of the current NT Process. + * + * @param hThread + * Handle to an existing NT Thread to which to associate this + * CSR Thread. + * + * @param ClientId + * Pointer to the Client ID structure of the NT Thread to associate + * with this CSR Thread. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateRemoteThread(IN HANDLE hThread, + IN PCLIENT_ID ClientId) +{ + NTSTATUS Status; + HANDLE ThreadHandle; + PCSR_THREAD CsrThread; + PCSR_PROCESS CsrProcess; + KERNEL_USER_TIMES KernelTimes; + DPRINT("CSRSRV: %s called\n", __FUNCTION__); + + /* Get the Thread Create Time */ + Status = NtQueryInformationThread(hThread, + ThreadTimes, + &KernelTimes, + sizeof(KernelTimes), + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to query thread times: %lx\n", Status); + return Status; + } + + /* Lock the Owner Process */ + Status = CsrLockProcessByClientId(&ClientId->UniqueProcess, &CsrProcess); + if (!NT_SUCCESS(Status)) + { + DPRINT1("No known process for %lx\n", ClientId->UniqueProcess); + return Status; + } + + /* Make sure the thread didn't terminate */ + if (KernelTimes.ExitTime.QuadPart) + { + /* Unlock the process and return */ + CsrUnlockProcess(CsrProcess); + DPRINT1("Dead thread: %I64x\n", KernelTimes.ExitTime.QuadPart); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Allocate a CSR Thread Structure */ + CsrThread = CsrAllocateThread(CsrProcess); + if (!CsrThread) + { + DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__); + CsrUnlockProcess(CsrProcess); + return STATUS_NO_MEMORY; + } + + /* Duplicate the Thread Handle */ + Status = NtDuplicateObject(NtCurrentProcess(), + hThread, + NtCurrentProcess(), + &ThreadHandle, + 0, + 0, + DUPLICATE_SAME_ACCESS); + /* Allow failure */ + if (!NT_SUCCESS(Status)) + { + DPRINT1("Thread duplication failed: %lx\n", Status); + ThreadHandle = hThread; + } + + /* Save the data we have */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = *ClientId; + CsrThread->ThreadHandle = ThreadHandle; + ProtectHandle(ThreadHandle); + CsrThread->Flags = 0; + + /* Insert the Thread into the Process */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Release the lock and return */ + CsrUnlockProcess(CsrProcess); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrThreadRefcountZero + * + * The CsrThreadRefcountZero routine is executed when a CSR Thread has lost + * all its active references. It removes and de-allocates the CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread that is to be deleted. + * + * @return None. + * + * @remarks Do not call this routine. It is reserved for the internal + * thread management routines when a CSR Thread has lost all + * its references. + * + * This routine is called with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrThreadRefcountZero(IN PCSR_THREAD CsrThread) +{ + PCSR_PROCESS CsrProcess = CsrThread->Process; + NTSTATUS Status; + ASSERT(ProcessStructureListLocked()); + + /* Remove this thread */ + CsrRemoveThread(CsrThread); + + /* Release the Process Lock */ + CsrReleaseProcessLock(); + + /* Close the NT Thread Handle */ + if (CsrThread->ThreadHandle) + { + UnProtectHandle(CsrThread->ThreadHandle); + Status = NtClose(CsrThread->ThreadHandle); + ASSERT(NT_SUCCESS(Status)); + } + + /* De-allocate the CSR Thread Object */ + CsrDeallocateThread(CsrThread); + + /* Remove a reference from the process */ + CsrDereferenceProcess(CsrProcess); +} + +/*++ + * @name CsrDestroyThread + * @implemented NT4 + * + * The CsrDestroyThread routine destroys the CSR Thread corresponding to + * a given Thread ID. + * + * @param Cid + * Pointer to the Client ID Structure corresponding to the CSR + * Thread which is about to be destroyed. + * + * @return STATUS_SUCCESS in case of success, STATUS_THREAD_IS_TERMINATING + * if the CSR Thread is already terminating. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrDestroyThread(IN PCLIENT_ID Cid) +{ + CLIENT_ID ClientId = *Cid; + PCSR_THREAD CsrThread; + PCSR_PROCESS CsrProcess; + + /* Acquire lock */ + CsrAcquireProcessLock(); + + /* Find the thread */ + CsrThread = CsrLocateThreadByClientId(&CsrProcess, + &ClientId); + + /* Make sure we got one back, and that it's not already gone */ + if (!CsrThread || CsrThread->Flags & CsrThreadTerminated) + { + /* Release the lock and return failure */ + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Set the terminated flag */ + CsrThread->Flags |= CsrThreadTerminated; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Do we have an active wait block? */ + if (CsrThread->WaitBlock) + { + /* Notify waiters of termination */ + CsrNotifyWaitBlock(CsrThread->WaitBlock, + NULL, + NULL, + NULL, + CsrProcessTerminating, + TRUE); + } + + /* Release the Wait Lock */ + CsrReleaseWaitLock(); + + /* Dereference the thread */ + CsrLockedDereferenceThread(CsrThread); + + /* Release the Process Lock and return success */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrLockedDereferenceThread + * + * The CsrLockedDereferenceThread dereferences a CSR Thread while the + * Process Lock is already being held. + * + * @param CsrThread + * Pointer to the CSR Thread to be dereferenced. + * + * @return None. + * + * @remarks This routine will return with the Process Lock held. + * + *--*/ +VOID +NTAPI +CsrLockedDereferenceThread(IN PCSR_THREAD CsrThread) +{ + LONG LockCount; + + /* Decrease reference count */ + LockCount = --CsrThread->ReferenceCount; + ASSERT(LockCount >= 0); + if (!LockCount) + { + /* Call the generic cleanup code */ + CsrThreadRefcountZero(CsrThread); + CsrAcquireProcessLock(); + } +} + +/*++ + * @name CsrCreateThread + * @implemented NT4 + * + * The CsrCreateThread routine creates a CSR Thread object for an NT Thread. + * + * @param CsrProcess + * Pointer to the CSR Process which will contain the CSR Thread. + * + * @param hThread + * Handle to an existing NT Thread to which to associate this + * CSR Thread. + * + * @param ClientId + * Pointer to the Client ID structure of the NT Thread to associate + * with this CSR Thread. + * + * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL + * otherwise. + * + * @remarks None. + * + *--*/ +NTSTATUS +NTAPI +CsrCreateThread(IN PCSR_PROCESS CsrProcess, + IN HANDLE hThread, + IN PCLIENT_ID ClientId) +{ + PCSR_THREAD CsrThread; + PCSR_PROCESS CurrentProcess; + PCSR_THREAD CurrentThread = NtCurrentTeb()->CsrClientThread; + CLIENT_ID CurrentCid; + KERNEL_USER_TIMES KernelTimes; + + /* Get the current thread and CID */ + CurrentCid = CurrentThread->ClientId; + + /* Acquire the Process Lock */ + CsrAcquireProcessLock(); + + /* Get the current Process and make sure the Thread is valid with this CID */ + CurrentThread = CsrLocateThreadByClientId(&CurrentProcess, + &CurrentCid); + + /* Something is wrong if we get an empty thread back */ + if (!CurrentThread) + { + DPRINT1("CSRSRV:%s: invalid thread!\n", __FUNCTION__); + CsrReleaseProcessLock(); + return STATUS_THREAD_IS_TERMINATING; + } + + /* Get the Thread Create Time */ + NtQueryInformationThread(hThread, + ThreadTimes, + (PVOID)&KernelTimes, + sizeof(KernelTimes), + NULL); + + /* Allocate a CSR Thread Structure */ + if (!(CsrThread = CsrAllocateThread(CsrProcess))) + { + DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__); + CsrReleaseProcessLock(); + return STATUS_NO_MEMORY; + } + + /* Save the data we have */ + CsrThread->CreateTime = KernelTimes.CreateTime; + CsrThread->ClientId = *ClientId; + CsrThread->ThreadHandle = hThread; + CsrThread->Flags = 0; + + /* Insert the Thread into the Process */ + CsrInsertThread(CsrProcess, CsrThread); + + /* Release the lock and return */ + CsrReleaseProcessLock(); + return STATUS_SUCCESS; +} + +/*++ + * @name CsrAddStaticServerThread + * @implemented NT4 + * + * The CsrAddStaticServerThread routine adds a new CSR Thread to the + * CSR Server Process (CsrRootProcess). + * + * @param hThread + * Handle to an existing NT Thread to which to associate this + * CSR Thread. + * + * @param ClientId + * Pointer to the Client ID structure of the NT Thread to associate + * with this CSR Thread. + * + * @param ThreadFlags + * Initial CSR Thread Flags to associate to this CSR Thread. Usually + * CsrThreadIsServerThread. + * + * @return Pointer to the newly allocated CSR Thread. + * + * @remarks None. + * + *--*/ +PCSR_THREAD +NTAPI +CsrAddStaticServerThread(IN HANDLE hThread, + IN PCLIENT_ID ClientId, + IN ULONG ThreadFlags) +{ + PCSR_THREAD CsrThread; + + /* Get the Lock */ + CsrAcquireProcessLock(); + + /* Allocate the Server Thread */ + CsrThread = CsrAllocateThread(CsrRootProcess); + if (CsrThread) + { + /* Setup the Object */ + CsrThread->ThreadHandle = hThread; + ProtectHandle(hThread); + CsrThread->ClientId = *ClientId; + CsrThread->Flags = ThreadFlags; + + /* Insert it into the Thread List */ + InsertTailList(&CsrRootProcess->ThreadList, &CsrThread->Link); + + /* Increment the thread count */ + CsrRootProcess->ThreadCount++; + } + else + { + DPRINT1("CsrAddStaticServerThread: alloc failed for thread 0x%x\n", hThread); + } + + /* Release the Process Lock and return */ + CsrReleaseProcessLock(); + return CsrThread; +} + +/*++ + * @name CsrDereferenceThread + * @implemented NT4 + * + * The CsrDereferenceThread routine removes a reference from a CSR Thread. + * + * @param CsrThread + * Pointer to the CSR Thread to dereference. + * + * @return None. + * + * @remarks If the reference count has reached zero (ie: the CSR Thread has + * no more active references), it will be deleted. + * + *--*/ +VOID +NTAPI +CsrDereferenceThread(IN PCSR_THREAD CsrThread) +{ + /* Acquire process lock */ + CsrAcquireProcessLock(); + + /* Decrease reference count */ + ASSERT(CsrThread->ReferenceCount > 0); + if (!(--CsrThread->ReferenceCount)) + { + /* Call the generic cleanup code */ + CsrThreadRefcountZero(CsrThread); + } + else + { + /* Just release the lock */ + CsrReleaseProcessLock(); + } +} + +/* EOF */ diff --git a/reactos/deprecated/csrsrv/wait.c b/reactos/deprecated/csrsrv/wait.c new file mode 100644 index 00000000000..1852c9dfb7e --- /dev/null +++ b/reactos/deprecated/csrsrv/wait.c @@ -0,0 +1,449 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSR Sub System + * FILE: subsystems/win32/csrss/csrsrv/wait.c + * PURPOSE: CSR Server DLL Wait Implementation + * PROGRAMMERS: Emanuele Aliberti + * Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES ******************************************************************/ + +#include "srv.h" + +#define NDEBUG +#include + +/* DATA **********************************************************************/ + +RTL_CRITICAL_SECTION CsrWaitListsLock; + +/* PRIVATE FUNCTIONS *********************************************************/ + +/*++ + * @name CsrInitializeWait + * + * The CsrInitializeWait routine initializes a CSR Wait Object. + * + * @param WaitFunction + * Pointer to the function that will handle this wait. + * + * @param CsrWaitThread + * Pointer to the CSR Thread that will perform the wait. + * + * @param WaitApiMessage + * Pointer to the CSR API Message associated to this wait. + * + * @param WaitContext + * Pointer to a user-defined parameter associated to this wait. + * + * @param NewWaitBlock + * Pointed to the initialized CSR Wait Block for this wait. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrInitializeWait(IN CSR_WAIT_FUNCTION WaitFunction, + IN PCSR_THREAD CsrWaitThread, + IN OUT PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + OUT PCSR_WAIT_BLOCK *NewWaitBlock) +{ + ULONG Size; + PCSR_WAIT_BLOCK WaitBlock; + + /* Calculate the size of the wait block */ + Size = sizeof(CSR_WAIT_BLOCK) - + sizeof(WaitBlock->WaitApiMessage) + + WaitApiMessage->Header.u1.s1.TotalLength; + + /* Allocate the Wait Block */ + WaitBlock = RtlAllocateHeap(CsrHeap, 0, Size); + if (!WaitBlock) + { + /* Fail */ + WaitApiMessage->Status = STATUS_NO_MEMORY; + return FALSE; + } + + /* Initialize it */ + WaitBlock->Size = Size; + WaitBlock->WaitThread = CsrWaitThread; + WaitBlock->WaitContext = WaitContext; + WaitBlock->WaitFunction = WaitFunction; + WaitBlock->UserWaitList.Flink = NULL; + WaitBlock->UserWaitList.Blink = NULL; + WaitBlock->WaitList = WaitBlock->UserWaitList; + + /* Copy the message */ + RtlMoveMemory(&WaitBlock->WaitApiMessage, + WaitApiMessage, + WaitApiMessage->Header.u1.s1.TotalLength); + + /* Return the block */ + *NewWaitBlock = WaitBlock; + return TRUE; +} + +/*++ + * @name CsrNotifyWaitBlock + * + * The CsrNotifyWaitBlock routine calls the wait function for a registered + * CSR Wait Block, and replies to the attached CSR API Message, if any. + * + * @param WaitBlock + * Pointer to the CSR Wait Block + * + * @param WaitList + * Pointer to the wait list for this wait. + * + * @param WaitArgument[1-2] + * User-defined values to pass to the wait function. + * + * @param WaitFlags + * Wait flags for this wait. + * + * @param DereferenceThread + * Specifies whether the CSR Thread should be dereferenced at the + * end of this wait. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks After a wait block is notified, the wait function becomes invalid. + * + *--*/ +BOOLEAN +NTAPI +CsrNotifyWaitBlock(IN PCSR_WAIT_BLOCK WaitBlock, + IN PLIST_ENTRY WaitList, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2, + IN ULONG WaitFlags, + IN BOOLEAN DereferenceThread) +{ + /* Call the wait function */ + if ((WaitBlock->WaitFunction)(WaitList, + WaitBlock->WaitThread, + &WaitBlock->WaitApiMessage, + WaitBlock->WaitContext, + WaitArgument1, + WaitArgument2, + WaitFlags)) + { + /* The wait is done, clear the block */ + WaitBlock->WaitThread->WaitBlock = NULL; + + /* Check for captured arguments */ + if (WaitBlock->WaitApiMessage.CsrCaptureData) + { + /* Release them */ + CsrReleaseCapturedArguments(&WaitBlock->WaitApiMessage); + } + + /* Reply to the port */ + NtReplyPort(WaitBlock->WaitThread->Process->ClientPort, + (PPORT_MESSAGE)&WaitBlock->WaitApiMessage); + + /* Check if we should dereference the thread */ + if (DereferenceThread) + { + /* Remove it from the Wait List */ + if (WaitBlock->WaitList.Flink) + { + RemoveEntryList(&WaitBlock->WaitList); + } + + /* Remove it from the User Wait List */ + if (WaitBlock->UserWaitList.Flink) + { + RemoveEntryList(&WaitBlock->UserWaitList); + } + + /* Dereference teh thread */ + CsrDereferenceThread(WaitBlock->WaitThread); + + /* Free the wait block */ + RtlFreeHeap(CsrHeap, 0, WaitBlock); + } + else + { + /* The wait is complete, but the thread is being kept alive */ + WaitBlock->WaitFunction = NULL; + } + + /* The wait suceeded */ + return TRUE; + } + + /* The wait failed */ + return FALSE; +} + +/* PUBLIC FUNCTIONS **********************************************************/ + +/*++ + * @name CsrCreateWait + * @implemented NT4 + * + * The CsrCreateWait routine creates a CSR Wait. + * + * @param WaitList + * Pointer to a list entry of the waits to associate. + * + * @param WaitFunction + * Pointer to the function that will handle this wait. + * + * @param CsrWaitThread + * Pointer to the CSR Thread that will perform the wait. + * + * @param WaitApiMessage + * Pointer to the CSR API Message associated to this wait. + * + * @param WaitContext + * Pointer to a user-defined parameter associated to this wait. + * + * @param UserWaitList + * Pointer to a list entry of the user-defined waits to associate. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrCreateWait(IN PLIST_ENTRY WaitList, + IN CSR_WAIT_FUNCTION WaitFunction, + IN PCSR_THREAD CsrWaitThread, + IN OUT PCSR_API_MESSAGE WaitApiMessage, + IN PVOID WaitContext, + IN PLIST_ENTRY UserWaitList OPTIONAL) +{ + PCSR_WAIT_BLOCK WaitBlock; + + /* Initialize the wait */ + if (!CsrInitializeWait(WaitFunction, + CsrWaitThread, + WaitApiMessage, + WaitContext, + &WaitBlock)) + { + return FALSE; + } + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Make sure the thread wasn't destroyed */ + if (CsrWaitThread->Flags & CsrThreadTerminated) + { + /* Fail the wait */ + RtlFreeHeap(CsrHeap, 0, WaitBlock); + CsrReleaseWaitLock(); + return FALSE; + } + + /* Insert the wait in the queue */ + InsertTailList(WaitList, &WaitBlock->WaitList); + + /* Insert the User Wait too, if one was given */ + if (UserWaitList) InsertTailList(UserWaitList, &WaitBlock->UserWaitList); + + /* Return */ + CsrReleaseWaitLock(); + return TRUE; +} + +/*++ + * @name CsrDereferenceWait + * @implemented NT4 + * + * The CsrDereferenceWait routine dereferences a CSR Wait Block. + * + * @param WaitList + * Pointer to the Wait List associated to the wait. + + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrDereferenceWait(IN PLIST_ENTRY WaitList) +{ + PLIST_ENTRY NextEntry; + PCSR_WAIT_BLOCK WaitBlock; + + /* Acquire the Process and Wait Locks */ + CsrAcquireProcessLock(); + CsrAcquireWaitLock(); + + /* Set the list pointers */ + NextEntry = WaitList->Flink; + + /* Start the loop */ + while (NextEntry != WaitList) + { + /* Get the wait block */ + WaitBlock = CONTAINING_RECORD(NextEntry, CSR_WAIT_BLOCK, WaitList); + + /* Move to the next entry */ + NextEntry = NextEntry->Flink; + + /* Check if there's no Wait Routine */ + if (!WaitBlock->WaitFunction) + { + /* Remove it from the Wait List */ + if (WaitBlock->WaitList.Flink) + { + RemoveEntryList(&WaitBlock->WaitList); + } + + /* Remove it from the User Wait List */ + if (WaitBlock->UserWaitList.Flink) + { + RemoveEntryList(&WaitBlock->UserWaitList); + } + + /* Dereference the thread waiting on it */ + CsrDereferenceThread(WaitBlock->WaitThread); + + /* Free the block */ + RtlFreeHeap(CsrHeap, 0, WaitBlock); + } + } + + /* Release the locks */ + CsrReleaseWaitLock(); + CsrReleaseProcessLock(); +} + +/*++ + * @name CsrMoveSatisfiedWait + * @implemented NT5 + * + * The CsrMoveSatisfiedWait routine moves satisfied waits from a wait list + * to another list entry. + * + * @param NewEntry + * Pointer to a list entry where the satisfied waits will be added. + * + * @param WaitList + * Pointer to a list entry to analyze for satisfied waits. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +CsrMoveSatisfiedWait(IN PLIST_ENTRY NewEntry, + IN PLIST_ENTRY WaitList) +{ + PLIST_ENTRY NextEntry; + PCSR_WAIT_BLOCK WaitBlock; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Set the List pointers */ + NextEntry = WaitList->Flink; + + /* Start looping */ + while (NextEntry != WaitList) + { + /* Get the Wait block */ + WaitBlock = CONTAINING_RECORD(NextEntry, CSR_WAIT_BLOCK, WaitList); + + /* Go to the next entry */ + NextEntry = NextEntry->Flink; + + /* Check if there is a Wait Callback */ + if (WaitBlock->WaitFunction) + { + /* Remove it from the Wait Block Queue */ + RemoveEntryList(&WaitBlock->WaitList); + + /* Insert the new entry */ + InsertTailList(&WaitBlock->WaitList, NewEntry); + } + } + + /* Release the wait lock */ + CsrReleaseWaitLock(); +} + +/*++ + * @name CsrNotifyWait + * @implemented NT4 + * + * The CsrNotifyWait notifies a CSR Wait Block. + * + * @param WaitList + * Pointer to the list entry for this wait. + * + * @param WaitType + * Type of the wait to perform, either WaitAny or WaitAll. + * + * @param WaitArgument[1-2] + * User-defined argument to pass on to the wait function. + * + * @return TRUE in case of success, FALSE otherwise. + * + * @remarks None. + * + *--*/ +BOOLEAN +NTAPI +CsrNotifyWait(IN PLIST_ENTRY WaitList, + IN ULONG WaitType, + IN PVOID WaitArgument1, + IN PVOID WaitArgument2) +{ + PLIST_ENTRY NextEntry; + PCSR_WAIT_BLOCK WaitBlock; + BOOLEAN NotifySuccess = FALSE; + + /* Acquire the Wait Lock */ + CsrAcquireWaitLock(); + + /* Set the List pointers */ + NextEntry = WaitList->Flink; + + /* Start looping */ + while (NextEntry != WaitList) + { + /* Get the Wait block */ + WaitBlock = CONTAINING_RECORD(NextEntry, CSR_WAIT_BLOCK, WaitList); + + /* Go to the next entry */ + NextEntry = NextEntry->Flink; + + /* Check if there is a Wait Callback */ + if (WaitBlock->WaitFunction) + { + /* Notify the Waiter */ + NotifySuccess |= CsrNotifyWaitBlock(WaitBlock, + WaitList, + WaitArgument1, + WaitArgument2, + 0, + FALSE); + + /* We've already done a wait, so leave unless this is a Wait All */ + if (WaitType != WaitAll) break; + } + } + + /* Release the wait lock and return */ + CsrReleaseWaitLock(); + return NotifySuccess; +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/CMakeLists.txt b/reactos/deprecated/win32csr/CMakeLists.txt new file mode 100644 index 00000000000..481cd4073a5 --- /dev/null +++ b/reactos/deprecated/win32csr/CMakeLists.txt @@ -0,0 +1,36 @@ + +include_directories( + BEFORE . + ${REACTOS_SOURCE_DIR}/subsystems/win32/csrss/include + ${REACTOS_SOURCE_DIR}/include/reactos/subsys + ${REACTOS_SOURCE_DIR}/include/reactos/drivers + ${REACTOS_SOURCE_DIR}/dll/cpl/console + ${REACTOS_SOURCE_DIR}/win32ss/include) + +spec2def(win32csr.dll win32csr.spec) + +list(APPEND SOURCE + alias.c + coninput.c + conoutput.c + console.c + desktopbg.c + dllmain.c + exitros.c + file.c + guiconsole.c + handle.c + harderror.c + lineinput.c + tuiconsole.c + win32csr.rc + ${CMAKE_CURRENT_BINARY_DIR}/win32csr.def) + +add_library(win32csr SHARED ${SOURCE}) +target_link_libraries(win32csr win32ksys ${PSEH_LIB}) +set_module_type(win32csr win32dll) +add_importlibs(win32csr psapi msvcrt kernel32 ntdll csrsrv) +add_delay_importlibs(win32csr user32 gdi32 advapi32) +add_pch(win32csr w32csr.h) +add_dependencies(win32csr bugcodes) +add_cd_file(TARGET win32csr DESTINATION reactos/system32 FOR all) diff --git a/reactos/deprecated/win32csr/alias.c b/reactos/deprecated/win32csr/alias.c new file mode 100644 index 00000000000..3329d84e355 --- /dev/null +++ b/reactos/deprecated/win32csr/alias.c @@ -0,0 +1,552 @@ +/* + * PROJECT: ReactOS CSRSS + * LICENSE: GPL - See COPYING in the top level directory + * FILE: subsystems/win32/csrss/api/alias.c + * PURPOSE: CSRSS alias support functions + * COPYRIGHT: Christoph Wittich + * Johannes Anderwald + * + */ + + +/* INCLUDES ******************************************************************/ + +#define NDEBUG +#include "w32csr.h" +#include + +typedef struct tagALIAS_ENTRY +{ + LPCWSTR lpSource; + LPCWSTR lpTarget; + struct tagALIAS_ENTRY * Next; +} ALIAS_ENTRY, *PALIAS_ENTRY; + + +typedef struct tagALIAS_HEADER +{ + LPCWSTR lpExeName; + PALIAS_ENTRY Data; + struct tagALIAS_HEADER * Next; + +} ALIAS_HEADER, *PALIAS_HEADER; + +static +PALIAS_HEADER +IntFindAliasHeader(PALIAS_HEADER RootHeader, LPCWSTR lpExeName) +{ + while(RootHeader) + { + INT diff = _wcsicmp(RootHeader->lpExeName, lpExeName); + if (!diff) + return RootHeader; + + if (diff > 0) + break; + + RootHeader = RootHeader->Next; + } + return NULL; +} + +PALIAS_HEADER +IntCreateAliasHeader(LPCWSTR lpExeName) +{ + PALIAS_HEADER Entry; + UINT dwLength = wcslen(lpExeName) + 1; + + Entry = RtlAllocateHeap(Win32CsrApiHeap, 0, sizeof(ALIAS_HEADER) + sizeof(WCHAR) * dwLength); + if (!Entry) + return Entry; + + Entry->lpExeName = (LPCWSTR)(Entry + 1); + wcscpy((WCHAR*)Entry->lpExeName, lpExeName); + Entry->Data = NULL; + Entry->Next = NULL; + return Entry; +} + +VOID +IntInsertAliasHeader(PALIAS_HEADER * RootHeader, PALIAS_HEADER NewHeader) +{ + PALIAS_HEADER CurrentHeader; + PALIAS_HEADER *LastLink = RootHeader; + + while ((CurrentHeader = *LastLink) != NULL) + { + INT Diff = _wcsicmp(NewHeader->lpExeName, CurrentHeader->lpExeName); + if (Diff < 0) + { + break; + } + LastLink = &CurrentHeader->Next; + } + + *LastLink = NewHeader; + NewHeader->Next = CurrentHeader; +} + +PALIAS_ENTRY +IntGetAliasEntry(PALIAS_HEADER Header, LPCWSTR lpSrcName) +{ + PALIAS_ENTRY RootHeader; + + if (Header == NULL) + return NULL; + + RootHeader = Header->Data; + while(RootHeader) + { + INT diff; + DPRINT("IntGetAliasEntry>lpSource %S\n", RootHeader->lpSource); + diff = _wcsicmp(RootHeader->lpSource, lpSrcName); + if (!diff) + return RootHeader; + + if (diff > 0) + break; + + RootHeader = RootHeader->Next; + } + return NULL; +} + + +VOID +IntInsertAliasEntry(PALIAS_HEADER Header, PALIAS_ENTRY NewEntry) +{ + PALIAS_ENTRY CurrentEntry; + PALIAS_ENTRY *LastLink = &Header->Data; + + while ((CurrentEntry = *LastLink) != NULL) + { + INT Diff = _wcsicmp(NewEntry->lpSource, CurrentEntry->lpSource); + if (Diff < 0) + { + break; + } + LastLink = &CurrentEntry->Next; + } + + *LastLink = NewEntry; + NewEntry->Next = CurrentEntry; +} + +PALIAS_ENTRY +IntCreateAliasEntry(LPCWSTR lpSource, LPCWSTR lpTarget) +{ + UINT dwSource; + UINT dwTarget; + PALIAS_ENTRY Entry; + + dwSource = wcslen(lpSource) + 1; + dwTarget = wcslen(lpTarget) + 1; + + Entry = RtlAllocateHeap(Win32CsrApiHeap, 0, sizeof(ALIAS_ENTRY) + sizeof(WCHAR) * (dwSource + dwTarget)); + if (!Entry) + return Entry; + + Entry->lpSource = (LPCWSTR)(Entry + 1); + wcscpy((LPWSTR)Entry->lpSource, lpSource); + Entry->lpTarget = Entry->lpSource + dwSource; + wcscpy((LPWSTR)Entry->lpTarget, lpTarget); + Entry->Next = NULL; + + return Entry; +} + +UINT +IntGetConsoleAliasesExesLength(PALIAS_HEADER RootHeader) +{ + UINT length = 0; + + while(RootHeader) + { + length += (wcslen(RootHeader->lpExeName) + 1) * sizeof(WCHAR); + RootHeader = RootHeader->Next; + } + if (length) + length += sizeof(WCHAR); // last entry entry is terminated with 2 zero bytes + + return length; +} + +UINT +IntGetConsoleAliasesExes(PALIAS_HEADER RootHeader, LPWSTR TargetBuffer, UINT TargetBufferSize) +{ + UINT Offset = 0; + UINT Length; + + TargetBufferSize /= sizeof(WCHAR); + while(RootHeader) + { + Length = wcslen(RootHeader->lpExeName) + 1; + if (TargetBufferSize > Offset + Length) + { + wcscpy(&TargetBuffer[Offset], RootHeader->lpExeName); + Offset += Length; + } + else + { + break; + } + RootHeader = RootHeader->Next; + } + Length = min(Offset+1, TargetBufferSize); + TargetBuffer[Length] = L'\0'; + return Length * sizeof(WCHAR); +} + +UINT +IntGetAllConsoleAliasesLength(PALIAS_HEADER Header) +{ + UINT Length = 0; + PALIAS_ENTRY CurEntry = Header->Data; + + while(CurEntry) + { + Length += wcslen(CurEntry->lpSource); + Length += wcslen(CurEntry->lpTarget); + Length += 2; // zero byte and '=' + CurEntry = CurEntry->Next; + } + + if (Length) + { + return (Length+1) * sizeof(WCHAR); + } + return 0; +} +UINT +IntGetAllConsoleAliases(PALIAS_HEADER Header, LPWSTR TargetBuffer, UINT TargetBufferLength) +{ + PALIAS_ENTRY CurEntry = Header->Data; + UINT Offset = 0; + UINT SrcLength, TargetLength; + + TargetBufferLength /= sizeof(WCHAR); + while(CurEntry) + { + SrcLength = wcslen(CurEntry->lpSource) + 1; + TargetLength = wcslen(CurEntry->lpTarget) + 1; + if (Offset + TargetLength + SrcLength >= TargetBufferLength) + break; + + wcscpy(&TargetBuffer[Offset], CurEntry->lpSource); + Offset += SrcLength; + TargetBuffer[Offset] = L'='; + wcscpy(&TargetBuffer[Offset], CurEntry->lpTarget); + Offset += TargetLength; + + CurEntry = CurEntry->Next; + } + TargetBuffer[Offset] = L'\0'; + return Offset * sizeof(WCHAR); +} +VOID +IntDeleteAliasEntry(PALIAS_HEADER Header, PALIAS_ENTRY Entry) +{ + PALIAS_ENTRY *LastLink = &Header->Data; + PALIAS_ENTRY CurEntry; + + while ((CurEntry = *LastLink) != NULL) + { + if (CurEntry == Entry) + { + *LastLink = Entry->Next; + RtlFreeHeap(Win32CsrApiHeap, 0, Entry); + return; + } + LastLink = &CurEntry->Next; + } +} +VOID +IntDeleteAllAliases(PALIAS_HEADER RootHeader) +{ + PALIAS_HEADER Header, NextHeader; + PALIAS_ENTRY Entry, NextEntry; + for (Header = RootHeader; Header; Header = NextHeader) + { + NextHeader = Header->Next; + for (Entry = Header->Data; Entry; Entry = NextEntry) + { + NextEntry = Entry->Next; + RtlFreeHeap(Win32CsrApiHeap, 0, Entry); + } + RtlFreeHeap(Win32CsrApiHeap, 0, Header); + } +} + +CSR_API(CsrAddConsoleAlias) +{ + PCSRSS_CONSOLE Console; + PALIAS_HEADER Header; + PALIAS_ENTRY Entry; + WCHAR * lpExeName; + WCHAR * lpSource; + WCHAR * lpTarget; + //ULONG TotalLength; + //WCHAR * Ptr; + + //TotalLength = Request->Data.AddConsoleAlias.SourceLength + Request->Data.AddConsoleAlias.ExeLength + Request->Data.AddConsoleAlias.TargetLength; + //Ptr = (WCHAR*)((ULONG_PTR)Request + sizeof(CSR_API_MESSAGE)); + + lpSource = (WCHAR*)((ULONG_PTR)Request + sizeof(CSR_API_MESSAGE)); + lpExeName = (WCHAR*)((ULONG_PTR)Request + sizeof(CSR_API_MESSAGE) + Request->Data.AddConsoleAlias.SourceLength * sizeof(WCHAR)); + lpTarget = (Request->Data.AddConsoleAlias.TargetLength != 0 ? lpExeName + Request->Data.AddConsoleAlias.ExeLength : NULL); + + DPRINT("CsrAddConsoleAlias entered Request %p lpSource %p lpExeName %p lpTarget %p\n", Request, lpSource, lpExeName, lpTarget); + + if (lpExeName == NULL || lpSource == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + Request->Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (!NT_SUCCESS(Request->Status)) + { + return Request->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, lpExeName); + if (!Header && lpTarget != NULL) + { + Header = IntCreateAliasHeader(lpExeName); + if (!Header) + { + ConioUnlockConsole(Console); + return STATUS_INSUFFICIENT_RESOURCES; + } + IntInsertAliasHeader(&Console->Aliases, Header); + } + + if (lpTarget == NULL) // delete the entry + { + Entry = IntGetAliasEntry(Header, lpSource); + if (Entry) + { + IntDeleteAliasEntry(Header, Entry); + Request->Status = STATUS_SUCCESS; + } + else + { + Request->Status = STATUS_INVALID_PARAMETER; + } + ConioUnlockConsole(Console); + return Request->Status; + } + + Entry = IntCreateAliasEntry(lpSource, lpTarget); + + if (!Entry) + { + ConioUnlockConsole(Console); + return STATUS_INSUFFICIENT_RESOURCES; + } + + IntInsertAliasEntry(Header, Entry); + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +CSR_API(CsrGetConsoleAlias) +{ + PCSRSS_CONSOLE Console; + PALIAS_HEADER Header; + PALIAS_ENTRY Entry; + UINT Length; + WCHAR * lpExeName; + WCHAR * lpSource; + WCHAR * lpTarget; + + lpSource = (LPWSTR)((ULONG_PTR)Request + sizeof(CSR_API_MESSAGE)); + lpExeName = lpSource + Request->Data.GetConsoleAlias.SourceLength; + lpTarget = Request->Data.GetConsoleAlias.TargetBuffer; + + + DPRINT("CsrGetConsoleAlias entered lpExeName %p lpSource %p TargetBuffer %p TargetBufferLength %u\n", + lpExeName, lpSource, lpTarget, Request->Data.GetConsoleAlias.TargetBufferLength); + + if (Request->Data.GetConsoleAlias.ExeLength == 0 || lpTarget == NULL || + Request->Data.GetConsoleAlias.TargetBufferLength == 0 || Request->Data.GetConsoleAlias.SourceLength == 0) + { + return STATUS_INVALID_PARAMETER; + } + + Request->Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (!NT_SUCCESS(Request->Status)) + { + return Request->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, lpExeName); + if (!Header) + { + ConioUnlockConsole(Console); + return STATUS_INVALID_PARAMETER; + } + + Entry = IntGetAliasEntry(Header, lpSource); + if (!Entry) + { + ConioUnlockConsole(Console); + return STATUS_INVALID_PARAMETER; + } + + Length = (wcslen(Entry->lpTarget)+1) * sizeof(WCHAR); + if (Length > Request->Data.GetConsoleAlias.TargetBufferLength) + { + ConioUnlockConsole(Console); + return STATUS_BUFFER_TOO_SMALL; + } + + if (!Win32CsrValidateBuffer(ProcessData, lpTarget, + Request->Data.GetConsoleAlias.TargetBufferLength, 1)) + { + ConioUnlockConsole(Console); + return STATUS_ACCESS_VIOLATION; + } + + wcscpy(lpTarget, Entry->lpTarget); + Request->Data.GetConsoleAlias.BytesWritten = Length; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +CSR_API(CsrGetAllConsoleAliases) +{ + PCSRSS_CONSOLE Console; + ULONG BytesWritten; + PALIAS_HEADER Header; + + if (Request->Data.GetAllConsoleAlias.lpExeName == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + Request->Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (!NT_SUCCESS(Request->Status)) + { + return Request->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, Request->Data.GetAllConsoleAlias.lpExeName); + if (!Header) + { + ConioUnlockConsole(Console); + return STATUS_INVALID_PARAMETER; + } + + if (IntGetAllConsoleAliasesLength(Header) > Request->Data.GetAllConsoleAlias.AliasBufferLength) + { + ConioUnlockConsole(Console); + return STATUS_BUFFER_OVERFLOW; + } + + if (!Win32CsrValidateBuffer(ProcessData, + Request->Data.GetAllConsoleAlias.AliasBuffer, + Request->Data.GetAllConsoleAlias.AliasBufferLength, + 1)) + { + ConioUnlockConsole(Console); + return STATUS_ACCESS_VIOLATION; + } + + BytesWritten = IntGetAllConsoleAliases(Header, + Request->Data.GetAllConsoleAlias.AliasBuffer, + Request->Data.GetAllConsoleAlias.AliasBufferLength); + + Request->Data.GetAllConsoleAlias.BytesWritten = BytesWritten; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +CSR_API(CsrGetAllConsoleAliasesLength) +{ + PCSRSS_CONSOLE Console; + PALIAS_HEADER Header; + UINT Length; + + if (Request->Data.GetAllConsoleAliasesLength.lpExeName == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + Request->Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (!NT_SUCCESS(Request->Status)) + { + return Request->Status; + } + + Header = IntFindAliasHeader(Console->Aliases, Request->Data.GetAllConsoleAliasesLength.lpExeName); + if (!Header) + { + ConioUnlockConsole(Console); + return STATUS_INVALID_PARAMETER; + } + + Length = IntGetAllConsoleAliasesLength(Header); + Request->Data.GetAllConsoleAliasesLength.Length = Length; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +CSR_API(CsrGetConsoleAliasesExes) +{ + PCSRSS_CONSOLE Console; + UINT BytesWritten; + UINT ExesLength; + + DPRINT("CsrGetConsoleAliasesExes entered\n"); + + Request->Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (!NT_SUCCESS(Request->Status)) + { + return Request->Status; + } + + ExesLength = IntGetConsoleAliasesExesLength(Console->Aliases); + + if (ExesLength > Request->Data.GetConsoleAliasesExes.Length) + { + ConioUnlockConsole(Console); + return STATUS_BUFFER_OVERFLOW; + } + + if (Request->Data.GetConsoleAliasesExes.ExeNames == NULL) + { + ConioUnlockConsole(Console); + return STATUS_INVALID_PARAMETER; + } + + if (!Win32CsrValidateBuffer(ProcessData, + Request->Data.GetConsoleAliasesExes.ExeNames, + Request->Data.GetConsoleAliasesExes.Length, + 1)) + { + ConioUnlockConsole(Console); + return STATUS_ACCESS_VIOLATION; + } + + BytesWritten = IntGetConsoleAliasesExes(Console->Aliases, + Request->Data.GetConsoleAliasesExes.ExeNames, + Request->Data.GetConsoleAliasesExes.Length); + + Request->Data.GetConsoleAliasesExes.BytesWritten = BytesWritten; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +CSR_API(CsrGetConsoleAliasesExesLength) +{ + PCSRSS_CONSOLE Console; + DPRINT("CsrGetConsoleAliasesExesLength entered\n"); + + Request->Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Request->Status)) + { + Request->Data.GetConsoleAliasesExesLength.Length = IntGetConsoleAliasesExesLength(Console->Aliases); + ConioUnlockConsole(Console); + } + return Request->Status; +} diff --git a/reactos/deprecated/win32csr/coninput.c b/reactos/deprecated/win32csr/coninput.c new file mode 100644 index 00000000000..93a3f658ffc --- /dev/null +++ b/reactos/deprecated/win32csr/coninput.c @@ -0,0 +1,651 @@ +/* + * reactos/subsys/csrss/win32csr/conio.c + * + * Console I/O functions + * + * ReactOS Operating System + */ + +/* INCLUDES ******************************************************************/ + +#define NDEBUG +#include "w32csr.h" +#include + +/* GLOBALS *******************************************************************/ + +#define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \ + WideCharToMultiByte((Console)->CodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL) + +#define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \ + MultiByteToWideChar((Console)->CodePage, 0, (sChar), 1, (dWChar), 1) + +/* FUNCTIONS *****************************************************************/ + +CSR_API(CsrReadConsole) +{ + PLIST_ENTRY CurrentEntry; + ConsoleInput *Input; + PCHAR Buffer; + PWCHAR UnicodeBuffer; + ULONG i = 0; + ULONG nNumberOfCharsToRead, CharSize; + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrReadConsole\n"); + + CharSize = (Request->Data.ReadConsoleRequest.Unicode ? sizeof(WCHAR) : sizeof(CHAR)); + + nNumberOfCharsToRead = Request->Data.ReadConsoleRequest.NrCharactersToRead; + + Buffer = (PCHAR)Request->Data.ReadConsoleRequest.Buffer; + UnicodeBuffer = (PWCHAR)Buffer; + if (!Win32CsrValidateBuffer(ProcessData, Buffer, nNumberOfCharsToRead, CharSize)) + return STATUS_ACCESS_VIOLATION; + + if (Request->Data.ReadConsoleRequest.NrCharactersRead * sizeof(WCHAR) > nNumberOfCharsToRead * CharSize) + return STATUS_INVALID_PARAMETER; + + Status = ConioLockConsole(ProcessData, Request->Data.ReadConsoleRequest.ConsoleHandle, + &Console, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Request->Data.ReadConsoleRequest.EventHandle = ProcessData->ConsoleEvent; + + Status = STATUS_PENDING; /* we haven't read anything (yet) */ + if (Console->Mode & ENABLE_LINE_INPUT) + { + if (Console->LineBuffer == NULL) + { + /* Starting a new line */ + Console->LineMaxSize = max(256, nNumberOfCharsToRead); + Console->LineBuffer = HeapAlloc(Win32CsrApiHeap, 0, Console->LineMaxSize * sizeof(WCHAR)); + if (Console->LineBuffer == NULL) + { + Status = STATUS_NO_MEMORY; + goto done; + } + Console->LineComplete = FALSE; + Console->LineUpPressed = FALSE; + Console->LineInsertToggle = 0; + Console->LineWakeupMask = Request->Data.ReadConsoleRequest.CtrlWakeupMask; + Console->LineSize = Request->Data.ReadConsoleRequest.NrCharactersRead; + Console->LinePos = Console->LineSize; + /* pre-filling the buffer is only allowed in the Unicode API, + * so we don't need to worry about conversion */ + memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR)); + if (Console->LineSize == Console->LineMaxSize) + { + Console->LineComplete = TRUE; + Console->LinePos = 0; + } + } + + /* If we don't have a complete line yet, process the pending input */ + while (!Console->LineComplete && !IsListEmpty(&Console->InputEvents)) + { + /* remove input event from queue */ + CurrentEntry = RemoveHeadList(&Console->InputEvents); + if (IsListEmpty(&Console->InputEvents)) + { + ResetEvent(Console->ActiveEvent); + } + Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); + + /* only pay attention to key down */ + if (KEY_EVENT == Input->InputEvent.EventType + && Input->InputEvent.Event.KeyEvent.bKeyDown) + { + LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent); + Request->Data.ReadConsoleRequest.ControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState; + } + HeapFree(Win32CsrApiHeap, 0, Input); + } + + /* Check if we have a complete line to read from */ + if (Console->LineComplete) + { + while (i < nNumberOfCharsToRead && Console->LinePos != Console->LineSize) + { + WCHAR Char = Console->LineBuffer[Console->LinePos++]; + if (Request->Data.ReadConsoleRequest.Unicode) + UnicodeBuffer[i++] = Char; + else + ConsoleInputUnicodeCharToAnsiChar(Console, &Buffer[i++], &Char); + } + if (Console->LinePos == Console->LineSize) + { + /* Entire line has been read */ + HeapFree(Win32CsrApiHeap, 0, Console->LineBuffer); + Console->LineBuffer = NULL; + } + Status = STATUS_SUCCESS; + } + } + else + { + /* Character input */ + while (i < nNumberOfCharsToRead && !IsListEmpty(&Console->InputEvents)) + { + /* remove input event from queue */ + CurrentEntry = RemoveHeadList(&Console->InputEvents); + if (IsListEmpty(&Console->InputEvents)) + { + ResetEvent(Console->ActiveEvent); + } + Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); + + /* only pay attention to valid ascii chars, on key down */ + if (KEY_EVENT == Input->InputEvent.EventType + && Input->InputEvent.Event.KeyEvent.bKeyDown + && Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0') + { + WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar; + if (Request->Data.ReadConsoleRequest.Unicode) + UnicodeBuffer[i++] = Char; + else + ConsoleInputUnicodeCharToAnsiChar(Console, &Buffer[i++], &Char); + Status = STATUS_SUCCESS; /* did read something */ + } + HeapFree(Win32CsrApiHeap, 0, Input); + } + } +done: + Request->Data.ReadConsoleRequest.NrCharactersRead = i; + ConioUnlockConsole(Console); + + return Status; +} + +static VOID FASTCALL +ConioInputEventToAnsi(PCSRSS_CONSOLE Console, PINPUT_RECORD InputEvent) +{ + if (InputEvent->EventType == KEY_EVENT) + { + WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar; + InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0; + ConsoleInputUnicodeCharToAnsiChar(Console, + &InputEvent->Event.KeyEvent.uChar.AsciiChar, + &UnicodeChar); + } +} + +static NTSTATUS FASTCALL +ConioProcessChar(PCSRSS_CONSOLE Console, + PINPUT_RECORD InputEvent) +{ + ConsoleInput *ConInRec; + + /* Check for pause or unpause */ + if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown) + { + WORD vk = InputEvent->Event.KeyEvent.wVirtualKeyCode; + if (!(Console->PauseFlags & PAUSED_FROM_KEYBOARD)) + { + DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState; + if (Console->Mode & ENABLE_LINE_INPUT && + (vk == VK_PAUSE || (vk == 'S' && + (cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && + !(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))))) + { + ConioPause(Console, PAUSED_FROM_KEYBOARD); + return STATUS_SUCCESS; + } + } + else + { + if ((vk < VK_SHIFT || vk > VK_CAPITAL) && vk != VK_LWIN && + vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL) + { + ConioUnpause(Console, PAUSED_FROM_KEYBOARD); + return STATUS_SUCCESS; + } + } + } + + /* add event to the queue */ + ConInRec = RtlAllocateHeap(Win32CsrApiHeap, 0, sizeof(ConsoleInput)); + if (ConInRec == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + ConInRec->InputEvent = *InputEvent; + InsertTailList(&Console->InputEvents, &ConInRec->ListEntry); + SetEvent(Console->ActiveEvent); + return STATUS_SUCCESS; +} + +static DWORD FASTCALL +ConioGetShiftState(PBYTE KeyState) +{ + DWORD ssOut = 0; + + if (KeyState[VK_CAPITAL] & 1) + ssOut |= CAPSLOCK_ON; + + if (KeyState[VK_NUMLOCK] & 1) + ssOut |= NUMLOCK_ON; + + if (KeyState[VK_SCROLL] & 1) + ssOut |= SCROLLLOCK_ON; + + if (KeyState[VK_SHIFT] & 0x80) + ssOut |= SHIFT_PRESSED; + + if (KeyState[VK_LCONTROL] & 0x80) + ssOut |= LEFT_CTRL_PRESSED; + if (KeyState[VK_RCONTROL] & 0x80) + ssOut |= RIGHT_CTRL_PRESSED; + + if (KeyState[VK_LMENU] & 0x80) + ssOut |= LEFT_ALT_PRESSED; + if (KeyState[VK_RMENU] & 0x80) + ssOut |= RIGHT_ALT_PRESSED; + + return ssOut; +} + +VOID WINAPI +ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode) +{ + static BYTE KeyState[256] = { 0 }; + /* MSDN mentions that you should use the last virtual key code received + * when putting a virtual key identity to a WM_CHAR message since multiple + * or translated keys may be involved. */ + static UINT LastVirtualKey = 0; + DWORD ShiftState; + UINT RepeatCount; + WCHAR UnicodeChar; + UINT VirtualKeyCode; + UINT VirtualScanCode; + BOOL Down = FALSE; + INPUT_RECORD er; + BOOLEAN Fake; // synthesized, not a real event + BOOLEAN NotChar; // message should not be used to return a character + + RepeatCount = 1; + VirtualScanCode = (msg->lParam >> 16) & 0xff; + Down = msg->message == WM_KEYDOWN || msg->message == WM_CHAR || + msg->message == WM_SYSKEYDOWN || msg->message == WM_SYSCHAR; + + GetKeyboardState(KeyState); + ShiftState = ConioGetShiftState(KeyState); + + if (msg->message == WM_CHAR || msg->message == WM_SYSCHAR) + { + VirtualKeyCode = LastVirtualKey; + UnicodeChar = msg->wParam; + } + else + { + WCHAR Chars[2]; + INT RetChars = 0; + + VirtualKeyCode = msg->wParam; + RetChars = ToUnicodeEx(VirtualKeyCode, + VirtualScanCode, + KeyState, + Chars, + 2, + 0, + 0); + UnicodeChar = (1 == RetChars ? Chars[0] : 0); + } + + er.EventType = KEY_EVENT; + er.Event.KeyEvent.bKeyDown = Down; + er.Event.KeyEvent.wRepeatCount = RepeatCount; + er.Event.KeyEvent.uChar.UnicodeChar = UnicodeChar; + er.Event.KeyEvent.dwControlKeyState = ShiftState; + er.Event.KeyEvent.wVirtualKeyCode = VirtualKeyCode; + er.Event.KeyEvent.wVirtualScanCode = VirtualScanCode; + + if (TextMode) + { + if (0 != (ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) + && VK_TAB == VirtualKeyCode) + { + if (Down) + { + TuiSwapConsole(ShiftState & SHIFT_PRESSED ? -1 : 1); + } + + return; + } + else if (VK_MENU == VirtualKeyCode && ! Down) + { + if (TuiSwapConsole(0)) + { + return; + } + } + } + else + { + if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyState[VK_MENU] & 0x80) && + (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE)) + { + DefWindowProcW( msg->hwnd, msg->message, msg->wParam, msg->lParam); + return; + } + } + + if (NULL == Console) + { + DPRINT1("No Active Console!\n"); + return; + } + + Fake = UnicodeChar && + (msg->message != WM_CHAR && msg->message != WM_SYSCHAR && + msg->message != WM_KEYUP && msg->message != WM_SYSKEYUP); + NotChar = (msg->message != WM_CHAR && msg->message != WM_SYSCHAR); + if (NotChar) + LastVirtualKey = msg->wParam; + + DPRINT ("csrss: %s %s %s %s %02x %02x '%lc' %04x\n", + Down ? "down" : "up ", + (msg->message == WM_CHAR || msg->message == WM_SYSCHAR) ? + "char" : "key ", + Fake ? "fake" : "real", + NotChar ? "notc" : "char", + VirtualScanCode, + VirtualKeyCode, + (UnicodeChar >= L' ') ? UnicodeChar : L'.', + ShiftState); + + if (Fake) + return; + + /* process Ctrl-C and Ctrl-Break */ + if (Console->Mode & ENABLE_PROCESSED_INPUT && + er.Event.KeyEvent.bKeyDown && + ((er.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) || + (er.Event.KeyEvent.wVirtualKeyCode == 'C')) && + (er.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) || KeyState[VK_CONTROL] & 0x80)) + { + PCSR_PROCESS current; + PLIST_ENTRY current_entry; + DPRINT1("Console_Api Ctrl-C\n"); + current_entry = Console->ProcessList.Flink; + while (current_entry != &Console->ProcessList) + { + current = CONTAINING_RECORD(current_entry, CSR_PROCESS, ConsoleLink); + current_entry = current_entry->Flink; + ConioConsoleCtrlEvent((DWORD)CTRL_C_EVENT, current); + } + if (Console->LineBuffer && !Console->LineComplete) + { + /* Line input is in progress; end it */ + Console->LinePos = Console->LineSize = 0; + Console->LineComplete = TRUE; + } + return; + } + + if (0 != (er.Event.KeyEvent.dwControlKeyState + & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) + && (VK_UP == er.Event.KeyEvent.wVirtualKeyCode + || VK_DOWN == er.Event.KeyEvent.wVirtualKeyCode)) + { + if (er.Event.KeyEvent.bKeyDown) + { + /* scroll up or down */ + if (VK_UP == er.Event.KeyEvent.wVirtualKeyCode) + { + /* only scroll up if there is room to scroll up into */ + if (Console->ActiveBuffer->CurrentY != Console->ActiveBuffer->MaxY - 1) + { + Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY + + Console->ActiveBuffer->MaxY - 1) % + Console->ActiveBuffer->MaxY; + Console->ActiveBuffer->CurrentY++; + } + } + else + { + /* only scroll down if there is room to scroll down into */ + if (Console->ActiveBuffer->CurrentY != 0) + { + Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY + 1) % + Console->ActiveBuffer->MaxY; + Console->ActiveBuffer->CurrentY--; + } + } + ConioDrawConsole(Console); + } + return; + } + ConioProcessChar(Console, &er); +} + +CSR_API(CsrReadInputEvent) +{ + PLIST_ENTRY CurrentEntry; + PCSRSS_CONSOLE Console; + NTSTATUS Status; + BOOLEAN Done = FALSE; + ConsoleInput *Input; + + DPRINT("CsrReadInputEvent\n"); + + Request->Data.ReadInputRequest.Event = ProcessData->ConsoleEvent; + + Status = ConioLockConsole(ProcessData, Request->Data.ReadInputRequest.ConsoleHandle, &Console, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + /* only get input if there is any */ + CurrentEntry = Console->InputEvents.Flink; + while (CurrentEntry != &Console->InputEvents) + { + Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); + CurrentEntry = CurrentEntry->Flink; + + if (Done) + { + Request->Data.ReadInputRequest.MoreEvents = TRUE; + break; + } + + RemoveEntryList(&Input->ListEntry); + + if (!Done) + { + Request->Data.ReadInputRequest.Input = Input->InputEvent; + if (Request->Data.ReadInputRequest.Unicode == FALSE) + { + ConioInputEventToAnsi(Console, &Request->Data.ReadInputRequest.Input); + } + Done = TRUE; + } + + HeapFree(Win32CsrApiHeap, 0, Input); + } + + if (Done) + Status = STATUS_SUCCESS; + else + Status = STATUS_PENDING; + + if (IsListEmpty(&Console->InputEvents)) + { + ResetEvent(Console->ActiveEvent); + } + + ConioUnlockConsole(Console); + + return Status; +} + +CSR_API(CsrFlushInputBuffer) +{ + PLIST_ENTRY CurrentEntry; + PCSRSS_CONSOLE Console; + ConsoleInput* Input; + NTSTATUS Status; + + DPRINT("CsrFlushInputBuffer\n"); + + Status = ConioLockConsole(ProcessData, + Request->Data.FlushInputBufferRequest.ConsoleInput, + &Console, + GENERIC_WRITE); + if(! NT_SUCCESS(Status)) + { + return Status; + } + + /* Discard all entries in the input event queue */ + while (!IsListEmpty(&Console->InputEvents)) + { + CurrentEntry = RemoveHeadList(&Console->InputEvents); + Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry); + /* Destroy the event */ + HeapFree(Win32CsrApiHeap, 0, Input); + } + ResetEvent(Console->ActiveEvent); + + ConioUnlockConsole(Console); + + return STATUS_SUCCESS; +} + +CSR_API(CsrGetNumberOfConsoleInputEvents) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PLIST_ENTRY CurrentItem; + DWORD NumEvents; + + DPRINT("CsrGetNumberOfConsoleInputEvents\n"); + + Status = ConioLockConsole(ProcessData, Request->Data.GetNumInputEventsRequest.ConsoleHandle, &Console, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + CurrentItem = Console->InputEvents.Flink; + NumEvents = 0; + + /* If there are any events ... */ + while (CurrentItem != &Console->InputEvents) + { + CurrentItem = CurrentItem->Flink; + NumEvents++; + } + + ConioUnlockConsole(Console); + + Request->Data.GetNumInputEventsRequest.NumInputEvents = NumEvents; + + return STATUS_SUCCESS; +} + +CSR_API(CsrPeekConsoleInput) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + DWORD Length; + PLIST_ENTRY CurrentItem; + PINPUT_RECORD InputRecord; + ConsoleInput* Item; + UINT NumItems; + + DPRINT("CsrPeekConsoleInput\n"); + + Status = ConioLockConsole(ProcessData, Request->Data.GetNumInputEventsRequest.ConsoleHandle, &Console, GENERIC_READ); + if(! NT_SUCCESS(Status)) + { + return Status; + } + + InputRecord = Request->Data.PeekConsoleInputRequest.InputRecord; + Length = Request->Data.PeekConsoleInputRequest.Length; + + if (!Win32CsrValidateBuffer(ProcessData, InputRecord, Length, sizeof(INPUT_RECORD))) + { + ConioUnlockConsole(Console); + return STATUS_ACCESS_VIOLATION; + } + + NumItems = 0; + + if (! IsListEmpty(&Console->InputEvents)) + { + CurrentItem = Console->InputEvents.Flink; + + while (CurrentItem != &Console->InputEvents && NumItems < Length) + { + Item = CONTAINING_RECORD(CurrentItem, ConsoleInput, ListEntry); + + ++NumItems; + *InputRecord = Item->InputEvent; + + if (Request->Data.PeekConsoleInputRequest.Unicode == FALSE) + { + ConioInputEventToAnsi(Console, InputRecord); + } + + InputRecord++; + CurrentItem = CurrentItem->Flink; + } + } + + ConioUnlockConsole(Console); + + Request->Data.PeekConsoleInputRequest.Length = NumItems; + + return STATUS_SUCCESS; +} + +CSR_API(CsrWriteConsoleInput) +{ + PINPUT_RECORD InputRecord; + PCSRSS_CONSOLE Console; + NTSTATUS Status; + DWORD Length; + DWORD i; + + DPRINT("CsrWriteConsoleInput\n"); + + Status = ConioLockConsole(ProcessData, Request->Data.WriteConsoleInputRequest.ConsoleHandle, &Console, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + InputRecord = Request->Data.WriteConsoleInputRequest.InputRecord; + Length = Request->Data.WriteConsoleInputRequest.Length; + + if (!Win32CsrValidateBuffer(ProcessData, InputRecord, Length, sizeof(INPUT_RECORD))) + { + ConioUnlockConsole(Console); + return STATUS_ACCESS_VIOLATION; + } + + for (i = 0; i < Length && NT_SUCCESS(Status); i++) + { + if (!Request->Data.WriteConsoleInputRequest.Unicode && + InputRecord->EventType == KEY_EVENT) + { + CHAR AsciiChar = InputRecord->Event.KeyEvent.uChar.AsciiChar; + ConsoleInputAnsiCharToUnicodeChar(Console, + &InputRecord->Event.KeyEvent.uChar.UnicodeChar, + &AsciiChar); + } + Status = ConioProcessChar(Console, InputRecord++); + } + + ConioUnlockConsole(Console); + + Request->Data.WriteConsoleInputRequest.Length = i; + + return Status; +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/conio.h b/reactos/deprecated/win32csr/conio.h new file mode 100644 index 00000000000..6f1b81a0b2e --- /dev/null +++ b/reactos/deprecated/win32csr/conio.h @@ -0,0 +1,237 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/include/conio.h + * PURPOSE: CSRSS internal console I/O interface + */ + +#pragma once + +#include "api.h" +#include "win32csr.h" + +#define CSR_DEFAULT_CURSOR_SIZE 25 + +/* Object type magic numbers */ + +#define CONIO_CONSOLE_MAGIC 0x00000001 +#define CONIO_SCREEN_BUFFER_MAGIC 0x00000002 + +/************************************************************************ + * Screen buffer structure represents the win32 screen buffer object. * + * Internally, the portion of the buffer being shown CAN loop past the * + * bottom of the virtual buffer and wrap around to the top. Win32 does * + * not do this. I decided to do this because it eliminates the need to * + * do a massive memcpy() to scroll the contents of the buffer up to * + * scroll the screen on output, instead I just shift down the position * + * to be displayed, and let it wrap around to the top again. * + * The VirtualY member keeps track of the top Y coord that win32 * + * clients THINK is currently being displayed, because they think that * + * when the display reaches the bottom of the buffer and another line * + * being printed causes another line to scroll down, that the buffer IS * + * memcpy()'s up, and the bottom of the buffer is still displayed, but * + * internally, I just wrap back to the top of the buffer. * + ***********************************************************************/ + +typedef struct tagCSRSS_SCREEN_BUFFER +{ + Object_t Header; /* Object header */ + BYTE *Buffer; /* pointer to screen buffer */ + USHORT MaxX, MaxY; /* size of the entire scrollback buffer */ + USHORT ShowX, ShowY; /* beginning offset for the actual display area */ + ULONG CurrentX; /* Current X cursor position */ + ULONG CurrentY; /* Current Y cursor position */ + WORD DefaultAttrib; /* default char attribute */ + USHORT VirtualY; /* top row of buffer being displayed, reported to callers */ + CONSOLE_CURSOR_INFO CursorInfo; + USHORT Mode; + LIST_ENTRY ListEntry; /* entry in console's list of buffers */ +} CSRSS_SCREEN_BUFFER, *PCSRSS_SCREEN_BUFFER; + +typedef struct tagCSRSS_CONSOLE *PCSRSS_CONSOLE; + +typedef struct tagCSRSS_CONSOLE_VTBL +{ + VOID (WINAPI *InitScreenBuffer)(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer); + VOID (WINAPI *WriteStream)(PCSRSS_CONSOLE Console, SMALL_RECT *Block, LONG CursorStartX, LONG CursorStartY, + UINT ScrolledLines, CHAR *Buffer, UINT Length); + VOID (WINAPI *DrawRegion)(PCSRSS_CONSOLE Console, SMALL_RECT *Region); + BOOL (WINAPI *SetCursorInfo)(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer); + BOOL (WINAPI *SetScreenInfo)(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer, + UINT OldCursorX, UINT OldCursorY); + BOOL (WINAPI *UpdateScreenInfo)(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer); + BOOL (WINAPI *ChangeTitle)(PCSRSS_CONSOLE Console); + VOID (WINAPI *CleanupConsole)(PCSRSS_CONSOLE Console); + BOOL (WINAPI *ChangeIcon)(PCSRSS_CONSOLE Console, HICON hWindowIcon); + NTSTATUS (WINAPI *ResizeBuffer)(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer, COORD Size); +} CSRSS_CONSOLE_VTBL, *PCSRSS_CONSOLE_VTBL; + +typedef struct tagCSRSS_CONSOLE +{ + Object_t Header; /* Object header */ + LONG ReferenceCount; + CRITICAL_SECTION Lock; + PCSRSS_CONSOLE Prev, Next; /* Next and Prev consoles in console wheel */ + HANDLE ActiveEvent; + LIST_ENTRY InputEvents; /* List head for input event queue */ + PWCHAR LineBuffer; /* current line being input, in line buffered mode */ + WORD LineMaxSize; /* maximum size of line in characters (including CR+LF) */ + WORD LineSize; /* current size of line */ + WORD LinePos; /* current position within line */ + BOOLEAN LineComplete; /* user pressed enter, ready to send back to client */ + BOOLEAN LineUpPressed; + BOOLEAN LineInsertToggle; /* replace character over cursor instead of inserting */ + ULONG LineWakeupMask; /* bitmap of which control characters will end line input */ + LIST_ENTRY HistoryBuffers; + WORD HistoryBufferSize; /* size for newly created history buffers */ + WORD NumberOfHistoryBuffers; /* maximum number of history buffers allowed */ + BOOLEAN HistoryNoDup; /* remove old duplicate history entries */ + LIST_ENTRY BufferList; /* List of all screen buffers for this console */ + PCSRSS_SCREEN_BUFFER ActiveBuffer; /* Pointer to currently active screen buffer */ + WORD Mode; /* Console mode flags */ + UNICODE_STRING Title; /* Title of console */ + DWORD HardwareState; /* _GDI_MANAGED, _DIRECT */ + HWND hWindow; + COORD Size; + PVOID PrivateData; + UINT CodePage; + UINT OutputCodePage; + PCSRSS_CONSOLE_VTBL Vtbl; + LIST_ENTRY ProcessList; + struct tagALIAS_HEADER *Aliases; + CONSOLE_SELECTION_INFO Selection; + BYTE PauseFlags; + HANDLE UnpauseEvent; +} CSRSS_CONSOLE; + +typedef struct ConsoleInput_t +{ + LIST_ENTRY ListEntry; + INPUT_RECORD InputEvent; +} ConsoleInput; + +/* CONSOLE_SELECTION_INFO dwFlags values */ +#define CONSOLE_NO_SELECTION 0x0 +#define CONSOLE_SELECTION_IN_PROGRESS 0x1 +#define CONSOLE_SELECTION_NOT_EMPTY 0x2 +#define CONSOLE_MOUSE_SELECTION 0x4 +#define CONSOLE_MOUSE_DOWN 0x8 +/* HistoryFlags values */ +#define HISTORY_NO_DUP_FLAG 0x1 + +/* PauseFlags values (internal only) */ +#define PAUSED_FROM_KEYBOARD 0x1 +#define PAUSED_FROM_SCROLLBAR 0x2 +#define PAUSED_FROM_SELECTION 0x4 + +#define ConioInitScreenBuffer(Console, Buff) (Console)->Vtbl->InitScreenBuffer((Console), (Buff)) +#define ConioDrawRegion(Console, Region) (Console)->Vtbl->DrawRegion((Console), (Region)) +#define ConioWriteStream(Console, Block, CurStartX, CurStartY, ScrolledLines, Buffer, Length) \ + (Console)->Vtbl->WriteStream((Console), (Block), (CurStartX), (CurStartY), \ + (ScrolledLines), (Buffer), (Length)) +#define ConioSetCursorInfo(Console, Buff) (Console)->Vtbl->SetCursorInfo((Console), (Buff)) +#define ConioSetScreenInfo(Console, Buff, OldCursorX, OldCursorY) \ + (Console)->Vtbl->SetScreenInfo((Console), (Buff), (OldCursorX), (OldCursorY)) +#define ConioUpdateScreenInfo(Console, Buff) \ + (Console)->Vtbl->UpdateScreenInfo(Console, Buff) +#define ConioChangeTitle(Console) (Console)->Vtbl->ChangeTitle(Console) +#define ConioCleanupConsole(Console) (Console)->Vtbl->CleanupConsole(Console) +#define ConioChangeIcon(Console, hWindowIcon) (Console)->Vtbl->ChangeIcon(Console, hWindowIcon) +#define ConioResizeBuffer(Console, Buff, Size) (Console)->Vtbl->ResizeBuffer(Console, Buff, Size) + +/* console.c */ +NTSTATUS FASTCALL ConioConsoleFromProcessData(PCSR_PROCESS ProcessData, PCSRSS_CONSOLE *Console); +VOID WINAPI ConioDeleteConsole(Object_t *Object); +VOID WINAPI CsrInitConsoleSupport(VOID); +VOID FASTCALL ConioPause(PCSRSS_CONSOLE Console, UINT Flags); +VOID FASTCALL ConioUnpause(PCSRSS_CONSOLE Console, UINT Flags); +VOID FASTCALL ConioConsoleCtrlEvent(DWORD Event, PCSR_PROCESS ProcessData); +VOID FASTCALL ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, + DWORD Timeout); +CSR_API(CsrAllocConsole); +CSR_API(CsrFreeConsole); +CSR_API(CsrSetConsoleMode); +CSR_API(CsrGetConsoleMode); +CSR_API(CsrSetTitle); +CSR_API(CsrGetTitle); +CSR_API(CsrHardwareStateProperty); +CSR_API(CsrGetConsoleWindow); +CSR_API(CsrSetConsoleIcon); +CSR_API(CsrGetConsoleCodePage); +CSR_API(CsrSetConsoleCodePage); +CSR_API(CsrGetConsoleOutputCodePage); +CSR_API(CsrSetConsoleOutputCodePage); +CSR_API(CsrGetProcessList); +CSR_API(CsrGenerateCtrlEvent); +CSR_API(CsrGetConsoleSelectionInfo); + +/* coninput.c */ +#define ConioLockConsole(ProcessData, Handle, Ptr, Access) \ + Win32CsrLockObject((ProcessData), (Handle), (Object_t **)(Ptr), Access, CONIO_CONSOLE_MAGIC) +#define ConioUnlockConsole(Console) \ + Win32CsrUnlockObject((Object_t *) Console) +void WINAPI ConioProcessKey(MSG *msg, PCSRSS_CONSOLE Console, BOOL TextMode); +CSR_API(CsrReadConsole); +CSR_API(CsrReadInputEvent); +CSR_API(CsrFlushInputBuffer); +CSR_API(CsrGetNumberOfConsoleInputEvents); +CSR_API(CsrPeekConsoleInput); +CSR_API(CsrWriteConsoleInput); + +/* conoutput.c */ +#define ConioRectHeight(Rect) \ + (((Rect)->Top) > ((Rect)->Bottom) ? 0 : ((Rect)->Bottom) - ((Rect)->Top) + 1) +#define ConioRectWidth(Rect) \ + (((Rect)->Left) > ((Rect)->Right) ? 0 : ((Rect)->Right) - ((Rect)->Left) + 1) +#define ConioLockScreenBuffer(ProcessData, Handle, Ptr, Access) \ + Win32CsrLockObject((ProcessData), (Handle), (Object_t **)(Ptr), Access, CONIO_SCREEN_BUFFER_MAGIC) +#define ConioUnlockScreenBuffer(Buff) \ + Win32CsrUnlockObject((Object_t *) Buff) +PBYTE FASTCALL ConioCoordToPointer(PCSRSS_SCREEN_BUFFER Buf, ULONG X, ULONG Y); +VOID FASTCALL ConioDrawConsole(PCSRSS_CONSOLE Console); +NTSTATUS FASTCALL ConioWriteConsole(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff, + CHAR *Buffer, DWORD Length, BOOL Attrib); +NTSTATUS FASTCALL CsrInitConsoleScreenBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buffer); +VOID WINAPI ConioDeleteScreenBuffer(PCSRSS_SCREEN_BUFFER Buffer); +DWORD FASTCALL ConioEffectiveCursorSize(PCSRSS_CONSOLE Console, DWORD Scale); + +CSR_API(CsrWriteConsole); +CSR_API(CsrGetScreenBufferInfo); +CSR_API(CsrSetCursor); +CSR_API(CsrWriteConsoleOutputChar); +CSR_API(CsrFillOutputChar); +CSR_API(CsrWriteConsoleOutputAttrib); +CSR_API(CsrFillOutputAttrib); +CSR_API(CsrGetCursorInfo); +CSR_API(CsrSetCursorInfo); +CSR_API(CsrSetTextAttrib); +CSR_API(CsrCreateScreenBuffer); +CSR_API(CsrSetScreenBuffer); +CSR_API(CsrWriteConsoleOutput); +CSR_API(CsrScrollConsoleScreenBuffer); +CSR_API(CsrReadConsoleOutputChar); +CSR_API(CsrReadConsoleOutputAttrib); +CSR_API(CsrReadConsoleOutput); +CSR_API(CsrSetScreenBufferSize); + +/* alias.c */ +VOID IntDeleteAllAliases(struct tagALIAS_HEADER *RootHeader); +CSR_API(CsrAddConsoleAlias); +CSR_API(CsrGetConsoleAlias); +CSR_API(CsrGetAllConsoleAliases); +CSR_API(CsrGetAllConsoleAliasesLength); +CSR_API(CsrGetConsoleAliasesExes); +CSR_API(CsrGetConsoleAliasesExesLength); + +/* lineinput.c */ +struct tagHISTORY_BUFFER; +VOID FASTCALL HistoryDeleteBuffer(struct tagHISTORY_BUFFER *Hist); +CSR_API(CsrGetCommandHistoryLength); +CSR_API(CsrGetCommandHistory); +CSR_API(CsrExpungeCommandHistory); +CSR_API(CsrSetHistoryNumberCommands); +CSR_API(CsrGetHistoryInfo); +CSR_API(CsrSetHistoryInfo); +VOID FASTCALL LineInputKeyDown(PCSRSS_CONSOLE Console, KEY_EVENT_RECORD *KeyEvent); + +/* EOF */ diff --git a/reactos/deprecated/win32csr/conoutput.c b/reactos/deprecated/win32csr/conoutput.c new file mode 100644 index 00000000000..5415a601ab9 --- /dev/null +++ b/reactos/deprecated/win32csr/conoutput.c @@ -0,0 +1,1457 @@ +/* + * reactos/subsys/csrss/win32csr/conio.c + * + * Console I/O functions + * + * ReactOS Operating System + */ + +/* INCLUDES ******************************************************************/ + +#define NDEBUG +#include "w32csr.h" +#include + +/* GLOBALS *******************************************************************/ + +#define ConioInitRect(Rect, top, left, bottom, right) \ + ((Rect)->Top) = top; \ + ((Rect)->Left) = left; \ + ((Rect)->Bottom) = bottom; \ + ((Rect)->Right) = right + +#define ConioIsRectEmpty(Rect) \ + (((Rect)->Left > (Rect)->Right) || ((Rect)->Top > (Rect)->Bottom)) + +#define ConsoleUnicodeCharToAnsiChar(Console, dChar, sWChar) \ + WideCharToMultiByte((Console)->OutputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL) + +#define ConsoleAnsiCharToUnicodeChar(Console, dWChar, sChar) \ + MultiByteToWideChar((Console)->OutputCodePage, 0, (sChar), 1, (dWChar), 1) + +/* FUNCTIONS *****************************************************************/ + +PBYTE FASTCALL +ConioCoordToPointer(PCSRSS_SCREEN_BUFFER Buff, ULONG X, ULONG Y) +{ + return &Buff->Buffer[2 * (((Y + Buff->VirtualY) % Buff->MaxY) * Buff->MaxX + X)]; +} + +static VOID FASTCALL +ClearLineBuffer(PCSRSS_SCREEN_BUFFER Buff) +{ + PBYTE Ptr = ConioCoordToPointer(Buff, 0, Buff->CurrentY); + UINT Pos; + + for (Pos = 0; Pos < Buff->MaxX; Pos++) + { + /* Fill the cell */ + *Ptr++ = ' '; + *Ptr++ = Buff->DefaultAttrib; + } +} + +NTSTATUS FASTCALL +CsrInitConsoleScreenBuffer(PCSRSS_CONSOLE Console, + PCSRSS_SCREEN_BUFFER Buffer) +{ + DPRINT("CsrInitConsoleScreenBuffer Size X %d Size Y %d\n", Buffer->MaxX, Buffer->MaxY); + + Buffer->Header.Type = CONIO_SCREEN_BUFFER_MAGIC; + Buffer->Header.Console = Console; + Buffer->Header.HandleCount = 0; + Buffer->ShowX = 0; + Buffer->ShowY = 0; + Buffer->VirtualY = 0; + Buffer->Buffer = HeapAlloc(Win32CsrApiHeap, HEAP_ZERO_MEMORY, Buffer->MaxX * Buffer->MaxY * 2); + if (NULL == Buffer->Buffer) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + ConioInitScreenBuffer(Console, Buffer); + /* initialize buffer to be empty with default attributes */ + for (Buffer->CurrentY = 0 ; Buffer->CurrentY < Buffer->MaxY; Buffer->CurrentY++) + { + ClearLineBuffer(Buffer); + } + Buffer->Mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; + Buffer->CurrentX = 0; + Buffer->CurrentY = 0; + + InsertHeadList(&Console->BufferList, &Buffer->ListEntry); + return STATUS_SUCCESS; +} + +static VOID FASTCALL +ConioNextLine(PCSRSS_SCREEN_BUFFER Buff, SMALL_RECT *UpdateRect, UINT *ScrolledLines) +{ + /* If we hit bottom, slide the viewable screen */ + if (++Buff->CurrentY == Buff->MaxY) + { + Buff->CurrentY--; + if (++Buff->VirtualY == Buff->MaxY) + { + Buff->VirtualY = 0; + } + (*ScrolledLines)++; + ClearLineBuffer(Buff); + if (UpdateRect->Top != 0) + { + UpdateRect->Top--; + } + } + UpdateRect->Left = 0; + UpdateRect->Right = Buff->MaxX - 1; + UpdateRect->Bottom = Buff->CurrentY; +} + +NTSTATUS FASTCALL +ConioWriteConsole(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff, + CHAR *Buffer, DWORD Length, BOOL Attrib) +{ + UINT i; + PBYTE Ptr; + SMALL_RECT UpdateRect; + LONG CursorStartX, CursorStartY; + UINT ScrolledLines; + + CursorStartX = Buff->CurrentX; + CursorStartY = Buff->CurrentY; + UpdateRect.Left = Buff->MaxX; + UpdateRect.Top = Buff->CurrentY; + UpdateRect.Right = -1; + UpdateRect.Bottom = Buff->CurrentY; + ScrolledLines = 0; + + for (i = 0; i < Length; i++) + { + if (Buff->Mode & ENABLE_PROCESSED_OUTPUT) + { + /* --- LF --- */ + if (Buffer[i] == '\n') + { + Buff->CurrentX = 0; + ConioNextLine(Buff, &UpdateRect, &ScrolledLines); + continue; + } + /* --- BS --- */ + else if (Buffer[i] == '\b') + { + /* Only handle BS if we're not on the first pos of the first line */ + if (0 != Buff->CurrentX || 0 != Buff->CurrentY) + { + if (0 == Buff->CurrentX) + { + /* slide virtual position up */ + Buff->CurrentX = Buff->MaxX - 1; + Buff->CurrentY--; + UpdateRect.Top = min(UpdateRect.Top, (LONG)Buff->CurrentY); + } + else + { + Buff->CurrentX--; + } + Ptr = ConioCoordToPointer(Buff, Buff->CurrentX, Buff->CurrentY); + Ptr[0] = ' '; + Ptr[1] = Buff->DefaultAttrib; + UpdateRect.Left = min(UpdateRect.Left, (LONG) Buff->CurrentX); + UpdateRect.Right = max(UpdateRect.Right, (LONG) Buff->CurrentX); + } + continue; + } + /* --- CR --- */ + else if (Buffer[i] == '\r') + { + Buff->CurrentX = 0; + UpdateRect.Left = min(UpdateRect.Left, (LONG) Buff->CurrentX); + UpdateRect.Right = max(UpdateRect.Right, (LONG) Buff->CurrentX); + continue; + } + /* --- TAB --- */ + else if (Buffer[i] == '\t') + { + UINT EndX; + + UpdateRect.Left = min(UpdateRect.Left, (LONG)Buff->CurrentX); + EndX = (Buff->CurrentX + 8) & ~7; + if (EndX > Buff->MaxX) + { + EndX = Buff->MaxX; + } + Ptr = ConioCoordToPointer(Buff, Buff->CurrentX, Buff->CurrentY); + while (Buff->CurrentX < EndX) + { + *Ptr++ = ' '; + *Ptr++ = Buff->DefaultAttrib; + Buff->CurrentX++; + } + UpdateRect.Right = max(UpdateRect.Right, (LONG) Buff->CurrentX - 1); + if (Buff->CurrentX == Buff->MaxX) + { + if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT) + { + Buff->CurrentX = 0; + ConioNextLine(Buff, &UpdateRect, &ScrolledLines); + } + else + { + Buff->CurrentX--; + } + } + continue; + } + } + UpdateRect.Left = min(UpdateRect.Left, (LONG)Buff->CurrentX); + UpdateRect.Right = max(UpdateRect.Right, (LONG) Buff->CurrentX); + Ptr = ConioCoordToPointer(Buff, Buff->CurrentX, Buff->CurrentY); + Ptr[0] = Buffer[i]; + if (Attrib) + { + Ptr[1] = Buff->DefaultAttrib; + } + Buff->CurrentX++; + if (Buff->CurrentX == Buff->MaxX) + { + if (Buff->Mode & ENABLE_WRAP_AT_EOL_OUTPUT) + { + Buff->CurrentX = 0; + ConioNextLine(Buff, &UpdateRect, &ScrolledLines); + } + else + { + Buff->CurrentX = CursorStartX; + } + } + } + + if (! ConioIsRectEmpty(&UpdateRect) && Buff == Console->ActiveBuffer) + { + ConioWriteStream(Console, &UpdateRect, CursorStartX, CursorStartY, ScrolledLines, + Buffer, Length); + } + + return STATUS_SUCCESS; +} + +__inline BOOLEAN ConioGetIntersection( + SMALL_RECT *Intersection, + SMALL_RECT *Rect1, + SMALL_RECT *Rect2) +{ + if (ConioIsRectEmpty(Rect1) || + (ConioIsRectEmpty(Rect2)) || + (Rect1->Top > Rect2->Bottom) || + (Rect1->Left > Rect2->Right) || + (Rect1->Bottom < Rect2->Top) || + (Rect1->Right < Rect2->Left)) + { + /* The rectangles do not intersect */ + ConioInitRect(Intersection, 0, -1, 0, -1); + return FALSE; + } + + ConioInitRect(Intersection, + max(Rect1->Top, Rect2->Top), + max(Rect1->Left, Rect2->Left), + min(Rect1->Bottom, Rect2->Bottom), + min(Rect1->Right, Rect2->Right)); + + return TRUE; +} + +__inline BOOLEAN ConioGetUnion( + SMALL_RECT *Union, + SMALL_RECT *Rect1, + SMALL_RECT *Rect2) +{ + if (ConioIsRectEmpty(Rect1)) + { + if (ConioIsRectEmpty(Rect2)) + { + ConioInitRect(Union, 0, -1, 0, -1); + return FALSE; + } + else + { + *Union = *Rect2; + } + } + else if (ConioIsRectEmpty(Rect2)) + { + *Union = *Rect1; + } + else + { + ConioInitRect(Union, + min(Rect1->Top, Rect2->Top), + min(Rect1->Left, Rect2->Left), + max(Rect1->Bottom, Rect2->Bottom), + max(Rect1->Right, Rect2->Right)); + } + + return TRUE; +} + +/* Move from one rectangle to another. We must be careful about the order that + * this is done, to avoid overwriting parts of the source before they are moved. */ +static VOID FASTCALL +ConioMoveRegion(PCSRSS_SCREEN_BUFFER ScreenBuffer, + SMALL_RECT *SrcRegion, + SMALL_RECT *DstRegion, + SMALL_RECT *ClipRegion, + WORD Fill) +{ + int Width = ConioRectWidth(SrcRegion); + int Height = ConioRectHeight(SrcRegion); + int SX, SY; + int DX, DY; + int XDelta, YDelta; + int i, j; + + SY = SrcRegion->Top; + DY = DstRegion->Top; + YDelta = 1; + if (SY < DY) + { + /* Moving down: work from bottom up */ + SY = SrcRegion->Bottom; + DY = DstRegion->Bottom; + YDelta = -1; + } + for (i = 0; i < Height; i++) + { + PWORD SRow = (PWORD)ConioCoordToPointer(ScreenBuffer, 0, SY); + PWORD DRow = (PWORD)ConioCoordToPointer(ScreenBuffer, 0, DY); + + SX = SrcRegion->Left; + DX = DstRegion->Left; + XDelta = 1; + if (SX < DX) + { + /* Moving right: work from right to left */ + SX = SrcRegion->Right; + DX = DstRegion->Right; + XDelta = -1; + } + for (j = 0; j < Width; j++) + { + WORD Cell = SRow[SX]; + if (SX >= ClipRegion->Left && SX <= ClipRegion->Right + && SY >= ClipRegion->Top && SY <= ClipRegion->Bottom) + { + SRow[SX] = Fill; + } + if (DX >= ClipRegion->Left && DX <= ClipRegion->Right + && DY >= ClipRegion->Top && DY <= ClipRegion->Bottom) + { + DRow[DX] = Cell; + } + SX += XDelta; + DX += XDelta; + } + SY += YDelta; + DY += YDelta; + } +} + +CSR_API(CsrWriteConsole) +{ + NTSTATUS Status; + PCHAR Buffer; + PCSRSS_SCREEN_BUFFER Buff; + PCSRSS_CONSOLE Console; + DWORD Written = 0; + ULONG Length; + ULONG CharSize = (Request->Data.WriteConsoleRequest.Unicode ? sizeof(WCHAR) : sizeof(CHAR)); + + DPRINT("CsrWriteConsole\n"); + + if (Request->Header.u1.s1.TotalLength + < CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE) + + (Request->Data.WriteConsoleRequest.NrCharactersToWrite * CharSize)) + { + DPRINT1("Invalid request size\n"); + return STATUS_INVALID_PARAMETER; + } + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.WriteConsoleRequest.ConsoleHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + if (Console->UnpauseEvent) + { + Status = NtDuplicateObject(GetCurrentProcess(), Console->UnpauseEvent, + ProcessData->ProcessHandle, &Request->Data.WriteConsoleRequest.UnpauseEvent, + SYNCHRONIZE, 0, 0); + ConioUnlockScreenBuffer(Buff); + return NT_SUCCESS(Status) ? STATUS_PENDING : Status; + } + + if(Request->Data.WriteConsoleRequest.Unicode) + { + Length = WideCharToMultiByte(Console->OutputCodePage, 0, + (PWCHAR)Request->Data.WriteConsoleRequest.Buffer, + Request->Data.WriteConsoleRequest.NrCharactersToWrite, + NULL, 0, NULL, NULL); + Buffer = RtlAllocateHeap(GetProcessHeap(), 0, Length); + if (Buffer) + { + WideCharToMultiByte(Console->OutputCodePage, 0, + (PWCHAR)Request->Data.WriteConsoleRequest.Buffer, + Request->Data.WriteConsoleRequest.NrCharactersToWrite, + Buffer, Length, NULL, NULL); + } + else + { + Status = STATUS_NO_MEMORY; + } + } + else + { + Buffer = (PCHAR)Request->Data.WriteConsoleRequest.Buffer; + } + + if (Buffer) + { + if (NT_SUCCESS(Status)) + { + Status = ConioWriteConsole(Console, Buff, Buffer, + Request->Data.WriteConsoleRequest.NrCharactersToWrite, TRUE); + if (NT_SUCCESS(Status)) + { + Written = Request->Data.WriteConsoleRequest.NrCharactersToWrite; + } + } + if (Request->Data.WriteConsoleRequest.Unicode) + { + RtlFreeHeap(GetProcessHeap(), 0, Buffer); + } + } + ConioUnlockScreenBuffer(Buff); + + Request->Data.WriteConsoleRequest.NrCharactersWritten = Written; + + return Status; +} + +VOID WINAPI +ConioDeleteScreenBuffer(PCSRSS_SCREEN_BUFFER Buffer) +{ + PCSRSS_CONSOLE Console = Buffer->Header.Console; + + RemoveEntryList(&Buffer->ListEntry); + if (Buffer == Console->ActiveBuffer) + { + /* Deleted active buffer; switch to most recently created */ + Console->ActiveBuffer = NULL; + if (!IsListEmpty(&Console->BufferList)) + { + Console->ActiveBuffer = CONTAINING_RECORD(Console->BufferList.Flink, CSRSS_SCREEN_BUFFER, ListEntry); + ConioDrawConsole(Console); + } + } + + HeapFree(Win32CsrApiHeap, 0, Buffer->Buffer); + HeapFree(Win32CsrApiHeap, 0, Buffer); +} + +VOID FASTCALL +ConioDrawConsole(PCSRSS_CONSOLE Console) +{ + SMALL_RECT Region; + + ConioInitRect(&Region, 0, 0, Console->Size.Y - 1, Console->Size.X - 1); + + ConioDrawRegion(Console, &Region); +} + +CSR_API(CsrGetScreenBufferInfo) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + PCONSOLE_SCREEN_BUFFER_INFO pInfo; + + DPRINT("CsrGetScreenBufferInfo\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.ScreenBufferInfoRequest.ConsoleHandle, &Buff, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + pInfo = &Request->Data.ScreenBufferInfoRequest.Info; + pInfo->dwSize.X = Buff->MaxX; + pInfo->dwSize.Y = Buff->MaxY; + pInfo->dwCursorPosition.X = Buff->CurrentX; + pInfo->dwCursorPosition.Y = Buff->CurrentY; + pInfo->wAttributes = Buff->DefaultAttrib; + pInfo->srWindow.Left = Buff->ShowX; + pInfo->srWindow.Right = Buff->ShowX + Console->Size.X - 1; + pInfo->srWindow.Top = Buff->ShowY; + pInfo->srWindow.Bottom = Buff->ShowY + Console->Size.Y - 1; + pInfo->dwMaximumWindowSize.X = Buff->MaxX; + pInfo->dwMaximumWindowSize.Y = Buff->MaxY; + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +CSR_API(CsrSetCursor) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + LONG OldCursorX, OldCursorY; + LONG NewCursorX, NewCursorY; + + DPRINT("CsrSetCursor\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.SetCursorRequest.ConsoleHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + NewCursorX = Request->Data.SetCursorRequest.Position.X; + NewCursorY = Request->Data.SetCursorRequest.Position.Y; + if (NewCursorX < 0 || NewCursorX >= Buff->MaxX || + NewCursorY < 0 || NewCursorY >= Buff->MaxY) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_INVALID_PARAMETER; + } + OldCursorX = Buff->CurrentX; + OldCursorY = Buff->CurrentY; + Buff->CurrentX = NewCursorX; + Buff->CurrentY = NewCursorY; + if (Buff == Console->ActiveBuffer) + { + if (! ConioSetScreenInfo(Console, Buff, OldCursorX, OldCursorY)) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_UNSUCCESSFUL; + } + } + + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +static VOID FASTCALL +ConioComputeUpdateRect(PCSRSS_SCREEN_BUFFER Buff, SMALL_RECT *UpdateRect, COORD *Start, UINT Length) +{ + if (Buff->MaxX <= Start->X + Length) + { + UpdateRect->Left = 0; + } + else + { + UpdateRect->Left = Start->X; + } + if (Buff->MaxX <= Start->X + Length) + { + UpdateRect->Right = Buff->MaxX - 1; + } + else + { + UpdateRect->Right = Start->X + Length - 1; + } + UpdateRect->Top = Start->Y; + UpdateRect->Bottom = Start->Y+ (Start->X + Length - 1) / Buff->MaxX; + if (Buff->MaxY <= UpdateRect->Bottom) + { + UpdateRect->Bottom = Buff->MaxY - 1; + } +} + +CSR_API(CsrWriteConsoleOutputChar) +{ + NTSTATUS Status; + PCHAR String, tmpString = NULL; + PBYTE Buffer; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + DWORD X, Y, Length, CharSize, Written = 0; + SMALL_RECT UpdateRect; + + DPRINT("CsrWriteConsoleOutputChar\n"); + + CharSize = (Request->Data.WriteConsoleOutputCharRequest.Unicode ? sizeof(WCHAR) : sizeof(CHAR)); + + if (Request->Header.u1.s1.TotalLength + < CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_CHAR) + + (Request->Data.WriteConsoleOutputCharRequest.Length * CharSize)) + { + DPRINT1("Invalid request size\n"); + return STATUS_INVALID_PARAMETER; + } + + Status = ConioLockScreenBuffer(ProcessData, + Request->Data.WriteConsoleOutputCharRequest.ConsoleHandle, + &Buff, + GENERIC_WRITE); + if (NT_SUCCESS(Status)) + { + Console = Buff->Header.Console; + if(Request->Data.WriteConsoleOutputCharRequest.Unicode) + { + Length = WideCharToMultiByte(Console->OutputCodePage, 0, + (PWCHAR)Request->Data.WriteConsoleOutputCharRequest.String, + Request->Data.WriteConsoleOutputCharRequest.Length, + NULL, 0, NULL, NULL); + tmpString = String = RtlAllocateHeap(GetProcessHeap(), 0, Length); + if (String) + { + WideCharToMultiByte(Console->OutputCodePage, 0, + (PWCHAR)Request->Data.WriteConsoleOutputCharRequest.String, + Request->Data.WriteConsoleOutputCharRequest.Length, + String, Length, NULL, NULL); + } + else + { + Status = STATUS_NO_MEMORY; + } + } + else + { + String = (PCHAR)Request->Data.WriteConsoleOutputCharRequest.String; + } + + if (String) + { + if (NT_SUCCESS(Status)) + { + X = Request->Data.WriteConsoleOutputCharRequest.Coord.X; + Y = (Request->Data.WriteConsoleOutputCharRequest.Coord.Y + Buff->VirtualY) % Buff->MaxY; + Length = Request->Data.WriteConsoleOutputCharRequest.Length; + Buffer = &Buff->Buffer[2 * (Y * Buff->MaxX + X)]; + while (Length--) + { + *Buffer = *String++; + Written++; + Buffer += 2; + if (++X == Buff->MaxX) + { + if (++Y == Buff->MaxY) + { + Y = 0; + Buffer = Buff->Buffer; + } + X = 0; + } + } + if (Buff == Console->ActiveBuffer) + { + ConioComputeUpdateRect(Buff, &UpdateRect, &Request->Data.WriteConsoleOutputCharRequest.Coord, + Request->Data.WriteConsoleOutputCharRequest.Length); + ConioDrawRegion(Console, &UpdateRect); + } + + Request->Data.WriteConsoleOutputCharRequest.EndCoord.X = X; + Request->Data.WriteConsoleOutputCharRequest.EndCoord.Y = (Y + Buff->MaxY - Buff->VirtualY) % Buff->MaxY; + + } + if (Request->Data.WriteConsoleRequest.Unicode) + { + RtlFreeHeap(GetProcessHeap(), 0, tmpString); + } + } + ConioUnlockScreenBuffer(Buff); + } + Request->Data.WriteConsoleOutputCharRequest.NrCharactersWritten = Written; + return Status; +} + +CSR_API(CsrFillOutputChar) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + DWORD X, Y, Length, Written = 0; + CHAR Char; + PBYTE Buffer; + SMALL_RECT UpdateRect; + + DPRINT("CsrFillOutputChar\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.FillOutputRequest.ConsoleHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + X = Request->Data.FillOutputRequest.Position.X; + Y = (Request->Data.FillOutputRequest.Position.Y + Buff->VirtualY) % Buff->MaxY; + Buffer = &Buff->Buffer[2 * (Y * Buff->MaxX + X)]; + if(Request->Data.FillOutputRequest.Unicode) + ConsoleUnicodeCharToAnsiChar(Console, &Char, &Request->Data.FillOutputRequest.Char.UnicodeChar); + else + Char = Request->Data.FillOutputRequest.Char.AsciiChar; + Length = Request->Data.FillOutputRequest.Length; + while (Length--) + { + *Buffer = Char; + Buffer += 2; + Written++; + if (++X == Buff->MaxX) + { + if (++Y == Buff->MaxY) + { + Y = 0; + Buffer = Buff->Buffer; + } + X = 0; + } + } + + if (Buff == Console->ActiveBuffer) + { + ConioComputeUpdateRect(Buff, &UpdateRect, &Request->Data.FillOutputRequest.Position, + Request->Data.FillOutputRequest.Length); + ConioDrawRegion(Console, &UpdateRect); + } + + ConioUnlockScreenBuffer(Buff); + Length = Request->Data.FillOutputRequest.Length; + Request->Data.FillOutputRequest.NrCharactersWritten = Length; + return STATUS_SUCCESS; +} + +CSR_API(CsrWriteConsoleOutputAttrib) +{ + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + PUCHAR Buffer; + PWORD Attribute; + int X, Y, Length; + NTSTATUS Status; + SMALL_RECT UpdateRect; + + DPRINT("CsrWriteConsoleOutputAttrib\n"); + + if (Request->Header.u1.s1.TotalLength + < CSR_API_MESSAGE_HEADER_SIZE(CSRSS_WRITE_CONSOLE_OUTPUT_ATTRIB) + + Request->Data.WriteConsoleOutputAttribRequest.Length * sizeof(WORD)) + { + DPRINT1("Invalid request size\n"); + return STATUS_INVALID_PARAMETER; + } + + Status = ConioLockScreenBuffer(ProcessData, + Request->Data.WriteConsoleOutputAttribRequest.ConsoleHandle, + &Buff, + GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + X = Request->Data.WriteConsoleOutputAttribRequest.Coord.X; + Y = (Request->Data.WriteConsoleOutputAttribRequest.Coord.Y + Buff->VirtualY) % Buff->MaxY; + Length = Request->Data.WriteConsoleOutputAttribRequest.Length; + Buffer = &Buff->Buffer[2 * (Y * Buff->MaxX + X) + 1]; + Attribute = Request->Data.WriteConsoleOutputAttribRequest.Attribute; + while (Length--) + { + *Buffer = (UCHAR)(*Attribute++); + Buffer += 2; + if (++X == Buff->MaxX) + { + if (++Y == Buff->MaxY) + { + Y = 0; + Buffer = Buff->Buffer + 1; + } + X = 0; + } + } + + if (Buff == Console->ActiveBuffer) + { + ConioComputeUpdateRect(Buff, &UpdateRect, &Request->Data.WriteConsoleOutputAttribRequest.Coord, + Request->Data.WriteConsoleOutputAttribRequest.Length); + ConioDrawRegion(Console, &UpdateRect); + } + + Request->Data.WriteConsoleOutputAttribRequest.EndCoord.X = X; + Request->Data.WriteConsoleOutputAttribRequest.EndCoord.Y = (Y + Buff->MaxY - Buff->VirtualY) % Buff->MaxY; + + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +CSR_API(CsrFillOutputAttrib) +{ + PCSRSS_SCREEN_BUFFER Buff; + PUCHAR Buffer; + NTSTATUS Status; + int X, Y, Length; + UCHAR Attr; + SMALL_RECT UpdateRect; + PCSRSS_CONSOLE Console; + + DPRINT("CsrFillOutputAttrib\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.FillOutputAttribRequest.ConsoleHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + X = Request->Data.FillOutputAttribRequest.Coord.X; + Y = (Request->Data.FillOutputAttribRequest.Coord.Y + Buff->VirtualY) % Buff->MaxY; + Length = Request->Data.FillOutputAttribRequest.Length; + Attr = Request->Data.FillOutputAttribRequest.Attribute; + Buffer = &Buff->Buffer[(Y * Buff->MaxX * 2) + (X * 2) + 1]; + while (Length--) + { + *Buffer = Attr; + Buffer += 2; + if (++X == Buff->MaxX) + { + if (++Y == Buff->MaxY) + { + Y = 0; + Buffer = Buff->Buffer + 1; + } + X = 0; + } + } + + if (Buff == Console->ActiveBuffer) + { + ConioComputeUpdateRect(Buff, &UpdateRect, &Request->Data.FillOutputAttribRequest.Coord, + Request->Data.FillOutputAttribRequest.Length); + ConioDrawRegion(Console, &UpdateRect); + } + + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +DWORD FASTCALL +ConioEffectiveCursorSize(PCSRSS_CONSOLE Console, DWORD Scale) +{ + DWORD Size = (Console->ActiveBuffer->CursorInfo.dwSize * Scale + 99) / 100; + /* If line input in progress, perhaps adjust for insert toggle */ + if (Console->LineBuffer && !Console->LineComplete && Console->LineInsertToggle) + return (Size * 2 <= Scale) ? (Size * 2) : (Size / 2); + return Size; +} + +CSR_API(CsrGetCursorInfo) +{ + PCSRSS_SCREEN_BUFFER Buff; + NTSTATUS Status; + + DPRINT("CsrGetCursorInfo\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.GetCursorInfoRequest.ConsoleHandle, &Buff, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Request->Data.GetCursorInfoRequest.Info.bVisible = Buff->CursorInfo.bVisible; + Request->Data.GetCursorInfoRequest.Info.dwSize = Buff->CursorInfo.dwSize; + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +CSR_API(CsrSetCursorInfo) +{ + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + DWORD Size; + BOOL Visible; + NTSTATUS Status; + + DPRINT("CsrSetCursorInfo\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.SetCursorInfoRequest.ConsoleHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + Size = Request->Data.SetCursorInfoRequest.Info.dwSize; + Visible = Request->Data.SetCursorInfoRequest.Info.bVisible; + if (Size < 1) + { + Size = 1; + } + if (100 < Size) + { + Size = 100; + } + + if (Size != Buff->CursorInfo.dwSize + || (Visible && ! Buff->CursorInfo.bVisible) || (! Visible && Buff->CursorInfo.bVisible)) + { + Buff->CursorInfo.dwSize = Size; + Buff->CursorInfo.bVisible = Visible; + + if (! ConioSetCursorInfo(Console, Buff)) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_UNSUCCESSFUL; + } + } + + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +CSR_API(CsrSetTextAttrib) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + + DPRINT("CsrSetTextAttrib\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.SetCursorRequest.ConsoleHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + Buff->DefaultAttrib = Request->Data.SetAttribRequest.Attrib; + if (Buff == Console->ActiveBuffer) + { + if (! ConioUpdateScreenInfo(Console, Buff)) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_UNSUCCESSFUL; + } + } + + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +CSR_API(CsrCreateScreenBuffer) +{ + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + NTSTATUS Status; + + DPRINT("CsrCreateScreenBuffer\n"); + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Buff = HeapAlloc(Win32CsrApiHeap, HEAP_ZERO_MEMORY, sizeof(CSRSS_SCREEN_BUFFER)); + + if (Buff != NULL) + { + if (Console->ActiveBuffer) + { + Buff->MaxX = Console->ActiveBuffer->MaxX; + Buff->MaxY = Console->ActiveBuffer->MaxY; + Buff->CursorInfo.bVisible = Console->ActiveBuffer->CursorInfo.bVisible; + Buff->CursorInfo.dwSize = Console->ActiveBuffer->CursorInfo.dwSize; + } + else + { + Buff->CursorInfo.bVisible = TRUE; + Buff->CursorInfo.dwSize = CSR_DEFAULT_CURSOR_SIZE; + } + + if (Buff->MaxX == 0) + { + Buff->MaxX = 80; + } + + if (Buff->MaxY == 0) + { + Buff->MaxY = 25; + } + + Status = CsrInitConsoleScreenBuffer(Console, Buff); + if (NT_SUCCESS(Status)) + { + Status = Win32CsrInsertObject(ProcessData, + &Request->Data.CreateScreenBufferRequest.OutputHandle, + &Buff->Header, + Request->Data.CreateScreenBufferRequest.Access, + Request->Data.CreateScreenBufferRequest.Inheritable, + Request->Data.CreateScreenBufferRequest.ShareMode); + } + } + else + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + ConioUnlockConsole(Console); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return Status; +} + +CSR_API(CsrSetScreenBuffer) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + + DPRINT("CsrSetScreenBuffer\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.SetScreenBufferRequest.OutputHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + if (Buff == Console->ActiveBuffer) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_SUCCESS; + } + + /* If old buffer has no handles, it's now unreferenced */ + if (Console->ActiveBuffer->Header.HandleCount == 0) + { + ConioDeleteScreenBuffer(Console->ActiveBuffer); + } + /* tie console to new buffer */ + Console->ActiveBuffer = Buff; + /* Redraw the console */ + ConioDrawConsole(Console); + + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +CSR_API(CsrWriteConsoleOutput) +{ + SHORT i, X, Y, SizeX, SizeY; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + SMALL_RECT ScreenBuffer; + CHAR_INFO* CurCharInfo; + SMALL_RECT WriteRegion; + CHAR_INFO* CharInfo; + COORD BufferCoord; + COORD BufferSize; + NTSTATUS Status; + PBYTE Ptr; + + DPRINT("CsrWriteConsoleOutput\n"); + + Status = ConioLockScreenBuffer(ProcessData, + Request->Data.WriteConsoleOutputRequest.ConsoleHandle, + &Buff, + GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + BufferSize = Request->Data.WriteConsoleOutputRequest.BufferSize; + BufferCoord = Request->Data.WriteConsoleOutputRequest.BufferCoord; + CharInfo = Request->Data.WriteConsoleOutputRequest.CharInfo; + if (!Win32CsrValidateBuffer(ProcessData, CharInfo, + BufferSize.X * BufferSize.Y, sizeof(CHAR_INFO))) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_ACCESS_VIOLATION; + } + WriteRegion = Request->Data.WriteConsoleOutputRequest.WriteRegion; + + SizeY = min(BufferSize.Y - BufferCoord.Y, ConioRectHeight(&WriteRegion)); + SizeX = min(BufferSize.X - BufferCoord.X, ConioRectWidth(&WriteRegion)); + WriteRegion.Bottom = WriteRegion.Top + SizeY - 1; + WriteRegion.Right = WriteRegion.Left + SizeX - 1; + + /* Make sure WriteRegion is inside the screen buffer */ + ConioInitRect(&ScreenBuffer, 0, 0, Buff->MaxY - 1, Buff->MaxX - 1); + if (! ConioGetIntersection(&WriteRegion, &ScreenBuffer, &WriteRegion)) + { + ConioUnlockScreenBuffer(Buff); + + /* It is okay to have a WriteRegion completely outside the screen buffer. + No data is written then. */ + return STATUS_SUCCESS; + } + + for (i = 0, Y = WriteRegion.Top; Y <= WriteRegion.Bottom; i++, Y++) + { + CurCharInfo = CharInfo + (i + BufferCoord.Y) * BufferSize.X + BufferCoord.X; + Ptr = ConioCoordToPointer(Buff, WriteRegion.Left, Y); + for (X = WriteRegion.Left; X <= WriteRegion.Right; X++) + { + CHAR AsciiChar; + if (Request->Data.WriteConsoleOutputRequest.Unicode) + { + ConsoleUnicodeCharToAnsiChar(Console, &AsciiChar, &CurCharInfo->Char.UnicodeChar); + } + else + { + AsciiChar = CurCharInfo->Char.AsciiChar; + } + *Ptr++ = AsciiChar; + *Ptr++ = CurCharInfo->Attributes; + CurCharInfo++; + } + } + + ConioDrawRegion(Console, &WriteRegion); + + ConioUnlockScreenBuffer(Buff); + + Request->Data.WriteConsoleOutputRequest.WriteRegion.Right = WriteRegion.Left + SizeX - 1; + Request->Data.WriteConsoleOutputRequest.WriteRegion.Bottom = WriteRegion.Top + SizeY - 1; + Request->Data.WriteConsoleOutputRequest.WriteRegion.Left = WriteRegion.Left; + Request->Data.WriteConsoleOutputRequest.WriteRegion.Top = WriteRegion.Top; + + return STATUS_SUCCESS; +} + +CSR_API(CsrScrollConsoleScreenBuffer) +{ + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + SMALL_RECT ScreenBuffer; + SMALL_RECT SrcRegion; + SMALL_RECT DstRegion; + SMALL_RECT UpdateRegion; + SMALL_RECT ScrollRectangle; + SMALL_RECT ClipRectangle; + NTSTATUS Status; + HANDLE ConsoleHandle; + BOOLEAN UseClipRectangle; + COORD DestinationOrigin; + CHAR_INFO Fill; + CHAR FillChar; + + DPRINT("CsrScrollConsoleScreenBuffer\n"); + + ConsoleHandle = Request->Data.ScrollConsoleScreenBufferRequest.ConsoleHandle; + UseClipRectangle = Request->Data.ScrollConsoleScreenBufferRequest.UseClipRectangle; + DestinationOrigin = Request->Data.ScrollConsoleScreenBufferRequest.DestinationOrigin; + Fill = Request->Data.ScrollConsoleScreenBufferRequest.Fill; + + Status = ConioLockScreenBuffer(ProcessData, ConsoleHandle, &Buff, GENERIC_WRITE); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + ScrollRectangle = Request->Data.ScrollConsoleScreenBufferRequest.ScrollRectangle; + + /* Make sure source rectangle is inside the screen buffer */ + ConioInitRect(&ScreenBuffer, 0, 0, Buff->MaxY - 1, Buff->MaxX - 1); + if (! ConioGetIntersection(&SrcRegion, &ScreenBuffer, &ScrollRectangle)) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_SUCCESS; + } + + /* If the source was clipped on the left or top, adjust the destination accordingly */ + if (ScrollRectangle.Left < 0) + { + DestinationOrigin.X -= ScrollRectangle.Left; + } + if (ScrollRectangle.Top < 0) + { + DestinationOrigin.Y -= ScrollRectangle.Top; + } + + if (UseClipRectangle) + { + ClipRectangle = Request->Data.ScrollConsoleScreenBufferRequest.ClipRectangle; + if (!ConioGetIntersection(&ClipRectangle, &ClipRectangle, &ScreenBuffer)) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_SUCCESS; + } + } + else + { + ClipRectangle = ScreenBuffer; + } + + ConioInitRect(&DstRegion, + DestinationOrigin.Y, + DestinationOrigin.X, + DestinationOrigin.Y + ConioRectHeight(&SrcRegion) - 1, + DestinationOrigin.X + ConioRectWidth(&SrcRegion) - 1); + + if (Request->Data.ScrollConsoleScreenBufferRequest.Unicode) + ConsoleUnicodeCharToAnsiChar(Console, &FillChar, &Fill.Char.UnicodeChar); + else + FillChar = Fill.Char.AsciiChar; + + ConioMoveRegion(Buff, &SrcRegion, &DstRegion, &ClipRectangle, Fill.Attributes << 8 | (BYTE)FillChar); + + if (Buff == Console->ActiveBuffer) + { + ConioGetUnion(&UpdateRegion, &SrcRegion, &DstRegion); + if (ConioGetIntersection(&UpdateRegion, &UpdateRegion, &ClipRectangle)) + { + /* Draw update region */ + ConioDrawRegion(Console, &UpdateRegion); + } + } + + ConioUnlockScreenBuffer(Buff); + + return STATUS_SUCCESS; +} + +CSR_API(CsrReadConsoleOutputChar) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + DWORD Xpos, Ypos; + PCHAR ReadBuffer; + DWORD i; + ULONG CharSize; + CHAR Char; + + DPRINT("CsrReadConsoleOutputChar\n"); + + ReadBuffer = Request->Data.ReadConsoleOutputCharRequest.String; + + CharSize = (Request->Data.ReadConsoleOutputCharRequest.Unicode ? sizeof(WCHAR) : sizeof(CHAR)); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.ReadConsoleOutputCharRequest.ConsoleHandle, &Buff, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + Xpos = Request->Data.ReadConsoleOutputCharRequest.ReadCoord.X; + Ypos = (Request->Data.ReadConsoleOutputCharRequest.ReadCoord.Y + Buff->VirtualY) % Buff->MaxY; + + for (i = 0; i < Request->Data.ReadConsoleOutputCharRequest.NumCharsToRead; ++i) + { + Char = Buff->Buffer[(Xpos * 2) + (Ypos * 2 * Buff->MaxX)]; + + if(Request->Data.ReadConsoleOutputCharRequest.Unicode) + { + ConsoleAnsiCharToUnicodeChar(Console, (WCHAR*)ReadBuffer, &Char); + ReadBuffer += sizeof(WCHAR); + } + else + *(ReadBuffer++) = Char; + + Xpos++; + + if (Xpos == Buff->MaxX) + { + Xpos = 0; + Ypos++; + + if (Ypos == Buff->MaxY) + { + Ypos = 0; + } + } + } + + *ReadBuffer = 0; + Request->Data.ReadConsoleOutputCharRequest.EndCoord.X = Xpos; + Request->Data.ReadConsoleOutputCharRequest.EndCoord.Y = (Ypos - Buff->VirtualY + Buff->MaxY) % Buff->MaxY; + + ConioUnlockScreenBuffer(Buff); + + Request->Data.ReadConsoleOutputCharRequest.CharsRead = (DWORD)((ULONG_PTR)ReadBuffer - (ULONG_PTR)Request->Data.ReadConsoleOutputCharRequest.String) / CharSize; + if (Request->Data.ReadConsoleOutputCharRequest.CharsRead * CharSize + CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE_OUTPUT_CHAR) > sizeof(CSR_API_MESSAGE)) + { + DPRINT1("Length won't fit in message\n"); + return STATUS_BUFFER_TOO_SMALL; + } + + return STATUS_SUCCESS; +} + + +CSR_API(CsrReadConsoleOutputAttrib) +{ + NTSTATUS Status; + PCSRSS_SCREEN_BUFFER Buff; + DWORD Xpos, Ypos; + PWORD ReadBuffer; + DWORD i; + DWORD CurrentLength; + + DPRINT("CsrReadConsoleOutputAttrib\n"); + + ReadBuffer = Request->Data.ReadConsoleOutputAttribRequest.Attribute; + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.ReadConsoleOutputAttribRequest.ConsoleHandle, &Buff, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Xpos = Request->Data.ReadConsoleOutputAttribRequest.ReadCoord.X; + Ypos = (Request->Data.ReadConsoleOutputAttribRequest.ReadCoord.Y + Buff->VirtualY) % Buff->MaxY; + + for (i = 0; i < Request->Data.ReadConsoleOutputAttribRequest.NumAttrsToRead; ++i) + { + *ReadBuffer = Buff->Buffer[(Xpos * 2) + (Ypos * 2 * Buff->MaxX) + 1]; + + ReadBuffer++; + Xpos++; + + if (Xpos == Buff->MaxX) + { + Xpos = 0; + Ypos++; + + if (Ypos == Buff->MaxY) + { + Ypos = 0; + } + } + } + + *ReadBuffer = 0; + + Request->Data.ReadConsoleOutputAttribRequest.EndCoord.X = Xpos; + Request->Data.ReadConsoleOutputAttribRequest.EndCoord.Y = (Ypos - Buff->VirtualY + Buff->MaxY) % Buff->MaxY; + + ConioUnlockScreenBuffer(Buff); + + CurrentLength = CSR_API_MESSAGE_HEADER_SIZE(CSRSS_READ_CONSOLE_OUTPUT_ATTRIB) + + Request->Data.ReadConsoleOutputAttribRequest.NumAttrsToRead * sizeof(WORD); + if (CurrentLength > sizeof(CSR_API_MESSAGE)) + { + DPRINT1("Length won't fit in message\n"); + return STATUS_BUFFER_TOO_SMALL; + } + + return STATUS_SUCCESS; +} + +CSR_API(CsrReadConsoleOutput) +{ + PCHAR_INFO CharInfo; + PCHAR_INFO CurCharInfo; + PCSRSS_SCREEN_BUFFER Buff; + DWORD SizeX, SizeY; + NTSTATUS Status; + COORD BufferSize; + COORD BufferCoord; + SMALL_RECT ReadRegion; + SMALL_RECT ScreenRect; + DWORD i; + PBYTE Ptr; + LONG X, Y; + UINT CodePage; + + DPRINT("CsrReadConsoleOutput\n"); + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.ReadConsoleOutputRequest.ConsoleHandle, &Buff, GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + CharInfo = Request->Data.ReadConsoleOutputRequest.CharInfo; + ReadRegion = Request->Data.ReadConsoleOutputRequest.ReadRegion; + BufferSize = Request->Data.ReadConsoleOutputRequest.BufferSize; + BufferCoord = Request->Data.ReadConsoleOutputRequest.BufferCoord; + + /* FIXME: Is this correct? */ + CodePage = ProcessData->Console->OutputCodePage; + + if (!Win32CsrValidateBuffer(ProcessData, CharInfo, + BufferSize.X * BufferSize.Y, sizeof(CHAR_INFO))) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_ACCESS_VIOLATION; + } + + SizeY = min(BufferSize.Y - BufferCoord.Y, ConioRectHeight(&ReadRegion)); + SizeX = min(BufferSize.X - BufferCoord.X, ConioRectWidth(&ReadRegion)); + ReadRegion.Bottom = ReadRegion.Top + SizeY; + ReadRegion.Right = ReadRegion.Left + SizeX; + + ConioInitRect(&ScreenRect, 0, 0, Buff->MaxY, Buff->MaxX); + if (! ConioGetIntersection(&ReadRegion, &ScreenRect, &ReadRegion)) + { + ConioUnlockScreenBuffer(Buff); + return STATUS_SUCCESS; + } + + for (i = 0, Y = ReadRegion.Top; Y < ReadRegion.Bottom; ++i, ++Y) + { + CurCharInfo = CharInfo + (i * BufferSize.X); + + Ptr = ConioCoordToPointer(Buff, ReadRegion.Left, Y); + for (X = ReadRegion.Left; X < ReadRegion.Right; ++X) + { + if (Request->Data.ReadConsoleOutputRequest.Unicode) + { + MultiByteToWideChar(CodePage, 0, + (PCHAR)Ptr++, 1, + &CurCharInfo->Char.UnicodeChar, 1); + } + else + { + CurCharInfo->Char.AsciiChar = *Ptr++; + } + CurCharInfo->Attributes = *Ptr++; + ++CurCharInfo; + } + } + + ConioUnlockScreenBuffer(Buff); + + Request->Data.ReadConsoleOutputRequest.ReadRegion.Right = ReadRegion.Left + SizeX - 1; + Request->Data.ReadConsoleOutputRequest.ReadRegion.Bottom = ReadRegion.Top + SizeY - 1; + Request->Data.ReadConsoleOutputRequest.ReadRegion.Left = ReadRegion.Left; + Request->Data.ReadConsoleOutputRequest.ReadRegion.Top = ReadRegion.Top; + + return STATUS_SUCCESS; +} + +CSR_API(CsrSetScreenBufferSize) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + + Status = ConioLockScreenBuffer(ProcessData, Request->Data.SetScreenBufferSize.OutputHandle, &Buff, GENERIC_WRITE); + if (!NT_SUCCESS(Status)) + { + return Status; + } + Console = Buff->Header.Console; + + Status = ConioResizeBuffer(Console, Buff, Request->Data.SetScreenBufferSize.Size); + ConioUnlockScreenBuffer(Buff); + + return Status; +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/console.c b/reactos/deprecated/win32csr/console.c new file mode 100644 index 00000000000..d4a1d7993e8 --- /dev/null +++ b/reactos/deprecated/win32csr/console.c @@ -0,0 +1,832 @@ +/* + * reactos/subsys/csrss/win32csr/conio.c + * + * Console I/O functions + * + * ReactOS Operating System + */ + +/* INCLUDES ******************************************************************/ + +#define NDEBUG +#include "w32csr.h" +#include + +/* FUNCTIONS *****************************************************************/ + +NTSTATUS FASTCALL +ConioConsoleFromProcessData(PCSR_PROCESS ProcessData, PCSRSS_CONSOLE *Console) +{ + PCSRSS_CONSOLE ProcessConsole; + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + ProcessConsole = ProcessData->Console; + + if (!ProcessConsole) + { + *Console = NULL; + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } + + InterlockedIncrement(&ProcessConsole->ReferenceCount); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + EnterCriticalSection(&(ProcessConsole->Lock)); + *Console = ProcessConsole; + + return STATUS_SUCCESS; +} + +VOID FASTCALL +ConioConsoleCtrlEventTimeout(DWORD Event, PCSR_PROCESS ProcessData, DWORD Timeout) +{ + HANDLE Thread; + + DPRINT("ConioConsoleCtrlEvent Parent ProcessId = %x\n", ProcessData->ClientId.UniqueProcess); + + if (ProcessData->CtrlDispatcher) + { + + Thread = CreateRemoteThread(ProcessData->ProcessHandle, NULL, 0, + (LPTHREAD_START_ROUTINE) ProcessData->CtrlDispatcher, + UlongToPtr(Event), 0, NULL); + if (NULL == Thread) + { + DPRINT1("Failed thread creation (Error: 0x%x)\n", GetLastError()); + return; + } + WaitForSingleObject(Thread, Timeout); + CloseHandle(Thread); + } +} + +VOID FASTCALL +ConioConsoleCtrlEvent(DWORD Event, PCSR_PROCESS ProcessData) +{ + ConioConsoleCtrlEventTimeout(Event, ProcessData, 0); +} + +static NTSTATUS WINAPI +CsrInitConsole(PCSRSS_CONSOLE Console, int ShowCmd) +{ + NTSTATUS Status; + SECURITY_ATTRIBUTES SecurityAttributes; + PCSRSS_SCREEN_BUFFER NewBuffer; + BOOL GuiMode; + WCHAR Title[255]; + HINSTANCE hInst; + + Console->Title.MaximumLength = Console->Title.Length = 0; + Console->Title.Buffer = NULL; + + hInst = GetModuleHandleW(L"win32csr"); + if (LoadStringW(hInst,IDS_COMMAND_PROMPT,Title,sizeof(Title)/sizeof(Title[0]))) + { + RtlCreateUnicodeString(&Console->Title, Title); + } + else + { + RtlCreateUnicodeString(&Console->Title, L"Command Prompt"); + } + + Console->ReferenceCount = 0; + Console->LineBuffer = NULL; + Console->Header.Type = CONIO_CONSOLE_MAGIC; + Console->Header.Console = Console; + Console->Mode = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT; + InitializeListHead(&Console->BufferList); + Console->ActiveBuffer = NULL; + InitializeListHead(&Console->InputEvents); + InitializeListHead(&Console->HistoryBuffers); + Console->CodePage = GetOEMCP(); + Console->OutputCodePage = GetOEMCP(); + + SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + SecurityAttributes.lpSecurityDescriptor = NULL; + SecurityAttributes.bInheritHandle = TRUE; + + Console->ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL); + if (NULL == Console->ActiveEvent) + { + RtlFreeUnicodeString(&Console->Title); + return STATUS_UNSUCCESSFUL; + } + Console->PrivateData = NULL; + InitializeCriticalSection(&Console->Lock); + + GuiMode = DtbgIsDesktopVisible(); + + /* allocate console screen buffer */ + NewBuffer = HeapAlloc(Win32CsrApiHeap, HEAP_ZERO_MEMORY, sizeof(CSRSS_SCREEN_BUFFER)); + if (NULL == NewBuffer) + { + RtlFreeUnicodeString(&Console->Title); + DeleteCriticalSection(&Console->Lock); + CloseHandle(Console->ActiveEvent); + return STATUS_INSUFFICIENT_RESOURCES; + } + /* init screen buffer with defaults */ + NewBuffer->CursorInfo.bVisible = TRUE; + NewBuffer->CursorInfo.dwSize = CSR_DEFAULT_CURSOR_SIZE; + /* make console active, and insert into console list */ + Console->ActiveBuffer = (PCSRSS_SCREEN_BUFFER) NewBuffer; + + /* + * If we are not in GUI-mode, start the text-mode console. If we fail, + * try to start the GUI-mode console (win32k will automatically switch + * to graphical mode, therefore no additional code is needed). + */ + if (!GuiMode) + { + DPRINT1("WIN32CSR: Opening text-mode console\n"); + Status = TuiInitConsole(Console); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open text-mode console, switching to gui-mode, Status = 0x%08lx\n", Status); + GuiMode = TRUE; + } + } + + /* + * Try to open the GUI-mode console. Two cases are possible: + * - We are in GUI-mode, therefore GuiMode == TRUE, the previous test-case + * failed and we start GUI-mode console. + * - We are in text-mode, therefore GuiMode == FALSE, the previous test-case + * succeeded BUT we failed at starting text-mode console. Then GuiMode + * was switched to TRUE in order to try to open the console in GUI-mode. + */ + if (GuiMode) + { + DPRINT1("WIN32CSR: Opening GUI-mode console\n"); + Status = GuiInitConsole(Console, ShowCmd); + if (!NT_SUCCESS(Status)) + { + HeapFree(Win32CsrApiHeap,0, NewBuffer); + RtlFreeUnicodeString(&Console->Title); + DeleteCriticalSection(&Console->Lock); + CloseHandle(Console->ActiveEvent); + DPRINT1("GuiInitConsole: failed, Status = 0x%08lx\n", Status); + return Status; + } + } + + Status = CsrInitConsoleScreenBuffer(Console, NewBuffer); + if (!NT_SUCCESS(Status)) + { + ConioCleanupConsole(Console); + RtlFreeUnicodeString(&Console->Title); + DeleteCriticalSection(&Console->Lock); + CloseHandle(Console->ActiveEvent); + HeapFree(Win32CsrApiHeap, 0, NewBuffer); + DPRINT1("CsrInitConsoleScreenBuffer: failed\n"); + return Status; + } + + /* copy buffer contents to screen */ + ConioDrawConsole(Console); + + return STATUS_SUCCESS; +} + +CSR_API(CsrAllocConsole) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN NewConsole = FALSE; + + DPRINT("CsrAllocConsole\n"); + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + if (ProcessData->Console) + { + DPRINT1("Process already has a console\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_PARAMETER; + } + + /* If we don't need a console, then get out of here */ + if (!Request->Data.AllocConsoleRequest.ConsoleNeeded) + { + DPRINT("No console needed\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_SUCCESS; + } + + /* If we already have one, then don't create a new one... */ + if (!Request->Data.AllocConsoleRequest.Console || + Request->Data.AllocConsoleRequest.Console != ProcessData->ParentConsole) + { + /* Allocate a console structure */ + NewConsole = TRUE; + Console = HeapAlloc(Win32CsrApiHeap, HEAP_ZERO_MEMORY, sizeof(CSRSS_CONSOLE)); + if (NULL == Console) + { + DPRINT1("Not enough memory for console\n"); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_NO_MEMORY; + } + /* initialize list head */ + InitializeListHead(&Console->ProcessList); + /* insert process data required for GUI initialization */ + InsertHeadList(&Console->ProcessList, &ProcessData->ConsoleLink); + /* Initialize the Console */ + Status = CsrInitConsole(Console, Request->Data.AllocConsoleRequest.ShowCmd); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Console init failed\n"); + HeapFree(Win32CsrApiHeap, 0, Console); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return Status; + } + } + else + { + /* Reuse our current console */ + Console = Request->Data.AllocConsoleRequest.Console; + } + + /* Set the Process Console */ + ProcessData->Console = Console; + + /* Return it to the caller */ + Request->Data.AllocConsoleRequest.Console = Console; + + /* Add a reference count because the process is tied to the console */ + _InterlockedIncrement(&Console->ReferenceCount); + + if (NewConsole || !ProcessData->bInheritHandles) + { + /* Insert the Objects */ + Status = Win32CsrInsertObject(ProcessData, + &Request->Data.AllocConsoleRequest.InputHandle, + &Console->Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (! NT_SUCCESS(Status)) + { + DPRINT1("Failed to insert object\n"); + ConioDeleteConsole((Object_t *) Console); + ProcessData->Console = 0; + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return Status; + } + + Status = Win32CsrInsertObject(ProcessData, + &Request->Data.AllocConsoleRequest.OutputHandle, + &Console->ActiveBuffer->Header, + GENERIC_READ | GENERIC_WRITE, + TRUE, + FILE_SHARE_READ | FILE_SHARE_WRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to insert object\n"); + ConioDeleteConsole((Object_t *) Console); + Win32CsrReleaseObject(ProcessData, + Request->Data.AllocConsoleRequest.InputHandle); + ProcessData->Console = 0; + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return Status; + } + } + + /* Duplicate the Event */ + if (!DuplicateHandle(GetCurrentProcess(), + ProcessData->Console->ActiveEvent, + ProcessData->ProcessHandle, + &ProcessData->ConsoleEvent, + EVENT_ALL_ACCESS, + FALSE, + 0)) + { + DPRINT1("DuplicateHandle() failed: %lu\n", GetLastError()); + ConioDeleteConsole((Object_t *) Console); + if (NewConsole || !ProcessData->bInheritHandles) + { + Win32CsrReleaseObject(ProcessData, + Request->Data.AllocConsoleRequest.OutputHandle); + Win32CsrReleaseObject(ProcessData, + Request->Data.AllocConsoleRequest.InputHandle); + } + ProcessData->Console = 0; + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return Status; + } + + /* Set the Ctrl Dispatcher */ + ProcessData->CtrlDispatcher = Request->Data.AllocConsoleRequest.CtrlDispatcher; + DPRINT("CSRSS:CtrlDispatcher address: %x\n", ProcessData->CtrlDispatcher); + + if (!NewConsole) + { + /* Insert into the list if it has not been added */ + InsertHeadList(&ProcessData->Console->ProcessList, &ProcessData->ConsoleLink); + } + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_SUCCESS; +} + +CSR_API(CsrFreeConsole) +{ + Win32CsrReleaseConsole(ProcessData); + return STATUS_SUCCESS; +} + +VOID WINAPI +ConioDeleteConsole(Object_t *Object) +{ + PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) Object; + ConsoleInput *Event; + + DPRINT("ConioDeleteConsole\n"); + + /* Drain input event queue */ + while (Console->InputEvents.Flink != &Console->InputEvents) + { + Event = (ConsoleInput *) Console->InputEvents.Flink; + Console->InputEvents.Flink = Console->InputEvents.Flink->Flink; + Console->InputEvents.Flink->Flink->Blink = &Console->InputEvents; + HeapFree(Win32CsrApiHeap, 0, Event); + } + + ConioCleanupConsole(Console); + if (Console->LineBuffer) + RtlFreeHeap(Win32CsrApiHeap, 0, Console->LineBuffer); + while (!IsListEmpty(&Console->HistoryBuffers)) + HistoryDeleteBuffer((struct tagHISTORY_BUFFER *)Console->HistoryBuffers.Flink); + + ConioDeleteScreenBuffer(Console->ActiveBuffer); + if (!IsListEmpty(&Console->BufferList)) + { + DPRINT1("BUG: screen buffer list not empty\n"); + } + + CloseHandle(Console->ActiveEvent); + if (Console->UnpauseEvent) CloseHandle(Console->UnpauseEvent); + DeleteCriticalSection(&Console->Lock); + RtlFreeUnicodeString(&Console->Title); + IntDeleteAllAliases(Console->Aliases); + HeapFree(Win32CsrApiHeap, 0, Console); +} + +VOID WINAPI +CsrInitConsoleSupport(VOID) +{ + DPRINT("CSR: CsrInitConsoleSupport()\n"); + + /* Should call LoadKeyboardLayout */ +} + +VOID FASTCALL +ConioPause(PCSRSS_CONSOLE Console, UINT Flags) +{ + Console->PauseFlags |= Flags; + if (!Console->UnpauseEvent) + Console->UnpauseEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +VOID FASTCALL +ConioUnpause(PCSRSS_CONSOLE Console, UINT Flags) +{ + Console->PauseFlags &= ~Flags; + if (Console->PauseFlags == 0 && Console->UnpauseEvent) + { + SetEvent(Console->UnpauseEvent); + CloseHandle(Console->UnpauseEvent); + Console->UnpauseEvent = NULL; + } +} + +CSR_API(CsrSetConsoleMode) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + + DPRINT("CsrSetConsoleMode\n"); + + Status = Win32CsrLockObject(ProcessData, + Request->Data.SetConsoleModeRequest.ConsoleHandle, + (Object_t **) &Console, GENERIC_WRITE, 0); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Buff = (PCSRSS_SCREEN_BUFFER)Console; + if (CONIO_CONSOLE_MAGIC == Console->Header.Type) + { + Console->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_INPUT_MODE_VALID; + } + else if (CONIO_SCREEN_BUFFER_MAGIC == Console->Header.Type) + { + Buff->Mode = Request->Data.SetConsoleModeRequest.Mode & CONSOLE_OUTPUT_MODE_VALID; + } + else + { + Status = STATUS_INVALID_HANDLE; + } + + Win32CsrUnlockObject((Object_t *)Console); + + return Status; +} + +CSR_API(CsrGetConsoleMode) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; /* gee, I really wish I could use an anonymous union here */ + + DPRINT("CsrGetConsoleMode\n"); + + Status = Win32CsrLockObject(ProcessData, Request->Data.GetConsoleModeRequest.ConsoleHandle, + (Object_t **) &Console, GENERIC_READ, 0); + if (! NT_SUCCESS(Status)) + { + return Status; + } + Status = STATUS_SUCCESS; + Buff = (PCSRSS_SCREEN_BUFFER) Console; + if (CONIO_CONSOLE_MAGIC == Console->Header.Type) + { + Request->Data.GetConsoleModeRequest.ConsoleMode = Console->Mode; + } + else if (CONIO_SCREEN_BUFFER_MAGIC == Buff->Header.Type) + { + Request->Data.GetConsoleModeRequest.ConsoleMode = Buff->Mode; + } + else + { + Status = STATUS_INVALID_HANDLE; + } + + Win32CsrUnlockObject((Object_t *)Console); + return Status; +} + +CSR_API(CsrSetTitle) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + PWCHAR Buffer; + + DPRINT("CsrSetTitle\n"); + + if (!Win32CsrValidateBuffer(ProcessData, Request->Data.SetTitleRequest.Title, + Request->Data.SetTitleRequest.Length, 1)) + { + return STATUS_ACCESS_VIOLATION; + } + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if(NT_SUCCESS(Status)) + { + Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Request->Data.SetTitleRequest.Length); + if (Buffer) + { + /* copy title to console */ + RtlFreeUnicodeString(&Console->Title); + Console->Title.Buffer = Buffer; + Console->Title.Length = Console->Title.MaximumLength = Request->Data.SetTitleRequest.Length; + memcpy(Console->Title.Buffer, Request->Data.SetTitleRequest.Title, Console->Title.Length); + if (! ConioChangeTitle(Console)) + { + Status = STATUS_UNSUCCESSFUL; + } + else + { + Status = STATUS_SUCCESS; + } + } + else + { + Status = STATUS_NO_MEMORY; + } + ConioUnlockConsole(Console); + } + + return Status; +} + +CSR_API(CsrGetTitle) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + DWORD Length; + + DPRINT("CsrGetTitle\n"); + + + if (!Win32CsrValidateBuffer(ProcessData, Request->Data.GetTitleRequest.Title, + Request->Data.GetTitleRequest.Length, 1)) + { + return STATUS_ACCESS_VIOLATION; + } + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + /* Copy title of the console to the user title buffer */ + if (Request->Data.GetTitleRequest.Length >= sizeof(WCHAR)) + { + Length = min(Request->Data.GetTitleRequest.Length - sizeof(WCHAR), Console->Title.Length); + memcpy(Request->Data.GetTitleRequest.Title, Console->Title.Buffer, Length); + Request->Data.GetTitleRequest.Title[Length / sizeof(WCHAR)] = L'\0'; + } + + Request->Data.GetTitleRequest.Length = Console->Title.Length; + + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +/********************************************************************** + * HardwareStateProperty + * + * DESCRIPTION + * Set/Get the value of the HardwareState and switch + * between direct video buffer ouput and GDI windowed + * output. + * ARGUMENTS + * Client hands us a CSRSS_CONSOLE_HARDWARE_STATE + * object. We use the same object to Request. + * NOTE + * ConsoleHwState has the correct size to be compatible + * with NT's, but values are not. + */ +static NTSTATUS FASTCALL +SetConsoleHardwareState (PCSRSS_CONSOLE Console, DWORD ConsoleHwState) +{ + DPRINT1("Console Hardware State: %d\n", ConsoleHwState); + + if ((CONSOLE_HARDWARE_STATE_GDI_MANAGED == ConsoleHwState) + ||(CONSOLE_HARDWARE_STATE_DIRECT == ConsoleHwState)) + { + if (Console->HardwareState != ConsoleHwState) + { + /* TODO: implement switching from full screen to windowed mode */ + /* TODO: or back; now simply store the hardware state */ + Console->HardwareState = ConsoleHwState; + } + + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER_3; /* Client: (handle, set_get, [mode]) */ +} + +CSR_API(CsrHardwareStateProperty) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrHardwareStateProperty\n"); + + Status = ConioLockConsole(ProcessData, + Request->Data.ConsoleHardwareStateRequest.ConsoleHandle, + &Console, + GENERIC_READ); + if (! NT_SUCCESS(Status)) + { + DPRINT1("Failed to get console handle in SetConsoleHardwareState\n"); + return Status; + } + + switch (Request->Data.ConsoleHardwareStateRequest.SetGet) + { + case CONSOLE_HARDWARE_STATE_GET: + Request->Data.ConsoleHardwareStateRequest.State = Console->HardwareState; + break; + + case CONSOLE_HARDWARE_STATE_SET: + DPRINT("Setting console hardware state.\n"); + Status = SetConsoleHardwareState(Console, Request->Data.ConsoleHardwareStateRequest.State); + break; + + default: + Status = STATUS_INVALID_PARAMETER_2; /* Client: (handle, [set_get], mode) */ + break; + } + + ConioUnlockConsole(Console); + + return Status; +} + +CSR_API(CsrGetConsoleWindow) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrGetConsoleWindow\n"); + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Request->Data.GetConsoleWindowRequest.WindowHandle = Console->hWindow; + ConioUnlockConsole(Console); + + return STATUS_SUCCESS; +} + +CSR_API(CsrSetConsoleIcon) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrSetConsoleIcon\n"); + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Status = (ConioChangeIcon(Console, Request->Data.SetConsoleIconRequest.WindowIcon) + ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); + ConioUnlockConsole(Console); + + return Status; +} + +CSR_API(CsrGetConsoleCodePage) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrGetConsoleCodePage\n"); + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Request->Data.GetConsoleCodePage.CodePage = Console->CodePage; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +CSR_API(CsrSetConsoleCodePage) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrSetConsoleCodePage\n"); + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + if (IsValidCodePage(Request->Data.SetConsoleCodePage.CodePage)) + { + Console->CodePage = Request->Data.SetConsoleCodePage.CodePage; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; + } + + ConioUnlockConsole(Console); + return STATUS_INVALID_PARAMETER; +} + +CSR_API(CsrGetConsoleOutputCodePage) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrGetConsoleOutputCodePage\n"); + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Request->Data.GetConsoleOutputCodePage.CodePage = Console->OutputCodePage; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; +} + +CSR_API(CsrSetConsoleOutputCodePage) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + + DPRINT("CsrSetConsoleOutputCodePage\n"); + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + if (IsValidCodePage(Request->Data.SetConsoleOutputCodePage.CodePage)) + { + Console->OutputCodePage = Request->Data.SetConsoleOutputCodePage.CodePage; + ConioUnlockConsole(Console); + return STATUS_SUCCESS; + } + + ConioUnlockConsole(Console); + return STATUS_INVALID_PARAMETER; +} + +CSR_API(CsrGetProcessList) +{ + PDWORD Buffer; + PCSRSS_CONSOLE Console; + PCSR_PROCESS current; + PLIST_ENTRY current_entry; + ULONG nItems = 0; + NTSTATUS Status; + + DPRINT("CsrGetProcessList\n"); + + Buffer = Request->Data.GetProcessListRequest.ProcessId; + if (!Win32CsrValidateBuffer(ProcessData, Buffer, Request->Data.GetProcessListRequest.nMaxIds, sizeof(DWORD))) + return STATUS_ACCESS_VIOLATION; + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + for (current_entry = Console->ProcessList.Flink; + current_entry != &Console->ProcessList; + current_entry = current_entry->Flink) + { + current = CONTAINING_RECORD(current_entry, CSR_PROCESS, ConsoleLink); + if (++nItems <= Request->Data.GetProcessListRequest.nMaxIds) + { + *Buffer++ = HandleToUlong(current->ClientId.UniqueProcess); + } + } + + ConioUnlockConsole(Console); + + Request->Data.GetProcessListRequest.nProcessIdsTotal = nItems; + return STATUS_SUCCESS; +} + +CSR_API(CsrGenerateCtrlEvent) +{ + PCSRSS_CONSOLE Console; + PCSR_PROCESS current; + PLIST_ENTRY current_entry; + DWORD Group; + NTSTATUS Status; + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (! NT_SUCCESS(Status)) + { + return Status; + } + + Group = Request->Data.GenerateCtrlEvent.ProcessGroup; + Status = STATUS_INVALID_PARAMETER; + for (current_entry = Console->ProcessList.Flink; + current_entry != &Console->ProcessList; + current_entry = current_entry->Flink) + { + current = CONTAINING_RECORD(current_entry, CSR_PROCESS, ConsoleLink); + if (Group == 0 || current->ProcessGroupId == Group) + { + ConioConsoleCtrlEvent(Request->Data.GenerateCtrlEvent.Event, current); + Status = STATUS_SUCCESS; + } + } + + ConioUnlockConsole(Console); + + return Status; +} + +CSR_API(CsrGetConsoleSelectionInfo) +{ + NTSTATUS Status; + PCSRSS_CONSOLE Console; + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Status)) + { + memset(&Request->Data.GetConsoleSelectionInfo.Info, 0, sizeof(CONSOLE_SELECTION_INFO)); + if (Console->Selection.dwFlags != 0) + Request->Data.GetConsoleSelectionInfo.Info = Console->Selection; + ConioUnlockConsole(Console); + } + return Status; +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/desktopbg.c b/reactos/deprecated/win32csr/desktopbg.c new file mode 100644 index 00000000000..c6ba490b4ab --- /dev/null +++ b/reactos/deprecated/win32csr/desktopbg.c @@ -0,0 +1,27 @@ +#define NDEBUG + +#include "w32csr.h" +#include + +CSR_API(CsrCreateDesktop) +{ + return STATUS_SUCCESS; +} + +CSR_API(CsrShowDesktop) +{ + return STATUS_SUCCESS; +} + +CSR_API(CsrHideDesktop) +{ + return STATUS_SUCCESS; +} + +BOOL +FASTCALL DtbgIsDesktopVisible(VOID) +{ + return !((BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_ISCONSOLEMODE)); +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/desktopbg.h b/reactos/deprecated/win32csr/desktopbg.h new file mode 100644 index 00000000000..84d3859eef3 --- /dev/null +++ b/reactos/deprecated/win32csr/desktopbg.h @@ -0,0 +1,20 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/include/destkopbg.h + * PURPOSE: CSRSS internal desktop background window interface + */ + +#pragma once + +#include "api.h" + +/* Api functions */ +CSR_API(CsrCreateDesktop); +CSR_API(CsrShowDesktop); +CSR_API(CsrHideDesktop); +CSR_API(CsrRegisterSystemClasses); + +BOOL FASTCALL DtbgIsDesktopVisible(VOID); + +/* EOF */ diff --git a/reactos/deprecated/win32csr/dllmain.c b/reactos/deprecated/win32csr/dllmain.c new file mode 100644 index 00000000000..ade36cb9e7b --- /dev/null +++ b/reactos/deprecated/win32csr/dllmain.c @@ -0,0 +1,372 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/win32csr/dllmain.c + * PURPOSE: Initialization + * PROGRAMMERS: Dmitry Philippov (shedon@mail.ru) + */ + +/* INCLUDES ******************************************************************/ +#define NDEBUG +#include "w32csr.h" +#include "file.h" +#include + +/* Not defined in any header file */ +extern VOID WINAPI PrivateCsrssManualGuiCheck(LONG Check); +extern LIST_ENTRY DosDeviceHistory; +extern RTL_CRITICAL_SECTION Win32CsrDefineDosDeviceCritSec; + +/* GLOBALS *******************************************************************/ + +HANDLE Win32CsrApiHeap; +HINSTANCE Win32CsrDllHandle = NULL; + +static CSRSS_API_DEFINITION Win32CsrApiDefinitions[] = +{ + CSRSS_DEFINE_API(GET_INPUT_HANDLE, CsrGetHandle), + CSRSS_DEFINE_API(GET_OUTPUT_HANDLE, CsrGetHandle), + CSRSS_DEFINE_API(CLOSE_HANDLE, CsrCloseHandle), + CSRSS_DEFINE_API(VERIFY_HANDLE, CsrVerifyHandle), + CSRSS_DEFINE_API(DUPLICATE_HANDLE, CsrDuplicateHandle), + CSRSS_DEFINE_API(GET_INPUT_WAIT_HANDLE, CsrGetInputWaitHandle), + CSRSS_DEFINE_API(WRITE_CONSOLE, CsrWriteConsole), + CSRSS_DEFINE_API(READ_CONSOLE, CsrReadConsole), + CSRSS_DEFINE_API(ALLOC_CONSOLE, CsrAllocConsole), + CSRSS_DEFINE_API(FREE_CONSOLE, CsrFreeConsole), + CSRSS_DEFINE_API(SCREEN_BUFFER_INFO, CsrGetScreenBufferInfo), + CSRSS_DEFINE_API(SET_CURSOR, CsrSetCursor), + CSRSS_DEFINE_API(FILL_OUTPUT, CsrFillOutputChar), + CSRSS_DEFINE_API(READ_INPUT, CsrReadInputEvent), + CSRSS_DEFINE_API(WRITE_CONSOLE_OUTPUT_CHAR, CsrWriteConsoleOutputChar), + CSRSS_DEFINE_API(WRITE_CONSOLE_OUTPUT_ATTRIB, CsrWriteConsoleOutputAttrib), + CSRSS_DEFINE_API(FILL_OUTPUT_ATTRIB, CsrFillOutputAttrib), + CSRSS_DEFINE_API(GET_CURSOR_INFO, CsrGetCursorInfo), + CSRSS_DEFINE_API(SET_CURSOR_INFO, CsrSetCursorInfo), + CSRSS_DEFINE_API(SET_ATTRIB, CsrSetTextAttrib), + CSRSS_DEFINE_API(GET_CONSOLE_MODE, CsrGetConsoleMode), + CSRSS_DEFINE_API(SET_CONSOLE_MODE, CsrSetConsoleMode), + CSRSS_DEFINE_API(CREATE_SCREEN_BUFFER, CsrCreateScreenBuffer), + CSRSS_DEFINE_API(SET_SCREEN_BUFFER, CsrSetScreenBuffer), + CSRSS_DEFINE_API(SET_TITLE, CsrSetTitle), + CSRSS_DEFINE_API(GET_TITLE, CsrGetTitle), + CSRSS_DEFINE_API(WRITE_CONSOLE_OUTPUT, CsrWriteConsoleOutput), + CSRSS_DEFINE_API(FLUSH_INPUT_BUFFER, CsrFlushInputBuffer), + CSRSS_DEFINE_API(SCROLL_CONSOLE_SCREEN_BUFFER, CsrScrollConsoleScreenBuffer), + CSRSS_DEFINE_API(READ_CONSOLE_OUTPUT_CHAR, CsrReadConsoleOutputChar), + CSRSS_DEFINE_API(READ_CONSOLE_OUTPUT_ATTRIB, CsrReadConsoleOutputAttrib), + CSRSS_DEFINE_API(GET_NUM_INPUT_EVENTS, CsrGetNumberOfConsoleInputEvents), + CSRSS_DEFINE_API(EXIT_REACTOS, CsrExitReactos), + CSRSS_DEFINE_API(PEEK_CONSOLE_INPUT, CsrPeekConsoleInput), + CSRSS_DEFINE_API(READ_CONSOLE_OUTPUT, CsrReadConsoleOutput), + CSRSS_DEFINE_API(WRITE_CONSOLE_INPUT, CsrWriteConsoleInput), + CSRSS_DEFINE_API(SETGET_CONSOLE_HW_STATE, CsrHardwareStateProperty), + CSRSS_DEFINE_API(GET_CONSOLE_WINDOW, CsrGetConsoleWindow), + CSRSS_DEFINE_API(CREATE_DESKTOP, CsrCreateDesktop), + CSRSS_DEFINE_API(SHOW_DESKTOP, CsrShowDesktop), + CSRSS_DEFINE_API(HIDE_DESKTOP, CsrHideDesktop), + CSRSS_DEFINE_API(SET_CONSOLE_ICON, CsrSetConsoleIcon), + CSRSS_DEFINE_API(SET_LOGON_NOTIFY_WINDOW, CsrSetLogonNotifyWindow), + CSRSS_DEFINE_API(REGISTER_LOGON_PROCESS, CsrRegisterLogonProcess), + CSRSS_DEFINE_API(GET_CONSOLE_CP, CsrGetConsoleCodePage), + CSRSS_DEFINE_API(SET_CONSOLE_CP, CsrSetConsoleCodePage), + CSRSS_DEFINE_API(GET_CONSOLE_OUTPUT_CP, CsrGetConsoleOutputCodePage), + CSRSS_DEFINE_API(SET_CONSOLE_OUTPUT_CP, CsrSetConsoleOutputCodePage), + CSRSS_DEFINE_API(GET_PROCESS_LIST, CsrGetProcessList), + CSRSS_DEFINE_API(ADD_CONSOLE_ALIAS, CsrAddConsoleAlias), + CSRSS_DEFINE_API(GET_CONSOLE_ALIAS, CsrGetConsoleAlias), + CSRSS_DEFINE_API(GET_ALL_CONSOLE_ALIASES, CsrGetAllConsoleAliases), + CSRSS_DEFINE_API(GET_ALL_CONSOLE_ALIASES_LENGTH, CsrGetAllConsoleAliasesLength), + CSRSS_DEFINE_API(GET_CONSOLE_ALIASES_EXES, CsrGetConsoleAliasesExes), + CSRSS_DEFINE_API(GET_CONSOLE_ALIASES_EXES_LENGTH, CsrGetConsoleAliasesExesLength), + CSRSS_DEFINE_API(GENERATE_CTRL_EVENT, CsrGenerateCtrlEvent), + CSRSS_DEFINE_API(SET_SCREEN_BUFFER_SIZE, CsrSetScreenBufferSize), + CSRSS_DEFINE_API(GET_CONSOLE_SELECTION_INFO, CsrGetConsoleSelectionInfo), + CSRSS_DEFINE_API(GET_COMMAND_HISTORY_LENGTH, CsrGetCommandHistoryLength), + CSRSS_DEFINE_API(GET_COMMAND_HISTORY, CsrGetCommandHistory), + CSRSS_DEFINE_API(EXPUNGE_COMMAND_HISTORY, CsrExpungeCommandHistory), + CSRSS_DEFINE_API(SET_HISTORY_NUMBER_COMMANDS, CsrSetHistoryNumberCommands), + CSRSS_DEFINE_API(GET_HISTORY_INFO, CsrGetHistoryInfo), + CSRSS_DEFINE_API(SET_HISTORY_INFO, CsrSetHistoryInfo), + CSRSS_DEFINE_API(GET_TEMP_FILE, CsrGetTempFile), + CSRSS_DEFINE_API(DEFINE_DOS_DEVICE, CsrDefineDosDevice), + CSRSS_DEFINE_API(SOUND_SENTRY, CsrSoundSentry), + { 0, 0, NULL } +}; + +static HHOOK hhk = NULL; + +/* FUNCTIONS *****************************************************************/ + +LRESULT +CALLBACK +KeyboardHookProc( + int nCode, + WPARAM wParam, + LPARAM lParam) +{ + return CallNextHookEx(hhk, nCode, wParam, lParam); +} + +ULONG +InitializeVideoAddressSpace(VOID) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING PhysMemName = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory"); + NTSTATUS Status; + HANDLE PhysMemHandle; + PVOID BaseAddress; + LARGE_INTEGER Offset; + SIZE_T ViewSize; + CHAR IVTAndBda[1024+256]; + + /* Free the 1MB pre-reserved region. In reality, ReactOS should simply support us mapping the view into the reserved area, but it doesn't. */ + BaseAddress = 0; + ViewSize = 1024 * 1024; + Status = ZwFreeVirtualMemory(NtCurrentProcess(), + &BaseAddress, + &ViewSize, + MEM_RELEASE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Couldn't unmap reserved memory (%x)\n", Status); + return 0; + } + + /* Open the physical memory section */ + InitializeObjectAttributes(&ObjectAttributes, + &PhysMemName, + 0, + NULL, + NULL); + Status = ZwOpenSection(&PhysMemHandle, + SECTION_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Couldn't open \\Device\\PhysicalMemory\n"); + return 0; + } + + /* Map the BIOS and device registers into the address space */ + Offset.QuadPart = 0xa0000; + ViewSize = 0x100000 - 0xa0000; + BaseAddress = (PVOID)0xa0000; + Status = ZwMapViewOfSection(PhysMemHandle, + NtCurrentProcess(), + &BaseAddress, + 0, + ViewSize, + &Offset, + &ViewSize, + ViewUnmap, + 0, + PAGE_EXECUTE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Couldn't map physical memory (%x)\n", Status); + ZwClose(PhysMemHandle); + return 0; + } + + /* Close physical memory section handle */ + ZwClose(PhysMemHandle); + + if (BaseAddress != (PVOID)0xa0000) + { + DPRINT1("Couldn't map physical memory at the right address (was %x)\n", + BaseAddress); + return 0; + } + + /* Allocate some low memory to use for the non-BIOS + * parts of the v86 mode address space + */ + BaseAddress = (PVOID)0x1; + ViewSize = 0xa0000 - 0x1000; + Status = ZwAllocateVirtualMemory(NtCurrentProcess(), + &BaseAddress, + 0, + &ViewSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to allocate virtual memory (Status %x)\n", Status); + return 0; + } + if (BaseAddress != (PVOID)0x0) + { + DPRINT1("Failed to allocate virtual memory at right address (was %x)\n", + BaseAddress); + return 0; + } + + /* Get the real mode IVT and BDA from the kernel */ + Status = NtVdmControl(VdmInitialize, IVTAndBda); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtVdmControl failed (status %x)\n", Status); + return 0; + } + + /* Return success */ + return 1; +} + +/********************************************************************** + * CsrpInitVideo/3 + * + * TODO: we need a virtual device for sessions other than + * TODO: the console one + */ +NTSTATUS +CsrpInitVideo (VOID) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\??\\DISPLAY1"); + IO_STATUS_BLOCK Iosb; + HANDLE VideoHandle = (HANDLE) 0; + NTSTATUS Status = STATUS_SUCCESS; + + DPRINT("CSR: %s called\n", __FUNCTION__); + + InitializeVideoAddressSpace(); + + InitializeObjectAttributes(&ObjectAttributes, + &DeviceName, + 0, + NULL, + NULL); + Status = NtOpenFile(&VideoHandle, + FILE_ALL_ACCESS, + &ObjectAttributes, + &Iosb, + 0, + 0); + if (NT_SUCCESS(Status)) + { + NtClose(VideoHandle); + } + return Status; +} + +BOOL WINAPI +DllMain(HANDLE hDll, + DWORD dwReason, + LPVOID lpReserved) +{ + if (DLL_PROCESS_ATTACH == dwReason) + { + Win32CsrDllHandle = hDll; +// +// HACK HACK HACK ReactOS to BOOT! Initialization BUG ALERT! See bug 5655. +// + hhk = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProc, NULL, 0); +// BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! +// BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! +// BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! BUG ALERT! + } + + if (DLL_PROCESS_DETACH == dwReason) + { + CsrCleanupDefineDosDevice(); + } + return TRUE; +} + +/* Ensure that a captured buffer is safe to access */ +BOOL FASTCALL +Win32CsrValidateBuffer(PCSR_PROCESS ProcessData, PVOID Buffer, + SIZE_T NumElements, SIZE_T ElementSize) +{ + /* Check that the following conditions are true: + * 1. The start of the buffer is somewhere within the process's + * shared memory section view. + * 2. The remaining space in the view is at least as large as the buffer. + * (NB: Please don't try to "optimize" this by using multiplication + * instead of division; remember that 2147483648 * 2 = 0.) + * 3. The buffer is DWORD-aligned. + */ + ULONG_PTR Offset = (BYTE *)Buffer - (BYTE *)ProcessData->ClientViewBase; + if (Offset >= ProcessData->ClientViewBounds + || NumElements > (ProcessData->ClientViewBounds - Offset) / ElementSize + || (Offset & (sizeof(DWORD) - 1)) != 0) + { + DPRINT1("Invalid buffer %p(%u*%u); section view is %p(%u)\n", + Buffer, NumElements, ElementSize, + ProcessData->ClientViewBase, ProcessData->ClientViewBounds); + return FALSE; + } + return TRUE; +} + +NTSTATUS FASTCALL +Win32CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc, + PVOID Context) +{ + return CsrEnumProcesses(EnumProc, Context); +} + +VOID +WINAPI +PrivateCsrssManualGuiCheck(LONG Check) +{ + NtUserCallOneParam(Check, ONEPARAM_ROUTINE_CSRSS_GUICHECK); +} + +DWORD +WINAPI +CreateSystemThreads(PVOID pParam) +{ + NtUserCallOneParam((DWORD)pParam, ONEPARAM_ROUTINE_CREATESYSTEMTHREADS); + DPRINT1("This thread should not terminate!\n"); + return 0; +} + +NTSTATUS +WINAPI +Win32CsrInitialization(IN PCSR_SERVER_DLL ServerDll) +{ + HANDLE ServerThread; + CLIENT_ID ClientId; + NTSTATUS Status; + UINT i; + + Win32CsrApiHeap = RtlGetProcessHeap(); + + CsrpInitVideo(); + + NtUserInitialize(0, NULL, NULL); + + PrivateCsrssManualGuiCheck(0); + CsrInitConsoleSupport(); + + /* HACK */ + ServerDll->DispatchTable = (PVOID)Win32CsrApiDefinitions; + ServerDll->HighestApiSupported = 0xDEADBABE; + + ServerDll->HardErrorCallback = Win32CsrHardError; + ServerDll->NewProcessCallback = Win32CsrDuplicateHandleTable; + ServerDll->DisconnectCallback = Win32CsrReleaseConsole; + + RtlInitializeCriticalSection(&Win32CsrDefineDosDeviceCritSec); + InitializeListHead(&DosDeviceHistory); + + /* Start the Raw Input Thread and the Desktop Thread */ + for (i = 0; i < 2; ++i) + { + Status = RtlCreateUserThread(NtCurrentProcess(), NULL, TRUE, 0, 0, 0, (PTHREAD_START_ROUTINE)CreateSystemThreads, (PVOID)i, &ServerThread, &ClientId); + if (NT_SUCCESS(Status)) + { + NtResumeThread(ServerThread, NULL); + NtClose(ServerThread); + } + else + DPRINT1("Cannot start Raw Input Thread!\n"); + } + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/exitros.c b/reactos/deprecated/win32csr/exitros.c new file mode 100644 index 00000000000..0b92f35c41a --- /dev/null +++ b/reactos/deprecated/win32csr/exitros.c @@ -0,0 +1,912 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS CSRSS subsystem + * FILE: subsys/csrss/win32csr/exitros.c + * PURPOSE: Logout/shutdown + */ + +/* INCLUDES ******************************************************************/ +#define NDEBUG +#include "w32csr.h" +#include +#include "resource.h" +#include + +static HANDLE LogonProcess = NULL; + +CSR_API(CsrRegisterLogonProcess) +{ + if (Request->Data.RegisterLogonProcessRequest.Register) + { + if (0 != LogonProcess) + { + return STATUS_LOGON_SESSION_EXISTS; + } + LogonProcess = Request->Data.RegisterLogonProcessRequest.ProcessId; + } + else + { + if (Request->Header.ClientId.UniqueProcess != LogonProcess) + { + DPRINT1("Current logon process 0x%x, can't deregister from process 0x%x\n", + LogonProcess, Request->Header.ClientId.UniqueProcess); + return STATUS_NOT_LOGON_PROCESS; + } + LogonProcess = 0; + } + + return STATUS_SUCCESS; +} + +CSR_API(CsrSetLogonNotifyWindow) +{ + return STATUS_SUCCESS; +} + +typedef struct tagSHUTDOWN_SETTINGS +{ + BOOL AutoEndTasks; + DWORD HungAppTimeout; + DWORD WaitToKillAppTimeout; +} SHUTDOWN_SETTINGS, *PSHUTDOWN_SETTINGS; + +#define DEFAULT_AUTO_END_TASKS FALSE +#define DEFAULT_HUNG_APP_TIMEOUT 5000 +#define DEFAULT_WAIT_TO_KILL_APP_TIMEOUT 20000 + +typedef struct tagNOTIFY_CONTEXT +{ + DWORD ProcessId; + UINT Msg; + WPARAM wParam; + LPARAM lParam; + HDESK Desktop; + HDESK OldDesktop; + DWORD StartTime; + DWORD QueryResult; + HWND Dlg; + DWORD EndNowResult; + BOOL ShowUI; + HANDLE UIThread; + HWND WndClient; + PSHUTDOWN_SETTINGS ShutdownSettings; + LPTHREAD_START_ROUTINE SendMessageProc; +} NOTIFY_CONTEXT, *PNOTIFY_CONTEXT; + +#define QUERY_RESULT_ABORT 0 +#define QUERY_RESULT_CONTINUE 1 +#define QUERY_RESULT_TIMEOUT 2 +#define QUERY_RESULT_ERROR 3 +#define QUERY_RESULT_FORCE 4 + +static void FASTCALL +UpdateProgressBar(HWND ProgressBar, PNOTIFY_CONTEXT NotifyContext) +{ + DWORD Passed; + + Passed = GetTickCount() - NotifyContext->StartTime; + Passed -= NotifyContext->ShutdownSettings->HungAppTimeout; + if (NotifyContext->ShutdownSettings->WaitToKillAppTimeout < Passed) + { + Passed = NotifyContext->ShutdownSettings->WaitToKillAppTimeout; + } + SendMessageW(ProgressBar, PBM_SETPOS, Passed / 2, 0); +} + +static INT_PTR CALLBACK +EndNowDlgProc(HWND Dlg, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + INT_PTR Result; + PNOTIFY_CONTEXT NotifyContext; + HWND ProgressBar; + DWORD TitleLength; + int Len; + LPWSTR Title; + + switch(Msg) + { + case WM_INITDIALOG: + NotifyContext = (PNOTIFY_CONTEXT) lParam; + NotifyContext->EndNowResult = QUERY_RESULT_ABORT; + SetWindowLongPtrW(Dlg, DWLP_USER, (LONG_PTR) lParam); + TitleLength = SendMessageW(NotifyContext->WndClient, WM_GETTEXTLENGTH, + 0, 0) + + GetWindowTextLengthW(Dlg); + Title = HeapAlloc(Win32CsrApiHeap, 0, (TitleLength + 1) * sizeof(WCHAR)); + if (NULL != Title) + { + Len = GetWindowTextW(Dlg, Title, TitleLength + 1); + SendMessageW(NotifyContext->WndClient, WM_GETTEXT, + TitleLength + 1 - Len, (LPARAM) (Title + Len)); + SetWindowTextW(Dlg, Title); + HeapFree(Win32CsrApiHeap, 0, Title); + } + ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS); + SendMessageW(ProgressBar, PBM_SETRANGE32, 0, + NotifyContext->ShutdownSettings->WaitToKillAppTimeout / 2); + UpdateProgressBar(ProgressBar, NotifyContext); + SetTimer(Dlg, 0, 200, NULL); + Result = FALSE; + break; + + case WM_TIMER: + NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER); + ProgressBar = GetDlgItem(Dlg, IDC_PROGRESS); + UpdateProgressBar(ProgressBar, NotifyContext); + Result = TRUE; + break; + + case WM_COMMAND: + if (BN_CLICKED == HIWORD(wParam) && IDC_END_NOW == LOWORD(wParam)) + { + NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER); + NotifyContext->EndNowResult = QUERY_RESULT_FORCE; + SendMessageW(Dlg, WM_CLOSE, 0, 0); + Result = TRUE; + } + else + { + Result = FALSE; + } + break; + + case WM_CLOSE: + DestroyWindow(Dlg); + Result = TRUE; + break; + + case WM_DESTROY: + NotifyContext = (PNOTIFY_CONTEXT) GetWindowLongPtrW(Dlg, DWLP_USER); + NotifyContext->Dlg = NULL; + KillTimer(Dlg, 0); + PostQuitMessage(NotifyContext->EndNowResult); + Result = TRUE; + break; + + default: + Result = FALSE; + break; + } + + return Result; +} + +typedef void (WINAPI *INITCOMMONCONTROLS_PROC)(void); + +static void +CallInitCommonControls() +{ + static BOOL Initialized = FALSE; + HMODULE Lib; + INITCOMMONCONTROLS_PROC InitProc; + + if (Initialized) + { + return; + } + + Lib = LoadLibraryW(L"COMCTL32.DLL"); + if (NULL == Lib) + { + return; + } + InitProc = (INITCOMMONCONTROLS_PROC) GetProcAddress(Lib, "InitCommonControls"); + if (NULL == InitProc) + { + return; + } + + (*InitProc)(); + + Initialized = TRUE; +} + +static DWORD WINAPI +EndNowThreadProc(LPVOID Parameter) +{ + PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) Parameter; + MSG Msg; + + SetThreadDesktop(NotifyContext->Desktop); + SwitchDesktop(NotifyContext->Desktop); + CallInitCommonControls(); + NotifyContext->Dlg = CreateDialogParam(GetModuleHandleW(L"win32csr"), + MAKEINTRESOURCE(IDD_END_NOW), NULL, + EndNowDlgProc, (LPARAM) NotifyContext); + if (NULL == NotifyContext->Dlg) + { + return 0; + } + ShowWindow(NotifyContext->Dlg, SW_SHOWNORMAL); + + while (GetMessageW(&Msg, NULL, 0, 0)) + { + if (! IsDialogMessage(NotifyContext->Dlg, &Msg)) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + } + } + + return Msg.wParam; +} + +typedef struct tagMESSAGE_CONTEXT +{ + HWND Wnd; + UINT Msg; + WPARAM wParam; + LPARAM lParam; + DWORD Timeout; +} MESSAGE_CONTEXT, *PMESSAGE_CONTEXT; + +static DWORD WINAPI +SendQueryEndSession(LPVOID Parameter) +{ + PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter; + DWORD_PTR Result; + + if (SendMessageTimeoutW(Context->Wnd, WM_QUERYENDSESSION, Context->wParam, + Context->lParam, SMTO_NORMAL, Context->Timeout, + &Result)) + { + return Result ? QUERY_RESULT_CONTINUE : QUERY_RESULT_ABORT; + } + + return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR; +} + +static DWORD WINAPI +SendEndSession(LPVOID Parameter) +{ + PMESSAGE_CONTEXT Context = (PMESSAGE_CONTEXT) Parameter; + DWORD_PTR Result; + + if (Context->wParam) + { + if (SendMessageTimeoutW(Context->Wnd, WM_ENDSESSION, Context->wParam, + Context->lParam, SMTO_NORMAL, Context->Timeout, + &Result)) + { + return QUERY_RESULT_CONTINUE; + } + return 0 == GetLastError() ? QUERY_RESULT_TIMEOUT : QUERY_RESULT_ERROR; + } + else + { + SendMessage(Context->Wnd, WM_ENDSESSION, Context->wParam, + Context->lParam); + return QUERY_RESULT_CONTINUE; + } +} + +static BOOL CALLBACK +NotifyTopLevelEnum(HWND Wnd, LPARAM lParam) +{ + PNOTIFY_CONTEXT NotifyContext = (PNOTIFY_CONTEXT) lParam; + MESSAGE_CONTEXT MessageContext; + DWORD Now, Passed; + DWORD Timeout, WaitStatus; + DWORD ProcessId; + HANDLE MessageThread; + HANDLE Threads[2]; + + if (0 == GetWindowThreadProcessId(Wnd, &ProcessId)) + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + return FALSE; + } + + if (ProcessId == NotifyContext->ProcessId) + { + Now = GetTickCount(); + if (0 == NotifyContext->StartTime) + { + NotifyContext->StartTime = Now; + } + /* Note: Passed is computed correctly even when GetTickCount() wraps due + to unsigned arithmetic */ + Passed = Now - NotifyContext->StartTime; + MessageContext.Wnd = Wnd; + MessageContext.Msg = NotifyContext->Msg; + MessageContext.wParam = NotifyContext->wParam; + MessageContext.lParam = NotifyContext->lParam; + MessageContext.Timeout = NotifyContext->ShutdownSettings->HungAppTimeout; + if (! NotifyContext->ShutdownSettings->AutoEndTasks) + { + MessageContext.Timeout += NotifyContext->ShutdownSettings->WaitToKillAppTimeout; + } + if (Passed < MessageContext.Timeout) + { + MessageContext.Timeout -= Passed; + MessageThread = CreateThread(NULL, 0, NotifyContext->SendMessageProc, + (LPVOID) &MessageContext, 0, NULL); + if (NULL == MessageThread) + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + return FALSE; + } + Timeout = NotifyContext->ShutdownSettings->HungAppTimeout; + if (Passed < Timeout) + { + Timeout -= Passed; + WaitStatus = WaitForSingleObjectEx(MessageThread, Timeout, FALSE); + } + else + { + WaitStatus = WAIT_TIMEOUT; + } + if (WAIT_TIMEOUT == WaitStatus) + { + NotifyContext->WndClient = Wnd; + if (NULL == NotifyContext->UIThread && NotifyContext->ShowUI) + { + NotifyContext->UIThread = CreateThread(NULL, 0, + EndNowThreadProc, + (LPVOID) NotifyContext, + 0, NULL); + } + Threads[0] = MessageThread; + Threads[1] = NotifyContext->UIThread; + WaitStatus = WaitForMultipleObjectsEx(NULL == NotifyContext->UIThread ? + 1 : 2, + Threads, FALSE, INFINITE, + FALSE); + if (WAIT_OBJECT_0 == WaitStatus) + { + if (! GetExitCodeThread(MessageThread, &NotifyContext->QueryResult)) + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + } + } + else if (WAIT_OBJECT_0 + 1 == WaitStatus) + { + if (! GetExitCodeThread(NotifyContext->UIThread, + &NotifyContext->QueryResult)) + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + } + } + else + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + } + if (WAIT_OBJECT_0 != WaitStatus) + { + TerminateThread(MessageThread, QUERY_RESULT_TIMEOUT); + } + } + else if (WAIT_OBJECT_0 == WaitStatus) + { + if (! GetExitCodeThread(MessageThread, + &NotifyContext->QueryResult)) + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + } + } + else + { + NotifyContext->QueryResult = QUERY_RESULT_ERROR; + } + CloseHandle(MessageThread); + } + else + { + NotifyContext->QueryResult = QUERY_RESULT_TIMEOUT; + } + } + + return QUERY_RESULT_CONTINUE == NotifyContext->QueryResult; +} + +static BOOL CALLBACK +NotifyDesktopEnum(LPWSTR DesktopName, LPARAM lParam) +{ + PNOTIFY_CONTEXT Context = (PNOTIFY_CONTEXT) lParam; + + Context->Desktop = OpenDesktopW(DesktopName, 0, FALSE, + DESKTOP_ENUMERATE | DESKTOP_SWITCHDESKTOP); + if (NULL == Context->Desktop) + { + DPRINT1("OpenDesktop failed with error %d\n", GetLastError()); + Context->QueryResult = QUERY_RESULT_ERROR; + return FALSE; + } + + Context->OldDesktop = GetThreadDesktop(GetCurrentThreadId()); + SwitchDesktop(Context->Desktop); + + EnumDesktopWindows(Context->Desktop, NotifyTopLevelEnum, lParam); + + SwitchDesktop(Context->OldDesktop); + + CloseDesktop(Context->Desktop); + + return QUERY_RESULT_CONTINUE == Context->QueryResult; +} + +static BOOL FASTCALL +NotifyTopLevelWindows(PNOTIFY_CONTEXT Context) +{ + HWINSTA WindowStation; + + WindowStation = GetProcessWindowStation(); + if (NULL == WindowStation) + { + DPRINT1("GetProcessWindowStation failed with error %d\n", GetLastError()); + return TRUE; + } + + EnumDesktopsW(WindowStation, NotifyDesktopEnum, (LPARAM) Context); + + return TRUE; +} + +static BOOL FASTCALL +NotifyAndTerminateProcess(PCSR_PROCESS ProcessData, + PSHUTDOWN_SETTINGS ShutdownSettings, + UINT Flags) +{ + NOTIFY_CONTEXT Context; + HANDLE Process; + DWORD QueryResult = QUERY_RESULT_CONTINUE; + + Context.QueryResult = QUERY_RESULT_CONTINUE; + + if (0 == (Flags & EWX_FORCE)) + { + if (NULL != ProcessData->Console) + { + ConioConsoleCtrlEventTimeout(CTRL_LOGOFF_EVENT, ProcessData, + ShutdownSettings->WaitToKillAppTimeout); + } + else + { + Context.ProcessId = (DWORD_PTR) ProcessData->ClientId.UniqueProcess; + Context.wParam = 0; + Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ? + ENDSESSION_LOGOFF : 0); + Context.StartTime = 0; + Context.UIThread = NULL; + Context.ShowUI = DtbgIsDesktopVisible(); + Context.Dlg = NULL; + Context.ShutdownSettings = ShutdownSettings; + Context.SendMessageProc = SendQueryEndSession; + + NotifyTopLevelWindows(&Context); + + Context.wParam = (QUERY_RESULT_ABORT != Context.QueryResult); + Context.lParam = (0 != (Flags & EWX_INTERNAL_FLAG_LOGOFF) ? + ENDSESSION_LOGOFF : 0); + Context.SendMessageProc = SendEndSession; + Context.ShowUI = DtbgIsDesktopVisible() && + (QUERY_RESULT_ABORT != Context.QueryResult); + QueryResult = Context.QueryResult; + Context.QueryResult = QUERY_RESULT_CONTINUE; + + NotifyTopLevelWindows(&Context); + + if (NULL != Context.UIThread) + { + if (NULL != Context.Dlg) + { + SendMessageW(Context.Dlg, WM_CLOSE, 0, 0); + } + else + { + TerminateThread(Context.UIThread, QUERY_RESULT_ERROR); + } + CloseHandle(Context.UIThread); + } + } + + if (QUERY_RESULT_ABORT == QueryResult) + { + return FALSE; + } + } + + /* Terminate this process */ + Process = OpenProcess(PROCESS_TERMINATE, FALSE, + (DWORD_PTR) ProcessData->ClientId.UniqueProcess); + if (NULL == Process) + { + DPRINT1("Unable to open process %d, error %d\n", ProcessData->ClientId.UniqueProcess, + GetLastError()); + return TRUE; + } + TerminateProcess(Process, 0); + CloseHandle(Process); + + return TRUE; +} + +typedef struct tagPROCESS_ENUM_CONTEXT +{ + UINT ProcessCount; + PCSR_PROCESS *ProcessData; + TOKEN_ORIGIN TokenOrigin; + DWORD ShellProcess; + DWORD CsrssProcess; +} PROCESS_ENUM_CONTEXT, *PPROCESS_ENUM_CONTEXT; + +static NTSTATUS WINAPI +ExitReactosProcessEnum(PCSR_PROCESS ProcessData, PVOID Data) +{ + HANDLE Process; + HANDLE Token; + TOKEN_ORIGIN Origin; + DWORD ReturnLength; + PPROCESS_ENUM_CONTEXT Context = (PPROCESS_ENUM_CONTEXT) Data; + PCSR_PROCESS *NewData; + + /* Do not kill winlogon or csrss */ + if ((DWORD_PTR) ProcessData->ClientId.UniqueProcess == Context->CsrssProcess || + ProcessData->ClientId.UniqueProcess == LogonProcess) + { + return STATUS_SUCCESS; + } + + /* Get the login session of this process */ + Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, + (DWORD_PTR) ProcessData->ClientId.UniqueProcess); + if (NULL == Process) + { + DPRINT1("Unable to open process %d, error %d\n", ProcessData->ClientId.UniqueProcess, + GetLastError()); + return STATUS_UNSUCCESSFUL; + } + + if (! OpenProcessToken(Process, TOKEN_QUERY, &Token)) + { + DPRINT1("Unable to open token for process %d, error %d\n", + ProcessData->ClientId.UniqueProcess, GetLastError()); + CloseHandle(Process); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(Process); + + if (! GetTokenInformation(Token, TokenOrigin, &Origin, + sizeof(TOKEN_ORIGIN), &ReturnLength)) + { + DPRINT1("GetTokenInformation failed for process %d with error %d\n", + ProcessData->ClientId.UniqueProcess, GetLastError()); + CloseHandle(Token); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(Token); + + /* This process will be killed if it's in the correct logon session */ + if (RtlEqualLuid(&(Context->TokenOrigin.OriginatingLogonSession), + &(Origin.OriginatingLogonSession))) + { + /* Kill the shell process last */ + if ((DWORD_PTR) ProcessData->ClientId.UniqueProcess == Context->ShellProcess) + { + ProcessData->ShutdownLevel = 0; + } + NewData = HeapAlloc(Win32CsrApiHeap, 0, (Context->ProcessCount + 1) + * sizeof(PCSR_PROCESS)); + if (NULL == NewData) + { + return STATUS_NO_MEMORY; + } + if (0 != Context->ProcessCount) + { + memcpy(NewData, Context->ProcessData, + Context->ProcessCount * sizeof(PCSR_PROCESS)); + HeapFree(Win32CsrApiHeap, 0, Context->ProcessData); + } + Context->ProcessData = NewData; + Context->ProcessData[Context->ProcessCount] = ProcessData; + Context->ProcessCount++; + } + + return STATUS_SUCCESS; +} + +static int +ProcessDataCompare(const void *Elem1, const void *Elem2) +{ + const PCSR_PROCESS *ProcessData1 = (PCSR_PROCESS *) Elem1; + const PCSR_PROCESS *ProcessData2 = (PCSR_PROCESS *) Elem2; + + if ((*ProcessData1)->ShutdownLevel < (*ProcessData2)->ShutdownLevel) + { + return +1; + } + else if ((*ProcessData2)->ShutdownLevel < (*ProcessData1)->ShutdownLevel) + { + return -1; + } + else if ((*ProcessData1)->ClientId.UniqueProcess < (*ProcessData2)->ClientId.UniqueProcess) + { + return +1; + } + else if ((*ProcessData2)->ClientId.UniqueProcess < (*ProcessData1)->ClientId.UniqueProcess) + { + return -1; + } + + return 0; +} + +static DWORD FASTCALL +GetShutdownSetting(HKEY DesktopKey, LPCWSTR ValueName, DWORD DefaultValue) +{ + BYTE ValueBuffer[16]; + LONG ErrCode; + DWORD Type; + DWORD ValueSize; + UNICODE_STRING StringValue; + ULONG Value; + + ValueSize = sizeof(ValueBuffer); + ErrCode = RegQueryValueExW(DesktopKey, ValueName, NULL, &Type, ValueBuffer, + &ValueSize); + if (ERROR_SUCCESS != ErrCode) + { + DPRINT("GetShutdownSetting for %S failed with error code %ld\n", + ValueName, ErrCode); + return DefaultValue; + } + + if (REG_SZ == Type) + { + RtlInitUnicodeString(&StringValue, (LPCWSTR) ValueBuffer); + if (! NT_SUCCESS(RtlUnicodeStringToInteger(&StringValue, 10, &Value))) + { + DPRINT1("Unable to convert value %S for setting %S\n", + StringValue.Buffer, ValueName); + return DefaultValue; + } + return (DWORD) Value; + } + else if (REG_DWORD == Type) + { + return *((DWORD *) ValueBuffer); + } + + DPRINT1("Unexpected registry type %d for setting %S\n", Type, ValueName); + return DefaultValue; +} + +static void FASTCALL +LoadShutdownSettings(PSID Sid, PSHUTDOWN_SETTINGS ShutdownSettings) +{ + static WCHAR Subkey[] = L"\\Control Panel\\Desktop"; + LPWSTR StringSid; + WCHAR InitialKeyName[128]; + LPWSTR KeyName; + HKEY DesktopKey; + LONG ErrCode; + + ShutdownSettings->AutoEndTasks = DEFAULT_AUTO_END_TASKS; + ShutdownSettings->HungAppTimeout = DEFAULT_HUNG_APP_TIMEOUT; + ShutdownSettings->WaitToKillAppTimeout = DEFAULT_WAIT_TO_KILL_APP_TIMEOUT; + + if (! ConvertSidToStringSidW(Sid, &StringSid)) + { + DPRINT1("ConvertSidToStringSid failed with error %d, using default shutdown settings\n", + GetLastError()); + return; + } + if (wcslen(StringSid) + wcslen(Subkey) + 1 <= + sizeof(InitialKeyName) / sizeof(WCHAR)) + { + KeyName = InitialKeyName; + } + else + { + KeyName = HeapAlloc(Win32CsrApiHeap, 0, + (wcslen(StringSid) + wcslen(Subkey) + 1) * + sizeof(WCHAR)); + if (NULL == KeyName) + { + DPRINT1("Failed to allocate memory, using default shutdown settings\n"); + LocalFree(StringSid); + return; + } + } + wcscat(wcscpy(KeyName, StringSid), Subkey); + LocalFree(StringSid); + + ErrCode = RegOpenKeyExW(HKEY_USERS, KeyName, 0, KEY_QUERY_VALUE, &DesktopKey); + if (KeyName != InitialKeyName) + { + HeapFree(Win32CsrApiHeap, 0, KeyName); + } + if (ERROR_SUCCESS != ErrCode) + { + DPRINT1("RegOpenKeyEx failed with error %ld, using default shutdown settings\n", ErrCode); + return; + } + + ShutdownSettings->AutoEndTasks = (BOOL) GetShutdownSetting(DesktopKey, L"AutoEndTasks", + (DWORD) DEFAULT_AUTO_END_TASKS); + ShutdownSettings->HungAppTimeout = GetShutdownSetting(DesktopKey, + L"HungAppTimeout", + DEFAULT_HUNG_APP_TIMEOUT); + ShutdownSettings->WaitToKillAppTimeout = GetShutdownSetting(DesktopKey, + L"WaitToKillAppTimeout", + DEFAULT_WAIT_TO_KILL_APP_TIMEOUT); + + RegCloseKey(DesktopKey); +} + +static NTSTATUS FASTCALL +InternalExitReactos(DWORD ProcessId, DWORD ThreadId, UINT Flags) +{ + HANDLE CallerThread; + HANDLE CallerToken; + NTSTATUS Status; + PROCESS_ENUM_CONTEXT Context; + DWORD ReturnLength; + HWND ShellWnd; + UINT ProcessIndex; + char FixedUserInfo[64]; + TOKEN_USER *UserInfo; + SHUTDOWN_SETTINGS ShutdownSettings; + + if (ProcessId != (DWORD_PTR) LogonProcess) + { + DPRINT1("Internal ExitWindowsEx call not from winlogon\n"); + return STATUS_ACCESS_DENIED; + } + + DPRINT1("FIXME: Need to close all user processes!\n"); + return STATUS_SUCCESS; + + CallerThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, ThreadId); + if (NULL == CallerThread) + { + DPRINT1("OpenThread failed with error %d\n", GetLastError()); + return STATUS_UNSUCCESSFUL; + } + if (! OpenThreadToken(CallerThread, TOKEN_QUERY, FALSE, &CallerToken)) + { + DPRINT1("OpenThreadToken failed with error %d\n", GetLastError()); + CloseHandle(CallerThread); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(CallerThread); + + Context.ProcessCount = 0; + Context.ProcessData = NULL; + if (! GetTokenInformation(CallerToken, TokenOrigin, &Context.TokenOrigin, + sizeof(TOKEN_ORIGIN), &ReturnLength)) + { + DPRINT1("GetTokenInformation failed with error %d\n", GetLastError()); + CloseHandle(CallerToken); + return STATUS_UNSUCCESSFUL; + } + if (! GetTokenInformation(CallerToken, TokenUser, FixedUserInfo, + sizeof(FixedUserInfo), &ReturnLength)) + { + if (sizeof(FixedUserInfo) < ReturnLength) + { + UserInfo = HeapAlloc(Win32CsrApiHeap, 0, ReturnLength); + if (NULL == UserInfo) + { + DPRINT1("Unable to allocate %u bytes for user info\n", + (unsigned) ReturnLength); + CloseHandle(CallerToken); + return STATUS_NO_MEMORY; + } + if (! GetTokenInformation(CallerToken, TokenUser, UserInfo, + ReturnLength, &ReturnLength)) + { + DPRINT1("GetTokenInformation failed with error %d\n", + GetLastError()); + HeapFree(Win32CsrApiHeap, 0, UserInfo); + CloseHandle(CallerToken); + return STATUS_UNSUCCESSFUL; + } + } + else + { + DPRINT1("GetTokenInformation failed with error %d\n", GetLastError()); + CloseHandle(CallerToken); + return STATUS_UNSUCCESSFUL; + } + } + else + { + UserInfo = (TOKEN_USER *) FixedUserInfo; + } + CloseHandle(CallerToken); + LoadShutdownSettings(UserInfo->User.Sid, &ShutdownSettings); + if (UserInfo != (TOKEN_USER *) FixedUserInfo) + { + HeapFree(Win32CsrApiHeap, 0, UserInfo); + } + Context.CsrssProcess = GetCurrentProcessId(); + ShellWnd = GetShellWindow(); + if (NULL == ShellWnd) + { + DPRINT("No shell present\n"); + Context.ShellProcess = 0; + } + else if (0 == GetWindowThreadProcessId(ShellWnd, &Context.ShellProcess)) + { + DPRINT1("Can't get process id of shell window\n"); + Context.ShellProcess = 0; + } + + Status = Win32CsrEnumProcesses(ExitReactosProcessEnum, &Context); + if (! NT_SUCCESS(Status)) + { + DPRINT1("Failed to enumerate registered processes, status 0x%x\n", + Status); + if (NULL != Context.ProcessData) + { + HeapFree(Win32CsrApiHeap, 0, Context.ProcessData); + } + return Status; + } + + qsort(Context.ProcessData, Context.ProcessCount, sizeof(PCSR_PROCESS), + ProcessDataCompare); + + /* Terminate processes, stop if we find one kicking and screaming it doesn't + want to die */ + Status = STATUS_SUCCESS; + for (ProcessIndex = 0; + ProcessIndex < Context.ProcessCount && NT_SUCCESS(Status); + ProcessIndex++) + { + if (! NotifyAndTerminateProcess(Context.ProcessData[ProcessIndex], + &ShutdownSettings, Flags)) + { + Status = STATUS_REQUEST_ABORTED; + } + } + + /* Cleanup */ + if (NULL != Context.ProcessData) + { + HeapFree(Win32CsrApiHeap, 0, Context.ProcessData); + } + + return Status; +} + +static NTSTATUS FASTCALL +UserExitReactos(DWORD UserProcessId, UINT Flags) +{ + NTSTATUS Status; + + /* FIXME Inside 2000 says we should impersonate the caller here */ + Status = NtUserCallTwoParam (UserProcessId, Flags, TWOPARAM_ROUTINE_EXITREACTOS); + + /* If the message isn't handled, the return value is 0, so 0 doesn't indicate + success. Success is indicated by a 1 return value, if anything besides 0 + or 1 it's a NTSTATUS value */ + if (1 == Status) + { + Status = STATUS_SUCCESS; + } + else if (0 == Status) + { + Status = STATUS_NOT_IMPLEMENTED; + } + + return Status; +} + +CSR_API(CsrExitReactos) +{ + if (0 == (Request->Data.ExitReactosRequest.Flags & EWX_INTERNAL_FLAG)) + { + return UserExitReactos((DWORD_PTR) Request->Header.ClientId.UniqueProcess, + Request->Data.ExitReactosRequest.Flags); + } + else + { + return InternalExitReactos((DWORD_PTR) Request->Header.ClientId.UniqueProcess, + (DWORD_PTR) Request->Header.ClientId.UniqueThread, + Request->Data.ExitReactosRequest.Flags); + } +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/file.c b/reactos/deprecated/win32csr/file.c new file mode 100644 index 00000000000..638769fad28 --- /dev/null +++ b/reactos/deprecated/win32csr/file.c @@ -0,0 +1,586 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/win32csr/file.c + * PURPOSE: File handling + * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org) + * NOTE: Belongs to basesrv.dll + */ + +/* INCLUDES ******************************************************************/ + +#include +#define NDEBUG +#include +#include "file.h" + +/* GLOBALS *******************************************************************/ + +typedef BOOL (WINAPI *PUSER_SOUND_SENTRY)(VOID); +BOOL +WINAPI +FirstSoundSentry(VOID); + +UINT CsrGetTempFileUnique; +LIST_ENTRY DosDeviceHistory; +RTL_CRITICAL_SECTION Win32CsrDefineDosDeviceCritSec; +PUSER_SOUND_SENTRY _UserSoundSentry = FirstSoundSentry; + +/* FUNCTIONS *****************************************************************/ + +BOOL +WINAPI +FailSoundSentry(VOID) +{ + /* In case the function can't be found/is unimplemented */ + return FALSE; +} + +BOOL +WINAPI +FirstSoundSentry(VOID) +{ + UNICODE_STRING DllString = RTL_CONSTANT_STRING(L"winsrv"); + STRING FuncString = RTL_CONSTANT_STRING("_UserSoundSentry"); + HANDLE DllHandle; + NTSTATUS Status; + PUSER_SOUND_SENTRY NewSoundSentry = FailSoundSentry; + + /* Load winsrv manually */ + Status = LdrGetDllHandle(NULL, NULL, &DllString, &DllHandle); + if (NT_SUCCESS(Status)) + { + /* If it was found, get SoundSentry export */ + Status = LdrGetProcedureAddress(DllHandle, + &FuncString, + 0, + (PVOID*)&NewSoundSentry); + } + + /* Set it as the callback for the future, and call it */ + _UserSoundSentry = NewSoundSentry; + return _UserSoundSentry(); +} + +CSR_API(CsrSoundSentry) +{ + /* Call the API and see if it suceeds */ + return _UserSoundSentry() ? STATUS_SUCCESS : STATUS_ACCESS_DENIED; +} + +CSR_API(CsrGetTempFile) +{ + DPRINT("CsrGetTempFile entered\n"); + + /* Return 16-bits ID */ + Request->Data.GetTempFile.UniqueID = (++CsrGetTempFileUnique & 0xFFFF); + + DPRINT("Returning: %u\n", Request->Data.GetTempFile.UniqueID); + + return STATUS_SUCCESS; +} + +CSR_API(CsrDefineDosDevice) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE LinkHandle = NULL; + NTSTATUS Status; + UNICODE_STRING DeviceName = {0}; + UNICODE_STRING RequestDeviceName = {0}; + UNICODE_STRING LinkTarget = {0}; + PUNICODE_STRING RequestLinkTarget; + ULONG Length; + SID_IDENTIFIER_AUTHORITY WorldAuthority = {SECURITY_WORLD_SID_AUTHORITY}; + SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY}; + PSECURITY_DESCRIPTOR SecurityDescriptor; + PACL Dacl; + PSID AdminSid; + PSID SystemSid; + PSID WorldSid; + ULONG SidLength; + PCSRSS_DOS_DEVICE_HISTORY_ENTRY HistoryEntry; + PLIST_ENTRY Entry; + PLIST_ENTRY ListHead; + BOOLEAN Matched, AddHistory; + DWORD dwFlags; + PWSTR lpBuffer; + + DPRINT("CsrDefineDosDevice entered, Flags:%d, DeviceName:%wZ, TargetName:%wZ\n", + Request->Data.DefineDosDeviceRequest.dwFlags, + &Request->Data.DefineDosDeviceRequest.DeviceName, + &Request->Data.DefineDosDeviceRequest.TargetName); + + Matched = AddHistory = FALSE; + HistoryEntry = NULL; + AdminSid = SystemSid = WorldSid = NULL; + SecurityDescriptor = NULL; + ListHead = &DosDeviceHistory; + dwFlags = Request->Data.DefineDosDeviceRequest.dwFlags; + + /* Validate the flags */ + if ( (dwFlags & 0xFFFFFFF0) || + ((dwFlags & DDD_EXACT_MATCH_ON_REMOVE) && + ! (dwFlags & DDD_REMOVE_DEFINITION)) ) + { + return STATUS_INVALID_PARAMETER; + } + + Status = RtlEnterCriticalSection(&Win32CsrDefineDosDeviceCritSec); + if (! NT_SUCCESS(Status)) + { + DPRINT1("RtlEnterCriticalSection() failed (Status %lx)\n", + Status); + return Status; + } + + _SEH2_TRY + { + Status = + RtlUpcaseUnicodeString(&RequestDeviceName, + &Request->Data.DefineDosDeviceRequest.DeviceName, + TRUE); + if (! NT_SUCCESS(Status)) + _SEH2_LEAVE; + + RequestLinkTarget = + &Request->Data.DefineDosDeviceRequest.TargetName; + lpBuffer = (PWSTR) RtlAllocateHeap(Win32CsrApiHeap, + HEAP_ZERO_MEMORY, + RequestDeviceName.MaximumLength + 5 * sizeof(WCHAR)); + if (! lpBuffer) + { + DPRINT1("Failed to allocate memory\n"); + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + + swprintf(lpBuffer, + L"\\??\\%wZ", + &RequestDeviceName); + RtlInitUnicodeString(&DeviceName, + lpBuffer); + InitializeObjectAttributes(&ObjectAttributes, + &DeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenSymbolicLinkObject(&LinkHandle, + DELETE | 0x1, + &ObjectAttributes); + if (NT_SUCCESS(Status)) + { + Status = NtQuerySymbolicLinkObject(LinkHandle, + &LinkTarget, + &Length); + if (! NT_SUCCESS(Status) && + Status == STATUS_BUFFER_TOO_SMALL) + { + LinkTarget.Length = 0; + LinkTarget.MaximumLength = Length; + LinkTarget.Buffer = (PWSTR) + RtlAllocateHeap(Win32CsrApiHeap, + HEAP_ZERO_MEMORY, + Length); + if (! LinkTarget.Buffer) + { + DPRINT1("Failed to allocate memory\n"); + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + + Status = NtQuerySymbolicLinkObject(LinkHandle, + &LinkTarget, + &Length); + } + + if (! NT_SUCCESS(Status)) + { + DPRINT1("NtQuerySymbolicLinkObject(%wZ) failed (Status %lx)\n", + &DeviceName, Status); + _SEH2_LEAVE; + } + + if ((dwFlags & DDD_REMOVE_DEFINITION)) + { + /* If no target name specified we remove the current symlink target */ + if (RequestLinkTarget->Length == 0) + Matched = TRUE; + else + { + if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE) + Matched = ! RtlCompareUnicodeString(RequestLinkTarget, + &LinkTarget, + TRUE); + else + Matched = RtlPrefixUnicodeString(RequestLinkTarget, + &LinkTarget, + TRUE); + } + + if (Matched && IsListEmpty(ListHead)) + { + /* Current symlink target macthed and there is nothing to revert to */ + RequestLinkTarget = NULL; + } + else if (Matched && ! IsListEmpty(ListHead)) + { + /* Fetch the first history entry we come across for the device name */ + /* This will become the current symlink target for the device name */ + Matched = FALSE; + Entry = ListHead->Flink; + while (Entry != ListHead) + { + HistoryEntry = (PCSRSS_DOS_DEVICE_HISTORY_ENTRY) + CONTAINING_RECORD(Entry, + CSRSS_DOS_DEVICE_HISTORY_ENTRY, + Entry); + Matched = + ! RtlCompareUnicodeString(&RequestDeviceName, + &HistoryEntry->Device, + FALSE); + if (Matched) + { + RemoveEntryList(&HistoryEntry->Entry); + RequestLinkTarget = &HistoryEntry->Target; + break; + } + Entry = Entry->Flink; + HistoryEntry = NULL; + } + + /* Nothing to revert to so delete the symlink */ + if (! Matched) + RequestLinkTarget = NULL; + } + else if (! Matched) + { + /* Locate a previous symlink target as we did not get a hit earlier */ + /* If we find one we need to remove it */ + Entry = ListHead->Flink; + while (Entry != ListHead) + { + HistoryEntry = (PCSRSS_DOS_DEVICE_HISTORY_ENTRY) + CONTAINING_RECORD(Entry, + CSRSS_DOS_DEVICE_HISTORY_ENTRY, + Entry); + Matched = + ! RtlCompareUnicodeString(&RequestDeviceName, + &HistoryEntry->Device, + FALSE); + if (! Matched) + { + HistoryEntry = NULL; + Entry = Entry->Flink; + continue; + } + + Matched = FALSE; + if (dwFlags & DDD_EXACT_MATCH_ON_REMOVE) + { + if (! RtlCompareUnicodeString(RequestLinkTarget, + &HistoryEntry->Target, + TRUE)) + { + Matched = TRUE; + } + } + else if (RtlPrefixUnicodeString(RequestLinkTarget, + &HistoryEntry->Target, + TRUE)) + { + Matched = TRUE; + } + + if (Matched) + { + RemoveEntryList(&HistoryEntry->Entry); + break; + } + Entry = Entry->Flink; + HistoryEntry = NULL; + } + + /* Leave existing symlink as is */ + if (! Matched) + Status = STATUS_OBJECT_NAME_NOT_FOUND; + else + Status = STATUS_SUCCESS; + _SEH2_LEAVE; + } + } + else + { + AddHistory = TRUE; + } + + Status = NtMakeTemporaryObject(LinkHandle); + if (! NT_SUCCESS(Status)) + { + DPRINT1("NtMakeTemporaryObject(%wZ) failed (Status %lx)\n", + &DeviceName, Status); + _SEH2_LEAVE; + } + + Status = NtClose(LinkHandle); + LinkHandle = NULL; + if (! NT_SUCCESS(Status)) + { + DPRINT1("NtClose(%wZ) failed (Status %lx)\n", + &DeviceName, Status); + _SEH2_LEAVE; + } + } + + /* Don't create symlink if we don't have a target */ + if (! RequestLinkTarget || RequestLinkTarget->Length == 0) + _SEH2_LEAVE; + + if (AddHistory) + { + HistoryEntry = (PCSRSS_DOS_DEVICE_HISTORY_ENTRY) + RtlAllocateHeap(Win32CsrApiHeap, + HEAP_ZERO_MEMORY, + sizeof(CSRSS_DOS_DEVICE_HISTORY_ENTRY)); + if (! HistoryEntry) + { + DPRINT1("Failed to allocate memory\n"); + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + + HistoryEntry->Target.Buffer = + RtlAllocateHeap(Win32CsrApiHeap, + HEAP_ZERO_MEMORY, + LinkTarget.Length); + if (! HistoryEntry->Target.Buffer) + { + DPRINT1("Failed to allocate memory\n"); + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + HistoryEntry->Target.Length = + HistoryEntry->Target.MaximumLength = + LinkTarget.Length; + RtlCopyUnicodeString(&HistoryEntry->Target, + &LinkTarget); + + HistoryEntry->Device.Buffer = + RtlAllocateHeap(Win32CsrApiHeap, + HEAP_ZERO_MEMORY, + RequestDeviceName.Length); + if (! HistoryEntry->Device.Buffer) + { + DPRINT1("Failed to allocate memory\n"); + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + HistoryEntry->Device.Length = + HistoryEntry->Device.MaximumLength = + RequestDeviceName.Length; + RtlCopyUnicodeString(&HistoryEntry->Device, + &RequestDeviceName); + + /* Remember previous symlink target for this device */ + InsertHeadList(ListHead, + &HistoryEntry->Entry); + HistoryEntry = NULL; + } + + RtlAllocateAndInitializeSid(&WorldAuthority, + 1, + SECURITY_WORLD_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + &WorldSid); + + RtlAllocateAndInitializeSid(&SystemAuthority, + 1, + SECURITY_LOCAL_SYSTEM_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + &SystemSid); + + RtlAllocateAndInitializeSid(&SystemAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + SECURITY_NULL_RID, + &AdminSid); + + SidLength = RtlLengthSid(SystemSid) + + RtlLengthSid(AdminSid) + + RtlLengthSid(WorldSid); + Length = sizeof(ACL) + SidLength + 3 * sizeof(ACCESS_ALLOWED_ACE); + + SecurityDescriptor = RtlAllocateHeap(Win32CsrApiHeap, + 0, + SECURITY_DESCRIPTOR_MIN_LENGTH + Length); + if (! SecurityDescriptor) + { + DPRINT1("Failed to allocate memory\n"); + Status = STATUS_NO_MEMORY; + _SEH2_LEAVE; + } + + Dacl = (PACL)((ULONG_PTR)SecurityDescriptor + SECURITY_DESCRIPTOR_MIN_LENGTH); + Status = RtlCreateSecurityDescriptor(SecurityDescriptor, + SECURITY_DESCRIPTOR_REVISION); + if (! NT_SUCCESS(Status)) + { + DPRINT1("RtlCreateSecurityDescriptor() failed (Status %lx)\n", + Status); + _SEH2_LEAVE; + } + + Status = RtlCreateAcl(Dacl, + Length, + ACL_REVISION); + if (! NT_SUCCESS(Status)) + { + DPRINT1("RtlCreateAcl() failed (Status %lx)\n", + Status); + _SEH2_LEAVE; + } + + (void) RtlAddAccessAllowedAce(Dacl, + ACL_REVISION, + GENERIC_ALL, + SystemSid); + (void) RtlAddAccessAllowedAce(Dacl, + ACL_REVISION, + GENERIC_ALL, + AdminSid); + (void) RtlAddAccessAllowedAce(Dacl, + ACL_REVISION, + STANDARD_RIGHTS_READ, + WorldSid); + + Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, + TRUE, + Dacl, + FALSE); + if (! NT_SUCCESS(Status)) + { + DPRINT1("RtlSetDaclSecurityDescriptor() failed (Status %lx)\n", + Status); + _SEH2_LEAVE; + } + + InitializeObjectAttributes(&ObjectAttributes, + &DeviceName, + OBJ_CASE_INSENSITIVE, + NULL, + SecurityDescriptor); + Status = NtCreateSymbolicLinkObject(&LinkHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes, + RequestLinkTarget); + if (NT_SUCCESS(Status)) + { + Status = NtMakePermanentObject(LinkHandle); + if (! NT_SUCCESS(Status)) + { + DPRINT1("NtMakePermanentObject(%wZ) failed (Status %lx)\n", + &DeviceName, Status); + } + } + else + { + DPRINT1("NtCreateSymbolicLinkObject(%wZ) failed (Status %lx)\n", + &DeviceName, Status); + } + } + _SEH2_FINALLY + { + (void) RtlLeaveCriticalSection(&Win32CsrDefineDosDeviceCritSec); + if (DeviceName.Buffer) + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + DeviceName.Buffer); + if (LinkTarget.Buffer) + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + LinkTarget.Buffer); + if (SecurityDescriptor) + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + SecurityDescriptor); + if (LinkHandle) + (void) NtClose(LinkHandle); + if (SystemSid) + (void) RtlFreeSid(SystemSid); + if (AdminSid) + (void) RtlFreeSid(AdminSid); + if (WorldSid) + (void) RtlFreeSid(WorldSid); + RtlFreeUnicodeString(&RequestDeviceName); + if (HistoryEntry) + { + if (HistoryEntry->Target.Buffer) + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + HistoryEntry->Target.Buffer); + if (HistoryEntry->Device.Buffer) + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + HistoryEntry->Device.Buffer); + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + HistoryEntry); + } + } + _SEH2_END + + DPRINT("CsrDefineDosDevice Exit, Statux: 0x%x\n", Status); + return Status; +} + +void CsrCleanupDefineDosDevice(void) +{ + PLIST_ENTRY Entry, ListHead; + PCSRSS_DOS_DEVICE_HISTORY_ENTRY HistoryEntry; + + (void) RtlDeleteCriticalSection(&Win32CsrDefineDosDeviceCritSec); + + ListHead = &DosDeviceHistory; + Entry = ListHead->Flink; + while (Entry != ListHead) + { + HistoryEntry = (PCSRSS_DOS_DEVICE_HISTORY_ENTRY) + CONTAINING_RECORD(Entry, + CSRSS_DOS_DEVICE_HISTORY_ENTRY, + Entry); + Entry = Entry->Flink; + + if (HistoryEntry) + { + if (HistoryEntry->Target.Buffer) + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + HistoryEntry->Target.Buffer); + if (HistoryEntry->Device.Buffer) + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + HistoryEntry->Device.Buffer); + (void) RtlFreeHeap(Win32CsrApiHeap, + 0, + HistoryEntry); + } + } +} +/* EOF */ diff --git a/reactos/deprecated/win32csr/file.h b/reactos/deprecated/win32csr/file.h new file mode 100644 index 00000000000..fa33b62efd2 --- /dev/null +++ b/reactos/deprecated/win32csr/file.h @@ -0,0 +1,28 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsystem/win32/csrss/win32csr/file.h + * PURPOSE: File handling + * PROGRAMMERS: Pierre Schweitzer (pierre.schweitzer@reactos.org) + * NOTE: Belongs to basesrv.dll + */ + +#pragma once + +#include "api.h" + +typedef struct tagCSRSS_DOS_DEVICE_HISTORY_ENTRY +{ + UNICODE_STRING Device; + UNICODE_STRING Target; + LIST_ENTRY Entry; +} CSRSS_DOS_DEVICE_HISTORY_ENTRY, *PCSRSS_DOS_DEVICE_HISTORY_ENTRY; + +/* Api functions */ +CSR_API(CsrGetTempFile); +CSR_API(CsrDefineDosDevice); + +/* functions */ +void CsrCleanupDefineDosDevice(); + +/* EOF */ diff --git a/reactos/deprecated/win32csr/guiconsole.c b/reactos/deprecated/win32csr/guiconsole.c new file mode 100644 index 00000000000..1db2047ccbe --- /dev/null +++ b/reactos/deprecated/win32csr/guiconsole.c @@ -0,0 +1,2362 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/win32csr/guiconsole.c + * PURPOSE: Implementation of gui-mode consoles + */ + +/* INCLUDES ******************************************************************/ + +#define NDEBUG +#include "w32csr.h" +#include + +/* Not defined in any header file */ +extern VOID WINAPI PrivateCsrssManualGuiCheck(LONG Check); + +/* GLOBALS *******************************************************************/ + +typedef struct GUI_CONSOLE_DATA_TAG +{ + HFONT Font; + unsigned CharWidth; + unsigned CharHeight; + BOOL CursorBlinkOn; + BOOL ForceCursorOff; + CRITICAL_SECTION Lock; + HMODULE ConsoleLibrary; + HANDLE hGuiInitEvent; + WCHAR FontName[LF_FACESIZE]; + DWORD FontSize; + DWORD FontWeight; + DWORD FullScreen; + DWORD QuickEdit; + DWORD InsertMode; + DWORD WindowPosition; + DWORD UseRasterFonts; + COLORREF ScreenText; + COLORREF ScreenBackground; + COLORREF PopupBackground; + COLORREF PopupText; + COLORREF Colors[16]; + WCHAR szProcessName[MAX_PATH]; + BOOL WindowSizeLock; + POINT OldCursor; +} GUI_CONSOLE_DATA, *PGUI_CONSOLE_DATA; + +#ifndef WM_APP +#define WM_APP 0x8000 +#endif +#define PM_CREATE_CONSOLE (WM_APP + 1) +#define PM_DESTROY_CONSOLE (WM_APP + 2) + +#define CURSOR_BLINK_TIME 500 +#define DEFAULT_ATTRIB (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED) + +static BOOL ConsInitialized = FALSE; +static HWND NotifyWnd; + +typedef struct _GUICONSOLE_MENUITEM +{ + UINT uID; + const struct _GUICONSOLE_MENUITEM *SubMenu; + WORD wCmdID; +} GUICONSOLE_MENUITEM, *PGUICONSOLE_MENUITEM; + +static const GUICONSOLE_MENUITEM GuiConsoleEditMenuItems[] = +{ + { IDS_MARK, NULL, ID_SYSTEM_EDIT_MARK }, + { IDS_COPY, NULL, ID_SYSTEM_EDIT_COPY }, + { IDS_PASTE, NULL, ID_SYSTEM_EDIT_PASTE }, + { IDS_SELECTALL, NULL, ID_SYSTEM_EDIT_SELECTALL }, + { IDS_SCROLL, NULL, ID_SYSTEM_EDIT_SCROLL }, + { IDS_FIND, NULL, ID_SYSTEM_EDIT_FIND }, + + { 0, NULL, 0 } /* End of list */ +}; + +static const GUICONSOLE_MENUITEM GuiConsoleMainMenuItems[] = +{ + { (UINT)-1, NULL, 0 }, /* Separator */ + { IDS_EDIT, GuiConsoleEditMenuItems, 0 }, + { IDS_DEFAULTS, NULL, ID_SYSTEM_DEFAULTS }, + { IDS_PROPERTIES, NULL, ID_SYSTEM_PROPERTIES }, + + { 0, NULL, 0 } /* End of list */ +}; + +static const COLORREF s_Colors[] = +{ + RGB(0, 0, 0), + RGB(0, 0, 128), + RGB(0, 128, 0), + RGB(0, 128, 128), + RGB(128, 0, 0), + RGB(128, 0, 128), + RGB(128, 128, 0), + RGB(192, 192, 192), + RGB(128, 128, 128), + RGB(0, 0, 255), + RGB(0, 255, 0), + RGB(0, 255, 255), + RGB(255, 0, 0), + RGB(255, 0, 255), + RGB(255, 255, 0), + RGB(255, 255, 255) +}; + +#define GuiConsoleRGBFromAttribute(GuiData, Attribute) ((GuiData)->Colors[(Attribute) & 0xF]) + +/* FUNCTIONS *****************************************************************/ + +static VOID +GuiConsoleAppendMenuItems(HMENU hMenu, + const GUICONSOLE_MENUITEM *Items) +{ + UINT i = 0; + WCHAR szMenuString[255]; + HMENU hSubMenu; + HINSTANCE hInst = GetModuleHandleW(L"win32csr"); + + do + { + if (Items[i].uID != (UINT)-1) + { + if (LoadStringW(hInst, + Items[i].uID, + szMenuString, + sizeof(szMenuString) / sizeof(szMenuString[0])) > 0) + { + if (Items[i].SubMenu != NULL) + { + hSubMenu = CreatePopupMenu(); + if (hSubMenu != NULL) + { + GuiConsoleAppendMenuItems(hSubMenu, + Items[i].SubMenu); + + if (!AppendMenuW(hMenu, + MF_STRING | MF_POPUP, + (UINT_PTR)hSubMenu, + szMenuString)) + { + DestroyMenu(hSubMenu); + } + } + } + else + { + AppendMenuW(hMenu, + MF_STRING, + Items[i].wCmdID, + szMenuString); + } + } + } + else + { + AppendMenuW(hMenu, + MF_SEPARATOR, + 0, + NULL); + } + i++; + } while(!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0)); +} + +static VOID +GuiConsoleCreateSysMenu(PCSRSS_CONSOLE Console) +{ + HMENU hMenu; + hMenu = GetSystemMenu(Console->hWindow, + FALSE); + if (hMenu != NULL) + { + GuiConsoleAppendMenuItems(hMenu, + GuiConsoleMainMenuItems); + DrawMenuBar(Console->hWindow); + } +} + +static VOID +GuiConsoleGetDataPointers(HWND hWnd, PCSRSS_CONSOLE *Console, PGUI_CONSOLE_DATA *GuiData) +{ + *Console = (PCSRSS_CONSOLE) GetWindowLongPtrW(hWnd, GWL_USERDATA); + *GuiData = (NULL == *Console ? NULL : (*Console)->PrivateData); +} + +static BOOL +GuiConsoleOpenUserRegistryPathPerProcessId(DWORD ProcessId, PHANDLE hProcHandle, PHKEY hResult, REGSAM samDesired) +{ + HANDLE hProcessToken = NULL; + HANDLE hProcess; + + BYTE Buffer[256]; + DWORD Length = 0; + UNICODE_STRING SidName; + LONG res; + PTOKEN_USER TokUser; + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | READ_CONTROL, FALSE, ProcessId); + if (!hProcess) + { + DPRINT("Error: OpenProcess failed(0x%x)\n", GetLastError()); + return FALSE; + } + + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken)) + { + DPRINT("Error: OpenProcessToken failed(0x%x)\n", GetLastError()); + CloseHandle(hProcess); + return FALSE; + } + + if (!GetTokenInformation(hProcessToken, TokenUser, (PVOID)Buffer, sizeof(Buffer), &Length)) + { + DPRINT("Error: GetTokenInformation failed(0x%x)\n",GetLastError()); + CloseHandle(hProcessToken); + CloseHandle(hProcess); + return FALSE; + } + + TokUser = ((PTOKEN_USER)Buffer)->User.Sid; + if (!NT_SUCCESS(RtlConvertSidToUnicodeString(&SidName, TokUser, TRUE))) + { + DPRINT("Error: RtlConvertSidToUnicodeString failed(0x%x)\n", GetLastError()); + CloseHandle(hProcessToken); + CloseHandle(hProcess); + return FALSE; + } + + res = RegOpenKeyExW(HKEY_USERS, SidName.Buffer, 0, samDesired, hResult); + RtlFreeUnicodeString(&SidName); + + CloseHandle(hProcessToken); + if (res != ERROR_SUCCESS) + { + CloseHandle(hProcess); + return FALSE; + } + + if (hProcHandle) + *hProcHandle = hProcess; + else + CloseHandle(hProcess); + + return TRUE; +} + +static BOOL +GuiConsoleOpenUserSettings(PGUI_CONSOLE_DATA GuiData, DWORD ProcessId, PHKEY hSubKey, REGSAM samDesired, BOOL bCreate) +{ + WCHAR szProcessName[MAX_PATH]; + WCHAR szBuffer[MAX_PATH]; + UINT fLength, wLength; + DWORD dwBitmask, dwLength; + WCHAR CurDrive[] = { 'A',':', 0 }; + HANDLE hProcess; + HKEY hKey; + WCHAR * ptr; + + /* + * console properties are stored under + * HKCU\Console\* + * + * There are 3 ways to store console properties + * + * 1. use console title as subkey name + * i.e. cmd.exe + * + * 2. use application name as subkey name + * + * 3. use unexpanded path to console application. + * i.e. %SystemRoot%_system32_cmd.exe + */ + + DPRINT("GuiConsoleOpenUserSettings entered\n"); + + if (!GuiConsoleOpenUserRegistryPathPerProcessId(ProcessId, &hProcess, &hKey, samDesired)) + { + DPRINT("GuiConsoleOpenUserRegistryPathPerProcessId failed\n"); + return FALSE; + } + + /* FIXME we do not getting the process name so no menu will be loading, why ?*/ + fLength = GetProcessImageFileNameW(hProcess, szProcessName, sizeof(GuiData->szProcessName) / sizeof(WCHAR)); + CloseHandle(hProcess); + + //DPRINT1("szProcessName3 : %S\n",szProcessName); + + if (!fLength) + { + DPRINT("GetProcessImageFileNameW failed(0x%x)ProcessId %d\n", GetLastError(), ProcessId); + return FALSE; + } + /* + * try the process name as path + */ + + ptr = wcsrchr(szProcessName, L'\\'); + wcscpy(GuiData->szProcessName, ptr); + + swprintf(szBuffer, L"Console%s",ptr); + DPRINT("#1 Path : %S\n", szBuffer); + + if (bCreate) + { + if (RegCreateKeyW(hKey, szBuffer, hSubKey) == ERROR_SUCCESS) + { + RegCloseKey(hKey); + return TRUE; + } + RegCloseKey(hKey); + return FALSE; + } + + if (RegOpenKeyExW(hKey, szBuffer, 0, samDesired, hSubKey) == ERROR_SUCCESS) + { + RegCloseKey(hKey); + return TRUE; + } + + /* + * try the "Shortcut to processname" as path + * FIXME: detect wheter the process was started as a shortcut + */ + + swprintf(szBuffer, L"Console\\Shortcut to %S", ptr); + DPRINT("#2 Path : %S\n", szBuffer); + if (RegOpenKeyExW(hKey, szBuffer, 0, samDesired, hSubKey) == ERROR_SUCCESS) + { + swprintf(GuiData->szProcessName, L"Shortcut to %S", ptr); + RegCloseKey(hKey); + return TRUE; + } + + /* + * if the path contains \\Device\\HarddiskVolume1\... remove it + */ + + if (szProcessName[0] == L'\\') + { + dwBitmask = GetLogicalDrives(); + while(dwBitmask) + { + if (dwBitmask & 0x1) + { + dwLength = QueryDosDeviceW(CurDrive, szBuffer, MAX_PATH); + if (dwLength) + { + if (!memcmp(szBuffer, szProcessName, (dwLength-2)*sizeof(WCHAR))) + { + wcscpy(szProcessName, CurDrive); + RtlMoveMemory(&szProcessName[2], &szProcessName[dwLength-1], fLength - dwLength -1); + break; + } + } + } + dwBitmask = (dwBitmask >> 1); + CurDrive[0]++; + } + } + + /* + * last attempt: check whether the file is under %SystemRoot% + * and use path like Console\%SystemRoot%_dir_dir2_file.exe + */ + + wLength = GetWindowsDirectoryW(szBuffer, MAX_PATH); + if (wLength) + { + if (!wcsncmp(szProcessName, szBuffer, wLength)) + { + /* replace slashes by underscores */ + while((ptr = wcschr(szProcessName, L'\\'))) + ptr[0] = L'_'; + + swprintf(szBuffer, L"Console\\%%SystemRoot%%%S", &szProcessName[wLength]); + DPRINT("#3 Path : %S\n", szBuffer); + if (RegOpenKeyExW(hKey, szBuffer, 0, samDesired, hSubKey) == ERROR_SUCCESS) + { + swprintf(GuiData->szProcessName, L"%%SystemRoot%%%S", &szProcessName[wLength]); + RegCloseKey(hKey); + return TRUE; + } + } + } + RegCloseKey(hKey); + return FALSE; +} + +static VOID +GuiConsoleWriteUserSettings(PCSRSS_CONSOLE Console, PGUI_CONSOLE_DATA GuiData) +{ + HKEY hKey; + PCSR_PROCESS ProcessData; + + if (Console->ProcessList.Flink == &Console->ProcessList) + { + DPRINT("GuiConsoleWriteUserSettings: No Process!!!\n"); + return; + } + ProcessData = CONTAINING_RECORD(Console->ProcessList.Flink, CSR_PROCESS, ConsoleLink); + if (!GuiConsoleOpenUserSettings(GuiData, PtrToUlong(ProcessData->ClientId.UniqueProcess), &hKey, KEY_READ | KEY_WRITE, TRUE)) + { + return; + } + + if (Console->ActiveBuffer->CursorInfo.dwSize <= 1) + { + RegDeleteKeyW(hKey, L"CursorSize"); + } + else + { + RegSetValueExW(hKey, L"CursorSize", 0, REG_DWORD, (const BYTE *)&Console->ActiveBuffer->CursorInfo.dwSize, sizeof(DWORD)); + } + + if (Console->NumberOfHistoryBuffers == 5) + { + RegDeleteKeyW(hKey, L"NumberOfHistoryBuffers"); + } + else + { + DWORD Temp = Console->NumberOfHistoryBuffers; + RegSetValueExW(hKey, L"NumberOfHistoryBuffers", 0, REG_DWORD, (const BYTE *)&Temp, sizeof(DWORD)); + } + + if (Console->HistoryBufferSize == 50) + { + RegDeleteKeyW(hKey, L"HistoryBufferSize"); + } + else + { + DWORD Temp = Console->HistoryBufferSize; + RegSetValueExW(hKey, L"HistoryBufferSize", 0, REG_DWORD, (const BYTE *)&Temp, sizeof(DWORD)); + } + + if (GuiData->FullScreen == FALSE) + { + RegDeleteKeyW(hKey, L"FullScreen"); + } + else + { + RegSetValueExW(hKey, L"FullScreen", 0, REG_DWORD, (const BYTE *)&GuiData->FullScreen, sizeof(DWORD)); + } + + if ( GuiData->QuickEdit == FALSE) + { + RegDeleteKeyW(hKey, L"QuickEdit"); + } + else + { + RegSetValueExW(hKey, L"QuickEdit", 0, REG_DWORD, (const BYTE *)&GuiData->QuickEdit, sizeof(DWORD)); + } + + if (GuiData->InsertMode == TRUE) + { + RegDeleteKeyW(hKey, L"InsertMode"); + } + else + { + RegSetValueExW(hKey, L"InsertMode", 0, REG_DWORD, (const BYTE *)&GuiData->InsertMode, sizeof(DWORD)); + } + + if (Console->HistoryNoDup == FALSE) + { + RegDeleteKeyW(hKey, L"HistoryNoDup"); + } + else + { + DWORD Temp = Console->HistoryNoDup; + RegSetValueExW(hKey, L"HistoryNoDup", 0, REG_DWORD, (const BYTE *)&Temp, sizeof(DWORD)); + } + + if (GuiData->ScreenText == RGB(192, 192, 192)) + { + /* + * MS uses console attributes instead of real color + */ + RegDeleteKeyW(hKey, L"ScreenText"); + } + else + { + RegSetValueExW(hKey, L"ScreenText", 0, REG_DWORD, (const BYTE *)&GuiData->ScreenText, sizeof(COLORREF)); + } + + if (GuiData->ScreenBackground == RGB(0, 0, 0)) + { + RegDeleteKeyW(hKey, L"ScreenBackground"); + } + else + { + RegSetValueExW(hKey, L"ScreenBackground", 0, REG_DWORD, (const BYTE *)&GuiData->ScreenBackground, sizeof(COLORREF)); + } + + RegCloseKey(hKey); +} + +static void +GuiConsoleReadUserSettings(HKEY hKey, PCSRSS_CONSOLE Console, PGUI_CONSOLE_DATA GuiData, PCSRSS_SCREEN_BUFFER Buffer) +{ + DWORD dwNumSubKeys = 0; + DWORD dwIndex; + DWORD dwValueName; + DWORD dwValue; + DWORD dwType; + WCHAR szValueName[MAX_PATH]; + WCHAR szValue[LF_FACESIZE] = L"\0"; + DWORD Value; + + if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwNumSubKeys, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + { + DPRINT("GuiConsoleReadUserSettings: RegQueryInfoKey failed\n"); + return; + } + + DPRINT("GuiConsoleReadUserSettings entered dwNumSubKeys %d\n", dwNumSubKeys); + + for (dwIndex = 0; dwIndex < dwNumSubKeys; dwIndex++) + { + dwValue = sizeof(Value); + dwValueName = MAX_PATH; + + if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, &dwType, (BYTE*)&Value, &dwValue) != ERROR_SUCCESS) + { + if (dwType == REG_SZ) + { + /* + * retry in case of string value + */ + dwValue = sizeof(szValue); + dwValueName = LF_FACESIZE; + if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, NULL, (BYTE*)szValue, &dwValue) != ERROR_SUCCESS) + break; + } + else + break; + } + if (!wcscmp(szValueName, L"CursorSize")) + { + if (Value == 0x32) + { + Buffer->CursorInfo.dwSize = Value; + } + else if (Value == 0x64) + { + Buffer->CursorInfo.dwSize = Value; + } + } + else if (!wcscmp(szValueName, L"ScreenText")) + { + GuiData->ScreenText = Value; + } + else if (!wcscmp(szValueName, L"ScreenBackground")) + { + GuiData->ScreenBackground = Value; + } + else if (!wcscmp(szValueName, L"FaceName")) + { + wcscpy(GuiData->FontName, szValue); + } + else if (!wcscmp(szValueName, L"FontSize")) + { + GuiData->FontSize = Value; + } + else if (!wcscmp(szValueName, L"FontWeight")) + { + GuiData->FontWeight = Value; + } + else if (!wcscmp(szValueName, L"HistoryNoDup")) + { + Console->HistoryNoDup = Value; + } + else if (!wcscmp(szValueName, L"WindowSize")) + { + Console->Size.X = LOWORD(Value); + Console->Size.Y = HIWORD(Value); + } + else if (!wcscmp(szValueName, L"ScreenBufferSize")) + { + if(Buffer) + { + Buffer->MaxX = LOWORD(Value); + Buffer->MaxY = HIWORD(Value); + } + } + else if (!wcscmp(szValueName, L"FullScreen")) + { + GuiData->FullScreen = Value; + } + else if (!wcscmp(szValueName, L"QuickEdit")) + { + GuiData->QuickEdit = Value; + } + else if (!wcscmp(szValueName, L"InsertMode")) + { + GuiData->InsertMode = Value; + } + } +} +static VOID +GuiConsoleUseDefaults(PCSRSS_CONSOLE Console, PGUI_CONSOLE_DATA GuiData, PCSRSS_SCREEN_BUFFER Buffer) +{ + /* + * init guidata with default properties + */ + + wcscpy(GuiData->FontName, L"DejaVu Sans Mono"); + GuiData->FontSize = 0x0008000C; // font is 8x12 + GuiData->FontWeight = FW_NORMAL; + GuiData->FullScreen = FALSE; + GuiData->QuickEdit = FALSE; + GuiData->InsertMode = TRUE; + GuiData->ScreenText = RGB(192, 192, 192); + GuiData->ScreenBackground = RGB(0, 0, 0); + GuiData->PopupText = RGB(128, 0, 128); + GuiData->PopupBackground = RGB(255, 255, 255); + GuiData->WindowPosition = UINT_MAX; + GuiData->UseRasterFonts = TRUE; + memcpy(GuiData->Colors, s_Colors, sizeof(s_Colors)); + + Console->HistoryBufferSize = 50; + Console->NumberOfHistoryBuffers = 5; + Console->HistoryNoDup = FALSE; + Console->Size.X = 80; + Console->Size.Y = 25; + + if (Buffer) + { + Buffer->MaxX = 80; + Buffer->MaxY = 300; + Buffer->CursorInfo.bVisible = TRUE; + Buffer->CursorInfo.dwSize = CSR_DEFAULT_CURSOR_SIZE; + } +} + +VOID +FASTCALL +GuiConsoleInitScrollbar(PCSRSS_CONSOLE Console, HWND hwnd) +{ + SCROLLINFO sInfo; + PGUI_CONSOLE_DATA GuiData = Console->PrivateData; + + DWORD Width = Console->Size.X * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + DWORD Height = Console->Size.Y * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + /* set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + sInfo.nMin = 0; + if (Console->ActiveBuffer->MaxY > Console->Size.Y) + { + sInfo.nMax = Console->ActiveBuffer->MaxY - 1; + sInfo.nPage = Console->Size.Y; + sInfo.nPos = Console->ActiveBuffer->ShowY; + SetScrollInfo(hwnd, SB_VERT, &sInfo, TRUE); + Width += GetSystemMetrics(SM_CXVSCROLL); + ShowScrollBar(hwnd, SB_VERT, TRUE); + } + else + { + ShowScrollBar(hwnd, SB_VERT, FALSE); + } + + if (Console->ActiveBuffer->MaxX > Console->Size.X) + { + sInfo.nMax = Console->ActiveBuffer->MaxX - 1; + sInfo.nPage = Console->Size.X; + sInfo.nPos = Console->ActiveBuffer->ShowX; + SetScrollInfo(hwnd, SB_HORZ, &sInfo, TRUE); + Height += GetSystemMetrics(SM_CYHSCROLL); + ShowScrollBar(hwnd, SB_HORZ, TRUE); + + } + else + { + ShowScrollBar(hwnd, SB_HORZ, FALSE); + } + + SetWindowPos(hwnd, NULL, 0, 0, Width, Height, + SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); +} + +static BOOL +GuiConsoleHandleNcCreate(HWND hWnd, CREATESTRUCTW *Create) +{ + PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) Create->lpCreateParams; + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA)Console->PrivateData; + HDC Dc; + HFONT OldFont; + TEXTMETRICW Metrics; + SIZE CharSize; + PCSR_PROCESS ProcessData; + HKEY hKey; + + Console->hWindow = hWnd; + + if (NULL == GuiData) + { + DPRINT1("GuiConsoleNcCreate: HeapAlloc failed\n"); + return FALSE; + } + + GuiConsoleUseDefaults(Console, GuiData, Console->ActiveBuffer); + if (Console->ProcessList.Flink != &Console->ProcessList) + { + ProcessData = CONTAINING_RECORD(Console->ProcessList.Flink, CSR_PROCESS, ConsoleLink); + if (GuiConsoleOpenUserSettings(GuiData, PtrToUlong(ProcessData->ClientId.UniqueProcess), &hKey, KEY_READ, FALSE)) + { + GuiConsoleReadUserSettings(hKey, Console, GuiData, Console->ActiveBuffer); + RegCloseKey(hKey); + } + } + + InitializeCriticalSection(&GuiData->Lock); + + GuiData->Font = CreateFontW(LOWORD(GuiData->FontSize), + 0, //HIWORD(GuiData->FontSize), + 0, + TA_BASELINE, + GuiData->FontWeight, + FALSE, + FALSE, + FALSE, + OEM_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + NONANTIALIASED_QUALITY, FIXED_PITCH | FF_DONTCARE, + GuiData->FontName); + if (NULL == GuiData->Font) + { + DPRINT1("GuiConsoleNcCreate: CreateFont failed\n"); + DeleteCriticalSection(&GuiData->Lock); + HeapFree(Win32CsrApiHeap, 0, GuiData); + return FALSE; + } + Dc = GetDC(hWnd); + if (NULL == Dc) + { + DPRINT1("GuiConsoleNcCreate: GetDC failed\n"); + DeleteObject(GuiData->Font); + DeleteCriticalSection(&GuiData->Lock); + HeapFree(Win32CsrApiHeap, 0, GuiData); + return FALSE; + } + OldFont = SelectObject(Dc, GuiData->Font); + if (NULL == OldFont) + { + DPRINT1("GuiConsoleNcCreate: SelectObject failed\n"); + ReleaseDC(hWnd, Dc); + DeleteObject(GuiData->Font); + DeleteCriticalSection(&GuiData->Lock); + HeapFree(Win32CsrApiHeap, 0, GuiData); + return FALSE; + } + if (! GetTextMetricsW(Dc, &Metrics)) + { + DPRINT1("GuiConsoleNcCreate: GetTextMetrics failed\n"); + SelectObject(Dc, OldFont); + ReleaseDC(hWnd, Dc); + DeleteObject(GuiData->Font); + DeleteCriticalSection(&GuiData->Lock); + HeapFree(Win32CsrApiHeap, 0, GuiData); + return FALSE; + } + GuiData->CharWidth = Metrics.tmMaxCharWidth; + GuiData->CharHeight = Metrics.tmHeight + Metrics.tmExternalLeading; + + /* Measure real char width more precisely if possible. */ + if (GetTextExtentPoint32W(Dc, L"R", 1, &CharSize)) + GuiData->CharWidth = CharSize.cx; + + SelectObject(Dc, OldFont); + + ReleaseDC(hWnd, Dc); + GuiData->CursorBlinkOn = TRUE; + GuiData->ForceCursorOff = FALSE; + + DPRINT("Console %p GuiData %p\n", Console, GuiData); + Console->PrivateData = GuiData; + SetWindowLongPtrW(hWnd, GWL_USERDATA, (DWORD_PTR) Console); + + SetTimer(hWnd, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL); + GuiConsoleCreateSysMenu(Console); + + GuiData->WindowSizeLock = TRUE; + GuiConsoleInitScrollbar(Console, hWnd); + GuiData->WindowSizeLock = FALSE; + + SetEvent(GuiData->hGuiInitEvent); + + return (BOOL) DefWindowProcW(hWnd, WM_NCCREATE, 0, (LPARAM) Create); +} + +static VOID +SmallRectToRect(PCSRSS_CONSOLE Console, PRECT Rect, PSMALL_RECT SmallRect) +{ + PCSRSS_SCREEN_BUFFER Buffer = Console->ActiveBuffer; + PGUI_CONSOLE_DATA GuiData = Console->PrivateData; + Rect->left = (SmallRect->Left - Buffer->ShowX) * GuiData->CharWidth; + Rect->top = (SmallRect->Top - Buffer->ShowY) * GuiData->CharHeight; + Rect->right = (SmallRect->Right + 1 - Buffer->ShowX) * GuiData->CharWidth; + Rect->bottom = (SmallRect->Bottom + 1 - Buffer->ShowY) * GuiData->CharHeight; +} + +static VOID +GuiConsoleUpdateSelection(PCSRSS_CONSOLE Console, PCOORD coord) +{ + RECT oldRect, newRect; + HWND hWnd = Console->hWindow; + + SmallRectToRect(Console, &oldRect, &Console->Selection.srSelection); + + if(coord != NULL) + { + SMALL_RECT rc; + /* exchange left/top with right/bottom if required */ + rc.Left = min(Console->Selection.dwSelectionAnchor.X, coord->X); + rc.Top = min(Console->Selection.dwSelectionAnchor.Y, coord->Y); + rc.Right = max(Console->Selection.dwSelectionAnchor.X, coord->X); + rc.Bottom = max(Console->Selection.dwSelectionAnchor.Y, coord->Y); + + SmallRectToRect(Console, &newRect, &rc); + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + if (memcmp(&rc, &Console->Selection.srSelection, sizeof(SMALL_RECT)) != 0) + { + HRGN rgn1, rgn2; + + /* calculate the region that needs to be updated */ + if((rgn1 = CreateRectRgnIndirect(&oldRect))) + { + if((rgn2 = CreateRectRgnIndirect(&newRect))) + { + if(CombineRgn(rgn1, rgn2, rgn1, RGN_XOR) != ERROR) + { + InvalidateRgn(hWnd, rgn1, FALSE); + } + + DeleteObject(rgn2); + } + DeleteObject(rgn1); + } + } + } + else + { + InvalidateRect(hWnd, &newRect, FALSE); + } + Console->Selection.dwFlags |= CONSOLE_SELECTION_NOT_EMPTY; + Console->Selection.srSelection = rc; + ConioPause(Console, PAUSED_FROM_SELECTION); + } + else + { + /* clear the selection */ + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + InvalidateRect(hWnd, &oldRect, FALSE); + } + Console->Selection.dwFlags = CONSOLE_NO_SELECTION; + ConioUnpause(Console, PAUSED_FROM_SELECTION); + } +} + + +static VOID +GuiConsolePaint(PCSRSS_CONSOLE Console, + PGUI_CONSOLE_DATA GuiData, + HDC hDC, + PRECT rc) +{ + PCSRSS_SCREEN_BUFFER Buff; + ULONG TopLine, BottomLine, LeftChar, RightChar; + ULONG Line, Char, Start; + PBYTE From; + PWCHAR To; + BYTE LastAttribute, Attribute; + ULONG CursorX, CursorY, CursorHeight; + HBRUSH CursorBrush, OldBrush; + HFONT OldFont; + + Buff = Console->ActiveBuffer; + + EnterCriticalSection(&Buff->Header.Console->Lock); + + TopLine = rc->top / GuiData->CharHeight + Buff->ShowY; + BottomLine = (rc->bottom + (GuiData->CharHeight - 1)) / GuiData->CharHeight - 1 + Buff->ShowY; + LeftChar = rc->left / GuiData->CharWidth + Buff->ShowX; + RightChar = (rc->right + (GuiData->CharWidth - 1)) / GuiData->CharWidth - 1 + Buff->ShowX; + LastAttribute = ConioCoordToPointer(Buff, LeftChar, TopLine)[1]; + + SetTextColor(hDC, GuiConsoleRGBFromAttribute(GuiData, LastAttribute)); + SetBkColor(hDC, GuiConsoleRGBFromAttribute(GuiData, LastAttribute >> 4)); + + if (BottomLine >= Buff->MaxY) BottomLine = Buff->MaxY - 1; + if (RightChar >= Buff->MaxX) RightChar = Buff->MaxX - 1; + + OldFont = SelectObject(hDC, + GuiData->Font); + + for (Line = TopLine; Line <= BottomLine; Line++) + { + WCHAR LineBuffer[80]; + From = ConioCoordToPointer(Buff, LeftChar, Line); + Start = LeftChar; + To = LineBuffer; + + for (Char = LeftChar; Char <= RightChar; Char++) + { + if (*(From + 1) != LastAttribute || (Char - Start == sizeof(LineBuffer) / sizeof(WCHAR))) + { + TextOutW(hDC, + (Start - Buff->ShowX) * GuiData->CharWidth, + (Line - Buff->ShowY) * GuiData->CharHeight, + LineBuffer, + Char - Start); + Start = Char; + To = LineBuffer; + Attribute = *(From + 1); + if (Attribute != LastAttribute) + { + SetTextColor(hDC, GuiConsoleRGBFromAttribute(GuiData, Attribute)); + SetBkColor(hDC, GuiConsoleRGBFromAttribute(GuiData, Attribute >> 4)); + LastAttribute = Attribute; + } + } + + MultiByteToWideChar(Console->OutputCodePage, + 0, + (PCHAR)From, + 1, + To, + 1); + To++; + From += 2; + } + + TextOutW(hDC, + (Start - Buff->ShowX) * GuiData->CharWidth, + (Line - Buff->ShowY) * GuiData->CharHeight, + LineBuffer, + RightChar - Start + 1); + } + + if (Buff->CursorInfo.bVisible && GuiData->CursorBlinkOn && + !GuiData->ForceCursorOff) + { + CursorX = Buff->CurrentX; + CursorY = Buff->CurrentY; + if (LeftChar <= CursorX && CursorX <= RightChar && + TopLine <= CursorY && CursorY <= BottomLine) + { + CursorHeight = ConioEffectiveCursorSize(Console, GuiData->CharHeight); + From = ConioCoordToPointer(Buff, Buff->CurrentX, Buff->CurrentY) + 1; + + if (*From != DEFAULT_ATTRIB) + { + CursorBrush = CreateSolidBrush(GuiConsoleRGBFromAttribute(GuiData, *From)); + } + else + { + CursorBrush = CreateSolidBrush(GuiData->ScreenText); + } + + OldBrush = SelectObject(hDC, + CursorBrush); + PatBlt(hDC, + (CursorX - Buff->ShowX) * GuiData->CharWidth, + (CursorY - Buff->ShowY) * GuiData->CharHeight + (GuiData->CharHeight - CursorHeight), + GuiData->CharWidth, + CursorHeight, + PATCOPY); + SelectObject(hDC, + OldBrush); + DeleteObject(CursorBrush); + } + } + + LeaveCriticalSection(&Buff->Header.Console->Lock); + + SelectObject(hDC, + OldFont); +} + +static VOID +GuiConsoleHandlePaint(HWND hWnd, HDC hDCPaint) +{ + HDC hDC; + PAINTSTRUCT ps; + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + + hDC = BeginPaint(hWnd, &ps); + if (hDC != NULL && + ps.rcPaint.left < ps.rcPaint.right && + ps.rcPaint.top < ps.rcPaint.bottom) + { + GuiConsoleGetDataPointers(hWnd, + &Console, + &GuiData); + if (Console != NULL && GuiData != NULL && + Console->ActiveBuffer != NULL) + { + if (Console->ActiveBuffer->Buffer != NULL) + { + EnterCriticalSection(&GuiData->Lock); + + GuiConsolePaint(Console, + GuiData, + hDC, + &ps.rcPaint); + + if (Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY) + { + RECT rc; + SmallRectToRect(Console, &rc, &Console->Selection.srSelection); + + /* invert the selection */ + if (IntersectRect(&rc, + &ps.rcPaint, + &rc)) + { + PatBlt(hDC, + rc.left, + rc.top, + rc.right - rc.left, + rc.bottom - rc.top, + DSTINVERT); + } + } + + LeaveCriticalSection(&GuiData->Lock); + } + } + + } + EndPaint(hWnd, &ps); +} + +static VOID +GuiConsoleHandleKey(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + MSG Message; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + Message.hwnd = hWnd; + Message.message = msg; + Message.wParam = wParam; + Message.lParam = lParam; + + if(msg == WM_CHAR || msg == WM_SYSKEYDOWN) + { + /* clear the selection */ + GuiConsoleUpdateSelection(Console, NULL); + } + + ConioProcessKey(&Message, Console, FALSE); +} + +static VOID WINAPI +GuiDrawRegion(PCSRSS_CONSOLE Console, SMALL_RECT *Region) +{ + RECT RegionRect; + SmallRectToRect(Console, &RegionRect, Region); + InvalidateRect(Console->hWindow, &RegionRect, FALSE); +} + +static VOID +GuiInvalidateCell(PCSRSS_CONSOLE Console, UINT x, UINT y) +{ + SMALL_RECT CellRect = { x, y, x, y }; + GuiDrawRegion(Console, &CellRect); +} + +static VOID WINAPI +GuiWriteStream(PCSRSS_CONSOLE Console, SMALL_RECT *Region, LONG CursorStartX, LONG CursorStartY, + UINT ScrolledLines, CHAR *Buffer, UINT Length) +{ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA) Console->PrivateData; + PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer; + LONG CursorEndX, CursorEndY; + RECT ScrollRect; + + if (NULL == Console->hWindow || NULL == GuiData) + { + return; + } + + if (0 != ScrolledLines) + { + ScrollRect.left = 0; + ScrollRect.top = 0; + ScrollRect.right = Console->Size.X * GuiData->CharWidth; + ScrollRect.bottom = Region->Top * GuiData->CharHeight; + + ScrollWindowEx(Console->hWindow, + 0, + -(ScrolledLines * GuiData->CharHeight), + &ScrollRect, + NULL, + NULL, + NULL, + SW_INVALIDATE); + } + + GuiDrawRegion(Console, Region); + + if (CursorStartX < Region->Left || Region->Right < CursorStartX + || CursorStartY < Region->Top || Region->Bottom < CursorStartY) + { + GuiInvalidateCell(Console, CursorStartX, CursorStartY); + } + + CursorEndX = Buff->CurrentX; + CursorEndY = Buff->CurrentY; + if ((CursorEndX < Region->Left || Region->Right < CursorEndX + || CursorEndY < Region->Top || Region->Bottom < CursorEndY) + && (CursorEndX != CursorStartX || CursorEndY != CursorStartY)) + { + GuiInvalidateCell(Console, CursorEndX, CursorEndY); + } + + // Set up the update timer (very short interval) - this is a "hack" for getting the OS to + // repaint the window without having it just freeze up and stay on the screen permanently. + GuiData->CursorBlinkOn = TRUE; + SetTimer(Console->hWindow, CONGUI_UPDATE_TIMER, CONGUI_UPDATE_TIME, NULL); +} + +static BOOL WINAPI +GuiSetCursorInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff) +{ + if (Console->ActiveBuffer == Buff) + { + GuiInvalidateCell(Console, Buff->CurrentX, Buff->CurrentY); + } + + return TRUE; +} + +static BOOL WINAPI +GuiSetScreenInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff, UINT OldCursorX, UINT OldCursorY) +{ + if (Console->ActiveBuffer == Buff) + { + /* Redraw char at old position (removes cursor) */ + GuiInvalidateCell(Console, OldCursorX, OldCursorY); + /* Redraw char at new position (shows cursor) */ + GuiInvalidateCell(Console, Buff->CurrentX, Buff->CurrentY); + } + + return TRUE; +} + +static BOOL WINAPI +GuiUpdateScreenInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff) +{ + PGUI_CONSOLE_DATA GuiData = (PGUI_CONSOLE_DATA) Console->PrivateData; + + if (Console->ActiveBuffer == Buff) + { + GuiData->ScreenText = GuiConsoleRGBFromAttribute(GuiData, Buff->DefaultAttrib); + GuiData->ScreenBackground = GuiConsoleRGBFromAttribute(GuiData, Buff->DefaultAttrib >> 4); + } + + return TRUE; +} + +static VOID +GuiConsoleHandleTimer(HWND hWnd) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + PCSRSS_SCREEN_BUFFER Buff; + + SetTimer(hWnd, CONGUI_UPDATE_TIMER, CURSOR_BLINK_TIME, NULL); + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + + Buff = Console->ActiveBuffer; + GuiInvalidateCell(Console, Buff->CurrentX, Buff->CurrentY); + GuiData->CursorBlinkOn = ! GuiData->CursorBlinkOn; + + if((GuiData->OldCursor.x != Buff->CurrentX) || (GuiData->OldCursor.y != Buff->CurrentY)) + { + SCROLLINFO xScroll; + int OldScrollX = -1, OldScrollY = -1; + int NewScrollX = -1, NewScrollY = -1; + + xScroll.cbSize = sizeof(SCROLLINFO); + xScroll.fMask = SIF_POS; + // Capture the original position of the scroll bars and save them. + if(GetScrollInfo(hWnd, SB_HORZ, &xScroll))OldScrollX = xScroll.nPos; + if(GetScrollInfo(hWnd, SB_VERT, &xScroll))OldScrollY = xScroll.nPos; + + // If we successfully got the info for the horizontal scrollbar + if(OldScrollX >= 0) + { + if((Buff->CurrentX < Buff->ShowX)||(Buff->CurrentX >= (Buff->ShowX + Console->Size.X))) + { + // Handle the horizontal scroll bar + if(Buff->CurrentX >= Console->Size.X) NewScrollX = Buff->CurrentX - Console->Size.X + 1; + else NewScrollX = 0; + } + else + { + NewScrollX = OldScrollX; + } + } + // If we successfully got the info for the vertical scrollbar + if(OldScrollY >= 0) + { + if((Buff->CurrentY < Buff->ShowY) || (Buff->CurrentY >= (Buff->ShowY + Console->Size.Y))) + { + // Handle the vertical scroll bar + if(Buff->CurrentY >= Console->Size.Y) NewScrollY = Buff->CurrentY - Console->Size.Y + 1; + else NewScrollY = 0; + } + else + { + NewScrollY = OldScrollY; + } + } + + // Adjust scroll bars and refresh the window if the cursor has moved outside the visible area + // NOTE: OldScroll# and NewScroll# will both be -1 (initial value) if the info for the respective scrollbar + // was not obtained successfully in the previous steps. This means their difference is 0 (no scrolling) + // and their associated scrollbar is left alone. + if((OldScrollX != NewScrollX) || (OldScrollY != NewScrollY)) + { + Buff->ShowX = NewScrollX; + Buff->ShowY = NewScrollY; + ScrollWindowEx(hWnd, + (OldScrollX - NewScrollX) * GuiData->CharWidth, + (OldScrollY - NewScrollY) * GuiData->CharHeight, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + if(NewScrollX >= 0) + { + xScroll.nPos = NewScrollX; + SetScrollInfo(hWnd, SB_HORZ, &xScroll, TRUE); + } + if(NewScrollY >= 0) + { + xScroll.nPos = NewScrollY; + SetScrollInfo(hWnd, SB_VERT, &xScroll, TRUE); + } + UpdateWindow(hWnd); + GuiData->OldCursor.x = Buff->CurrentX; + GuiData->OldCursor.y = Buff->CurrentY; + } + } +} + +static VOID +GuiConsoleHandleClose(HWND hWnd) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + PLIST_ENTRY current_entry; + PCSR_PROCESS current; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + + EnterCriticalSection(&Console->Lock); + + current_entry = Console->ProcessList.Flink; + while (current_entry != &Console->ProcessList) + { + current = CONTAINING_RECORD(current_entry, CSR_PROCESS, ConsoleLink); + current_entry = current_entry->Flink; + + /* FIXME: Windows will wait up to 5 seconds for the thread to exit. + * We shouldn't wait here, though, since the console lock is entered. + * A copy of the thread list probably needs to be made. */ + ConioConsoleCtrlEvent(CTRL_CLOSE_EVENT, current); + } + + LeaveCriticalSection(&Console->Lock); +} + +static VOID +GuiConsoleHandleNcDestroy(HWND hWnd) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + KillTimer(hWnd, 1); + Console->PrivateData = NULL; + DeleteCriticalSection(&GuiData->Lock); + GetSystemMenu(hWnd, TRUE); + if (GuiData->ConsoleLibrary) + FreeLibrary(GuiData->ConsoleLibrary); + + HeapFree(Win32CsrApiHeap, 0, GuiData); +} + +static COORD +PointToCoord(PCSRSS_CONSOLE Console, LPARAM lParam) +{ + PCSRSS_SCREEN_BUFFER Buffer = Console->ActiveBuffer; + PGUI_CONSOLE_DATA GuiData = Console->PrivateData; + COORD Coord; + Coord.X = Buffer->ShowX + ((short)LOWORD(lParam) / (int)GuiData->CharWidth); + Coord.Y = Buffer->ShowY + ((short)HIWORD(lParam) / (int)GuiData->CharHeight); + + /* Clip coordinate to ensure it's inside buffer */ + if (Coord.X < 0) Coord.X = 0; + else if (Coord.X >= Buffer->MaxX) Coord.X = Buffer->MaxX - 1; + if (Coord.Y < 0) Coord.Y = 0; + else if (Coord.Y >= Buffer->MaxY) Coord.Y = Buffer->MaxY - 1; + return Coord; +} + +static VOID +GuiConsoleLeftMouseDown(HWND hWnd, LPARAM lParam) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + if (Console == NULL || GuiData == NULL) return; + + Console->Selection.dwSelectionAnchor = PointToCoord(Console, lParam); + + SetCapture(hWnd); + + Console->Selection.dwFlags |= CONSOLE_SELECTION_IN_PROGRESS | CONSOLE_MOUSE_SELECTION | CONSOLE_MOUSE_DOWN; + + GuiConsoleUpdateSelection(Console, &Console->Selection.dwSelectionAnchor); +} + +static VOID +GuiConsoleLeftMouseUp(HWND hWnd, LPARAM lParam) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + COORD c; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + if (Console == NULL || GuiData == NULL) return; + if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) return; + + c = PointToCoord(Console, lParam); + + Console->Selection.dwFlags &= ~CONSOLE_MOUSE_DOWN; + + GuiConsoleUpdateSelection(Console, &c); + + ReleaseCapture(); +} + +static VOID +GuiConsoleMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + COORD c; + + if (!(wParam & MK_LBUTTON)) return; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + if (Console == NULL || GuiData == NULL) return; + if (!(Console->Selection.dwFlags & CONSOLE_MOUSE_DOWN)) return; + + c = PointToCoord(Console, lParam); /* TODO: Scroll buffer to bring c into view */ + + GuiConsoleUpdateSelection(Console, &c); +} + +static VOID +GuiConsoleCopy(HWND hWnd, PCSRSS_CONSOLE Console) +{ + if (OpenClipboard(hWnd) == TRUE) + { + HANDLE hData; + PBYTE ptr; + LPSTR data, dstPos; + ULONG selWidth, selHeight; + ULONG xPos, yPos, size; + + selWidth = Console->Selection.srSelection.Right - Console->Selection.srSelection.Left + 1; + selHeight = Console->Selection.srSelection.Bottom - Console->Selection.srSelection.Top + 1; + DPRINT("Selection is (%d|%d) to (%d|%d)\n", + Console->Selection.srSelection.Left, + Console->Selection.srSelection.Top, + Console->Selection.srSelection.Right, + Console->Selection.srSelection.Bottom); + + /* Basic size for one line and termination */ + size = selWidth + 1; + if (selHeight > 0) + { + /* Multiple line selections have to get \r\n appended */ + size += ((selWidth + 2) * (selHeight - 1)); + } + + /* Allocate memory, it will be passed to the system and may not be freed here */ + hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, size); + if (hData == NULL) + { + CloseClipboard(); + return; + } + data = GlobalLock(hData); + if (data == NULL) + { + CloseClipboard(); + return; + } + + DPRINT("Copying %dx%d selection\n", selWidth, selHeight); + dstPos = data; + + for (yPos = 0; yPos < selHeight; yPos++) + { + ptr = ConioCoordToPointer(Console->ActiveBuffer, + Console->Selection.srSelection.Left, + yPos + Console->Selection.srSelection.Top); + /* Copy only the characters, leave attributes alone */ + for (xPos = 0; xPos < selWidth; xPos++) + { + dstPos[xPos] = ptr[xPos * 2]; + } + dstPos += selWidth; + if (yPos != (selHeight - 1)) + { + strcat(data, "\r\n"); + dstPos += 2; + } + } + + DPRINT("Setting data <%s> to clipboard\n", data); + GlobalUnlock(hData); + + EmptyClipboard(); + SetClipboardData(CF_TEXT, hData); + CloseClipboard(); + } +} + +static VOID +GuiConsolePaste(HWND hWnd, PCSRSS_CONSOLE Console) +{ + if (OpenClipboard(hWnd) == TRUE) + { + HANDLE hData; + LPSTR str; + size_t len; + + hData = GetClipboardData(CF_TEXT); + if (hData == NULL) + { + CloseClipboard(); + return; + } + + str = GlobalLock(hData); + if (str == NULL) + { + CloseClipboard(); + return; + } + DPRINT("Got data <%s> from clipboard\n", str); + len = strlen(str); + + ConioWriteConsole(Console, Console->ActiveBuffer, str, len, TRUE); + + GlobalUnlock(hData); + CloseClipboard(); + } +} + +static VOID +GuiConsoleRightMouseDown(HWND hWnd) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + if (Console == NULL || GuiData == NULL) return; + + if (!(Console->Selection.dwFlags & CONSOLE_SELECTION_NOT_EMPTY)) + { + GuiConsolePaste(hWnd, Console); + } + else + { + GuiConsoleCopy(hWnd, Console); + + /* Clear the selection */ + GuiConsoleUpdateSelection(Console, NULL); + } + +} + + +static VOID +GuiConsoleShowConsoleProperties(HWND hWnd, BOOL Defaults, PGUI_CONSOLE_DATA GuiData) +{ + PCSRSS_CONSOLE Console; + APPLET_PROC CPLFunc; + TCHAR szBuffer[MAX_PATH]; + ConsoleInfo SharedInfo; + + DPRINT("GuiConsoleShowConsoleProperties entered\n"); + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + + if (GuiData == NULL) + { + DPRINT("GuiConsoleGetDataPointers failed\n"); + return; + } + + if (GuiData->ConsoleLibrary == NULL) + { + GetWindowsDirectory(szBuffer,MAX_PATH); + _tcscat(szBuffer, _T("\\system32\\console.dll")); + GuiData->ConsoleLibrary = LoadLibrary(szBuffer); + + if (GuiData->ConsoleLibrary == NULL) + { + DPRINT1("failed to load console.dll"); + return; + } + } + + CPLFunc = (APPLET_PROC) GetProcAddress(GuiData->ConsoleLibrary, _T("CPlApplet")); + if (!CPLFunc) + { + DPRINT("Error: Console.dll misses CPlApplet export\n"); + return; + } + + /* setup struct */ + SharedInfo.InsertMode = GuiData->InsertMode; + SharedInfo.HistoryBufferSize = Console->HistoryBufferSize; + SharedInfo.NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers; + SharedInfo.ScreenText = GuiData->ScreenText; + SharedInfo.ScreenBackground = GuiData->ScreenBackground; + SharedInfo.PopupText = GuiData->PopupText; + SharedInfo.PopupBackground = GuiData->PopupBackground; + SharedInfo.WindowSize = (DWORD)MAKELONG(Console->Size.X, Console->Size.Y); + SharedInfo.WindowPosition = GuiData->WindowPosition; + SharedInfo.ScreenBuffer = (DWORD)MAKELONG(Console->ActiveBuffer->MaxX, Console->ActiveBuffer->MaxY); + SharedInfo.UseRasterFonts = GuiData->UseRasterFonts; + SharedInfo.FontSize = (DWORD)GuiData->FontSize; + SharedInfo.FontWeight = GuiData->FontWeight; + SharedInfo.CursorSize = Console->ActiveBuffer->CursorInfo.dwSize; + SharedInfo.HistoryNoDup = Console->HistoryNoDup; + SharedInfo.FullScreen = GuiData->FullScreen; + SharedInfo.QuickEdit = GuiData->QuickEdit; + memcpy(&SharedInfo.Colors[0], GuiData->Colors, sizeof(s_Colors)); + + if (!CPLFunc(hWnd, CPL_INIT, 0, 0)) + { + DPRINT("Error: failed to initialize console.dll\n"); + return; + } + + if (CPLFunc(hWnd, CPL_GETCOUNT, 0, 0) != 1) + { + DPRINT("Error: console.dll returned unexpected CPL count\n"); + return; + } + + CPLFunc(hWnd, CPL_DBLCLK, (LPARAM)&SharedInfo, Defaults); +} +static LRESULT +GuiConsoleHandleSysMenuCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + LRESULT Ret = TRUE; + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + COORD bottomRight = { 0, 0 }; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + + switch(wParam) + { + case ID_SYSTEM_EDIT_MARK: + DPRINT1("Marking not handled yet\n"); + break; + + case ID_SYSTEM_EDIT_COPY: + GuiConsoleCopy(hWnd, Console); + break; + + case ID_SYSTEM_EDIT_PASTE: + GuiConsolePaste(hWnd, Console); + break; + + case ID_SYSTEM_EDIT_SELECTALL: + bottomRight.X = Console->Size.X - 1; + bottomRight.Y = Console->Size.Y - 1; + GuiConsoleUpdateSelection(Console, &bottomRight); + break; + + case ID_SYSTEM_EDIT_SCROLL: + DPRINT1("Scrolling is not handled yet\n"); + break; + + case ID_SYSTEM_EDIT_FIND: + DPRINT1("Finding is not handled yet\n"); + break; + + case ID_SYSTEM_DEFAULTS: + GuiConsoleShowConsoleProperties(hWnd, TRUE, GuiData); + break; + + case ID_SYSTEM_PROPERTIES: + GuiConsoleShowConsoleProperties(hWnd, FALSE, GuiData); + break; + + default: + Ret = DefWindowProcW(hWnd, WM_SYSCOMMAND, wParam, lParam); + break; + } + return Ret; +} + +static VOID +GuiConsoleGetMinMaxInfo(HWND hWnd, PMINMAXINFO minMaxInfo) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + DWORD windx, windy; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + if((Console == NULL)|| (GuiData == NULL)) return; + + windx = CONGUI_MIN_WIDTH * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = CONGUI_MIN_HEIGHT * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + minMaxInfo->ptMinTrackSize.x = windx; + minMaxInfo->ptMinTrackSize.y = windy; + + windx = (Console->ActiveBuffer->MaxX) * GuiData->CharWidth + 2 * (GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXEDGE)); + windy = (Console->ActiveBuffer->MaxY) * GuiData->CharHeight + 2 * (GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYEDGE)) + GetSystemMetrics(SM_CYCAPTION); + + if(Console->Size.X < Console->ActiveBuffer->MaxX) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if(Console->Size.Y < Console->ActiveBuffer->MaxY) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + minMaxInfo->ptMaxTrackSize.x = windx; + minMaxInfo->ptMaxTrackSize.y = windy; +} +static VOID +GuiConsoleResize(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PCSRSS_CONSOLE Console; + PGUI_CONSOLE_DATA GuiData; + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + if((Console == NULL) || (GuiData == NULL)) return; + + if ((GuiData->WindowSizeLock == FALSE) && (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED || wParam == SIZE_MINIMIZED)) + { + PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer; + DWORD windx, windy, charx, chary; + + GuiData->WindowSizeLock = TRUE; + + windx = LOWORD(lParam); + windy = HIWORD(lParam); + + // Compensate for existing scroll bars (because lParam values do not accommodate scroll bar) + if(Console->Size.X < Buff->MaxX) windy += GetSystemMetrics(SM_CYHSCROLL); // window currently has a horizontal scrollbar + if(Console->Size.Y < Buff->MaxY) windx += GetSystemMetrics(SM_CXVSCROLL); // window currently has a vertical scrollbar + + charx = windx / GuiData->CharWidth; + chary = windy / GuiData->CharHeight; + + // Character alignment (round size up or down) + if((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx; + if((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary; + + // Compensate for added scroll bars in new window + if(charx < Buff->MaxX)windy -= GetSystemMetrics(SM_CYHSCROLL); // new window will have a horizontal scroll bar + if(chary < Buff->MaxY)windx -= GetSystemMetrics(SM_CXVSCROLL); // new window will have a vertical scroll bar + + charx = windx / GuiData->CharWidth; + chary = windy / GuiData->CharHeight; + + // Character alignment (round size up or down) + if((windx % GuiData->CharWidth) >= (GuiData->CharWidth / 2)) ++charx; + if((windy % GuiData->CharHeight) >= (GuiData->CharHeight / 2)) ++chary; + + // Resize window + if((charx != Console->Size.X) || (chary != Console->Size.Y)) + { + Console->Size.X = (charx <= Buff->MaxX) ? charx : Buff->MaxX; + Console->Size.Y = (chary <= Buff->MaxY) ? chary : Buff->MaxY; + } + + GuiConsoleInitScrollbar(Console, hWnd); + + // Adjust the start of the visible area if we are attempting to show nonexistent areas + if((Buff->MaxX - Buff->ShowX) < Console->Size.X) Buff->ShowX = Buff->MaxX - Console->Size.X; + if((Buff->MaxY - Buff->ShowY) < Console->Size.Y) Buff->ShowY = Buff->MaxY - Console->Size.Y; + InvalidateRect(hWnd, NULL, TRUE); + + GuiData->WindowSizeLock = FALSE; + } +} + +VOID +FASTCALL +GuiConsoleHandleScrollbarMenu(VOID) +{ + HMENU hMenu; + + hMenu = CreatePopupMenu(); + if (hMenu == NULL) + { + DPRINT("CreatePopupMenu failed\n"); + return; + } + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLHERE); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLTOP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLBOTTOM); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_UP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLPAGE_DOWN); + //InsertItem(hMenu, MFT_SEPARATOR, MIIM_FTYPE, 0, NULL, -1); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLUP); + //InsertItem(hMenu, MIIM_STRING, MIIM_ID | MIIM_FTYPE | MIIM_STRING, 0, NULL, IDS_SCROLLDOWN); + +} + +static NTSTATUS WINAPI +GuiResizeBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer, COORD Size) +{ + BYTE * Buffer; + DWORD Offset = 0; + BYTE * OldPtr; + USHORT CurrentY; + BYTE * OldBuffer; +#if HAVE_WMEMSET + USHORT value = MAKEWORD(' ', ScreenBuffer->DefaultAttrib); +#endif + DWORD diff; + DWORD i; + + /* Buffer size is not allowed to be smaller than window size */ + if (Size.X < Console->Size.X || Size.Y < Console->Size.Y) + return STATUS_INVALID_PARAMETER; + + if (Size.X == ScreenBuffer->MaxX && Size.Y == ScreenBuffer->MaxY) + return STATUS_SUCCESS; + + Buffer = HeapAlloc(Win32CsrApiHeap, 0, Size.X * Size.Y * 2); + if (!Buffer) + return STATUS_NO_MEMORY; + + DPRINT1("Resizing (%d,%d) to (%d,%d)\n", ScreenBuffer->MaxX, ScreenBuffer->MaxY, Size.X, Size.Y); + OldBuffer = ScreenBuffer->Buffer; + + for (CurrentY = 0; CurrentY < ScreenBuffer->MaxY && CurrentY < Size.Y; CurrentY++) + { + OldPtr = ConioCoordToPointer(ScreenBuffer, 0, CurrentY); + if (Size.X <= ScreenBuffer->MaxX) + { + /* reduce size */ + RtlCopyMemory(&Buffer[Offset], OldPtr, Size.X * 2); + Offset += (Size.X * 2); + } + else + { + /* enlarge size */ + RtlCopyMemory(&Buffer[Offset], OldPtr, ScreenBuffer->MaxX * 2); + Offset += (ScreenBuffer->MaxX * 2); + + diff = Size.X - ScreenBuffer->MaxX; + /* zero new part of it */ +#if HAVE_WMEMSET + wmemset((WCHAR*)&Buffer[Offset], value, diff); +#else + for (i = 0; i < diff; i++) + { + Buffer[Offset++] = ' '; + Buffer[Offset++] = ScreenBuffer->DefaultAttrib; + } +#endif + } + } + + if (Size.Y > ScreenBuffer->MaxY) + { + diff = Size.X * (Size.Y - ScreenBuffer->MaxY); +#if HAVE_WMEMSET + wmemset((WCHAR*)&Buffer[Offset], value, diff); +#else + for (i = 0; i < diff; i++) + { + Buffer[Offset++] = ' '; + Buffer[Offset++] = ScreenBuffer->DefaultAttrib; + } +#endif + } + + (void)InterlockedExchangePointer((PVOID volatile *)&ScreenBuffer->Buffer, Buffer); + HeapFree(Win32CsrApiHeap, 0, OldBuffer); + ScreenBuffer->MaxX = Size.X; + ScreenBuffer->MaxY = Size.Y; + ScreenBuffer->VirtualY = 0; + + /* Ensure cursor and window are within buffer */ + if (ScreenBuffer->CurrentX >= Size.X) + ScreenBuffer->CurrentX = Size.X - 1; + if (ScreenBuffer->CurrentY >= Size.Y) + ScreenBuffer->CurrentY = Size.Y - 1; + if (ScreenBuffer->ShowX > Size.X - Console->Size.X) + ScreenBuffer->ShowX = Size.X - Console->Size.X; + if (ScreenBuffer->ShowY > Size.Y - Console->Size.Y) + ScreenBuffer->ShowY = Size.Y - Console->Size.Y; + + /* TODO: Should update scrollbar, but can't use anything that + * calls SendMessage or it could cause deadlock */ + + return STATUS_SUCCESS; +} + +static VOID +GuiApplyUserSettings(PCSRSS_CONSOLE Console, PGUI_CONSOLE_DATA GuiData, PConsoleInfo pConInfo) +{ + DWORD windx, windy; + PCSRSS_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer; + COORD BufSize; + BOOL SizeChanged = FALSE; + + EnterCriticalSection(&Console->Lock); + + /* apply text / background color */ + GuiData->ScreenText = pConInfo->ScreenText; + GuiData->ScreenBackground = pConInfo->ScreenBackground; + + /* apply cursor size */ + ActiveBuffer->CursorInfo.dwSize = min(max(pConInfo->CursorSize, 1), 100); + + windx = LOWORD(pConInfo->WindowSize); + windy = HIWORD(pConInfo->WindowSize); + + if (windx != Console->Size.X || windy != Console->Size.Y) + { + /* resize window */ + Console->Size.X = windx; + Console->Size.Y = windy; + SizeChanged = TRUE; + } + + BufSize.X = LOWORD(pConInfo->ScreenBuffer); + BufSize.Y = HIWORD(pConInfo->ScreenBuffer); + if (BufSize.X != ActiveBuffer->MaxX || BufSize.Y != ActiveBuffer->MaxY) + { + if (NT_SUCCESS(GuiResizeBuffer(Console, ActiveBuffer, BufSize))) + SizeChanged = TRUE; + } + + if (SizeChanged) + { + GuiData->WindowSizeLock = TRUE; + GuiConsoleInitScrollbar(Console, pConInfo->hConsoleWindow); + GuiData->WindowSizeLock = FALSE; + } + + LeaveCriticalSection(&Console->Lock); + InvalidateRect(pConInfo->hConsoleWindow, NULL, TRUE); +} + +static +LRESULT +GuiConsoleHandleScroll(HWND hwnd, UINT uMsg, WPARAM wParam) +{ + PCSRSS_CONSOLE Console; + PCSRSS_SCREEN_BUFFER Buff; + PGUI_CONSOLE_DATA GuiData; + SCROLLINFO sInfo; + int fnBar; + int old_pos, Maximum; + PUSHORT pShowXY; + + GuiConsoleGetDataPointers(hwnd, &Console, &GuiData); + if (Console == NULL || GuiData == NULL) + return FALSE; + Buff = Console->ActiveBuffer; + + if (uMsg == WM_HSCROLL) + { + fnBar = SB_HORZ; + Maximum = Buff->MaxX - Console->Size.X; + pShowXY = &Buff->ShowX; + } + else + { + fnBar = SB_VERT; + Maximum = Buff->MaxY - Console->Size.Y; + pShowXY = &Buff->ShowY; + } + + /* set scrollbar sizes */ + sInfo.cbSize = sizeof(SCROLLINFO); + sInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS; + + if (!GetScrollInfo(hwnd, fnBar, &sInfo)) + { + return FALSE; + } + + old_pos = sInfo.nPos; + + switch(LOWORD(wParam)) + { + case SB_LINELEFT: + sInfo.nPos -= 1; + break; + + case SB_LINERIGHT: + sInfo.nPos += 1; + break; + + case SB_PAGELEFT: + sInfo.nPos -= sInfo.nPage; + break; + + case SB_PAGERIGHT: + sInfo.nPos += sInfo.nPage; + break; + + case SB_THUMBTRACK: + sInfo.nPos = sInfo.nTrackPos; + ConioPause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_THUMBPOSITION: + ConioUnpause(Console, PAUSED_FROM_SCROLLBAR); + break; + + case SB_TOP: + sInfo.nPos = sInfo.nMin; + break; + + case SB_BOTTOM: + sInfo.nPos = sInfo.nMax; + break; + + default: + break; + } + + sInfo.nPos = max(sInfo.nPos, 0); + sInfo.nPos = min(sInfo.nPos, Maximum); + + if (old_pos != sInfo.nPos) + { + USHORT OldX = Buff->ShowX; + USHORT OldY = Buff->ShowY; + *pShowXY = sInfo.nPos; + + ScrollWindowEx(hwnd, + (OldX - Buff->ShowX) * GuiData->CharWidth, + (OldY - Buff->ShowY) * GuiData->CharHeight, + NULL, + NULL, + NULL, + NULL, + SW_INVALIDATE); + + sInfo.fMask = SIF_POS; + SetScrollInfo(hwnd, fnBar, &sInfo, TRUE); + + UpdateWindow(hwnd); + } + return 0; +} + +static LRESULT CALLBACK +GuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT Result = 0; + PGUI_CONSOLE_DATA GuiData = NULL; + PCSRSS_CONSOLE Console = NULL; + + GuiConsoleGetDataPointers(hWnd, &Console, &GuiData); + + switch(msg) + { + case WM_NCCREATE: + Result = (LRESULT) GuiConsoleHandleNcCreate(hWnd, (CREATESTRUCTW *) lParam); + break; + case WM_PAINT: + GuiConsoleHandlePaint(hWnd, (HDC)wParam); + break; + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_CHAR: + GuiConsoleHandleKey(hWnd, msg, wParam, lParam); + break; + case WM_TIMER: + GuiConsoleHandleTimer(hWnd); + break; + case WM_CLOSE: + GuiConsoleHandleClose(hWnd); + break; + case WM_NCDESTROY: + GuiConsoleHandleNcDestroy(hWnd); + break; + case WM_LBUTTONDOWN: + GuiConsoleLeftMouseDown(hWnd, lParam); + break; + case WM_LBUTTONUP: + GuiConsoleLeftMouseUp(hWnd, lParam); + break; + case WM_RBUTTONDOWN: + GuiConsoleRightMouseDown(hWnd); + break; + case WM_MOUSEMOVE: + GuiConsoleMouseMove(hWnd, wParam, lParam); + break; + case WM_SYSCOMMAND: + Result = GuiConsoleHandleSysMenuCommand(hWnd, wParam, lParam); + break; + case WM_HSCROLL: + case WM_VSCROLL: + Result = GuiConsoleHandleScroll(hWnd, msg, wParam); + break; + case WM_GETMINMAXINFO: + GuiConsoleGetMinMaxInfo(hWnd, (PMINMAXINFO)lParam); + break; + case WM_SIZE: + GuiConsoleResize(hWnd, wParam, lParam); + break; + case PM_APPLY_CONSOLE_INFO: + GuiApplyUserSettings(Console, GuiData, (PConsoleInfo)wParam); + if (lParam) + { + GuiConsoleWriteUserSettings(Console, GuiData); + } + break; + default: + Result = DefWindowProcW(hWnd, msg, wParam, lParam); + break; + } + + return Result; +} + +static LRESULT CALLBACK +GuiConsoleNotifyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND NewWindow; + LONG WindowCount; + MSG Msg; + PWCHAR Buffer, Title; + PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) lParam; + + + + switch(msg) + { + case WM_CREATE: + SetWindowLongW(hWnd, GWL_USERDATA, 0); + return 0; + case PM_CREATE_CONSOLE: + Buffer = HeapAlloc(Win32CsrApiHeap, 0, + Console->Title.Length + sizeof(WCHAR)); + if (NULL != Buffer) + { + memcpy(Buffer, Console->Title.Buffer, Console->Title.Length); + Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0'; + Title = Buffer; + } + else + { + Title = L""; + } + NewWindow = CreateWindowExW(WS_EX_CLIENTEDGE, + L"ConsoleWindowClass", + Title, + WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + (HINSTANCE) GetModuleHandleW(NULL), + (PVOID) Console); + if (NULL != Buffer) + { + HeapFree(Win32CsrApiHeap, 0, Buffer); + } + if (NULL != NewWindow) + { + SetWindowLongW(hWnd, GWL_USERDATA, GetWindowLongW(hWnd, GWL_USERDATA) + 1); + ShowWindow(NewWindow, (int)wParam); + } + return (LRESULT) NewWindow; + case PM_DESTROY_CONSOLE: + /* Window creation is done using a PostMessage(), so it's possible that the + * window that we want to destroy doesn't exist yet. So first empty the message + * queue */ + while(PeekMessageW(&Msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + } + DestroyWindow(Console->hWindow); + Console->hWindow = NULL; + WindowCount = GetWindowLongW(hWnd, GWL_USERDATA); + WindowCount--; + SetWindowLongW(hWnd, GWL_USERDATA, WindowCount); + if (0 == WindowCount) + { + NotifyWnd = NULL; + DestroyWindow(hWnd); + PrivateCsrssManualGuiCheck(-1); + PostQuitMessage(0); + } + return 0; + default: + return DefWindowProcW(hWnd, msg, wParam, lParam); + } +} + +static DWORD WINAPI +GuiConsoleGuiThread(PVOID Data) +{ + MSG msg; + PHANDLE GraphicsStartupEvent = (PHANDLE) Data; + + NotifyWnd = CreateWindowW(L"Win32CsrCreateNotify", + L"", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + (HINSTANCE) GetModuleHandleW(NULL), + NULL); + if (NULL == NotifyWnd) + { + PrivateCsrssManualGuiCheck(-1); + SetEvent(*GraphicsStartupEvent); + return 1; + } + + SetEvent(*GraphicsStartupEvent); + + while(GetMessageW(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + return 1; +} + +static BOOL +GuiInit(VOID) +{ + WNDCLASSEXW wc; + + if (NULL == NotifyWnd) + { + PrivateCsrssManualGuiCheck(+1); + } + + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = L"Win32CsrCreateNotify"; + wc.lpfnWndProc = GuiConsoleNotifyWndProc; + wc.style = 0; + wc.hInstance = (HINSTANCE) GetModuleHandleW(NULL); + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hIconSm = NULL; + if (RegisterClassExW(&wc) == 0) + { + DPRINT1("Failed to register notify wndproc\n"); + return FALSE; + } + + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = L"ConsoleWindowClass"; + wc.lpfnWndProc = GuiConsoleWndProc; + wc.style = 0; + wc.hInstance = (HINSTANCE) GetModuleHandleW(NULL); + wc.hIcon = LoadIconW(GetModuleHandleW(L"win32csr"), MAKEINTRESOURCEW(1)); + wc.hCursor = LoadCursorW(NULL, (LPCWSTR) IDC_ARROW); + wc.hbrBackground = CreateSolidBrush(RGB(0,0,0)); + wc.lpszMenuName = NULL; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hIconSm = LoadImageW(GetModuleHandleW(L"win32csr"), MAKEINTRESOURCEW(1), IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), + LR_SHARED); + if (RegisterClassExW(&wc) == 0) + { + DPRINT1("Failed to register console wndproc\n"); + return FALSE; + } + + return TRUE; +} + +static VOID WINAPI +GuiInitScreenBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buffer) +{ + Buffer->DefaultAttrib = DEFAULT_ATTRIB; +} + +static BOOL WINAPI +GuiChangeTitle(PCSRSS_CONSOLE Console) +{ + PWCHAR Buffer, Title; + + Buffer = HeapAlloc(Win32CsrApiHeap, 0, + Console->Title.Length + sizeof(WCHAR)); + if (NULL != Buffer) + { + memcpy(Buffer, Console->Title.Buffer, Console->Title.Length); + Buffer[Console->Title.Length / sizeof(WCHAR)] = L'\0'; + Title = Buffer; + } + else + { + Title = L""; + } + + SendMessageW(Console->hWindow, WM_SETTEXT, 0, (LPARAM) Title); + + if (NULL != Buffer) + { + HeapFree(Win32CsrApiHeap, 0, Buffer); + } + + return TRUE; +} + +static BOOL WINAPI +GuiChangeIcon(PCSRSS_CONSOLE Console, HICON hWindowIcon) +{ + SendMessageW(Console->hWindow, WM_SETICON, ICON_BIG, (LPARAM)hWindowIcon); + SendMessageW(Console->hWindow, WM_SETICON, ICON_SMALL, (LPARAM)hWindowIcon); + + return TRUE; +} + +static VOID WINAPI +GuiCleanupConsole(PCSRSS_CONSOLE Console) +{ + SendMessageW(NotifyWnd, PM_DESTROY_CONSOLE, 0, (LPARAM) Console); +} + +static CSRSS_CONSOLE_VTBL GuiVtbl = +{ + GuiInitScreenBuffer, + GuiWriteStream, + GuiDrawRegion, + GuiSetCursorInfo, + GuiSetScreenInfo, + GuiUpdateScreenInfo, + GuiChangeTitle, + GuiCleanupConsole, + GuiChangeIcon, + GuiResizeBuffer, +}; + +NTSTATUS FASTCALL +GuiInitConsole(PCSRSS_CONSOLE Console, int ShowCmd) +{ + HANDLE GraphicsStartupEvent; + HANDLE ThreadHandle; + PGUI_CONSOLE_DATA GuiData; + + if (! ConsInitialized) + { + ConsInitialized = TRUE; + if (! GuiInit()) + { + ConsInitialized = FALSE; + return STATUS_UNSUCCESSFUL; + } + } + + Console->Vtbl = &GuiVtbl; + if (NULL == NotifyWnd) + { + GraphicsStartupEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (NULL == GraphicsStartupEvent) + { + return STATUS_UNSUCCESSFUL; + } + + ThreadHandle = CreateThread(NULL, + 0, + GuiConsoleGuiThread, + (PVOID) &GraphicsStartupEvent, + 0, + NULL); + if (NULL == ThreadHandle) + { + CloseHandle(GraphicsStartupEvent); + DPRINT1("Win32Csr: Failed to create graphics console thread. Expect problems\n"); + return STATUS_UNSUCCESSFUL; + } + SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST); + CloseHandle(ThreadHandle); + + WaitForSingleObject(GraphicsStartupEvent, INFINITE); + CloseHandle(GraphicsStartupEvent); + + if (NULL == NotifyWnd) + { + DPRINT1("Win32Csr: Failed to create notification window.\n"); + return STATUS_UNSUCCESSFUL; + } + } + GuiData = HeapAlloc(Win32CsrApiHeap, HEAP_ZERO_MEMORY, + sizeof(GUI_CONSOLE_DATA)); + if (!GuiData) + { + DPRINT1("Win32Csr: Failed to create GUI_CONSOLE_DATA\n"); + return STATUS_UNSUCCESSFUL; + } + + Console->PrivateData = (PVOID) GuiData; + /* + * we need to wait untill the GUI has been fully initialized + * to retrieve custom settings i.e. WindowSize etc.. + * Ideally we could use SendNotifyMessage for this but its not + * yet implemented. + * + */ + GuiData->hGuiInitEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + /* create console */ + PostMessageW(NotifyWnd, PM_CREATE_CONSOLE, ShowCmd, (LPARAM) Console); + + /* wait untill initialization has finished */ + WaitForSingleObject(GuiData->hGuiInitEvent, INFINITE); + DPRINT("received event Console %p GuiData %p X %d Y %d\n", Console, Console->PrivateData, Console->Size.X, Console->Size.Y); + CloseHandle(GuiData->hGuiInitEvent); + GuiData->hGuiInitEvent = NULL; + + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/guiconsole.h b/reactos/deprecated/win32csr/guiconsole.h new file mode 100644 index 00000000000..d715883c089 --- /dev/null +++ b/reactos/deprecated/win32csr/guiconsole.h @@ -0,0 +1,18 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/win32csr/guiconsole.h + * PURPOSE: Interface to GUI consoles + */ + +#include "api.h" + +#define CONGUI_MIN_WIDTH 10 +#define CONGUI_MIN_HEIGHT 10 +#define CONGUI_UPDATE_TIME 0 +#define CONGUI_UPDATE_TIMER 1 + +NTSTATUS FASTCALL GuiInitConsole(PCSRSS_CONSOLE Console, BOOL Visible); +VOID FASTCALL GuiConsoleHandleScrollbarMenu(VOID); + +/*EOF*/ diff --git a/reactos/deprecated/win32csr/handle.c b/reactos/deprecated/win32csr/handle.c new file mode 100644 index 00000000000..33a654cfa63 --- /dev/null +++ b/reactos/deprecated/win32csr/handle.c @@ -0,0 +1,381 @@ +/* + * reactos/subsys/csrss/api/handle.c + * + * CSRSS handle functions + * + * ReactOS Operating System + */ + +/* INCLUDES ******************************************************************/ + +#include + +#define NDEBUG +#include + +/* FUNCTIONS *****************************************************************/ + +static +BOOL +CsrIsConsoleHandle(HANDLE Handle) +{ + return ((ULONG_PTR)Handle & 0x10000003) == 0x3; +} + +static INT +AdjustHandleCounts(PCSRSS_HANDLE Entry, INT Change) +{ + Object_t *Object = Entry->Object; + if (Entry->Access & GENERIC_READ) Object->AccessRead += Change; + if (Entry->Access & GENERIC_WRITE) Object->AccessWrite += Change; + if (!(Entry->ShareMode & FILE_SHARE_READ)) Object->ExclusiveRead += Change; + if (!(Entry->ShareMode & FILE_SHARE_WRITE)) Object->ExclusiveWrite += Change; + Object->HandleCount += Change; + return Object->HandleCount; +} + +static VOID +Win32CsrCreateHandleEntry( + PCSRSS_HANDLE Entry) +{ + Object_t *Object = Entry->Object; + EnterCriticalSection(&Object->Console->Lock); + AdjustHandleCounts(Entry, +1); + LeaveCriticalSection(&Object->Console->Lock); +} + +static VOID +Win32CsrCloseHandleEntry( + PCSRSS_HANDLE Entry) +{ + Object_t *Object = Entry->Object; + if (Object != NULL) + { + PCSRSS_CONSOLE Console = Object->Console; + EnterCriticalSection(&Console->Lock); + /* If the last handle to a screen buffer is closed, delete it */ + if (AdjustHandleCounts(Entry, -1) == 0 + && Object->Type == CONIO_SCREEN_BUFFER_MAGIC) + { + PCSRSS_SCREEN_BUFFER Buffer = (PCSRSS_SCREEN_BUFFER)Object; + /* ...unless it's the only buffer left. Windows allows deletion + * even of the last buffer, but having to deal with a lack of + * any active buffer might be error-prone. */ + if (Buffer->ListEntry.Flink != Buffer->ListEntry.Blink) + ConioDeleteScreenBuffer(Buffer); + } + LeaveCriticalSection(&Console->Lock); + Entry->Object = NULL; + } +} + +NTSTATUS +FASTCALL +Win32CsrReleaseObject( + PCSR_PROCESS ProcessData, + HANDLE Handle) +{ + ULONG_PTR h = (ULONG_PTR)Handle >> 2; + Object_t *Object; + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + if (h >= ProcessData->HandleTableSize + || (Object = ProcessData->HandleTable[h].Object) == NULL) + { + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } + Win32CsrCloseHandleEntry(&ProcessData->HandleTable[h]); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_SUCCESS; +} + +NTSTATUS +FASTCALL +Win32CsrLockObject(PCSR_PROCESS ProcessData, + HANDLE Handle, + Object_t **Object, + DWORD Access, + LONG Type) +{ + ULONG_PTR h = (ULONG_PTR)Handle >> 2; + + DPRINT("CsrGetObject, Object: %x, %x, %x\n", + Object, Handle, ProcessData ? ProcessData->HandleTableSize : 0); + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + if (!CsrIsConsoleHandle(Handle) || h >= ProcessData->HandleTableSize + || (*Object = ProcessData->HandleTable[h].Object) == NULL + || ~ProcessData->HandleTable[h].Access & Access + || (Type != 0 && (*Object)->Type != Type)) + { + DPRINT1("CsrGetObject returning invalid handle (%x)\n", Handle); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } + _InterlockedIncrement(&(*Object)->Console->ReferenceCount); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + EnterCriticalSection(&((*Object)->Console->Lock)); + return STATUS_SUCCESS; +} + +VOID +FASTCALL +Win32CsrUnlockObject(Object_t *Object) +{ + PCSRSS_CONSOLE Console = Object->Console; + LeaveCriticalSection(&Console->Lock); + /* dec ref count */ + if (_InterlockedDecrement(&Console->ReferenceCount) == 0) + ConioDeleteConsole(&Console->Header); +} + +VOID +WINAPI +Win32CsrReleaseConsole( + PCSR_PROCESS ProcessData) +{ + PCSRSS_CONSOLE Console; + ULONG i; + + /* Close all console handles and detach process from console */ + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + for (i = 0; i < ProcessData->HandleTableSize; i++) + Win32CsrCloseHandleEntry(&ProcessData->HandleTable[i]); + ProcessData->HandleTableSize = 0; + RtlFreeHeap(Win32CsrApiHeap, 0, ProcessData->HandleTable); + ProcessData->HandleTable = NULL; + + Console = ProcessData->Console; + if (Console != NULL) + { + ProcessData->Console = NULL; + EnterCriticalSection(&Console->Lock); + RemoveEntryList(&ProcessData->ConsoleLink); + LeaveCriticalSection(&Console->Lock); + if (_InterlockedDecrement(&Console->ReferenceCount) == 0) + ConioDeleteConsole(&Console->Header); + //CloseHandle(ProcessData->ConsoleEvent); + //ProcessData->ConsoleEvent = NULL; + } + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); +} + +NTSTATUS +FASTCALL +Win32CsrInsertObject( + PCSR_PROCESS ProcessData, + PHANDLE Handle, + Object_t *Object, + DWORD Access, + BOOL Inheritable, + DWORD ShareMode) +{ + ULONG i; + PCSRSS_HANDLE Block; + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + + for (i = 0; i < ProcessData->HandleTableSize; i++) + { + if (ProcessData->HandleTable[i].Object == NULL) + { + break; + } + } + if (i >= ProcessData->HandleTableSize) + { + Block = RtlAllocateHeap(Win32CsrApiHeap, + HEAP_ZERO_MEMORY, + (ProcessData->HandleTableSize + 64) * sizeof(CSRSS_HANDLE)); + if (Block == NULL) + { + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return(STATUS_UNSUCCESSFUL); + } + RtlCopyMemory(Block, + ProcessData->HandleTable, + ProcessData->HandleTableSize * sizeof(CSRSS_HANDLE)); + RtlFreeHeap(Win32CsrApiHeap, 0, ProcessData->HandleTable); + ProcessData->HandleTable = Block; + ProcessData->HandleTableSize += 64; + } + ProcessData->HandleTable[i].Object = Object; + ProcessData->HandleTable[i].Access = Access; + ProcessData->HandleTable[i].Inheritable = Inheritable; + ProcessData->HandleTable[i].ShareMode = ShareMode; + Win32CsrCreateHandleEntry(&ProcessData->HandleTable[i]); + *Handle = UlongToHandle((i << 2) | 0x3); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return(STATUS_SUCCESS); +} + +NTSTATUS +WINAPI +Win32CsrDuplicateHandleTable( + PCSR_PROCESS SourceProcessData, + PCSR_PROCESS TargetProcessData) +{ + ULONG i; + + /* Only inherit if the flag was set */ + if (!TargetProcessData->bInheritHandles) return STATUS_SUCCESS; + + if (TargetProcessData->HandleTableSize) + { + return STATUS_INVALID_PARAMETER; + } + + RtlEnterCriticalSection(&SourceProcessData->HandleTableLock); + + /* we are called from CreateProcessData, it isn't necessary to lock the target process data */ + + TargetProcessData->HandleTable = RtlAllocateHeap(Win32CsrApiHeap, + HEAP_ZERO_MEMORY, + SourceProcessData->HandleTableSize + * sizeof(CSRSS_HANDLE)); + if (TargetProcessData->HandleTable == NULL) + { + RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock); + return(STATUS_UNSUCCESSFUL); + } + TargetProcessData->HandleTableSize = SourceProcessData->HandleTableSize; + for (i = 0; i < SourceProcessData->HandleTableSize; i++) + { + if (SourceProcessData->HandleTable[i].Object != NULL && + SourceProcessData->HandleTable[i].Inheritable) + { + TargetProcessData->HandleTable[i] = SourceProcessData->HandleTable[i]; + Win32CsrCreateHandleEntry(&TargetProcessData->HandleTable[i]); + } + } + RtlLeaveCriticalSection(&SourceProcessData->HandleTableLock); + return(STATUS_SUCCESS); +} + +CSR_API(CsrGetHandle) +{ + NTSTATUS Status = STATUS_SUCCESS; + + Request->Data.GetInputHandleRequest.Handle = INVALID_HANDLE_VALUE; + + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + if (ProcessData->Console) + { + DWORD DesiredAccess = Request->Data.GetInputHandleRequest.Access; + DWORD ShareMode = Request->Data.GetInputHandleRequest.ShareMode; + + PCSRSS_CONSOLE Console = ProcessData->Console; + Object_t *Object; + + EnterCriticalSection(&Console->Lock); + if (Request->Type == GET_OUTPUT_HANDLE) + Object = &Console->ActiveBuffer->Header; + else + Object = &Console->Header; + + if (((DesiredAccess & GENERIC_READ) && Object->ExclusiveRead != 0) || + ((DesiredAccess & GENERIC_WRITE) && Object->ExclusiveWrite != 0) || + (!(ShareMode & FILE_SHARE_READ) && Object->AccessRead != 0) || + (!(ShareMode & FILE_SHARE_WRITE) && Object->AccessWrite != 0)) + { + DPRINT1("Sharing violation\n"); + Status = STATUS_SHARING_VIOLATION; + } + else + { + Status = Win32CsrInsertObject(ProcessData, + &Request->Data.GetInputHandleRequest.Handle, + Object, + DesiredAccess, + Request->Data.GetInputHandleRequest.Inheritable, + ShareMode); + } + LeaveCriticalSection(&Console->Lock); + } + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + return Status; +} + +CSR_API(CsrCloseHandle) +{ + return Win32CsrReleaseObject(ProcessData, Request->Data.CloseHandleRequest.Handle); +} + +CSR_API(CsrVerifyHandle) +{ + ULONG_PTR Index; + NTSTATUS Status = STATUS_SUCCESS; + + Index = (ULONG_PTR)Request->Data.VerifyHandleRequest.Handle >> 2; + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + if (Index >= ProcessData->HandleTableSize || + ProcessData->HandleTable[Index].Object == NULL) + { + DPRINT("CsrVerifyObject failed\n"); + Status = STATUS_INVALID_HANDLE; + } + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + + return Status; +} + +CSR_API(CsrDuplicateHandle) +{ + ULONG_PTR Index; + PCSRSS_HANDLE Entry; + DWORD DesiredAccess; + + Index = (ULONG_PTR)Request->Data.DuplicateHandleRequest.Handle >> 2; + RtlEnterCriticalSection(&ProcessData->HandleTableLock); + if (Index >= ProcessData->HandleTableSize + || (Entry = &ProcessData->HandleTable[Index])->Object == NULL) + { + DPRINT1("Couldn't dup invalid handle %p\n", Request->Data.DuplicateHandleRequest.Handle); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_HANDLE; + } + + if (Request->Data.DuplicateHandleRequest.Options & DUPLICATE_SAME_ACCESS) + { + DesiredAccess = Entry->Access; + } + else + { + DesiredAccess = Request->Data.DuplicateHandleRequest.Access; + /* Make sure the source handle has all the desired flags */ + if (~Entry->Access & DesiredAccess) + { + DPRINT1("Handle %p only has access %X; requested %X\n", + Request->Data.DuplicateHandleRequest.Handle, Entry->Access, DesiredAccess); + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return STATUS_INVALID_PARAMETER; + } + } + + Request->Status = Win32CsrInsertObject(ProcessData, + &Request->Data.DuplicateHandleRequest.Handle, + Entry->Object, + DesiredAccess, + Request->Data.DuplicateHandleRequest.Inheritable, + Entry->ShareMode); + if (NT_SUCCESS(Request->Status) + && Request->Data.DuplicateHandleRequest.Options & DUPLICATE_CLOSE_SOURCE) + { + Win32CsrCloseHandleEntry(Entry); + } + + RtlLeaveCriticalSection(&ProcessData->HandleTableLock); + return Request->Status; +} + +CSR_API(CsrGetInputWaitHandle) +{ + Request->Data.GetConsoleInputWaitHandle.InputWaitHandle = ProcessData->ConsoleEvent; + return STATUS_SUCCESS; +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/harderror.c b/reactos/deprecated/win32csr/harderror.c new file mode 100644 index 00000000000..128b16bc433 --- /dev/null +++ b/reactos/deprecated/win32csr/harderror.c @@ -0,0 +1,578 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/win32csr/dllmain.c + * PURPOSE: Initialization + * PROGRAMMERS: Dmitry Philippov (shedon@mail.ru) + * Timo Kreuzer (timo.kreuzer@reactos.org) + */ + +/* INCLUDES ******************************************************************/ + +#define NDEBUG +#include "w32csr.h" +#include +#include + +#define IDTRYAGAIN 10 +#define IDCONTINUE 11 + +/* FUNCTIONS *****************************************************************/ + +static +NTSTATUS +CsrpGetClientFileName( + OUT PUNICODE_STRING ClientFileNameU, + HANDLE hProcess) +{ + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY Module; + PPEB_LDR_DATA Ldr; + PROCESS_BASIC_INFORMATION ClientBasicInfo; + LDR_DATA_TABLE_ENTRY ModuleData; + PVOID ClientDllBase; + NTSTATUS Status; + PPEB Peb; + + /* Initialize string */ + ClientFileNameU->MaximumLength = 0; + ClientFileNameU->Length = 0; + ClientFileNameU->Buffer = NULL; + + /* Query process information */ + Status = NtQueryInformationProcess(hProcess, + ProcessBasicInformation, + &ClientBasicInfo, + sizeof(ClientBasicInfo), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + Peb = ClientBasicInfo.PebBaseAddress; + if (!Peb) return STATUS_UNSUCCESSFUL; + + Status = NtReadVirtualMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL); + if (!NT_SUCCESS(Status)) return Status; + + ModuleListHead = &Ldr->InLoadOrderModuleList; + Status = NtReadVirtualMemory(hProcess, + &ModuleListHead->Flink, + &Entry, + sizeof(Entry), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + if (Entry == ModuleListHead) return STATUS_UNSUCCESSFUL; + + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + + Status = NtReadVirtualMemory(hProcess, + Module, + &ModuleData, + sizeof(ModuleData), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + Status = NtReadVirtualMemory(hProcess, + &Peb->ImageBaseAddress, + &ClientDllBase, + sizeof(ClientDllBase), + NULL); + if (!NT_SUCCESS(Status)) return Status; + + if (ClientDllBase != ModuleData.DllBase) return STATUS_UNSUCCESSFUL; + + ClientFileNameU->MaximumLength = ModuleData.BaseDllName.MaximumLength; + ClientFileNameU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + ClientFileNameU->MaximumLength); + + Status = NtReadVirtualMemory(hProcess, + ModuleData.BaseDllName.Buffer, + ClientFileNameU->Buffer, + ClientFileNameU->MaximumLength, + NULL); + if (!NT_SUCCESS(Status)) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, ClientFileNameU->Buffer); + ClientFileNameU->Buffer = NULL; + ClientFileNameU->MaximumLength = 0; + return Status; + } + + ClientFileNameU->Length = wcslen(ClientFileNameU->Buffer)*sizeof(wchar_t); + DPRINT("ClientFileNameU=\'%wZ\'\n", &ClientFileNameU); + + return STATUS_SUCCESS; +} + +static +VOID +CsrpFreeStringParameters( + IN OUT PULONG_PTR Parameters, + IN PHARDERROR_MSG HardErrorMessage) +{ + ULONG nParam; + + /* Loop all parameters */ + for (nParam = 0; nParam < HardErrorMessage->NumberOfParameters; nParam++) + { + /* Check if the current parameter is a string */ + if (HardErrorMessage->UnicodeStringParameterMask & (1 << nParam) && Parameters[nParam]) + { + /* Free the string buffer */ + RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID)Parameters[nParam]); + } + } +} + +static +NTSTATUS +CsrpCaptureStringParameters( + OUT PULONG_PTR Parameters, + OUT PULONG SizeOfAllUnicodeStrings, + IN PHARDERROR_MSG HardErrorMessage, + HANDLE hProcess) +{ + ULONG nParam, Size = 0; + NTSTATUS Status = STATUS_SUCCESS; + UNICODE_STRING TempStringU, ParamStringU; + ANSI_STRING TempStringA; + + /* Read all strings from client space */ + for (nParam = 0; nParam < HardErrorMessage->NumberOfParameters; nParam++) + { + Parameters[nParam] = 0; + + /* Check if the current parameter is a unicode string */ + if (HardErrorMessage->UnicodeStringParameterMask & (1 << nParam)) + { + /* Read the UNICODE_STRING from the process memory */ + Status = NtReadVirtualMemory(hProcess, + (PVOID)HardErrorMessage->Parameters[nParam], + &ParamStringU, + sizeof(ParamStringU), + NULL); + + if (!NT_SUCCESS(Status)) + break; + + /* Allocate a buffer for the string */ + TempStringU.MaximumLength = ParamStringU.Length; + TempStringU.Length = ParamStringU.Length; + TempStringU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + TempStringU.MaximumLength); + + if (!TempStringU.Buffer) + { + DPRINT1("Cannot allocate memory %u\n", TempStringU.MaximumLength); + Status = STATUS_NO_MEMORY; + } + + /* Read the string buffer from the process memory */ + Status = NtReadVirtualMemory(hProcess, + ParamStringU.Buffer, + TempStringU.Buffer, + ParamStringU.Length, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtReadVirtualMemory failed with code: %lx\n", Status); + RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); + break; + } + + DPRINT("ParamString=\'%wZ\'\n", &TempStringU); + + /* Allocate a buffer for converted to ANSI string */ + TempStringA.MaximumLength = RtlUnicodeStringToAnsiSize(&TempStringU); + TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + TempStringA.MaximumLength); + + if (!TempStringA.Buffer) + { + DPRINT1("Cannot allocate memory %u\n", TempStringA.MaximumLength); + RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); + Status = STATUS_NO_MEMORY; + break; + } + + /* Convert string to ANSI and free temporary buffer */ + Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE); + RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); + if (!NT_SUCCESS(Status)) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer); + break; + } + + /* Note: RtlUnicodeStringToAnsiString returns NULL terminated string */ + Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer; + Size += TempStringU.Length; + } + else + { + /* It's not a unicode string */ + Parameters[nParam] = HardErrorMessage->Parameters[nParam]; + } + } + + if (!NT_SUCCESS(Status)) + { + CsrpFreeStringParameters(Parameters, HardErrorMessage); + return Status; + } + + *SizeOfAllUnicodeStrings = Size; + return Status; +} + +static +NTSTATUS +CsrpFormatMessages( + OUT PUNICODE_STRING TextStringU, + OUT PUNICODE_STRING CaptionStringU, + IN PULONG_PTR Parameters, + IN ULONG SizeOfStrings, + IN PHARDERROR_MSG Message, + IN HANDLE hProcess) +{ + NTSTATUS Status; + UNICODE_STRING FileNameU, TempStringU, FormatU; + ANSI_STRING FormatA; + PMESSAGE_RESOURCE_ENTRY MessageResource; + PWSTR FormatString; + ULONG Size, ExceptionCode; + + /* Get the file name of the client process */ + CsrpGetClientFileName(&FileNameU, hProcess); + + /* Check if we have a file name */ + if (!FileNameU.Buffer) + { + /* No, use system */ + RtlInitUnicodeString(&FileNameU, L"System"); + } + + /* Get text string of the error code */ + Status = RtlFindMessage(GetModuleHandleW(L"ntdll"), + (ULONG_PTR)RT_MESSAGETABLE, + LANG_NEUTRAL, + Message->Status, + &MessageResource); + + if (NT_SUCCESS(Status)) + { + if (MessageResource->Flags) + { + RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text); + FormatA.Buffer = NULL; + } + else + { + RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text); + RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE); + } + } + else + { + /* Fall back to hardcoded value */ + RtlInitUnicodeString(&FormatU, L"Unknown Hard Error"); + FormatA.Buffer = NULL; + } + + FormatString = FormatU.Buffer; + + /* Check whether a caption exists */ + if (FormatString[0] == L'{') + { + /* Set caption start */ + TempStringU.Buffer = ++FormatString; + + /* Get size of the caption */ + for (Size = 0; *FormatString != 0 && *FormatString != L'}'; Size++) + FormatString++; + + /* Skip '}', '\r', '\n' */ + FormatString += 3; + + TempStringU.Length = Size * sizeof(WCHAR); + TempStringU.MaximumLength = TempStringU.Length; + } + else + { + /* FIXME: Set string based on severity */ + RtlInitUnicodeString(&TempStringU, L"Application Error"); + } + + /* Calculate buffer length for the caption */ + CaptionStringU->MaximumLength = FileNameU.Length + TempStringU.Length + + 4 * sizeof(WCHAR); + + /* Allocate a buffer for the caption */ + CaptionStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + CaptionStringU->MaximumLength); + + /* Append the file name, seperator and the caption text */ + CaptionStringU->Length = 0; + RtlAppendUnicodeStringToString(CaptionStringU, &FileNameU); + RtlAppendUnicodeToString(CaptionStringU, L" - "); + RtlAppendUnicodeStringToString(CaptionStringU, &TempStringU); + + /* Zero terminate the buffer */ + CaptionStringU->Buffer[CaptionStringU->Length / sizeof(WCHAR)] = 0; + + /* Free the file name buffer */ + RtlFreeUnicodeString(&FileNameU); + + /* Check if this is an exception message */ + if (Message->Status == STATUS_UNHANDLED_EXCEPTION) + { + ExceptionCode = Parameters[0]; + + /* Handle special cases */ + if (ExceptionCode == STATUS_ACCESS_VIOLATION) + { + Parameters[0] = Parameters[1]; + Parameters[1] = Parameters[3]; + if (Parameters[2]) Parameters[2] = (ULONG_PTR)L"written"; + else Parameters[2] = (ULONG_PTR)L"read"; + MessageResource = NULL; + } + else if (ExceptionCode == STATUS_IN_PAGE_ERROR) + { + Parameters[0] = Parameters[1]; + Parameters[1] = Parameters[3]; + MessageResource = NULL; + } + else + { + /* Fall back to hardcoded value */ + Parameters[2] = Parameters[1]; + Parameters[1] = Parameters[0]; + Parameters[0] = (ULONG_PTR)L"unknown software exception"; + } + + if (!MessageResource) + { + /* Get text string of the exception code */ + Status = RtlFindMessage(GetModuleHandleW(L"ntdll"), + (ULONG_PTR)RT_MESSAGETABLE, + LANG_NEUTRAL, + ExceptionCode, + &MessageResource); + + if (NT_SUCCESS(Status)) + { + if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU); + + if (MessageResource->Flags) + { + RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text); + FormatA.Buffer = NULL; + } + else + { + RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text); + RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE); + } + FormatString = FormatU.Buffer; + } + else + { + /* Fall back to hardcoded value */ + Parameters[2] = Parameters[1]; + Parameters[1] = Parameters[0]; + Parameters[0] = (ULONG_PTR)L"unknown software exception"; + } + } + } + + /* Calculate length of text buffer */ + TextStringU->MaximumLength = FormatU.Length + SizeOfStrings + 42 * sizeof(WCHAR); + + /* Allocate a buffer for the text */ + TextStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + TextStringU->MaximumLength); + + /* Wrap in SEH to protect from invalid string parameters */ + _SEH2_TRY + { + /* Print the string into the buffer */ + StringCbPrintfW(TextStringU->Buffer, + TextStringU->MaximumLength, + FormatString, + Parameters[0], + Parameters[1], + Parameters[2], + Parameters[3]); + Status = STATUS_SUCCESS; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* Set error and free buffers */ + Status = _SEH2_GetExceptionCode(); + RtlFreeHeap(RtlGetProcessHeap(), 0, TextStringU->Buffer); + RtlFreeHeap(RtlGetProcessHeap(), 0, CaptionStringU->Buffer); + } + _SEH2_END + + if (NT_SUCCESS(Status)) + { + TextStringU->Length = wcslen(TextStringU->Buffer) * sizeof(WCHAR); + } + + if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU); + + return Status; +} + +static +ULONG +CsrpMessageBox( + PWSTR Text, + PWSTR Caption, + ULONG ValidResponseOptions, + ULONG Severity) +{ + ULONG Type, MessageBoxResponse; + + /* Set the message box type */ + switch (ValidResponseOptions) + { + case OptionAbortRetryIgnore: + Type = MB_ABORTRETRYIGNORE; + break; + case OptionOk: + Type = MB_OK; + break; + case OptionOkCancel: + Type = MB_OKCANCEL; + break; + case OptionRetryCancel: + Type = MB_RETRYCANCEL; + break; + case OptionYesNo: + Type = MB_YESNO; + break; + case OptionYesNoCancel: + Type = MB_YESNOCANCEL; + break; + case OptionShutdownSystem: + Type = MB_RETRYCANCEL; // FIXME??? + break; + /* Anything else is invalid */ + default: + return ResponseNotHandled; + } + + /* Set severity */ + if (Severity == STATUS_SEVERITY_INFORMATIONAL) Type |= MB_ICONINFORMATION; + else if (Severity == STATUS_SEVERITY_WARNING) Type |= MB_ICONWARNING; + else if (Severity == STATUS_SEVERITY_ERROR) Type |= MB_ICONERROR; + + Type |= MB_SYSTEMMODAL | MB_SETFOREGROUND; + + DPRINT("Text = '%S', Caption = '%S', Severity = %d, Type = 0x%lx\n", + Text, Caption, Severity, Type); + + /* Display a message box */ + MessageBoxResponse = MessageBoxW(0, Text, Caption, Type); + + /* Return response value */ + switch (MessageBoxResponse) + { + case IDOK: return ResponseOk; + case IDCANCEL: return ResponseCancel; + case IDYES: return ResponseYes; + case IDNO: return ResponseNo; + case IDABORT: return ResponseAbort; + case IDIGNORE: return ResponseIgnore; + case IDRETRY: return ResponseRetry; + case IDTRYAGAIN: return ResponseTryAgain; + case IDCONTINUE: return ResponseContinue; + } + + return ResponseNotHandled; +} + +VOID +WINAPI +Win32CsrHardError( + IN PCSR_THREAD ThreadData, + IN PHARDERROR_MSG Message) +{ +#if DBG + PCSR_PROCESS ProcessData = ThreadData->Process; +#endif + ULONG_PTR Parameters[MAXIMUM_HARDERROR_PARAMETERS]; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING TextU, CaptionU; + NTSTATUS Status; + HANDLE hProcess; + ULONG Size; + + /* Default to not handled */ + ASSERT(ProcessData != NULL); + Message->Response = ResponseNotHandled; + + /* Make sure we don't have too many parameters */ + if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS) + Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS; + + /* Initialize object attributes */ + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); + + /* Open client process */ + Status = NtOpenProcess(&hProcess, + PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, + &ObjectAttributes, + &Message->h.ClientId); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("NtOpenProcess failed with code: %lx\n", Status); + return; + } + + /* Capture all string parameters from the process memory */ + Status = CsrpCaptureStringParameters(Parameters, &Size, Message, hProcess); + if (!NT_SUCCESS(Status)) + { + NtClose(hProcess); + return; + } + + /* Format the caption and message box text */ + Status = CsrpFormatMessages(&TextU, + &CaptionU, + Parameters, + Size, + Message, + hProcess); + + /* Cleanup */ + CsrpFreeStringParameters(Parameters, Message); + NtClose(hProcess); + + if (!NT_SUCCESS(Status)) + { + return; + } + + /* Display the message box */ + Message->Response = CsrpMessageBox(TextU.Buffer, + CaptionU.Buffer, + Message->ValidResponseOptions, + (ULONG)Message->Status >> 30); + + RtlFreeUnicodeString(&TextU); + RtlFreeUnicodeString(&CaptionU); + + return; +} + diff --git a/reactos/deprecated/win32csr/lang/bg-BG.rc b/reactos/deprecated/win32csr/lang/bg-BG.rc new file mode 100644 index 00000000000..2166ae0d236 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/bg-BG.rc @@ -0,0 +1,64 @@ +/* + * PROJECT: ReactOS CSRSS subsystem + * FILE: subsystems/win32/csrss/win32csr/lang/bg-BG.rc + * PURPOSE: Bulgarian resource file + */ + +LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ïðåêðàòÿâàíå íà ïðèëîæåíèå - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Ïðåêðàòÿâàíå íà ïðèëîæåíèåòî... Ïî÷àêàéòå",IDC_STATIC,7,7,186,11 + CONTROL "Õîä",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Àêî ðåøèòå äà çàòâîðèòå ïðèëîæåíèåòî âåäíàãà, ùå èçãóáèòå âñè÷êè íåçàïèñàíè äàííè. Çà äà ïðåêðàòèòå ïðèëîæåíèåòî âåäíàãà, íàòèñíåòå „Ïðåêðàòÿâàíå âåäíàãà„.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Ïðåêðàòÿâàíå âåäíàãà",IDC_END_NOW,150,71,60,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ïðåêðàòÿâàíå íà ïðèëîæåíèå - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Îòêàç",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Ïðåêðàòÿâàíå âåäíàãà",IDC_END_NOW,150,71,60,17 + LTEXT "Ïðèëîæåíèåòî íå îòãîâàðÿ",IDC_STATIC,7,7,178,8 + LTEXT "Çà äà ñå âúðíåòå â ÐåàêòÎÑ è äà ïðîâåðèòå ñúñòîÿíèåòî íà ïðèëîæåíèåòî, íàòèñíåòå „Îòêàç“.", + IDC_STATIC,7,26,178,16 + LTEXT "Àêî ðåøèòå äà çàòâîðèòå ïðèëîæåíèåòî âåäíàãà, ùå èçãóáèòå âñè÷êè íåçàïèñàíè äàííè. Çà äà ïðåêðàòèòå ïðèëîæåíèåòî âåäíàãà, íàòèñíåòå „Ïðåêðàòÿâàíå âåäíàãà“.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Îáðàáîòêà" + IDS_MARK "Îòáåëÿçâàíå" + IDS_COPY "Çàïîìíÿíå\tEnter" + IDS_PASTE "Ïîñòàâÿíå" + IDS_SELECTALL "Èçáîð íà âñè÷êî" + IDS_SCROLL "Ïðåëèñòâàíå" + IDS_FIND "Òúðñåíå..." + IDS_DEFAULTS "Ïîäðàçáèðàíè" + IDS_PROPERTIES "Ñâîéñòâà" + IDS_SCROLLHERE "Ïðåëèñòâàíå òóê" + IDS_SCROLLTOP "Ïðåëèñòâàíå äî ãîðå" + IDS_SCROLLBOTTOM "Ïðåëèñòâàíå äî äîëó" + IDS_SCROLLPAGE_UP "Ãîðíà ñòðàíèöà" + IDS_SCROLLPAGE_DOWN "Äîëíà ñòðàíèöà" + IDS_SCROLLUP "Ïðåëèñòâàíå íàãîðå" + IDS_SCROLLDOWN "Ïðåëèñòâàíå íàäîëó" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/cs-CZ.rc b/reactos/deprecated/win32csr/lang/cs-CZ.rc new file mode 100644 index 00000000000..283cdfe48c3 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/cs-CZ.rc @@ -0,0 +1,63 @@ +/* FILE: subsystems/win32/csrss/win32csr/lang/cs-CZ.rc + * TRANSLATOR: Radek Liska aka Black_Fox (radekliska at gmail dot com) + * UPDATED: 2011-04-09 + */ + +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ukonèit program - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Probíhá ukonèení programu... Èekejte, prosím",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "IPokud zvolíte okamžité ukonèení programu, všechna neuložená data budou ztracena. Kliknutím na Ukonèit okamžitì ukonèíte program.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Ukonèit okamžitì",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ukonèit program - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Storno",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Ukonèit okamžitì",IDC_END_NOW,78,98,43,17 + LTEXT "Tento program neodpovídá",IDC_STATIC,7,7,178,8 + LTEXT "Kliknutím na Storno se lze vrátit do systému ReactOS a ovìøit stav programu", + IDC_STATIC,7,26,178,16 + LTEXT "Pokud zvolíte okamžité ukonèení programu, všechna neuložená data budou ztracena. Kliknutím na Ukonèit okamžitì ukonèíte program.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Upravit" + IDS_MARK "Oznaèit" + IDS_COPY "Kopírovat\tEnter" + IDS_PASTE "Vložit" + IDS_SELECTALL "Oznaèit vše" + IDS_SCROLL "Posunout" + IDS_FIND "Najít..." + IDS_DEFAULTS "Výchozí" + IDS_PROPERTIES "Vlastnosti" + IDS_SCROLLHERE "Posunout sem" + IDS_SCROLLTOP "Posunout na zaèátek" + IDS_SCROLLBOTTOM "Posunout na konec" + IDS_SCROLLPAGE_UP "O stránku výše" + IDS_SCROLLPAGE_DOWN "O stránku níže" + IDS_SCROLLUP "Posunout nahoru" + IDS_SCROLLDOWN "Posunout dolù" + + IDS_COMMAND_PROMPT "Pøíkazový øádek" +END diff --git a/reactos/deprecated/win32csr/lang/de-DE.rc b/reactos/deprecated/win32csr/lang/de-DE.rc new file mode 100644 index 00000000000..b5084f4f0cc --- /dev/null +++ b/reactos/deprecated/win32csr/lang/de-DE.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Programm beenden - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Programm wird beendet...",IDC_STATIC,7,7,186,11 + CONTROL "Fortschritt",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Wenn Sie das Programm sofort beenden, werden Sie eventuell ungespeicherte Daten verlieren. Um das Programm zu beenden, wählen Sie Jetzt beenden.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "J&etzt beenden",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Programm beenden - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Abbechen",IDCANCEL,142,98,43,17 + PUSHBUTTON "J&etzt beenden",IDC_END_NOW,78,98,43,17 + LTEXT "Das Programm reagiert nicht",IDC_STATIC,7,7,178,8 + LTEXT "Um zu ReactOS zurückzukehren und den Status der Anwendung zu überprüfen, wählen Sie Abbrechen.", + IDC_STATIC,7,26,178,16 + LTEXT "Wenn Sie das Programm sofort beenden, werden Sie eventuell ungespeicherte Daten verlieren. Um das Programm zu beenden, wählen Sie Jetzt beenden.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Bearbeiten" + IDS_MARK "Markieren" + IDS_COPY "Kopieren\tEnter" + IDS_PASTE "Einfügen" + IDS_SELECTALL "Alles auswählen" + IDS_SCROLL "Scrollen" + IDS_FIND "Suchen..." + IDS_DEFAULTS "Standardwerte" + IDS_PROPERTIES "Eigenschaften" + IDS_SCROLLHERE "Hier scrollen" + IDS_SCROLLTOP "Ganz nach oben scrollen" + IDS_SCROLLBOTTOM "Ganz nach unten scrollen" + IDS_SCROLLPAGE_UP "Seite nach oben" + IDS_SCROLLPAGE_DOWN "Seite nach unten" + IDS_SCROLLUP "Nach oben scrollen" + IDS_SCROLLDOWN "Nach unten scrollen" + + IDS_COMMAND_PROMPT "Eingabeaufforderung" +END diff --git a/reactos/deprecated/win32csr/lang/el-GR.rc b/reactos/deprecated/win32csr/lang/el-GR.rc new file mode 100644 index 00000000000..4990fb194c4 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/el-GR.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_GREEK, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Êëåßóéìï ðñïãñÜììáôïò - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Êëåßóéìï ðñïãñÜììáôïò... Ðáñáêáëþ ðåñéìÝíåôå",IDC_STATIC,7,7,186,11 + CONTROL "Ðñüïäïò",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Áí êëåßóåôå ôçí åöáñìïãÞ ôþñá , èá ÷Üóåôå üëá ôá ìç áðïèçêåõìÝíá äåäïìÝíá. Ãéá íá êëåßóåôå ôï ðñüãñáììá ôþñá, ðáôÞóôå 'Êëåßóéìï Ôþñá'.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Êëåßóéìï Ôþñá",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Êëåßóçìï ðñïãñÜììáôïò - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "¢êõñï",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Êëåßóéìï Ôþñá",IDC_END_NOW,78,98,43,17 + LTEXT "Ôï ðñüãñáììá äåí áðïêñßíåôáé",IDC_STATIC,7,7,178,8 + LTEXT "Ãéá íá åðéóôñÝøåôå óôï ReactOS êáé íá åëÝãîåôå ôçí êáôÜóôáóç ôïõ ðñïãñÜììáôïò, ðáôÞóôå '¢êõñï'", + IDC_STATIC,7,26,178,16 + LTEXT "Áí êëåßóåôå ôçí åöáñìïãÞ ôþñá , èá ÷Üóåôå üëá ôá ìç áðïèçêåõìÝíá äåäïìÝíá. Ãéá íá êëåßóåôå ôï ðñüãñáììá ôþñá, ðáôÞóôå 'Êëåßóéìï Ôþñá'.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Åðåîåñãáóßá" + IDS_MARK "ÌáñêÜñéóìá" + IDS_COPY "ÁíôéãñáöÞ\tEnter" + IDS_PASTE "Åðéêüëëçóç" + IDS_SELECTALL "ÅðéëïãÞ üëùí" + IDS_SCROLL "Êýëéóç" + IDS_FIND "Åýñåóç..." + IDS_DEFAULTS "ÐñïåðéëïãÞ" + IDS_PROPERTIES "Éäéüôçôåò" + IDS_SCROLLHERE "Êýëéóç åäþ" + IDS_SCROLLTOP "Áñ÷Þ" + IDS_SCROLLBOTTOM "ÔÝëïò" + IDS_SCROLLPAGE_UP "Ðñïçãïýìåíç óåëßäá" + IDS_SCROLLPAGE_DOWN "Åðüìåíç óåëßäá" + IDS_SCROLLUP "Êýëéóç ðÜíù" + IDS_SCROLLDOWN "Êýëéóç êÜôù" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/en-US.rc b/reactos/deprecated/win32csr/lang/en-US.rc new file mode 100644 index 00000000000..9c4e16f8513 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/en-US.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "End Program - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Ending Program... Please wait",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "If you choose to end the program immediately, you will lose any unsaved data. To end the program now, click End Now.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&End Now",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "End Program - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Cancel",IDCANCEL,142,98,43,17 + PUSHBUTTON "&End Now",IDC_END_NOW,78,98,43,17 + LTEXT "This program is not responding",IDC_STATIC,7,7,178,8 + LTEXT "To return to ReactOS and check the status of the program, click Cancel", + IDC_STATIC,7,26,178,16 + LTEXT "If you choose to end the program immediately, you will lose any unsaved data. To end the program now, click End Now", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Edit" + IDS_MARK "Mark" + IDS_COPY "Copy\tEnter" + IDS_PASTE "Paste" + IDS_SELECTALL "Select All" + IDS_SCROLL "Scroll" + IDS_FIND "Find..." + IDS_DEFAULTS "Defaults" + IDS_PROPERTIES "Properties" + IDS_SCROLLHERE "Scroll here" + IDS_SCROLLTOP "Scroll top" + IDS_SCROLLBOTTOM "Scroll bottom" + IDS_SCROLLPAGE_UP "Page up" + IDS_SCROLLPAGE_DOWN "Page down" + IDS_SCROLLUP "Scroll up" + IDS_SCROLLDOWN "Scroll down" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/es-ES.rc b/reactos/deprecated/win32csr/lang/es-ES.rc new file mode 100644 index 00000000000..d24849f9c38 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/es-ES.rc @@ -0,0 +1,63 @@ +/* + *Spanish Language resource file + * Traducido por: Javier Remacha 2008-26-01 + */ + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Finalizar programa - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Finalizando programa... Por favor, espere",IDC_STATIC,7,7,186,11 + CONTROL "Progreso",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Si elige finalizar el programa ahora, perderá todos los datos no guardados. Para finalizar el programa ahora, haga clic en Finalizar ahora.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Finalizar ahora",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Finalizar programa - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Cancelar",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Finalizar ahora",IDC_END_NOW,78,98,43,17 + LTEXT "Este programa no responde",IDC_STATIC,7,7,178,8 + LTEXT "Para volver a ReactOS y ver el estado del programa, haga clic en Cancelar", + IDC_STATIC,7,26,178,16 + LTEXT "Si elige finalizar el programa ahora, perderá todos los datos no guardados. Para finalizar el programa ahora, haga clic en Finalizar ahora.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Editar" + IDS_MARK "Marcar" + IDS_COPY "Copiar\tIntroducir" + IDS_PASTE "Pegar" + IDS_SELECTALL "Seleccionar Todo" + IDS_SCROLL "Desplazar" + IDS_FIND "Buscar..." + IDS_DEFAULTS "Por defecto" + IDS_PROPERTIES "Propiedades" + IDS_SCROLLHERE "Desplazar aquí" + IDS_SCROLLTOP "Desplazar hasta arriba" + IDS_SCROLLBOTTOM "Desplazar hasta abajo" + IDS_SCROLLPAGE_UP "Subir página" + IDS_SCROLLPAGE_DOWN "Bajar página" + IDS_SCROLLUP "Desplazar arriba" + IDS_SCROLLDOWN "Desplazar abajo" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/fr-FR.rc b/reactos/deprecated/win32csr/lang/fr-FR.rc new file mode 100644 index 00000000000..8dbe9f55a05 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/fr-FR.rc @@ -0,0 +1,60 @@ +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Fin du programme - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Termine le programme... Veuillez patienter",IDC_STATIC,7,7,186,11 + CONTROL "Avancement",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Si vous choisissez de terminer le programme immédiatement, vous perdrez toutes les données non sauvegardées. Pour terminer le programme maintenant, cliquez sur Terminer maintenant.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "T&erminer maintenant",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Fin du programme - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Annuler",IDCANCEL,142,98,43,17 + PUSHBUTTON "T&erminer maintenant",IDC_END_NOW,78,98,43,17 + LTEXT "Ce programme ne répond pas",IDC_STATIC,7,7,178,8 + LTEXT "Pour retourner à ReactOS et vérifier l'état du programme, cliquez sur Annuler", + IDC_STATIC,7,26,178,16 + LTEXT "Si vous choisissez de terminer le programme immédiatement, vous perdrez toutes les données non sauvegardées. Pour terminer le programme maintenant, cliquez sur Terminer maintenant", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +/* Fixme : Translation could be really improved, with context + La traduction pourrait réellement être améliorée grâce au contexte */ +STRINGTABLE +BEGIN + IDS_EDIT "Éditer" + IDS_MARK "Marquer" + IDS_COPY "Copier\tEntrée" + IDS_PASTE "Coller" + IDS_SELECTALL "Tout sélectionner" + IDS_SCROLL "Défiler" + IDS_FIND "Trouver..." + IDS_DEFAULTS "Défauts" + IDS_PROPERTIES "Propriétés" + IDS_SCROLLHERE "Défiler ici" + IDS_SCROLLTOP "Défiler tout en haut" + IDS_SCROLLBOTTOM "Défiler tout en bas" + IDS_SCROLLPAGE_UP "Page précédente" + IDS_SCROLLPAGE_DOWN "Page suivante" + IDS_SCROLLUP "Défiler en haut" + IDS_SCROLLDOWN "Défiler en bas" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/he-IL.rc b/reactos/deprecated/win32csr/lang/he-IL.rc new file mode 100644 index 00000000000..fc833755a45 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/he-IL.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "×¡×™×•× ×ª×›× ×™×ª - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "×ž×¡×™×™× ×ª×›× ×™×ª... × × ×œ×”×ž×ª×™×Ÿ",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "If you choose to end the program immediately, you will lose any unsaved data. To end the program now, click End Now.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "×¡×™×™× ×›×¢×ª",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "×¡×™×•× ×ª×›× ×™×ª - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "ביטול",IDCANCEL,142,98,43,17 + PUSHBUTTON "×¡×™×™× ×›×¢×ª",IDC_END_NOW,78,98,43,17 + LTEXT "התכנית ××™× ×” מגיבה",IDC_STATIC,7,7,178,8 + LTEXT "To return to ReactOS and check the status of the program, click Cancel", + IDC_STATIC,7,26,178,16 + LTEXT "If you choose to end the program immediately, you will lose any unsaved data. To end the program now, click End Now", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "ערוך" + IDS_MARK "סמן" + IDS_COPY "העתק\tEnter" + IDS_PASTE "הדבק" + IDS_SELECTALL "בחר הכל" + IDS_SCROLL "גלול" + IDS_FIND "מצ×..." + IDS_DEFAULTS "ברירת מחדל" + IDS_PROPERTIES "מ×פייני×" + IDS_SCROLLHERE "גלול לכ×ן" + IDS_SCROLLTOP "גלול למעלה" + IDS_SCROLLBOTTOM "גלול למטה" + IDS_SCROLLPAGE_UP "עמוד מעלה" + IDS_SCROLLPAGE_DOWN "עמוד מטה" + IDS_SCROLLUP "גלול מעלה" + IDS_SCROLLDOWN "גלול מטה" + + IDS_COMMAND_PROMPT "שורת הפקודה" +END diff --git a/reactos/deprecated/win32csr/lang/id-ID.rc b/reactos/deprecated/win32csr/lang/id-ID.rc new file mode 100644 index 00000000000..3df62722928 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/id-ID.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_INDONESIAN, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Akhir Program - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Mengakhiri Program... Harap menunggu",IDC_STATIC,7,7,186,11 + CONTROL "Progres",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Jika anda memilih untuk segera mengakhiri program, anda akan kehilangan data yang belum disimpan. Untuk mengakhiri program sekarang, klik Akhiri Sekarang.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Akhiri Sekarang",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mengakhiri Program - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Batal",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Akhiri Sekarang",IDC_END_NOW,78,98,43,17 + LTEXT "Program ini tidak merespon",IDC_STATIC,7,7,178,8 + LTEXT "Untuk kembali ke ReactOS dan memeriksa status program, klik Batal", + IDC_STATIC,7,26,178,16 + LTEXT "Jika anda memilih untuk segera mengakhiri program, anda akan kehilangan data yang belum disimpan. Untuk mengakhiri program sekarang, klik Akhiri Sekarang.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Edit" + IDS_MARK "Tandai" + IDS_COPY "Copy\tEnter" + IDS_PASTE "Paste" + IDS_SELECTALL "Pilih Semua" + IDS_SCROLL "Gulung" + IDS_FIND "Cari..." + IDS_DEFAULTS "Standar" + IDS_PROPERTIES "Properti" + IDS_SCROLLHERE "Gulung ke Sini" + IDS_SCROLLTOP "Gulung ke Atas" + IDS_SCROLLBOTTOM "Gulung ke Bawah" + IDS_SCROLLPAGE_UP "Halaman Naik" + IDS_SCROLLPAGE_DOWN "Halaman Turun" + IDS_SCROLLUP "Gulung Naik" + IDS_SCROLLDOWN "Gulung Turun" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/it-IT.rc b/reactos/deprecated/win32csr/lang/it-IT.rc new file mode 100644 index 00000000000..a6402c1b5e4 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/it-IT.rc @@ -0,0 +1,67 @@ +/* +* PROJECT: ReactOS Client/Server Runtime subsystem +* LICENSE: GPL - See COPYING in the top level directory +* FILE: subsystems/win32/csrss/win32csr/lang/it-IT.rc +* PURPOSE: Italian Translation of subsystems/win32/csrss/win32csr/lang/en-US.rc +* PROGRAMMERS: +* Copyright (C) 2007 Daniele Forsi (dforsi at gmail.com) Italian Translation +*/ + +LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Arresto del programma - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Arresto del programma... Attendere",IDC_STATIC,7,7,186,11 + CONTROL "Progresso",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Se si sceglie di terminare il programma immediatamente, si perderanno tutti i dati non salvati. Per terminare il programma ora, selezionare Termina ora.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "T&ermina ora",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Arresto del programma - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Annulla",IDCANCEL,142,98,43,17 + PUSHBUTTON "T&ermina ora",IDC_END_NOW,78,98,43,17 + LTEXT "Il programma non risponde",IDC_STATIC,7,7,178,8 + LTEXT "Per tornare a ReactOS e controllare lo stato del programma, selezionare Annulla", + IDC_STATIC,7,26,178,16 + LTEXT "Se si sceglie di terminare il programma immediatamente, si perderanno tutti i dati non salvati. Per terminare il programma ora, selezionare Termina ora.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Modifica" + IDS_MARK "Seleziona" + IDS_COPY "Copia\tInvio" + IDS_PASTE "Incolla" + IDS_SELECTALL "Seleziona tutto" + IDS_SCROLL "Scorri" + IDS_FIND "Trova..." + IDS_DEFAULTS "Impostazioni predefinite" + IDS_PROPERTIES "Proprietà" + IDS_SCROLLHERE "Scorri qui" + IDS_SCROLLTOP "Scorri in cima" + IDS_SCROLLBOTTOM "Scorri in fondo" + IDS_SCROLLPAGE_UP "Pagina sù" + IDS_SCROLLPAGE_DOWN "Pagina giù" + IDS_SCROLLUP "Scorri sù" + IDS_SCROLLDOWN "Scorri giù" + + IDS_COMMAND_PROMPT "Prompt dei commandi" +END diff --git a/reactos/deprecated/win32csr/lang/ja-JP.rc b/reactos/deprecated/win32csr/lang/ja-JP.rc new file mode 100644 index 00000000000..fd87c2d86d1 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/ja-JP.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ƒvƒƒOƒ‰ƒ€‚ÌI—¹ - " +FONT 9, "MS UI Gothic" +BEGIN + LTEXT "ƒvƒƒOƒ‰ƒ€‚ðI—¹‚µ‚Ä‚¢‚Ü‚·... ‚µ‚΂炭‚¨‘Ò‚¿‚­‚¾‚³‚¢",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "ƒvƒƒOƒ‰ƒ€‚ð‚·‚®‚ÉI—¹‚³‚¹‚邿‚¤‘I‘ð‚µ‚½ê‡A•Û‘¶‚³‚ê‚Ä‚¢‚È‚¢ƒf[ƒ^‚Í‚·‚ׂϸ‚í‚ê‚Ü‚·BƒvƒƒOƒ‰ƒ€‚ðI—¹‚·‚é‚É‚Í[‚·‚®‚ÉI—¹]‚ðƒNƒŠƒbƒN‚µ‚Ä‚­‚¾‚³‚¢B", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "‚·‚®‚ÉI—¹(&E)",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ƒvƒƒOƒ‰ƒ€‚ÌI—¹ - " +FONT 9, "MS UI Gothic" +BEGIN + DEFPUSHBUTTON "ƒLƒƒƒ“ƒZƒ‹",IDCANCEL,142,98,43,17 + PUSHBUTTON "‚·‚®‚ÉI—¹(&E)",IDC_END_NOW,78,98,43,17 + LTEXT "‚±‚̃vƒƒOƒ‰ƒ€‚͉ž“š‚µ‚Ä‚¢‚Ü‚¹‚ñ",IDC_STATIC,7,7,178,8 + LTEXT "ReactOS ‚É–ß‚Á‚ăvƒƒOƒ‰ƒ€‚Ìó‘Ô‚ðŠm”F‚·‚é‚É‚Í[ƒLƒƒƒ“ƒZƒ‹]‚ðƒNƒŠƒbƒN‚µ‚Ä‚­‚¾‚³‚¢", + IDC_STATIC,7,26,178,16 + LTEXT "ƒvƒƒOƒ‰ƒ€‚ð‚·‚®‚ÉI—¹‚³‚¹‚邿‚¤‘I‘ð‚µ‚½ê‡A•Û‘¶‚³‚ê‚Ä‚¢‚È‚¢ƒf[ƒ^‚Í‚·‚ׂϸ‚í‚ê‚Ü‚·BƒvƒƒOƒ‰ƒ€‚𒼂¿‚ÉI—¹‚·‚é‚É‚Í[‚·‚®‚ÉI—¹]‚ðƒNƒŠƒbƒN‚µ‚Ä‚­‚¾‚³‚¢", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 9, "MS UI Gothic", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "•ÒW" + IDS_MARK "”͈͎w’è" + IDS_COPY "ƒRƒs[\tEnter" + IDS_PASTE "“\\‚è•t‚¯" + IDS_SELECTALL "‚·‚ׂđI‘ð" + IDS_SCROLL "ƒXƒNƒ[ƒ‹" + IDS_FIND "ŒŸõ..." + IDS_DEFAULTS "‹K’è’l" + IDS_PROPERTIES "ƒvƒƒpƒeƒB" + IDS_SCROLLHERE "‚±‚±‚ɃXƒNƒ[ƒ‹" + IDS_SCROLLTOP "ˆê”Ôã‚ɃXƒNƒ[ƒ‹" + IDS_SCROLLBOTTOM "ˆê”Ô‰º‚ɃXƒNƒ[ƒ‹" + IDS_SCROLLPAGE_UP "Page up" + IDS_SCROLLPAGE_DOWN "Page down" + IDS_SCROLLUP "ã‚ɃXƒNƒ[ƒ‹" + IDS_SCROLLDOWN "‰º‚ɃXƒNƒ[ƒ‹" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/no-NO.rc b/reactos/deprecated/win32csr/lang/no-NO.rc new file mode 100644 index 00000000000..684b28ddb86 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/no-NO.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_NORWEGIAN, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Avslutt program - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Avslutt Program... Vennligst vent",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Hvis du velger å avslutte programmet øyeblikkelig, vil du miste alt data som ikke er lagret. For å avslutte programmet nå, Trykk på Avslutt nå.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Avslutt nå",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Avslutt program - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Avbryt",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Avslutt nå",IDC_END_NOW,78,98,43,17 + LTEXT "Programmet svarer ikke",IDC_STATIC,7,7,178,8 + LTEXT "For å returnere til ReactOS for å sjekke statusen på programmet, trykk på avbryt", + IDC_STATIC,7,26,178,16 + LTEXT "Hvis du velger å avslutte programmet øyeblikkelig, vil du miste alt data som ikke er lagret. For å avslutte programmet nå, Trykk på Avslutt nå", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Rediger" + IDS_MARK "Merk" + IDS_COPY "Kopier\tEnter" + IDS_PASTE "Lim inn" + IDS_SELECTALL "Velg alt" + IDS_SCROLL "Rull" + IDS_FIND "Finn..." + IDS_DEFAULTS "Standard" + IDS_PROPERTIES "Egenskaper" + IDS_SCROLLHERE "Rull her" + IDS_SCROLLTOP "Rull til toppen" + IDS_SCROLLBOTTOM "Rull knapp" + IDS_SCROLLPAGE_UP "Side opp" + IDS_SCROLLPAGE_DOWN "Side ned" + IDS_SCROLLUP "Rull opp" + IDS_SCROLLDOWN "Rull ned" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/pl-PL.rc b/reactos/deprecated/win32csr/lang/pl-PL.rc new file mode 100644 index 00000000000..ee4baf9419b --- /dev/null +++ b/reactos/deprecated/win32csr/lang/pl-PL.rc @@ -0,0 +1,66 @@ +/* + * translated by xrogers + * xxrogers@users.sourceforge.net + * https://sourceforge.net/projects/reactospl + * translation update by Olaf Siejka (Caemyr), Apr 2011 + * UTF-8 conversion by Caemyr (May, 2011) + */ + +LANGUAGE LANG_POLISH, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ZakoÅ„cz program - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Trwa zamykanie programu... ProszÄ™ czekać",IDC_STATIC,7,7,186,11 + CONTROL "PostÄ™p",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "JeÅ›li zdecydujesz siÄ™ zamknąć program natychmiastowo, utracisz wszelkie niezapisane dane. Aby zakoÅ„czyć program, wciÅ›nij ZakoÅ„cz teraz.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&ZakoÅ„cz teraz",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ZakoÅ„cz program - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Anuluj",IDCANCEL,142,98,43,17 + PUSHBUTTON "&ZakoÅ„cz teraz",IDC_END_NOW,78,98,43,17 + LTEXT "Wybrany program nie odpowiada",IDC_STATIC,7,7,178,8 + LTEXT "Aby powrócić do ReactOS i sprawdzić status programu, kliknij Anuluj", + IDC_STATIC,7,26,178,16 + LTEXT "JeÅ›li zdecydujesz siÄ™ zamknąć program natychmiastowo, utracisz wszelkie niezapisane dane. Aby zakoÅ„czyć program, wciÅ›nij ZakoÅ„cz teraz.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Edytuj" + IDS_MARK "Zaznacz" + IDS_COPY "Kopiuj\tWejdź" + IDS_PASTE "Wklej" + IDS_SELECTALL "Zaznacz wszystko" + IDS_SCROLL "PrzewiÅ„" + IDS_FIND "Znajdź..." + IDS_DEFAULTS "Ustawienia domyÅ›lne" + IDS_PROPERTIES "WÅ‚aÅ›ciwoÅ›ci" + IDS_SCROLLHERE "PrzewiÅ„ tutaj" + IDS_SCROLLTOP "PrzewiÅ„ na poczÄ…tek" + IDS_SCROLLBOTTOM "PrzewiÅ„ na koniec" + IDS_SCROLLPAGE_UP "Poprzednia strona" + IDS_SCROLLPAGE_DOWN "NastÄ™pna strona" + IDS_SCROLLUP "PrzewiÅ„ do góry" + IDS_SCROLLDOWN "PrzewiÅ„ na dół" + + IDS_COMMAND_PROMPT "Wiersz polecenia" +END diff --git a/reactos/deprecated/win32csr/lang/pt-BR.rc b/reactos/deprecated/win32csr/lang/pt-BR.rc new file mode 100644 index 00000000000..c2f772c3852 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/pt-BR.rc @@ -0,0 +1,60 @@ +/* Translation and UTF-8 Conversion by mkbu95 (May, 2012) */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Finalizar Programa - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Finalizando Programa... Por favor aguarde",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Se você finalizar o programa imediatamente, você irá perder qualquer alteração não salva. Para finalizar o programa agora, pressione Finalizar Agora.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Finalizar Agora",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Finalizar Programa - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Cancelar",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Finalizar Agora",IDC_END_NOW,78,98,43,17 + LTEXT "O programa não está respondendo",IDC_STATIC,7,7,178,8 + LTEXT "Parar retornar ao ReactOS e verificar o estado do programa, pressione Cancelar", + IDC_STATIC,7,26,178,16 + LTEXT "Se você finalizar o programa imediatamente, você irá perder qualquer alteração não salva. Para finalizar o programa agora, pressione Finalizar Agora", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Editar" + IDS_MARK "Marcar" + IDS_COPY "Copiar\tEnter" + IDS_PASTE "Colar" + IDS_SELECTALL "Selecionar Tudo" + IDS_SCROLL "Rolar" + IDS_FIND "Procurar..." + IDS_DEFAULTS "Padrões" + IDS_PROPERTIES "Propriedades" + IDS_SCROLLHERE "Rolar aqui" + IDS_SCROLLTOP "Rolar até o topo" + IDS_SCROLLBOTTOM "Rolar até o fim" + IDS_SCROLLPAGE_UP "Page up" + IDS_SCROLLPAGE_DOWN "Page down" + IDS_SCROLLUP "Scroll up" + IDS_SCROLLDOWN "Scroll down" + + IDS_COMMAND_PROMPT "Prompt de Comando" +END diff --git a/reactos/deprecated/win32csr/lang/ro-RO.rc b/reactos/deprecated/win32csr/lang/ro-RO.rc new file mode 100644 index 00000000000..e1d4213a85d --- /dev/null +++ b/reactos/deprecated/win32csr/lang/ro-RO.rc @@ -0,0 +1,65 @@ +/* + * FILE: subsystems/win32/csrss/win32csr/lang/ro-RO.rc + * ReactOS Project (http://www.reactos.org) + * TRANSLATOR: Fulea Ștefan (PM on ReactOS Forum at fulea.stefan) + * CHANGE LOG: 2011-10-16 initial translation + */ + +LANGUAGE LANG_ROMANIAN, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Terminare execuÈ›ie - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Terminare execuÈ›ie… aÈ™teptaÈ›i",IDC_STATIC,7,7,186,11 + CONTROL "Progress", IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Dacă alegeÈ›i închiderea forÈ›ată, riscaÈ›i pierderi de date. Pentru a forÈ›a închiderea, apăsaÈ›i „Termină forÈ›atâ€.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "Termină &forÈ›at",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Terminare execuÈ›ie - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "A&nulează", IDCANCEL,142,98,43,17 + PUSHBUTTON "Termină &forÈ›at",IDC_END_NOW,78,98,43,17 + LTEXT "Acest program nu răspunde",IDC_STATIC,7,7,178,8 + LTEXT "Pentru a vă întoarce în ReactOS È™i a verifica starea programului, apăsaÈ›i „Anuleazăâ€", + IDC_STATIC,7,26,178,16 + LTEXT "Dacă alegeÈ›i închiderea imediată a programului, riscaÈ›i pierderi de date. Pentru a forÈ›a închiderea, apăsaÈ›i „Termină forÈ›atâ€.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Editare" + IDS_MARK "Marchează" + IDS_COPY "Copiază\tEnter" + IDS_PASTE "LipeÈ™te" + IDS_SELECTALL "Selectează tot" + IDS_SCROLL "Derulează" + IDS_FIND "Găsire…" + IDS_DEFAULTS "Implicite" + IDS_PROPERTIES "Proprietăți" + IDS_SCROLLHERE "Derulează aici" + IDS_SCROLLTOP "Derulează la început" + IDS_SCROLLBOTTOM "Derulează la sfârÈ™it" + IDS_SCROLLPAGE_UP "Pagina anterioară" + IDS_SCROLLPAGE_DOWN "Pagina următoare" + IDS_SCROLLUP "Derulează în sus" + IDS_SCROLLDOWN "Derulează în jos" + + IDS_COMMAND_PROMPT "Linie de comandă" +END diff --git a/reactos/deprecated/win32csr/lang/ru-RU.rc b/reactos/deprecated/win32csr/lang/ru-RU.rc new file mode 100644 index 00000000000..a8acd99f5e0 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/ru-RU.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Завершение программы - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Завершение программы... ПажалуйÑта подождите",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "ЕÑли вы завершите программу ÑейчаÑ, то вы можете потерÑть неÑохраненные данные. Чтобы завешить программу ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ð°Ð¶Ð¼Ð¸Ñ‚Ðµ ""Завершить""", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Завешить",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Завершение программы - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Отмена",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Завершить",IDC_END_NOW,78,98,43,17 + LTEXT "Эта программа не отвечает",IDC_STATIC,7,7,178,8 + LTEXT "Ð”Ð»Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‚Ð° в ReactOS и ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… нажмите ""Отмена""", + IDC_STATIC,7,26,178,16 + LTEXT "ЕÑли вы завершите программу ÑейчаÑ, то вы можете потерÑть неÑохраненные данные. Чтобы завешить программу ÑÐµÐ¹Ñ‡Ð°Ñ Ð½Ð°Ð¶Ð¼Ð¸Ñ‚Ðµ ""Завершить""", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Изменить" + IDS_MARK "Пометить" + IDS_COPY "Копировать\tEnter" + IDS_PASTE "Ð’Ñтавить" + IDS_SELECTALL "Выделить вÑе" + IDS_SCROLL "Прокрутить" + IDS_FIND "ИÑкать..." + IDS_DEFAULTS "УмолчаниÑ" + IDS_PROPERTIES "СвойÑтва" + IDS_SCROLLHERE "Scroll Here" + IDS_SCROLLTOP "Scroll Top" + IDS_SCROLLBOTTOM "Прокрутить вниз" + IDS_SCROLLPAGE_UP "Вверх Ñтраницы" + IDS_SCROLLPAGE_DOWN "Вниз Ñтраницы" + IDS_SCROLLUP "Прокрутить вверх" + IDS_SCROLLDOWN "Scroll Down" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/sk-SK.rc b/reactos/deprecated/win32csr/lang/sk-SK.rc new file mode 100644 index 00000000000..9c009370114 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/sk-SK.rc @@ -0,0 +1,63 @@ +/* TRANSLATOR: Mário Kaèmár /Mario Kacmar/ aka Kario (kario@szm.sk) + * DATE OF TR: 29-05-2008 + * LastChange: 12-04-2011 + */ + +LANGUAGE LANG_SLOVAK, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ukonèenie programu - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Ukonèujem program... Poèkajte, prosím.",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Ak zvolíte okamžité ukonèenie programu, stratíte všetky neuložené údaje. Ak chcete program ukonèi okamžite, kliknite na tlaèidlo Ukonèi ihneï.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Ukonèi ihneï",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ukonèenie programu - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Zruši",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Ukonèi ihneï",IDC_END_NOW,78,98,43,17 + LTEXT "Tento program neodpovedá.",IDC_STATIC,7,7,178,8 + LTEXT "Pre návrat do systému ReactOS a overenie stavu programu kliknite na tlaèidlo Zruši.", + IDC_STATIC,7,26,178,16 + LTEXT "Ak zvolíte okamžité ukonèenie programu, stratíte všetky neuložené údaje. Ak chcete program ukonèi okamžite, kliknite na tlaèidlo Ukonèi ihneï.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Upravi" + IDS_MARK "Oznaèi" + IDS_COPY "Kopírova\tEnter" + IDS_PASTE "Vloži" + IDS_SELECTALL "Vybra všetko" + IDS_SCROLL "Rolova" + IDS_FIND "Nájs..." //Find + IDS_DEFAULTS "Predvolené" //Defaults + IDS_PROPERTIES "Vlastnosti" + IDS_SCROLLHERE "Rolova sem" + IDS_SCROLLTOP "K hornému okraju" + IDS_SCROLLBOTTOM "K dolnému okraju" + IDS_SCROLLPAGE_UP "O stránku vyššie" + IDS_SCROLLPAGE_DOWN "O stránku nižšie" + IDS_SCROLLUP "Rolova nahor" + IDS_SCROLLDOWN "Rolova nadol" + + IDS_COMMAND_PROMPT "Príkazový riadok" +END diff --git a/reactos/deprecated/win32csr/lang/sv-SE.rc b/reactos/deprecated/win32csr/lang/sv-SE.rc new file mode 100644 index 00000000000..2642ffbb1df --- /dev/null +++ b/reactos/deprecated/win32csr/lang/sv-SE.rc @@ -0,0 +1,65 @@ +/* + * PROJECT: ReactOS CSRSS subsystem + * FILE: subsystems/win32/csrss/win32csr/lang/sv-SE.rc + * PURPOSE: Swedish resource file + * Translation: Jaix Bly + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "End Program - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Avsluta Program... Var vänlig vänta",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Om du väljer att avsluta programmet omedelbart kommer du att förlora all osparad data. För att avsluta programmet, klicka Avsluta nu.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Avsluta Nu",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Avsluta Program - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "Avbryt",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Avsluta Nu",IDC_END_NOW,78,98,43,17 + LTEXT "Detta programmet svarar inte",IDC_STATIC,7,7,178,8 + LTEXT "För att återgå till ReactOS och undersöka programmets status klicka Avbryt", + IDC_STATIC,7,26,178,16 + LTEXT "m du väljer att avsluta programmet omedelbart kommer du att förlora all osparad data. För att avsluta programmet, klicka Avsluta nu.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Redigera" + IDS_MARK "Markera" + IDS_COPY "Kopiera\tEnter" + IDS_PASTE "Klistra in" + IDS_SELECTALL "Markera Allt" + IDS_SCROLL "Skrolla" + IDS_FIND "Sök..." + IDS_DEFAULTS "Ursprunglig" + IDS_PROPERTIES "Egenskaper" + IDS_SCROLLHERE "Skrolla hit" + IDS_SCROLLTOP "Skrolla till topp" + IDS_SCROLLBOTTOM "Skrolla till botten" + IDS_SCROLLPAGE_UP "Sida upp" + IDS_SCROLLPAGE_DOWN "Sida ner" + IDS_SCROLLUP "Skrolla upp" + IDS_SCROLLDOWN "Skrolla ner" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/tr-TR.rc b/reactos/deprecated/win32csr/lang/tr-TR.rc new file mode 100644 index 00000000000..7a52434a221 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/tr-TR.rc @@ -0,0 +1,78 @@ +/* + * Turkish resources + * + * Copyright 2012 Arda Tanrikulu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +LANGUAGE LANG_TURKISH, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Programı Sonlandır - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Program Sonlandırılıyor... Lütfen bekleyin",IDC_STATIC,7,7,186,11 + CONTROL "İşlem",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Programı hemen kapatmayı seçerseniz, kaydedilmemiÅŸ veriyi kaybedersiniz. Programdan ÅŸimdi çıkmak için Åžimdi Sonlandır'ı tıklatın.", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Åžimdi Sonlandır",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "End Program - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "İptal",IDCANCEL,142,98,43,17 + PUSHBUTTON "Åžimdi Sonlandır",IDC_END_NOW,78,98,43,17 + LTEXT "Bu program yanıt vermiyor.",IDC_STATIC,7,7,178,8 + LTEXT "ReactOS'a dönmek ve programın durumunu denetlemek için İptal'i tıklatın.", + IDC_STATIC,7,26,178,16 + LTEXT "Programı hemen kapatmayı seçerseniz, kaydedilmemiÅŸ veriyi kaybedersiniz. Programdan ÅŸimdi çıkmak için Åžimdi Sonlandır'ı tıklatın.", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Düzenle" + IDS_MARK "İşaretle" + IDS_COPY "Kopyala\tEnter" + IDS_PASTE "Yapıştır" + IDS_SELECTALL "Tümünü Seç" + IDS_SCROLL "Yuvarla" + IDS_FIND "Bul..." + IDS_DEFAULTS "Varsayılanlar" + IDS_PROPERTIES "Özellikler" + IDS_SCROLLHERE "Buraya yuvarla" + IDS_SCROLLTOP "Üste yuvarla" + IDS_SCROLLBOTTOM "Alta yuvarla" + IDS_SCROLLPAGE_UP "Sayfa yukarı" + IDS_SCROLLPAGE_DOWN "Sayfa aÅŸağı" + IDS_SCROLLUP "Yukarı yuvarla" + IDS_SCROLLDOWN "AÅŸağı yuvarla" + + IDS_COMMAND_PROMPT "Komut İstemi" +END diff --git a/reactos/deprecated/win32csr/lang/uk-UA.rc b/reactos/deprecated/win32csr/lang/uk-UA.rc new file mode 100644 index 00000000000..8b6e67bf543 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/uk-UA.rc @@ -0,0 +1,66 @@ +/* + * PROJECT: ReactOS CSRSS subsystem + * LICENSE: GPL - See COPYING in the top level directory + * FILE: subsystems/win32/csrss/win32csr/lang/uk-UA.rc + * PURPOSE: Ukraianian resource file + * TRANSLATOR: Artem Reznikov + */ + +LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ - " +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸... Зачекайте",IDC_STATIC,7,7,186,11 + CONTROL "ПрогреÑ",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Якщо завершити програму негайно, то можна втратити вÑÑ– незбережені дані. Щоб завершити програму зараз, натиÑніть 'Завершити зараз'", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "&Завершити зараз",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ð—Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ - " +FONT 8, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "СкаÑувати",IDCANCEL,142,98,43,17 + PUSHBUTTON "&Завершити зараз",IDC_END_NOW,78,98,43,17 + LTEXT "Ð¦Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð° не відповідає",IDC_STATIC,7,7,178,8 + LTEXT "Щоб повернутиÑÑ Ñƒ ReactOS Ñ– перевірити Ñтан програми, натиÑніть 'СкаÑувати'", + IDC_STATIC,7,26,178,16 + LTEXT "Якщо завершити програму негайно, то можна втратити вÑÑ– незбережені дані. Щоб завершити програму зараз, натиÑніть 'Завершити зараз'", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "Редагувати" + IDS_MARK "Виділити" + IDS_COPY "Копіювати\tEnter" + IDS_PASTE "Ð’Ñтавити" + IDS_SELECTALL "Виділити вÑе" + IDS_SCROLL "Прокрутити" + IDS_FIND "Знайти..." + IDS_DEFAULTS "ЗамовчуваннÑ" + IDS_PROPERTIES "ВлаÑтивоÑті" + IDS_SCROLLHERE "Прокрутити Ñюди" + IDS_SCROLLTOP "Прокрутити на початок" + IDS_SCROLLBOTTOM "Прокрутити на кінець" + IDS_SCROLLPAGE_UP "ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ Ñотрінка" + IDS_SCROLLPAGE_DOWN "ÐаÑтупна Ñторінка" + IDS_SCROLLUP "Прокрутити догори" + IDS_SCROLLDOWN "Прокрутити донизу" + + IDS_COMMAND_PROMPT "Командний Ñ€Ñдок" +END diff --git a/reactos/deprecated/win32csr/lang/zh-CN.rc b/reactos/deprecated/win32csr/lang/zh-CN.rc new file mode 100644 index 00000000000..4c32bdd6e04 --- /dev/null +++ b/reactos/deprecated/win32csr/lang/zh-CN.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ÖÕÖ¹³ÌÐò - " +FONT 9, "MS Shell Dlg" +BEGIN + LTEXT "ÕýÔÚÖÕÖ¹³ÌÐò... ÇëµÈ´ý",IDC_STATIC,7,7,186,11 + CONTROL "½ø³Ì",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "Èç¹ûÄúÑ¡ÔñÁ¢¼´ÖÕÖ¹³ÌÐò£¬Äú½«Ê§È¥ËùÓÐδ±£´æµÄÊý¾Ý¡£ÈôÒªÁ¢¼´½áÊø³ÌÐò£¬µ¥»÷¡°Á¢¼´ÖÕÖ¹¡±¡£", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "Á¢¼´ÖÕÖ¹(&E)",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ÖÕÖ¹³ÌÐò - " +FONT 9, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "È¡Ïû",IDCANCEL,142,98,43,17 + PUSHBUTTON "Á¢¼´ÖÕÖ¹(&E)",IDC_END_NOW,78,98,43,17 + LTEXT "´Ë³ÌÐò²¢Î´ÏìÓ¦",IDC_STATIC,7,7,178,8 + LTEXT "ÈôÒª·µ»ØÖÁ ReactOS ²¢¼ì²é¸Ã³ÌÐòµÄ״̬£¬Çëµã»÷¡°È¡Ïû¡±¡£", + IDC_STATIC,7,26,178,16 + LTEXT "Èç¹ûÄúÑ¡ÔñÁ¢¼´ÖÕÖ¹³ÌÐò£¬Äú½«Ê§È¥ËùÓÐδ±£´æµÄÊý¾Ý¡£ÈôÒªÁ¢¼´½áÊø³ÌÐò£¬µ¥»÷¡°Á¢¼´ÖÕÖ¹¡±¡£", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 9, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "±à¼­" + IDS_MARK "±ê¼Ç" + IDS_COPY "¸´ÖÆ\tEnter" + IDS_PASTE "ð¤Ìù" + IDS_SELECTALL "È«²¿Ñ¡Ôñ" + IDS_SCROLL "¹ö¶¯" + IDS_FIND "²éÕÒ..." + IDS_DEFAULTS "ĬÈÏ" + IDS_PROPERTIES "ÊôÐÔ" + IDS_SCROLLHERE "¹ö¶¯µ½´Ë" + IDS_SCROLLTOP "¹ö¶¯ÖÁ¶¥¶Ë" + IDS_SCROLLBOTTOM "¹ö¶¯ÖÁÄ©¶Ë" + IDS_SCROLLPAGE_UP "ÉÏÒ»Ò³" + IDS_SCROLLPAGE_DOWN "ÏÂÒ»Ò³" + IDS_SCROLLUP "ÏòÉϹö¶¯" + IDS_SCROLLDOWN "ÏòϹö¶¯" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lang/zh-TW.rc b/reactos/deprecated/win32csr/lang/zh-TW.rc new file mode 100644 index 00000000000..1d24b7dda9b --- /dev/null +++ b/reactos/deprecated/win32csr/lang/zh-TW.rc @@ -0,0 +1,58 @@ +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL + +IDD_END_NOW DIALOGEX DISCARDABLE 0, 0, 200, 95 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "µ²§ôµ{¦¡" +FONT 9, "MS Shell Dlg" +BEGIN + LTEXT "¥¿¦bµ²§ôµ{¦¡... ½Ðµyµ¥",IDC_STATIC,7,7,186,11 + CONTROL "Progress",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 7,20,186,13 + LTEXT "¦pªG±z¿ï¾Ü¥ß§Yµ²§ôµ{¦¡¡A±z±N¥¢¥h©Ò¦³¥¼Àx¦sªº¸ê®Æ¡C­Y­n¥ß§Yµ²§ôµ{¦¡¡A½Ð«ö¤U¡§¥ß§Yµ²§ô¡¨¡C", + IDC_STATIC,7,40,186,26 + DEFPUSHBUTTON "¥ß§Yµ²§ô(&E)",IDC_END_NOW,150,71,43,17 +END + +IDD_NOT_RESPONDING DIALOGEX DISCARDABLE 0, 0, 192, 122 +STYLE DS_SHELLFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "µ²§ôµ{¦¡ - " +FONT 9, "MS Shell Dlg" +BEGIN + DEFPUSHBUTTON "¨ú®ø",IDCANCEL,142,98,43,17 + PUSHBUTTON "¥ß§Yµ²§ô(&E) ",IDC_END_NOW,78,98,43,17 + LTEXT "¦¹µ{¦¡¨Ã¥¼ÅTÀ³",IDC_STATIC,7,7,178,8 + LTEXT "­Y­nªð¦^¦Ü ReactOS ¨ÃÀ˵øµ{¦¡ªºª¬ºA¡A½Ð«ö¤U¡§¨ú®ø¡¨", + IDC_STATIC,7,26,178,16 + LTEXT "¦pªG±z¿ï¾Ü¥ß§Yµ²§ôµ{¦¡¡A±z±N¥¢¥h©Ò¦³¥¼Àx¦sªº¸ê®Æ¡C­Y­n¥ß§Yµ²§ôµ{¦¡¡A½Ð«ö¤U¡§¥ß§Yµ²§ô¡¨¡C", + IDC_STATIC,7,53,178,26 +END + +IDD_SWITCH_APP DIALOGEX 90, 90, 265, 170 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE +EXSTYLE WS_EX_TOPMOST | WS_EX_WINDOWEDGE +FONT 9, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_CUR_APP,39,139,204,16,SS_SUNKEN,WS_EX_STATICEDGE +END + +STRINGTABLE +BEGIN + IDS_EDIT "½s¿è" + IDS_MARK "¼Ð°O" + IDS_COPY "½Æ»s\tEnter" + IDS_PASTE "ÂH¶K" + IDS_SELECTALL "¥þ³¡¿ï¾Ü" + IDS_SCROLL "ºu°Ê" + IDS_FIND "´M§ä..." + IDS_DEFAULTS "¹w³]" + IDS_PROPERTIES "ÄÝ©Ê" + IDS_SCROLLHERE "ºu°Ê¨ì¦¹ " + IDS_SCROLLTOP "ºu°Ê¨ì³»¸m" + IDS_SCROLLBOTTOM "ºu°Ê¨ì¥½ºÝ" + IDS_SCROLLPAGE_UP "¤W¤@­¶" + IDS_SCROLLPAGE_DOWN "¤U¤@­¶" + IDS_SCROLLUP "¦V¤Wºu°Ê" + IDS_SCROLLDOWN "¦V¤Uºu°Ê" + + IDS_COMMAND_PROMPT "Command Prompt" +END diff --git a/reactos/deprecated/win32csr/lineinput.c b/reactos/deprecated/win32csr/lineinput.c new file mode 100644 index 00000000000..78cbe27cfb1 --- /dev/null +++ b/reactos/deprecated/win32csr/lineinput.c @@ -0,0 +1,597 @@ +/* + * PROJECT: ReactOS CSRSS + * LICENSE: GPL - See COPYING in the top level directory + * FILE: subsystems/win32/csrss/win32csr/lineinput.c + * PURPOSE: Console line input functions + * PROGRAMMERS: Jeffrey Morlan + */ + +/* INCLUDES ******************************************************************/ + +#define NDEBUG +#include "w32csr.h" +#include + +typedef struct tagHISTORY_BUFFER +{ + LIST_ENTRY ListEntry; + WORD Position; + WORD MaxEntries; + WORD NumEntries; + PUNICODE_STRING Entries; + UNICODE_STRING ExeName; +} HISTORY_BUFFER, *PHISTORY_BUFFER; + +/* FUNCTIONS *****************************************************************/ + +static PHISTORY_BUFFER +HistoryCurrentBuffer(PCSRSS_CONSOLE Console) +{ + /* TODO: use actual EXE name sent from process that called ReadConsole */ + UNICODE_STRING ExeName = { 14, 14, L"cmd.exe" }; + PLIST_ENTRY Entry = Console->HistoryBuffers.Flink; + PHISTORY_BUFFER Hist; + + for (; Entry != &Console->HistoryBuffers; Entry = Entry->Flink) + { + Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); + if (RtlEqualUnicodeString(&ExeName, &Hist->ExeName, FALSE)) + return Hist; + } + + /* Couldn't find the buffer, create a new one */ + Hist = HeapAlloc(Win32CsrApiHeap, 0, sizeof(HISTORY_BUFFER) + ExeName.Length); + if (!Hist) + return NULL; + Hist->MaxEntries = Console->HistoryBufferSize; + Hist->NumEntries = 0; + Hist->Entries = HeapAlloc(Win32CsrApiHeap, 0, Hist->MaxEntries * sizeof(UNICODE_STRING)); + if (!Hist->Entries) + { + HeapFree(Win32CsrApiHeap, 0, Hist); + return NULL; + } + Hist->ExeName.Length = Hist->ExeName.MaximumLength = ExeName.Length; + Hist->ExeName.Buffer = (PWCHAR)(Hist + 1); + memcpy(Hist->ExeName.Buffer, ExeName.Buffer, ExeName.Length); + InsertHeadList(&Console->HistoryBuffers, &Hist->ListEntry); + return Hist; +} + +static VOID +HistoryAddEntry(PCSRSS_CONSOLE Console) +{ + UNICODE_STRING NewEntry; + PHISTORY_BUFFER Hist; + INT i; + + NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR); + NewEntry.Buffer = Console->LineBuffer; + + if (!(Hist = HistoryCurrentBuffer(Console))) + return; + + /* Don't add blank or duplicate entries */ + if (NewEntry.Length == 0 || Hist->MaxEntries == 0 || + (Hist->NumEntries > 0 && + RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], &NewEntry, FALSE))) + { + return; + } + + if (Console->HistoryNoDup) + { + /* Check if this line has been entered before */ + for (i = Hist->NumEntries - 1; i >= 0; i--) + { + if (RtlEqualUnicodeString(&Hist->Entries[i], &NewEntry, FALSE)) + { + /* Just rotate the list to bring this entry to the end */ + NewEntry = Hist->Entries[i]; + memmove(&Hist->Entries[i], &Hist->Entries[i + 1], + (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING)); + Hist->Entries[Hist->NumEntries - 1] = NewEntry; + Hist->Position = Hist->NumEntries - 1; + return; + } + } + } + + if (Hist->NumEntries == Hist->MaxEntries) + { + /* List is full, remove oldest entry */ + RtlFreeUnicodeString(&Hist->Entries[0]); + memmove(&Hist->Entries[0], &Hist->Entries[1], + --Hist->NumEntries * sizeof(UNICODE_STRING)); + } + + if (NT_SUCCESS(RtlDuplicateUnicodeString(0, &NewEntry, &Hist->Entries[Hist->NumEntries]))) + Hist->NumEntries++; + Hist->Position = Hist->NumEntries - 1; +} + +static VOID +HistoryGetCurrentEntry(PCSRSS_CONSOLE Console, PUNICODE_STRING Entry) +{ + PHISTORY_BUFFER Hist; + if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0) + Entry->Length = 0; + else + *Entry = Hist->Entries[Hist->Position]; +} + +static PHISTORY_BUFFER +HistoryFindBuffer(PCSRSS_CONSOLE Console, PUNICODE_STRING ExeName) +{ + PLIST_ENTRY Entry = Console->HistoryBuffers.Flink; + while (Entry != &Console->HistoryBuffers) + { + /* For the history APIs, the caller is allowed to give only part of the name */ + PHISTORY_BUFFER Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry); + if (RtlPrefixUnicodeString(ExeName, &Hist->ExeName, TRUE)) + return Hist; + Entry = Entry->Flink; + } + return NULL; +} + +VOID FASTCALL +HistoryDeleteBuffer(PHISTORY_BUFFER Hist) +{ + if (!Hist) + return; + while (Hist->NumEntries != 0) + RtlFreeUnicodeString(&Hist->Entries[--Hist->NumEntries]); + HeapFree(Win32CsrApiHeap, 0, Hist->Entries); + RemoveEntryList(&Hist->ListEntry); + HeapFree(Win32CsrApiHeap, 0, Hist); +} + +CSR_API(CsrGetCommandHistoryLength) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + PHISTORY_BUFFER Hist; + ULONG Length = 0; + INT i; + + if (!Win32CsrValidateBuffer(ProcessData, + Request->Data.GetCommandHistoryLength.ExeName.Buffer, + Request->Data.GetCommandHistoryLength.ExeName.Length, 1)) + { + return STATUS_ACCESS_VIOLATION; + } + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &Request->Data.GetCommandHistory.ExeName); + if (Hist) + { + for (i = 0; i < Hist->NumEntries; i++) + Length += Hist->Entries[i].Length + sizeof(WCHAR); + } + Request->Data.GetCommandHistoryLength.Length = Length; + ConioUnlockConsole(Console); + } + return Status; +} + +CSR_API(CsrGetCommandHistory) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status; + PHISTORY_BUFFER Hist; + PBYTE Buffer = (PBYTE)Request->Data.GetCommandHistory.History; + ULONG BufferSize = Request->Data.GetCommandHistory.Length; + INT i; + + if (!Win32CsrValidateBuffer(ProcessData, Buffer, BufferSize, 1) || + !Win32CsrValidateBuffer(ProcessData, + Request->Data.GetCommandHistory.ExeName.Buffer, + Request->Data.GetCommandHistory.ExeName.Length, 1)) + { + return STATUS_ACCESS_VIOLATION; + } + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &Request->Data.GetCommandHistory.ExeName); + if (Hist) + { + for (i = 0; i < Hist->NumEntries; i++) + { + if (BufferSize < (Hist->Entries[i].Length + sizeof(WCHAR))) + { + Status = STATUS_BUFFER_OVERFLOW; + break; + } + memcpy(Buffer, Hist->Entries[i].Buffer, Hist->Entries[i].Length); + Buffer += Hist->Entries[i].Length; + *(PWCHAR)Buffer = L'\0'; + Buffer += sizeof(WCHAR); + } + } + Request->Data.GetCommandHistory.Length = Buffer - (PBYTE)Request->Data.GetCommandHistory.History; + ConioUnlockConsole(Console); + } + return Status; +} + +CSR_API(CsrExpungeCommandHistory) +{ + PCSRSS_CONSOLE Console; + PHISTORY_BUFFER Hist; + NTSTATUS Status; + + if (!Win32CsrValidateBuffer(ProcessData, + Request->Data.ExpungeCommandHistory.ExeName.Buffer, + Request->Data.ExpungeCommandHistory.ExeName.Length, 1)) + { + return STATUS_ACCESS_VIOLATION; + } + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &Request->Data.ExpungeCommandHistory.ExeName); + HistoryDeleteBuffer(Hist); + ConioUnlockConsole(Console); + } + return Status; +} + +CSR_API(CsrSetHistoryNumberCommands) +{ + PCSRSS_CONSOLE Console; + PHISTORY_BUFFER Hist; + NTSTATUS Status; + WORD MaxEntries = Request->Data.SetHistoryNumberCommands.NumCommands; + PUNICODE_STRING OldEntryList, NewEntryList; + + if (!Win32CsrValidateBuffer(ProcessData, + Request->Data.SetHistoryNumberCommands.ExeName.Buffer, + Request->Data.SetHistoryNumberCommands.ExeName.Length, 1)) + { + return STATUS_ACCESS_VIOLATION; + } + + Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Status)) + { + Hist = HistoryFindBuffer(Console, &Request->Data.SetHistoryNumberCommands.ExeName); + if (Hist) + { + OldEntryList = Hist->Entries; + NewEntryList = HeapAlloc(Win32CsrApiHeap, 0, + MaxEntries * sizeof(UNICODE_STRING)); + if (!NewEntryList) + { + Status = STATUS_NO_MEMORY; + } + else + { + /* If necessary, shrink by removing oldest entries */ + for (; Hist->NumEntries > MaxEntries; Hist->NumEntries--) + { + RtlFreeUnicodeString(Hist->Entries++); + Hist->Position += (Hist->Position == 0); + } + + Hist->MaxEntries = MaxEntries; + Hist->Entries = memcpy(NewEntryList, Hist->Entries, + Hist->NumEntries * sizeof(UNICODE_STRING)); + HeapFree(Win32CsrApiHeap, 0, OldEntryList); + } + } + ConioUnlockConsole(Console); + } + return Status; +} + +CSR_API(CsrGetHistoryInfo) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Status)) + { + Request->Data.SetHistoryInfo.HistoryBufferSize = Console->HistoryBufferSize; + Request->Data.SetHistoryInfo.NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers; + Request->Data.SetHistoryInfo.dwFlags = Console->HistoryNoDup; + ConioUnlockConsole(Console); + } + return Status; +} + +CSR_API(CsrSetHistoryInfo) +{ + PCSRSS_CONSOLE Console; + NTSTATUS Status = ConioConsoleFromProcessData(ProcessData, &Console); + if (NT_SUCCESS(Status)) + { + Console->HistoryBufferSize = (WORD)Request->Data.SetHistoryInfo.HistoryBufferSize; + Console->NumberOfHistoryBuffers = (WORD)Request->Data.SetHistoryInfo.NumberOfHistoryBuffers; + Console->HistoryNoDup = Request->Data.SetHistoryInfo.dwFlags & HISTORY_NO_DUP_FLAG; + ConioUnlockConsole(Console); + } + return Status; +} + +static VOID +LineInputSetPos(PCSRSS_CONSOLE Console, UINT Pos) +{ + if (Pos != Console->LinePos && Console->Mode & ENABLE_ECHO_INPUT) + { + PCSRSS_SCREEN_BUFFER Buffer = Console->ActiveBuffer; + UINT OldCursorX = Buffer->CurrentX; + UINT OldCursorY = Buffer->CurrentY; + INT XY = OldCursorY * Buffer->MaxX + OldCursorX; + + XY += (Pos - Console->LinePos); + if (XY < 0) + XY = 0; + else if (XY >= Buffer->MaxY * Buffer->MaxX) + XY = Buffer->MaxY * Buffer->MaxX - 1; + + Buffer->CurrentX = XY % Buffer->MaxX; + Buffer->CurrentY = XY / Buffer->MaxX; + ConioSetScreenInfo(Console, Buffer, OldCursorX, OldCursorY); + } + + Console->LinePos = Pos; +} + +static VOID +LineInputEdit(PCSRSS_CONSOLE Console, UINT NumToDelete, UINT NumToInsert, WCHAR *Insertion) +{ + UINT Pos = Console->LinePos; + UINT NewSize = Console->LineSize - NumToDelete + NumToInsert; + INT i; + + /* Make sure there's always enough room for ending \r\n */ + if (NewSize + 2 > Console->LineMaxSize) + return; + + memmove(&Console->LineBuffer[Pos + NumToInsert], + &Console->LineBuffer[Pos + NumToDelete], + (Console->LineSize - (Pos + NumToDelete)) * sizeof(WCHAR)); + memcpy(&Console->LineBuffer[Pos], Insertion, NumToInsert * sizeof(WCHAR)); + + if (Console->Mode & ENABLE_ECHO_INPUT) + { + for (i = Pos; i < NewSize; i++) + { + CHAR AsciiChar; + WideCharToMultiByte(Console->OutputCodePage, 0, + &Console->LineBuffer[i], 1, + &AsciiChar, 1, NULL, NULL); + ConioWriteConsole(Console, Console->ActiveBuffer, &AsciiChar, 1, TRUE); + } + for (; i < Console->LineSize; i++) + { + ConioWriteConsole(Console, Console->ActiveBuffer, " ", 1, TRUE); + } + Console->LinePos = i; + } + + Console->LineSize = NewSize; + LineInputSetPos(Console, Pos + NumToInsert); +} + +static VOID +LineInputRecallHistory(PCSRSS_CONSOLE Console, INT Offset) +{ + PHISTORY_BUFFER Hist; + + if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0) + return; + + Offset += Hist->Position; + Offset = max(Offset, 0); + Offset = min(Offset, Hist->NumEntries - 1); + Hist->Position = Offset; + + LineInputSetPos(Console, 0); + LineInputEdit(Console, Console->LineSize, + Hist->Entries[Offset].Length / sizeof(WCHAR), + Hist->Entries[Offset].Buffer); +} + +VOID FASTCALL +LineInputKeyDown(PCSRSS_CONSOLE Console, KEY_EVENT_RECORD *KeyEvent) +{ + UINT Pos = Console->LinePos; + PHISTORY_BUFFER Hist; + UNICODE_STRING Entry; + INT HistPos; + + switch (KeyEvent->wVirtualKeyCode) + { + case VK_ESCAPE: + /* Clear entire line */ + LineInputSetPos(Console, 0); + LineInputEdit(Console, Console->LineSize, 0, NULL); + return; + case VK_HOME: + /* Move to start of line. With ctrl, erase everything left of cursor */ + LineInputSetPos(Console, 0); + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + LineInputEdit(Console, Pos, 0, NULL); + return; + case VK_END: + /* Move to end of line. With ctrl, erase everything right of cursor */ + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + LineInputEdit(Console, Console->LineSize - Pos, 0, NULL); + else + LineInputSetPos(Console, Console->LineSize); + return; + case VK_LEFT: + /* Move left. With ctrl, move to beginning of previous word */ + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + { + while (Pos > 0 && Console->LineBuffer[Pos - 1] == L' ') Pos--; + while (Pos > 0 && Console->LineBuffer[Pos - 1] != L' ') Pos--; + } + else + { + Pos -= (Pos > 0); + } + LineInputSetPos(Console, Pos); + return; + case VK_RIGHT: + case VK_F1: + /* Move right. With ctrl, move to beginning of next word */ + if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + { + while (Pos < Console->LineSize && Console->LineBuffer[Pos] != L' ') Pos++; + while (Pos < Console->LineSize && Console->LineBuffer[Pos] == L' ') Pos++; + LineInputSetPos(Console, Pos); + return; + } + else + { + /* Recall one character (but don't overwrite current line) */ + HistoryGetCurrentEntry(Console, &Entry); + if (Pos < Console->LineSize) + LineInputSetPos(Console, Pos + 1); + else if (Pos * sizeof(WCHAR) < Entry.Length) + LineInputEdit(Console, 0, 1, &Entry.Buffer[Pos]); + } + return; + case VK_INSERT: + /* Toggle between insert and overstrike */ + Console->LineInsertToggle = !Console->LineInsertToggle; + ConioSetCursorInfo(Console, Console->ActiveBuffer); + return; + case VK_DELETE: + /* Remove character to right of cursor */ + if (Pos != Console->LineSize) + LineInputEdit(Console, 1, 0, NULL); + return; + case VK_PRIOR: + /* Recall first history entry */ + LineInputRecallHistory(Console, -((WORD)-1)); + return; + case VK_NEXT: + /* Recall last history entry */ + LineInputRecallHistory(Console, +((WORD)-1)); + return; + case VK_UP: + case VK_F5: + /* Recall previous history entry. On first time, actually recall the + * current (usually last) entry; on subsequent times go back. */ + LineInputRecallHistory(Console, Console->LineUpPressed ? -1 : 0); + Console->LineUpPressed = TRUE; + return; + case VK_DOWN: + /* Recall next history entry */ + LineInputRecallHistory(Console, +1); + return; + case VK_F3: + /* Recall remainder of current history entry */ + HistoryGetCurrentEntry(Console, &Entry); + if (Pos * sizeof(WCHAR) < Entry.Length) + { + UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos); + UINT DeleteSize = min(Console->LineSize - Pos, InsertSize); + LineInputEdit(Console, DeleteSize, InsertSize, &Entry.Buffer[Pos]); + } + return; + case VK_F6: + /* Insert a ^Z character */ + KeyEvent->uChar.UnicodeChar = 26; + break; + case VK_F7: + if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) + HistoryDeleteBuffer(HistoryCurrentBuffer(Console)); + return; + case VK_F8: + /* Search for history entries starting with input. */ + if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0) + return; + + /* Like Up/F5, on first time start from current (usually last) entry, + * but on subsequent times start at previous entry. */ + if (Console->LineUpPressed) + Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1; + Console->LineUpPressed = TRUE; + + Entry.Length = Console->LinePos * sizeof(WCHAR); + Entry.Buffer = Console->LineBuffer; + + /* Keep going backwards, even wrapping around to the end, + * until we get back to starting point */ + HistPos = Hist->Position; + do + { + if (RtlPrefixUnicodeString(&Entry, &Hist->Entries[HistPos], FALSE)) + { + Hist->Position = HistPos; + LineInputEdit(Console, Console->LineSize - Pos, + Hist->Entries[HistPos].Length / sizeof(WCHAR) - Pos, + &Hist->Entries[HistPos].Buffer[Pos]); + /* Cursor stays where it was */ + LineInputSetPos(Console, Pos); + return; + } + if (--HistPos < 0) HistPos += Hist->NumEntries; + } while (HistPos != Hist->Position); + return; + } + + if (KeyEvent->uChar.UnicodeChar == L'\b' && Console->Mode & ENABLE_PROCESSED_INPUT) + { + /* backspace handling - if processed input enabled then we handle it here + * otherwise we treat it like a normal char. */ + if (Pos > 0) + { + LineInputSetPos(Console, Pos - 1); + LineInputEdit(Console, 1, 0, NULL); + } + } + else if (KeyEvent->uChar.UnicodeChar == L'\r') + { + HistoryAddEntry(Console); + + /* TODO: Expand aliases */ + + LineInputSetPos(Console, Console->LineSize); + Console->LineBuffer[Console->LineSize++] = L'\r'; + if (Console->Mode & ENABLE_ECHO_INPUT) + ConioWriteConsole(Console, Console->ActiveBuffer, "\r", 1, TRUE); + + /* Add \n if processed input. There should usually be room for it, + * but an exception to the rule exists: the buffer could have been + * pre-filled with LineMaxSize - 1 characters. */ + if (Console->Mode & ENABLE_PROCESSED_INPUT && + Console->LineSize < Console->LineMaxSize) + { + Console->LineBuffer[Console->LineSize++] = L'\n'; + if (Console->Mode & ENABLE_ECHO_INPUT) + ConioWriteConsole(Console, Console->ActiveBuffer, "\n", 1, TRUE); + } + Console->LineComplete = TRUE; + Console->LinePos = 0; + } + else if (KeyEvent->uChar.UnicodeChar != L'\0') + { + if (KeyEvent->uChar.UnicodeChar < 0x20 && + Console->LineWakeupMask & (1 << KeyEvent->uChar.UnicodeChar)) + { + /* Control key client wants to handle itself (e.g. for tab completion) */ + Console->LineBuffer[Console->LineSize++] = L' '; + Console->LineBuffer[Console->LinePos] = KeyEvent->uChar.UnicodeChar; + Console->LineComplete = TRUE; + Console->LinePos = 0; + } + else + { + /* Normal character */ + BOOL Overstrike = Console->LineInsertToggle && Console->LinePos != Console->LineSize; + LineInputEdit(Console, Overstrike, 1, &KeyEvent->uChar.UnicodeChar); + } + } +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/res/terminal.ico b/reactos/deprecated/win32csr/res/terminal.ico new file mode 100644 index 0000000000000000000000000000000000000000..7e3e77d2927643b84087068140f60632667bfa3d GIT binary patch literal 236566 zcmeEv2Vm9J_5V!*S%i^SGFDL`J-;%GvHNNnD=d_rV|^K0y__+%T24QW zv0Dci3qv}A&y}(Kqm23c%jumN`{iwn&6puQ_fBT);ky|dKpF>zL>>;z_Z*Yy!SgB5 zbHPET2hY8Du5gs;!E+P+?kLlPXDyyxon(6O9E;~_Cz&2R&&2aaCz&2Rd*FGl zt*wncc=aM{8|+~-RY7ccc$lTFY-72(xoqXxL3Zl>%j~0%K4Q6jU97N8$F972g$1cK(&~toqGi7FnIf-uwhO`_{6ijlHbr)JE1f(8sFVnppq2)9kg6 zUSmC1PqB>~H!_cz9-yb1oqqZpQ>CS`ciwpiG;Cx+xsfbV70I;K)krhQwEexTy1$KS zS5~v(2hXwo6T?hZt!34qF%9odpF4+q2HDzUgY4p^OYFj>3oNa-kPSb5o`n^ru~*)I zi}m#Ovaa4PcImClfJ?0HSTFNe#j+EpPO;%r$53owdanpPe1*Xee~%^OxL7i zuf2JdUAczxUO2%{ojt|;!-Ci=ue`!8UB1NBx-53>kJnf)%F?^Gm#sbB#|AE(W?3lH zS@1(uh~M9TA8-x%Awol}y(U80um=N(=gXK~pc{O@s*!d{!LboPSoR zCh$yfz={JauOnC`w&TThpkHYRI;$xEr|{Q)rooaIpDA*I%q)l6T30{E2J_(WKVJ$t zO2C-IQ3+Xcm`9;p2^ADPJi>~Qg-HgrwPEvJTny+H%nII|9AGx0b~M9nM2<`Zb`JzU zy1T=oqMDoV zj10=l1)8uZ+!re)a4nuM(2ooMq`<&QVNpfRz>iQ-A>&78elq@ISHJiM&@ish9XTT~ zusf=#NT3gcXk|3Gl{r-G>KLCzY#0yy-GzLHz%TI@_!SO^d4x#?C@yY@r`O}cAC_Mj zN&G1yvLOPJFRnywh0LJh;<|WA?Bn53SW^>NQiLGMSm94pScQi|zf0E*swNcx84%S7 z68%vI{6#LE5`UmnP+^lGD(7|ofsRrZR`{vf$pN>5vZG?!tQSp~6_~Se0p=XovPbn)i{5Rya zKwE>!f<>sd4}Vh>;V+l*1GjZhT{rz`D(A`cH-*J_W}(sX3nTuNscZyir{sEDVS&oo z8UIa!iqsqO1^;c}k7m*4(Up`7oNNXgk*)EE5&am$$n;xtN4&97DqFA0Wc()lA^eVL z^!&o;H=F!1#UwbQBDUC|-q{9!fZr2CCK;4g4mP%r}S5h}9s zTA?5OvA~}dCio*Hs&Y!rTTq|~*XDJE?43>MpC{EXq921xbNm4T?#N*TB%{2xh2O8! z1iw_!Jh#Rm?FuTy^#)%QKQ%DFRL4a0!mScS#|~qKaEFB>7Q@FFVK6ZEN!XRB8wX2fgIdgQZt>TEEd=Z z>Bcb2fZs3IQR0sW_yhd8f)HBqr1&9p}%4Nd>Ma6Z*PVOl_I1pUAi7v~Radsfj2N)aKp|!<{J{wc!NCckI;nP% z@|X04mM#&c>aFyau-N#UDFp>T#tZ&A#bW$cQ7-6j0Dp}1>%@46DsM~aQdy-)p}gBh z(H;D>K&Bt~8w~h8tLwlZf!|rMCpeVw*EANkw=8#86iVoAWyTmeNchPp(J)`3U%`)o zf^)Y6g%({6R3=s5Hf!5V*&5r49-Q8zic1vxF0GmtKe5s;I5 zr!puS~Ax5O*_CE`P*APKtHic9kND_x==!;yIn4d(cp zi^>v-KS1x$(lSTnLsTodC>)}EkV|EzEnRg2etpF}vJlzApBOhMZ4SaR`XfTNkcaWl z!i5HoEm3<}^ND6068<`c{&^CAh<@O&q98I+vY3;dZJCP$vY~vCg?X4t_|xWPVZi2M z6TgFk)kw17RCl)|VToS~ua+$7t*o3X*Dbol5Ppw&WTi9U&oZIE!+?Lz9Age76ADTe zLMDbh&~O;CFvDLY>hC-Q{=~$@j;bCd2u3M?rprN+2^dC!2mqf19SMsiR+-XYB$iFH z%6C|*fs_Zyg7R3n5P1mv<&GGSxHMSccTIG5?nsjY@QE8m8q!rz zYWTClz%J>As$PmSGVmyOM1LM%XA{4m+mMNr&1kupXR^c@jetjaF8cF$m!e|KxR`-I zt{5m!^Jz5?Ms}I<%7j~Bn}Jyni7EbO8~9r_6k0Xrx!_68$UGO*d_*n_CH!Rhjd!ua z9~U>RCphr zR)Xn08GlVevl5y_kgV32(8&RYNMb+eZ*G?K$E7-+UNm?_A!U)te+3b)5*ABbWHC1h zG%WWh7t}Yncs4Y{cZS-Z`PH5R|7ZHA(=)3%jPN6a&g#z36gQB?L%f1~!k=67S!WRM z=~><+kxv=;dschK8yCQCurPJ;ot~apkRs-Ykd>9wSzTRSb3?(?G{nOMCnKi|hFkMp zkm+X;Iy>VVXf@3Qy~rRdOIceS;cB)lhyl0~OqU*nhKkh|GxT^ctSTyKEU#kFt>B~y zmJ$R`>5o(wuvB4*ed2k71LNX=g-!qSdmK>6b>RO}ZW-J85958or^frxPmK4|uNm)M zOz$rn-#bm*3ua94LDGr;X+B(&ctC$$uHlayTukefXgg{zQyFH+;d`3_$rg1^6;he@L4|1hEJR@^Hl zEpI$`f^9_l)0ZC- zD$3hH_}qYR-00w~*4k=@Po;I!x{Zr8Ig!>BUv42Pw7@4XXk6z7I0Zh9T;ANmRt2Al zNlLmcPcnqbN=Pz~16WeEVoo+(8=VZsF>ymSFo`~a zFI3@rf5&{FWxjM4C z?8QjUDwcA@(tHBz$P4QoWj+Q>y&}>uQ0qHn21- zJm0BB3L_VOGSSzHF3nLz@+JD}b91#3gpd3z(#yj976b-@$lAb2*RCdskz{qZ3{u^Z z@#W;?N%*uT_{gcE*{eD{y{yVF5@goa1!h6mqXr2dx-to$ityFT^ko|yL9)}kdR*Nj z0|Ns??81i@Ik%O{3w(9D`g)Yt!=pJ!xKoIAF$=+=!3ch6Csy6nLY)F>D2;Xy%C>Ir)@x64}9I(K|xbQNlD10HR;Wx zvM*q=pbz5-8J|j5i-9iim6uN?*SH?n1(L#$R)>^<1?s8Iu_jB5hFfXfQYtS-t@VO` z8WVik0-rnSGq0_S%o5m$rRt{G5rd4cwbj=OpG2QK3JTRr=$S&|A%?2e>ZzuTGrO1Y zVQ{XCB6(<3!BTnApk%ir^tjj61}<<7=}DX=xpUyJM59zb%fB*xw(tq3vFYxCrLG|{ z4sEo?EkY*+VD+>_C7$OAd7z7w>C;H`6&vX5n9z&*&JUo=p$DuGh}Av zPBbaF5oLe{z9?tX7q?=zcXNVxMQQ0NmJ2MVAC{kCrITebgZN;>< zD7VBEADkT8XV0EJ7r{_$+!!bwso>q__#74bfX^Ww0XWUVB2(-%N;NouNvud48DBdQ zCxS#56<(oFF1ui+kpW<2w!nw^d|_@$1lE&H>6<;9ygelH2(Jlg3SLFx1}RD9C4BWI z81$Lq%aqrqsn}AGP%lC-;lUy^7IX^wbiTk>Y=O_KqC)CFO#X@<&)hlD_(V;0s)SFc zlkgez#loHCdLE%Ks1j!QT8s79gTXv*i#7`QymeGw78P6@tkdNdSEPx$CVI^!t{^KBO;VCpRgqYH;&@{XaI9B9H!K}*&kXYV{z573uaTZl)U&di8|_DnVj z*}Ra+NZ5|VACbJsTP5L(DiQ>Wm*fBA?Csn@>>~ob>x#yYNJ{DKU8oi0Q45pnUF&V) zxHQVGv-xj|Q&v%-cOiznf;>X#=+S-{$?~Ht> zJCcMHjDm{7^e+?#1{s5;Y)$QTQJ5~uK{nNq#ZL+35}6a4V}{L$cjE3p%mEsTN`W!u zj+R9}vwNj{z-d_jEnv*BE)1=_}{tGuURb9>ha$fwR@%KLH;GI)jbQ zT%+^WuKfH8>py)0=ab%hm8~5%p0%cGfWJXk78^QtjP=21VQ7ea8KiU1 zO#K;xvQ(_%>M_uJjGe!FnUOz(zdDSC#fC904(}`M(lK3wj-5VxR`^dSehot?ADzQ? z_WXHv0_WB#ehz6}@Mi!Yl=IgjwOMTK*<(0|4duzo0;qA$-7)x-;M_IxZ-75WMIX*| zd;hBNap*-^(z*&k_mDUjP7>0@-w6)<`#6BU{NIQCe*v>$%b0vK-ZK9O@f4Srjwh6x z6_spo<;qOa{kd-#0N;D7RTz(j9#Ma2GBk$^#ns?T5?%M!5;6SPfy}4GlPD z0I29OY?6YgIGoVf8(a%q!{*ONdLWR}Ov=a;+%B%IA-wj{gZ1))hWWVygG5hwSVkD( z$;;E#+e-}M6@`MHTx=gh(d1-VS;V2-yqZ+$g+$RPgbAvNKbTgcXp;ngaLp(-#bel? zponGUYZe40WCm2kD1j$e(1X{CIFa6H;TiR=qJnsxCF7Z#N_tQVWBGN5(u%Bb8PELO z$@M5w#&zQm3A{j%M9=)(9Kw?|K0Huo1<(APdT@mB7#4r*yg?FDYKfkP`8j?g;0Z}5 zgPmyznOZWnL!69znJ14OyZVwIGH zv{o6XtGJu%zQ8PoOizY{Cp<@2Dd91Tgolvm1ao?3JDEk1>B*?1a)pHfPi00JUK%2a zJ(BtP3rtx<_i6GE^2G`d>7^+i1H#d71Rm6PnVxK&jK@5;v7aI&xf}8L3VI?a8FVm-$)y(WMpJdHo;RiWlAS-Ehb#Ex}q12 z#v*|yE33j$#*t>?%_5&Vb-j!8Drwf$g0p3=LvcgJknAFd_{n! zyVV^ebWGHyNbNu|g=1mI(caBb1NqFdFhoJ6g|E?-(cdEMl6FsJvDJ0mq5}t~T3a8t!+R;1&xWBGS3Dr z^h)5VtaPlWa^dU|8IMa)TU%_K|6FKJYj-Ry{GgnI_c8NqE};uN6_pxuJSF)KZ4M5J z?z0vbCOQ~awZ%eXN5{e#X+_TT@4Tr~!JmqXRGkSuxg{m}VwJDeEwPOlg&YemXv`uQVI?ZSnmb$_V&0K zfd}jTHP{2Al*`#Azd2JNk_a-=BnoNdk)VpSp`*hE^!TQxn&ZjOZ&poyBo~uN zG8VKwzobOQGv7!L@LZ7_u8e-8 zG}+DhE-qP^xYgt|gemk?5It6*rNzAavwi|Sy2;r_JgK>*R#?V(0i?~v8L2v5PIkDU zCmZz(70g*)&T^J+XpKt>^0Ci!h=4dKgXR=c$jYcd(wv<6z{4tPs%UbWT46brsH`-Z zYf86bl4|_p2x@e?Y#qc7Jes*g(6~d6cZOJS^sUg{2%;SC`mjkv4>YJ|u6K5}ap!;? zcVhJpf3m%SCQP6qM4B4B7)OAJDh+7xK@o7#$UW0Lh11f69)B@~FbP?h5N65LNSD!; zO9`=pu%bdiDcKXukW4&`$N`K|q<|R8Em;+B=w8l^@F;5|p}1GpOt2hiSW9ui{bc|J zEXj;`KR^)>XxU?cZV)E!C-^X$4qTSlGpb%)ljD#cn(C*i-YQo1@ze`i$14O30F-q+ z=7k3(PyrCkzN^%b%PO$H_}HZrhW*Mx_`VQ*ONqX%)I{G_!Wtf}=g~KnvhXb>WgV~Y z+Ig{#r^fe~Xx}h>U&;7gr3=P2yuMyWYk2fMCHj_<^bMt%Vy%z9p+xI;{%K)i4bSwu zNrk;SmZsHWeebOJmJxkFDH3amYq9=C>v^=UNBf*0+m0;QDRjnadiN+>pW5w-c`<3nXo(!2TjxwVX7+Z*9^ix1Kl z5&b&1Ih1~ccq6_p0s%^TXpaJ(wjt}^d-u~P+TKMN*{vsMIwL8^w+&CNT1~>(!&t!R2|q_J;Moi zqXv)Jvq}Dfem{+OrlU{T?AadJI}%)7$>+9CZgvW$hY^FE-jT}hhV)rJ_)L_mUhkxz zOvd}lz=$bM$s?u?4z^66N$ERb){SqQToO395*XauW`x7Snryv)32$6Mf3crmmYjcG zrbbR*4is@65s86w=LVKc8Icc2Uxf6TBE6$`U7biz4j0iNB04TEvN|y^usR%B3Coz} zA4*^3=9U?T@)tW+)=KGvV8jKA_MpgW_tv?UbE_?r8J;cE*J^Yg+D=LxG-aB^fw`>_ zQ?iKxwt`5H?^%V&`PVw?wAw1{R*IaAj3EKYux(0vd-S|;3sb)ISOEHcisbZJm9@t7 z0Z3grH*s>LRG8>^1I?y4QIy`NsFw2gF{F>2HLD?@vNEuwx?$EVSnkCVN2)ChB7Ywr zv-GYl4Gs=!_rQ_`Y6r>09!Z8<7=EKo7e~Z>0X|BGp4=pTo-5KfG&oepG$3;dNT^U@ zMFY|oV@OaNg4wG?f1ayWFBU&KfJ6>56+}i;tQire={1?ezdYAMeNvKy0e>?T91e0> z%u$H+j+z?3eC)rI(rXJTe=_OOjuRxOOM@UmDh$d4dCKx5`W+oj(-#&_mF>AWxY!WH z8Me|%`VJ0QEO2mWY>b^gJy=ffrsS{5FT}?w=Fi7EnMi9=ln(2nG$vE}%&c5Dtm6p! zH7=cS<$=jd8&9K?wsxQ`F0udVS83BdqXQ!~!JK67e8KGHjS zd!q!`8*1iY+)3$yU{0mC9kze-DBihlQgR^BWY#DlF_pErHd zAk^aTC`4Gqlpb$1zQNuw#*LIurb~>pN7M`% zczuo*%EP_f-4Vgdi{fV{sL5eoxXa6q@Y19B8y-D5JOgnNKP=oU3<)x|ly=0RiVlYh zD#b@|r})kz#+834&nTxmN`?3t&Y)gC3(Ukx&H9$2iTLhb?seozIC`jyF^NBVROwea zez~)|yH}FwS#Gkwnz$H@e{f_B503#o<<2p3e8nfl&FGZa5Uaejim&{TlT%Zon7m7> ztmB7y#>9XgBN3AdjTX5=EW1jPjw2cPWTL0!<2MzGTk-Qa+*lb6yM7c^tgMLsu7>;7y8g_*D1aw zzEx#L#L6AwyL+XG_}+-0BOMD;t!r>i_YH8e&Vk~)t3`ZmsTAK|7ZOlkQe2{%Z5^HH zF~rv*J|+{czQvrE%&Eeu8v00i*O9njy6WT+5fkHHD&g0b=j8Y!elho*f~5>1O+UjT z4~ic{@nbx-h+l^}Z~$UE2upApD$RWmnGo=y5A=;9+y zjDU!rkYExY=CKfABAc$57%Gufuwgc6h@Sv@GV1C?Tx8xndNx6r>ITh zXU(EbF7zEe3Wj{>Mx1jqDaT!{cFr);18b0oN*PfsB`c}?fpv&qYKV`o@0taX(kvUq zFZDKwKXvNJmUFSNmgDZN^(>d@DbF1#vhfVaTz9P;KS-NWZsN0+A@7hQmqVrLhKr%Q zS{vj{__Yw2eZg5Bjt<7r(=*7P_(9Ij6rDLb2HE$Sn^G^?@5KBQZSs@UVVE2$sl=u{ z{(FKnk5c^8M!tSRB*W3{GF_Fu{CcEn{T}(=BK%se>L^@uo;mv*_EqT#5n|5Qe>ykpJN~W z@gr8fu}#cbX|5WnO=BDTahAYJ?CHOJ4s*&uG5@@B*|;;oM$iq!SDiWAT+{ob;W=0=roq# zov92}jn{0iXdiD^A0O|8w1iA=<9p>UDLWcJ(oVovaMRVc`ut+Y~7 z$s(ao-Y3m#?xq9nov?4#PXaR>gXu{o|HtQF+>q)#P89Bf7(=O zIZEEGf(y;l!YAhiI@0gSQwwc|u*{S|pXq@vDVa2G&J#*5{Yg(3hK>}MOdpo1oB?R# zn&OQLk|{-&9Lwl0tw;(yq4e_&25I~t{k@D4#()@g-5}CYk`w;R$VbVS#wq>)vZ*WM zl(UX_P=Y*8f#Wr6fPMQMe3n%WTGn^{|+Z$5R2^OVH5p5RB_1pn-y+*lTg@lPSfAT(yF zZfJmi@(D4nQNf1a1bg)bj4!g9wCw7&x7b@4BX#xmV*G$H)9{cq)&LE3E)o=lUAT0C z_4VN_qK!T56viE=;OpH3J9jU>x1{Z9VngRoF)eJ~6Jde_6CAM3f$@w}oSj1qHcZ0} zd@!9I9i>gIslK8|#lf#CSJ-r%9Tmfdbe)uj-Vg6%A}|m^BxAr+GS1VU5+~b?OfGS8 zA!&FoTJkV4NC=~QKfF(#qVf~Nj7VW-!ID{Qtau-+^79Uq#wgO?KyP~QI$7lx7!(>A zhL=je;}@q2iJR;@Ez-x+b4I4}7T;1<`8vl%`?{c8&`kGqF#Ir0mEx?5PDxaUPml7P zt~PY;(|jGBQ=?O2{bzd4&^V~onXBD!K&1@sy-?VKD+q8Wv+q`2N>)YASwh!)NFT8k#9lP}wcInxt*_Y0Li51pYu@(FK zS?}JhZ1J8x*1c;J>)N@2weMKRTKd2apIlh}MKeB`M3~gmydpEJx-5Xfb&b6$zZz(HW)ymQr&ts{JbSx-S z#qK(LhV9(Dn>~E-QTF8r?q{i`d2G!s1MIC2e#A~(yoWXJT*)feEo50edY0T?#gaP! zoz*N*6U$ECejD3@@}Ga`0e0^f&#~C-R93mHm9-8mXAgYiQTFWH&#~CLd=^uk$6{&- za9^9xeB;B}@UbJ{y#;J355z-oKZvxoJJSA`+1qCm|e3R756Mm!(^WdmS8RWM*is6X4W zdly@=ZZ$grnhzg4$~^qMn4c<~`6WiMwylfUm!7|$-3QRr#Up5;qfdrud;<eXpV+n~WmVo_Z zF$r-jDmI4Ajv&M6fcVb_286AT{^FKhzI+*1+%VKxsJ^fL+4a@WmHjWZ(VnciE4B{A2dv zhaU>S2tm+|e*PhPzb8NyeQwFCMAcLJUS{1EU7i;U#hgUyi#XUC3lcJU$z znu+)!h#!misfeG8_+^McAMsZq{$9kti&MU4;qk{Gf5H%+eDcY6ha4T3KlJd!XWn__ z(RbbneC%DZbHVWE{JSCeisz|Y5DwxRa`(F-2jN0;_~_B2$5yYtCFJdQLvGozahC{t z_UsMW|8B_YHEY+c$F{*un>O`@On*0|vuk1ZqFnJMzu}M-?}q3y=jr3;FCgdFT_N|p z8&a5AToPRxTaJy|yF!BB4N1{x($h1rxg|R%H{_vrL$GKS6&({B7ayOH8}hCNeeYTZ zv%=zmWH;VGX{ntZAOvVn9Ykx3dsmajm42HD6C9Y}zyt>-I55G12@Xtf;QuHGl2X+U zGC1N&;3PqcQ%tscQgU|Ir1Z>;Noncnlhe}EEWz2vJH$z!oQZTXfP@Uyq@>go8K=Na zd5Ap!kFxCl1lfYe8j`2r{UlXJ`s5T%s&i6`+C`O|y0v3fy?6L}#bDs}d94lGMqr>eLix4RlPCu9=h!x&e+!nv{uw z_b3_%IXfn!&c>%FOiIg0hkTP=RY{3%s_e8WkO6(&b!wa@ZdwxLK(fKgq8H@s9iN#v zGbugY2Qu@;J3q+ZFEKgMUzL#*kdU4p2n$>wewzhZ5dw4|A8 zjm8(h`6X%+{S(sFfq0%pa!k@B1*_84A&DAID1N77iDxINlfo0z6C+fbl*okC)F`|s zh{kU*>J)WMa%yr6(#0mFNsyS97-s^~`*i%4k`zmEMLM-Qns6impDHyqGBHgZnUtOs z0UZg)``PN$)G+K92~EsMB!9m0IPk zN>BA6UL~qgf74O|iI0$HuqrJz6l>>U#P7tk(@}0ul6O*CiVyKY@B@4yStn{zBw42=hojGoM7@Zn`l?DziGvO#5YG`O zDUoEGs!kzkk_;h(bUbI^_e}6C3%_S4snv7T8g&k2n2Yy$NvQ;>`S^{XU^Jk3z>}Y> zQRg9TF5!S4EE+cC;nXThd?9 zIUDqb;e80=2a&Fm&XYbQrlkmdNJ>wc0ld?|HxKk-?kQ;+*Mv-rebO~fDH)po%Kid2 zZHL_A0w+yDo^wW4rYrd2E@YkxnWN8EX_CEBH+|8z`X{EU1HpqJ!2_z>8d=_{B=2Oj zy{Yl2cPf%K&nDxBt&X`Scf=D4CxPDxBnoC^Lx z?r7Vf(^MYtBLHQd1wMy>-;jAqIOGroIiX)x#|hq~CMQyBYmj$}I)nI0d<8DZI)&1>9`Y5kos0V_;Hp9VTI5rQ=X&6phi3vk;x#}A=R@Z6LEi#PfV9MKh|_@h z1bX0`M|hA%2U*wQT`iug5w{BW(2?W{+?U~Ai@3$OFT_1)MB71a2I(r=C6rz01KvR& zQj$TJ3i0C+5919QUtnB8Z9}RWZ6L;|I0+o@{i)7?PH*7wM0PxS<^W}+OZ?oXW}K~tu6tS;$1m@tHc%cHl+sd>L7!9 z#M2{v1NgcC_f7b%8NapSxea-@j1?H!O;JKF44 zJh$Mv8TmGXp82?^yr3Iu9dOs;eKlY%?$KsIA5zt#od7L`xQE`NAAv659=d>bELDwG zO|3@!MC6x%d(;86f3hBcccEwpgK!N*U+Ithd~gP)H~2b3ovxmipjJT54o)z>^Xx4!0i8D5yHtGR;vE4aQmS6BTKu7K*7 zD|OYc0KOr^tCe-t-;|*0HC(?XK^0xUU0GN49T~1x)Kw|)I^MlrQELtKUi?PC%ke2a z?gNihh#qwJE}jj1ZgDcmB0MzH~7onc!j_C$}9ZJD=%~S zZSfagdXYc>;uYX{fj{@c^ZYX4>(4*Ozy921{wx5#T>P1@Kg*v6JoW4|{K;pY7VyN= zPw`7nJt^SvC!gS7d*Tv*jNsDa{Nm$Z6Lfv`Ymf0qAG^pOdF-qFk&9pD7ry!^|H@Y% z;a_?55&rNa7x+Vue1$)B;Vb;XuRP2j_{u~4{)Yh%J;=ZO&;$JZgZK0E58Thc^uU+- zefOW|_kH<1|KgXw#Lu0-kKg+xzJxz7F`VxPIsL ztNhznukvqQ{mzIw0KYwh4!mTd124S%qCp4XzxSEzz@sKQaKS_e9yZZ|2mgQ2ful!I z9}kNEVZ^`t*8&9-p+3&Fs+lf-)5|r zO5MEeqR~aC$gGWhThxz{DgM4WC5I=BuSjdm+ z^CA3Bb(`uqrKdW-_s{`8cyI`H9WbIzaNV?R3x}U6hkq*Hy>F23g^UlN9>cd)wEridd(ray!96U{#!o~`BY6V}pZVjil0DBTR_9KWoz8URm-}ZjK75YHqx+6jdPKY)^ zNk7(g>%b0qJTT1RV=vlqc^vn1v;iaT{peb{sFlVJ&;ij$z4i7F`CC8yA*XTnSb0wT zZq$vINA~fmA09UYdZ+Mc>B{AN@$z229C|?SNC(!5e&i-R6BuzFM0-wSe`?Q7-;Z^_ zb8xpo2e4nl&;~%ZZ6mb-(2e^6qVCh&?1yi^&A*Sff9K$y>&km9+Wvq4z3I{rKl%RG z`C}h{mp8w2htLChr<9xIzo>T!U$SDEEO$lTitME;>BN4F{b)=`V>%_hJy+~;g3e%U z3H_mQqB4)e^#NP*U)b9-sj0K=_ZTN~?33Yd|L8{oXs&HfeiLyN8E)c(&)tiyYf`xQ z@#`bXzh~J}zJ9Zkj@kgF9iVs8Gs$iUb)T-+%VVmy(p+Phw{>?aI`9Y7@yV7t06Sl3 zb5}cW?rIZl&5z!Bhrj*KkHoy`dh`8XkH7J;ll*W0`iD?}J3n|{$iMZ1QGQDOr#8UY z-b3D`3yMC-eFFBHVNS50WB=H7%fD|&KjbgXH_1Mvv;i33g;~lU^*_61VF$WAIYA8 z#(*o=ujMo+ShH~hU%P1|bb!W$n>qHb8s>#N_UtmGSMZE=r7@*qDa%bfASNd1Jv&j9fXT)OQZ`lUL7m%iQh-&5zYM+`72lI3wmizpv()w zW7Byd_M?uA=Tv{mp0ufdi$VSv=a~! zFz5pGLFmFn+i=}-p}I)pe?|7nmEI}y!nHST;2Y5f^nv$VA$w{M##5)HHG`v=Q*JZJ zAM5wxi_NAwuyplueS6PBVK1Qdj-R}DO#sb1s2|yk^aMf|K$Fk~=mS0P+_RhS-M^m? z!4|2&0oWc04jwur0mh=(fy)oUEs};5q_8e{S?6w*u9QmvoOI?%zX)t zVg5sK^YP;nU_IpK{||upGh_h=1fLE`JOY1+AC_e$o?s8Vun~-<6Uw>~$xvzkG5)iX zfA5-=`i>>tykqe~vF>&4z4tf)%^%1XLH0#zg9wkJ4~Zhp5z3#-g>Km+its!V}rJU+i$;}6Wnpf9RlvW^G;51`t)f5cinXt zC%F6Wy9HpcI{#k=;=>`-qtW_fW$n;h{;gK>$M{g+xpWc7JV2~{Ui&HJ51{!&KkQK3 zU{fL>T_701+~P3uK7zVSaNxiJzGKG@zGLT30Xqi$m>OawRNwZ5yj2kSfF^+fm1C>44%xK<&5E z{=>#&CI1!c*XS26Tg)*J6l=Bb{fy-QQ>;JUEbM`hk?C& z1dv^s#(JU+Mmy}`Ic-Az{~`H1PjwUHVrm0!MW0|x{)#UV_1&Y@|5Y2;=@+e7%3%i( zej7ji+0UQ@KNIVZWP7A_Bm&Zfedv=({?t#~(#wf=pI<-N54n>3Y0PX({*pi0NvtJ~ zF8|dxt=IRg?BzYsfd{_wAphCVCHYf&$@c_)C$K}3F6>92PvgD4=m#cb{rSn-7O%&Y zzwEO_YssV4f1H`8U%YA=U%Yae@QEb(6Odmb&NCBsAhHFKzaiCs8u!uI-4;#L0%mg0 zF#ieo40$juaHdUOk0pQMTMEC@{@tU?f9>Xt`X#HE^Chd6!=LYA$o}U7$VZj@3ds&c zK)OKnU&tTxH(T<@Iey$bDrf@i=0PxcH9E`)@e{mu!C1o-#+1M8$4GNFWBqTl8vkSM zQNMJ}3chsp3gI99^It&z0Ptw*Qgb>l=q=@CYpU&r@-;QwZdi-BHXyS*U*zqVIGg*%hXLq11|;Bj8{mg`w!kYq zkh@^sZwX|N8EyP0e44SxfadL^$$!Jv&H82QR`F$6C&n4N{O7-f`~i1h?}D<1psXd4 z{K;OoYxiy|eIyF|+MsRz;J7SiV>`524k0t**CBOC^gQLrTLh&?Ql4bkY=z905jA^%;wY|`X48}b+Rfa=0{!FK(yRcGj3Xl@)&YB9?H zgTH>j|K0HOzx-V2+3FLJe+%TFG>ZHoXPi+yZt#u>;x4eUTEcdW>qe{pVy_W&fb2Gg z`rl>U{@>KUMZaRh8V)-kzi{zUS^mGoex@_R20%6>VM~NPi0c0!#(p?=*rxnN{f~v* z?EyR5h|${-=t&&ppA(BVSd#x+e|$@Ezxuac|M2%e2>Gvq{7Lux62on_=_Bdv^}P2& z{U;l{CD6ETH2Ew0T*wz}H2LEkbp6VWYdP$IV&B{^fAvdFK>ON`i#a6rq=|iLw{WWe zH1?zQ9ZP+rXX>lQ?EjSh(MBJ5J$zB(TGszx|Ml1W-wi+e%g?0xueAT8)qfjpzvvI_ zY!kdAgShK-4=dP?{W+t_pY}VE4(x>eb~O3p41E2no7QpI0U`g3ko~U&&|WK?wJz;X zq`fH61(N@6^!>EfXG{J>E6!fG3j&fO`N$B3-|U0gg(|LY&*JA^yzg`E`)RDP=C+%7 zN=G&i)Wq&n{G~l_WWOIx{`mfZe)XpH9CkoX`)7ak zKI9Mh0`|EoV@PGMszLr(?{%5xVFfhTv8PNTPAbZOLlBGfu{@$AnWuE-@PZW;ymVa+ zFI{gh)FSSUL1JAN56Mp8v(nkQO?3!riRX!T#&e@u4!(d55nV7q?J z<_#QnK(UAVSMR?M9e5vmp1+9pgZ3=nE`WSPN&YxFpKss3-KI{`9B-B;_QoI$>92#I z;YGM^69a8pCQ~lqGzQ4b}AMMdpDc1`d_wO0duipwE)2*AtnHul^ z`qz;EuZ1rV%>fCIz=bm{$RCU3zpa0pO`Y@st=H3bj~LgEe$k%3bF?^Pw%cR1>-PSB z18qO`8)R2Iweq4$hziWWc+Oo;@JemnKMqvK{`7jG$$Fh13MuX z@x~2mq_qKgcxMMtd+P2r%?ftx9lic5XVUCBFuMGKN58Ru3*Xq^2l+n<`F|jE-~pVC za}S+!gEMXj&cGf(zL?baZ{51pN*_r-Y3?@~PZJyGz+RTaqsr56fS-yiZ19x$YzI#% zPT2K8KH^(J3f|oS6deI8`P009wDw;)mx$Kw4fTJab^pI}-yZ!<+u_&0eG9*Y zGnORze<03wQO-M3&R`+=Q{TU3%NFhiowNk9YXoPiMg&zu7S}Aw7m(bR!=o!SXb%uS zYdnyMbT$C(Pw?jgNyx_zKwB1k1AyE}=FFg9FbOsvcfixGyH!6kvsqe>k1FiItbd=UPM&oG$ z>VGQgKiST{_2IYpp+}B!Eo?Bb|Y^V5E1SOB(?iF~+e4 zw#Po!VX8(f1oCghZm)d2ihAfOe=jP{iOL^1o%G^s5=UTjH&+98Y-=$z4zz$gg$)#58vnafBPZ6@Yo8Txw2G1 zYIhOZggMv8`%=it4#0P5x!q8PxZ{Q_q_qL%cxMMtzcXbfDOMNz-1vQ%d2M-PDw_+`M>+T&mck}D~BO(8f#F?5j2cYvY#Tl76$4`;}O(V#E ztol#&Izz7iB!4CR`5%84`k?r4TzdOSzV>t<&t6r=Gteez78UXM`Z=iEXj_UE7@=*j zyZ(=?r&ju_MO-Vmkv73T9VI;$^?#-(U@qz3uU|>k8|95h;J-YIKZP91ne_rh4HORl) zy8YjG_@KTYw&ec70dfBBN5A=vkpHD8pTN1f7cdX_ihzgU8%*-2z8@b;=F|OWa_?Dw z+$+$Rdj|Ljh{;dm$yEhBu`-Xxm(Srb)bAChiFq=~A+b4E$Wdwk6}ekp|A8}9KKkoN z{Ov!zE%ZQ)6(7Bsmu;%&>Al4~ZE+D#m{0A2#(EBJ$DCT4caP0`gM11RciaFP3$0-` z-q{1QU}yC3@v;I%{=qCDjnBChE%`b{^Q&Hm!JNUfAY6a_}~BacS{uz&;R<@zxc<0`Z{D<$`>vv!-v18wEaTfP)e*0Vg zo8SIM%mu|++L#9@XK7RY7xLc#`J>Jo=z$n3 ze&=Dn=)`J1XZ>6O>AmG#)mQ{S@I1R9wk97w;C4bS;*A^7XVuz(I=r(3sNeDMMg6yc zfdjJq;o~Fi(Z#wv)`XS)+R7Q{)Z>;J)9H~N)U`IAib)X7kA9#MeX$h3egV`6reB2Cupff-%tmgn8(XkK_50X@!I}o zUb78=>*8Z;_@N6o^9!$E=kB+E`z`kregYumP@vQ~u0?B^^>i*hw>$oTS zd!Nu*+$V%U(u0gzE!TE6@RCjfJufC8eUNpc5PDJAR?iDaC!hzU69t_Z6D-y9inR-P z^%l~D7NHAuJ6iebQ=9pTC+^@+{_q+8*6-gE{RE92shv>94x|s%PAFqVnlogsD&@%? zg*+Vm8wu>Oy?i!tvS5#}0k8=~*a5V5Fcq@5gk1+E`O_IW(*E52;%xZcI6sAS;gO3M z;m`e5;k&EU|Mo>)R{j6*Eywh`4tqVooBDD#9XliHbks3jR?8;%}%I7ym3Pw(%OJ(yt4zS|DWm)`CEXg{K>|z?9rw5 z9$NEP`t0MkoiNmYoAN(#vwjb3!~}H4|Hq$vB6NVx0DtNke0v3Jf&?@lp#Fb7+Wxg` z*P`E-Pi}wFCzHMQ;N0;oqqzlS- zacCd6q5k(+*Z;#OkLw4A@tq04l~-QkpFsWuFG3f@cX;rv5BlB*=>o}r9oqgiYu1Qy zpWp0YZVH7hb-aGV62M}vTi+v~4%fPM(1~@>3Ft))pn9#O7gcMZ6M(s^I|YzV%w5~b zYqu=q^LF*{1qYUJlJ$avi}~VPH}K)dZsixhcZq-hcTyif{Q%7gXur=N|M5p*OD6f} zZ`Gj>s2-KSiH&9O8MPCd5O3U2j>LX}hy(R26$p0=rgtOYjw;vAEe(ghi zANFgDJ{vZ?lknkJ)hq-5-e-ul>%C!1MOV5 zrHeQ0>gJ967xM*xrlBQ#(>(+HuIJD3D?fc%wC(Tw=^ak+i@*Fr*ac_|Na1h)`kNR7 zkUqSQv$ts;pjlkTV=!kM3GA7BeI{`*_K&>*n2q%`OWD)2sr+f5w&h-JrOzh6ZKM1b zTJ`@&Z#$vicN98veYN^XrMvl_#gf~Iv3y-Yg`yM*V zANuyA{Egqf$>06+y8?u50Q-7qy~xxyKx03$7b|-4!h0|A-47h%Wn1TSO-~t*LZ3Yn zjK4p>k+FbD+G^a}0im!F(-{z!uzPq&wEuK2BJIyr_UMZ5gFzqYyJ_^zwA)VKKC1kW zojR%Ce+=J^IXW!9*GBRupl`S--)IxxX@f3M{in8n)v8ruycZA=%1xoDMaS#cEaLU6 zySa{F)k0n;K+*~QrbWDY_fp<|a5--yIIxVbKfRrwdio51_MPYWJAZt~AahzD_|4yb z!~gQnzrgxqod47PpZ@VD{)>YV{?ZSg5@SRC9`qZ#(e7+%=Ow-KdFp($0W`+4 zF?OT(p9{do6mtVWw&Q7mK2~5Vf12Yf=Y2}wo;qsyb{_2mGt~dZ*7g79+i%qm-Hh}6 zjveOo&9>kD{`Y|2iSK@0d6~Y|c0~Z`0?B_h>i)`=E4feDEFKscW`KamP+s2M$Xm9r z5zySfniDkjUnbeTr*I&S7G0nozQ`J-YX9tk(PMZb!TY#zjkDf63ULbUV&hDe{ zC{mwIdpH#NciNEu@jGtQA2^P4JYgq(<+WG&?;(GJ*YIse8Uwrtpm_m-BL5YTzwhiI z9vC%S0$C3#7B}$GWNwAi;5bRjXn|G|?-A6a@CD=1ZvL+y#&PRXy$jZhi zI(7LEtp$6P1l_z6P_n9xr*-S>;<=Svx*#hnsKLAOL(0M$ZV%8L(NgyGy!-G0sr|?I?Zp0E z?9ruf(kb7iQ^sz0p1I3J{@qsn|1Ec((jUC#C_i}oDF4Q{zR7?82go1rZG7ADl~<+j zchWaJU&7ds`mH(?juI|Q(Ye| z&{{C*2I&d45#L5zaoaOzpg$XV$IYvG`;par{;p-bZqs63i}^;)h8|vnxkmN+MFOfY z-ct4RQ7# zwf)OiEa!e0`v*nC-xDxPhKhw~|NB<*=03E?nA~DZsQ=s6@V2e1c`Lz|RlG$2>BLIjeP|tDdSatt&aTK`8PkzXSIGTO zKju%q^(^0Y{~`E;tpT8&-;e$sZM+V)1hO&Jnb??wF2KGd>`aP{Nzn(`m(2BnbfJ}( z!_HK^q>-mKSMqqs;rgK%Y3u+!;@kj`4SITzpA{(epX4vTVTyga;=96_@6o<}qf11nDc#p>V)W5&;hj;iRZ#>Soefa?Iy}gew zh5f(#=tkbUXDx5qv6?q-C%{+|umI!A`2-kOHV9b3^;-#+^LYeYmh<{8%Xod?GOp|E z6;QW%DX%5iw8R7!^U^gPJRM`i6!hnxIV`M2KRRNrGWV@|oAH|_p^?41Ri z9oLbs$04&V%Ohr%M#CVoSVpllg3&NDGcz+YXe5o8?bwbTFvM{g95lZlhK z>iz3<_thN`TKishpXA@?bGmO&-@aAVb?R`OiQW0JygM(Jcjd+KuG|>j33lX0^NyTo zcFl=m*X;9b&x+)2LS_WtpXkp^!gsMV`lALBisP$h2mW?AE@3OW8y$;zrm7kW|?PY{ON6ZEVku$e_j&%eL6?CXY%tO{gUe+@8sx?O!$O! z4r@$h|B^)ZLY=`Abq0+X_Q({X**ybo31~x5q(`zF*qtUs@Giuf!cN4QJ5$1VhmLD< zC~p^%LKNGQ&@YjQ_#rU}ZI3#rFKiYNQ)~hoQD4}IxMBn93LDUGwJLZQJ9w-|Kf?Xr z02!Aw0_YR4z6>P(A4&gG|1DsrKg-*nJLI-T&m%sis{{m&Ui~~3H?>B<}+j@Ik z>)Klw?Le2*zpVcy`VR_*{^!l)>CnHR*}+`i9_hy+#pxi8gNsrXL4~OtsB9vcBk^v> zc4mVtjsf8f=^Tjn**7;q;e*sCC!W1^yt3n96R1z>Hlga1unBi*TT(WG_9fVbTRLI_ zu?etCYy#{QQp0%%%73v5SHavSl0$eKY(wl~t6>+)HguanJ#v%OC1DfyB34}FyZN&E zYc8ixM_uEN{-4W0+OJ1Vn=lN~zx4fg!S~BtKV!b1%=?tN-@isI_bb%rWbW2^{Ra*! z|J(QNb*=AcWwZnFuV1+M`!9R4goK1}U{DZG68$fnrvp1!IG1KH&` zNK+h{ju=u%3*`+-0lXp{bp{pBU0(amH;Mh^Z>8_(F6e(KCjF!RU&?qv=KN{%{(AX0 z@a19y2ag`+t-D>74PcLtVfydb-|O0dJ_57@LI1zIsQ)j<+P}eg_eKAc5% z=P;ozk3)4r>T)@_R;MP1gLDF`vsnnJ&Qkaz^{>igKSc)Go;7?BTliF@DZG(-m8WVT z&hRWtQFxRlqn$ygB#HNwCZT^S(ZtT8c;1i|&a0Arc}cAMCGf!K!&w^V!Iy*0o(Hk6 z>cnB>Rh6nevZM?UA8*X}(dKOL=ij0IM%wTWA3LJ<0T&y<9v{Q>-+7?dwXv(6(GGNJ z{bTLlAm~0IFpwuB-kV_~`ah2+>iS6?m7Lg7!HMk}3GJ{2v57X=gV=-ucFG zwP6oPk@u~FO#mO*hEHWWdkbO{I-al%!J|BdJzyK|ie%V>AU2VNvFLI9P-ZBvNc0|A zpYfi&^c!H~{?IVpi#_C_|4;UQkTO7<>vIS;a8R-DU?0Em(u>Llj$j_Z_B}h54FrY< z57U2F-#*u-?hZyf5dY@Ii@*Oxy8itG0(c7aKhp;DJr(nj&S&?eaE@;%<~X6gh+`Fq z?du9Tx~_nuYc-;3^I3?j$x}q2&JaX19Z4wNA~P>&Q`QGb+rwY2(7&dF@tJvLYtGcH%TAVnn({`9OQ~VC-f5yY#=x?Y?%Ig4jpi9+1tfv2fC#GWxu`v z=-$uYpQj?;n`LV?2+!n5PQ<&P0OIlPE9#1rO(=VSO~eR8Z36GU$tEOr)Z@o+*he^g zKv)HA0))aYLSPqS6DoGZ`yYgNUkJqeFLn{2fIWy^sMt}9AAR-s(Z>`&YIdP)LgGiW z7*dNJC4Ph-5SsvQViRC+>?m!JM(lXr9!#;Lv_ArAbG$d!o9_wT#ZJ-(c?Io^^U*&s zdM5NggdFH!_Rp00Kjw3E#or%3c9dUv?NxsH)mPLUPVD=vY#<~me3<_C9`18R8vwV$ z27dd}#ovEf``I75_x1DRX^8n|+buN1iv4pFIct9tXyi;`Ujt|8r1#ddkk(trX?p?E z)V;Nel)W{a(o@69ifT^k)=2EG;>0fObJUdPAvqx1r#xo}Kmf~1`AUB*>X9V&J zjCZ^cVk<*+FUO*P*$+};fMcgls=a$;&X@RmnfLYT>#xBEUQ=uOW8Y^M8wib#7_R># z2VL9t^>7<(;J1hwhW7uNxBtt&otplAc^cyW!TMjwfoSi^IoP7e?rUZts}J^YpoudN zXf}}nyU5rNy8!9?U=tus0ecXe04cq-oC4cOR@873>>{bBniF*rU>8C>Y(t}x<6s-H zuni$bfw3yEkLZpvjuK!SVi%FHiwM|-K{)I}2vfiwU>Bh+um{+M5Zqj#2tpdv1e*YX z0_-C|>;m}1HUvM|h2X2mW*^vv;9Zxc@Pd5^p0E!^O$K{_-Ib}lwIrS&%8%r=*&)0l z1NBTDd(;6(&zd}pY>fYuy>vzYCoo4>ko|iE@%gX4@w&1BS<`>Fw+HX?a_6v^$YJ{L zJ#xsk9eqT?rS)IHet7qNe0_L2;=MWc)`JMlgZ}aU3)%Y`Im@88fipn{-v5lf&^gF} z4@mEUALyyo=!P%oh96K=b4nNXE>~1>QYZWX^qZ*a7Vrgl_v2B&(7>2v)GuODzYt=C z)>1_@-uo!jF*MM(fOl| z->@5Xi)}@5yfH75mjt@<-Lt1$Q2!DGp29pILFRlJ^S<7A^G$yJjW^Vq{%$^=$_B94 z?=b!EJ9@~q<3KM9zq`cyFZ=iU`ueiBj}Pj83wW-Bjbe_2W(NUT37pwcqsVBlW+A=3 ziqqPJN=|LnNNKHLA-SbokKAp)`ht7 z#09f>!h)GRem?d@nTP!q=1$}6iej~&w&-8x=NfZ#jXB$IzV#Nr@z$Ga-sT=(FBTh! zh>O02{$IMN|2OpS$moDAfOG-&pxH#F zW)p1{unWx|QVe@Qn$%)u6U|~1um_#EM%V<16?D50n~2tlLR~YePHaIVLbnOkHwBvr z(^36X$}V6N=(kjTmU_RX^jRAHmI1Jh0I6pJf7pfeP5Hq#d{NKzMLkn&L9+*=zf!jc zu?gv$DiqK+g}SEJXNmqP!zNGHw17%^lZ%8@|CkwQQ{Vo2bx&|(FNAtioPv4V;p z5i1}*P_ZN8$Mxugm<0VB7`BiF{Wml;s5-zSGCvoX&((hW-FNtH*nph(2=HOCfvCjT zYo`BM_6vEQ6ZTz${tY6^bGYnuucGu6)(btgmrHbtPxi1-bh4X^PG}UK=whMZxPW<# zApdv==N&U>=iH<1oO85I<47B4AHkkh!eNa}q?w0XIOCAe%;_eYIPG8)rygwN)IN=r zz6MS{U{KFV2f+S1PSi=*U&{&mYB_#ijYe+`$0@2YZUOAA;uxK1q|rT<9HkT4UCEK% z6&xXSm24bEkA4Mm)qnL%D_98_f(!jPt4$#?*euv2m=O`v2owQK% z1z8nEO8+gW1Ik<_N8)m2cz}*hk7i*o4>yNb1wiYV6JQJ4l_p!y)iBw?Y|bn%;`;h}RR?_h$se$+*YnsDk1HPi!DIa1`|t6)K<4dw z1^KbqKumJ{2>L&47XMv-`9B*mpSkWOA6}Tw#eHpDBphhvLScUk7YO^BIbSDlUlZr` zHgRrmqatT-0}I(gPd#S|-SwQ=U8l%EJRzia)o?oEiL}mYPD3n_s*{44LP&0}R3stR zOhR0dD4;JYK~cu>h$-TwPYUsbMhV9vrievgv?~-!jifkU3w*ys!5^_<-O0;C*%M z9qg}cAT}l8n(2QI%KQb+_JfEl&g9ZV9g32J?Od$T?V(S%2iQcx0oa4EA2uO~J?NNh z0`C|JU>9N&DIKr}VAzACVh_qDU<-y#NFSxL2cw@7eU#Eq zscb>F39*MLh2BRAyU_Y5(I*9)5W-*+g6gA0tQn#}pA_sO*yy8#J(&6^VG{vB`X^N% zC2Ye_@1OKl{gddUEKg%^uo2@@rr6BWfepYGCR@+p4CueMwpO(PJo#hH&jm8)%b54| z!G|C6`yYOw?7}A`fW-#lQWLM4{^vT{u$7C0Vu1_n0K}B%a@EORt~?>^WZbd25(qmm*aujoyL9t-OjYtaxBYs3Gv7?F~4{7nEiXHpV&SVxlO3bLCw>?Vi zXtX^V?M!<6qr{B{($18k$Bxqen1uERX@8X1QEPjY_D6{yrR`DTMzlTZNc*D}H|p(= zL*qsjGorl-X}AGm$1tGUna~ztYG=}8#~`HA_9*R3((br5C6uSy&ewn)Och&z4>0uK zgf<|V>!aps|K!K~L#*K{w(#LcA1R*r@ekQIG?2vx;?t6@o&FK~8TvQU#5%;JPaFrw zxc&#iQAOS3N4f6tBV4Of^VngodHgU}KX%AO)uRVls1&|`kSo65XQKR(16-!q&t(tq z=TcCj*vBP^Gl~_xTy$30%Y|n&3eNPfkbhd}R^*++o+vuGh%<6dcACgOfwh!CmTd_W9NIBZfDMy+)S<%EvhcyxpH*(^kMouu%!10JT;t$nx zoN!Ph_Fx^y_SJDrU#%kgKn)AKFm`bo^e-58ApRhuypU^ZYE&D5%+EFE=>Ax@g^xe} zm_PdXBV`YMVL{3Uu-EG~)Bik__X`&}>NxO1_=2Q{Qf~g?32qXeJi(1m9#@!bLhRxR z*aGZAsQm$K0@T1R#2%DQAgvN$8$zW3o2bw!f3%OwVH;%%tVs^LD18L>An0~c{4i_+ zX!g*{g|G{;i2@V4UFf!v2ip*GPYJLIMJH#&HVnijGEKC@9yHR$E{?UGw+Yxr>aiA1 z5nvz53fKkgBMG)4BpzwvMA%0HY(t0_4mH3YbYK&)I&SEjo@TdT5L0aDaXR#0U0tp8 zujc1I`wXl3+WMSr*~9zePdu=fx! zzduN03(~=O^5!6&K%{{lyh&#xQWF7K3q)swzdIYO_us=K@sPh8Kji1e4=Q#eJj?TcZ~wjg_!93(I#9Vx)fX|wXa%(7aaBeU*IM$erbT(q03uDc*9Cc79nq|gJq4?aw~b_HmYVyaFRbl?`C8*Kef%fo7=K>wAMmFoRh^K&uZ$9%rF+Q$=nxr#psiV9OUfW2Oa>A!bS|IfwW|9Z4H zO8<}Gyk_F0s*n)hf`-cnJf}B6^7p}|}#6MivCm5fN z>>JMeg)6ZI@ei7Bn6=D-OR=tdMMZ@g|7XngFy?E^ydPs9M_I=$I67R}KEX42g47HlztQ6aCNqz*~2AmL2TmRwVaUh0lvdC zCREMEmif3xF!s+Fqak)+*n+W-+cVES%ONol$_8YwUyC9C_8vaC+R*>c)%<^L{t2RAo#skQMjSuij6Zi9qjb>~>US~PdYt(s_j(m-A ztxDJ9nxWucONQLn{BM@vdK3D!repmx^Lxr?YqxIT@T6ES!#q4e;(uAE&(M#N%D8{o z!%6n?lJ>yR*hpmqc>mW7QNKmc;l8K&krZTof zkTFJrj5QQw%&8z_&jp!-AjoqFGABvqB?>Y>Rgihgf~;2{$hsARtY0I@I!1!5XC-Ue z39`1KAZwfovevC2YaR=-_O&2u;0v-Bf*^aM$i6s&?2jbKK52sNmnX=+i9!zc&lF@I zT|xF!mOa&l0*tQ^WSpH)gz=U_F~(gBC76F9$UHpJzpTk?=-fP&wHvg3oId9x7!MW} z7o}_7qfv&*@3wYNWYJ)$!M+<^Zc6__hIbg)q*{|!{ejH2EN8#kGBrh zzZ^sNPl^6J`}V25AH)V^9a!-r=Jy?%7vFOdV-Ncd9=x_-9z!2EXyOp^0OQ()LnaO* z4~}RYMtbu^gwf2baY9)J4T zr~Dt-!%gY{@oC8%kq{#`@bpN!mvh~R4#bK61@k!|(tkAc-q(-MuZQyQC<__)Y3$)8 z>-k3}#wxJCZ~TSy-*@8JM2!FZo9KVvvBPTpZ}9=L7Tl{CNB-~lJFh>}a66Paf1e}f z6aC8`-q_obqmtr;zhNKXi5JqpoJSm3YWRSkz;28 zUw@WgsTl1YjP(+|8+$j&nvHR3NgSJ+C^m4exUy{2rSvbK!w0N@{=YJ8;MX!A6lH*6 z2T~r0UwI#K!CP;?t;QG27~|_)0~uhvzKq$|cmt`1)We4&@|LlE@=l7*jXj$r|1^BJ z?ByMYxDPh)7214OUXJdKYhVNZ&_5YA@B@q|kah&a2Ck=C``9&;gS=nBSa(;}-Zj=> zmUY;T_4ti_8I1iow0&BzR}c1U5_@^A>Urgv zh3{j$tl|lbnFfyt7&H5@iAPmin6!tT#U5zF83FSw3{F3ac@~&!aZ12^3*jW>BDyd* z@$hLrarQJH7i4}OICci}^JI>m&e7ADvx)h7M^0hhCfe!_pTzv#6WF^^z`X85$1$H< zz`Nad6!ZI!9^$=y`?&$@HOU^3vUj((e|sk4ybMmtPJf}WqSSGyKCkBZ(5VyOf-mrd z?%y@^|L>d{^BC29)_py!<~3tpt1-V_);UmXU37P_TJxl4->^^0pjbp;m*RpneLV4sx&p`ujn zxg`58$=+MJ0_tlyLP;TKm&l%s1=yEU_U6pT{;YYNAr$0tI`-<7_g&k!J6qWR?BHEo z?}h!`zI8RazSQ3wIDW(teMKeE{}bS4@JBH4?+EN*K!6=6U|g-v91Oq?24DvY z*uj7RI~c$kNCU8g0jz~J06Q3f9VlQ216Ut%0Cq5dbutHF2LrGH1?*q|b}#@t7=RrN zzzzmr2LrH!fs)#)0qmk_ywf$I{u zE`jS3xGsU~61Xmb>k_ywf$I{uE`k495-|4O`DSS@e)Rb}kS2mv#u@ok{^e*V{;VBY zSbSSMHV*X*{(k=WM!Ddr^T#*H@%cmb2l|(3XQwfn?k;EU9Q5bv8_oY54?XD5al?NN z{(P0$)5s^&2HhTCF!cC?!GC|_rH-E(eEqkt9}g19b$JXDhsAYyT$hKr1g_hIxisM5 zKNx&`#73 z4*m~rMzSJ)orV2m{~|g6=Y!8$wB2A~(IyA@hpa{F*f5Jiq?3O?+Tzt;ZLoOfv(Xl# z-k5fS-*>g^{({eb`pIaE&+fIb=y#cLgIq6vC-;`?NdyW5=&8Ebhg%a=iNe@#^|VaQvD@kj0BPYM*z0JX&s}zUKvew&|@v{j<#$ z#%HH}##r2h>zv;?|C!wXrmvo{_~6-b7LOw}zHhbfv>TjXiLqF>%=6xufTXCP=){ES ze$j!`alJ3@@%MjzorA2!gUBEKb8V)@zrM~1#PJRs@Aa8>gLEXRYakPO;82`@|D)W@ zkYGIHr=u-eT=LZ4;3JDhd}jVnWRKjRuXo(!3vsdy#D;?G~_NZzsZm6Uhz8RPHeZ{-@f-V!Kb$^rh3 z(h>_x{Eczm_S<$AFW_%Q&V_jH#_znI@*qC9uoz?Ecw=2ARBe%Z;~1q+H=CH?;IM!o zn}pc-Aiuzb(4e413yYX|iv)|<_?Xl*i&%@K!~}~pi@(HL1OoYI{0{lPTHN>LNH_I- zLw~0{$9Eo`aD&)}VIyK!N8|zU?~dGFm0IE&vC+Q(u|u(Ci!Xm}{2Uxy-oN>{%ojWb zZm^h+|65q(Hmc$5hFb~o5LIeg2;!S35O&W_);cXYVX+TQji>m@F?I5|1p zYHMqIyOra@JM0%P9);t(?Ck7DTiaUSZRKcVxxmHA(#hF*jH9FDSO*7(vG(@%;{;q| z!Z;_N%XM=7LKmkoR!+8-av!-L&fn#@Xwj$z&enI@*x1~T=(P-0Xd9X=wvh6!O_vu*4}ok z^&uj8#W9MKu$JWtyj;)jJ zTpLFl6LPH1JJ`;V>(ur5yM<0pGi{vhX4pE|PFMGJbewAKjPJ3xorG&9Si3lmx3RYw zE6;=P9}PPgWxvSz4l8@R+wfl9V(Vmgqk|Ld#mQ0L$$v|oh`jL|VK?^Qb8vRP3He)L zPj^^5IE=D$u)o{d(RPfzBYvlY?L^qtWIKDisW#3IGwkf`XW{ojo{sbE9qi}ZI@?*< zI67J*Z)4|dXA2v&w}Fsw>}cm`=Y;>9alH$ChYPL~77fPUMLw77Ua2UVP(RQpnKmO(}dk2R* zaL?Q9oE>k*HQ&QK{B2vO|93uY@rq?PIN!76+fIv}zh~p*cr$+EZMM!1cjC8=hTO)$ z-;9G^CW0woI)EKJ%z;$qL4H>FE!Mcs4!_UA*2&%pvJkl~#(kFJ+&#!I$8{@h?Cn?C zJKC?t@fzG?Ev{P!nck~me;@8~pN)f{os-Y;ck6KdT6-t^HTY~5?zs}jE9AboZYgxM z7(b@RZd`N! zy6@b#{(=8CJ#5|Wdeg@3TP7`8xpao}J&R{ATDoMO%RNi17A;+5a}#(R$}4i#HEebk34XW*N$D{9GAGn%5`!*{ytLv z9%-oDPrI*+zkCnQdFkKlhE$$Io@XoA1lHquAH;Lthv#35-?2*jJ@UI2EyC}1!m&Mm zyUpTdi>+KB2l#^7F3T59eQ?u;TQ~36rnLoq!vfsH&+A(r{@(MkZ(im$w;j)I*}46r z%{#Y!4*p=qACWis0{QdoQ29UMdH%F%$5w+sBX7=s;@F(O;CLwig>zTS(0j?T`S(fQ z{CSK#o_p(`@Ouo-|E}}D*ZAF%icCZ{A7P*2pTSnMc6zwY$9_{+H4fc7$Zw+So}C@I z-PwbBfW6m4cB08qU~-w1cDCL{+CG2^K#Do_}b;MHr6&gFP>TLy(`cD z-D{SWETv7>~2FbR8L6866&?1f6$2bK2q9U$zD zOxPEhutzpwe{913*wneVhdO$?slB_4+PXTawX1`$RthzDv{6%gD>b#XQe$fiHMBHS zeRC7lH8oOgQv=mB)Khgs9aYuWQe|BYRn%5fc}*1+mX#3JS0e1ODE3JrLy_e!&pj)L z%6X(?pJ4x$(AgJaCyD7PvZgZ`dw4?Tj5Xcpx#yqfpZ@G;q7R0QWi1dg*89}utk#15 zA>?eV2d>Ll?E~=W6OW6WO?yP>a>kx9nAfDq+1N*lwEe^|UrCd-+GFs@iDQs8<|Se6 zltYm9L9Dl;?>8#)X4!YPSChBwLoM=VeLr%Mx7wFpmpMb`+}f#R&X74bw?ppO%LX!M zUFHnAv&bDVWY4vYO8yMFb44v=UsKJHJ(pEiQdza6Ra{zCNjZgBrxNR3>+i)Ecn?;M zBy;0jKv>XZ_?&me9~6`p6V_26?477=Se`Mjq=?dTvM4nxLy>~?ipa$EigcvulzcU0 z;O{ch6iG<0iu6=U0*MzPAvKwVR6Jv5ZVu%Z7En=HDV0I4)pfPdMFZs(7g9o6${^%< z(sQ%bdmuU}t|(LQg1iUN!@JPIWJBIV)7{?gow7%5LRzxOUF};b_J!}@_|#-YTuKs2 z-blY`{+s^&rJm!GpQk5tbY3a}jzeCq2OJ)Qb;{ynDIq19p##p$%b~oYLM|>Z>e^Ve%F&_yy?Xn+WaEcSVtb`C^k8fgCnqpTy&(; zh5mb#9f~c=dsA6k!!-?cEbr09es5O3=sCWb%9mD_i{Dp1 z>|!#4ZcM%?C?Zsq$F6&JC7H|LGr;#-?tWfBa`W+E#5uC2g~SYl{LN4uU(BwCUMt5J zy8dFHUFdJF}+c=zMjk4X1FhWy1B3G%nV@WWCbb3qCA z*f#UeL+@|ML3D)pl=E@D9t*t+A7oks&^IJ-H2nUji2LccFZ_<)eeXTQ{6*^BlKUs5 zsxmTI5wssO7czkP$Dc`$(!IzXADl9mJFPE``LF*D`;V_ltwgheU=;8KBBy$w$Uw zoz^6*#|oQF&(7rZ>@4{`_$}#5HW#vgIbT&*i#VuU_CJ?6^xPgFPZ>U<{sl#bS;7bP z`-KLw)U~i@mXsNsithjt<15*qJm!MpBC4#alr?ur_Fbu|t(Et&LENOZU*;?I8nupoNz zm6zy)4?j@#h77!8Qg)=H&LwGCc{z#PvEKz@eO!&gLc$ukg!OT$2)ZdME~es=5-KSz zC9IE2rDbL6@32NTl~*8UsHh;UmoGY0_o>4DbX`&n-cjiDe;l=Sb=1|>Mcv(K$AB%R z!DbCVW~9PpTk$YaE>gt`Y8 zq3n=-2ErpE=`x%;%wo-PXh{LPho-<4c@ z#m^%4DDR%}{)wN4{8{90?tkiAnpAtKVgDX}-X{MOofu~s92MRl78}K{VvX02KKWR+ zFBKzRlJ+~qt&H+itwC?dUD6xxxRvjkF@Zd=1RWfLf1|*d*-Xl z-!*e0Lk1@Ke-ARbko=`R8+`>P|AYFLN&eW6*D@qJqCY$?nqGVTb;5pas=qw;rqn?E$2+(G4rOMkV+Tec)>J+Y}k6I6MGi;QFtxZY}XhuA5j}afOSU7Q$!s;7jSJz zRRlZx+{06r%%!nb(|N4b3;}tSp3g7D@j{%}QXYpqkkphPyI?xsH0n0G@%CF-5MNY; zcA=5{5Bk!$2JO`%f2n_&+dta(EMfcoQHgPC9Yd^PDSdG2eUbh((F0<8wKs-*$BnmX z-*wZSw?X!hnbmatx5)3gjJ(KmQG{3cH`DE}Ku|tY@;0o}b0zY-SOV^pbgb_CfN${TB4mj4XfYTay@c z=Lqu0exQ~S@iG1ILu%bw>>EJs=$Di0p)=|4;X>>`B70LP`RlqC`CHBvyPpR8h3vtw zJZwblWDM+DQY{Z1>G`3@V-`%O@wPKz|IU#A8TyZ}{)0*WlYd(7%JOD^_t$^@Rmp!v z=zTnS$vl(J$B6ywc0R_;Hq7&47sfS49?!}*-*vn41L6zt4qQ!CM zkl6owe|?WW8TjOy@ZsM+6!{bEpQdQ`FLrN+vVF1t%R+wl&7Yhu*~Z%YHrjcc~P*=kYeP zd4m010+J5TJK&rK>8Tu>=Z7BCWS9AD=dpw{_vKOjW6i8HV!q+AW@>m0edUUg@`KM+ zo~dKU-OJ&B7Scqg`K&R|EI)s6-Wk^jPB=d#FTdxOyYE1JdmG|b;Z_wZj^zK_x;xRI z-^3z+@&D%X$9{yCG06%2@Iz`nagjg94RDK$2f#Rt##ZbNk2mGBlU%HzXDb#4lr9$-lj)3%1;R zUj6~+{SVd#x5OIp{qRHd_B(GA_KQLP*8$Ubm}ZQD$;;0t>|aGU;yVoF-5X~;3;ufc zg|WkD!Dy9oNbC-2|LXz!Z5Z`U084Yyj}Nm2$IK>j289~ti=F{sJ^n8`ma zFUvAMJ-HwIvP$0<_Uq}Fu}h|LGOZo$s_e}~+p6|Gx2yD)yYFN?gYG}(Ar<6zULx-} zk0vqxut zi1shzjwA*({Et_Fuc`da$j`CFTIu~MITiwO%#F_x>(A1?itdR3dabCkLZ*%?{?OOBW!TDvlmj>yn9Gmjfme~?u zo#FX=@cBLXT;mdXJ6vanYgDS`mm{x1dMd{%KLyWz+t|D47WiR7VxbGkA97UVyGD|K zW?`-+)++DE+Qsw^`W~^j1RXhk3}pbu>tU>3C*GYL#6Ib0)4K`ZVQ}l1QSjA}F@B>t z(^uM)gZB!u+P0WxuC|xoJQ(fylHK^Hm)-)KWWiIzHQuS5|z zkBWcL*KYDZImHE*@ICz*SkL>t_urSjn`C?|_n{1sF|2q8RP2+A{Ut@uw~UtWx{(B7b)DUqK6ZF6NnQ?L`*mau|AS{@Eq-Qo0WP#_WE`!}lqc4$q7I+&1oR$bS^%4+hCUdr`N(q@3LnhVH`4JOgBq8Z~$Y9GqHDSGWJ>(*^90vh5UKODn}Oi%v$Hdg5+o4 zzldEz*YPRH|MRat=Yg*W`1gPRJ-zW?Z}5}v{D{;0@@Z}I7IqF^hjxbLJm&%Vy^C1q z%JPuS?E4n8AaWX-AL-nMuAO~9?s5MTJpU3F%=0)loj3CMt$h0gOBIJmECRVu3HsQ( zy1O{LAdl2|5RCJX@jo&y8*K_KV+^JKWiJ1M@)Aqwz@H3r_0zfaJ4 z`^P>VmRQffAN%y7@9{(W1LQArAWoh=jkyk|=rHCqU@u|9p4&8P{1_TNX&jB7IF`mt zn@IK>R+Gb~HDtSfC0RXi56#27JLkSd=-=1$HGT1qFI3yki+_5N z8lPw**L*K>30+GzyYHdd54g~D$l-FBvD%5Q8EbH_D?s#p$HXzP|GP|-R98?J#z9D* zt&ACw@%l0z0%Mug+z*kzjDPS6@;CV(?8RW2U7X*aUtY`~V!z`*{PB-67lkqBgk{dj zQTUF&wndGw_5G;Zb;Ug5EYSNpiqDxdAV%6l8H^xj4*eYf(4j6Hnp zH>c>e|9qX_|J(b@|NP~je_?qCL>clFJRCa+8Tv6 z{G5lD@g0-K(rsf#vw(OI<3=hN<50K&edRLtITK`H?D{e6kGtkj(!PKO}o<%U;}f!S3&#GJ)@&Jb}i|n#OB0L> z-Lx)n7q1K0#cKn0(%QhCyd~R8PmM`s);)lNc=a)(c zqK_~C^(B8i@G-ygpRdxXmmlV+t~6erxRDlxKZv%SwLE|GN}9KMCF_{-^S7)bz&a!4 zM>;3hk9>aXYMQ@wHS1h7FSc>Vi;FV%(vUzi)7W zN&clZm6q6Rq`#=LjHK@u`$>_^&k`Smy@m;U6%+QfChUo>e2?YSiDWrtB8{ItomTDH zOl!Ti(;BaBwAynUt@7APD?PR-*7$Cv`@&u6q4-_2G0ly(W_wapOETIqTj{aao}d^0 z@FGcB@X_BsqCb4~2a@*rzy0fPBxN=FUFgT}KSingb7?tZriHtf(+rgDLz#)#=b|9? znfXnCFP(V-B>uTmlfSBmNnH%GRO7HD4wA7Wx$r}|MFn&UenjF)k-zvK%u6!KAN#~u zV(*uJ*&_&jkJxwj3z;j&k3IPVnXe`D-qil`vN!$c$>ZVoC-Io6lW5}H8NAkeJKyKO zlkWA~$@lv0;B~$`Xf57}HSkAkytmV8_@vcdTj8U&(Ym1Re1G&#-jISe*ennBC=21_ zy*b?V)IR#j`_J%;U%aIHw&XoH_tiOhAL!M;yvpts!EEiel4hy+L}C=gOM2djMI>Ii zKz>-Sc>vTrI*Y@uYBi}adbQ45JBoJA8m~G<&9B3ydlbm)<=4?@KA&oJs9E14}=RIbbpvT-zS8+ z)BTYiygtc`w-xyDcK9jRk^l~AO+p!POv(K9FJEV%^1h8x>=gJQVx+aKW6CddT}Oa* zhUa(S^BwqH;}ZFMaoxSRMx|PQC-NGoKMu~%-?p0XoH}0W04&@#{=EEUZici46@fzd zBFyJREO`R*_fY=FT>g~}b(Yu%sUPnFN#7&(I3&yipdVuXqVhxVK`AL1qa7PV;(Nx; zn4*}xU^Z=v@};fufr>400km0&^`}iS(22sAHbg@wqL*lI+L(yzGyQ2tVG!*u$GGm+ zBq}{yPp42my!^#0^zL8ZRefE5{Kp>&Wi#q-Z;)S8G&u)9B)@+c*1PVbYsPNeYxn(^ z4Qzo%O`D+VU{Vhg%4(~W|G{`?j2)?v`hc`asCl($Qy|Q7QU1q2G{|KCRZaDl@IC!_ z5BSsHeq9*SS`Yi*9PiEB)BR{yK>&MIgmYYHCbj)&FaPA-XZY1Wy{6)ybALa_ z|Mk!R;(vbqPx|WXulSE&{gI{b@#mlYl6RK{vYppDnu8ce$CQ_t2T1Ir;-6u8#6K#A z(&L|t=e3vzF_6STDi*p}Ug95-{~Z%7S&+H_=7wQ@Qk7|5D*TAri&^9^^)GYz z*EBa+Vt=syO6(JXIX z6_1MC;rFFpz|Vj33obiTPo5QFygAE@m-}xftIey1%lXps9U}ir$QH5XT*R9?!(vYJ zSai<%dwA4z$baG(7R==@^HXKKRVBtQ$lME=2Yu%I5Az;f{+R1)vj5uFM$6K=s(!o& z==#u2P z;NPF?mpyQ?&cJV2-T~}kEo%m-x>rPGB>c`)nmA_$Pn2Nde(JrSid-4;<$uw?)c6sjEkf#E;%`13_>^D&^BZ*bmB%^rNE!P! z#L=FzNZy_q%A1peX;V@VZ%hp24T%A?At8X*$NTgBu|Bjccqc;-Xy=A}yj41KUXvf% zV{y%8l*xjaz7LWQ@C&IrH?Br=RBU zV_(xfzMjhe$ebf%{tNaww8Y+p{k5%4^n3KZi2Q$xH6Dz09iDpnX+qtLWbD8Aok??M zC}!9$pj``$?L`Mt4oKZMEIgbi%$`P*=gs2D^Jdaadn?|P z9L`>uG31#M!=4$@?2#Tt9+}Y`(vZl{y!#ye=^uX*lb3b}5`Par#J@fF0(Cvz#}RF* z>{}Jhd-B3*dr~kXj$(IW#PcC(M~nQ|8a&Df4F0YzJ%hElA>^ z@(c#;ivmZRK+8JaF+XsLBfPVhTues$X`#64o9)+}~v0qg( zdln_KM}9oH=f^`2aRRx)f9)1>VrW-R4DZZ|=A9^0c4SAfYZiP{RwQlDjNolTMmTNF z2b#zCy{4S5_u>R$-OXv_6P;>H@ z#nR5a7}}Yuu_HH{Tyumd)Gfm4-b6oI9KMTO!ggFCJIN*d@^K8@it*D^N#t%&R4M zdI5DY_%)5;dH5&T|5O?^bvz4F7nbr@`hR8INEgPBboTaeC&qIA@;ATcr_iP#@|XA@ z>jRnO-@3Qk68jAI<2|^b{9{n}3JMP9$@6E?bgQ{M&1x>qbGGBaykrh5&7jbd3=Szt z=iuTr3MoxP9V>@pJ98+aK9hq=QrW*CnV=i?1HSo*|cw2Fb`gQkKuo! zqoXM>C`kF8=?mwPFyF<2B5U#}uDKMHP;65%#puL$l~8nZ5k)l?Qe;DcBBDN@!iBm# z3In0FxfCMQ(if7jj~I1tqkVvk>1}MsclJ$EBr|V_Qln zrn!V;nu|FadWmW(;;6hn3YK9577myo(#4zA7Rpjzmp zCWixSvN-^{38>Cu|EesM+nMB7mBGH13i7Q?XXu5!D?~48>{Xu1p3sd)Sqj=%p_9^N zb}vohJtH|Qe@+=B1&*YG@Ob$le6kM84L8a-8vYZ3qzXFQkuZmLGuLxyEQ8N1$CbLgr z68RJ)vN!O`PawdadGP}3dvWXz|K*+&OM6hhxMj!iZq&zjXGQa_tSH)<6~#LycUC<7cC(s4Crbm#j3)+$rLJ!3I(Tear zk*NY*OAm<2V4@Ceofl>T#B$4z`W)_9OP@aH1qV@&ez zK6t>gwYR$;?}3bc8}5Ied4|JL_ww`eV`=l5ZMTqT*;Am%w)>}t_Q}@<#%HCQ^*;~WOJvE%vQ$tDJ)to4FRdGUBCB=7Da(rh6 z$8}UtY)1vhc9e5Wdl^Nym$4AlUW&fV62wv^9MM`#fWupg1PW^|;?U+o4uxL}X)54g zp)sF=8uK}@F^>QTG~}{>LoWH(=dd5}t;;6BKDAi_dDmvLSB-)^t25XGxK*aowvu>$ zC_j?dWrxtp^Z;Iw=Euv^pd+M96Ff0TY^KzKr5%FrLR}d0m-Q|9F#5Wrz5giIQ$qha z$-Gjm7s>u%!7>K%Ty$crN&Y>D`Ycff^miTHe*yVFt?ItMzP>yIen;dF$mX6!oYqyt zIR{%Pr>~i_LDm82;y@F0(a0J58wjwj4^0=Mk6zJ5E&9F;ePA6ar4P|X6?9R>qK~-F zN{WR}L?1C7*S3pr9vMwKU8ihOx zhc64O$sJzbtIVQc5LA%~GAI!Ban!-dzf4FYKj4eHo-g7gpORG6_fY36mik_@s_&^f zU!m0ZK!IN0L!I1BukYdS0ra4;CxOtd=g*bE!>H?5I$yzU1 zM@+3(div}cHJ0#Atm!5(5Z0QJKA3Z{DG6rsKd9^<@4<-n5BYOwXefL8c=IefE1K(I z!*d*L$Zq*!PHm~+?CyHX?ylpkt~$=_swKb~oi(iV(NWE5@KLGIOKN*1r?gdaavSu~ zR>4WF<%&e;B?0kLe6xb$no3xRgfs!Wx_D4!76&1I4XnuIfQk$bfL{F3HsXhN5?`g0G)5hqeMBE6DeMJ2MJL6{ zj5;{GBYuTW7fc&w8Pj&-8`+rjz5(RRu^+RnL0+62l$ntiwxv~bp;7Ro%- z%$Wz9Ipbh6rS~;qjA9d~^)(XU)B_Eia-e}y4%Bn<{(4T@S4V&o_tkPDd|5(oEhqHW zaQxnCirZVwaXnQW+fzlc-IW~ET}jd16&&4Ffi|gf0vy>{&Jmqu6wy(};T@$M)=^4f z?Ij!vLfVQ6aBy1@2elRv;K0^G4rnc)fR+OGZ^>uB=6v#N&O;fI&NCO;(j=?dEKIbT zP5D)2vhESfm=TN}Vd?jkcK>(YeHUvhz06o^hENAPmyn)pl7FvB{ulN?uz&LQ_NLkJ zJCMJ^VbwA!INVMZXZKV2nSE5I=%v!ry;O2~FO{6yOU0*ps2KVwI@wKy!ig>_0Qo06 zDgSsUM8L+og!g>EyWA_YSH#oL$SRYF}>9kv$u+(K@{{9)l*54AfmfM5#C)+VM14# zBDAxVLKGzw+)+Y79mN#XUQ9mqIW%jr9Zgy|M+fq+ETx8q20DZBCNg&9u^-4~&{I!YJ@uRgpOpzR;IqmNN5E&H z?#+6=o5O1J&>p4g-HduS2W$0i4no~duXi)*-t3RE#1C~mKh*7fQJ$dQ%|2zR>@D@~ z(iBGdz_Sn&w!j(-@^sHOWV${$|SE54}J)Ir|zp2fE4bZQv~ECUb8+WkNR@J$0Pk4ZU>Na$0u{r*_p) zY8Q0UDL^kMQ<6HMmySw8{hJd&d|NpIR`qY_C>HvOK^;6Ab?|7^!K0d`4qlAK-(k>EsH%VGa|qf;PzUE=5Ck0sq8=Vto5KM$+2oIUxIgOPe$|=eTb0QkC@1DD zaiA$S^H_imD7T`7Wi7Tx;Bzo`g!T2W32R!ZwH>g=gRF&n4r_Fq$=~q*m-avNoNUPo zvKO4!tf2Cfy;T4BQL20Fh@$qE=ZN)wJWQ>sqN;U-EJ4mDDeqJa_*Hc)~99~KYd`syeSek``HmSPXoQp^FY z&AMNxrYI1(uZn_ER?J=INK5Vtvr0AqL!q~TS zX_#|*HU4M5vmGs5NNs*K>so(k5=iE<7?{bJrSCz@urfQ2cx^C_pISk@zC@%vbJpCxwu z$Xnfwj=zeM~A|G&V+UdbOiAnV0W?7U_$>Z@YhhPcS$h}DHaD_*e*fn=Gjnrub19+o3a_7$c=FFM7XU_RP z0fYJ|NCHr2L5itkU~fx`d4CG`bpHXpkZLJAQd2tbhiSk5Jr%^%mG?#7w|@@>F%_SF zJrsnryO~<~?$WKFmg?F!SWM}B7fg5Q6Rf)Q4MM7=n07X#blN*m#~g8&j=eB8z|mE; zHsjwC@prB$5pp5XXNP^*$mW+PpLvS?+T_i1=LF-d72dsnIuBz`t@6L?GS5GF|Eu)# zPuLvAa__^~o8o!!RM+kfrG<65z9pW!8bTe$`$*>+vh%${ZK2*{-G_HRwbTLgI^e#< z_i^rOk7+|bNDZ}f_$Y5`>*%Jm@O<6YR2!s*x+1Nq)|hUM-$I7E@Eo8HLit}_qIr(& zv%|h4&p!X0Zg(bA0AqVqCK#{LN%5b~UsO~b|3jB9|M0dDzW-wTLwD5qz9{!7`&KIY zTp062p9|!>p>5PWFHD8!!%TJEg=x-XUSCsPbm|a&E`t4~GsW{_=6Oy~_X*$mbi%Wj zPLbE=L#JjQ@8tMiPM;5*;+!xI^c8(Bj1+mE4?RzKJ}@oqDC7Jv72}-fRP;G!OiDtH`0qTlzcJc>KG=cw zAM5=iycb;kAJ`89j7}Z@km*xZV1LvTeR?QIzh2_#*HavQdn$;j_#^L+-^HeMp1+gr zSRX>#okCux;)fJcerU@xrF@Y$<;tV2Pq+86&CjQIkn-sjB&N^~8ziP`8~3|6xk; zezJPa8kNDmDEK|hHX(}rB7R7oZF!Jx(?*{z;kyOC-{CtpNa0;4`ue5{-Z?AQdmMMb zbI(37nOT|ai=?u%vn4wxTi7p>eS<0X(Plq+isMBJFlGnib~tuTD2yQ_Lqz5~o#J#l z6~~l=X+$ZqrHYG-DRO-Uyd#QSU$DQHkiSL-o>1iavYb=o`hw}J>l}=GRJ`AM#tlq2 zj_K9!zY_8LD>XOsQ9S?D@TI~z$YlHc+wd#tQH+x*Eh{Ot43WXIlJl9ZLEa1Y3`o_(xz-_gp}sDm!xJg9f^TY=XA z$3i0JHLfLY_nNLe25+Fw;dpA+J>bizl$?wU7-!nrio1J?_W!lK22MP9hGQ^z9SB*t z?2Bt%1J-%9QtO)VTz;kVuXMeu{l2UH+>hNadZY1q|MTg~eC*+H-zSMOQx4~_({hId@$UXhB`5*gvYkw}boy#r1*ypZl zKA#)W&LCvyu@3*_lTYN}(Mpw30N;}QoIfvKx%3|Qn0w|?5A{U+zvMYMjds%yupQP# zmN+))|DL{59257Y)|cu_j1Q$aPWKCpmF9Tq^Ix3T;@D9w%md3rZP~S5fvu##xB9Yp zc}Y*tczc`Pf9$Xw*TFf~MR+H#e(~hv@~1z)E+61G^k?TkSD)j&eva=m(jS|@*yqn{ z4g$W1-&H%0Ym(O`q|CHN{xfsF-d}TF<}n|~0HIFCG>@lt9?vR#UeW8g=g~KT{S#Q9 ze2RX`?5j*x70Wort)R?*$lti~+ZTT)2k`zKOcTkOUnpQJom;$enZLJKUu@o?6UUn4 zef(Nn2Ve0#P`EeCp2G*#o+F2l9x~D&o4?rS5A!P1-cc+3P88gu1iup% z`wF1nj4mHr(Z>&cX9WG1)MoTyVn4D?D0iE{yknm}_N&~0zOCrrsw%c^mWr*LWZl+H z3gaGRE$U_TyH^OP-^vtb+UT>TH$ygj>#%rhcQI!%5>l673kl|F?Q(ZCK$6N z$P^_o~o$8`@Nbew0<{%UXq)R+VjDU$th_ z`YQD8u0l+!R&7{YRl2^sYGnnquB>Xsx;0hkuUxgP94cE?g?LsiS+lYVV-c$m&#J|% zmRBt*T~>v$xm6e+T?I}IR8m#6Y;hHM0aebWi>l@?DXyAdvakyLjHXu%su zf3axAvUJ3~{pI3&ZTYnbT#LZ92waQ6wFq2`z_kecUyA_8hF;0v6E<5Nb3J6Oi7IAo zw%fU+NM~&}WWO`BwpJovTlsn-?^yZkMOj&!ZGmfbn{9x(MJ{dS`Y68Po$B@Lx#yp# zo`1c2zNmVBoojg=jB9Z)%EbSPI6Pq-EB~)?UA$90zn**E{aSOs2Hmesss1c4_@Csj zdXxX%xs#vmy1ot%p{}8ukrZWiwE4G)D&g#}4?Jz#j1@L>0)JdLziEPPG13+vH@5xp zh0(TiA2qf$e7#kjh#QRc{)T1Ge9+kT(NLT1li=ocxIRC}ZF9Y4?=`lihqlsR`}%n! z(`KW<)U9Hs-lx7mhbT}}^t`!2oy{h0ofEynh8J!AQ{_l?)V{GOMvZ0sA+ z=CW}%YuU-LJ8d^%y`Xc|%eeiUzJ1E}&eP3o4-pc<-K_1(R2L#*I0+sEfDilE6?WHzW#mz^8w4w=AFYb!RPr|uEX`&fkX$B6ZmUr zB(^5_j5Xiydb{luMFFLHF>(;zs{JR z0PTY6*jnR%o9&4>om~6xS_G~|z%2s44*xp70seJ-f&=OV1O?Rz2n@U~ATYpC08&a% zV?LI1JwL3^&$w;oe_d7|W1!^Pk{Xf|Dd3He*XS9b#wUK;^@(%K4Nm4pPyfY zZuV}sqyJh%NB7`HK|w){5U<7#hr`kxR-W^@+#lkg_c!Pk=&0`q z47eHlc_Y&6xxfFDG4%^{)bS0n*Tp%m=Z9F~JU8%lI2z&nH}MU$H$!^2U!ecJc7J;- z-#|xOOt<$9^zUSMIJ)=++PnD%1o-*~_}h_p_@H3yK_CR&(HY<`Zw_x39m)UwXH?9HhR}=0hW2*NX z`|r#1j^p`H$FuK{0d;!x?^`#ZM{qrSUwuD^<94KtQ4cls4X`&ydM}O-!Aq|C7dY?Boe`hL0OnH>h8)oBV?uw_=}e$G+ZyeQAd8z6bl+3VB>p z4(y|USM0AZQU|2tg|si`F;;_l+*p4E@}rTC!}LU?A=u^=p0#%ctx00{o}&b695rx1+Zg;6FldAK!y%eqUdxC&c}5U>Wy|`^Wuki_f(}dA$eu zX7)h;I}pp;{euGP_rP%uA3O5;A6p*5N^FC%@(B~Cg{&VpdBU+#6ULr~&WxNe=6>jb zk>kfa1U)=*{OCtUj350d^h@YhBgTz>40;@Ta>Te%Pa*bCBYkH0*ip|y&q2S2euI7d zEmCOY3()VN7wmgKctTb$Hn6tHDS#0u^|&Sf>V+NRzsT~8b^4$w4OA5@_ulBzJbV_ z5o}Aq-T2RF8n%n=^E-Z^$YS}SpSkS%JYG{g_HXbV2f*ELeI;>(FW_)|<_pFOW8?(P zY#wPuauUp(aczSpQC@VfRn7_jhK81Zo-I5#Soqb{SfPaV0^9i?%;S^#tj^0Il*H( zNfy8fdU_bgbsYXG@HT)q0{Ted*br-zS7IF>j&Z;Cfr5XFd7OK3oYlulUoRdn7+0=i z25+qTxZ!)MeH?l0;Ol|KM^8;F2IPclUsNzE%{Z(T9Sf7)T3Wsa{8sufuM#l5gM~A@7ucfOCzu}P7Vn$PZ%%`M43!G{Xw!F_W%3^=hyA?85OHpVbc4+ zN*0ObqyBSCGtfup0;XBlHkp9N{4# zlV-qIVd|uFh+!ko7{X7x0P$1s%v1Ex2LDFj|HD3aHN^epe*bLbv47m(xbpcj?UChL z%yV8sT8dyifnZ#KU@U;-!dE9BevI%K(Pa=G?KO_mI&L2CyoC#NIpk--{#1x#p?mzm zWV#XM?kw3Hube%r`)hlP9k==45Wnz8aIJpyuuOPky-a@yW3(C5H33*5K&-w?knjQ8L9>=}u87{?2a4);GUB}o`RaGxY4D?`$9vRv2l z0(uJTw$gIdP1%C;}KCcTFFA|IuqVJZj1G7+m88=Hm zo4Swnuz3w$E=|rz)A5Upi_vL3;)yX-lAWI?h0gi;mO;IZ>w&Sx6N4?b`qR{T)! zh2R?2%p7nM=$oQ_vrO+SUDwWs|7yun)B&g`mMvedmaSOMv4&bJN>{2CrKM^mJU>cT zuhLot|A|!?E43Qz_cdi@YE5~WDg%ey&|0KFnFQOq0+kE)PdGl3!ahsss#Svh6pWXb z@^!{GR%7fouV-)dKY8}x78vmhojKWxUu;T32)T0PJiYVoJKFbz_oc!G^9BA_vIN`X z_(_hFGRIRXb3BzEv&MKX$2h7Lh$q%jrHJLKRjaf(o( zU2eJkHo5WEo8^Z3H>sbIxIaIcZict|^w~3Y4419|N7Jo-D63ef%M-6(_xL5GCx=8P z#DF(;5@WXBQ+!5;=aGJPEWvm^jM0-@;aST4{NElw9^*|7>dOW&*w%rA;agOKYj>F*^Jtyp{|oWsv9gY24Bg|$-->fluX)BV zIV(McT(y(;pVpiaUgsq!yYL;-@k3qbx;_{``s6fh+3e>cu%p3G|B=Em$&I|eOnHkR z+a35@#E9Q4SN)fooe_dMP=S@EI98MG68zoF-+fC_{ubl6p?mqey#+k@I<)xFYUbM- zex04PS|h!*I%Bzq`sr!Ge65y!I+90!d3D4(Zfb%&8m!F1o1ZW7%aSSPqs}uJ}w21W&RJn4X?$OU|cOGoQ9tr?wimy<2X)?G~4o zAa*Gx4TE`A%2#; z#gA=>^mFu#AD9jy99NC@OmJpC(!LAuKcer8eqQF-dh6V9`-~sC#a@M&-g!lW4}*?h zoc37w2EL6~DS;o1j$b##uPMq_&GJ`W_H-HKaxc>H!+p1&DYhwimOr+`vOUW)e%W*L zLsGIcv`5-UAAh9ilWernZqV@~qu|Ed>T7ZPceHPz?(%EzU3Sc`r4E=^TWtpTs2RoK z>Y>-b>AQLfJ}dj4Jt$o#21#>AM=$Dxd6!m`PA#S0?TDYL6{!C`w9DLdcbZU!@{v2fTgZH4G0N9Y1m&tn*T@!L5StQg;V*@~V3RJE@xLjA`#`JIGJj zXZ2Q&*?p8F^kV9ZWtWs-x%f@*EiHn(%00ndJj8j`YSF!`coXUvxe3qCmb}G}?*rIQ z;~Bp^=iCr-p&t3=qwpX5nCyI4`#FE_f|Y^ua;p}%PtO%^)G9b;Np?S97d$ zQ8}>8YkBi5gKlLn0C_KJ(z%t~)bKW!(3V%L{PCU2!lh>X!mG<)LDBpWj4x)pJvl?# z>)d(=vkdVgbL6ImI5%3iX=eW2C{x~aPY}vl4Ryivk5Di^duf^Ft-6f4m$!RR-rVJ_ z>~S9J%$pnC=F*DQt7^q>JG@AW(YB(?pIiJ2i=81^bMxtur~P-pPM4jyhIt*MZ9wME zn=3bA+Z4a!F5h;lWpBS4>Np}uwH*Zi^*(-HWXHVAs4td#mG+jWzkF@=!)FYic*`&T zA}Rde`x$Bte|T>~8JAlcHE_vW{P1o=|1R^4-@HXdA*ch@FQ0gfoF#JS@vh8xU&i>+ zyV`Yb8aLqcle)J@w;J-9+(QLM_E$brdaAaA!4&Q317RJVGJloo<*V+!v;@m9uHJrf z?-i(-UkAAbzws@xt=aSb!S{xIN9P&8`6b06c?;&MUp@J_`V5>TzH{aK)qU`;VEn)m z!gg_POfh~f5I=WyL;O~qSqt9d8Z|U?tn!`SOSK#3P%RL*7CkN_u5%GxGVZ-_PI~zu zc9+s6V(S+7W`6Bmv11JRj+60YyQ%dXc-OwY`n{%S{1z-(6oRq->M?MH$P7R`jDBy< zch!s^c}&)Mx#f=Ah4o%rcvrj2KeWHv`ovD{e|z@pv&ipKQ%b^AaO@EH-}ON(>9ZYt zaSg>XFKP$x?TZTA#CGM`rqJh_~KC&QI@27CeSCiFTAZ0M~~QFa{5v+H5!Rh!#RL8G)#gY(=xmW0Wq0WPQ0p7RK*8u$|mZKde z2W+qgP4AEf@aS&Pw6U}d25)c>%FDn&{HDh}qo2R>ozX{ad3;9=zesvT!m|**sOQv~ zKR&2ZH|MI}iSXf?+8bpG+|z#iEW~YGMBM(x)M@~h4Z!_C3oOq8S1(*sQol(fmyGyr z((R>u_r^X5>_5V`QP22+8xaDAfkOWk%~E*~{Zh~tsQVwV{E@TPuz6Eyd`~ki$B6!_ z|Lh5>->h+}&x|pu=d@93VDfmi``H8fx7hh_&#QmJQSCzbQi z5;Ztyf^tN_>v96ir1o6*nsyk`OI=2SdmO5kJMKvDF#`hOP1CU;UCSV;e zLiv~axV9{@4Vm%d?_DR_T-fe{_Z#S|P-et0vO0cXn1mE9D^ciYrP)1XW?Oxu7(X)q z8Y6D-UY92K-Yr3+2dlwzCaFQ86V<@k6V-rO6IB0Mp+u+kFN1f4LR$v&8sq#=9gwFY|q55q`I^Zx}p{b^Opz%~k%ug9!nj zL_PbPUu#AZ_>Y?V&VFW$ANh%Q+}oVq=~_J}+^0sw&QQZ+rmOp+r>S94)6`Iu#UYVX z)R2hDYH&n|*3h^qYDC&}H9CK$nphO3k~ZdoExrL>#Sf|%KmR>_y0wq)8~=C%UJr#R zg98;jv1_O!rY|GGatQwE-c;?aZh3!uOhZUp((Qile)F!r z&a?J8RY3Ge)otp~8uFRS`}RdN4fCfBgHPn8_`WIdU(EgwGQLu%K5;|HLQyQHqH-m~gPFLfX5!`1@>>HqE$ zKQg#bsE!8bsXLI$}|S+jlp$$N%h0> zYIPdhPrRvB{{VrHyGyIUydp=KeGXU`^1jP{k?hN<%U^VL{NURj0^dz~oT|V6^{?b} z%G1AoR`Y84?w4Fymc6^%w}yXpYq>9SnxaoTJVw=w`KfC9ycGC3WUAG;E}wnxdHtNi zws=0XFjm$d@4=sb`zd_GUsH#EcLE+NPBnc6{EwZnYE)LF8i{joAIfg8m=Jif8@~5U zFDk^_KWsF-)+z1Je)%*U>v<9CU1@MPeArE`T30UH&|b=S=DcU{og}YGdLlD^bD|?$ zohB-z>yW#}GcPk*l&$ zmL5`HeEWqiV^++V@>%ess*ltkK6_d1cxpfDzyf&O!*_39oEn=It;S@++dd;wje6aeTWsWi9Gc)TryxURFzyDjc?%}N}t|A*=&OJROp?5uhn>WCFSxc)b$`yIYc;}|@-jeSm_3sO`0n-0j zq0gzS{DFlRf;v!s2hVvkelFfC>%WA=1YPdnc@E$Cb}}}8j<`HJ;msKizvuXhLb?C! zqe8!VzRy9OC-B{t)emo$#4Yg9S(Pm_i{Z@)PtIxZx$w@y>cnZS!)w_(S&5crSF zi(t2UmOrq*B@VvhEO(~wxJ*b0qvyD|d5*)ww@}I-+6oUfc$!rdNYpZT#ljQT^c#11 zjnij*Mxo&~?&dRYdW?=JjM_(7v=X};S6}Dj;h#7(EmV5MOu{u;dca@4;90EPqSTe+|Ch z^qq$1bSJHudCA)E%JiFt-xd0Vwo4wCMH&LOs{G+q zFY!a)EBbcBub!UK^lz0J*%$|~ZV6N(>F|_HD_<<|`;-*;NmB4sm!#E2l88DoVHM?+ z_)^rBD66sXl8jj~SK#p}(eROsLRpQ3-+cspB*WozAHF0{!ca%fLH!s?;odYGp3$=w zW#RslDKk-4XTUphI?C#_qI5MCb>tM(k5f=yCp%MQ;=CjonTz{P+HC2LIx-+;Vhshx zP84tIGI3Ci_$^2K)wb>1&GA(5fX4fbmtTDatFo#7kYRU#H-(5_v zz=kid>CYRz|85eA_&MPnI3M1D^Whsf4{@8jYpoRSf@k1P_yt1w z@C?j@a^V@63qR$YZL1_3am$96au$>czrYN51*UIaE@_+Kag91O74>Hdl#Kc_8J^Zj zs6!J`hbEv7O{iET@u)xJpjgzOF>8w`6VvPM6U|$Tl{@JiN#SF%xWqV7yay$O$G zNkZM3h`Q5wzBHdP@q7t?WPv}jtL}_K-Ko8iQFp={S=XKL;T8BI>v~f^U&4EL7V1uT zBFhZaozp4Qo$y1Jsi;4vEWk4+>Q2_1gJY%{9=x5~%3Yn?2pG;fey0upT{(%iQ~J-r zi&tiZhe}dLYIXb$T{?bq;Csw+*AaeIjA7j30x7B7DvJ+o5%|AK@xjf|CRqrt%c27t z=?AX8z@6~W)n4HH=mn0x*QOUZuk(B;59L++fTO&^lU=fRu9htLfWrga^8a>S=kWig zAG>j#o8I5J#!VmgCFV75_^?|Z?8Y??-*5PRYyWkW*@(6DUw0bUIK~!QUf&}#A}kMG z_~^Ei62xx{`umywxpD^YGT!{lIe6*bFEcT|7k!>x@dF$ElI4%_>i|D5`W913E1Uwq zT!CM%_C|zXuH}m;@XFP`h>HzhL|Jrf8$1!=hj>)`A;J@pUWo8Pgyz!=vC{BCghwts z5I4{RQTrci??ZSO!~f9mKHP8m9_}+e54G&*Zk`^t4?X&@pzT1? zbF+R>{NNo(&swdNWlkwQwo6t*D~|4zihUgT;FKx#WkLSdr}Ioaoof8eS9OYZwU9Kc!>A$Slq{Ba8J_LIIiz;IkD2xx0BYr zz8z&zDdLCsZ)}7A<>Q7AEyl(O-%W|-&BKRe^+N}>Rv|5Y@PMqO9@sA{9@r<#@85^^g}t)$47Ep=oZcfPr*~^D zK7~F`r_fLAB!&KVP|*o$yDT`qO`O!Rtuh~)cWjHAdvuG;J-S&6k8GBLBby}u@J6k? z!y6>Ga)abhht^9rly#^AS|^zY*Ga~~wUT~-DwnkVWxCCxm0xFpha-GUJ4x}%WjcOr zgNJ{q@SWQGAH1*Ipy6LC@DKk1@e8BJqV{2=2jj4*6J$b6sMh%C*+PwrnpHz%F+DbF zri`H?XUgcv8HOTe$SAGpGBU!@h=^&T6+TUdYfY8mVN>Nkttm*S$gnwvhR&HR)Q~wL zG9)xaK}=<^)+8A;+YsKX%D`EM@P1V+^+&${%n8zO=6F-%r02=-a_Z@|GYG@dyFQd^| zZfe-1u`FBqz0U&=J?LtOho2aIf8b~SONmQM;yNR(@xO?l9r07opscd}iof$1J4<`f zZ;)-!EQb_-k6cdHy8P`N85t>2QBhhv7cpoLi;a!d?Xui1y(BF^Nq9+$2w|uSm9&aGW2- zcV2bX2fiUGaT=s`%PPeMkD%5xUlt@y!zxdGz$!7=pv`x@^rF^0?aIqUPRKaZj9 zVL#f#ey9%MGY1YHlmlo3I;eFBDO7n#4rx`&p~IC@sdZQ?j~tf6T1SvVI2Inyk)ucD z2>K-+b?KNoihbgK9Xozpj^Vfdajg?bA^c`Np>Xt5LYF`G>wt&phw~Q~HL&8x6lHKbJ)+LN{TAQB$shkzvkY2sWXkjM zGm&!Num4p3WtP3qKGW@g^Z?A6SFpo9cFeKl&`-28a{Tf*E$dX z*WyPG6YIT?KKWRdp-l$y`zw6pI(f#A(|C?aM?2Pcr|=u^Q~0K_9MUh1zx!T)Kq(sh{`gf%pOHvkiE^0T0CQ3Kq`K@D@AH zgV)$~h~ZL{!|x6pKcd@HKRxuGr``#mmV$in#AmaEI^bK@D z3qHZPU;ku6opFJJzug7AN4S9ZsTbfIcme))7vOJq0q^%O;QinQc-vjTd)5nhSA7A# zb{F6k_$_?xF2L9B0(|W*pbyH073<0_EML3k0zB<5EG=7g0qw{a&=z_D{qruMP2~dm zIbJAUx%9%q6-zF_%kBdD-Clr~-38~8MHes*<^p`|E}$>{Hz;dwVfw+vrORUwx7#mv z&0d>-Edtjfa4iDYB5*ANR}+D16Cdr|WlkFTB6@kj^hXT0t^r2=34RT{z%bu52WrLq zJDd%(A?Ay87Ax6oE}yRj@N+R{!`Ta7D^?zNws)*NJW`&p@}O6{=ZmcQc!Ic=M=-9% z!6*^`gK>Dm*aH;$nq~DJYd*@ed)~dwyL`0<@cOd;et6#cD$kVuKZHe>L6-kdb&q|| zwXaQIi@?u20`O+2V-F0d1Mh?DeEsd$`}+IU?H25)>*(J7hQOep8v+9ZZ-5^Sbt6+w z2L=b-0AHCKx&`~!#b@fmQ|5Yp-q_B6-ml}Qj41Y-oc7Xb%p&2{E|EuSd|W z@O-%)o;(d5!NK(0Y2s`5y|Zf|yfxqh1B;WpFt54xh4HuF4gZ=uTK}dTb9=)($A1v>OdGH{y4}@n4 zJz@qM@;6flJW3pc5T}7yuRoUc;3og>j=Dj;y8ooH8a8Icb-f1myV21-u)bfA9o{eSN^v;g{la~q z_lx$W>5P0g%(Ej6gy-KhNz9)!W7Pv2A`&cL;7q{&vjohI}Wa^vh|b_Y?bz_%((a1auF& zrO%*#H{Lg9#8t-$y!o5JQ11g4ee#$|;};JZdEfG(Bkx;@bk&fN!`47+p^70RhHZd0 z4IVLU3$zW|0qq(*eCQr%A9P^Q@S%sG!_X1v*r4G_7y;)#((nt6vrWBEH`{vo+ncPZRpyUg?ttE zB@ z!;j^V-j8Z8mVL_dV!^l>WpcpOW_Xfp+P+npJ}k=3heeUgi*cHYV>B_|Rr7Z-Zdj2Y zOP>df)ntqmJZVl&4V~$=Ef(X!>cSV}2;!){P+ofF6~;mDGr32uaXh=}m16kAa4e(t zh%xrfZLAr*Vz$yR#qf*4_&t|r3cWfk?-;$`+Bc9kAchhnN{U$WX-y@SZ{^U4@EO2z?C4&j$_l8Vmldm*}B{z zMn@5cx}FzXXjFI;6>hSlhL>mtL4F zY0iAbl*>}HGbJT6UC{$%F4*b|OBO5os37i&F~a9+90RvAzj&b{OE)7oTbComA<8{I zX4yH%7_#pM`|I;_KX+vyCLzy)XY*>gOhU@(#Pn35HvnSg+JAD`S!T!{A&<%1agk5L z7{LdC^%6a1TxAIJf>9@{gXkZDF*J%yd(ZR4ZCdj@(@QP0*8Qp0ao=9r{5*B+%mriV;)LL7V-TwE`dmmHb9(5c8QMXYL-A8s=XJ`1@16#DIP{Ifzm+}$ce zh)>Ln@Y#CIIO|kz@%=f@V|h9JrzhmZ$1lj_r_J(%_-LP#+-zNjaP0bc$n7T+on`4_ z$Fguqi7Z;aRNvP^W=xH-;u9VlHJAPpuV8!&7$nMMg6Wu=+o}l|7s*Rk>!OMj|$ ztnFLd%P2togL1=^%aSwG1s)uVF^Wlym!$Mmm6@AEzLcUz20mBgSgh?Zg)fQrHo!9< z%g!=E<;u7hlmU({mO4jwh4*RWh>^3}8YQ%>gVSJ7VVSHv+ z$0s&5(HWH(t9`#P-a={KRB@3iLY+_Vj8S7pD|#Y;A+N|k_e{Ob`%kT7ZTH7~=S{cW zBBREQQH&Ap|E{_L&qIpOT8NKwk57DBvJ+)U_sik4E}xsoQD(VhxuNF<_ve3;$QiG| zb2a>UTxEzHEk2j>8QDEPi5V$Qj7LOYsMiJDC+%N@x?ZtP;q~Np&Q!eyV5B#wk5se1 zt<>8*Ynhe)RO|eR?U7-Bv+Ek59}xVBT;&I>F|`W)#rd4<9-ri_G$;H8bRRbM-?yIG zS=W}J{;|r9X1jyK-?%--(O#wQ=x`6b5nB9sb!hR!_o3b0ntyK{j};%Qj^_QJEFG2~ z{8n|<&nY<>PWlIMd?7qP73(O}<L++_tz8{wL!*uO5Xcl;g!+qt>PTB*aIyL+tz^BIiF0Dk%$}TTCb32ibqxiZ|{m%n&LUj6dbAFNltd_|7@?wE8Q>+s?;;pU;>i@c}6 zz$PD^*8#N>br0iXl^_1LrMHPLKcUs-2Yg*;YEGu&_{yiiFl7wOD%R3lOcv660_BF= zyXp4&I0rm0+K}$D-0(K5{zCaN;$uB?;`hH|+k!4XZt=+~%yXvWw<-I8 zgRyCOw`vbF_Qki@{^YAS!}r%ZFE~$k`FW~6=G9jF`}}0-k(Ki6kDiz3Kl;s8Y2R}P zQGNmhJp7sh;~UI-E%LrS#r?S%3~-C#Uy6Ffz5LKqh~BlHW58IL0t`;(Ln&9`ltA5UP7CN)$=Bc;P4;x%F-4+Kg z2}S=q)7wBX<~KKPpum>rIaAGzbEi|Cho?bvf1J;Yq{cGsRlxI0&w^U1dA{{BlgwzT|C9_s^6rczQ%Z+S@E&zXSO451Z!)uEFsw~40xP8#ft5E?&8wyt0h!d64}q13pN^gUW5sKX*TO2?yWGnU+Q+oF z4}QC=X`$8ef&YLL{rUyoh44s!(Z%cGXSMf$u{~_Np}@WlM|{LR zKIlj7oLjs=z5D)q^81(mpbU@mL-YjI-U2r_zFl#f*fu>aYMk4y6MIV8gX`tF4}Yze z94nQfS>wqc2a}%saew!7tj{aXZOs(t*<_Rp8O3{$%(~NQ} zC9~Yh>-_=07{>yRJC2F%_GbO8+l2T`OdgvqKXa96GN`}eZB_Xt7O}=O>iZy7qZrL`EiR6#x6MF zu|RJ;-Turmg!EdaXKOCl_O~`^q#(Ji*&Rl`!RsD8WY!f{`N8v^VlOX~X)mw;^>wxP z*9XAziUFUrKc45hvwT#Sss8wUe{82_YOKRD#dY0NuPVyUxe*4;`q)+%?)-v8N|J(jS?{HrRUU<5;$p?^okrQ8-B4>-v8J8^7nuLUBG)4 zg@1f52LDySxR%prACSb2S<)+Mg!oMDC+&yh9J{f*-SeL3{Hmw%9sC}5vAo{a{7$*` zPH?Uv#-|)@cJx$an;@T^@g9T)?(8) z1tWJ*ICub2Qw3al0heAzq|cD>HOXMLl*_R{oRU|*cvZlk*Vh}kt~yR&((8G@eCu(U zduXu?$Q}=#-Z1GhX@HQ6=Pt15aZP%Wi$hNy{nc^k$SLA;;Yu8c@1pmNf&&xn~AdrhNaLxo7 z8VBxt`V<*kFheC)WXYNbHmC>AJ}fVve}#;6fscfcJOAOoK9m&moGG24CECJ5H3s>Hi( z#7Dtj%~gJuty$$%BDZU95Xq ze(=7_6`vL5tDP8^2k*2`jlZNO(a?z~B>dsMSFE#z*nf5(E|IX+XfChswC5FagtC#w5;>(Fvh4IuY|S!(`Ij2${JgPO^45Wy@o`<*B!y74YEoeV=vE zx8Hv&;K9qAe|uB*J%3pGq>j;S@(wt!mUxc6>XHm|i*a7fF?VO0TX{0L$xdwm4m`CQ zZG{_A=UDw8=>Nld7r(ve|Kln@U>`dXBY1axCism?PBNJ(YU8F&7Q0-L{eEwk4r)?H zj7-UiSCez%BqTdlO?M_p{Q4YOc4DnM`r;{h35;#{T9AWGKE9A84L=}VU;pu|*VJCb zC1F#Znotypx;<3&iI^yU=DFqh?KT-@X$s1eXUa0h`PHd=-dr{e<#8D1n*RYcBb-&p(IH zZq}>^vdPJIZw0n2`MR^g-JJyv>`Z5xgs;q$Lob{V@W=K29nU}d{*JK7zrO#Md|LIX z{QlFIWXqF#C2mumOk0vD6ABFG^JuUmM`cB!u8NR;@zcZ+21e{0@YlWRiumpLJhwqr zGo)R4zuBWS!?OW6A}D@L;9(*zY%$2y8O7sr)=|jCt^hJEqNFI@h00q(f=Yb zDGAK`R^*P8;i;zOB*@JCWHlo{Nv7u|syT~Oh~p$wt?Y&O~9Z57$!?fknba$CU-S0*8NIb=?I=9IQE(su1sFIGI-%&ITocV zvce}hlhw$)80nKTLwT^ml^ZYI%A4sFq?#43+!*0jUIl`s3$~`B*Ea8CEI-?EPe(sE zwFQ0P*x!$R1liZeJw6zN?W_R5;4`oY+{+LBjWin^_bsx)+k^EKS(IJP1~1N)gf#`S z^7MKs*twWsk+{pUYH`&3X)|iuFWa=i6#>~UNgZbqQ2KW zaFnC*V1kd!jFzz(QJM!nBw?lm&lxAaUVL3@^1v_60cSnk81JVn4mj)Q?K^k6`VOAK zxP9K6Sboq?)pdPs+_~L}7}bgo?|qv6-KD+eev@HH)^`dRp~+x`CV>;2wy{{!w-iev z*r{>ggT}6)=1R4qQD1@1Xno%TxHEx1|N71_`tQe%HYn=02kEYDyPX*ut7Cb z8EoLm^OGfHUXqMKc^Z&1Q-WeANkH@jF9K^h+7Qc%r^xNUqeDv<=bIi8JJ6SsOnLVC zME_CrIeiY^#IpqcIIj3$OurN3`4w3fC_k?2i~e@x8G-Gs+W2%Pi%g|glnA)q%4B=1 zjE&1A-QaqwRDz-fi7lzT=g@b z56OLM)vXiQ-Cd+}u)m~~E|B!H#ge{ek>*XOxOmejPgs^%YO$v0OWbmlC6uFBl%pCv zd9dUoP>wW99%X3`7_*_pS(+v9;!NZD!D7j~IPxq@sOv+tXGEe*glEK9cti|MnbrI^6V&Zvf`p#@d2OPi5S%? zKkGMakO;7`P3snMGzVT&6O0zsyI~7@;YmTQ1MLC+iEKkjXPsFUHIe1owUkN-SrZ3J?(-vpSl;U(XW?q5}&5ct1GG*fAv@7tci#7734xIlMAgZE_B|el`7ZZLMzRL z-mpxWOlV~>q0`ous8leaQ_2^qWR#<%vSOuq&})iRLbczB(k$rGd5Sz}aP*an1&wk9 z7PJaOeLn{*XgoJ4lLKA-+&~6&ai)H5AO{-H4aT#>f>br3FhTCMcUH|hp#?1H`P#ffr69{D`5+%=CJ3|R8W3&3&D2me*G$jN^NkKAC9g8>U3dFEF1%Lex~ z9ZJ(Ia&XAOBG(*pFkh2uaLB9AV*m$FM=!vxy2u!Z}7)ggFU{g(8V90wj^6x z2Xq79xxHpQFGc^2UAuQ{#v$7T>Gwq5RdiCE#HS{C#K(L2VSK=LR^&SS_3fcbz|IBR zSuF?CdD+QbYUznxvgE{0aGiIklH)sM@v-e{(Xs7hIx8}r7arNF+?dWv^PDR;sk!7i zAKIWyp0jfCocFI&x%<(syKk+^HdxLot0v1?rGe#4j&thHRVu~cI4hImtjKVNl)-T( z!+EJP8O|!aqDa~V`6`p)tjG}GwP!b(!|K6D9wIYN{R#c!V^b1TLVAi@eD>q|x_J4q z*v{s*`)6J+kt|ZuI5ZEh~=ilI6#C$}(sv zSj|h0ZkH0UnoEvslf{R($|9(^a*HfHv{{Ov1z@o+I0yziIPCKeY>;{2u+N1G!C|Kg zP~YdHuFr#VQP=0dZy^VDeKwQ@274xyf%-mu2Y7GWOC=R`ed;#&$f3SZMtz@z`aW^X zGD+C7RN_(R$8TPOwgdQNfUn-ZyWM0kcM`NIBR+df2J>Tl_s8}@@*gE7JKZBb7r(y1 ztySc&2K4W(R)AT(=Kcd}_5J&0)tUXO^vpgfJ-rvdG4?8xsjilv;{L+gZkRTHMr1Vs;eSggA0yqQO?6#45s?w&1xQ)>T}I&un^Z^0hsFf2iL2-gX_st zSGi!S=inO5-oI95?OQ8Z`^r`3-g3#Ak&awiFO7fXN097|`2I zy>yvkxuX1C>M8bqHRac@hq#36)VFVUmwb@d>VvdTuvRyuhJvJPAE~7tTO)WuxsQm{H=srEXcm3)0*A&|a(ce&I7Ua6(12$+OIBv%M9q-^R&gQAp zr?ofBi1Bzf@7-Pe`}A;AcRi2uDL<`Xq^5cWi?3D?QbTxt5}%&c;`64by5gL>)FS}T z?6uXoheKTIjy%-KC4b}%b;LQT7GfW4i0e*{4Ap(R?}z8D#X4pGRJ0FDIvAknQ|B6g zf!IWYYlrt=ACobyOva~fM=ZlTYOr|K>^Y$l8a_vbhKC^y)9GwXL!uRiJe~zOkL%2d z2v>6=BP1*`LWM^~Ny0e;HQCmUl= zaxhLQw;*5T78Xd}+(OBpSEvf`?znLNJT=!jA7kd6stDsUFh)Z0w*`NX^1jYvX50I@ zxBepR%Zu{k8ovX7i$KQbWsKh;7Zv`Ft}?{fp1%K#_PXZ#!YkK${r-%<;rO12zjfIj zz&3w!)7iGf>z8e~^xZuaLz|?Zdl>ef#(0`Z06>*OA2tCI^`BKzVG|wL<>$$9Vt8_A&5x75YR4 zS>sc7S01dpZQq_Zjz0i?@!$Tc`?=tAI;K`xx>S0pW!Aa*sn+?aw`c9wo9FNz>hsU> zP6K`dxmj;IONw3N2N|b5hYym;`TcMI@Szg@QNJOVywdVcoz zU^j$OtW!7gdxel4oroe~(0{Jhh(=RZMeIV=CEp^X(F0-cP{Q2{WWrNoa-(Mjf`aS5! zpMIiv-g#fIfQKyNqVkL8AHZ+=`c^!=r3a25t&3}IHe&P|^d0Z}yWqJ9#`;gIL;D5o zDuQ+uVVOl6oowB^Tej@kC2Y&wv}=ce<0|yk+km!l`V;aU0=#Yo?^>iB@1x+mEO>7x zV7Ln3Z?0Nju6;LGu3H2D!_`6$Kl-TA3yi--`Fo4MDfrx6ykd#WFIg!03+I!m{2Exd zv(X;pwcL1%)Ba;et_SnGI~cbS$mbnAezXwf>n9Z$x`hXh9Vy&@^l%}*s}SE+h;vhj z`$ggILk9|X9o%1t7#1Rig@|F{_I?X z3pZ@vQn-HGmO{j_5OFMAw`F7D+RYmZ%QvkrEZbO7xMst;!qw~77Q*|r5dM+*;3h{v z-4PcM(yskoi@>!AT#LZZD*_gIQLtMc`6oy@HReBorPmvI1KddDsl_}c^3=sVW!_>t z4nW>wJ`x)94#YoTAzf@!R*X+T{A0d|8T=9KRKyJauH~=A;ioJEPZ;6U<;^|+jya#@ z)4klfmuHV_#F)hT7n^Tx^1nWL;*CSs>xURV;Xwq7J(1be=HDWwga`Nafv0VoagsK3 z0)IST`ArjSi;=eYxUubzFO0UG`>3(4;p?sHMBHGk_ctti=7YwzkA~W8p9D9r!}a+& zZky{Zd#|xAJ+u{P*wRJZSObcX7u;Ggk@vjh&Gpvb1gd=cBkzotQT~ydKtHW)3;CA-g&y2?O~+W_YDkh zRVV0dnr%q$*+bK!a#CVav$I-9q(-;K`r+6{)xW=AfUIo<@`t}Jn`is(`vuXMo{Z^T zVXf)}Ag|X!Ot{1GnE&>BMf2ifv5y}%wrvbvs6T^6whAnB|6{DOx-Xo^G8d27^7Hp& zotu}Q%^P)HOIsIE%%U-$%PPNlE$_u6kDlY_zs5S8Zh@GeT6s3l_VxD*m=9QXHt!sk z2|mxyaviSU1k%Ce1pXQtiLD7fW6k%w-fnvdpJkjEW4|lvzLqxv%WbwhZ2|Sl=iyY_ z^6TBH&*yaCwAk2$s2Ja@^vsxu=&bmdm~5LZEz_1|OV3QpFR-QCa&qhS_J-s5dg2Uj&Gpj zI;7WwJzCcv3|=rT>-qTk-PA46ezUzt@GU{XLAQXw@iiJ*KV8?mu`0SGH*$r$4x^;BnsI>%hJS1=jZggBHx(Mm|CI zCPnP8 z7GU$gZeLmN}y%X%07eouj(_fe_7vw{nsL2 z(RX0K4Vd28XJEfg&}Itrm}0vBM&#Ect-xo?k*?wAkzWBVg}B{fXaT-d?&^6MC{xCOYn&}27sKahkpStGW5yCSUzy&9?)ZNPU2mi9v^zj zUP?|Gi@}?#~$af;>5p zu}KN&hmxa3hA8)wOdoH@&t*j>cMiwTG2E_S1GA&TOVHm?^PyO_e-6j%xes~hoB2J) zj1|0h+;#k9VDh-ZFVg$WbTOE5WHWkxp3~$FlMRNkwO~9>(ZAby9n6V|+&y=p6Ym7y z66d1gOEN~iZTJ7jJn}#D&@VN1y%Rp3xssBVp*g8>$%#UyY#OcyGKqN|@Hj8FPaF#w zf&R%j{ykP55D_0!S+r!4=Fd167wgYkOoTjaYhKX;nU8CVd{HNbI)&e(8JmdL0*I`~ zBCs~8gAtM=x7*8q<9|G zF(8}4iUBz9wT>VCWhumL9nejJ+U(%vJxT=44c^L|=ywKKxKKOn8qVCz-fQ?lBQ%sda4U~k(Ru0 zBf&=OILeN38mLRbF!9uyQ)|VmPGdflu^~s=4fO$6u#KC*7$j$q@~N%c?rQ8}K#~#V ze*V{Q*`$vjj4{{oqd#UPpL@t>*?{GI-Y0vs80<%jC3$O89RF2vF?syII`^26k?=$G z^@Xox&ole=@td4SG9RTWj@gp;gFLMP>0=B&+kN2ufpukwc~f9*d1d~2n@?t-mW!k1mHF2Gz8<(g)D#&37UPe{ zPYyVJLnyW>^7zrd?YjRhSh~1!8`}EV50-vYM~)wp&FJs%&OYRLUz5G4(@tZ0U`%DH z=8}+UL%!JAFVBL7xIsqdPZnP=cH0dLs__{&=AOmg(>z5o6>Dh_=3P`lSY~Q4QV;Q2 z(Pa4HS*Bb+%UPZtY+p^vNUd@F=(}3EeeW*x7d{W~tp_x_Z!7KrE_UDDO@(8|x`zyx zzzIXeK6aourVYY4(=yHPA>W99K-ZA5rkPMXpWZ7|7l(tdG62tiJ&kgMGU3jSH1goD zp?uVk!DJ&}?XvP#p1?qGG5UD?Y!kCQJ1UPJLE8z&Ezhn#|LD(Kxf6Yezd*lg@_opN z+K&4~HJk4)X&>w-L%>k8xO~Hsrb+bLbg6iBt33SrFZ3~g`t7GeMh(jYpYzD+JO0WU zj7`jv?ujG72N_g@#Yg6uR~8@VyLfz;Vew7Cat}4_+SbM3<8vLKf!VIZ-}UrROU=%x zas24lT)7+V{a=3drEa@tdp*ankiB}tL*gdM z@U$s1aek=e>~PA~$9BmR|F6CCfRC!m_jnN3T?Y$I5s;z~IssB>(o29qC}Aie3F(bg zl8~NENQY2D5=!WyCNwFET|rcyuDZ5G-Bp(-mer^4sX^A=!1=%5@7$ST5(1XpeQ%5S z`Frk6rrdkx-kE#P`Tf4X51!TMdE|kT2lmmQK9VQjdq(`rBCxI_8-1qDeSY72-&M9= zv)>7K_35rTpXBU;(RGEwjQ^g z+{!-i6Hbp=EM8!tO^f!IsbK6)iCQF{k-joH0(`yj1>(5`?7bvEnPq`TYjup|gBN@1 z{26)Y;yd!*x9>^uu{Gl2H?a|4u`ifPKf2~^yFJ2XHC-j~*(s6GLTYLWp?B2aC7C zp%SlH^hw-eFqVR3PHqUcJ5~ygtd;|>9F;e}c#9k>{T<1+`1o2dlgOpC@uxIjij1in z)I|KPNr96_R@(K7*9JBQ9OrK;F$QOwkKys1gmGew_uIE0?6#ud5A^fn~ zn-w1)FJy9(jW>YX9~UgMVuLgvZ)RK|SW_XgATM0vt21T&V_W5kcb}2--=3E*{_=%f zx_n804{-lK_uuDa^D{MIyhMtd-<0P0cTKr?H{{`6<={2uE`rerJ|p@2yar-%`^iw$ z^I^EI=iFQ}#*ZDhAKr($+9QYHi~1V;@fXnt&&xi{0ma3|>1`+5ZSbT~;+JbM-F$Q6 z!IDapvWGXwZ$5k7{u$eT2W-5%SHN|vdTP4_RV)>syhxe8EELB_h)j(47Z38cz|U%y z#O-~yk_C{lY_)@KVH{!jxr@P4UDp@pFcbxbq9_fmT&66(Q}=g zFlY7rPwzF?}zeQxYw&i+Byh z;&@**uK6oB8M%?1t4&3Q92jl({U2VKx`W3a#P-+AD^^cj9nJxlEV;^NbN3u8fqBUi zXi3uitl*Ml*?abo-hT4x$W?v(chBHEZw(kn;6PboWNs?iS&=elSp?Ww55ayTN6L?JaO>0;4r zwJ5XEg2&^)T60*|j5XXwyX4yH0){#nYpil?KhI^h;JTmvzW(VJLDzk7&0u` zvT(7V{qu)MtnR^wPc+{Cx;o8H>)O{D?6lr8YSvUq09z-%qJVrINmySXiPd0wfcF(m zwhs7AwtYi$b;`0N6zq=vaC7=}CDl)5z!K}hDV`WwenN{-(mdYGU ztV~8FUGhWxf_Qva&+sY#Y&A$C$ zd(|C(^kn1hudS7k&`>fv<$jmGGIoxaBvlm2(sf{;mE-eYn=kQXpMj$n3yxk4*=J=o z_F1;UJ}U)B5A3tBk_-tgPL~ic^bGFVG6^gI_Y90ngL{@FetAn}QFF{Q@XpLu-kA^h zmNS-x$@n;b85TZQhBU)4CFhJ|0B!F>1{jJAIG)SZ;ryodV4da*F?$Z05Bv6m-Bowu zu}2zhKY#y&gM;P1fd;#4;=ME8cR#sQkhC+$l(3sN|3La2MWu63ASfn{d*P=CM}_I?+vUs%>r7AV=GM$%hT6KmhavmdEg{jYN{l+2IHeU(I4QsWuY>`NXpo@R?<;vTh~Y``efOb zRg!{!Nk+dUpGZzWfYOf7L>YKdlQ)q?2*rk3Vu zf#c)A(~{J^736723K%|CmX<69zb6qKt%RKhOUvMBtr9CoiwrG^Vr?yz$gSjP6-)T$ zVhP(^BpyCf#LCbTGRF8_{>WpGYNiA=E}ZK`9_0_({$AMjKHx)n`OlM*eVa8qt8mXI zDcrqL^07Z$!_C4mk^^Rz{W#gWRx+?Z@Hk1`>~NfriOAoRW_ejQ9wL8FaGW$cMzBv< zS%~`g#CnW4awy$j#@ujAmp(k4R5D=Q@!$`GdBf+ujeH7w_OjQvT3BS$kry ztU10%R-;xO1NRC0#LA;JvI4BEQtT5YN61vcbv90^ccf9~iY@WTo$|lC-^*l8XId*>3ATu?w248stQx60tvUpGeq2zS=5@ z+fgR5+sh$XBq$|6U$&T+1mSX+sbIt{3^|70;{Pj`T*H1av0;;)bfJAsONZ@Qv!z7)$QN6 zr?%<$|3er91$Rh>O&B8s!S!Mp{b013Uv+~%G5SLDtFHEiwLcvDLiY!bG4_M4Zqye& zUBRhhXIhEWRO&Wl>0=F2B$qVflvzi+^~q1cYPU4Z7CFtjJIa zL{>Ezo011z9<^uOZf+BG+{`%OIUyK1Hmjl&7!XM{;B#>Ni;OB84+mL&;2dp}!XoIA zkrVoT#VpYRXhsU5zoT}BdDz9s?=6A;sBtFtN+~OAoQu60I=eN{955SuZMiKQd!1BN z*z|OjSM_vOX46K^Y%+LFWHpf|PfimVP1~^E-h+k)ZKA#{ucSc0`Pu2X?Ofh__`sdG4mt-03v_@NG$V@rz&`uOuaEWi@wbgd9h*@X z@O}TOeEH><`nWDxgLAIz^mBz}rNj|j^AEh3yYOB36u3RY--BPj_Nx5$owwy2a=HJS z`YoQX3$Ml3d+VKZ@)nLY9&c}+WBpdw8|U7VH^5kAk@aS)*RU_z>Njt`F2BJwJnL0_ zuC{swY|oc%UKNgEVt@aSmtWFz8JydM#zVj_dI~(6yRO;rCU@*UST_^Q!XxO14^iKt z8vfeC_e%q-de@EyjJGs^P1LYq$Cd_gi5igq(10<>hIN}aHXtXXVeO{%4Qn=5Heg)7 zVO7<-2Fy=1tf*MqP`YkSLrMAShT^ql4Ml5KHh@LcfVs;Ca4H)xhudH&Eo#UuDQL(k z&Tq&rT;2d(!*`kamJf4_3yu`6T0!gf&;NezfuDQe=N_>4fPM1Hz5w#aow%W)AKgUG z^PvX&1`EdBPEKR3e2_c!pMkVFg+Jn7%RJpUwfMQ!9r1VXhf_v>x3snAjoZKc&S>!g z-uR#Kuz&vlKl+!dmN#=V_&?OGXwD_I0iPv8`@H_$_$=gq3`PZ^Z2XTrXWaPtU$Z>` z4}A;pDsCD$WbjRchPt(Mbw|0mwsduMy_v0>yL-z4?yh87kZsX|k8k#c{*HGJ0#^f` z|C_;H_$Al}t-wRLV~~ey8}KLEgEP^=Ww1-f{%%7$4{>$v0+vKqFeShRaOsA&D{q6_ zaBqLtL7lPOaiIH)x*8V5D&M$ zLxuuj;o;_idH^*J<%yb(nvF6K@o-&;)_;h*Ti{?1w_w~3L4~pfA0!CvV*G6p9#w&P?Owe_<5nEMdG3W5G=KjYat5H^u`vcTU*K^oU*P1!%#Q>XwzYu31jXP8XE^dqPSwv@Mwm^Sf##=6#U=uJ5P!ZCPf9f(8`pmd|at zgl({Y4p{d+TX$~P89jKcf{$9~#j|x>=Ihw*bG-j2FK4Ezs5iFDoLAPW#EfOi@Uy4# zy|Lb@0uPdX{jKkz*FO>5d9WJx@H0S8U`yYifRFwI>O@K|#{;b+a~H1oOJ@3ZY2 z*4Eg6jQ!2 zT0L{A$)})in*0Hd@6tcJVx=+Gy9$0fdZ{_STaNMFbXAWQE+u6v73K|$^~XB5?a@iK?FlVM&(4l5mmA$~=iC|oy!%xrH(g?FdHcG{@XD%o z$lFA*y}Im-W%_*a9_%;0{uNi(f8F{jtp7aS|Hu(CVJ@+rxiHika^JG~X4&s|_3nCo ze{4VaWv$g=zz|O8Y=Zx{{txH>ptSe3szT)#Vj(xiuZ9m;|83iwDzx&uK`cUI5 zlNGbkzi#-Nns2tvE<+QIoU#@0?K!Kv;o(Io_*q%>nDVvs>%r@)e%1Om#cgohe=vWi z@ZEV48g>(MLQIKiDfM7CAVYSWRBhUz;9J$t`Bm%N6+DKj6ZEhsqjg1lwdm>8w>!J{ zk?}EJDyTFP6$QN>TibFJ@3Y=-EKi7-0smiL)!DtTpx7GA9%zlie%D9c+U9me4<$7C z_WggumTF`D1AI-GhcYE+rPU*s6z3&1$O=Xtl*OWbmw2w+L(gR+f2HmA5A{ttcqOmn zmvr<`c5*{sS&KSi`rqh*q#x3@|8O03|6~2rELkSZr_^I^MQQKk&K)WzH;3Cz@0fzW zP0^p&?Ex3nYpR=WaU1u!DD4Gg>$r@^Sv`VmRX1q)P;8CmQD}{V-%l@FTjW~N12`J3 zQS=n5R_$9WZUffe-v8K7?ERmapKHp<%c;Nc@kgo_I*FP+yH$2}wz?J1f#)BeWk02T zp2PbqpBz7#XIZRzO?OjWMh;L`KOkGw0QduWABsLd$7S>Z?lbPcc3Je_MhsN<8a_`2 z{T@r-58u5)KS~9@bI||#`xED__Vv#xTyDZRe?8}@4jnxr^ml3BBA!FfK7D2M?e&=C z0WDsp)@eU-?s17;pCm*4$HBJU~>O~2Hgbjohi3Czn-~A$cfZhqwpQ7JML~LcXqr>^`10LInzHl-d!!-oI$<0 zx`O}UjaSc~KZ~5uT;&lsQT6n6mu_Q*sBU9*iM3T$&mP;W%j2*-&XpcKht>_tZn#gk z#xkA*4-xjEHoE`8(o)=p-TU|H{)dLzl)s|bw7j&a9^Pq1-xRJr6nulKZRZa3&ZzN0 zbJh4@vy2O#t0F3s`*mo8t@x$kd&{+22|UM>@oeAIviFq)tHhLEm4yE*-V+XToA1nGi_d*BlwQc#cerm@88g=0ne$fZV%EdFahY z{*WWfyWE`3F~_M$u;+T4woKHivGVlI^CtcT$Q*cqlalZd|dRf z>GcovGr_xMLN-S|yt|6mU(mU!z1}js8~EG&$t?Of?8|)q z9i8u1^kP+RT(5ZETns%Du2XgYLxXKX&WH*At9p2E6|Z}rc={=ol$50Cx$4~OKIOe+ zv6>0bk$22uH9ILt=H-N`W!v)9wo|*+OBY_2&%gUz!GEFt=P&;wXaDm>m9#BKxy+wP zj}gA7bTwX{f%|mPL&Rn6C1R_W9rrappE>RHQsJCIt-*OG&u_4w8|xqBZ(6l}ooQ87 zc|Egds53cr=Ct;W-P5C+_D%T02e1gfA>Z5tWFfAT_rLl;`@!fDYETXG#h<^BCx3Se z*@tCfE{K*HsbSh9G;WEnxKiJ&Jwo&eIaDB)ujda6f_KPTg95y?4}d-(oEPZpdio*B zc*FJYMKA$P@cx*P|5A@^9rZHijG>cK@d*j4lXG`u%J)*fX)$V1hW7lZ%15@+q3 z<mM9o!kn=QbH??|9-?lA>mM5z zr|#{2KeXF@RQQTa6@i@JaO6c-pV}kufAfJl@al1uy1PV$tbv}mXc@D8m7%3WuJ7_h zH6PxTdC2pHFGcy}#>&jhC^a_0U%5xl757LVv@9;qz1l`$ISTg~tzJfcyMh;8weHeE zuRlB}isL)aKL4Cve`xSc$dWN({+sKsbvM+h#l*x&7i88`dmRR^iT0f!Gdz1Qv)GZf z3*B>I9{eVjrQ%OdT6Ti?!ke`K-mLiyc(X@mhc>rFc zL4MPk@fp#7)Yw-4hsUq}-P(q6BaTVU^ILcktWXoYBPQevHR}K9=xBv~ zN9XdwC!uq{ks+@VH$nHjzKHqp3VLT{)qpE#omGUPb7o$=LS8&PR9Vc4SJZt6AS0f+ z-ixUDWUWceMYsR~S#~~P> zlg@RAPLtk-faS?{Z^J@-2Q0{i_uQ7_j{NqS$ZwyRuvkV!&XXa*GaKi%!v{z&J9!2? zZui5Zx*wV@dRQ=KbrItZCg?IuTXyek)c@h(;fkIOWM!(XofXRJSx}kqEM(MFDCCPP zQ7McC#VQ`e!{Er6*9z?)t8{O zFQGg~1+2|he#m>Cw{n^CE=*9K7UYGehbZLD>pXRE*VMryht$Cb59^#&Fc&X^LuW$n z606brhlYhI>hAj?A2hRiwaVUFF31B{8PHFsSFcjc0$;XqrOp9QUSF!<$5GmkQ(2_q zDva#!CFRJFNA9=Pv&PJC6^8GDP~?3FugJi)4E#4GY0BzT)87HU(9?$^)=9j{@JR1w&|X?8oKAmEzrS310N0zd>Fi0%#-d09c<_R zJ@s`t&uw^)_73z&7`M8JaR(E;AJ_JOXsGIqeII$_3OaXH4BdOt-fBhNJ2dYKIngSw zrb<~o5DHp%W%WO(^sQ@@_C9Q0rBbTP6my}MZY)*sfv5z-?_fLr>2*+1hSx#C=b*ID zVReoQMm9S=4*vLj?nBP%y~ulBv39lMxeb52IM#!4tBWy7@z-5{<~a{~V3>kNU9Ek1 zpCGSTXBC&7*rQe=t9S)6!b_1;jErJc^xzIvh-`3V6RUh{Hn9b{;JL^p&VgPXdBiHK zZlgjLv831P9AaAru|fv1Bq4v;mOYHzVTIgbojD9|g6e@hZ|blyHl?0;>PdZ$hAcjX zajT0Mcfk5LzyDBMO}+8pQOJZvF}oYvgF+UpwFZtvM({}F0o%)6`Xd9D)epJ9x{v|e zSC^~wLH4VzVaRiJs9r;n@E%w$_B`NX=;)up`6ZdhLSJ+xc}b@ z-QYL2-(TiiCgA*Gur?u_86pe=BztflryyD&h9Q~T~tqZ7eO)eSB;o9k(ycsJ+I<8 zIkbT)HYKqh>{PCMOMHi3_ypH4)O>RNd7LM}`yY(sAS6r|^JA6HkTqJ|AEuBaE69;m z5y+8^gjY6d33AzEVidAt1=+DG4%xBs$c{}|x>O}5C#j{FpGaPoqEe6-Ys-mELr!c4 zu4^;1vvfXeHu7O}@+`^%&S+kKz5;_&t-+Y_$wwYx9-_WJ9JdR-J-$7RR+9Pj^YmK|U7y36Qy#pCwf-{BfBFXvlJaejZ>`m^12 zuy!6aisRiA$Lnd12zj|pqpmThg*`>*%-8{W@zcD|1_3)9HMG;%N%Sw8=zGcB2C z`I(&m@ITe#Pd}-U3wtTAq;Q_&y4#kaf9}7dcJEFc6CXqW`4?1!wJtYlo8Hgc#$)#F zZou;|Q!l#N(0X3pxMOR>`t4g9aISk9xrxwuZfHREUZ0^|J?rE13#q)40RZUeR2wEcWBR3 z?dRmA_+l1hG_mbw&8|bn(J9`>w{9L=C}3&s@5C zn}7aS_*Z+hrsXfVAylcLsK5I($prmf`aPb_>dc<=Y3ly0$^iQhVee-8~pNJS;9Fcc?vdZqR)Vz7zVjR;XX0ZiZ&=N9pv2 zj~&$lnmW9n)~g}sXK*(Zb!ya_QFk^H#XOz~(56j--i#SN%=lr}4>Ny;p_l_iUE2Nl zJF=0q9_?0-5ySNPYI7d}#*^GJr(ZE`_KYJ_yuFT3@t*cD>QT=b)1E**jXI5b&U41p zv#1wQFL_R%`m(MWQ-6*2CF|dwNBb-u`wZ$y)MKcVc&!tj-qRkOId4uS=6>BB*TFZ) zKd7Lz=rZ+FjkQw;pq(=ESa$8(3pVU-JqAWrA~~w$#F8gWrn2S>Q8$HoLNexUx+!L` zFq4G2DAdD{Lx*vL%NV~8;yPd)r*(R6R(;*!gLZv}O{eiw+qhk&60$R2Vvr9){S-AR zMJui{z{$Y_|DhgpP;LA|0;h2snGf<)->>QOHXf*vxSCR-o`yLMq!S*=LfpNX? zd3#|D%X}2`UfGzJ;d(N=fK>?od=b5qSntx6(9zH@2@TDUsnW8Qc#UE_f2FKK|Fg*c z;d*c$3)^oFi-|IyfOd|b4d1VQrw(@JH+2tx%WB{8Zmh!^{r;JGIh?~WV+_W866_Ib z?26$N<@?>%p{-^ZlVjYp+H~(A?R$4}sLoivTJ4-WUGtnfyLOPGbX5YpeDu6S#C1--+k-=DcDyks}AEBYo;bg%%s-A zD6o-j%iBT82CA?@z54=1|Ppg?Z*P@WoRvT8j5KwQH^UX`DA- zkQ*wWeEW&)JiSl6k{4jEe5k=)b>OW!GI5(_n_>==^O)>6Xc2TK6}6#cknsI+ZD*z@ zmwXcDi*Vj}(3H_KV&Qc0T^=DXfB3491JC)ro(2Z<^~&o3 zakf^M5iXjEN**-zdHjCHZ;lxrLNmvNIQPq(6Y?&HfuAusbb(9^nJ1H@=Yu~KDmlAL zwPoF+{XFjnZ5)DpA z@@8^az}^9iBN+URKyWwwF`u&ttSZjsa9!qMUFKq4$fX(`y+B->%;5~ida*dqjJZs$ zv!>38S|R8U%<#dQUx(k9?_UI70Oy?+CP&HA$|6YsYb&lCzvEzuMXv^XZ&i+juf!Y% znQCB!1Qvq{LdKrvfPnu2_JD$*z>b5nK0+vzI=9OT+t&n)|i()r`UA4YYqN@rd zvSPV}ugjCr@>~gCn4b5A_|q@09qKQcZB@85X)$x@j(Z>G!&^^=()i^Ll>Bg9vx z2m6Xw5cs-53uJ2Ge3^psT#WCh0JBUAm@5-e6a0N-ydQKbsIiM?%a}#8WVG)r8MSbx zxK1~iFq@DY`0Vp%ptE?A3>fzRZ2HYPcfJ8UiHTlQWz3X`GTM_hK}I>0aUbsI-*rur z(NibM7}m7O_8RLoS;l&K8Z~{2j75!`F-5!=nq_}&tsW14^3>B3zbt7s<`0}tJ%2{M zjX5cf6CXZuMD~GK)wK54?$;cr+PXT;wIHAK0K5%k+Z+P3{ty^1eC`qajy;Hdi>$As z$BxOd&gFpS&Qybb*}HLP5Zzj?ib*M#pM8h6fJ z>9O#v0!xkU8U$mmzt~y3FaOBHCoVkt^plrSSH7=KKK1w&Fbb}mc>K{TVE0`)_Q=Cm zz;L_*j^!1wJg^tZzh7_g^;vvh{`>pA)Bo)&C;Dw_c?;jtVf^dsq+ekCE2@Zh z|GW85|G&q-l7}wO=KdA+X4$N)YYjkl0U<6KmS&D>Fu;v4nF># z{*3S#-oE#vwob>&-b!=&>WYQSuBC6Ky@$UEp3mpDFT5z=Vj1=+N+i$%#qf z!C}c!;o&JxP6lASV~zDaZnL-C(&JNSO(c-=Q+7wYxn_}@LK z!6@7KUl0dnf8syq{(W2Y?bi?0w?*&%{aW1L%h|SxKk5~c-;MD+dQAr-vwJYTpv=(j zg|WK+@N*8NrxUjapk=o8{TR<)DZ-g>L5-q^Kw zkK%bNGl7wnqBcRtrpLajHz|&N(fgsZ7An>$>)4m};~-lB*E=PO*L%#Kg#S)4UvhDH z@Eh=ewZiyU2+v0`CZV<69GkHDteP%6-qwCBc;vFq)k2GqKyQ}L`nA0d-vjF&3Y|{w zfg^`i8s^uL-Kmh-slWwO$j4O3I8?~XRLJ_fZs9sv&%yJ#$kEY{(_TXOqOFptEXFk|D=;fep>#W7io3wS!lI8wFVoM%oM4VW@bxkNVtEppTa3%lGorxG zSNE`7SUq(OUpPVSKYdWuoj#=Mo;9i#%VQT$Q}+yZM)lG@6ZkZ=Uxj^9fPTaEy#?7? z7G^47+)H~RI1bQd1T;Mu7r7VXBWR6f-qyvxAzMm&3Uq6D3ACr6Km8c0kYjbYR^;nv zGo1zwxuZMgoo;P|k-`PN%%{f71G?(X-hsS%5mXT(A^CB|2U zlqIT~=W5ma-@LCD6-H}~vV%UE+IORD`gdsLsdvY52A%0vh3k7uS>;*_y@|*G(3)Oz zN+hmh6V<-chvn^0-<1!)xuAAEdk{LrV1vcqSm#acw@uTnm+!duZpASKj)&v=*@Ag| z%X`qHz+mQkLk~AVDQ5i zSQ6A+Frz(_g4GC&-MEI$M$J*K;dAsd^|&0j=QSY4{VmWa=+{R^1T=>OBo(Y)aEuMj zD)nlWg^CQ;DDZg6T?KPhk+mAIB2D>$wQJ5#lxZ1}YFtX7jEMJzPp^xDmsoqW@f~Nu z^`-6~_%an(m}>c+4Jr?nyQ@lNQH!|)I?Qd*U~XNd5;sG8xv5m)+FC`ghu*RhTFZ5L zD!4pX1+Iau2ac)1dw2wR6(;)hZEOYU_2girNUxU{yZ!f_+uZ-rf3oFgh`gub<2Q-&9?#219SA z^=Bi8Di>(ZsOK6m!d+?28MIvepfBqS-BurH%c$4tZPk@QqotgwCxa#nx-&%$D_MlN z{>#PnXUMzoo*9t}%0<^~=F{OHOso_8hEN z4zw^i>#8^8K!2YD##zpa^3^%`KFG0@6a?dYO}{o@|L^%vPK(o5PIo%x+0WSiHt)vQ z`xm;raf-6kw8SZFqwAYXNiA`G6aDk`P3W??@c5ME?!ob4-O)usIN$%S;R@6Miu_% z`0tM%9b0_g^$pMec|Fn{Wjp^L*X-Fd9^mk=>;2tt>e;*3Exr2ny|w?q0j>HE7|^PB z|Gu|6_vwAheLe1PcK#aY%|pga8oy}d*wNu5$Bs%wWuo#?C8$zV0V*4ngvUgV8b8Jl z=jrSMCwLHiasI4~bLVoc)8<@p3CA7WsVow8aO}kEnHc$ zpYx`fxjB+$$<c-dd|Tj2Yo+0=XK17Y^D94cf#{& z)NS{)k=r}6+UjENwe4?wEMFHnx^-368|#>_#yLxS7BBLb{4+yk|FZ{l?L+H^`4XH@ zYCkW|QSf|>X(zT2!BdKJK+cOXi<0k;96#o^ z)oq3Exg%Kj6EW8P`p0idVa;lBg$IltyPhoLyiA{e@qDaZudcdJk%LxOd-z}-^AY-t za7C_}AG{h%}v@)){p05b8`RoOjW0sPpTXFSiOlFytsPH>Qqbk*#Gp^!7x)3V82i zyTeZsgmW4{_(v8Z6Jut2q>PKhcz(E#xQ5Q=`LJvQyTyXs%154hqK;#ulaNngK|V#+ zt_n%x*!b4fl7KPsSd2qQaV)$dPlC#GWMNsF^q(?HXMvQiDXYUcqkjIlInyPkBwM12 zG9?`2?;*=mCCCE*N-lCa;4h-L$P8c6oU|wz%Wy;aqN$p`>`}NMbGz> zpP}dZaX;t#QgbpjcQ_p!Qj9%n#!dN#%B|3U_C5W=bMImf?h1IN%2sEgb$;Qc7uEAG zp4I0%&%W@S!gYjt=FDjY|JRix$B(`XhI3zsL-!{wegzV2-cP!cr-tn(z2!gTlcH|h z)ACnbGk!;+I8^fIq?_>jw&dsEw|~IjH`}<^zv}p1({W0UBi?+nV~cmb=+=V!CfiRY VVJwmZi1>F$UX5 + +CRITICAL_SECTION ActiveConsoleLock; +static COORD PhysicalConsoleSize; +static HANDLE ConsoleDeviceHandle; +static PCSRSS_CONSOLE ActiveConsole; + +static BOOL ConsInitialized = FALSE; + +static LRESULT CALLBACK +TuiConsoleWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_ACTIVATE) + { + if (LOWORD(wParam) != WA_INACTIVE) + { + SetFocus(hWnd); + ConioDrawConsole(ActiveConsole); + } + } + return DefWindowProcW(hWnd, msg, wParam, lParam); +} + +static BOOL FASTCALL +TuiStartService(LPCWSTR lpServiceName) +{ + SC_HANDLE hSCManager = NULL; + SC_HANDLE hService = NULL; + BOOL ret = FALSE; + + hSCManager = OpenSCManagerW(NULL, NULL, 0); + if (hSCManager == NULL) + goto cleanup; + + hService = OpenServiceW(hSCManager, lpServiceName, SERVICE_START); + if (hService == NULL) + goto cleanup; + + ret = StartServiceW(hService, 0, NULL); + if (!ret) + goto cleanup; + + ret = TRUE; + +cleanup: + if (hSCManager != NULL) + CloseServiceHandle(hSCManager); + if (hService != NULL) + CloseServiceHandle(hService); + return ret; +} + +static BOOL FASTCALL +TuiInit(DWORD OemCP) +{ + CONSOLE_SCREEN_BUFFER_INFO ScrInfo; + DWORD BytesReturned; + WNDCLASSEXW wc; + USHORT TextAttribute = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; + + TuiStartService(L"Blue"); + + ConsoleDeviceHandle = CreateFileW(L"\\\\.\\BlueScreen", FILE_ALL_ACCESS, 0, NULL, + OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == ConsoleDeviceHandle) + { + DPRINT1("Failed to open BlueScreen.\n"); + return FALSE; + } + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_LOADFONT, + &OemCP, sizeof(OemCP), NULL, 0, + &BytesReturned, NULL)) + { + DPRINT1("Failed to load the font for codepage %d\n", OemCP); + /* Let's suppose the font is good enough to continue */ + } + + if (!DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE, + &TextAttribute, sizeof(TextAttribute), NULL, 0, + &BytesReturned, NULL)) + { + DPRINT1("Failed to set text attribute\n"); + } + + ActiveConsole = NULL; + InitializeCriticalSection(&ActiveConsoleLock); + if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO, + NULL, 0, &ScrInfo, sizeof(ScrInfo), &BytesReturned, NULL)) + { + DPRINT1("Failed to get console info\n"); + return FALSE; + } + PhysicalConsoleSize = ScrInfo.dwSize; + + RtlZeroMemory(&wc, sizeof(WNDCLASSEXW)); + wc.cbSize = sizeof(WNDCLASSEXW); + wc.lpszClassName = L"TuiConsoleWindowClass"; + wc.lpfnWndProc = TuiConsoleWndProc; + wc.hInstance = (HINSTANCE) GetModuleHandleW(NULL); + if (RegisterClassExW(&wc) == 0) + { + DPRINT1("Failed to register console wndproc\n"); + return FALSE; + } + + return TRUE; +} + +static VOID WINAPI +TuiInitScreenBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buffer) +{ + Buffer->DefaultAttrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; +} + +static void FASTCALL +TuiCopyRect(char *Dest, PCSRSS_SCREEN_BUFFER Buff, SMALL_RECT *Region) +{ + UINT SrcDelta, DestDelta; + LONG i; + PBYTE Src, SrcEnd; + + Src = ConioCoordToPointer(Buff, Region->Left, Region->Top); + SrcDelta = Buff->MaxX * 2; + SrcEnd = Buff->Buffer + Buff->MaxY * Buff->MaxX * 2; + DestDelta = ConioRectWidth(Region) * 2; + for (i = Region->Top; i <= Region->Bottom; i++) + { + memcpy(Dest, Src, DestDelta); + Src += SrcDelta; + if (SrcEnd <= Src) + { + Src -= Buff->MaxY * Buff->MaxX * 2; + } + Dest += DestDelta; + } +} + +static VOID WINAPI +TuiDrawRegion(PCSRSS_CONSOLE Console, SMALL_RECT *Region) +{ + DWORD BytesReturned; + PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer; + PCONSOLE_DRAW ConsoleDraw; + UINT ConsoleDrawSize; + + if (ActiveConsole != Console) + { + return; + } + + ConsoleDrawSize = sizeof(CONSOLE_DRAW) + + (ConioRectWidth(Region) * ConioRectHeight(Region)) * 2; + ConsoleDraw = HeapAlloc(Win32CsrApiHeap, 0, ConsoleDrawSize); + if (NULL == ConsoleDraw) + { + DPRINT1("HeapAlloc failed\n"); + return; + } + ConsoleDraw->X = Region->Left; + ConsoleDraw->Y = Region->Top; + ConsoleDraw->SizeX = ConioRectWidth(Region); + ConsoleDraw->SizeY = ConioRectHeight(Region); + ConsoleDraw->CursorX = Buff->CurrentX; + ConsoleDraw->CursorY = Buff->CurrentY; + + TuiCopyRect((char *) (ConsoleDraw + 1), Buff, Region); + + if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_DRAW, + NULL, 0, ConsoleDraw, ConsoleDrawSize, &BytesReturned, NULL)) + { + DPRINT1("Failed to draw console\n"); + HeapFree(Win32CsrApiHeap, 0, ConsoleDraw); + return; + } + + HeapFree(Win32CsrApiHeap, 0, ConsoleDraw); +} + +static VOID WINAPI +TuiWriteStream(PCSRSS_CONSOLE Console, SMALL_RECT *Region, LONG CursorStartX, LONG CursorStartY, + UINT ScrolledLines, CHAR *Buffer, UINT Length) +{ + DWORD BytesWritten; + PCSRSS_SCREEN_BUFFER Buff = Console->ActiveBuffer; + + if (ActiveConsole->ActiveBuffer != Buff) + { + return; + } + + if (! WriteFile(ConsoleDeviceHandle, Buffer, Length, &BytesWritten, NULL)) + { + DPRINT1("Error writing to BlueScreen\n"); + } +} + +static BOOL WINAPI +TuiSetCursorInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff) +{ + CONSOLE_CURSOR_INFO Info; + DWORD BytesReturned; + + if (ActiveConsole->ActiveBuffer != Buff) + { + return TRUE; + } + + Info.dwSize = ConioEffectiveCursorSize(Console, 100); + Info.bVisible = Buff->CursorInfo.bVisible; + + if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_CURSOR_INFO, + &Info, sizeof(Info), NULL, 0, &BytesReturned, NULL)) + { + DPRINT1( "Failed to set cursor info\n" ); + return FALSE; + } + + return TRUE; +} + +static BOOL WINAPI +TuiSetScreenInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff, UINT OldCursorX, UINT OldCursorY) +{ + CONSOLE_SCREEN_BUFFER_INFO Info; + DWORD BytesReturned; + + if (ActiveConsole->ActiveBuffer != Buff) + { + return TRUE; + } + + Info.dwCursorPosition.X = Buff->CurrentX; + Info.dwCursorPosition.Y = Buff->CurrentY; + Info.wAttributes = Buff->DefaultAttrib; + + if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO, + &Info, sizeof(CONSOLE_SCREEN_BUFFER_INFO), NULL, 0, + &BytesReturned, NULL)) + { + DPRINT1( "Failed to set cursor position\n" ); + return FALSE; + } + + return TRUE; +} + +static BOOL WINAPI +TuiUpdateScreenInfo(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER Buff) +{ + return TRUE; +} + +static BOOL WINAPI +TuiChangeTitle(PCSRSS_CONSOLE Console) +{ + return TRUE; +} + +static VOID WINAPI +TuiCleanupConsole(PCSRSS_CONSOLE Console) +{ + DestroyWindow(Console->hWindow); + + EnterCriticalSection(&ActiveConsoleLock); + + /* Switch to next console */ + if (ActiveConsole == Console) + { + ActiveConsole = Console->Next != Console ? Console->Next : NULL; + } + + if (Console->Next != Console) + { + Console->Prev->Next = Console->Next; + Console->Next->Prev = Console->Prev; + } + LeaveCriticalSection(&ActiveConsoleLock); + + if (NULL != ActiveConsole) + { + ConioDrawConsole(ActiveConsole); + } +} + +static BOOL WINAPI +TuiChangeIcon(PCSRSS_CONSOLE Console, HICON hWindowIcon) +{ + return TRUE; +} + +static NTSTATUS WINAPI +TuiResizeBuffer(PCSRSS_CONSOLE Console, PCSRSS_SCREEN_BUFFER ScreenBuffer, COORD Size) +{ + UNIMPLEMENTED; + return STATUS_NOT_IMPLEMENTED; +} + +DWORD WINAPI +TuiConsoleThread (PVOID Data) +{ + PCSRSS_CONSOLE Console = (PCSRSS_CONSOLE) Data; + HWND NewWindow; + MSG msg; + + NewWindow = CreateWindowW(L"TuiConsoleWindowClass", + Console->Title.Buffer, + 0, + -32000, -32000, 0, 0, + NULL, NULL, + (HINSTANCE) GetModuleHandleW(NULL), + (PVOID) Console); + Console->hWindow = NewWindow; + if (NULL == NewWindow) + { + DPRINT1("CSR: Unable to create console window\n"); + return 1; + } + + SetForegroundWindow(Console->hWindow); + + while (TRUE) + { + GetMessageW(&msg, 0, 0, 0); + DispatchMessage(&msg); + TranslateMessage(&msg); + + if (msg.message == WM_CHAR || msg.message == WM_SYSCHAR || + msg.message == WM_KEYDOWN || msg.message == WM_KEYUP || + msg.message == WM_SYSKEYDOWN || msg.message == WM_SYSKEYUP) + { + ConioProcessKey(&msg, Console, TRUE); + } + } + + return 0; +} + +static CSRSS_CONSOLE_VTBL TuiVtbl = +{ + TuiInitScreenBuffer, + TuiWriteStream, + TuiDrawRegion, + TuiSetCursorInfo, + TuiSetScreenInfo, + TuiUpdateScreenInfo, + TuiChangeTitle, + TuiCleanupConsole, + TuiChangeIcon, + TuiResizeBuffer, +}; + +NTSTATUS FASTCALL +TuiInitConsole(PCSRSS_CONSOLE Console) +{ + HANDLE ThreadHandle; + + if (! ConsInitialized) + { + ConsInitialized = TRUE; + if (! TuiInit(Console->CodePage)) + { + ConsInitialized = FALSE; + return STATUS_UNSUCCESSFUL; + } + } + + Console->Vtbl = &TuiVtbl; + Console->hWindow = NULL; + Console->Size = PhysicalConsoleSize; + Console->ActiveBuffer->MaxX = PhysicalConsoleSize.X; + Console->ActiveBuffer->MaxY = PhysicalConsoleSize.Y; + + ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) TuiConsoleThread, + Console, 0, NULL); + if (NULL == ThreadHandle) + { + DPRINT1("CSR: Unable to create console thread\n"); + return STATUS_UNSUCCESSFUL; + } + CloseHandle(ThreadHandle); + + EnterCriticalSection(&ActiveConsoleLock); + if (NULL != ActiveConsole) + { + Console->Prev = ActiveConsole; + Console->Next = ActiveConsole->Next; + ActiveConsole->Next->Prev = Console; + ActiveConsole->Next = Console; + } + else + { + Console->Prev = Console; + Console->Next = Console; + } + ActiveConsole = Console; + LeaveCriticalSection(&ActiveConsoleLock); + + return STATUS_SUCCESS; +} + +PCSRSS_CONSOLE FASTCALL +TuiGetFocusConsole(VOID) +{ + return ActiveConsole; +} + +BOOL FASTCALL +TuiSwapConsole(int Next) +{ + static PCSRSS_CONSOLE SwapConsole = NULL; /* console we are thinking about swapping with */ + DWORD BytesReturned; + ANSI_STRING Title; + void * Buffer; + COORD *pos; + + if (0 != Next) + { + /* alt-tab, swap consoles */ + /* move SwapConsole to next console, and print its title */ + EnterCriticalSection(&ActiveConsoleLock); + if (! SwapConsole) + { + SwapConsole = ActiveConsole; + } + + SwapConsole = (0 < Next ? SwapConsole->Next : SwapConsole->Prev); + Title.MaximumLength = RtlUnicodeStringToAnsiSize(&SwapConsole->Title); + Title.Length = 0; + Buffer = HeapAlloc(Win32CsrApiHeap, + 0, + sizeof(COORD) + Title.MaximumLength); + pos = (COORD *)Buffer; + Title.Buffer = (PVOID)((ULONG_PTR)Buffer + sizeof( COORD )); + + RtlUnicodeStringToAnsiString(&Title, &SwapConsole->Title, FALSE); + pos->Y = PhysicalConsoleSize.Y / 2; + pos->X = (PhysicalConsoleSize.X - Title.Length) / 2; + /* redraw the console to clear off old title */ + ConioDrawConsole(ActiveConsole); + if (! DeviceIoControl(ConsoleDeviceHandle, IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER, + NULL, 0, Buffer, sizeof(COORD) + Title.Length, + &BytesReturned, NULL)) + { + DPRINT1( "Error writing to console\n" ); + } + HeapFree(Win32CsrApiHeap, 0, Buffer); + LeaveCriticalSection(&ActiveConsoleLock); + + return TRUE; + } + else if (NULL != SwapConsole) + { + EnterCriticalSection(&ActiveConsoleLock); + if (SwapConsole != ActiveConsole) + { + /* first remove swapconsole from the list */ + SwapConsole->Prev->Next = SwapConsole->Next; + SwapConsole->Next->Prev = SwapConsole->Prev; + /* now insert before activeconsole */ + SwapConsole->Next = ActiveConsole; + SwapConsole->Prev = ActiveConsole->Prev; + ActiveConsole->Prev->Next = SwapConsole; + ActiveConsole->Prev = SwapConsole; + } + ActiveConsole = SwapConsole; + SwapConsole = NULL; + ConioDrawConsole(ActiveConsole); + LeaveCriticalSection(&ActiveConsoleLock); + return TRUE; + } + else + { + return FALSE; + } +} + +/* EOF */ diff --git a/reactos/deprecated/win32csr/tuiconsole.h b/reactos/deprecated/win32csr/tuiconsole.h new file mode 100644 index 00000000000..c4012bd8107 --- /dev/null +++ b/reactos/deprecated/win32csr/tuiconsole.h @@ -0,0 +1,14 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/win32csr/tuiconsole.h + * PURPOSE: Interface to text-mode consoles + */ + +#include "api.h" + +extern NTSTATUS FASTCALL TuiInitConsole(PCSRSS_CONSOLE Console); +extern PCSRSS_CONSOLE FASTCALL TuiGetFocusConsole(VOID); +extern BOOL FASTCALL TuiSwapConsole(int Next); + +/* EOF */ diff --git a/reactos/deprecated/win32csr/w32csr.h b/reactos/deprecated/win32csr/w32csr.h new file mode 100644 index 00000000000..8759ebc6281 --- /dev/null +++ b/reactos/deprecated/win32csr/w32csr.h @@ -0,0 +1,46 @@ +/* PSDK/NDK Headers */ +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define NTOS_MODE_USER +#include +#include +#include +#include +#include +#include + +/* External Winlogon Header */ +#include + +/* Internal CSRSS Headers */ +#include +#include +#include +#include "guiconsole.h" +#include "tuiconsole.h" + +/* Public Win32K Headers */ +#include + +#include "resource.h" + +/* shared header with console.dll */ +#include "console.h" + +VOID +WINAPI +Win32CsrHardError( + IN PCSR_THREAD ThreadData, + IN PHARDERROR_MSG Message); + +/* EOF */ diff --git a/reactos/deprecated/win32csr/win32csr.h b/reactos/deprecated/win32csr/win32csr.h new file mode 100644 index 00000000000..9e68a0d46b6 --- /dev/null +++ b/reactos/deprecated/win32csr/win32csr.h @@ -0,0 +1,78 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: subsys/csrss/win32csr/win32csr.h + * PURPOSE: Interface to win32csr.dll + */ + +#pragma once + +#include + +extern HANDLE Win32CsrApiHeap; +extern HINSTANCE Win32CsrDllHandle; + +typedef struct Object_tt +{ + LONG Type; + struct tagCSRSS_CONSOLE *Console; + LONG AccessRead, AccessWrite; + LONG ExclusiveRead, ExclusiveWrite; + LONG HandleCount; +} Object_t; + +typedef struct _CSRSS_HANDLE +{ + Object_t *Object; + DWORD Access; + BOOL Inheritable; + DWORD ShareMode; +} CSRSS_HANDLE, *PCSRSS_HANDLE; + +typedef VOID (WINAPI *CSR_CLEANUP_OBJECT_PROC)(Object_t *Object); + +typedef struct tagCSRSS_OBJECT_DEFINITION +{ + LONG Type; + CSR_CLEANUP_OBJECT_PROC CsrCleanupObjectProc; +} CSRSS_OBJECT_DEFINITION, *PCSRSS_OBJECT_DEFINITION; + +/* handle.c */ +NTSTATUS FASTCALL Win32CsrInsertObject(PCSR_PROCESS ProcessData, + PHANDLE Handle, + Object_t *Object, + DWORD Access, + BOOL Inheritable, + DWORD ShareMode); +NTSTATUS FASTCALL Win32CsrLockObject(PCSR_PROCESS ProcessData, + HANDLE Handle, + Object_t **Object, + DWORD Access, + long Type); +VOID FASTCALL Win32CsrUnlockObject(Object_t *Object); +NTSTATUS FASTCALL Win32CsrReleaseObject(PCSR_PROCESS ProcessData, + HANDLE Object); +VOID WINAPI Win32CsrReleaseConsole(PCSR_PROCESS ProcessData); +NTSTATUS WINAPI Win32CsrDuplicateHandleTable(PCSR_PROCESS SourceProcessData, + PCSR_PROCESS TargetProcessData); +CSR_API(CsrGetHandle); +CSR_API(CsrCloseHandle); +CSR_API(CsrVerifyHandle); +CSR_API(CsrDuplicateHandle); +CSR_API(CsrGetInputWaitHandle); + +BOOL FASTCALL Win32CsrValidateBuffer(PCSR_PROCESS ProcessData, + PVOID Buffer, + SIZE_T NumElements, + SIZE_T ElementSize); +NTSTATUS FASTCALL Win32CsrEnumProcesses(CSRSS_ENUM_PROCESS_PROC EnumProc, + PVOID Context); + +/* exitros.c */ +CSR_API(CsrExitReactos); +CSR_API(CsrSetLogonNotifyWindow); +CSR_API(CsrRegisterLogonProcess); + +CSR_API(CsrSoundSentry); + +/* EOF */ diff --git a/reactos/deprecated/win32csr/win32csr.rc b/reactos/deprecated/win32csr/win32csr.rc new file mode 100644 index 00000000000..8ce91c6d7bd --- /dev/null +++ b/reactos/deprecated/win32csr/win32csr.rc @@ -0,0 +1,12 @@ +#include +#include + +#include "resource.h" + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "CSRSS subsystem usermode code\0" +#define REACTOS_STR_INTERNAL_NAME "win32csr\0" +#define REACTOS_STR_ORIGINAL_FILENAME "win32csr.dll\0" +#include + +#include "rsrc.rc" diff --git a/reactos/deprecated/win32csr/win32csr.spec b/reactos/deprecated/win32csr/win32csr.spec new file mode 100644 index 00000000000..025171277d1 --- /dev/null +++ b/reactos/deprecated/win32csr/win32csr.spec @@ -0,0 +1 @@ +@ stdcall Win32CsrInitialization(ptr)