xref: /reactos/base/system/userinit/userinit.c (revision 845faec4)
1 /*
2  *  ReactOS applications
3  *  Copyright (C) 2001, 2002 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:   See COPYING in the top level directory
21  * PROJECT:     ReactOS Userinit Logon Application
22  * FILE:        base/system/userinit/userinit.c
23  * PROGRAMMERS: Thomas Weidenmueller (w3seek@users.sourceforge.net)
24  *              Herv� Poussineau (hpoussin@reactos.org)
25  */
26 
27 #include "userinit.h"
28 
29 #define CMP_MAGIC  0x01234567
30 
31 /* GLOBALS ******************************************************************/
32 
33 HINSTANCE hInstance;
34 
35 
36 /* FUNCTIONS ****************************************************************/
37 
38 LONG
39 ReadRegSzKey(
40     IN HKEY hKey,
41     IN LPCWSTR pszKey,
42     OUT LPWSTR *pValue)
43 {
44     LONG rc;
45     DWORD dwType;
46     DWORD cbData = 0;
47     LPWSTR Value;
48 
49     TRACE("(%p, %s, %p)\n", hKey, debugstr_w(pszKey), pValue);
50 
51     rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData);
52     if (rc != ERROR_SUCCESS)
53     {
54         WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
55         return rc;
56     }
57     if (dwType != REG_SZ)
58     {
59         WARN("Wrong registry data type (%u vs %u)\n", dwType, REG_SZ);
60         return ERROR_FILE_NOT_FOUND;
61     }
62     Value = (WCHAR*) HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
63     if (!Value)
64     {
65         WARN("No memory\n");
66         return ERROR_NOT_ENOUGH_MEMORY;
67     }
68     rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)Value, &cbData);
69     if (rc != ERROR_SUCCESS)
70     {
71         WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
72         HeapFree(GetProcessHeap(), 0, Value);
73         return rc;
74     }
75     /* NULL-terminate the string */
76     Value[cbData / sizeof(WCHAR)] = L'\0';
77 
78     *pValue = Value;
79     return ERROR_SUCCESS;
80 }
81 
82 static BOOL
83 IsConsoleShell(VOID)
84 {
85     HKEY ControlKey = NULL;
86     LPWSTR SystemStartOptions = NULL;
87     LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */
88     LONG rc;
89     BOOL ret = FALSE;
90 
91     TRACE("()\n");
92 
93     rc = RegOpenKeyEx(
94         HKEY_LOCAL_MACHINE,
95         REGSTR_PATH_CURRENT_CONTROL_SET,
96         0,
97         KEY_QUERY_VALUE,
98         &ControlKey);
99     if (rc != ERROR_SUCCESS)
100     {
101         WARN("RegOpenKeyEx() failed with error %lu\n", rc);
102         goto cleanup;
103     }
104 
105     rc = ReadRegSzKey(ControlKey, L"SystemStartOptions", &SystemStartOptions);
106     if (rc != ERROR_SUCCESS)
107     {
108         WARN("ReadRegSzKey() failed with error %lu\n", rc);
109         goto cleanup;
110     }
111 
112     /* Check for CONSOLE switch in SystemStartOptions */
113     CurrentOption = SystemStartOptions;
114     while (CurrentOption)
115     {
116         NextOption = wcschr(CurrentOption, L' ');
117         if (NextOption)
118             *NextOption = L'\0';
119         if (_wcsicmp(CurrentOption, L"CONSOLE") == 0)
120         {
121             TRACE("Found 'CONSOLE' boot option\n");
122             ret = TRUE;
123             goto cleanup;
124         }
125         CurrentOption = NextOption ? NextOption + 1 : NULL;
126     }
127 
128 cleanup:
129     if (ControlKey != NULL)
130         RegCloseKey(ControlKey);
131     HeapFree(GetProcessHeap(), 0, SystemStartOptions);
132     TRACE("IsConsoleShell() returning %d\n", ret);
133     return ret;
134 }
135 
136 static BOOL
137 GetShell(
138     OUT WCHAR *CommandLine, /* must be at least MAX_PATH long */
139     IN HKEY hRootKey)
140 {
141     HKEY hKey;
142     DWORD Type, Size;
143     WCHAR Shell[MAX_PATH];
144     BOOL ConsoleShell = IsConsoleShell();
145     LONG rc;
146 
147     TRACE("(%p, %p)\n", CommandLine, hRootKey);
148 
149     rc = RegOpenKeyExW(hRootKey, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
150                        0, KEY_QUERY_VALUE, &hKey);
151     if (rc != ERROR_SUCCESS)
152     {
153         WARN("RegOpenKeyEx() failed with error %lu\n", rc);
154         return FALSE;
155     }
156 
157     Size = sizeof(Shell);
158     rc = RegQueryValueExW(hKey,
159                           ConsoleShell ? L"ConsoleShell" : L"Shell",
160                           NULL,
161                           &Type,
162                           (LPBYTE)Shell,
163                           &Size);
164     RegCloseKey(hKey);
165 
166     if (rc != ERROR_SUCCESS)
167     {
168         WARN("RegQueryValueEx() failed with error %lu\n", rc);
169         return FALSE;
170     }
171 
172     if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
173     {
174         TRACE("Found command line %s\n", debugstr_w(Shell));
175         wcscpy(CommandLine, Shell);
176         return TRUE;
177     }
178     else
179     {
180         WARN("Wrong type %lu (expected %u or %u)\n", Type, REG_SZ, REG_EXPAND_SZ);
181         return FALSE;
182     }
183 }
184 
185 static VOID
186 StartAutoApplications(
187     IN INT clsid)
188 {
189     WCHAR szPath[MAX_PATH] = {0};
190     HRESULT hResult;
191     HANDLE hFind;
192     WIN32_FIND_DATAW findData;
193     SHELLEXECUTEINFOW ExecInfo;
194     size_t len;
195 
196     TRACE("(%d)\n", clsid);
197 
198     hResult = SHGetFolderPathW(NULL, clsid, NULL, SHGFP_TYPE_CURRENT, szPath);
199     len = wcslen(szPath);
200     if (!SUCCEEDED(hResult) || len == 0)
201     {
202         WARN("SHGetFolderPath() failed with error %lu\n", GetLastError());
203         return;
204     }
205 
206     wcscat(szPath, L"\\*");
207     hFind = FindFirstFileW(szPath, &findData);
208     if (hFind == INVALID_HANDLE_VALUE)
209     {
210         WARN("FindFirstFile(%s) failed with error %lu\n", debugstr_w(szPath), GetLastError());
211         return;
212     }
213 
214     do
215     {
216         if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (findData.nFileSizeHigh || findData.nFileSizeLow))
217         {
218             ZeroMemory(&ExecInfo, sizeof(ExecInfo));
219             ExecInfo.cbSize = sizeof(ExecInfo);
220             wcscpy(&szPath[len+1], findData.cFileName);
221             ExecInfo.lpVerb = L"open";
222             ExecInfo.lpFile = szPath;
223             ExecInfo.lpDirectory = NULL;
224             TRACE("Executing %s in directory %s\n",
225                 debugstr_w(findData.cFileName), debugstr_w(szPath));
226             ShellExecuteExW(&ExecInfo);
227         }
228     } while (FindNextFileW(hFind, &findData));
229     FindClose(hFind);
230 }
231 
232 static BOOL
233 TryToStartShell(
234     IN LPCWSTR Shell)
235 {
236     STARTUPINFO si;
237     PROCESS_INFORMATION pi;
238     WCHAR ExpandedShell[MAX_PATH];
239 
240     TRACE("(%s)\n", debugstr_w(Shell));
241 
242     ZeroMemory(&si, sizeof(si));
243     si.cb = sizeof(si);
244     si.dwFlags = STARTF_USESHOWWINDOW;
245     si.wShowWindow = SW_SHOWNORMAL;
246     ZeroMemory(&pi, sizeof(pi));
247 
248     ExpandEnvironmentStringsW(Shell, ExpandedShell, ARRAYSIZE(ExpandedShell));
249 
250     if (!CreateProcessW(NULL,
251                         ExpandedShell,
252                         NULL,
253                         NULL,
254                         FALSE,
255                         NORMAL_PRIORITY_CLASS,
256                         NULL,
257                         NULL,
258                         &si,
259                         &pi))
260     {
261         WARN("CreateProcess() failed with error %lu\n", GetLastError());
262         return FALSE;
263     }
264 
265     StartAutoApplications(CSIDL_STARTUP);
266     StartAutoApplications(CSIDL_COMMON_STARTUP);
267     CloseHandle(pi.hProcess);
268     CloseHandle(pi.hThread);
269     return TRUE;
270 }
271 
272 static BOOL
273 StartShell(VOID)
274 {
275     WCHAR Shell[MAX_PATH];
276     WCHAR szMsg[RC_STRING_MAX_SIZE];
277     DWORD Type, Size;
278     DWORD Value = 0;
279     LONG rc;
280     HKEY hKey;
281 
282     TRACE("()\n");
283 
284     /* Safe Mode shell run */
285     rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
286                        L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
287                        0, KEY_QUERY_VALUE, &hKey);
288     if (rc == ERROR_SUCCESS)
289     {
290         Size = sizeof(Value);
291         rc = RegQueryValueExW(hKey, L"UseAlternateShell", NULL,
292                               &Type, (LPBYTE)&Value, &Size);
293         RegCloseKey(hKey);
294 
295         if (rc == ERROR_SUCCESS)
296         {
297             if (Type == REG_DWORD)
298             {
299                 if (Value)
300                 {
301                     /* Safe Mode Alternate Shell required */
302                     rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
303                                        L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot",
304                                        0, KEY_READ, &hKey);
305                     if (rc == ERROR_SUCCESS)
306                     {
307                         Size = sizeof(Shell);
308                         rc = RegQueryValueExW(hKey, L"AlternateShell", NULL,
309                                               &Type, (LPBYTE)Shell, &Size);
310                         RegCloseKey(hKey);
311 
312                         if (rc == ERROR_SUCCESS)
313                         {
314                             if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
315                             {
316                                 TRACE("Key located - %s\n", debugstr_w(Shell));
317 
318                                 /* Try to run alternate shell */
319                                 if (TryToStartShell(Shell))
320                                 {
321                                     TRACE("Alternate shell started (Safe Mode)\n");
322                                     return TRUE;
323                                 }
324                             }
325                             else
326                             {
327                                 WARN("Wrong type %lu (expected %u or %u)\n",
328                                      Type, REG_SZ, REG_EXPAND_SZ);
329                             }
330                         }
331                         else
332                         {
333                             WARN("Alternate shell in Safe Mode required but not specified.");
334                         }
335                     }
336                 }
337             }
338             else
339             {
340                 WARN("Wrong type %lu (expected %u)\n", Type, REG_DWORD);
341             }
342         }
343     }
344 
345     /* Try to run shell in user key */
346     if (GetShell(Shell, HKEY_CURRENT_USER) && TryToStartShell(Shell))
347     {
348         TRACE("Started shell from HKEY_CURRENT_USER\n");
349         return TRUE;
350     }
351 
352     /* Try to run shell in local machine key */
353     if (GetShell(Shell, HKEY_LOCAL_MACHINE) && TryToStartShell(Shell))
354     {
355         TRACE("Started shell from HKEY_LOCAL_MACHINE\n");
356         return TRUE;
357     }
358 
359     /* Try default shell */
360     if (IsConsoleShell())
361     {
362         if (GetSystemDirectoryW(Shell, ARRAYSIZE(Shell) - 8))
363             wcscat(Shell, L"\\cmd.exe");
364         else
365             wcscpy(Shell, L"cmd.exe");
366     }
367     else
368     {
369         if (GetWindowsDirectoryW(Shell, ARRAYSIZE(Shell) - 13))
370             wcscat(Shell, L"\\explorer.exe");
371         else
372             wcscpy(Shell, L"explorer.exe");
373     }
374 
375     if (!TryToStartShell(Shell))
376     {
377         WARN("Failed to start default shell %s\n", debugstr_w(Shell));
378         LoadStringW(GetModuleHandle(NULL), IDS_SHELL_FAIL, szMsg, ARRAYSIZE(szMsg));
379         MessageBoxW(NULL, szMsg, NULL, MB_OK);
380         return FALSE;
381     }
382     return TRUE;
383 }
384 
385 const WCHAR g_RegColorNames[][32] = {
386     L"Scrollbar",             /* 00 = COLOR_SCROLLBAR */
387     L"Background",            /* 01 = COLOR_DESKTOP */
388     L"ActiveTitle",           /* 02 = COLOR_ACTIVECAPTION  */
389     L"InactiveTitle",         /* 03 = COLOR_INACTIVECAPTION */
390     L"Menu",                  /* 04 = COLOR_MENU */
391     L"Window",                /* 05 = COLOR_WINDOW */
392     L"WindowFrame",           /* 06 = COLOR_WINDOWFRAME */
393     L"MenuText",              /* 07 = COLOR_MENUTEXT */
394     L"WindowText",            /* 08 = COLOR_WINDOWTEXT */
395     L"TitleText",             /* 09 = COLOR_CAPTIONTEXT */
396     L"ActiveBorder",          /* 10 = COLOR_ACTIVEBORDER */
397     L"InactiveBorder",        /* 11 = COLOR_INACTIVEBORDER */
398     L"AppWorkSpace",          /* 12 = COLOR_APPWORKSPACE */
399     L"Hilight",               /* 13 = COLOR_HIGHLIGHT */
400     L"HilightText",           /* 14 = COLOR_HIGHLIGHTTEXT */
401     L"ButtonFace",            /* 15 = COLOR_BTNFACE */
402     L"ButtonShadow",          /* 16 = COLOR_BTNSHADOW */
403     L"GrayText",              /* 17 = COLOR_GRAYTEXT */
404     L"ButtonText",            /* 18 = COLOR_BTNTEXT */
405     L"InactiveTitleText",     /* 19 = COLOR_INACTIVECAPTIONTEXT */
406     L"ButtonHilight",         /* 20 = COLOR_BTNHIGHLIGHT */
407     L"ButtonDkShadow",        /* 21 = COLOR_3DDKSHADOW */
408     L"ButtonLight",           /* 22 = COLOR_3DLIGHT */
409     L"InfoText",              /* 23 = COLOR_INFOTEXT */
410     L"InfoWindow",            /* 24 = COLOR_INFOBK */
411     L"ButtonAlternateFace",   /* 25 = COLOR_ALTERNATEBTNFACE */
412     L"HotTrackingColor",      /* 26 = COLOR_HOTLIGHT */
413     L"GradientActiveTitle",   /* 27 = COLOR_GRADIENTACTIVECAPTION */
414     L"GradientInactiveTitle", /* 28 = COLOR_GRADIENTINACTIVECAPTION */
415     L"MenuHilight",           /* 29 = COLOR_MENUHILIGHT */
416     L"MenuBar"                /* 30 = COLOR_MENUBAR */
417 };
418 
419 static COLORREF
420 StrToColorref(
421     IN LPWSTR lpszCol)
422 {
423     BYTE rgb[3];
424 
425     TRACE("(%s)\n", debugstr_w(lpszCol));
426 
427     rgb[0] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
428     rgb[1] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
429     rgb[2] = (BYTE)wcstoul(lpszCol, &lpszCol, 10);
430     return RGB(rgb[0], rgb[1], rgb[2]);
431 }
432 
433 static VOID
434 SetUserSysColors(VOID)
435 {
436     HKEY hKey;
437     INT i;
438     WCHAR szColor[25];
439     DWORD Type, Size;
440     COLORREF crColor;
441     LONG rc;
442 
443     TRACE("()\n");
444 
445     rc = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_COLORS,
446                        0, KEY_QUERY_VALUE, &hKey);
447     if (rc != ERROR_SUCCESS)
448     {
449         WARN("RegOpenKeyEx() failed with error %lu\n", rc);
450         return;
451     }
452 
453     for (i = 0; i < ARRAYSIZE(g_RegColorNames); i++)
454     {
455         Size = sizeof(szColor);
456         rc = RegQueryValueExW(hKey, g_RegColorNames[i], NULL, &Type,
457                               (LPBYTE)szColor, &Size);
458         if (rc == ERROR_SUCCESS && Type == REG_SZ)
459         {
460             crColor = StrToColorref(szColor);
461             SetSysColors(1, &i, &crColor);
462         }
463         else
464         {
465             WARN("RegQueryValueEx(%s) failed with error %lu\n",
466                 debugstr_w(g_RegColorNames[i]), rc);
467         }
468     }
469 
470     RegCloseKey(hKey);
471 }
472 
473 static VOID
474 SetUserWallpaper(VOID)
475 {
476     HKEY hKey;
477     DWORD Type, Size;
478     WCHAR szWallpaper[MAX_PATH + 1];
479     LONG rc;
480 
481     TRACE("()\n");
482 
483     rc = RegOpenKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP,
484                        0, KEY_QUERY_VALUE, &hKey);
485     if (rc != ERROR_SUCCESS)
486     {
487         WARN("RegOpenKeyEx() failed with error %lu\n", rc);
488         return;
489     }
490 
491     Size = sizeof(szWallpaper);
492     rc = RegQueryValueExW(hKey,
493                           L"Wallpaper",
494                           NULL,
495                           &Type,
496                           (LPBYTE)szWallpaper,
497                           &Size);
498     RegCloseKey(hKey);
499 
500     if (rc == ERROR_SUCCESS && Type == REG_SZ)
501     {
502         ExpandEnvironmentStringsW(szWallpaper, szWallpaper, ARRAYSIZE(szWallpaper));
503         TRACE("Using wallpaper %s\n", debugstr_w(szWallpaper));
504 
505         /* Load and change the wallpaper */
506         SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, szWallpaper, SPIF_SENDCHANGE);
507     }
508     else
509     {
510         /* Remove the wallpaper */
511         TRACE("No wallpaper set in registry (error %lu)\n", rc);
512         SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE);
513     }
514 }
515 
516 static VOID
517 SetUserSettings(VOID)
518 {
519     TRACE("()\n");
520 
521     UpdatePerUserSystemParameters(1, TRUE);
522     SetUserSysColors();
523     SetUserWallpaper();
524 }
525 
526 typedef DWORD (WINAPI *PCMP_REPORT_LOGON)(DWORD, DWORD);
527 
528 static VOID
529 NotifyLogon(VOID)
530 {
531     HINSTANCE hModule;
532     PCMP_REPORT_LOGON CMP_Report_LogOn;
533 
534     TRACE("()\n");
535 
536     hModule = LoadLibraryW(L"setupapi.dll");
537     if (!hModule)
538     {
539         WARN("LoadLibrary() failed with error %lu\n", GetLastError());
540         return;
541     }
542 
543     CMP_Report_LogOn = (PCMP_REPORT_LOGON)GetProcAddress(hModule, "CMP_Report_LogOn");
544     if (CMP_Report_LogOn)
545         CMP_Report_LogOn(CMP_MAGIC, GetCurrentProcessId());
546     else
547         WARN("GetProcAddress() failed\n");
548 
549     FreeLibrary(hModule);
550 }
551 
552 static BOOL
553 StartInstaller(VOID)
554 {
555     WCHAR Shell[MAX_PATH];
556     WCHAR szMsg[RC_STRING_MAX_SIZE];
557 
558     if (GetWindowsDirectoryW(Shell, ARRAYSIZE(Shell) - 12))
559         wcscat(Shell, L"\\reactos.exe");
560     else
561         wcscpy(Shell, L"reactos.exe");
562 
563     if (!TryToStartShell(Shell))
564     {
565         WARN("Failed to start the installer: %s\n", debugstr_w(Shell));
566         LoadStringW(GetModuleHandle(NULL), IDS_INSTALLER_FAIL, szMsg, ARRAYSIZE(szMsg));
567         MessageBoxW(NULL, szMsg, NULL, MB_OK);
568         return FALSE;
569     }
570     return TRUE;
571 }
572 
573 /* Used to get the shutdown privilege */
574 static BOOL
575 EnablePrivilege(LPCWSTR lpszPrivilegeName, BOOL bEnablePrivilege)
576 {
577     BOOL   Success;
578     HANDLE hToken;
579     TOKEN_PRIVILEGES tp;
580 
581     Success = OpenProcessToken(GetCurrentProcess(),
582                                TOKEN_ADJUST_PRIVILEGES,
583                                &hToken);
584     if (!Success) return Success;
585 
586     Success = LookupPrivilegeValueW(NULL,
587                                     lpszPrivilegeName,
588                                     &tp.Privileges[0].Luid);
589     if (!Success) goto Quit;
590 
591     tp.PrivilegeCount = 1;
592     tp.Privileges[0].Attributes = (bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0);
593 
594     Success = AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
595 
596 Quit:
597     CloseHandle(hToken);
598     return Success;
599 }
600 
601 
602 int WINAPI
603 wWinMain(IN HINSTANCE hInst,
604          IN HINSTANCE hPrevInstance,
605          IN LPWSTR lpszCmdLine,
606          IN int nCmdShow)
607 {
608     BOOL bIsLiveCD, Success = TRUE;
609     STATE State;
610 
611     hInstance = hInst;
612 
613     bIsLiveCD = IsLiveCD();
614 
615 Restart:
616     SetUserSettings();
617 
618     if (bIsLiveCD)
619     {
620         State.NextPage = LOCALEPAGE;
621         State.Run = SHELL;
622     }
623     else
624     {
625         State.NextPage = DONE;
626         State.Run = SHELL;
627     }
628 
629     if (State.NextPage != DONE) // && bIsLiveCD
630     {
631         RunLiveCD(&State);
632     }
633 
634     switch (State.Run)
635     {
636         case SHELL:
637             Success = StartShell();
638             if (Success)
639                 NotifyLogon();
640             break;
641 
642         case INSTALLER:
643             Success = StartInstaller();
644             break;
645 
646         case REBOOT:
647         {
648             EnablePrivilege(SE_SHUTDOWN_NAME, TRUE);
649             ExitWindowsEx(EWX_REBOOT, 0);
650             EnablePrivilege(SE_SHUTDOWN_NAME, FALSE);
651             Success = TRUE;
652             break;
653         }
654 
655         default:
656             Success = FALSE;
657             break;
658     }
659 
660     /*
661      * In LiveCD mode, go back to the main menu if we failed
662      * to either start the shell or the installer.
663      */
664     if (bIsLiveCD && !Success)
665         goto Restart;
666 
667     return 0;
668 }
669 
670 /* EOF */
671