xref: /reactos/win32ss/user/winsrv/consrv/console.c (revision cc439606)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Server DLL
4  * FILE:            win32ss/user/winsrv/consrv/console.c
5  * PURPOSE:         Console Management Functions
6  * PROGRAMMERS:     G� van Geldorp
7  *                  Jeffrey Morlan
8  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9  */
10 
11 /* INCLUDES *******************************************************************/
12 
13 #include "consrv.h"
14 
15 /* This is for COM usage */
16 #define COBJMACROS
17 #include <shlobj.h>
18 
19 
20 #include <alias.h>
21 #include <history.h>
22 #include "procinit.h"
23 
24 #define NDEBUG
25 #include <debug.h>
26 
27 
28 /* GLOBALS ********************************************************************/
29 
30 /* The list of the ConSrv consoles */
31 static ULONG ConsoleListSize;
32 static PCONSRV_CONSOLE* ConsoleList;
33 static RTL_RESOURCE ListLock;
34 
35 #define ConSrvLockConsoleListExclusive()    \
36     RtlAcquireResourceExclusive(&ListLock, TRUE)
37 
38 #define ConSrvLockConsoleListShared()       \
39     RtlAcquireResourceShared(&ListLock, TRUE)
40 
41 #define ConSrvUnlockConsoleList()           \
42     RtlReleaseResource(&ListLock)
43 
44 
45 static NTSTATUS
46 InsertConsole(OUT PHANDLE Handle,
47               IN PCONSRV_CONSOLE Console)
48 {
49 #define CONSOLE_HANDLES_INCREMENT   2 * 3
50 
51     NTSTATUS Status = STATUS_SUCCESS;
52     ULONG i = 0;
53     PCONSRV_CONSOLE* Block;
54 
55     ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
56             (ConsoleList != NULL && ConsoleListSize != 0) );
57 
58     /* All went right, so add the console to the list */
59     ConSrvLockConsoleListExclusive();
60     DPRINT("Insert in the list\n");
61 
62     if (ConsoleList)
63     {
64         for (i = 0; i < ConsoleListSize; i++)
65         {
66             if (ConsoleList[i] == NULL) break;
67         }
68     }
69 
70     if (i >= ConsoleListSize)
71     {
72         DPRINT("Creation of a new handles table\n");
73         /* Allocate a new handles table */
74         Block = ConsoleAllocHeap(HEAP_ZERO_MEMORY,
75                                  (ConsoleListSize +
76                                     CONSOLE_HANDLES_INCREMENT) * sizeof(PCONSRV_CONSOLE));
77         if (Block == NULL)
78         {
79             Status = STATUS_UNSUCCESSFUL;
80             goto Quit;
81         }
82 
83         /* If we previously had a handles table, free it and use the new one */
84         if (ConsoleList)
85         {
86             /* Copy the handles from the old table to the new one */
87             RtlCopyMemory(Block,
88                           ConsoleList,
89                           ConsoleListSize * sizeof(PCONSRV_CONSOLE));
90             ConsoleFreeHeap(ConsoleList);
91         }
92         ConsoleList = Block;
93         ConsoleListSize += CONSOLE_HANDLES_INCREMENT;
94     }
95 
96     ConsoleList[i] = Console;
97     *Handle = ULongToHandle((i << 2) | 0x3);
98 
99 Quit:
100     /* Unlock the console list and return status */
101     ConSrvUnlockConsoleList();
102     return Status;
103 }
104 
105 /* Unused */
106 #if 0
107 static NTSTATUS
108 RemoveConsoleByHandle(IN HANDLE Handle)
109 {
110     NTSTATUS Status = STATUS_SUCCESS;
111     PCONSRV_CONSOLE Console;
112 
113     BOOLEAN ValidHandle = ((HandleToULong(Handle) & 0x3) == 0x3);
114     ULONG Index = HandleToULong(Handle) >> 2;
115 
116     if (!ValidHandle) return STATUS_INVALID_HANDLE;
117 
118     ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
119             (ConsoleList != NULL && ConsoleListSize != 0) );
120 
121     /* Remove the console from the list */
122     ConSrvLockConsoleListExclusive();
123 
124     if (Index >= ConsoleListSize ||
125         (Console = ConsoleList[Index]) == NULL)
126     {
127         Status = STATUS_INVALID_HANDLE;
128         goto Quit;
129     }
130 
131     ConsoleList[Index] = NULL;
132 
133 Quit:
134     /* Unlock the console list and return status */
135     ConSrvUnlockConsoleList();
136     return Status;
137 }
138 #endif
139 
140 static NTSTATUS
141 RemoveConsoleByPointer(IN PCONSRV_CONSOLE Console)
142 {
143     ULONG i = 0;
144 
145     if (!Console) return STATUS_INVALID_PARAMETER;
146 
147     ASSERT( (ConsoleList == NULL && ConsoleListSize == 0) ||
148             (ConsoleList != NULL && ConsoleListSize != 0) );
149 
150     /* Remove the console from the list */
151     ConSrvLockConsoleListExclusive();
152 
153     if (ConsoleList)
154     {
155         for (i = 0; i < ConsoleListSize; i++)
156         {
157             if (ConsoleList[i] == Console) ConsoleList[i] = NULL;
158         }
159     }
160 
161     /* Unlock the console list and return */
162     ConSrvUnlockConsoleList();
163     return STATUS_SUCCESS;
164 }
165 
166 BOOLEAN NTAPI
167 ConSrvValidateConsole(OUT PCONSRV_CONSOLE* Console,
168                       IN HANDLE ConsoleHandle,
169                       IN CONSOLE_STATE ExpectedState,
170                       IN BOOLEAN LockConsole)
171 {
172     BOOLEAN RetVal = FALSE;
173     PCONSRV_CONSOLE ValidatedConsole;
174 
175     BOOLEAN ValidHandle = ((HandleToULong(ConsoleHandle) & 0x3) == 0x3);
176     ULONG Index = HandleToULong(ConsoleHandle) >> 2;
177 
178     if (!ValidHandle) return FALSE;
179 
180     if (!Console) return FALSE;
181     *Console = NULL;
182 
183     /*
184      * Forbid creation or deletion of consoles when
185      * checking for the existence of a console.
186      */
187     ConSrvLockConsoleListShared();
188 
189     if (Index >= ConsoleListSize ||
190         (ValidatedConsole = ConsoleList[Index]) == NULL)
191     {
192         /* Unlock the console list and return */
193         ConSrvUnlockConsoleList();
194         return FALSE;
195     }
196 
197     ValidatedConsole = ConsoleList[Index];
198 
199     /* Unlock the console list and return */
200     ConSrvUnlockConsoleList();
201 
202     RetVal = ConDrvValidateConsoleUnsafe((PCONSOLE)ValidatedConsole,
203                                          ExpectedState,
204                                          LockConsole);
205     if (RetVal) *Console = ValidatedConsole;
206 
207     return RetVal;
208 }
209 
210 
211 /* PRIVATE FUNCTIONS **********************************************************/
212 
213 // Adapted from reactos/lib/rtl/unicode.c, RtlCreateUnicodeString line 2180
214 static BOOLEAN
215 ConsoleCreateUnicodeString(IN OUT PUNICODE_STRING UniDest,
216                            IN PCWSTR Source)
217 {
218     SIZE_T Size = (wcslen(Source) + 1) * sizeof(WCHAR);
219     if (Size > MAXUSHORT) return FALSE;
220 
221     UniDest->Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Size);
222     if (UniDest->Buffer == NULL) return FALSE;
223 
224     RtlCopyMemory(UniDest->Buffer, Source, Size);
225     UniDest->MaximumLength = (USHORT)Size;
226     UniDest->Length = (USHORT)Size - sizeof(WCHAR);
227 
228     return TRUE;
229 }
230 
231 // Adapted from reactos/lib/rtl/unicode.c, RtlFreeUnicodeString line 431
232 static VOID
233 ConsoleFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
234 {
235     if (UnicodeString->Buffer)
236     {
237         ConsoleFreeHeap(UnicodeString->Buffer);
238         RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
239     }
240 }
241 
242 VOID
243 ConioPause(PCONSRV_CONSOLE Console, UINT Flags)
244 {
245     Console->PauseFlags |= Flags;
246     ConDrvPause((PCONSOLE)Console);
247 }
248 
249 VOID
250 ConioUnpause(PCONSRV_CONSOLE Console, UINT Flags)
251 {
252     Console->PauseFlags &= ~Flags;
253 
254     // if ((Console->PauseFlags & (PAUSED_FROM_KEYBOARD | PAUSED_FROM_SCROLLBAR | PAUSED_FROM_SELECTION)) == 0)
255     if (Console->PauseFlags == 0)
256     {
257         ConDrvUnpause((PCONSOLE)Console);
258 
259         CsrNotifyWait(&Console->WriteWaitQueue,
260                       TRUE,
261                       NULL,
262                       NULL);
263         if (!IsListEmpty(&Console->WriteWaitQueue))
264         {
265             CsrDereferenceWait(&Console->WriteWaitQueue);
266         }
267     }
268 }
269 
270 NTSTATUS
271 ConSrvGetConsole(IN PCONSOLE_PROCESS_DATA ProcessData,
272                  OUT PCONSRV_CONSOLE* Console,
273                  IN BOOLEAN LockConsole)
274 {
275     NTSTATUS Status = STATUS_INVALID_HANDLE;
276     PCONSRV_CONSOLE GrabConsole;
277 
278     // if (Console == NULL) return STATUS_INVALID_PARAMETER;
279     ASSERT(Console);
280     *Console = NULL;
281 
282     if (ConSrvValidateConsole(&GrabConsole,
283                               ProcessData->ConsoleHandle,
284                               CONSOLE_RUNNING,
285                               LockConsole))
286     {
287         InterlockedIncrement(&GrabConsole->ReferenceCount);
288         *Console = GrabConsole;
289         Status = STATUS_SUCCESS;
290     }
291 
292     return Status;
293 }
294 
295 VOID
296 ConSrvReleaseConsole(IN PCONSRV_CONSOLE Console,
297                      IN BOOLEAN IsConsoleLocked)
298 {
299     LONG RefCount = 0;
300 
301     if (!Console) return;
302     // if (Console->ReferenceCount == 0) return; // This shouldn't happen
303     ASSERT(Console->ReferenceCount > 0);
304 
305     /* The console must be locked */
306     // ASSERT(Console_locked);
307 
308     /*
309      * Decrement the reference count. Save the new value too,
310      * because Console->ReferenceCount might be modified after
311      * the console gets unlocked but before we check whether we
312      * can destroy it.
313      */
314     RefCount = _InterlockedDecrement(&Console->ReferenceCount);
315 
316     /* Unlock the console if needed */
317     if (IsConsoleLocked) LeaveCriticalSection(&Console->Lock);
318 
319     /* Delete the console if needed */
320     if (RefCount <= 0) ConSrvDeleteConsole(Console);
321 }
322 
323 
324 /* CONSOLE INITIALIZATION FUNCTIONS *******************************************/
325 
326 VOID NTAPI
327 ConSrvInitConsoleSupport(VOID)
328 {
329     DPRINT("CONSRV: ConSrvInitConsoleSupport()\n");
330 
331     /* Initialize the console list and its lock */
332     ConsoleListSize = 0;
333     ConsoleList = NULL;
334     RtlInitializeResource(&ListLock);
335 
336     /* Should call LoadKeyboardLayout */
337 }
338 
339 NTSTATUS NTAPI
340 ConSrvInitTerminal(IN OUT PTERMINAL Terminal,
341                    IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
342                    IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
343                    IN HANDLE ConsoleLeaderProcessHandle);
344 NTSTATUS NTAPI
345 ConSrvDeinitTerminal(IN OUT PTERMINAL Terminal);
346 
347 
348 static BOOL
349 LoadShellLinkConsoleInfo(IN OUT PCONSOLE_STATE_INFO ConsoleInfo,
350                          IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo)
351 {
352 #define PATH_SEPARATOR L'\\'
353 
354     BOOL    RetVal   = FALSE;
355     HRESULT hRes     = S_OK;
356     SIZE_T  Length   = 0;
357     LPWSTR  LinkName = NULL;
358     LPWSTR  IconPath = NULL;
359     WCHAR   Buffer[MAX_PATH + 1];
360 
361     ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
362 
363     if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
364     {
365         // return FALSE; // FIXME!! (for icon loading)
366         RetVal = TRUE;
367         goto Finish;
368     }
369 
370     /* 1- Find the last path separator if any */
371     LinkName = wcsrchr(ConsoleInfo->ConsoleTitle, PATH_SEPARATOR);
372     if (LinkName == NULL)
373         LinkName = ConsoleInfo->ConsoleTitle;
374     else
375         ++LinkName; // Skip the path separator
376 
377     /* 2- Check for the link extension. The name ".lnk" is considered invalid. */
378     Length = wcslen(LinkName);
379     if ( (Length <= 4) || (wcsicmp(LinkName + (Length - 4), L".lnk") != 0) )
380         return FALSE;
381 
382     /* 3- It may be a link. Try to retrieve some properties */
383     hRes = CoInitialize(NULL);
384     if (SUCCEEDED(hRes))
385     {
386         /* Get a pointer to the IShellLink interface */
387         IShellLinkW* pshl = NULL;
388         hRes = CoCreateInstance(&CLSID_ShellLink,
389                                 NULL,
390                                 CLSCTX_INPROC_SERVER,
391                                 &IID_IShellLinkW,
392                                 (LPVOID*)&pshl);
393         if (SUCCEEDED(hRes))
394         {
395             /* Get a pointer to the IPersistFile interface */
396             IPersistFile* ppf = NULL;
397             hRes = IPersistFile_QueryInterface(pshl, &IID_IPersistFile, (LPVOID*)&ppf);
398             if (SUCCEEDED(hRes))
399             {
400                 /* Load the shortcut */
401                 hRes = IPersistFile_Load(ppf, ConsoleInfo->ConsoleTitle, STGM_READ);
402                 if (SUCCEEDED(hRes))
403                 {
404                     /*
405                      * Finally we can get the properties !
406                      * Update the old ones if needed.
407                      */
408                     INT ShowCmd = 0;
409                     // WORD HotKey = 0;
410 
411                     /* Reset the name of the console with the name of the shortcut */
412                     Length = min(/*Length*/ Length - 4, // 4 == len(".lnk")
413                                  (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
414                     wcsncpy(ConsoleInfo->ConsoleTitle, LinkName, Length);
415                     ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL;
416 
417                     /* Get the window showing command */
418                     hRes = IShellLinkW_GetShowCmd(pshl, &ShowCmd);
419                     if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->wShowWindow = (WORD)ShowCmd;
420 
421                     /* Get the hotkey */
422                     // hRes = pshl->GetHotkey(&ShowCmd);
423                     // if (SUCCEEDED(hRes)) ConsoleInitInfo->ConsoleStartInfo->HotKey = HotKey;
424 
425                     /* Get the icon location, if any */
426                     hRes = IShellLinkW_GetIconLocation(pshl,
427                                                        Buffer,
428                                                        sizeof(Buffer)/sizeof(Buffer[0]) - 1, // == MAX_PATH
429                                                        &ConsoleInitInfo->ConsoleStartInfo->IconIndex);
430                     if (!SUCCEEDED(hRes))
431                     {
432                         ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
433                     }
434                     else
435                     {
436                         IconPath = Buffer;
437                     }
438 
439                     // FIXME: Since we still don't load console properties from the shortcut,
440                     // return false. When this will be done, we will return true instead.
441                     RetVal = TRUE; // FALSE;
442                 }
443                 IPersistFile_Release(ppf);
444             }
445             IShellLinkW_Release(pshl);
446         }
447     }
448     CoUninitialize();
449 
450 Finish:
451 
452     if (RetVal)
453     {
454         /* Get the associated icon, if any */
455         if (IconPath == NULL)
456         {
457             // Question: How to retrieve the full path name
458             // of the app we are going to run??
459             Length = RtlDosSearchPath_U(ConsoleInitInfo->CurDir,
460                                         ConsoleInitInfo->AppName,
461                                         NULL,
462                                         sizeof(Buffer),
463                                         Buffer,
464                                         NULL);
465             if (Length > 0 && Length < sizeof(Buffer))
466                 IconPath = Buffer;
467             else
468                 IconPath = ConsoleInitInfo->AppName;
469 
470             // ConsoleInitInfo->ConsoleStartInfo->IconIndex = 0;
471         }
472         DPRINT("IconPath = '%S' ; IconIndex = %lu\n",
473                IconPath, ConsoleInitInfo->ConsoleStartInfo->IconIndex);
474         if (IconPath && *IconPath)
475         {
476             HICON hIcon = NULL, hIconSm = NULL;
477             /*
478              * FIXME!! Because of a strange bug we have in PrivateExtractIconExW
479              * (see r65683 for more details), we cannot use this API to extract
480              * at the same time the large and small icons from the app.
481              * Instead we just use PrivateExtractIconsW.
482              *
483             PrivateExtractIconExW(IconPath,
484                                   ConsoleInitInfo->ConsoleStartInfo->IconIndex,
485                                   &hIcon,
486                                   &hIconSm,
487                                   1);
488              */
489             PrivateExtractIconsW(IconPath,
490                                  ConsoleInitInfo->ConsoleStartInfo->IconIndex,
491                                  32, 32,
492                                  &hIcon, NULL, 1, LR_COPYFROMRESOURCE);
493             PrivateExtractIconsW(IconPath,
494                                  ConsoleInitInfo->ConsoleStartInfo->IconIndex,
495                                  16, 16,
496                                  &hIconSm, NULL, 1, LR_COPYFROMRESOURCE);
497 
498             DPRINT("hIcon = 0x%p ; hIconSm = 0x%p\n", hIcon, hIconSm);
499             if (hIcon   != NULL) ConsoleInitInfo->ConsoleStartInfo->hIcon   = hIcon;
500             if (hIconSm != NULL) ConsoleInitInfo->ConsoleStartInfo->hIconSm = hIconSm;
501         }
502     }
503 
504     // FIXME: See the previous FIXME above.
505     RetVal = FALSE;
506 
507     return RetVal;
508 }
509 
510 NTSTATUS NTAPI
511 ConSrvInitConsole(OUT PHANDLE NewConsoleHandle,
512                   OUT PCONSRV_CONSOLE* NewConsole,
513                   IN OUT PCONSOLE_INIT_INFO ConsoleInitInfo,
514                   IN PCSR_PROCESS ConsoleLeaderProcess)
515 {
516     NTSTATUS Status;
517     HANDLE ConsoleHandle;
518     PCONSRV_CONSOLE Console;
519 
520     BYTE ConsoleInfoBuffer[sizeof(CONSOLE_STATE_INFO) + MAX_PATH * sizeof(WCHAR)]; // CONSRV console information
521     PCONSOLE_STATE_INFO ConsoleInfo = (PCONSOLE_STATE_INFO)&ConsoleInfoBuffer;
522     CONSOLE_INFO DrvConsoleInfo; // Console information for CONDRV
523 
524     SIZE_T Length = 0;
525 
526     TERMINAL Terminal; /* The ConSrv terminal for this console */
527 
528     if (NewConsole == NULL || ConsoleInitInfo == NULL)
529         return STATUS_INVALID_PARAMETER;
530 
531     *NewConsole = NULL;
532 
533     /*
534      * Load the console settings
535      */
536     RtlZeroMemory(ConsoleInfo, sizeof(ConsoleInfoBuffer));
537     ConsoleInfo->cbSize = sizeof(ConsoleInfoBuffer);
538 
539     /* 1. Get the title of the console (initialize ConsoleInfo->ConsoleTitle) */
540     Length = min(ConsoleInitInfo->TitleLength,
541                  (ConsoleInfo->cbSize - FIELD_OFFSET(CONSOLE_STATE_INFO, ConsoleTitle) - sizeof(UNICODE_NULL)) / sizeof(WCHAR));
542     wcsncpy(ConsoleInfo->ConsoleTitle, ConsoleInitInfo->ConsoleTitle, Length);
543     ConsoleInfo->ConsoleTitle[Length] = UNICODE_NULL; // NULL-terminate it.
544 
545     /* 2. Impersonate the caller in order to retrieve settings in its context */
546     if (!CsrImpersonateClient(NULL))
547         return STATUS_BAD_IMPERSONATION_LEVEL;
548 
549     /* 3. Load the default settings */
550     ConCfgGetDefaultSettings(ConsoleInfo);
551 
552     /*
553      * 4. Load per-application terminal settings.
554      *
555      * Check whether the process creating the console was launched via
556      * a shell-link. ConsoleInfo->ConsoleTitle may be updated with the
557      * name of the shortcut, and ConsoleStartInfo->Icon[Path|Index] too.
558      */
559     // if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) // FIXME!! (for icon loading)
560     {
561         if (!LoadShellLinkConsoleInfo(ConsoleInfo, ConsoleInitInfo))
562         {
563             ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags &= ~STARTF_TITLEISLINKNAME;
564         }
565     }
566 
567     /*
568      * 5. Load the remaining console settings via the registry.
569      */
570     if ((ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_TITLEISLINKNAME) == 0)
571     {
572         /*
573          * Either we weren't created by an app launched via a shell-link,
574          * or we failed to load shell-link console properties.
575          * Therefore, load the console infos for the application from the registry.
576          */
577         ConCfgReadUserSettings(ConsoleInfo, FALSE);
578 
579         /*
580          * Now, update them with the properties the user might gave to us
581          * via the STARTUPINFO structure before calling CreateProcess
582          * (and which was transmitted via the ConsoleStartInfo structure).
583          * We therefore overwrite the values read in the registry.
584          */
585         if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEFILLATTRIBUTE)
586         {
587             ConsoleInfo->ScreenAttributes = (USHORT)ConsoleInitInfo->ConsoleStartInfo->wFillAttribute;
588         }
589         if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USECOUNTCHARS)
590         {
591             ConsoleInfo->ScreenBufferSize = ConsoleInitInfo->ConsoleStartInfo->dwScreenBufferSize;
592         }
593         if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USESIZE)
594         {
595             ConsoleInfo->WindowSize = ConsoleInitInfo->ConsoleStartInfo->dwWindowSize;
596         }
597 
598 #if 0
599         /*
600          * Now, update them with the properties the user might gave to us
601          * via the STARTUPINFO structure before calling CreateProcess
602          * (and which was transmitted via the ConsoleStartInfo structure).
603          * We therefore overwrite the values read in the registry.
604          */
605         if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_USEPOSITION)
606         {
607             ConsoleInfo->AutoPosition = FALSE;
608             ConsoleInfo->WindowPosition.x = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.X;
609             ConsoleInfo->WindowPosition.y = ConsoleInitInfo->ConsoleStartInfo->dwWindowOrigin.Y;
610         }
611         if (ConsoleInitInfo->ConsoleStartInfo->dwStartupFlags & STARTF_RUNFULLSCREEN)
612         {
613             ConsoleInfo->FullScreen = TRUE;
614         }
615 #endif
616     }
617 
618     /* 6. Revert impersonation */
619     CsrRevertToSelf();
620 
621     /* Set-up the code page */
622     ConsoleInfo->CodePage = GetOEMCP();
623 
624     /*
625      * Initialize the ConSrv terminal and give it a chance to load
626      * its own settings and override the console settings.
627      */
628     Status = ConSrvInitTerminal(&Terminal,
629                                 ConsoleInfo,
630                                 ConsoleInitInfo,
631                                 ConsoleLeaderProcess->ProcessHandle);
632     if (!NT_SUCCESS(Status))
633     {
634         DPRINT1("CONSRV: Failed to initialize a terminal, Status = 0x%08lx\n", Status);
635         return Status;
636     }
637     DPRINT("CONSRV: Terminal initialized\n");
638 
639     /* Initialize a new console via the driver */
640     // DrvConsoleInfo.InputBufferSize = 0;
641     DrvConsoleInfo.ScreenBufferSize = ConsoleInfo->ScreenBufferSize;
642     DrvConsoleInfo.ConsoleSize = ConsoleInfo->WindowSize;
643     DrvConsoleInfo.CursorSize = ConsoleInfo->CursorSize;
644     // DrvConsoleInfo.CursorBlinkOn = ConsoleInfo->CursorBlinkOn;
645     DrvConsoleInfo.ScreenAttrib = ConsoleInfo->ScreenAttributes;
646     DrvConsoleInfo.PopupAttrib = ConsoleInfo->PopupAttributes;
647     DrvConsoleInfo.CodePage = ConsoleInfo->CodePage;
648     Status = ConDrvInitConsole(&Console, &DrvConsoleInfo);
649     if (!NT_SUCCESS(Status))
650     {
651         DPRINT1("Creating a new console failed, Status = 0x%08lx\n", Status);
652         ConSrvDeinitTerminal(&Terminal);
653         return Status;
654     }
655 
656     ASSERT(Console);
657     DPRINT("Console initialized\n");
658 
659     /*** Register ConSrv features ***/
660 
661     /* Initialize the console title */
662 #if 0
663     WCHAR DefaultTitle[128];
664 #endif
665     ConsoleCreateUnicodeString(&Console->OriginalTitle, ConsoleInfo->ConsoleTitle);
666 #if 0
667     if (ConsoleInfo->ConsoleTitle[0] == UNICODE_NULL)
668     {
669         if (LoadStringW(ConSrvDllInstance, IDS_CONSOLE_TITLE, DefaultTitle, sizeof(DefaultTitle) / sizeof(DefaultTitle[0])))
670         {
671             ConsoleCreateUnicodeString(&Console->Title, DefaultTitle);
672         }
673         else
674         {
675             ConsoleCreateUnicodeString(&Console->Title, L"ReactOS Console");
676         }
677     }
678     else
679     {
680 #endif
681         ConsoleCreateUnicodeString(&Console->Title, ConsoleInfo->ConsoleTitle);
682 #if 0
683     }
684 #endif
685 
686     /* Initialize process support */
687     InitializeListHead(&Console->ProcessList);
688     Console->NotifiedLastCloseProcess = NULL;
689     Console->NotifyLastClose = FALSE;
690     Console->HasFocus = FALSE;
691 
692     /* Initialize pausing support */
693     Console->PauseFlags = 0;
694     InitializeListHead(&Console->ReadWaitQueue);
695     InitializeListHead(&Console->WriteWaitQueue);
696 
697     /* Initialize the alias and history buffers */
698     Console->Aliases = NULL;
699     InitializeListHead(&Console->HistoryBuffers);
700     Console->HistoryBufferSize      = ConsoleInfo->HistoryBufferSize;
701     Console->NumberOfHistoryBuffers = ConsoleInfo->NumberOfHistoryBuffers;
702     Console->HistoryNoDup           = ConsoleInfo->HistoryNoDup;
703 
704     /* Initialize the Input Line Discipline */
705     Console->LineBuffer = NULL;
706     Console->LinePos = Console->LineMaxSize = Console->LineSize = 0;
707     Console->LineComplete = Console->LineUpPressed = FALSE;
708     // LineWakeupMask
709     Console->LineInsertToggle =
710     Console->InsertMode = ConsoleInfo->InsertMode;
711     Console->QuickEdit  = ConsoleInfo->QuickEdit;
712 
713     /* Popup windows */
714     InitializeListHead(&Console->PopupWindows);
715 
716     /* Colour table */
717     RtlCopyMemory(Console->Colors, ConsoleInfo->ColorTable,
718                   sizeof(ConsoleInfo->ColorTable));
719 
720     /* Create the Initialization Events */
721     Status = NtCreateEvent(&Console->InitEvents[INIT_SUCCESS], EVENT_ALL_ACCESS,
722                            NULL, NotificationEvent, FALSE);
723     if (!NT_SUCCESS(Status))
724     {
725         DPRINT1("NtCreateEvent(InitEvents[INIT_SUCCESS]) failed: %lu\n", Status);
726         ConDrvDeleteConsole(Console);
727         ConSrvDeinitTerminal(&Terminal);
728         return Status;
729     }
730     Status = NtCreateEvent(&Console->InitEvents[INIT_FAILURE], EVENT_ALL_ACCESS,
731                            NULL, NotificationEvent, FALSE);
732     if (!NT_SUCCESS(Status))
733     {
734         DPRINT1("NtCreateEvent(InitEvents[INIT_FAILURE]) failed: %lu\n", Status);
735         NtClose(Console->InitEvents[INIT_SUCCESS]);
736         ConDrvDeleteConsole(Console);
737         ConSrvDeinitTerminal(&Terminal);
738         return Status;
739     }
740 
741     /*
742      * Attach the ConSrv terminal to the console.
743      * This call makes a copy of our local Terminal variable.
744      */
745     Status = ConDrvAttachTerminal(Console, &Terminal);
746     if (!NT_SUCCESS(Status))
747     {
748         DPRINT1("Failed to register terminal to the given console, Status = 0x%08lx\n", Status);
749         NtClose(Console->InitEvents[INIT_FAILURE]);
750         NtClose(Console->InitEvents[INIT_SUCCESS]);
751         ConDrvDeleteConsole(Console);
752         ConSrvDeinitTerminal(&Terminal);
753         return Status;
754     }
755     DPRINT("Terminal attached\n");
756 
757     /* All went right, so add the console to the list */
758     Status = InsertConsole(&ConsoleHandle, Console);
759 
760     // FIXME! We do not support at all asynchronous console creation!
761     NtSetEvent(Console->InitEvents[INIT_SUCCESS], NULL);
762     // NtSetEvent(Console->InitEvents[INIT_FAILURE], NULL);
763 
764     /* Return the newly created console to the caller and a success code too */
765     *NewConsoleHandle = ConsoleHandle;
766     *NewConsole       = Console;
767     return STATUS_SUCCESS;
768 }
769 
770 VOID NTAPI
771 ConSrvDeleteConsole(PCONSRV_CONSOLE Console)
772 {
773     DPRINT("ConSrvDeleteConsole\n");
774 
775     // FIXME: Send a terminate message to all the processes owning this console
776 
777     /* Remove the console from the list */
778     RemoveConsoleByPointer(Console);
779 
780     /* Destroy the Initialization Events */
781     NtClose(Console->InitEvents[INIT_FAILURE]);
782     NtClose(Console->InitEvents[INIT_SUCCESS]);
783 
784     /* Clean the Input Line Discipline */
785     if (Console->LineBuffer) ConsoleFreeHeap(Console->LineBuffer);
786 
787     /* Clean aliases and history */
788     IntDeleteAllAliases(Console);
789     HistoryDeleteBuffers(Console);
790 
791     /* Free the console title */
792     ConsoleFreeUnicodeString(&Console->OriginalTitle);
793     ConsoleFreeUnicodeString(&Console->Title);
794 
795     /* Now, call the driver. ConDrvDetachTerminal is called on-demand. */
796     ConDrvDeleteConsole((PCONSOLE)Console);
797 
798     /* Deinit the ConSrv terminal */
799     // FIXME!!
800     // ConSrvDeinitTerminal(&Terminal);
801 }
802 
803 
804 
805 
806 
807 
808 static NTSTATUS
809 ConSrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
810                               IN PCONSOLE_PROCESS_DATA ProcessData,
811                               IN ULONG Timeout)
812 {
813     NTSTATUS Status = STATUS_SUCCESS;
814 
815     DPRINT("ConSrvConsoleCtrlEventTimeout Parent ProcessId = %x\n", ProcessData->Process->ClientId.UniqueProcess);
816 
817     /*
818      * Be sure we effectively have a control routine. It resides in kernel32.dll (client).
819      */
820     if (ProcessData->CtrlRoutine == NULL) return Status;
821 
822     _SEH2_TRY
823     {
824         HANDLE Thread = NULL;
825 
826         _SEH2_TRY
827         {
828             Thread = CreateRemoteThread(ProcessData->Process->ProcessHandle, NULL, 0,
829                                         ProcessData->CtrlRoutine,
830                                         UlongToPtr(CtrlEvent), 0, NULL);
831             if (NULL == Thread)
832             {
833                 Status = RtlGetLastNtStatus();
834                 DPRINT1("Failed thread creation, Status = 0x%08lx\n", Status);
835             }
836             else
837             {
838                 DPRINT("ProcessData->CtrlRoutine remote thread creation succeeded, ProcessId = %x, Process = 0x%p\n",
839                        ProcessData->Process->ClientId.UniqueProcess, ProcessData->Process);
840                 WaitForSingleObject(Thread, Timeout);
841             }
842         }
843         _SEH2_FINALLY
844         {
845             CloseHandle(Thread);
846         }
847         _SEH2_END;
848     }
849     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
850     {
851         Status = _SEH2_GetExceptionCode();
852         DPRINT1("ConSrvConsoleCtrlEventTimeout - Caught an exception, Status = 0x%08lx\n", Status);
853     }
854     _SEH2_END;
855 
856     return Status;
857 }
858 
859 NTSTATUS
860 ConSrvConsoleCtrlEvent(IN ULONG CtrlEvent,
861                        IN PCONSOLE_PROCESS_DATA ProcessData)
862 {
863     return ConSrvConsoleCtrlEventTimeout(CtrlEvent, ProcessData, 0);
864 }
865 
866 PCONSOLE_PROCESS_DATA NTAPI
867 ConSrvGetConsoleLeaderProcess(IN PCONSRV_CONSOLE Console)
868 {
869     if (Console == NULL) return NULL;
870 
871     return CONTAINING_RECORD(Console->ProcessList.Blink,
872                              CONSOLE_PROCESS_DATA,
873                              ConsoleLink);
874 }
875 
876 NTSTATUS NTAPI
877 ConSrvGetConsoleProcessList(IN PCONSRV_CONSOLE Console,
878                             IN OUT PULONG ProcessIdsList,
879                             IN ULONG MaxIdListItems,
880                             OUT PULONG ProcessIdsTotal)
881 {
882     PCONSOLE_PROCESS_DATA current;
883     PLIST_ENTRY current_entry;
884 
885     if (Console == NULL || ProcessIdsList == NULL || ProcessIdsTotal == NULL)
886         return STATUS_INVALID_PARAMETER;
887 
888     *ProcessIdsTotal = 0;
889 
890     for (current_entry = Console->ProcessList.Flink;
891          current_entry != &Console->ProcessList;
892          current_entry = current_entry->Flink)
893     {
894         current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
895         if (++(*ProcessIdsTotal) <= MaxIdListItems)
896         {
897             *ProcessIdsList++ = HandleToUlong(current->Process->ClientId.UniqueProcess);
898         }
899     }
900 
901     return STATUS_SUCCESS;
902 }
903 
904 // ConSrvGenerateConsoleCtrlEvent
905 NTSTATUS NTAPI
906 ConSrvConsoleProcessCtrlEvent(IN PCONSRV_CONSOLE Console,
907                               IN ULONG ProcessGroupId,
908                               IN ULONG CtrlEvent)
909 {
910     NTSTATUS Status = STATUS_SUCCESS;
911     PLIST_ENTRY current_entry;
912     PCONSOLE_PROCESS_DATA current;
913 
914     /* If the console is already being destroyed, just return */
915     if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
916         return STATUS_UNSUCCESSFUL;
917 
918     /*
919      * Loop through the process list, from the most recent process
920      * (the active one) to the oldest one (the first created, i.e.
921      * the console leader process), and for each, send an event
922      * (new processes are inserted at the head of the console process list).
923      */
924     current_entry = Console->ProcessList.Flink;
925     while (current_entry != &Console->ProcessList)
926     {
927         current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
928         current_entry = current_entry->Flink;
929 
930         /*
931          * Only processes belonging to the same process group are signaled.
932          * If the process group ID is zero, then all the processes are signaled.
933          */
934         if (ProcessGroupId == 0 || current->Process->ProcessGroupId == ProcessGroupId)
935         {
936             Status = ConSrvConsoleCtrlEvent(CtrlEvent, current);
937         }
938     }
939 
940     return Status;
941 }
942 
943 VOID
944 ConSrvSetProcessFocus(IN PCSR_PROCESS CsrProcess,
945                       IN BOOLEAN SetForeground)
946 {
947     // FIXME: Call NtUserSetInformationProcess (currently unimplemented!)
948     // for setting Win32 foreground/background flags.
949 
950     if (SetForeground)
951         CsrSetForegroundPriority(CsrProcess);
952     else
953         CsrSetBackgroundPriority(CsrProcess);
954 }
955 
956 NTSTATUS NTAPI
957 ConSrvSetConsoleProcessFocus(IN PCONSRV_CONSOLE Console,
958                              IN BOOLEAN SetForeground)
959 {
960     PLIST_ENTRY current_entry;
961     PCONSOLE_PROCESS_DATA current;
962 
963     /* If the console is already being destroyed, just return */
964     if (!ConDrvValidateConsoleState((PCONSOLE)Console, CONSOLE_RUNNING))
965         return STATUS_UNSUCCESSFUL;
966 
967     /*
968      * Loop through the process list, from the most recent process
969      * to the oldest one, and for each, set its foreground priority.
970      */
971     current_entry = Console->ProcessList.Flink;
972     while (current_entry != &Console->ProcessList)
973     {
974         current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
975         current_entry = current_entry->Flink;
976 
977         ConSrvSetProcessFocus(current->Process, SetForeground);
978     }
979 
980     return STATUS_SUCCESS;
981 }
982 
983 
984 /* PUBLIC SERVER APIS *********************************************************/
985 
986 CSR_API(SrvAllocConsole)
987 {
988     NTSTATUS Status = STATUS_SUCCESS;
989     PCONSOLE_ALLOCCONSOLE AllocConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AllocConsoleRequest;
990     PCSR_PROCESS CsrProcess = CsrGetClientThread()->Process;
991     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrProcess);
992     CONSOLE_INIT_INFO ConsoleInitInfo;
993 
994     if (ProcessData->ConsoleHandle != NULL)
995     {
996         DPRINT1("Process already has a console\n");
997         return STATUS_ACCESS_DENIED;
998     }
999 
1000     if ( !CsrValidateMessageBuffer(ApiMessage,
1001                                    (PVOID*)&AllocConsoleRequest->ConsoleStartInfo,
1002                                    1,
1003                                    sizeof(CONSOLE_START_INFO))      ||
1004          !CsrValidateMessageBuffer(ApiMessage,
1005                                    (PVOID*)&AllocConsoleRequest->ConsoleTitle,
1006                                    AllocConsoleRequest->TitleLength,
1007                                    sizeof(BYTE))                    ||
1008          !CsrValidateMessageBuffer(ApiMessage,
1009                                    (PVOID*)&AllocConsoleRequest->Desktop,
1010                                    AllocConsoleRequest->DesktopLength,
1011                                    sizeof(BYTE))                    ||
1012          !CsrValidateMessageBuffer(ApiMessage,
1013                                    (PVOID*)&AllocConsoleRequest->CurDir,
1014                                    AllocConsoleRequest->CurDirLength,
1015                                    sizeof(BYTE))                    ||
1016          !CsrValidateMessageBuffer(ApiMessage,
1017                                    (PVOID*)&AllocConsoleRequest->AppName,
1018                                    AllocConsoleRequest->AppNameLength,
1019                                    sizeof(BYTE)) )
1020     {
1021         return STATUS_INVALID_PARAMETER;
1022     }
1023 
1024     /* Initialize the console initialization info structure */
1025     ConsoleInitInfo.ConsoleStartInfo = AllocConsoleRequest->ConsoleStartInfo;
1026     ConsoleInitInfo.IsWindowVisible  = TRUE; // The console window is always visible.
1027     ConsoleInitInfo.TitleLength      = AllocConsoleRequest->TitleLength;
1028     ConsoleInitInfo.ConsoleTitle     = AllocConsoleRequest->ConsoleTitle;
1029     ConsoleInitInfo.DesktopLength    = AllocConsoleRequest->DesktopLength;
1030     ConsoleInitInfo.Desktop          = AllocConsoleRequest->Desktop;
1031     ConsoleInitInfo.AppNameLength    = AllocConsoleRequest->AppNameLength;
1032     ConsoleInitInfo.AppName          = AllocConsoleRequest->AppName;
1033     ConsoleInitInfo.CurDirLength     = AllocConsoleRequest->CurDirLength;
1034     ConsoleInitInfo.CurDir           = AllocConsoleRequest->CurDir;
1035 
1036     /* Initialize a new Console owned by the Console Leader Process */
1037     Status = ConSrvAllocateConsole(ProcessData,
1038                                    &AllocConsoleRequest->ConsoleStartInfo->InputHandle,
1039                                    &AllocConsoleRequest->ConsoleStartInfo->OutputHandle,
1040                                    &AllocConsoleRequest->ConsoleStartInfo->ErrorHandle,
1041                                    &ConsoleInitInfo);
1042     if (!NT_SUCCESS(Status))
1043     {
1044         DPRINT1("Console allocation failed\n");
1045         return Status;
1046     }
1047 
1048     /* Set the Property-Dialog and Control-Dispatcher handlers */
1049     ProcessData->PropRoutine = AllocConsoleRequest->PropRoutine;
1050     ProcessData->CtrlRoutine = AllocConsoleRequest->CtrlRoutine;
1051 
1052     return STATUS_SUCCESS;
1053 }
1054 
1055 CSR_API(SrvAttachConsole)
1056 {
1057     NTSTATUS Status = STATUS_SUCCESS;
1058     PCONSOLE_ATTACHCONSOLE AttachConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.AttachConsoleRequest;
1059     PCSR_PROCESS SourceProcess = NULL;  // The parent process.
1060     PCSR_PROCESS TargetProcess = CsrGetClientThread()->Process; // Ourselves.
1061     HANDLE ProcessId = ULongToHandle(AttachConsoleRequest->ProcessId);
1062     PCONSOLE_PROCESS_DATA SourceProcessData, TargetProcessData;
1063 
1064     TargetProcessData = ConsoleGetPerProcessData(TargetProcess);
1065 
1066     if (TargetProcessData->ConsoleHandle != NULL)
1067     {
1068         DPRINT1("Process already has a console\n");
1069         return STATUS_ACCESS_DENIED;
1070     }
1071 
1072     if (!CsrValidateMessageBuffer(ApiMessage,
1073                                   (PVOID*)&AttachConsoleRequest->ConsoleStartInfo,
1074                                   1,
1075                                   sizeof(CONSOLE_START_INFO)))
1076     {
1077         return STATUS_INVALID_PARAMETER;
1078     }
1079 
1080     /* Check whether we try to attach to the parent's console */
1081     if (ProcessId == ULongToHandle(ATTACH_PARENT_PROCESS))
1082     {
1083         PROCESS_BASIC_INFORMATION ProcessInfo;
1084         ULONG Length = sizeof(ProcessInfo);
1085 
1086         /* Get the real parent's PID */
1087 
1088         Status = NtQueryInformationProcess(TargetProcess->ProcessHandle,
1089                                            ProcessBasicInformation,
1090                                            &ProcessInfo,
1091                                            Length, &Length);
1092         if (!NT_SUCCESS(Status))
1093         {
1094             DPRINT1("SrvAttachConsole - Cannot retrieve basic process info, Status = 0x%08lx\n", Status);
1095             return Status;
1096         }
1097 
1098         ProcessId = ULongToHandle(ProcessInfo.InheritedFromUniqueProcessId);
1099     }
1100 
1101     /* Lock the source process via its PID */
1102     Status = CsrLockProcessByClientId(ProcessId, &SourceProcess);
1103     if (!NT_SUCCESS(Status)) return Status;
1104 
1105     SourceProcessData = ConsoleGetPerProcessData(SourceProcess);
1106 
1107     if (SourceProcessData->ConsoleHandle == NULL)
1108     {
1109         Status = STATUS_INVALID_HANDLE;
1110         goto Quit;
1111     }
1112 
1113     /*
1114      * Inherit the console from the parent,
1115      * if any, otherwise return an error.
1116      */
1117     Status = ConSrvInheritConsole(TargetProcessData,
1118                                   SourceProcessData->ConsoleHandle,
1119                                   TRUE,
1120                                   &AttachConsoleRequest->ConsoleStartInfo->InputHandle,
1121                                   &AttachConsoleRequest->ConsoleStartInfo->OutputHandle,
1122                                   &AttachConsoleRequest->ConsoleStartInfo->ErrorHandle,
1123                                   AttachConsoleRequest->ConsoleStartInfo);
1124     if (!NT_SUCCESS(Status))
1125     {
1126         DPRINT1("Console inheritance failed\n");
1127         goto Quit;
1128     }
1129 
1130     /* Set the Property-Dialog and Control-Dispatcher handlers */
1131     TargetProcessData->PropRoutine = AttachConsoleRequest->PropRoutine;
1132     TargetProcessData->CtrlRoutine = AttachConsoleRequest->CtrlRoutine;
1133 
1134     Status = STATUS_SUCCESS;
1135 
1136 Quit:
1137     /* Unlock the "source" process and exit */
1138     CsrUnlockProcess(SourceProcess);
1139     return Status;
1140 }
1141 
1142 CSR_API(SrvFreeConsole)
1143 {
1144     return ConSrvRemoveConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process));
1145 }
1146 
1147 NTSTATUS NTAPI
1148 ConDrvGetConsoleMode(IN PCONSOLE Console,
1149                      IN PCONSOLE_IO_OBJECT Object,
1150                      OUT PULONG ConsoleMode);
1151 CSR_API(SrvGetConsoleMode)
1152 {
1153     NTSTATUS Status;
1154     PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1155     PCONSOLE_IO_OBJECT Object;
1156 
1157     PULONG ConsoleMode = &ConsoleModeRequest->Mode;
1158 
1159     Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1160                              ConsoleModeRequest->Handle,
1161                              &Object, NULL, GENERIC_READ, TRUE, 0);
1162     if (!NT_SUCCESS(Status)) return Status;
1163 
1164     /* Get the standard console modes */
1165     Status = ConDrvGetConsoleMode(Object->Console, Object,
1166                                   ConsoleMode);
1167     if (NT_SUCCESS(Status))
1168     {
1169         /*
1170          * If getting the console modes succeeds, then retrieve
1171          * the extended CONSRV-specific input modes.
1172          */
1173         if (INPUT_BUFFER == Object->Type)
1174         {
1175             if (Object->Console->InsertMode || Object->Console->QuickEdit)
1176             {
1177                 /* Windows also adds ENABLE_EXTENDED_FLAGS, even if it's not documented on MSDN */
1178                 *ConsoleMode |= ENABLE_EXTENDED_FLAGS;
1179 
1180                 if (Object->Console->InsertMode) *ConsoleMode |= ENABLE_INSERT_MODE;
1181                 if (Object->Console->QuickEdit ) *ConsoleMode |= ENABLE_QUICK_EDIT_MODE;
1182             }
1183         }
1184     }
1185 
1186     ConSrvReleaseObject(Object, TRUE);
1187     return Status;
1188 }
1189 
1190 NTSTATUS NTAPI
1191 ConDrvSetConsoleMode(IN PCONSOLE Console,
1192                      IN PCONSOLE_IO_OBJECT Object,
1193                      IN ULONG ConsoleMode);
1194 CSR_API(SrvSetConsoleMode)
1195 {
1196 #define CONSOLE_VALID_CONTROL_MODES ( ENABLE_EXTENDED_FLAGS | \
1197                                       ENABLE_INSERT_MODE    | ENABLE_QUICK_EDIT_MODE )
1198 
1199     NTSTATUS Status;
1200     PCONSOLE_GETSETCONSOLEMODE ConsoleModeRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ConsoleModeRequest;
1201     PCONSOLE_IO_OBJECT Object;
1202 
1203     ULONG ConsoleMode = ConsoleModeRequest->Mode;
1204 
1205     Status = ConSrvGetObject(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
1206                              ConsoleModeRequest->Handle,
1207                              &Object, NULL, GENERIC_WRITE, TRUE, 0);
1208     if (!NT_SUCCESS(Status)) return Status;
1209 
1210     /* Set the standard console modes (without the CONSRV-specific input modes) */
1211     ConsoleMode &= ~CONSOLE_VALID_CONTROL_MODES; // Remove CONSRV-specific input modes.
1212     Status = ConDrvSetConsoleMode(Object->Console, Object,
1213                                   ConsoleMode);
1214     if (NT_SUCCESS(Status))
1215     {
1216         /*
1217          * If setting the console modes succeeds, then set
1218          * the extended CONSRV-specific input modes.
1219          */
1220         if (INPUT_BUFFER == Object->Type)
1221         {
1222             ConsoleMode = ConsoleModeRequest->Mode;
1223 
1224             if (ConsoleMode & CONSOLE_VALID_CONTROL_MODES)
1225             {
1226                 /*
1227                  * If we use control mode flags without ENABLE_EXTENDED_FLAGS,
1228                  * then consider the flags invalid.
1229                  */
1230                 if ((ConsoleMode & ENABLE_EXTENDED_FLAGS) == 0)
1231                 {
1232                     Status = STATUS_INVALID_PARAMETER;
1233                 }
1234                 else
1235                 {
1236                     Object->Console->InsertMode = !!(ConsoleMode & ENABLE_INSERT_MODE);
1237                     Object->Console->QuickEdit  = !!(ConsoleMode & ENABLE_QUICK_EDIT_MODE);
1238                 }
1239             }
1240         }
1241     }
1242 
1243     ConSrvReleaseObject(Object, TRUE);
1244     return Status;
1245 }
1246 
1247 CSR_API(SrvGetConsoleTitle)
1248 {
1249     NTSTATUS Status;
1250     PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1251     PCONSRV_CONSOLE Console;
1252     ULONG Length;
1253 
1254     if (!CsrValidateMessageBuffer(ApiMessage,
1255                                   (PVOID)&TitleRequest->Title,
1256                                   TitleRequest->Length,
1257                                   sizeof(BYTE)))
1258     {
1259         return STATUS_INVALID_PARAMETER;
1260     }
1261 
1262     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1263     if (!NT_SUCCESS(Status))
1264     {
1265         DPRINT1("Can't get console, status %lx\n", Status);
1266         return Status;
1267     }
1268 
1269     /* Copy title of the console to the user title buffer */
1270     if (TitleRequest->Unicode)
1271     {
1272         if (TitleRequest->Length >= sizeof(WCHAR))
1273         {
1274             Length = min(TitleRequest->Length - sizeof(WCHAR), Console->Title.Length);
1275             RtlCopyMemory(TitleRequest->Title, Console->Title.Buffer, Length);
1276             ((PWCHAR)TitleRequest->Title)[Length / sizeof(WCHAR)] = UNICODE_NULL;
1277             TitleRequest->Length = Length;
1278         }
1279         else
1280         {
1281             TitleRequest->Length = Console->Title.Length;
1282         }
1283     }
1284     else
1285     {
1286         if (TitleRequest->Length >= sizeof(CHAR))
1287         {
1288             Length = min(TitleRequest->Length - sizeof(CHAR), Console->Title.Length / sizeof(WCHAR));
1289             Length = WideCharToMultiByte(Console->InputCodePage, 0,
1290                                          Console->Title.Buffer, Length,
1291                                          TitleRequest->Title, Length,
1292                                          NULL, NULL);
1293             ((PCHAR)TitleRequest->Title)[Length] = ANSI_NULL;
1294             TitleRequest->Length = Length;
1295         }
1296         else
1297         {
1298             TitleRequest->Length = Console->Title.Length / sizeof(WCHAR);
1299         }
1300     }
1301 
1302     ConSrvReleaseConsole(Console, TRUE);
1303     return Status;
1304 }
1305 
1306 CSR_API(SrvSetConsoleTitle)
1307 {
1308     NTSTATUS Status;
1309     PCONSOLE_GETSETCONSOLETITLE TitleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.TitleRequest;
1310     PCONSRV_CONSOLE Console;
1311 
1312     PWCHAR Buffer;
1313     ULONG  Length;
1314 
1315     if (!CsrValidateMessageBuffer(ApiMessage,
1316                                   (PVOID)&TitleRequest->Title,
1317                                   TitleRequest->Length,
1318                                   sizeof(BYTE)))
1319     {
1320         return STATUS_INVALID_PARAMETER;
1321     }
1322 
1323     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1324     if (!NT_SUCCESS(Status))
1325     {
1326         DPRINT1("Can't get console, status %lx\n", Status);
1327         return Status;
1328     }
1329 
1330     if (TitleRequest->Unicode)
1331     {
1332         /* Length is in bytes */
1333         Length = TitleRequest->Length;
1334     }
1335     else
1336     {
1337         /* Use the console input CP for the conversion */
1338         Length = MultiByteToWideChar(Console->InputCodePage, 0,
1339                                      TitleRequest->Title, TitleRequest->Length,
1340                                      NULL, 0);
1341         /* The returned Length was in number of wchars, convert it in bytes */
1342         Length *= sizeof(WCHAR);
1343     }
1344 
1345     /* Allocate a new buffer to hold the new title (NULL-terminated) */
1346     Buffer = ConsoleAllocHeap(HEAP_ZERO_MEMORY, Length + sizeof(WCHAR));
1347     if (!Buffer)
1348     {
1349         Status = STATUS_NO_MEMORY;
1350         goto Quit;
1351     }
1352 
1353     /* Free the old title */
1354     ConsoleFreeUnicodeString(&Console->Title);
1355 
1356     /* Copy title to console */
1357     Console->Title.Buffer = Buffer;
1358     Console->Title.Length = Length;
1359     Console->Title.MaximumLength = Console->Title.Length + sizeof(WCHAR);
1360 
1361     if (TitleRequest->Unicode)
1362     {
1363         RtlCopyMemory(Console->Title.Buffer, TitleRequest->Title, Console->Title.Length);
1364     }
1365     else
1366     {
1367         MultiByteToWideChar(Console->InputCodePage, 0,
1368                             TitleRequest->Title, TitleRequest->Length,
1369                             Console->Title.Buffer,
1370                             Console->Title.Length / sizeof(WCHAR));
1371     }
1372 
1373     /* NULL-terminate */
1374     Console->Title.Buffer[Console->Title.Length / sizeof(WCHAR)] = UNICODE_NULL;
1375 
1376     TermChangeTitle(Console);
1377     Status = STATUS_SUCCESS;
1378 
1379 Quit:
1380     ConSrvReleaseConsole(Console, TRUE);
1381     return Status;
1382 }
1383 
1384 NTSTATUS NTAPI
1385 ConDrvGetConsoleCP(IN PCONSOLE Console,
1386                    OUT PUINT CodePage,
1387                    IN BOOLEAN OutputCP);
1388 CSR_API(SrvGetConsoleCP)
1389 {
1390     NTSTATUS Status;
1391     PCONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetConsoleCPRequest;
1392     PCONSRV_CONSOLE Console;
1393 
1394     DPRINT("SrvGetConsoleCP, getting %s Code Page\n",
1395             GetConsoleCPRequest->OutputCP ? "Output" : "Input");
1396 
1397     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1398     if (!NT_SUCCESS(Status)) return Status;
1399 
1400     Status = ConDrvGetConsoleCP(Console,
1401                                 &GetConsoleCPRequest->CodePage,
1402                                 GetConsoleCPRequest->OutputCP);
1403 
1404     ConSrvReleaseConsole(Console, TRUE);
1405     return Status;
1406 }
1407 
1408 NTSTATUS NTAPI
1409 ConDrvSetConsoleCP(IN PCONSOLE Console,
1410                    IN UINT CodePage,
1411                    IN BOOLEAN OutputCP);
1412 CSR_API(SrvSetConsoleCP)
1413 {
1414     NTSTATUS Status = STATUS_INVALID_PARAMETER;
1415     PCONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetConsoleCPRequest;
1416     PCONSRV_CONSOLE Console;
1417 
1418     DPRINT("SrvSetConsoleCP, setting %s Code Page\n",
1419             SetConsoleCPRequest->OutputCP ? "Output" : "Input");
1420 
1421     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1422     if (!NT_SUCCESS(Status)) return Status;
1423 
1424     Status = ConDrvSetConsoleCP(Console,
1425                                 SetConsoleCPRequest->CodePage,
1426                                 SetConsoleCPRequest->OutputCP);
1427 
1428     ConSrvReleaseConsole(Console, TRUE);
1429     return Status;
1430 }
1431 
1432 CSR_API(SrvGetConsoleProcessList)
1433 {
1434     NTSTATUS Status;
1435     PCONSOLE_GETPROCESSLIST GetProcessListRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetProcessListRequest;
1436     PCONSRV_CONSOLE Console;
1437 
1438     if (!CsrValidateMessageBuffer(ApiMessage,
1439                                   (PVOID)&GetProcessListRequest->ProcessIdsList,
1440                                   GetProcessListRequest->ProcessCount,
1441                                   sizeof(DWORD)))
1442     {
1443         return STATUS_INVALID_PARAMETER;
1444     }
1445 
1446     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1447     if (!NT_SUCCESS(Status)) return Status;
1448 
1449     Status = ConSrvGetConsoleProcessList(Console,
1450                                          GetProcessListRequest->ProcessIdsList,
1451                                          GetProcessListRequest->ProcessCount,
1452                                          &GetProcessListRequest->ProcessCount);
1453 
1454     ConSrvReleaseConsole(Console, TRUE);
1455     return Status;
1456 }
1457 
1458 CSR_API(SrvGenerateConsoleCtrlEvent)
1459 {
1460     NTSTATUS Status;
1461     PCONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GenerateCtrlEventRequest;
1462     PCONSRV_CONSOLE Console;
1463 
1464     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1465     if (!NT_SUCCESS(Status)) return Status;
1466 
1467     Status = ConSrvConsoleProcessCtrlEvent(Console,
1468                                            GenerateCtrlEventRequest->ProcessGroupId,
1469                                            GenerateCtrlEventRequest->CtrlEvent);
1470 
1471     ConSrvReleaseConsole(Console, TRUE);
1472     return Status;
1473 }
1474 
1475 CSR_API(SrvConsoleNotifyLastClose)
1476 {
1477     NTSTATUS Status;
1478     PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
1479     PCONSRV_CONSOLE Console;
1480 
1481     Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
1482     if (!NT_SUCCESS(Status)) return Status;
1483 
1484     /* Only one process is allowed to be registered for last close notification */
1485     if (!Console->NotifyLastClose)
1486     {
1487         Console->NotifyLastClose = TRUE;
1488         Console->NotifiedLastCloseProcess = ProcessData;
1489         Status = STATUS_SUCCESS;
1490     }
1491     else
1492     {
1493         Status = STATUS_ACCESS_DENIED;
1494     }
1495 
1496     ConSrvReleaseConsole(Console, TRUE);
1497     return Status;
1498 }
1499 
1500 
1501 
1502 CSR_API(SrvGetConsoleMouseInfo)
1503 {
1504     NTSTATUS Status;
1505     PCONSOLE_GETMOUSEINFO GetMouseInfoRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetMouseInfoRequest;
1506     PCONSRV_CONSOLE Console;
1507 
1508     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1509     if (!NT_SUCCESS(Status)) return Status;
1510 
1511     /* Just retrieve the number of buttons of the mouse attached to this console */
1512     GetMouseInfoRequest->NumButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
1513 
1514     ConSrvReleaseConsole(Console, TRUE);
1515     return STATUS_SUCCESS;
1516 }
1517 
1518 CSR_API(SrvSetConsoleKeyShortcuts)
1519 {
1520     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1521     return STATUS_NOT_IMPLEMENTED;
1522 }
1523 
1524 CSR_API(SrvGetConsoleKeyboardLayoutName)
1525 {
1526     NTSTATUS Status;
1527     PCONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetKbdLayoutNameRequest;
1528     PCONSRV_CONSOLE Console;
1529 
1530     Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console, TRUE);
1531     if (!NT_SUCCESS(Status)) return Status;
1532 
1533     /* Retrieve the keyboard layout name of the system */
1534     if (GetKbdLayoutNameRequest->Ansi)
1535         GetKeyboardLayoutNameA((PCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1536     else
1537         GetKeyboardLayoutNameW((PWCHAR)GetKbdLayoutNameRequest->LayoutBuffer);
1538 
1539     ConSrvReleaseConsole(Console, TRUE);
1540     return STATUS_SUCCESS;
1541 }
1542 
1543 CSR_API(SrvGetConsoleCharType)
1544 {
1545     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1546     return STATUS_NOT_IMPLEMENTED;
1547 }
1548 
1549 CSR_API(SrvSetConsoleLocalEUDC)
1550 {
1551     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1552     return STATUS_NOT_IMPLEMENTED;
1553 }
1554 
1555 CSR_API(SrvSetConsoleCursorMode)
1556 {
1557     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1558     return STATUS_NOT_IMPLEMENTED;
1559 }
1560 
1561 CSR_API(SrvGetConsoleCursorMode)
1562 {
1563     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1564     return STATUS_NOT_IMPLEMENTED;
1565 }
1566 
1567 CSR_API(SrvGetConsoleNlsMode)
1568 {
1569     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1570     return STATUS_NOT_IMPLEMENTED;
1571 }
1572 
1573 CSR_API(SrvSetConsoleNlsMode)
1574 {
1575     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1576     return STATUS_NOT_IMPLEMENTED;
1577 }
1578 
1579 CSR_API(SrvGetConsoleLangId)
1580 {
1581     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1582     return STATUS_NOT_IMPLEMENTED;
1583 }
1584 
1585 /* EOF */
1586