- Synchronize up to trunk's revision r57864.
[reactos.git] / base / system / winlogon / sas.c
index d4dbad8..cc59e3f 100644 (file)
@@ -25,7 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(winlogon);
 #define HK_CTRL_ALT_DEL   0
 #define HK_CTRL_SHIFT_ESC 1
 
-extern BOOL WINAPI SetLogonNotifyWindow(HWND Wnd, HWINSTA WinSta);
+static BOOL inScrn = FALSE;
 
 /* FUNCTIONS ****************************************************************/
 
@@ -57,6 +57,37 @@ StartTaskManager(
        return ret;
 }
 
+static BOOL
+StartUserShell(
+       IN OUT PWLSESSION Session)
+{
+       LPVOID lpEnvironment = NULL;
+       BOOLEAN Old;
+       BOOL ret;
+
+       /* Create environment block for the user */
+       if (!CreateEnvironmentBlock(&lpEnvironment, Session->UserToken, TRUE))
+       {
+               WARN("WL: CreateEnvironmentBlock() failed\n");
+               return FALSE;
+       }
+
+       /* Get privilege */
+       /* FIXME: who should do it? winlogon or gina? */
+       /* FIXME: reverting to lower privileges after creating user shell? */
+       RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &Old);
+
+       ret = Session->Gina.Functions.WlxActivateUserShell(
+                               Session->Gina.Context,
+                               L"Default",
+                               NULL, /* FIXME */
+                               lpEnvironment);
+
+       DestroyEnvironmentBlock(lpEnvironment);
+       return ret;
+}
+
+
 BOOL
 SetDefaultLanguage(
        IN BOOL UserProfile)
@@ -164,13 +195,197 @@ cleanup:
        return ret;
 }
 
+BOOL
+PlaySoundRoutine(
+       IN LPCWSTR FileName,
+       IN UINT bLogon,
+       IN UINT Flags)
+{
+       typedef BOOL (WINAPI *PLAYSOUNDW)(LPCWSTR,HMODULE,DWORD);
+       typedef UINT (WINAPI *WAVEOUTGETNUMDEVS)(VOID);
+       PLAYSOUNDW Play;
+       WAVEOUTGETNUMDEVS waveOutGetNumDevs;
+       UINT NumDevs;
+       HMODULE hLibrary;
+       BOOL Ret = FALSE;
+
+       hLibrary = LoadLibraryW(L"winmm.dll");
+       if (hLibrary)
+       {
+               waveOutGetNumDevs = (WAVEOUTGETNUMDEVS)GetProcAddress(hLibrary, "waveOutGetNumDevs");
+               if (waveOutGetNumDevs)
+               {
+                       NumDevs = waveOutGetNumDevs();
+                       if (!NumDevs)
+                       {
+                               if (!bLogon)
+                               {
+                                       Beep(500, 500);
+                               }
+                               FreeLibrary(hLibrary);
+                               return FALSE;
+                       }
+               }
+
+               Play = (PLAYSOUNDW)GetProcAddress(hLibrary, "PlaySoundW");
+               if (Play)
+               {
+                       Ret = Play(FileName, NULL, Flags);
+               }
+               FreeLibrary(hLibrary);
+       }
+
+       return Ret;
+}
+
+DWORD
+WINAPI
+PlayLogonSoundThread(
+       IN LPVOID lpParameter)
+{
+    BYTE TokenUserBuffer[256];
+    PTOKEN_USER pTokenUser = (TOKEN_USER*)TokenUserBuffer;
+    ULONG Length;
+       HKEY hKey;
+       WCHAR wszBuffer[MAX_PATH] = {0};
+       WCHAR wszDest[MAX_PATH];
+       DWORD dwSize = sizeof(wszBuffer), dwType;
+       SERVICE_STATUS_PROCESS Info;
+       UNICODE_STRING SidString;
+       NTSTATUS Status;
+       ULONG Index = 0;
+       SC_HANDLE hSCManager, hService;
+
+    /* Get SID of current user */
+    Status = NtQueryInformationToken((HANDLE)lpParameter,
+                                     TokenUser,
+                                     TokenUserBuffer,
+                                     sizeof(TokenUserBuffer),
+                                     &Length);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("NtQueryInformationToken failed: %x!\n", Status);
+        return 0;
+    }
+
+    /* Convert SID to string */
+    RtlInitEmptyUnicodeString(&SidString, wszBuffer, sizeof(wszBuffer));
+    Status = RtlConvertSidToUnicodeString(&SidString, pTokenUser->User.Sid, FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("RtlConvertSidToUnicodeString failed: %x!\n", Status);
+        return 0;
+    }
+
+    /* Build path to logon sound registry key.
+       Note: We can't use HKCU here, because Winlogon is owned by SYSTEM user */
+    if (FAILED(StringCbCopyW(wszBuffer + SidString.Length/sizeof(WCHAR),
+                             sizeof(wszBuffer) - SidString.Length,
+                             L"\\AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current")))
+    {
+        /* SID is too long. Should not happen. */
+        ERR("StringCbCopyW failed!\n");
+               return 0;
+    }
+
+    /* Open registry key and query sound path */
+       if (RegOpenKeyExW(HKEY_USERS, wszBuffer, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
+       {
+           ERR("RegOpenKeyExW(%ls) failed!\n", wszBuffer);
+               return 0;
+       }
+
+       if (RegQueryValueExW(hKey, NULL, NULL, &dwType,
+                      (LPBYTE)wszBuffer, &dwSize) != ERROR_SUCCESS ||
+        (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
+       {
+           ERR("RegQueryValueExW failed!\n");
+               RegCloseKey(hKey);
+               return 0;
+       }
+
+       RegCloseKey(hKey);
+
+       if (!wszBuffer[0])
+       {
+           /* No sound has been set */
+           ERR("No sound has been set\n");
+           return 0;
+       }
+
+    /* Expand environment variables */
+       if (!ExpandEnvironmentStringsW(wszBuffer, wszDest, MAX_PATH))
+       {
+           ERR("ExpandEnvironmentStringsW failed!\n");
+           return 0;
+       }
+
+    /* Open service manager */
+    hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+    if (!hSCManager)
+    {
+        ERR("OpenSCManager failed (%x)\n", GetLastError());
+        return 0;
+    }
+
+    /* Open wdmaud service */
+    hService = OpenServiceW(hSCManager, L"wdmaud", GENERIC_READ);
+    if (!hService)
+    {
+        /* Sound is not installed */
+        TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
+        CloseServiceHandle(hSCManager);
+        return 0;
+    }
+
+    /* Wait for wdmaud start */
+    do
+    {
+        if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&Info, sizeof(SERVICE_STATUS_PROCESS), &dwSize))
+        {
+            TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
+            break;
+        }
+
+        if (Info.dwCurrentState == SERVICE_RUNNING)
+            break;
+
+        Sleep(1000);
+
+    } while (Index++ < 20);
+
+    CloseServiceHandle(hService);
+    CloseServiceHandle(hSCManager);
+
+    /* If wdmaud is not running exit */
+    if (Info.dwCurrentState != SERVICE_RUNNING)
+    {
+        WARN("wdmaud has not started!\n");
+        return 0;
+    }
+
+    /* Sound subsystem is running. Play logon sound. */
+    TRACE("Playing logon sound: %ls\n", wszDest);
+    PlaySoundRoutine(wszDest, TRUE, SND_FILENAME);
+       return 0;
+}
+
+static VOID
+PlayLogonSound(
+    IN OUT PWLSESSION Session)
+{
+    HANDLE hThread;
+
+       hThread = CreateThread(NULL, 0, PlayLogonSoundThread, (PVOID)Session->UserToken, 0, NULL);
+       if (hThread)
+               CloseHandle(hThread);
+}
+
 static BOOL
 HandleLogon(
        IN OUT PWLSESSION Session)
 {
        PROFILEINFOW ProfileInfo;
-       LPVOID lpEnvironment = NULL;
-       BOOLEAN Old;
        BOOL ret = FALSE;
 
        /* Loading personal settings */
@@ -213,13 +428,6 @@ HandleLogon(
                goto cleanup;
        }
 
-       /* Create environment block for the user */
-       if (!CreateEnvironmentBlock(&lpEnvironment, Session->UserToken, TRUE))
-       {
-               WARN("WL: CreateEnvironmentBlock() failed\n");
-               goto cleanup;
-       }
-
        DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGYOURPERSONALSETTINGS);
        UpdatePerUserSystemParameters(0, TRUE);
 
@@ -230,16 +438,7 @@ HandleLogon(
                goto cleanup;
        }
 
-       /* Get privilege */
-       /* FIXME: who should do it? winlogon or gina? */
-       /* FIXME: reverting to lower privileges after creating user shell? */
-       RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &Old);
-
-       if (!Session->Gina.Functions.WlxActivateUserShell(
-               Session->Gina.Context,
-               L"Default",
-               NULL, /* FIXME */
-               lpEnvironment))
+       if (!StartUserShell(Session))
        {
                //WCHAR StatusMsg[256];
                WARN("WL: WlxActivateUserShell() failed\n");
@@ -252,6 +451,10 @@ HandleLogon(
                WARN("WL: Failed to initialize screen saver\n");
 
        Session->hProfileInfo = ProfileInfo.hProfile;
+
+    /* Logon has successed. Play sound. */
+       PlayLogonSound(Session);
+       
        ret = TRUE;
 
 cleanup:
@@ -266,8 +469,6 @@ cleanup:
        {
                UnloadUserProfile(WLSession->UserToken, ProfileInfo.hProfile);
        }
-       if (lpEnvironment)
-               DestroyEnvironmentBlock(lpEnvironment);
        RemoveStatusMessage(Session);
        if (!ret)
        {
@@ -843,6 +1044,40 @@ CheckForShutdownPrivilege(
        return STATUS_SUCCESS;
 }
 
+BOOL
+WINAPI
+HandleMessageBeep(UINT uType)
+{
+       LPWSTR EventName;
+
+       switch(uType)
+       {
+       case 0xFFFFFFFF:
+               EventName = NULL;
+               break;
+       case MB_OK:
+               EventName = L"SystemDefault";
+               break;
+       case MB_ICONASTERISK:
+               EventName = L"SystemAsterisk";
+               break;
+       case MB_ICONEXCLAMATION:
+               EventName = L"SystemExclamation";
+               break;
+       case MB_ICONHAND:
+               EventName = L"SystemHand";
+               break;
+       case MB_ICONQUESTION:
+               EventName = L"SystemQuestion";
+               break;
+       default:
+               WARN("Unhandled type %d\n", uType);
+               EventName = L"SystemDefault";
+       }
+
+       return PlaySoundRoutine(EventName, FALSE, SND_ALIAS | SND_NOWAIT | SND_NOSTOP | SND_ASYNC);
+}
+
 static LRESULT CALLBACK
 SASWindowProc(
        IN HWND hwndDlg,
@@ -902,6 +1137,61 @@ SASWindowProc(
                        }
                        return TRUE;
                }
+        case WM_LOGONNOTIFY:
+        {
+            switch(wParam)
+            {
+                case LN_MESSAGE_BEEP:
+                {
+                    return HandleMessageBeep(lParam);
+                }
+                case LN_SHELL_EXITED:
+                {
+                    /* lParam is the exit code */
+                    if(lParam != 1)
+                    {
+                        SetTimer(hwndDlg, 1, 1000, NULL);
+                    }
+                    break;
+                }
+                case LN_START_SCREENSAVE:
+                {
+                    BOOL bSecure = FALSE;
+
+                    if (inScrn)
+                       break;
+
+                    inScrn = TRUE;
+
+                    // lParam 1 == Secure
+                    if (lParam)
+                    {
+                       if (Session->Gina.Functions.WlxScreenSaverNotify(Session->Gina.Context, &bSecure))
+                       {
+                          if (bSecure) DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
+                      }
+                    }
+
+                    StartScreenSaver(Session);
+                    inScrn = FALSE;
+                    break;
+                }
+                default:
+                {
+                    ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam);
+                }
+            }
+            return 0;
+        }
+        case WM_TIMER:
+        {
+            if (wParam == 1)
+            {
+                KillTimer(hwndDlg, 1);
+                StartUserShell(Session);
+            }
+            break;
+        }
                case WLX_WM_SAS:
                {
                        DispatchSAS(Session, (DWORD)wParam);