/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS Winlogon
 * FILE:            services/winlogon/winlogon.c
 * PURPOSE:         Logon
 * PROGRAMMERS:     Thomas Weidenmueller (w3seek@users.sourceforge.net)
 *                  Filip Navara
 *                  Hervé Poussineau (hpoussin@reactos.org)
 */

/* INCLUDES *****************************************************************/
#include "winlogon.h"

//#define YDEBUG
#include <wine/debug.h>

/* GLOBALS ******************************************************************/

HINSTANCE hAppInstance;
PWLSESSION WLSession = NULL;

/* FUNCTIONS *****************************************************************/

static BOOL
StartServicesManager(VOID)
{
	HANDLE ServicesInitEvent = NULL;
	STARTUPINFOW StartupInfo;
	PROCESS_INFORMATION ProcessInformation;
	DWORD Count;
	LPCWSTR ServiceString = L"services.exe";
	BOOL res;

	/* Start the service control manager (services.exe) */
	StartupInfo.cb = sizeof(StartupInfo);
	StartupInfo.lpReserved = NULL;
	StartupInfo.lpDesktop = NULL;
	StartupInfo.lpTitle = NULL;
	StartupInfo.dwFlags = 0;
	StartupInfo.cbReserved2 = 0;
	StartupInfo.lpReserved2 = 0;

	TRACE("WL: Creating new process - %S\n", ServiceString);

	res = CreateProcessW(
		ServiceString,
		NULL,
		NULL,
		NULL,
		FALSE,
		DETACHED_PROCESS,
		NULL,
		NULL,
		&StartupInfo,
		&ProcessInformation);
	if (!res)
	{
		ERR("WL: Failed to execute services (error %lu)\n", GetLastError());
		return FALSE;
	}

	/* Wait for event creation (by SCM) for max. 20 seconds */
	for (Count = 0; Count < 20; Count++)
	{
		Sleep(1000);

		TRACE("WL: Attempting to open event \"SvcctrlStartEvent_A3725DX\"\n");
		ServicesInitEvent = OpenEventW(
			SYNCHRONIZE,
			FALSE,
			L"SvcctrlStartEvent_A3725DX");
		if (ServicesInitEvent)
			break;
	}

	if (!ServicesInitEvent)
	{
		ERR("WL: Failed to open event \"SvcctrlStartEvent_A3725DX\"\n");
		return FALSE;
	}

	/* Wait for event signalization */
	WaitForSingleObject(ServicesInitEvent, INFINITE);
	CloseHandle(ServicesInitEvent);
	TRACE("WL: StartServicesManager() done.\n");

	return TRUE;
}

static BOOL
StartCustomService(
	IN LPCWSTR ServiceName)
{
	SC_HANDLE hSCManager = NULL;
	SC_HANDLE hService = NULL;
	BOOL ret = FALSE;

	hSCManager = OpenSCManager(NULL, NULL, 0);
	if (!hSCManager)
		goto cleanup;

	hService = OpenServiceW(hSCManager, ServiceName, SERVICE_START);
	if (!hService)
		goto cleanup;
	if (!StartServiceW(hService, 0, NULL))
		goto cleanup;

	ret = TRUE;

cleanup:
	if (hService)
		CloseServiceHandle(hService);
	if (hSCManager)
		CloseServiceHandle(hSCManager);
	return ret;
}

static BOOL
StartLsass(VOID)
{
	HANDLE LsassInitEvent;

	LsassInitEvent = CreateEventW(
		NULL,
		TRUE,
		FALSE,
		L"Global\\SECURITY_SERVICES_STARTED");
	if (!LsassInitEvent)
	{
		ERR("WL: Failed to create lsass notification event (error %lu)\n", GetLastError());
		return FALSE;
	}

	/* Start the local security authority subsystem (Netlogon service) */
	if (!StartCustomService(L"Netlogon"))
	{
		ERR("WL: Failed to start NetLogon service (error %lu)\n", GetLastError());
		return FALSE;
	}

	WaitForSingleObject(LsassInitEvent, INFINITE);
	CloseHandle(LsassInitEvent);

	return TRUE;
}

#if 0
static BOOL
OpenRegistryKey(
	OUT HKEY *WinLogonKey)
{
   return ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                        L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon",
                                        0,
                                        KEY_QUERY_VALUE,
                                        WinLogonKey);
}
#endif

#if 0
static BOOL
StartProcess(
	IN PWCHAR ValueName)
{
   BOOL StartIt;
   HKEY WinLogonKey;
   DWORD Type;
   DWORD Size;
   DWORD StartValue;

   StartIt = TRUE;
   if (OpenRegistryKey(&WinLogonKey))
     {
	Size = sizeof(DWORD);
	if (ERROR_SUCCESS == RegQueryValueEx(WinLogonKey,
                                             ValueName,
	                                     NULL,
	                                     &Type,
	                                     (LPBYTE) &StartValue,
                                             &Size))
	   {
	   if (REG_DWORD == Type)
	     {
		StartIt = (0 != StartValue);
	     }
	   }
	RegCloseKey(WinLogonKey);
     }

   return StartIt;
}
#endif

/*
static BOOL RestartShell(
	IN OUT PWLSESSION Session)
{
  HKEY WinLogonKey;
  DWORD Type, Size, Value;

  if(OpenRegistryKey(&WinLogonKey))
  {
    Size = sizeof(DWORD);
    if(ERROR_SUCCESS == RegQueryValueEx(WinLogonKey,
                                        L"AutoRestartShell",
                                        NULL,
                                        &Type,
                                        (LPBYTE)&Value,
                                        &Size))
    {
      if(Type == REG_DWORD)
      {
        RegCloseKey(WinLogonKey);
        return (Value != 0);
      }
    }
    RegCloseKey(WinLogonKey);
  }
  return FALSE;
}
*/

#if 0
static PWCHAR
GetUserInit(
	OUT WCHAR *CommandLine,
	IN DWORD BufferLength)
{
   HKEY WinLogonKey;
   BOOL GotCommandLine;
   DWORD Type;
   DWORD Size;
   WCHAR Shell[_MAX_PATH];

   GotCommandLine = FALSE;
   if (OpenRegistryKey(&WinLogonKey))
     {
	Size = MAX_PATH;
	if (ERROR_SUCCESS == RegQueryValueEx(WinLogonKey,
                                         L"UserInit",
	                                     NULL,
	                                     &Type,
	                                     (LPBYTE) Shell,
                                         &Size))
	   {
	   if (REG_EXPAND_SZ == Type)
	     {
		ExpandEnvironmentStrings(Shell, CommandLine, _MAX_PATH);
		GotCommandLine = TRUE;
	     }
	   else if (REG_SZ == Type)
	     {
		wcscpy(CommandLine, Shell);
		GotCommandLine = TRUE;
	     }
	   }
	RegCloseKey(WinLogonKey);
     }

   if (! GotCommandLine)
     {
	GetSystemDirectory(CommandLine, MAX_PATH - 15);
	wcscat(CommandLine, L"\\userinit.exe");
     }

   return CommandLine;
}

#endif

BOOL
DisplayStatusMessage(
	IN PWLSESSION Session,
	IN HDESK hDesktop,
	IN UINT ResourceId)
{
	WCHAR StatusMsg[MAX_PATH];

	if (Session->Gina.Version < WLX_VERSION_1_3)
		return TRUE;

	if (Session->SuppressStatus)
		return TRUE;

	if (LoadStringW(hAppInstance, ResourceId, StatusMsg, MAX_PATH) == 0)
		return FALSE;

	return Session->Gina.Functions.WlxDisplayStatusMessage(Session->Gina.Context, hDesktop, 0, NULL, StatusMsg);
}

BOOL
RemoveStatusMessage(
	IN PWLSESSION Session)
{
	if (Session->Gina.Version < WLX_VERSION_1_3)
		return TRUE;

	return Session->Gina.Functions.WlxRemoveStatusMessage(Session->Gina.Context);
}

static BOOL CALLBACK
GinaLoadFailedWindowProc(
	IN HWND hwndDlg,
	IN UINT uMsg,
	IN WPARAM wParam,
	IN LPARAM lParam)
{
	switch (uMsg)
	{
		case WM_COMMAND:
		{
			switch (LOWORD(wParam))
			{
				case IDOK:
					EndDialog(hwndDlg, IDOK);
					return TRUE;
			}
			break;
		}
		case WM_INITDIALOG:
		{
			int len;
			WCHAR templateText[MAX_PATH], text[MAX_PATH];

			len = GetDlgItemTextW(hwndDlg, IDC_GINALOADFAILED, templateText, MAX_PATH);
			if (len)
			{
				wsprintfW(text, templateText, (LPWSTR)lParam);
				SetDlgItemTextW(hwndDlg, IDC_GINALOADFAILED, text);
			}
			SetFocus(GetDlgItem(hwndDlg, IDOK));
			return TRUE;
		}
		case WM_CLOSE:
		{
			EndDialog(hwndDlg, IDCANCEL);
			return TRUE;
		}
	}

	return FALSE;
}

int WINAPI
WinMain(
	IN HINSTANCE hInstance,
	IN HINSTANCE hPrevInstance,
	IN LPSTR lpCmdLine,
	IN int nShowCmd)
{
#if 0
	LSA_STRING ProcessName, PackageName;
	HANDLE LsaHandle;
	LSA_OPERATIONAL_MODE Mode;
	BOOLEAN Old;
	ULONG AuthenticationPackage;
	NTSTATUS Status;
#endif
	ULONG HardErrorResponse;
	MSG Msg;

	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	UNREFERENCED_PARAMETER(nShowCmd);

	hAppInstance = hInstance;

	if (!RegisterLogonProcess(GetCurrentProcessId(), TRUE))
	{
		ERR("WL: Could not register logon process\n");
		HandleShutdown(NULL, WLX_SAS_ACTION_SHUTDOWN_POWER_OFF);
		NtShutdownSystem(ShutdownNoReboot);
		ExitProcess(0);
	}

	WLSession = (PWLSESSION)HeapAlloc(GetProcessHeap(), 0, sizeof(WLSESSION));
	if (!WLSession)
	{
		ERR("WL: Could not allocate memory for winlogon instance\n");
		NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
		ExitProcess(1);
	}
	ZeroMemory(WLSession, sizeof(WLSESSION));
	WLSession->DialogTimeout = 120; /* 2 minutes */

	if (!CreateWindowStationAndDesktops(WLSession))
	{
		ERR("WL: Could not create window station and desktops\n");
		NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
		ExitProcess(1);
	}
	LockWorkstation(WLSession);

	if (!StartServicesManager())
	{
		ERR("WL: Could not start services.exe\n");
		NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
		ExitProcess(1);
	}

	/* Check for pending setup */
	if (GetSetupType() != 0)
	{
		TRACE("WL: Setup mode detected\n");

		/* Set locale */
		SetDefaultLanguage(FALSE);

		/* Run setup and reboot when done */
		SwitchDesktop(WLSession->ApplicationDesktop);
		RunSetup();

		HandleShutdown(WLSession, WLX_SAS_ACTION_SHUTDOWN_REBOOT);
		ExitProcess(0);
	}

	if (!StartLsass())
	{
		DPRINT1("WL: Failed to start lsass.exe service (error %lu)\n", GetLastError());
		NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, 0, OptionOk, &HardErrorResponse);
		ExitProcess(1);
	}

	/* Load and initialize gina */
	if (!GinaInit(WLSession))
	{
		ERR("WL: Failed to initialize Gina\n");
		DialogBoxParam(hAppInstance, MAKEINTRESOURCE(IDD_GINALOADFAILED), GetDesktopWindow(), GinaLoadFailedWindowProc, (LPARAM)L"");
		HandleShutdown(WLSession, WLX_SAS_ACTION_SHUTDOWN_REBOOT);
		ExitProcess(1);
	}

	DisplayStatusMessage(WLSession, WLSession->WinlogonDesktop, IDS_REACTOSISSTARTINGUP);

#if 0
	/* Connect to NetLogon service (lsass.exe) */
	/* Real winlogon uses "Winlogon" */
	RtlInitUnicodeString((PUNICODE_STRING)&ProcessName, L"Winlogon");
	Status = LsaRegisterLogonProcess(&ProcessName, &LsaHandle, &Mode);
	if (Status == STATUS_PORT_CONNECTION_REFUSED)
	{
		/* Add the 'SeTcbPrivilege' privilege and try again */
		Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, TRUE, &Old);
		if (!NT_SUCCESS(Status))
		{
			ERR("RtlAdjustPrivilege() failed with error %lu\n", LsaNtStatusToWinError(Status));
			return 1;
		}
		Status = LsaRegisterLogonProcess(&ProcessName, &LsaHandle, &Mode);
	}
	if (!NT_SUCCESS(Status))
	{
		ERR("LsaRegisterLogonProcess() failed with error %lu\n", LsaNtStatusToWinError(Status));
		return 1;
	}

	RtlInitUnicodeString((PUNICODE_STRING)&PackageName, MICROSOFT_KERBEROS_NAME_W);
	Status = LsaLookupAuthenticationPackage(LsaHandle, &PackageName, &AuthenticationPackage);
	if (!NT_SUCCESS(Status))
	{
		ERR("LsaLookupAuthenticationPackage() failed with error %lu\n", LsaNtStatusToWinError(Status));
		LsaDeregisterLogonProcess(LsaHandle);
		return 1;
	}
#endif

	/* Create a hidden window to get SAS notifications */
	if (!InitializeSAS(WLSession))
	{
		ERR("WL: Failed to initialize SAS\n");
		ExitProcess(2);
	}

	//DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_PREPARENETWORKCONNECTIONS);
	//DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGCOMPUTERSETTINGS);

	/* Display logged out screen */
	WLSession->LogonStatus = WKSTA_IS_LOGGED_OFF;
	RemoveStatusMessage(WLSession);
	PostMessageW(WLSession->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_TIMEOUT, 0);

	/* Message loop for the SAS window */
	while (GetMessageW(&Msg, WLSession->SASWindow, 0, 0))
	{
		TranslateMessage(&Msg);
		DispatchMessageW(&Msg);
	}

	/* We never go there */

	return 0;
}
