/* ********************************************************************* */
/* *                                                                   * */
/* * Copyright (c) 2010 - Dipl.-Ing. Dirk Krause                       * */
/* *                                                                   * */
/* * All rights reserved.                                              * */
/* *                                                                   * */
/* * Redistribution and use in source and binary forms,                * */
/* * with or without modification, are permitted provided              * */
/* * that the following conditions are met:                            * */
/* *                                                                   * */
/* * * Redistributions of source code must retain the above            * */
/* *   copyright notice, this list of conditions and the               * */
/* *   following disclaimer.                                           * */
/* * * Redistributions in binary form must reproduce the above         * */
/* *   opyright notice, this list of conditions and the following      * */
/* *   disclaimer in the documentation and/or other materials          * */
/* *   provided with the distribution.                                 * */
/* * * Neither the name of Dirk Krause nor the names of                * */
/* *   contributors may be used to endorse or promote                  * */
/* *   products derived from this software without specific            * */
/* *   prior written permission.                                       * */
/* *                                                                   * */
/* * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND            * */
/* * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,       * */
/* * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF          * */
/* * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE          * */
/* * DISCLAIMED.                                                       * */
/* * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE          * */
/* * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,             * */
/* * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT           * */
/* * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          * */
/* * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)          * */
/* * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN         * */
/* * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE         * */
/* * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           * */
/* * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH              * */
/* * DAMAGE.                                                           * */
/* *                                                                   * */
/* ********************************************************************* */

/**	@file	ikwindown.c	Install the kwindown service.
This is the installer for the kwindown service.
*/



#include "kwintool.h"




#line 51 "ikwindown.ctr"




/**	Command: Install service.
*/
#define CMD_INSTALL		0



/**	Command: Uninstall service.
*/
#define CMD_UNINSTALL		1


/**	A sizeof version for WCHARs.
*/
#define SIZEOF(var,t) (sizeof(var)/sizeof(t))



/**	Command line arguments.
*/
static WCHAR const * const c[] = {
/*  0 */	L"-i",
/*  1 */	L"--install",
/*  2 */	L"-u",
/*  3 */	L"--uninstall",
NULL
};



/**	Strings used by the program.
*/
static WCHAR const * const n[] = {
/*  0 */ L"C:\\Windows\\System32\\kWinDown.exe",
/*  1 */ L"\\kWinDown.exe",
/*  2 */ L"Dnscache\0Tcpip\0",
/*  3 */ L"System\\CurrentControlSet\\Services\\kWinDown",
/*  4 */ L"Description",
/*  5 */ L"Shut down computer if no user is logged on",
/*  6 */ L"System\\CurrentControlSet\\Services\\Eventlog\\System\\kwindown",
/*  7 */ L"C:\\Windows\\System32\\kwindownm.dll",
/*  8 */ L"kwindownm.dll",
/*  9 */ L"CategoryCount",
/* 10 */ L"CategoryMessageFile",
/* 11 */ L"EventMessageFile",
/* 12 */ L"TypesSupported",
/* 13 */ L"ParameterMessageFile",
NULL
};


/**	Messages issued by the program.
*/
static char const * const messages[] = {
/*    0 */	"ERROR: Failed to create service!",
/*    1 */	"ERROR: Failed to connect to service manager!",
/*    2 */	"ERROR: Failed to install service, setup is incomplete!",
/*    3 */	"The service was installed successfully.",
/*    4 */	"ERROR: Failed to save service settings, uninstalling service!",
/*    5 */	"Service running, attempting to stop...",
/*    6 */	"The service was stopped successfully.",
/*    7 */	"Waiting some seconds for the service to stop...",
/*    8 */	"ERROR: Service stop operation timed out!",
/*    9 */	"The service was uninstalled successfully.",
/*   10 */	"ERROR: Failed to uninstall the service!",
/*   11 */	"ERROR: Service not found and not uninstalled!",
/*   12 */	"ERROR: Service manager not found, service not uninstalled!",
NULL
};



/**	Name of the service.
*/
static WCHAR service_name[] = { L"kWinDown" };



/**	Flag: Debug mode.
*/
static int debug_mode = 1;


/**	Maximum number of attempts to stop the service.
*/
static int max_attempts_to_stop = 10;


/**	Sleep time between attempts to stop the service (10 seconds).
*/
static DWORD service_stop_sleep_time = 1000;



/**	Service handler.
*/
HANDLE	h_service_status;


/**	Service status.
*/
SERVICE_STATUS	service_status;



/**	Save the setup.
	@return	1 on success, 0 on error.
*/
static
int
save_setup(void)
{
  int back = 0;
  HKEY hk; long res;
  DWORD dwDisp;
  WCHAR filenamebuffer[512], *ptr;
  res = RegCreateKeyEx(
    HKEY_LOCAL_MACHINE, n[3], 0, NULL, REG_OPTION_NON_VOLATILE,
    KEY_ALL_ACCESS, NULL, &hk, &dwDisp
  );
  if(res == ERROR_SUCCESS) {
    RegSetValueEx(
      hk, n[4], 0, REG_SZ,
      (const BYTE *)(n[5]), (sizeof(DWORD)*(1 + wcslen(n[5])))
    );
    RegCloseKey(hk);
  } else {
  }
  res = RegCreateKeyEx(
    HKEY_LOCAL_MACHINE, n[6], 0, NULL, REG_OPTION_NON_VOLATILE,
    KEY_ALL_ACCESS, NULL, &hk, &dwDisp
  );
  if(res == ERROR_SUCCESS) {
    if(GetModuleFileName(GetModuleHandle(NULL), filenamebuffer, SIZEOF(filenamebuffer,WCHAR))) {
      ptr = wcsrchr(filenamebuffer, L'\\');
      if(ptr) {
        ptr++;
	*ptr = L'\0';
	if((wcslen(filenamebuffer)+wcslen(n[8])) < SIZEOF(filenamebuffer,WCHAR)) {
	  wcscat(filenamebuffer, n[8]); back = 1;
	}
      } else {
        if(wcslen(n[7]) < SIZEOF(filenamebuffer,WCHAR)) {
	  wcscpy(filenamebuffer, n[7]); back = 1;
	}
      }
    } else {
      if(wcslen(n[7]) < SIZEOF(filenamebuffer,WCHAR)) {
	wcscpy(filenamebuffer, n[7]); back = 1;
      }
    }
    if(back) {
      DWORD myDw;
      myDw = 1;
      RegSetValueEx(
        hk, n[9], 0, REG_DWORD,
        (const BYTE *)(&myDw), sizeof(DWORD)
      );
      myDw = EVENTLOG_ERROR_TYPE
           | EVENTLOG_INFORMATION_TYPE
	   | EVENTLOG_WARNING_TYPE
	   | EVENTLOG_AUDIT_SUCCESS;
      RegSetValueEx(
        hk, n[12], 0, REG_DWORD,
        (const BYTE *)(&myDw), sizeof(DWORD)
      );
      RegSetValueEx(
        hk, n[10], 0, REG_EXPAND_SZ,
        (const BYTE *)filenamebuffer,(sizeof(WCHAR)*(1+wcslen(filenamebuffer)))
      );
      RegSetValueEx(
        hk, n[11], 0, REG_EXPAND_SZ,
        (const BYTE *)filenamebuffer,(sizeof(WCHAR)*(1+wcslen(filenamebuffer)))
      );
    }
    RegCloseKey(hk);
  } else {
  }
  return back;
}



/**	Uninstall service.
*/
static
void
uninstall_service(void)
{
  int i = 0;
  SC_HANDLE handle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if(handle != NULL) {
    SC_HANDLE service = OpenService(handle, service_name, DELETE);
    if(service != NULL) {
      if(QueryServiceStatus(service, &service_status)) {
        if(service_status.dwCurrentState != SERVICE_STOPPED) {
          fputs(messages[5], stdout); fputc('\n', stdout);
          ControlService(service, SERVICE_CONTROL_STOP, &service_status);
          for(i = 0; i < max_attempts_to_stop; i++) {
            if(QueryServiceStatus(service, &service_status)) {
              if(service_status.dwCurrentState == SERVICE_STOPPED) {
                i = max_attempts_to_stop;
                fputs(messages[6], stdout); fputc('\n', stdout);
              } else {
                fputs(messages[7], stdout); fputc('\n', stdout);
                Sleep(service_stop_sleep_time);
              }
            } else {
              /* something is wrong
 */
              i = max_attempts_to_stop;
              fputs(messages[8], stdout); fputc('\n', stdout);
            }
          }
        }
      }
      if(DeleteService(service)) {
        fputs(messages[9], stdout); fputc('\n', stdout);
      } else {
        fputs(messages[10], stdout); fputc('\n', stdout);
      }
    } else {
      fputs(messages[11], stdout); fputc('\n', stdout);
    }
    CloseServiceHandle(handle);
  } else {
    fputs(messages[12], stdout); fputc('\n', stdout);
  }
}



/**	Ask user for setup.
	@return	1 on success, 0 on error.
*/
static
int
ask_user_for_setup(void) { return 1; }



/**	Install the service.
*/
static
void
install_service(void)
{

  int must_uninstall, nameok;
  WCHAR filenamebuffer[512], *ptr;

  must_uninstall = 0; nameok = 0;
  if(ask_user_for_setup()) {
    SC_HANDLE handle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if(GetModuleFileName(GetModuleHandle(NULL), filenamebuffer, SIZEOF(filenamebuffer,WCHAR))) {
      ptr = wcsrchr(filenamebuffer, L'\\');
      if(ptr) {
        *ptr = L'\0';
	if((wcslen(n[1]) + wcslen(filenamebuffer)) < SIZEOF(filenamebuffer,WCHAR)) {
	  wcscpy(ptr, n[1]); nameok = 1;
	}
      } else {
        wcscpy(filenamebuffer, n[0]); nameok = 1;
      }
    } else {
      wcscpy(filenamebuffer, n[0]); nameok = 1;
    }
    if(handle != NULL) {
      SC_HANDLE service = CreateService(
        handle,
        service_name,
        service_name,
        GENERIC_READ | GENERIC_EXECUTE,
        SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
        SERVICE_AUTO_START,  /* SERVICE_DEMAND_START
 */
        SERVICE_ERROR_IGNORE,
        filenamebuffer,
        NULL,
        NULL,
        n[2],
        NULL,
        NULL
      );
      if(service != NULL) {
        must_uninstall = 1;
        CloseServiceHandle(service);
      } else {
        /* Failed to create service
 */
        if(debug_mode) { fputs(messages[0], stdout); fputc('\n', stdout); }
      }
      CloseServiceHandle(handle);
    } else {
      /* Failed to connect to service manager
 */
      if(debug_mode) { fputs(messages[1], stdout); fputc('\n', stdout); }
    }
  } else {
    /* Failed to install service, setup incomplete
 */
    if(debug_mode) { fputs(messages[2], stdout); fputc('\n', stdout); }
  }
  if(must_uninstall) {
    if(save_setup()) {
      fputs(messages[3], stdout); fputc('\n', stdout);
    } else {
      fputs(messages[4], stdout); fputc('\n', stdout);
      uninstall_service();
    }
  }
}



/**	Run and execute the command.
	@param	c	Command.
*/
static
void run(int c)
{
  switch(c) {
    case CMD_INSTALL: {
      install_service();
    } break;
    case CMD_UNINSTALL: {
      uninstall_service();
    } break;
  }
}



/**	Main program.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
*/
int wmain(int argc, WCHAR *argv[])
{
  int cmd = CMD_INSTALL;
  if(argc > 1) {
    switch(wstr_array_index(c, argv[1])) {
      case 2: case 3: {
        cmd = CMD_UNINSTALL;
      } break;
    }
  }
  run(cmd);
  exit(0); return 0;
}


