// Created by Cyrus Patel <cyp@fb14.uni-mainz.de>
// Copyright distributed.net 1997-1999 - All Rights Reserved
// For use in distributed.net projects only.
// Any other distribution or use of this source violates copyright.
//
// $Log: w32svc.cpp,v $
// Revision 1.8  1999/01/01 02:45:21  cramer
// Part 1 of 1999 Copyright updates...
//
// Revision 1.7  1998/12/30 19:17:10  cyp
// Properly fixed NT service name fix.
//
// Revision 1.6  1998/12/28 20:11:42  silby
// Blarg, meant "bovrc5nt" key in previous comment.
//
// Revision 1.5  1998/12/28 20:11:07  silby
// NT service now uninstalls correctly, and in addition will uninstall
// old versions of the client that used the bovwin32 key.
//
// Revision 1.4  1998/12/20 07:10:39  cyp
// New func win32CliServiceRunning() returns !0 if the clients is running
// as a service (effective only once service mode has been initialized).
//
// Revision 1.3  1998/12/01 00:21:43  cyp
// Consolidated w32ConGetVersion() [w32cons] and winCliGetVersion() [w32svc]
// into winGetVersion() [w32pre]. This probably fixes the service start bug.
//
// Revision 1.2  1998/10/04 01:32:09  silby
// Changed declarations and included windows headers to make msvc happy. May break other compilers (if so, we need to #ifdef.  ugh)
//
// Revision 1.1  1998/09/30 18:47:18  cyp
// Created.
//
//
//
#if (!defined(lint) && defined(__showids__))
const char *w32svc_cpp(void) {
return "@(#)$Id: w32svc.cpp,v 1.8 1999/01/01 02:45:21 cramer Exp $"; }
#endif

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "triggers.h"
#include "w32svc.h"   /* ourselves */
#include "w32pre.h"   /* WinGetVersion() */

char *NTSERVICEIDS[]={"rc5desnt" /* new id */,"bovrc5nt" /* old id */};

#define W95SERVICEID "bovwin32"
#define SERVICENAME "Distributed.Net RC5/DES Service Client"

extern int realmain( int argc, char *argv[] );
static int win32cli_servicified = 0;

/* ---------------------------------------------------------- */

int win32CliServiceRunning(void) 
{
  return win32cli_servicified;
}  

/* ---------------------------------------------------------- */

int win32CliUninstallService(int quiet) 
{                    /* quiet is used internally by the service itself */
  int retcode = -1;
  const char *msg = NULL;

  if (winGetVersion() >= 2000) /* NT */
    {
    SC_HANDLE myService, scm;
    SERVICE_STATUS status;
    int foundcount = 0, deletedcount = 0;
  
    msg = "The Service Control Manager could not be opened.\n"
          "The distributed.net service client could not be uninstalled.";
  
    scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
    if (scm)
      {
      retcode = 0;
      unsigned int i;
      for (i=0;i<(sizeof(NTSERVICEIDS)/sizeof(NTSERVICEIDS[0]));i++)
        {
        myService = OpenService(scm, NTSERVICEIDS[i], SERVICE_ALL_ACCESS);
        if (myService)
          {
          foundcount++;
          if (QueryServiceStatus(myService, &status) &&
             status.dwCurrentState != SERVICE_STOPPED)
            {
            if (!ControlService(myService, SERVICE_CONTROL_STOP, &status))
              {
              retcode = -1;
              msg = "The distributed.net client is running and "
                    "could not be stopped.\n"
                    "The client has not been uninstalled.";
              }
            }
          CloseServiceHandle(myService);
          }
        if (retcode != 0)
          break;
        }
      if (retcode == 0)
        {
        if (foundcount == 0)
          {
          msg = "The distributed.net client is not installed as an NT service.";
          retcode = 0; //not installed, so retcode zero is ok
          }
        else
          {
          for (i=0;i<(sizeof(NTSERVICEIDS)/sizeof(NTSERVICEIDS[0]));i++)
            {
            myService = OpenService(scm, NTSERVICEIDS[i], SERVICE_ALL_ACCESS|DELETE);
            if (myService)
              {
              if (DeleteService(myService))
                {
                deletedcount++;
                }
              CloseServiceHandle(myService);
              }
            }  
          if (deletedcount == foundcount)
            {
            msg = "The distributed.net client has been uninstalled.\n";
            retcode = 0;
            }
          else 
            {
            retcode = -1;
            if (deletedcount != 0)
              msg = "One or more distributed.net clients has been uninstalled.\n"
                    "However, one or mode deinstallations failed.\n";
            else
              msg = "The distributed.net client could not be uninstalled.\n";
            }
          }
        }
      CloseServiceHandle(scm);
      }
    }
  else if (winGetVersion() >= 400) /* Win9x */
    {
    HKEY srvkey;

    msg = "Unable to open the service registry.\n"
          "The distributed.net client service entry could not deleted.";

    /* unregister a Win95 "RunService" item */
    if (RegOpenKey(HKEY_LOCAL_MACHINE,
        "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
        &srvkey) == ERROR_SUCCESS)
      {
      DWORD valuetype = REG_SZ;
      char buffer[260]; /* maximum registry key length */
      DWORD valuesize = sizeof(buffer);

      int wasinstalled = ( RegQueryValueEx(srvkey, W95SERVICEID, NULL,
                         &valuetype, (unsigned char *)(&buffer[0]), 
                         &valuesize) == ERROR_SUCCESS );

      if ( RegDeleteValue(srvkey, W95SERVICEID) == ERROR_SUCCESS )
        {
        msg = "The distributed.net client's service entry has been deleted.\n",
            "The client has is no longer installed as a service.";
        retcode = 0;
        }
      else if (!wasinstalled)
        {
        msg = "The distributed.net client is not installed as a service.";
        retcode = 0;
        }
      else
        msg = "The distributed.net client's service entry could not\n",
              "be deleted. The client has not be uninstalled.";      

      RegCloseKey(srvkey);
      }

    /* unregister a Win95 "Run" item */
    if (RegOpenKey(HKEY_LOCAL_MACHINE,
      "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
      &srvkey) == ERROR_SUCCESS)
      {
      RegDeleteValue(srvkey, W95SERVICEID);
      RegCloseKey(srvkey);
      }
    }
  
  if (msg && !quiet)
    MessageBox( NULL, msg, SERVICENAME, MB_OK | MB_TASKMODAL );

  return(retcode);
}    

/* ---------------------------------------------------------- */

int win32CliInstallService(int quiet) 
{                    /* quiet is used internally by the service itself */
  int retcode = -1;
  char mypath[260]; /* max size of a reg entry */
  const char *msg = "The distributed.net client could not be installed\n"
                    "as a service.\n";
  
  GetModuleFileName(NULL, mypath, sizeof(mypath));

  if (winGetVersion() >= 2000) /* NT */
    {
    SC_HANDLE myService, scm;
    scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
    if (!scm)
      {
      msg = "Unable to open the Service Control Manager.\n"
        "The distributed.net client could not be installed as a service.\n";
      }
    else
      {
      int reinstalled = 0;
      myService = OpenService(scm, NTSERVICEIDS[0], SERVICE_ALL_ACCESS);
      if (myService)
        {
        CloseServiceHandle(myService);
        msg = "The distributed.net client is already installed\n"
              "as an NT service and could not be reinstalled.";
        win32CliUninstallService(1); /* try to delete it for reinstallation */
        reinstalled = 1;
        }
      strcat(mypath, " -svcrun");
      myService = CreateService(scm, NTSERVICEIDS[0], SERVICENAME,
          SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
          SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
          mypath, 0, 0, 0, 0, 0);
      if (myService)
        {
        CloseServiceHandle(myService);
        sprintf( mypath, 
          "The distributed.net client has been successfully %sinstalled as\n"
          "an NT service and has been configured to start automatically.",
          (reinstalled)?("re-"):("") );
        msg = &mypath[0];
        retcode = 0;
        }
      CloseServiceHandle(scm);
      }
    }
  else if (winGetVersion() >= 400) /* Win9x */
    {
    HKEY srvkey=NULL;
    DWORD dwDisp=NULL;

    /* register a Win95 "RunService" item */
    if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
        "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",0,"",
              REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,
              &srvkey,&dwDisp) == ERROR_SUCCESS)
      {
      strcat( mypath, " -hide" );
      RegSetValueEx(srvkey, W95SERVICEID, 0, REG_SZ, 
                        (unsigned const char *)mypath, strlen(mypath) + 1);
      RegCloseKey(srvkey);

      msg = "The distributed.net client has been successfully\n"
                 "installed as a Windows 9x service.\n\n";
      retcode = 0;
      }

    /* unregister a Win95 "Run" item */
    if (RegOpenKey(HKEY_LOCAL_MACHINE,
        "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
      &srvkey) == ERROR_SUCCESS)
      {
      RegDeleteValue(srvkey, W95SERVICEID);
      RegCloseKey(srvkey);
      }
    }

  if (!quiet)
    MessageBox( NULL, msg, SERVICENAME, MB_OK | MB_TASKMODAL );

  return (retcode);
}

/* ---------------------------------------------------------- */

static SERVICE_STATUS_HANDLE serviceStatusHandle;

void __stdcall ServiceCtrlHandler(DWORD controlCode)
{
  /* update our status to stopped */
  SERVICE_STATUS serviceStatus;
  serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  serviceStatus.dwWin32ExitCode = NO_ERROR;
  serviceStatus.dwServiceSpecificExitCode = 0;
  serviceStatus.dwCheckPoint = 0;
  if (controlCode == SERVICE_CONTROL_SHUTDOWN ||
      controlCode == SERVICE_CONTROL_STOP)
    {
    win32cli_servicified = 0;
    serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
    serviceStatus.dwControlsAccepted = 0;
    serviceStatus.dwWaitHint = 10000;
    RaiseExitRequestTrigger();
    } 
  else 
    {
    /* SERVICE_CONTROL_INTERROGATE */
    serviceStatus.dwCurrentState = SERVICE_RUNNING;
    serviceStatus.dwWaitHint = 0;
    win32cli_servicified = 1;
    }
  SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

/* ---------------------------------------------------------- */

void ServiceMain(DWORD /* Argc */, LPTSTR * /* Argv */)
{
  SERVICE_STATUS serviceStatus;
  char exename[260];
  char *argv[3];
  
  serviceStatusHandle = RegisterServiceCtrlHandler(NTSERVICEIDS[0],
                                              ServiceCtrlHandler);

  /* update our status to running */
  win32cli_servicified = 1;

  serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  serviceStatus.dwCurrentState = SERVICE_RUNNING;
  serviceStatus.dwControlsAccepted = (SERVICE_ACCEPT_SHUTDOWN |
                                      SERVICE_ACCEPT_STOP);
  serviceStatus.dwWin32ExitCode = NO_ERROR;
  serviceStatus.dwServiceSpecificExitCode = 0;
  serviceStatus.dwCheckPoint = 0;
  serviceStatus.dwWaitHint = 0;
  SetServiceStatus(serviceStatusHandle, &serviceStatus);

  GetModuleFileName(NULL, exename, sizeof(exename));
  argv[0] = strrchr(exename, '\\');
  if (argv[0])
    {
    argv[0][0] = 0;
    SetCurrentDirectory(exename);
    argv[0][0] = '\\';
    }
  argv[0] = exename;
  argv[1] = "-hide";
  argv[2] = NULL;

  realmain( 2, &argv[0] ); /* recursive call */

  /* update our status to stopped */
  serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  serviceStatus.dwCurrentState = SERVICE_STOPPED;
  serviceStatus.dwControlsAccepted = 0;
  serviceStatus.dwWin32ExitCode = NO_ERROR;
  serviceStatus.dwServiceSpecificExitCode = 0;
  serviceStatus.dwCheckPoint = 0;
  serviceStatus.dwWaitHint = 0;
  SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

/* ---------------------------------------------------------- */

int win32CliInitializeService(int argc, char *argv[])
{
  static int is_running = 0;  /* actually only needed for nt service */
  int retcode = 0;

  if (winGetVersion() >= 2000) /* NT */
    {
    if (!is_running)
      {
      SERVICE_TABLE_ENTRY serviceTable[] = {
          {NTSERVICEIDS[0], (LPSERVICE_MAIN_FUNCTION) ServiceMain},
          {NULL, NULL}};
      if (StartServiceCtrlDispatcher(serviceTable)) 
        is_running = 1;
      else             
        retcode = -1; /* client was not started from the service manager */
      }  
    }
  else if (winGetVersion() >= 400) /* w9x */
    {
    if (!is_running)  
      {
      //check if we are registered as a w9x "service" (survive logouts)

      HKEY srvkey = NULL;
      int run_as_w9x_service = 0;
      #ifdef W9x_ALWAYS_RUN_AS_SERVICE
      run_as_w9x_service = 1;
      #endif
        
      if (!run_as_w9x_service)
        {
        if (RegOpenKey(HKEY_LOCAL_MACHINE, 
              "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",
            &srvkey) == ERROR_SUCCESS)
          {
          DWORD valuetype = REG_SZ;
          char buffer[260]; // maximum registry key length
          DWORD valuesize = sizeof(buffer);

          run_as_w9x_service = ( RegQueryValueEx(srvkey, 
              W95SERVICEID, NULL, &valuetype, (unsigned char *)(&buffer[0]), 
                               &valuesize) == ERROR_SUCCESS );
          RegCloseKey(srvkey);
          }
        }

      if (run_as_w9x_service)
        {              
        HMODULE kernl = GetModuleHandle("KERNEL32");
        if (kernl)
          {
          typedef DWORD (CALLBACK *ULPRET)(DWORD,DWORD);
          ULPRET func = (ULPRET) GetProcAddress(kernl, 
                                         "RegisterServiceProcess");
          if (func) (*func)(0, 1);
          is_running = 1;
          }
        }
      }
    }

  win32cli_servicified = is_running;
  return retcode;
}

/* ---------------------------------------------------------- */

int win32CliOldStyleInitializeService(void)
{
  if (winGetVersion() >= 2000) /* NT */
    {
    /* ******************** HACK * HACK * HACK * HACK * HACK *********** */
    /* this hack is to speed up the load process on NT. It assumes that the */
    /* presence of progman/exploder means we cannot have been started as */
    /* a service. (since progman/exploder starts only after service init). */
    /* This hack will not work if M$ decides to rename the exploder's */
    /* window class - but it won't break anything. */
    if (FindWindow( "Progman", NULL )!=NULL) 
      return -1;
    /* ************************** END HACK ***************************** */

    if (win32CliInitializeService(0,NULL) != 0)
      return -1;  /* failed to launch. not starting as a service */

    SC_HANDLE myService, scm;
    scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
    
    if (scm)
      {
      myService = OpenService(scm, NTSERVICEIDS[0], SERVICE_QUERY_CONFIG );
      if (myService)
        {
        DWORD cbBytesNeeded;
        LPQUERY_SERVICE_CONFIG pqsc;
        char exename[MAX_PATH];

        QueryServiceConfig(myService, NULL, 0, &cbBytesNeeded);
        pqsc = (LPQUERY_SERVICE_CONFIG)malloc( cbBytesNeeded );
        if (pqsc)
          {
          QueryServiceConfig(myService, pqsc, cbBytesNeeded, &cbBytesNeeded);
      
          GetModuleFileName(NULL, exename, sizeof(exename));
          if ( strcmp( pqsc->lpBinaryPathName, exename ) == 0 )
            {                       //old name without cmdline options
            strcat( exename, " -svcrun" );
            ChangeServiceConfig(myService, pqsc->dwServiceType, 
                pqsc->dwStartType, pqsc->dwErrorControl, exename,
                pqsc->lpLoadOrderGroup, NULL /* &(pqsc->dwTagId) */, 
                pqsc->lpDependencies, pqsc->lpServiceStartName, 
                0, pqsc->lpDisplayName );
            }
          free(pqsc);
          }
        CloseServiceHandle(myService);
        }
      CloseServiceHandle(scm);
      }
    } //if NT
  return 0;
}  
