[BASESRV-CONSRV-WINSRV]
[reactos.git] / win32ss / user / consrv / settings.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/settings.c
5 * PURPOSE: Console settings management
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 *
8 * NOTE: Adapted from existing code.
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include "consrv.h"
14 #include "include/conio.h"
15 #include "conio.h"
16 #include "include/settings.h"
17
18 #include <stdio.h> // for swprintf
19
20 #define NDEBUG
21 #include <debug.h>
22
23
24 /* GLOBALS ********************************************************************/
25
26 extern const COLORREF s_Colors[16];
27
28
29 /* FUNCTIONS ******************************************************************/
30
31 static VOID
32 TranslateConsoleName(OUT LPWSTR DestString,
33 IN LPCWSTR ConsoleName,
34 IN UINT MaxStrLen)
35 {
36 #define PATH_SEPARATOR L'\\'
37
38 UINT wLength;
39
40 if ( DestString == NULL || ConsoleName == NULL ||
41 *ConsoleName == L'\0' || MaxStrLen == 0 )
42 {
43 return;
44 }
45
46 wLength = GetWindowsDirectoryW(DestString, MaxStrLen);
47 if ((wLength > 0) && (_wcsnicmp(ConsoleName, DestString, wLength) == 0))
48 {
49 wcsncpy(DestString, L"%SystemRoot%", MaxStrLen);
50 // FIXME: Fix possible buffer overflows there !!!!!
51 wcsncat(DestString, ConsoleName + wLength, MaxStrLen);
52 }
53 else
54 {
55 wcsncpy(DestString, ConsoleName, MaxStrLen);
56 }
57
58 /* Replace path separators (backslashes) by underscores */
59 while ((DestString = wcschr(DestString, PATH_SEPARATOR))) *DestString = L'_';
60 }
61
62 static BOOL
63 OpenUserRegistryPathPerProcessId(DWORD ProcessId,
64 PHKEY hResult,
65 REGSAM samDesired)
66 {
67 BOOL bRet = TRUE;
68 HANDLE hProcessToken = NULL;
69 HANDLE hProcess;
70 BYTE Buffer[256];
71 DWORD Length = 0;
72 UNICODE_STRING SidName;
73 PTOKEN_USER TokUser;
74
75 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | READ_CONTROL, FALSE, ProcessId);
76 if (!hProcess)
77 {
78 DPRINT("Error: OpenProcess failed(0x%x)\n", GetLastError());
79 return FALSE;
80 }
81
82 if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken))
83 {
84 DPRINT("Error: OpenProcessToken failed(0x%x)\n", GetLastError());
85 CloseHandle(hProcess);
86 return FALSE;
87 }
88
89 if (!GetTokenInformation(hProcessToken, TokenUser, (PVOID)Buffer, sizeof(Buffer), &Length))
90 {
91 DPRINT("Error: GetTokenInformation failed(0x%x)\n",GetLastError());
92 CloseHandle(hProcessToken);
93 CloseHandle(hProcess);
94 return FALSE;
95 }
96
97 TokUser = ((PTOKEN_USER)Buffer)->User.Sid;
98 if (!NT_SUCCESS(RtlConvertSidToUnicodeString(&SidName, TokUser, TRUE)))
99 {
100 DPRINT("Error: RtlConvertSidToUnicodeString failed(0x%x)\n", GetLastError());
101 CloseHandle(hProcessToken);
102 CloseHandle(hProcess);
103 return FALSE;
104 }
105
106 bRet = (RegOpenKeyExW(HKEY_USERS,
107 SidName.Buffer,
108 0,
109 samDesired,
110 hResult) == ERROR_SUCCESS);
111
112 RtlFreeUnicodeString(&SidName);
113
114 CloseHandle(hProcessToken);
115 CloseHandle(hProcess);
116
117 return bRet;
118 }
119
120 /*static*/ BOOL
121 ConSrvOpenUserSettings(DWORD ProcessId,
122 LPCWSTR ConsoleTitle,
123 PHKEY hSubKey,
124 REGSAM samDesired,
125 BOOL bCreate)
126 {
127 BOOL bRet = TRUE;
128 WCHAR szBuffer[MAX_PATH] = L"Console\\";
129 WCHAR szBuffer2[MAX_PATH] = L"";
130 HKEY hKey;
131
132 /*
133 * Console properties are stored under the HKCU\Console\* key.
134 *
135 * We use the original console title as the subkey name for storing
136 * console properties. We need to distinguish whether we were launched
137 * via the console application directly or via a shortcut.
138 *
139 * If the title of the console corresponds to a path (more precisely,
140 * if the title is of the form: C:\ReactOS\<some_path>\<some_app.exe>),
141 * then use the corresponding unexpanded path and with the backslashes
142 * replaced by underscores, to make the registry happy,
143 * i.e. %SystemRoot%_<some_path>_<some_app.exe>
144 */
145
146 /* Open the registry key where we saved the console properties */
147 if (!OpenUserRegistryPathPerProcessId(ProcessId, &hKey, samDesired))
148 {
149 DPRINT1("OpenUserRegistryPathPerProcessId failed\n");
150 return FALSE;
151 }
152
153 /*
154 * Try to open properties via the console title:
155 * to make the registry happy, replace all the
156 * backslashes by underscores.
157 */
158 TranslateConsoleName(szBuffer2, ConsoleTitle, MAX_PATH);
159
160 /* Create the registry path */
161 wcsncat(szBuffer, szBuffer2, MAX_PATH);
162
163 /* Create or open the registry key */
164 if (bCreate)
165 {
166 /* Create the key */
167 bRet = (RegCreateKeyExW(hKey,
168 szBuffer,
169 0, NULL,
170 REG_OPTION_NON_VOLATILE,
171 samDesired,
172 NULL,
173 hSubKey,
174 NULL) == ERROR_SUCCESS);
175 }
176 else
177 {
178 /* Open the key */
179 bRet = (RegOpenKeyExW(hKey,
180 szBuffer,
181 0,
182 samDesired,
183 hSubKey) == ERROR_SUCCESS);
184 }
185
186 /* Close the parent key and return success or not */
187 RegCloseKey(hKey);
188 return bRet;
189 }
190
191 BOOL
192 ConSrvReadUserSettings(IN OUT PCONSOLE_INFO ConsoleInfo,
193 IN DWORD ProcessId)
194 {
195 BOOL RetVal = FALSE;
196 HKEY hKey;
197 DWORD dwNumSubKeys = 0;
198 DWORD dwIndex;
199 DWORD dwColorIndex = 0;
200 DWORD dwType;
201 WCHAR szValueName[MAX_PATH];
202 DWORD dwValueName;
203 WCHAR szValue[LF_FACESIZE] = L"\0";
204 DWORD Value;
205 DWORD dwValue;
206
207 if (!ConSrvOpenUserSettings(ProcessId,
208 ConsoleInfo->ConsoleTitle,
209 &hKey, KEY_READ,
210 FALSE))
211 {
212 DPRINT("ConSrvOpenUserSettings failed\n");
213 return FALSE;
214 }
215
216 if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
217 &dwNumSubKeys, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
218 {
219 DPRINT("ConSrvReadUserSettings: RegQueryInfoKey failed\n");
220 RegCloseKey(hKey);
221 return FALSE;
222 }
223
224 DPRINT("ConSrvReadUserSettings entered dwNumSubKeys %d\n", dwNumSubKeys);
225
226 for (dwIndex = 0; dwIndex < dwNumSubKeys; dwIndex++)
227 {
228 dwValue = sizeof(Value);
229 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
230
231 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, &dwType, (BYTE*)&Value, &dwValue) != ERROR_SUCCESS)
232 {
233 if (dwType == REG_SZ)
234 {
235 /*
236 * Retry in case of string value
237 */
238 dwValue = sizeof(szValue);
239 dwValueName = MAX_PATH; // sizeof(szValueName)/sizeof(szValueName[0])
240 if (RegEnumValueW(hKey, dwIndex, szValueName, &dwValueName, NULL, NULL, (BYTE*)szValue, &dwValue) != ERROR_SUCCESS)
241 break;
242 }
243 else
244 {
245 break;
246 }
247 }
248
249 /* Maybe it is UI-specific ?? */
250 if (!wcsncmp(szValueName, L"ColorTable", wcslen(L"ColorTable")))
251 {
252 dwColorIndex = 0;
253 swscanf(szValueName, L"ColorTable%2d", &dwColorIndex);
254 if (dwColorIndex < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0]))
255 {
256 ConsoleInfo->Colors[dwColorIndex] = Value;
257 RetVal = TRUE;
258 }
259 }
260 else if (!wcscmp(szValueName, L"HistoryBufferSize"))
261 {
262 ConsoleInfo->HistoryBufferSize = Value;
263 RetVal = TRUE;
264 }
265 else if (!wcscmp(szValueName, L"NumberOfHistoryBuffers"))
266 {
267 ConsoleInfo->NumberOfHistoryBuffers = Value;
268 RetVal = TRUE;
269 }
270 else if (!wcscmp(szValueName, L"HistoryNoDup"))
271 {
272 ConsoleInfo->HistoryNoDup = Value;
273 RetVal = TRUE;
274 }
275 else if (!wcscmp(szValueName, L"FullScreen"))
276 {
277 ConsoleInfo->FullScreen = Value;
278 RetVal = TRUE;
279 }
280 else if (!wcscmp(szValueName, L"QuickEdit"))
281 {
282 ConsoleInfo->QuickEdit = Value;
283 RetVal = TRUE;
284 }
285 else if (!wcscmp(szValueName, L"InsertMode"))
286 {
287 ConsoleInfo->InsertMode = Value;
288 RetVal = TRUE;
289 }
290 else if (!wcscmp(szValueName, L"ScreenBufferSize"))
291 {
292 ConsoleInfo->ScreenBufferSize.X = LOWORD(Value);
293 ConsoleInfo->ScreenBufferSize.Y = HIWORD(Value);
294 RetVal = TRUE;
295 }
296 else if (!wcscmp(szValueName, L"WindowSize"))
297 {
298 ConsoleInfo->ConsoleSize.X = LOWORD(Value);
299 ConsoleInfo->ConsoleSize.Y = HIWORD(Value);
300 RetVal = TRUE;
301 }
302 else if (!wcscmp(szValueName, L"CursorSize"))
303 {
304 ConsoleInfo->CursorSize = min(max(Value, 0), 100);
305 RetVal = TRUE;
306 }
307 else if (!wcscmp(szValueName, L"ScreenColors"))
308 {
309 ConsoleInfo->ScreenAttrib = Value;
310 RetVal = TRUE;
311 }
312 else if (!wcscmp(szValueName, L"PopupColors"))
313 {
314 ConsoleInfo->PopupAttrib = Value;
315 RetVal = TRUE;
316 }
317 }
318
319 RegCloseKey(hKey);
320 return RetVal;
321 }
322
323 BOOL
324 ConSrvWriteUserSettings(IN PCONSOLE_INFO ConsoleInfo,
325 IN DWORD ProcessId)
326 {
327 BOOL GlobalSettings = (ConsoleInfo->ConsoleTitle[0] == L'\0');
328 HKEY hKey;
329 DWORD Storage = 0;
330
331 #define SetConsoleSetting(SettingName, SettingType, SettingSize, Setting, DefaultValue) \
332 do { \
333 if (GlobalSettings || (!GlobalSettings && (*(Setting) != (DefaultValue)))) \
334 { \
335 RegSetValueExW(hKey, (SettingName), 0, (SettingType), (PBYTE)(Setting), (SettingSize)); \
336 } \
337 else \
338 { \
339 RegDeleteValue(hKey, (SettingName)); \
340 } \
341 } while (0)
342
343 WCHAR szValueName[15];
344 UINT i;
345
346 if (!ConSrvOpenUserSettings(ProcessId,
347 ConsoleInfo->ConsoleTitle,
348 &hKey, KEY_WRITE,
349 TRUE))
350 {
351 return FALSE;
352 }
353
354 for (i = 0 ; i < sizeof(ConsoleInfo->Colors)/sizeof(ConsoleInfo->Colors[0]) ; ++i)
355 {
356 /*
357 * Write only the new value if we are saving the global settings
358 * or we are saving settings for a particular console, which differs
359 * from the default ones.
360 */
361 swprintf(szValueName, L"ColorTable%02d", i);
362 SetConsoleSetting(szValueName, REG_DWORD, sizeof(DWORD), &ConsoleInfo->Colors[i], s_Colors[i]);
363 }
364
365 SetConsoleSetting(L"HistoryBufferSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->HistoryBufferSize, 50);
366 SetConsoleSetting(L"NumberOfHistoryBuffers", REG_DWORD, sizeof(DWORD), &ConsoleInfo->NumberOfHistoryBuffers, 4);
367
368 Storage = ConsoleInfo->HistoryNoDup;
369 SetConsoleSetting(L"HistoryNoDup", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
370
371 Storage = ConsoleInfo->FullScreen;
372 SetConsoleSetting(L"FullScreen", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
373
374 Storage = ConsoleInfo->QuickEdit;
375 SetConsoleSetting(L"QuickEdit", REG_DWORD, sizeof(DWORD), &Storage, FALSE);
376
377 Storage = ConsoleInfo->InsertMode;
378 SetConsoleSetting(L"InsertMode", REG_DWORD, sizeof(DWORD), &Storage, TRUE);
379
380 Storage = MAKELONG(ConsoleInfo->ScreenBufferSize.X, ConsoleInfo->ScreenBufferSize.Y);
381 SetConsoleSetting(L"ScreenBufferSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 300));
382
383 Storage = MAKELONG(ConsoleInfo->ConsoleSize.X, ConsoleInfo->ConsoleSize.Y);
384 SetConsoleSetting(L"WindowSize", REG_DWORD, sizeof(DWORD), &Storage, MAKELONG(80, 25));
385
386 SetConsoleSetting(L"CursorSize", REG_DWORD, sizeof(DWORD), &ConsoleInfo->CursorSize, CSR_DEFAULT_CURSOR_SIZE);
387
388 Storage = ConsoleInfo->ScreenAttrib;
389 SetConsoleSetting(L"ScreenColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_SCREEN_ATTRIB);
390
391 Storage = ConsoleInfo->PopupAttrib;
392 SetConsoleSetting(L"PopupColors", REG_DWORD, sizeof(DWORD), &Storage, DEFAULT_POPUP_ATTRIB);
393
394 RegCloseKey(hKey);
395 return TRUE;
396 }
397
398 VOID
399 ConSrvGetDefaultSettings(IN OUT PCONSOLE_INFO ConsoleInfo,
400 IN DWORD ProcessId)
401 {
402 if (ConsoleInfo == NULL) return;
403
404 /// HKCU,"Console","LoadConIme",0x00010003,1
405
406 /*
407 * 1. Load the default values
408 */
409 // #define DEFAULT_HISTORY_COMMANDS_NUMBER 50
410 // #define DEFAULT_HISTORY_BUFFERS_NUMBER 4
411 ConsoleInfo->HistoryBufferSize = 50;
412 ConsoleInfo->NumberOfHistoryBuffers = 4;
413 ConsoleInfo->HistoryNoDup = FALSE;
414
415 ConsoleInfo->FullScreen = FALSE;
416 ConsoleInfo->QuickEdit = FALSE;
417 ConsoleInfo->InsertMode = TRUE;
418 // ConsoleInfo->InputBufferSize;
419 ConsoleInfo->ScreenBufferSize = (COORD){80, 300};
420 ConsoleInfo->ConsoleSize = (COORD){80, 25 };
421
422 ConsoleInfo->CursorBlinkOn;
423 ConsoleInfo->ForceCursorOff;
424 ConsoleInfo->CursorSize = CSR_DEFAULT_CURSOR_SIZE; // #define SMALL_SIZE 25
425
426 ConsoleInfo->ScreenAttrib = DEFAULT_SCREEN_ATTRIB;
427 ConsoleInfo->PopupAttrib = DEFAULT_POPUP_ATTRIB;
428
429 memcpy(ConsoleInfo->Colors, s_Colors, sizeof(s_Colors));
430
431 // ConsoleInfo->CodePage;
432
433 ConsoleInfo->ConsoleTitle[0] = L'\0';
434
435 /*
436 * 2. Overwrite them with the ones stored in HKCU\Console.
437 * If the HKCU\Console key doesn't exist, create it
438 * and store the default values inside.
439 */
440 if (!ConSrvReadUserSettings(ConsoleInfo, ProcessId))
441 {
442 ConSrvWriteUserSettings(ConsoleInfo, ProcessId);
443 }
444 }
445
446 VOID
447 ConSrvApplyUserSettings(IN PCONSOLE Console,
448 IN PCONSOLE_INFO ConsoleInfo)
449 {
450 PCONSOLE_SCREEN_BUFFER ActiveBuffer = Console->ActiveBuffer;
451 COORD BufSize;
452 BOOL SizeChanged = FALSE;
453
454 /*
455 * Apply full-screen mode.
456 */
457 if (ConsoleInfo->FullScreen)
458 Console->ActiveBuffer->DisplayMode |= CONSOLE_FULLSCREEN_MODE;
459 else
460 Console->ActiveBuffer->DisplayMode &= ~CONSOLE_FULLSCREEN_MODE;
461 // TODO: Apply it really
462
463 /*
464 * Apply terminal-edition settings:
465 * - QuickEdit and Insert modes,
466 * - history settings.
467 */
468 Console->QuickEdit = ConsoleInfo->QuickEdit;
469 Console->InsertMode = ConsoleInfo->InsertMode;
470
471 /*
472 * Apply foreground and background colors for both screen and popup
473 * and copy the new palette.
474 */
475 ActiveBuffer->ScreenDefaultAttrib = ConsoleInfo->ScreenAttrib;
476 ActiveBuffer->PopupDefaultAttrib = ConsoleInfo->PopupAttrib;
477 memcpy(Console->Colors, ConsoleInfo->Colors, sizeof(s_Colors)); // FIXME: Possible buffer overflow if s_colors is bigger than pConInfo->Colors.
478
479 // TODO: Really update the screen attributes as FillConsoleOutputAttribute does.
480
481 /* Apply cursor size */
482 ActiveBuffer->CursorInfo.bVisible = (ConsoleInfo->CursorSize != 0);
483 ActiveBuffer->CursorInfo.dwSize = min(max(ConsoleInfo->CursorSize, 0), 100);
484
485 /* Resize the console */
486 if (ConsoleInfo->ConsoleSize.X != Console->ConsoleSize.X ||
487 ConsoleInfo->ConsoleSize.Y != Console->ConsoleSize.Y)
488 {
489 Console->ConsoleSize = ConsoleInfo->ConsoleSize;
490 SizeChanged = TRUE;
491 }
492
493 /* Resize its active screen-buffer */
494 BufSize = ConsoleInfo->ScreenBufferSize;
495 if (BufSize.X != ActiveBuffer->ScreenBufferSize.X ||
496 BufSize.Y != ActiveBuffer->ScreenBufferSize.Y)
497 {
498 if (NT_SUCCESS(ConioResizeBuffer(Console, ActiveBuffer, BufSize)))
499 SizeChanged = TRUE;
500 }
501
502 if (SizeChanged) ConioResizeTerminal(Console);
503 }
504
505 /* EOF */