You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
502 lines
14 KiB
502 lines
14 KiB
/*************************************************************************\
|
|
*
|
|
* icamsg.c
|
|
*
|
|
* Process ICA send message requests
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* $Author:
|
|
*
|
|
\*************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <dbt.h>
|
|
#include <ntdddisk.h>
|
|
#include "ntuser.h"
|
|
|
|
#include <winsta.h>
|
|
#include <wstmsg.h>
|
|
|
|
|
|
#define MAX_STRING_BYTES (512 * sizeof(WCHAR))
|
|
|
|
/*
|
|
* maximum messages (messagebox) a session can have pending.
|
|
*/
|
|
#define MAX_MESSAGES_PER_SESSION 25
|
|
|
|
NTSTATUS RemoteMessageThread(
|
|
PVOID pVoid);
|
|
|
|
VOID UserHardErrorEx(
|
|
PCSR_THREAD pt,
|
|
PHARDERROR_MSG pmsg,
|
|
PCTXHARDERRORINFO pCtxHEInfo);
|
|
|
|
NTSTATUS ReplyMessageToTerminalServer(
|
|
NTSTATUS ReplyStatus,
|
|
PNTSTATUS pStatus,
|
|
ULONG Response,
|
|
PULONG pResponse,
|
|
HANDLE hEvent);
|
|
|
|
PCTXHARDERRORINFO gpchiList;
|
|
HANDLE g_hDoMessageEvent;
|
|
ULONG PendingMessages;
|
|
|
|
/******************************************************************************\
|
|
* RemoteDoMessage
|
|
\******************************************************************************/
|
|
NTSTATUS
|
|
RemoteDoMessage(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
WINSTATIONSENDMESSAGEMSG * pSMsg = &pMsg->u.SendMessage;
|
|
PCTXHARDERRORINFO pchi;
|
|
NTSTATUS Status;
|
|
CLIENT_ID ClientId;
|
|
static HANDLE hMessageThread = NULL;
|
|
|
|
/*
|
|
* if termsrv rpc is going to wait we must have status and event.
|
|
* also if we are not going to wait, we must not have status and event.
|
|
*/
|
|
UserAssert(pSMsg->DoNotWait == (pSMsg->pStatus == 0));
|
|
UserAssert(pSMsg->DoNotWait == (pSMsg->hEvent == 0));
|
|
UserAssert(PendingMessages <= MAX_MESSAGES_PER_SESSION);
|
|
|
|
/*
|
|
* are we being flooded with messages?
|
|
*/
|
|
if (PendingMessages == MAX_MESSAGES_PER_SESSION) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/*
|
|
* Create list entry
|
|
*/
|
|
if ((pchi = LocalAlloc(LPTR, sizeof(CTXHARDERRORINFO))) == NULL) {
|
|
goto memError;
|
|
} else if ((pchi->pTitle = LocalAlloc(LPTR, pSMsg->TitleLength + sizeof(WCHAR))) == NULL) {
|
|
goto memError;
|
|
} else if ((pchi->pMessage = LocalAlloc(LPTR, pSMsg->MessageLength + sizeof(WCHAR))) == NULL) {
|
|
goto memError;
|
|
}
|
|
|
|
/*
|
|
* Increment our pending message count.
|
|
*/
|
|
EnterCrit();
|
|
PendingMessages++;
|
|
pchi->CountPending = TRUE;
|
|
|
|
/*
|
|
* Initialize
|
|
*/
|
|
pchi->ClientId = pMsg->h.ClientId;
|
|
pchi->MessageId = pMsg->MessageId;
|
|
pchi->Timeout = pSMsg->Timeout;
|
|
pchi->pStatus = pSMsg->pStatus;
|
|
pchi->pResponse = pSMsg->pResponse;
|
|
pchi->hEvent = pSMsg->hEvent;
|
|
pchi->DoNotWait = pSMsg->DoNotWait;
|
|
pchi->Style = pSMsg->Style;
|
|
pchi->DoNotWaitForCorrectDesktop = pSMsg->DoNotWaitForCorrectDesktop;
|
|
|
|
pchi->pTitle[pSMsg->TitleLength / sizeof(WCHAR)] = L'\0';
|
|
RtlCopyMemory(pchi->pTitle, pSMsg->pTitle, pSMsg->TitleLength);
|
|
|
|
pchi->pMessage[pSMsg->MessageLength / sizeof(WCHAR)] = L'\0';
|
|
RtlCopyMemory(pchi->pMessage, pSMsg->pMessage, pSMsg->MessageLength);
|
|
|
|
/*
|
|
* Link in at the head.
|
|
*/
|
|
pchi->pchiNext = gpchiList;
|
|
gpchiList = pchi;
|
|
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Start message thread if not running, otherwise signal thread.
|
|
*/
|
|
if (hMessageThread == NULL) {
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
0,
|
|
0,
|
|
RemoteMessageThread,
|
|
NULL,
|
|
&hMessageThread,
|
|
&ClientId);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
/*
|
|
* Add thread to server thread pool.
|
|
*/
|
|
CsrAddStaticServerThread(hMessageThread, &ClientId, 0);
|
|
NtResumeThread(hMessageThread, NULL);
|
|
} else {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Cannot start RemoteMessageThread, Status 0x%x",
|
|
Status);
|
|
}
|
|
} else {
|
|
if (g_hDoMessageEvent == NULL) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Status = NtSetEvent(g_hDoMessageEvent, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Error NtSetEvent failed, Status = 0x%x",
|
|
Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
memError:
|
|
|
|
if (pchi) {
|
|
if (pchi->pMessage) {
|
|
LocalFree(pchi->pMessage);
|
|
}
|
|
|
|
if (pchi->pTitle) {
|
|
LocalFree(pchi->pTitle);
|
|
}
|
|
|
|
LocalFree(pchi);
|
|
}
|
|
|
|
if (!pSMsg->DoNotWait) {
|
|
ReplyMessageToTerminalServer(
|
|
STATUS_NO_MEMORY,
|
|
pSMsg->pStatus,
|
|
0, // response is NA at this case, since we havent gotten one.
|
|
pSMsg->pResponse,
|
|
pSMsg->hEvent);
|
|
}
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* RemoteDoLoadStringNMessage
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
* STATUS_SUCCESS - successful
|
|
*
|
|
******************************************************************************/
|
|
|
|
NTSTATUS
|
|
RemoteDoLoadStringNMessage(
|
|
PWINSTATION_APIMSG pMsg)
|
|
{
|
|
WINSTATIONLOADSTRINGMSG * pSMsg = &pMsg->u.LoadStringMessage;
|
|
PCTXHARDERRORINFO pchi = NULL;
|
|
NTSTATUS Status;
|
|
CLIENT_ID ClientId;
|
|
static HANDLE hMessageThread = NULL;
|
|
WCHAR *szText = NULL;
|
|
WCHAR *szTitle = NULL;
|
|
WCHAR *FUSDisconnectMsg = NULL;
|
|
int cchTitle, cchMessage;
|
|
BOOL f;
|
|
|
|
/*
|
|
* If termsrv rpc is going to wait we must have status and event. Also
|
|
* if we are not going to wait, we must not have status and event.
|
|
*/
|
|
UserAssert(pSMsg->DoNotWait == (pSMsg->pStatus == 0));
|
|
UserAssert(pSMsg->DoNotWait == (pSMsg->hEvent == 0));
|
|
UserAssert(PendingMessages <= MAX_MESSAGES_PER_SESSION);
|
|
|
|
//
|
|
// Allocate the strings needed to display the Popup MessageBox.
|
|
//
|
|
if ((szTitle = LocalAlloc(LMEM_FIXED, MAX_STRING_BYTES)) == NULL) {
|
|
goto NoMem;
|
|
}
|
|
|
|
if ((FUSDisconnectMsg = LocalAlloc(LMEM_FIXED, MAX_STRING_BYTES)) == NULL) {
|
|
goto NoMem;
|
|
}
|
|
|
|
szText = ServerLoadString(ghModuleWin, pSMsg->TitleId, NULL, &f);
|
|
cchTitle = wsprintf(szTitle, L"%s", szText);
|
|
cchTitle = (cchTitle + 1) * sizeof(WCHAR);
|
|
|
|
szText = ServerLoadString(ghModuleWin, pSMsg->MessageId, NULL, &f);
|
|
cchMessage = wsprintf(FUSDisconnectMsg, L"%s\\%s %s", pSMsg->pDomain, pSMsg->pUserName, szText);
|
|
cchMessage = (cchMessage + 1) * sizeof(WCHAR);
|
|
|
|
/*
|
|
* Create list entry.
|
|
*/
|
|
if ((pchi = LocalAlloc(LPTR, sizeof(CTXHARDERRORINFO))) == NULL) {
|
|
goto NoMem;
|
|
} else if ((pchi->pTitle = LocalAlloc(LPTR, cchTitle + sizeof(WCHAR))) == NULL) {
|
|
goto NoMem;
|
|
} else if ((pchi->pMessage = LocalAlloc(LPTR, cchMessage + sizeof(WCHAR))) == NULL) {
|
|
goto NoMem;
|
|
}
|
|
|
|
/*
|
|
* Initialize.
|
|
*/
|
|
|
|
pchi->ClientId = pMsg->h.ClientId;
|
|
pchi->MessageId = pMsg->MessageId;
|
|
pchi->Timeout = pSMsg->Timeout;
|
|
pchi->pResponse = pSMsg->pResponse;
|
|
pchi->pStatus = pSMsg->pStatus;
|
|
pchi->hEvent = pSMsg->hEvent;
|
|
pchi->DoNotWait = pSMsg->DoNotWait;
|
|
pchi->Style = pSMsg->Style;
|
|
pchi->CountPending = FALSE;
|
|
pchi->DoNotWaitForCorrectDesktop = FALSE;
|
|
|
|
pchi->pTitle[cchTitle / sizeof(WCHAR)] = L'\0';
|
|
RtlCopyMemory(pchi->pTitle, szTitle, cchTitle);
|
|
|
|
pchi->pMessage[cchMessage / sizeof(WCHAR)] = L'\0';
|
|
RtlCopyMemory(pchi->pMessage, FUSDisconnectMsg, cchMessage);
|
|
|
|
/*
|
|
* Link in at the head.
|
|
*/
|
|
EnterCrit();
|
|
|
|
pchi->pchiNext = gpchiList;
|
|
gpchiList = pchi;
|
|
|
|
LeaveCrit();
|
|
|
|
LocalFree(szTitle);
|
|
LocalFree(FUSDisconnectMsg);
|
|
|
|
/*
|
|
* Start message thread if not running, otherwise signal thread.
|
|
*/
|
|
if (hMessageThread == NULL) {
|
|
Status = RtlCreateUserThread(NtCurrentProcess(),
|
|
NULL,
|
|
TRUE,
|
|
0,
|
|
0,
|
|
0,
|
|
RemoteMessageThread,
|
|
NULL,
|
|
&hMessageThread,
|
|
&ClientId);
|
|
if (NT_SUCCESS(Status)) {
|
|
/*
|
|
* Add thread to server thread pool.
|
|
*/
|
|
CsrAddStaticServerThread(hMessageThread, &ClientId, 0);
|
|
NtResumeThread(hMessageThread, NULL);
|
|
} else {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Cannot start RemoteMessageThread, Status 0x%x",
|
|
Status);
|
|
}
|
|
} else {
|
|
if (g_hDoMessageEvent == NULL) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Status = NtSetEvent(g_hDoMessageEvent, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSGF2(RIP_WARNING,
|
|
"NtSetEvent(0x%x) failed with Status 0x%x",
|
|
g_hDoMessageEvent,
|
|
Status);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
NoMem:
|
|
if (szTitle) {
|
|
LocalFree(szTitle);
|
|
}
|
|
|
|
if (FUSDisconnectMsg) {
|
|
LocalFree(FUSDisconnectMsg);
|
|
}
|
|
|
|
if (pchi) {
|
|
if (pchi->pMessage) {
|
|
LocalFree(pchi->pMessage);
|
|
}
|
|
|
|
if (pchi->pTitle) {
|
|
LocalFree(pchi->pTitle);
|
|
}
|
|
|
|
LocalFree(pchi);
|
|
}
|
|
|
|
if (!pSMsg->DoNotWait) {
|
|
ReplyMessageToTerminalServer(
|
|
STATUS_NO_MEMORY,
|
|
pSMsg->pStatus,
|
|
0, // response is NA at this case, since we havent gotten one.
|
|
pSMsg->pResponse,
|
|
pSMsg->hEvent);
|
|
}
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
|
|
/******************************************************************************\
|
|
* RemoteMessageThread
|
|
\******************************************************************************/
|
|
NTSTATUS RemoteMessageThread(
|
|
PVOID pVoid)
|
|
{
|
|
HARDERROR_MSG hemsg;
|
|
PCTXHARDERRORINFO pchi, *ppchi;
|
|
UNICODE_STRING Message, Title;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
|
|
UNREFERENCED_PARAMETER(pVoid);
|
|
|
|
/*
|
|
* Create sync event.
|
|
*/
|
|
InitializeObjectAttributes(&ObjA, NULL, 0, NULL, NULL);
|
|
Status = NtCreateEvent(&g_hDoMessageEvent, EVENT_ALL_ACCESS, &ObjA,
|
|
NotificationEvent, FALSE);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPMSGF1(RIP_WARNING, "NtCreateEvent failed, Status = 0x%x", Status);
|
|
goto Exit;
|
|
}
|
|
|
|
while (!gbExitInProgress) {
|
|
EnterCrit();
|
|
|
|
if (gpchiList != NULL) {
|
|
|
|
/*
|
|
* Find last entry
|
|
*/
|
|
for (ppchi = &gpchiList; *ppchi != NULL && (*ppchi)->pchiNext != NULL;
|
|
ppchi = &(*ppchi)->pchiNext) {
|
|
/* do nothing */;
|
|
}
|
|
|
|
/*
|
|
* Found it.
|
|
*/
|
|
if ((pchi = *ppchi) != NULL) {
|
|
|
|
/*
|
|
* Unlink from the list.
|
|
*/
|
|
for (ppchi = &gpchiList; *ppchi != NULL && *ppchi != pchi;
|
|
ppchi = &(*ppchi)->pchiNext) {
|
|
/* do nothing */;
|
|
}
|
|
|
|
if (*ppchi != NULL) {
|
|
*ppchi = pchi->pchiNext;
|
|
}
|
|
|
|
LeaveCrit();
|
|
|
|
/*
|
|
* Make strings unicode
|
|
*/
|
|
RtlInitUnicodeString(&Title, pchi->pTitle);
|
|
RtlInitUnicodeString(&Message, pchi->pMessage);
|
|
|
|
/*
|
|
* Initialize harderror message struct
|
|
*/
|
|
hemsg.h.ClientId = pchi->ClientId;
|
|
hemsg.Status = STATUS_SERVICE_NOTIFICATION;
|
|
hemsg.NumberOfParameters = 3;
|
|
hemsg.UnicodeStringParameterMask = 3;
|
|
hemsg.ValidResponseOptions = OptionOk;
|
|
hemsg.Parameters[0] = (ULONG_PTR)&Message;
|
|
hemsg.Parameters[1] = (ULONG_PTR)&Title;
|
|
hemsg.Parameters[2] = (ULONG_PTR)pchi->Style;
|
|
|
|
/*
|
|
* Place message in harderror queue.
|
|
*/
|
|
UserHardErrorEx(NULL, &hemsg, pchi);
|
|
} else {
|
|
LeaveCrit();
|
|
}
|
|
} else {
|
|
LeaveCrit();
|
|
}
|
|
|
|
if (gpchiList == NULL) {
|
|
UserAssert(g_hDoMessageEvent != NULL);
|
|
|
|
Status = NtWaitForSingleObject(g_hDoMessageEvent, FALSE, NULL);
|
|
|
|
UserAssert(NT_SUCCESS(Status));
|
|
|
|
NtResetEvent(g_hDoMessageEvent, NULL);
|
|
}
|
|
}
|
|
|
|
NtClose(g_hDoMessageEvent);
|
|
g_hDoMessageEvent = NULL;
|
|
|
|
Exit:
|
|
UserExitWorkerThread(Status);
|
|
return Status;
|
|
}
|
|
|
|
/******************************************************************************\
|
|
* HardErrorRemove
|
|
\******************************************************************************/
|
|
VOID HardErrorRemove(
|
|
PCTXHARDERRORINFO pchi)
|
|
{
|
|
/*
|
|
* Notify ICASRV's RPC thread if waiting.
|
|
*/
|
|
if (!pchi->DoNotWait) {
|
|
ReplyMessageToTerminalServer(
|
|
STATUS_SUCCESS,
|
|
pchi->pStatus,
|
|
pchi->Response,
|
|
pchi->pResponse,
|
|
pchi->hEvent);
|
|
}
|
|
|
|
EnterCrit();
|
|
|
|
if (pchi->CountPending) {
|
|
UserAssert(PendingMessages <= MAX_MESSAGES_PER_SESSION);
|
|
PendingMessages--;
|
|
}
|
|
|
|
LocalFree(pchi->pMessage);
|
|
LocalFree(pchi->pTitle);
|
|
LocalFree(pchi);
|
|
|
|
LeaveCrit();
|
|
}
|