/*
Copyright 2006 - 2018 Intel Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#if defined(WINSOCK2)
	#include <winsock2.h>
	#include <ws2tcpip.h>
#elif defined(WINSOCK1)
	#include <winsock.h>
	#include <wininet.h>
#endif

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winhttp.h>
#include <shlobj.h>
#include "resource.h"
#include "meshcore/signcheck.h"
#include "meshcore/meshdefines.h"
#include "meshcore/meshinfo.h"
#include "microstack/ILibParsers.h"
#include "microstack/ILibCrypto.h"
#include "meshcore/agentcore.h"
#include "microscript/ILibDuktape_ScriptContainer.h"
#include "microscript/ILibDuktape_Commit.h"
#include <shellscalingapi.h>

#ifndef _MINCORE
// #include "../kvm/kvm.h"
int SetupWindowsFirewall(wchar_t* processname);
int ClearWindowsFirewall(wchar_t* processname);
#endif

#if defined(WIN32) && defined (_DEBUG) && !defined(_MINCORE)
#include <crtdbg.h>
#define _CRTDBG_MAP_ALLOC
#endif

#include <WtsApi32.h>

TCHAR* serviceFile = TEXT("Mesh Agent");
TCHAR* serviceFileOld = TEXT("Mesh Agent v2");
TCHAR* serviceName = TEXT("Mesh Agent background service");
TCHAR* serviceDesc = TEXT("Remote monitoring and management service.");

SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
INT_PTR CALLBACK DialogHandler(HWND, UINT, WPARAM, LPARAM);

MeshAgentHostContainer *agent = NULL;
DWORD g_serviceArgc;
char **g_serviceArgv;

/*
extern int g_TrustedHashSet;
extern char g_TrustedHash[32];
extern char NullNodeId[32];
extern struct PolicyInfoBlock* g_TrustedPolicy;
extern char g_selfid[UTIL_HASHSIZE];
extern struct sockaddr_in6 g_ServiceProxy;
extern char* g_ServiceProxyHost;
extern int g_ServiceConnectFlags;
*/

#if defined(_LINKVM)
extern DWORD WINAPI kvm_server_mainloop(LPVOID Param);
#endif

BOOL IsAdmin()
{
	BOOL admin;
	PSID AdministratorsGroup; 
	SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;

	if ((admin = AllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup)) != 0)
	{
		if (!CheckTokenMembership( NULL, AdministratorsGroup, &admin)) admin = FALSE;
		FreeSid(AdministratorsGroup);
	}
	return admin;
}

BOOL RunAsAdmin(char* args) {
	char szPath[_MAX_PATH + 100];
	if (GetModuleFileNameA(NULL, szPath, _MAX_PATH))
	{
		SHELLEXECUTEINFO sei = { sizeof(sei) };
		sei.hwnd = NULL;
		sei.nShow = SW_NORMAL;
		sei.lpVerb = "runas";
		sei.lpFile = szPath;
		sei.lpParameters = args;
		return ShellExecuteExA(&sei);
	}
	return FALSE;
}

DWORD WINAPI ServiceControlHandler( DWORD controlCode, DWORD eventType, void *eventData, void* eventContext )
{
	switch (controlCode)
	{
		case SERVICE_CONTROL_INTERROGATE:
			break;
		case SERVICE_CONTROL_SHUTDOWN:
		case SERVICE_CONTROL_STOP:
			serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
			SetServiceStatus( serviceStatusHandle, &serviceStatus );
			if (agent != NULL) { MeshAgent_Stop(agent); }
			return(0);
		case SERVICE_CONTROL_POWEREVENT:
			switch (eventType)
			{
				case PBT_APMPOWERSTATUSCHANGE:	// Power status has changed.
					break;
				case PBT_APMRESUMEAUTOMATIC:	// Operation is resuming automatically from a low - power state.This message is sent every time the system resumes.
					break;
				case PBT_APMRESUMESUSPEND:		// Operation is resuming from a low - power state.This message is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered by user input, such as pressing a key.
					break;
				case PBT_APMSUSPEND:			// System is suspending operation.
					break;
				case PBT_POWERSETTINGCHANGE:	// Power setting change event has been received.
					break;
			}
			break;
		case SERVICE_CONTROL_SESSIONCHANGE:
			if (agent == NULL)
			{
				break; // If there isn't an agent, no point in doing anything, cuz nobody will hear us
			}

			switch (eventType)
			{
				case WTS_CONSOLE_CONNECT:		// The session identified by lParam was connected to the console terminal or RemoteFX session.
					break;
				case WTS_CONSOLE_DISCONNECT:	// The session identified by lParam was disconnected from the console terminal or RemoteFX session.
					break;
				case WTS_REMOTE_CONNECT:		// The session identified by lParam was connected to the remote terminal.
					break;
				case WTS_REMOTE_DISCONNECT:		// The session identified by lParam was disconnected from the remote terminal.
					break;
				case WTS_SESSION_LOGON:			// A user has logged on to the session identified by lParam.
				case WTS_SESSION_LOGOFF:		// A user has logged off the session identified by lParam.					
					break;
				case WTS_SESSION_LOCK:			// The session identified by lParam has been locked.
					break;
				case WTS_SESSION_UNLOCK:		// The session identified by lParam has been unlocked.
					break;
				case WTS_SESSION_REMOTE_CONTROL:// The session identified by lParam has changed its remote controlled status.To determine the status, call GetSystemMetrics and check the SM_REMOTECONTROL metric.
					break;
				case WTS_SESSION_CREATE:		// Reserved for future use.
				case WTS_SESSION_TERMINATE:		// Reserved for future use.
					break;
			}
			break;
		default:
			break;
	}

	SetServiceStatus( serviceStatusHandle, &serviceStatus );
	return(0);
}


// Add the uninstallation icon in the Windows Control Panel.
void WINAPI AddUninstallIcon()
{
	/*
	[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\MeshAgent]
	"DisplayName"="Mesh Agent Service - Remote Control Software"
	"Publisher"="MeshCentral"
	"MajorVersion"="2"
	"MinorVersion"="13"
	"InstallLocation"="C:\\Program Files\\Mesh Agent"
	"UninstallString"="C:\\Program Files\\Mesh Agent\\meshuninstaller.bat"
	"DisplayIcon"="C:\\Program Files\\Mesh Agent\\MeshAgent.exe"
	"DisplayVersion"="2.1.3"
	"URLInfoAbout"="http://www.meshcentral.com/"
	"VersionMajor"=dword:00000002
	"VersionMinor"=dword:00000013
	"EstimatedSize"=dword:00208000
	"NoModify"=dword:00000001
	"NoRepair"=dword:00000001
	*/

	int i;
	HKEY hKey;
	if (RegCreateKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent", &hKey) == ERROR_SUCCESS)
	{
		LPCTSTR str;
		char targetexe[_MAX_PATH + 40];
		size_t targetexelen = 0;

		str = "MeshCentral Agent - Remote Control Software\0";
		RegSetValueEx(hKey, "DisplayName", 0, REG_SZ, (LPBYTE)str, (DWORD)strlen(str)+1);

		str = "Open Source\0";
		RegSetValueEx(hKey, "Publisher", 0, REG_SZ, (LPBYTE)str, (DWORD)strlen(str) + 1);

		str = "1\0";
		RegSetValueEx(hKey, "MajorVersion", 0, REG_SZ, (LPBYTE)str, (DWORD)strlen(str) + 1);

		str = "0\0";
		RegSetValueEx(hKey, "MinorVersion", 0, REG_SZ, (LPBYTE)str, (DWORD)strlen(str) + 1);

		// Install location
		if (SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, targetexe) != S_FALSE) {
			targetexelen = strnlen_s(targetexe, _MAX_PATH + 40);
			if (targetexelen <= MAX_PATH) {
				memcpy_s(targetexe + targetexelen, _MAX_PATH + 40 - targetexelen, "\\Mesh Agent\\\0", 13);
				RegSetValueEx(hKey, "InstallLocation", 0, REG_SZ, (LPBYTE)targetexe, (DWORD)strlen(targetexe) + 1);
			}
		}

		// Uninstall command
		if (SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, targetexe) != S_FALSE) {
			targetexelen = strnlen_s(targetexe, _MAX_PATH + 40);
			if (targetexelen <= MAX_PATH) {
				memcpy_s(targetexe + targetexelen, _MAX_PATH + 40 - targetexelen, "\\Mesh Agent\\MeshAgent.exe -fulluninstall\0", 41);
				RegSetValueEx(hKey, "UninstallString", 0, REG_SZ, (LPBYTE)targetexe, (DWORD)strlen(targetexe) + 1);
			}
		}

		// Display icon
		if (SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, targetexe) != S_FALSE) {
			targetexelen = strnlen_s(targetexe, _MAX_PATH + 40);
			if (targetexelen <= MAX_PATH) {
				memcpy_s(targetexe + targetexelen, _MAX_PATH + 40 - targetexelen, "\\Mesh Agent\\MeshAgent.exe\0", 26);
				RegSetValueEx(hKey, "DisplayIcon", 0, REG_SZ, (LPBYTE)targetexe, (DWORD)strlen(targetexe) + 1);
			}
		}

		str = "1.0.0\0";
		RegSetValueEx(hKey, "DisplayVersion", 0, REG_SZ, (LPBYTE)str, (DWORD)strlen(str) + 1);

		str = "http://www.meshcentral.com/\0"; // TODO - Change this to .msg content
		RegSetValueEx(hKey, "URLInfoAbout", 0, REG_SZ, (LPBYTE)str, (DWORD)strlen(str) + 1);

		i = 2;
		RegSetValueEx(hKey, "VersionMajor", 0, REG_DWORD, (BYTE*)&i, (DWORD)4);

		i = 13;
		RegSetValueEx(hKey, "VersionMinor", 0, REG_DWORD, (BYTE*)&i, (DWORD)4);

		i = 0x00208000;
		RegSetValueEx(hKey, "EstimatedSize", 0, REG_DWORD, (BYTE*)&i, (DWORD)4);

		i = 1;
		RegSetValueEx(hKey, "NoModify", 0, REG_DWORD, (BYTE*)&i, (DWORD)4);
		RegSetValueEx(hKey, "NoRepair", 0, REG_DWORD, (BYTE*)&i, (DWORD)4);

		RegCloseKey(hKey);
	}
	else
	{
		printf("Error writing to registry, try running as administrator.");
	}

}


void WINAPI RemoveUninstallIcon()
{
	RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent");
}


void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
	CONTEXT winException;
	size_t len = 0;
	WCHAR str[_MAX_PATH];


	UNREFERENCED_PARAMETER( argc );
	UNREFERENCED_PARAMETER( argv );

	// Initialise service status
	serviceStatus.dwServiceType = SERVICE_WIN32;
	serviceStatus.dwCurrentState = SERVICE_STOPPED;
	serviceStatus.dwControlsAccepted = 0;
	serviceStatus.dwWin32ExitCode = NO_ERROR;
	serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
	serviceStatus.dwCheckPoint = 0;
	serviceStatus.dwWaitHint = 0;
	serviceStatusHandle = RegisterServiceCtrlHandlerExA(serviceName, (LPHANDLER_FUNCTION_EX)ServiceControlHandler, NULL);

	if (serviceStatusHandle)
	{
		// Service is starting
		serviceStatus.dwCurrentState = SERVICE_START_PENDING;
		SetServiceStatus(serviceStatusHandle, &serviceStatus);

		// Service running
		serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SESSIONCHANGE);
		serviceStatus.dwCurrentState = SERVICE_RUNNING;
		SetServiceStatus( serviceStatusHandle, &serviceStatus);

		// Get our own executable name
		GetModuleFileNameW(NULL, str, _MAX_PATH);

#ifndef _MINCORE
		// Setup firewall
		SetupWindowsFirewall(str);
#endif

		// Run the mesh agent
		CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

		__try
		{
			agent = MeshAgent_Create(0);
			MeshAgent_Start(agent, g_serviceArgc, g_serviceArgv);
			agent = NULL;
		}
		__except (ILib_WindowsExceptionFilter(GetExceptionCode(), GetExceptionInformation(), &winException))
		{
			ILib_WindowsExceptionDebug(&winException);
		}
		CoUninitialize();

		// Service was stopped
		serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
		SetServiceStatus(serviceStatusHandle, &serviceStatus);

		// Service is now stopped
		serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
		serviceStatus.dwCurrentState = SERVICE_STOPPED;
		SetServiceStatus(serviceStatusHandle, &serviceStatus);
	}
}

int RunService(int argc, char* argv[])
{
	SERVICE_TABLE_ENTRY serviceTable[2];
	serviceTable[0].lpServiceName = serviceName;
	serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
	serviceTable[1].lpServiceName = NULL;
	serviceTable[1].lpServiceProc = NULL;
	g_serviceArgc = argc;
	g_serviceArgv = argv;

	return StartServiceCtrlDispatcher( serviceTable );
}

BOOL InstallService()
{
	SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );
	SERVICE_DESCRIPTION sd;
	SERVICE_DELAYED_AUTO_START_INFO as;
	SERVICE_FAILURE_ACTIONS fa;
	SC_ACTION failactions[3];
	BOOL r = FALSE;

	if ( serviceControlManager )
	{
		char path[1024];
		if (GetModuleFileName( 0, (LPTSTR)path, 1024) > 0)
		{
			// Install the service
			SC_HANDLE service = CreateService( 
				serviceControlManager,
				serviceFile,
				serviceName,
				SERVICE_ALL_ACCESS,
				SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
				SERVICE_AUTO_START,
				SERVICE_ERROR_IGNORE,
				(LPCTSTR)path,
				0, 0, 0, 0, 0 );

			if (service)
			{
				// Update the service description
				sd.lpDescription = serviceDesc;
				ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd);

				// Update the service auto-start
				as.fDelayedAutostart = FALSE;
				ChangeServiceConfig2(service, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &as);

				// Update the faliure action
				failactions[0].Type = SC_ACTION_RESTART;
				failactions[0].Delay = 60000;                          // Wait 1 minutes before faliure restart (milliseconds)
				failactions[1].Type = SC_ACTION_RESTART;
				failactions[1].Delay = 60000;                          // Wait 1 minutes before faliure restart (milliseconds)
				failactions[2].Type = SC_ACTION_RESTART;
				failactions[2].Delay = 60000;
				memset(&fa, 0, sizeof(SERVICE_FAILURE_ACTIONS));
				fa.dwResetPeriod = 86400;					// After 1 days, reset the faliure counters (seconds)
				fa.cActions = 3;
				fa.lpsaActions = failactions;
				r = ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS, &fa);

				// Cleanup
				CloseServiceHandle( service );
				#ifdef _DEBUG
				//ILIBMESSAGE("Mesh service installed successfully");
				#endif
			}
			else
			{
				#ifdef _DEBUG
				if(GetLastError() == ERROR_SERVICE_EXISTS)
				{
					ILIBMESSAGE("Mesh service already exists.");
				}
				else
				{
					ILIBMESSAGE("Mesh service was not Installed Successfully.");
				}
				#endif
			}
		}

		CloseServiceHandle( serviceControlManager );
	}
	return r;
}

int UninstallService(TCHAR* serviceName)
{
	int r = 0;
	SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT);

	if (serviceControlManager)
	{
		SC_HANDLE service = OpenService( serviceControlManager, serviceName, SERVICE_QUERY_STATUS | DELETE );
		if (service)
		{
			SERVICE_STATUS serviceStatusEx;
			if ( QueryServiceStatus( service, &serviceStatusEx ) )
			{
				if ( serviceStatusEx.dwCurrentState == SERVICE_STOPPED )
				{
					if (DeleteService(service))
					{
						#ifdef _DEBUG
						//ILIBMESSAGE("Mesh service removed successfully");
						#endif
						r = 1;
					}
					else
					{
						#ifdef _DEBUG
						DWORD dwError = GetLastError();
						if(dwError == ERROR_ACCESS_DENIED) {
							ILIBMESSAGE("Access denied while trying to remove mesh service");
						}
						else if(dwError == ERROR_INVALID_HANDLE) {
							ILIBMESSAGE("Handle invalid while trying to remove mesh service");
						}
						else if(dwError == ERROR_SERVICE_MARKED_FOR_DELETE) {
							ILIBMESSAGE("Mesh service already marked for deletion");
						}
						#endif
					}
				}
				else
				{
					r = 2;
					#ifdef _DEBUG
					ILIBMESSAGE("Mesh service is still running");
					#endif
				}
			}
			CloseServiceHandle( service );
		}
		CloseServiceHandle( serviceControlManager );
	}
	return r;
}

// SERVICE_STOPPED				  1    The service is not running.
// SERVICE_START_PENDING		  2    The service is starting.
// SERVICE_STOP_PENDING			  3    The service is stopping.
// SERVICE_RUNNING				  4    The service is running.
// SERVICE_CONTINUE_PENDING		  5    The service continue is pending.
// SERVICE_PAUSE_PENDING		  6    The service pause is pending.
// SERVICE_PAUSED				  7    The service is paused.
// SERVICE_NOT_INSTALLED		100    The service is not installed.
int GetServiceState(LPCSTR servicename)
{
	int r = 0;
	SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);

	if (serviceControlManager)
	{
		SC_HANDLE service = OpenService( serviceControlManager, servicename, SERVICE_QUERY_STATUS );
		if (service)
		{
			SERVICE_STATUS serviceStatusEx;
			if ( QueryServiceStatus( service, &serviceStatusEx) )
			{
				r = serviceStatusEx.dwCurrentState;
			}
			CloseServiceHandle( service );
		}
		else
		{
			r = 100;
		}
		CloseServiceHandle( serviceControlManager );
	}
	return r;
}


int LaunchService(LPCSTR servicename)
{
	int r = 0;
	SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SERVICE_QUERY_STATUS | SERVICE_START);

	if (serviceControlManager)
	{
		SC_HANDLE service = OpenService( serviceControlManager, servicename, SERVICE_QUERY_STATUS | SERVICE_START );
		if (service)
		{
			SERVICE_STATUS serviceStatusEx;
			if ( QueryServiceStatus( service, &serviceStatusEx) )
			{
				if (serviceStatusEx.dwCurrentState == SERVICE_STOPPED ) { if (StartService(service, 0, NULL) == TRUE) { r = 1; } } else { r = 2; }
			}
			CloseServiceHandle( service );
		}
		CloseServiceHandle( serviceControlManager );
	}
	return r;
}

int StopService(LPCSTR servicename)
{
	int r = 0;
	SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SERVICE_QUERY_STATUS | SERVICE_STOP);

	if (serviceControlManager)
	{
		SC_HANDLE service = OpenService( serviceControlManager, servicename, SERVICE_QUERY_STATUS | SERVICE_STOP );
		if (service)
		{
			SERVICE_STATUS serviceStatusEx;
			if ( QueryServiceStatus( service, &serviceStatusEx) )
			{
				if (serviceStatusEx.dwCurrentState != SERVICE_STOPPED )
				{
					if (ControlService(service, SERVICE_CONTROL_STOP, &serviceStatusEx) == FALSE)
					{
						// TODO: Unable to stop service
						#ifdef _DEBUG
						ILIBMESSAGE("Unable to stop service");
						#endif
					}
					else
					{
						Sleep(3000);
						r = 1;
					}
				}
			}
			CloseServiceHandle( service );
		}
		CloseServiceHandle( serviceControlManager );
	}
	return r;
}

int RunProcess(char* exe, int waitForExit)
{
	BOOL r = TRUE;
	int count = 50;
	DWORD exitcode;
	STARTUPINFOA info = {sizeof(info)};
	PROCESS_INFORMATION processInfo;
	if (CreateProcessA(NULL, exe, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo) == 0) return 0;
	if (waitForExit != 0) {
		do
		{
			Sleep(100);
			r = GetExitCodeProcess(processInfo.hProcess, &exitcode);
			if (exitcode == STILL_ACTIVE) r = 0;
		} while (r == 0 && count-- > 0);
	}
	CloseHandle(processInfo.hProcess);
	CloseHandle(processInfo.hThread);
	return r;
}

/*
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

	return _tmain( 0, NULL );
}
*/

#ifndef _MINCORE
void fullinstall(int uninstallonly, char* proxy, int proxylen, char* tag, int taglen)
{
	int r = 0;
	int loops = 0;
	char targetexe2[_MAX_PATH + 40];
	char *targetexe = targetexe2 + 1;
	size_t targetexelen = 0;
	char selfexe[_MAX_PATH];
	size_t selfexelen = 0;
	char setup1[_MAX_PATH];
	char setup2[_MAX_PATH];
	int setup1len;
	int setup2len;

	if (IsAdmin() == FALSE) { printf("Requires administrator permissions.\r\n"); return; }
	if (uninstallonly != 0) { printf("Performing uninstall...\r\n"); } else { printf("Performing install...\r\n"); }

	HKEY hKey;
	DWORD len = 0;
	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\Mesh Agent", 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
	{
		if (RegQueryValueExA(hKey, "ImagePath", NULL, NULL, NULL, &len) == ERROR_SUCCESS && len > 0)
		{
			char *ipath = ILibMemory_Allocate(len, 0, NULL, NULL);
			RegQueryValueExA(hKey, "ImagePath", NULL, NULL, ipath, &len);
			printf("ImagePath: %s\n", ipath);

			STARTUPINFOA info = { sizeof(info) };
			PROCESS_INFORMATION processInfo;

			sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "%s -exec \"try { require('service-manager').manager.uninstallService('meshagentDiagnostic'); require('task-scheduler').delete('meshagentDiagnostic/periodicStart').then(function(){process.exit();}, function(){process.exit();}); } catch(e){process.exit();}\"", ipath);
			CreateProcessA(NULL, ILibScratchPad, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &info, &processInfo);
			CloseHandle(processInfo.hProcess);
			CloseHandle(processInfo.hThread);
			free(ipath);
		}
		RegCloseKey(hKey);
	}




	// Stop and remove the service
	StopService(serviceFile);

	// Wait for the service to stop
	int serviceStateLoopCount = 0;;
	int serviceState;
	do {
		serviceStateLoopCount++;
		Sleep(100);
		serviceState = GetServiceState(serviceFile);
	} while ((serviceState == 3) && (serviceStateLoopCount < 400));
	UninstallService(serviceFile);
	UninstallService(serviceFileOld);

	// Get our own executable
	selfexelen = GetModuleFileNameA(NULL, selfexe, _MAX_PATH);

	// Get the target executable
	if (SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, targetexe) != S_FALSE)
	{
		targetexe2[0] = '\"';
		targetexelen = strnlen_s(targetexe, _MAX_PATH + 40);
		if (targetexelen <= MAX_PATH) memcpy_s(targetexe + targetexelen, _MAX_PATH + 40 - targetexelen, "\\Mesh Agent\\MeshAgent.exe\" -uninstall", 38);
		targetexelen += 25;
	}

	// Check if we are uninstalling ourself
	if ((uninstallonly != 0) && (targetexelen == selfexelen) && (memcmp(selfexe, targetexe, targetexelen) == 0)) {
		// Copy ourself to a temp folder and run full uninstall.
		char tempPath[_MAX_PATH + 40];
		int tempPathLen = GetTempPathA(_MAX_PATH, tempPath);
		memcpy_s(tempPath + tempPathLen, _MAX_PATH + 40 - tempPathLen, "MeshAgent.exe\0", 15);
		remove(tempPath);
		util_CopyFile(selfexe, tempPath, FALSE);
		memcpy_s(tempPath + tempPathLen, _MAX_PATH + 40 - tempPathLen, "MeshAgent.exe -fulluninstall\0", 30);
		RunProcess(tempPath, 0); // Don't wait for the process to terminate since we want to self-delete.
		return;
	}

	// Call uninstall, this will remove the firewall rules.
	RunProcess(targetexe2, 1);

#ifdef _MINCORE
	// Remove the MeshAgent registry keys
	RegDeleteKeyEx(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2", KEY_WOW64_32KEY, 0);
	RegDeleteKeyEx(HKEY_CURRENT_USER, "Software\\Open Source\\MeshAgent2", KEY_WOW64_32KEY, 0);
#else
	// Remove the MeshAgent registry keys
	RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2");
	RegDeleteKey(HKEY_CURRENT_USER, "Software\\Open Source\\MeshAgent2");
#endif

	// Remove the uninstall icon from the control panel if present
	RemoveUninstallIcon();

	// Check if selfexe is already located at the target, if so, skip to copy steps.
	if ((uninstallonly != 0) || (targetexelen != selfexelen) || (memcmp(selfexe, targetexe, targetexelen) != 0))
	{
		// Remove the target executable, wait if needed
		int selfExeDelLoopCount = 0;;
		int selfExeDel;
		targetexe[targetexelen] = 0;
		do {
			Sleep(100);
			selfExeDelLoopCount++;
			selfExeDel = remove(targetexe);
		} while ((selfExeDel != 0) && (selfExeDel != -1) && (selfExeDelLoopCount < 400));

		// Remove "[Executable].msh" file
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 259) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "msh", 4);
		setup2[setup2len] = 0;
		remove(setup2);

		// Remove "[Executable].mshx" file
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 259) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "mshx", 5);
		setup2[setup2len + 1] = 0;
		remove(setup2);


		// Remove "[Executable].proxy" file
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 257) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "proxy", 6);
		setup2[setup2len + 2] = 0;
		remove(setup2);

		// Remove "[Executable].tag" file
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 259) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "tag", 4);
		setup2[setup2len] = 0;
		remove(setup2);

		// Remove "[Executable].log" file
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 259) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "log", 4);
		setup2[setup2len] = 0;
		remove(setup2);

		// Remove "[Executable].db" file
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 256) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "db", 3);
		setup2[setup2len] = 0;
		remove(setup2);

		// Remove the folder.
		targetexe[targetexelen - 14] = 0;
		RemoveDirectoryA(targetexe);

		if (uninstallonly != 0) return;

		// Get the target executable, create folders if needed
		if (!CreateDirectoryA(targetexe, NULL) && GetLastError() == ERROR_ACCESS_DENIED) { ILIBMESSAGE("Access denied (1)"); return; }
		targetexe[targetexelen - 14] = '\\';

		// Attempt to copy our own exe over the original exe
		loops = 0;
		while (!util_CopyFile(selfexe, targetexe, TRUE))
		{
			if (GetLastError() == ERROR_ACCESS_DENIED) { ILIBMESSAGE("Access denied (2)"); return; }
			if (loops++ > 5) { ILIBMESSAGE("Error copying executable file"); return; }
			Sleep(5000);
		}

		// Try to copy "[Executable].msh" file to target directory
		if ((setup1len = (int)strnlen_s(selfexe, sizeof(selfexe))) < 4) return;
		memcpy_s(setup1, sizeof(setup1), selfexe, setup1len);
		memcpy_s(setup1 + (setup1len - 3), sizeof(setup1) - setup1len - 3, "msh", 4);
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 259) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "msh", 4);
		util_CopyFile(setup1, setup2, TRUE);

		// Write the tag if one was passed
		if (tag != NULL)
		{
			FILE *SourceFile = NULL;
			if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 259) return;
			memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
			memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "tag", 4);
			if (taglen > 0) {
				fopen_s(&SourceFile, setup2, "wb");
				if (SourceFile != NULL)
				{
					if (fwrite(tag, sizeof(char), taglen, SourceFile)) {}
					fclose(SourceFile);
				}
			}
			else
			{
				remove(setup2);
			}
		}

		// Setup proxy filenames
		if ((setup1len = (int)strnlen_s(selfexe, sizeof(selfexe))) < 4) return;
		memcpy_s(setup1, sizeof(setup1), selfexe, setup1len);
		memcpy_s(setup1 + (setup1len - 3), sizeof(setup1) - setup1len - 3, "proxy", 6);
		if ((setup2len = (int)strnlen_s(targetexe, _MAX_PATH + 40)) < 4 || setup2len > 259) return;
		memcpy_s(setup2, sizeof(setup2), targetexe, setup2len);
		memcpy_s(setup2 + (setup2len - 3), sizeof(setup2) - setup2len - 3, "proxy", 6);

		if (proxy != NULL && proxylen > 0)
		{
			// Use the specified proxy in the command line switch
			FILE *SourceFile = NULL;
			fopen_s(&SourceFile, setup2, "wb");
			if (SourceFile != NULL)
			{
				if (fwrite(proxy, sizeof(char), proxylen, SourceFile)) {}
				fclose(SourceFile);
			}
		}
		else
		{
			// Try to copy "[Executable].proxy" file to target directory
			if (util_CopyFile(setup1, setup2, TRUE) == FALSE)
			{
				// Failed to copy proxy file, lets try to create one.
				WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyEx;
				if (WinHttpGetIEProxyConfigForCurrentUser(&proxyEx))
				{
					if (proxyEx.lpszProxy != NULL)
					{
						FILE *SourceFile = NULL;
						size_t len;
						if (wcstombs_s(&len, ILibScratchPad, 4095, proxyEx.lpszProxy, 2000) == 0)
						{
							char* ptr = strstr(ILibScratchPad, "https=");
							if (ptr != NULL) {
								char* ptr2 = strstr(ptr, ";");
								ptr += 6;
								if (ptr2 != NULL) ptr2[0] = 0;
							} else {
								ptr = ILibScratchPad;
							}
							fopen_s(&SourceFile, setup2, "wb");
							if (SourceFile != NULL)
							{
								if (fwrite(ptr, sizeof(char), strnlen_s(ptr, sizeof(ILibScratchPad)), SourceFile)) {}
								fclose(SourceFile);
							}
						}
						GlobalFree(proxyEx.lpszProxy);
					}

					// Release the rest of the proxy settings
					if (proxyEx.lpszAutoConfigUrl != NULL) GlobalFree(proxyEx.lpszAutoConfigUrl);
					if (proxyEx.lpszProxyBypass != NULL) GlobalFree(proxyEx.lpszProxyBypass);
				}
			}
		}
	}

	// Add the uninstall icon in the control panel
	AddUninstallIcon();

	/*
#if defined(_LINKVM)
	// Setup the SendSAS permission
	kvm_setupSasPermissions();
#endif
	*/

	// Attempt to start the updated service up again
	memcpy(targetexe + targetexelen, "\" -install", 11);
	r = RunProcess(targetexe2, 1);
	memcpy(targetexe + targetexelen, "\" -start", 9);
	r = RunProcess(targetexe2, 1);
}
#endif


ILibTransport_DoneState kvm_serviceWriteSink(char *buffer, int bufferLen, void *reserved)
{
	DWORD len;
	WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bufferLen, &len, NULL);
	return ILibTransport_DoneState_COMPLETE;
}
BOOL CtrlHandler(DWORD fdwCtrlType)
{
	switch (fdwCtrlType)
	{
		// Handle the CTRL-C signal. 
	case CTRL_C_EVENT:
	case CTRL_BREAK_EVENT:
	{
		if (agent != NULL) { MeshAgent_Stop(agent); }
		return TRUE;
	}
	default:
		return FALSE;
	}
}
int main(int argc, char* argv[])
{
	int i;
	size_t str2len = 0;// , proxylen = 0, taglen = 0;
	wchar_t str[_MAX_PATH];
	char str2[_MAX_PATH];
	char* proxyarg = NULL;
	char* tagarg = NULL;
	CONTEXT winException;
	int retCode = 0;

	/*
#ifndef NOMESHCMD
	// Check if this is a Mesh command operation
	if (argc >= 1 && strlen(argv[0]) >= 7 && strcasecmp(argv[0] + strlen(argv[0]) - 7, "meshcmd") == 0) return MeshCmd_ProcessCommand(argc, argv, 1);
	if (argc >= 2 && strcasecmp(argv[1], "meshcmd") == 0) return MeshCmd_ProcessCommand(argc, argv, 2);
#endif
	*/

	//CoInitializeEx(NULL, COINIT_MULTITHREADED);

	if (argc > 1 && strcasecmp(argv[1], "-info") == 0)
	{
		printf("Compiled on: %s, %s\n", __TIME__, __DATE__);
		if (SOURCE_COMMIT_HASH != NULL && SOURCE_COMMIT_DATE != NULL)
		{
			printf("   Commit Hash: %s\n", SOURCE_COMMIT_HASH);
			printf("   Commit Date: %s\n", SOURCE_COMMIT_DATE);
		}
#ifndef MICROSTACK_NOTLS
		printf("Using %s\n", SSLeay_version(SSLEAY_VERSION));
#endif
		return(0);
	}

	if (argc > 2 && strcasecmp(argv[1], "-faddr") == 0)
	{
#ifdef WIN64
		uint64_t addrOffset = 0;
		sscanf_s(argv[2] + 2, "%016llx", &addrOffset);
#else
		uint32_t addrOffset = 0;
		sscanf_s(argv[2] + 2, "%x", &addrOffset);
#endif
		ILibChain_DebugOffset(ILibScratchPad, sizeof(ILibScratchPad), (uint64_t)addrOffset);
		printf("%s", ILibScratchPad);
		return(0);
	}

	if (argc > 2 && strcasecmp(argv[1], "-fdelta") == 0)
	{
		uint64_t delta = 0;
		sscanf_s(argv[2], "%lld", &delta);
		ILibChain_DebugDelta(ILibScratchPad, sizeof(ILibScratchPad), delta);
		printf("%s", ILibScratchPad);
		return(0);
	}

	char *integratedJavaScript;
	int integragedJavaScriptLen;
	ILibDuktape_ScriptContainer_CheckEmbedded(&integratedJavaScript, &integragedJavaScriptLen);
	if (argc > 2 && strcmp(argv[1], "-exec") == 0 && integragedJavaScriptLen == 0)
	{
		integratedJavaScript = ILibString_Copy(argv[2], -1);
		integragedJavaScriptLen = (int)strnlen_s(integratedJavaScript, sizeof(ILibScratchPad));
	}
	if (argc > 1 && strcasecmp(argv[1], "-nodeid") == 0)
	{
		char script[] = "console.log(require('_agentNodeId')());process.exit();";
		integratedJavaScript = ILibString_Copy(script, (int)sizeof(script) - 1);
		integragedJavaScriptLen = (int)sizeof(script) - 1;
	}

	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

	// Process extra switches
	for (i = 0; i < argc; i++)
	{
		str2len = strnlen_s(argv[i], _MAX_PATH - 1);
		if (str2len > 7 && memcmp(argv[i], "-proxy:", 7) == 0) { proxyarg = argv[i] + 7; } // Set the HTTPS proxy
		else if (str2len >= 5 && memcmp(argv[i], "-tag:", 5) == 0) { tagarg = argv[i] + 5; } // Set the TAG
	}

	/*
	#ifdef _MINCORE
	if (argc == 2 && ((strcasecmp(argv[1], "-?") == 0) || (strcasecmp(argv[1], "/?") == 0)))
	{
		printf("Mesh Agent v%d available switches:\r\n  start             Start the service.\r\n  restart           Restart the service.\r\n  stop              Stop the service.\r\n  -signcheck        Perform self-check.\r\n  -install          Install the service from this location.\r\n  -uninstall        Remove the service from this location.\r\n  -nodeidhex        Return the current agent identifier.\r\n  -proxy:host:port  Specifiy an HTTPS proxy (after -fullinstall only).\r\n  -tag:xxx          Specifiy a agent tag  (after -fullinstall only).\r\n\r\n  -resetnodeid      Reset the NodeID next time the service is started.", MESH_AGENT_VERSION);
		return 0;
	}
	#else
	if (argc == 2 && ((strcasecmp(argv[1], "-?") == 0) || (strcasecmp(argv[1], "/?") == 0)))
	{
		//printf("Mesh Agent v%d available switches:\r\n  start             Start the service.\r\n  restart           Restart the service.\r\n  stop              Stop the service.\r\n  -proxy:host:port  Specifiy an HTTPS proxy.\r\n  -leader           Force the agent to always be a leader.\r\n  -signcheck        Perform self-check.\r\n  -install          Install the service from this location.\r\n  -uninstall        Remove the service from this location.\r\n  -nodeidhex        Return the current agent identifier.\r\n  -fullinstall      Copy agent into program files, install and launch.\r\n  -fulluninstall    Stop agent and clean up the program files location.\r\n  -loadcert:c.pem   Load a pem cert as node certificate.\r\n", MESH_AGENT_VERSION);
		printf("Mesh Agent v%d available switches:\r\n  start             Start the service.\r\n  restart           Restart the service.\r\n  stop              Stop the service.\r\n  -signcheck        Perform self-check.\r\n  -install          Install the service from this location.\r\n  -uninstall        Remove the service from this location.\r\n  -nodeidhex        Return the current agent identifier.\r\n  -fullinstall      Copy agent into program files, install and launch.\r\n  -fulluninstall    Stop agent and clean up the program files location.\r\n  -proxy:host:port  Specifiy an HTTPS proxy (after -fullinstall only).\r\n  -tag:xxx          Specifiy a agent tag  (after -fullinstall only).\r\n  -resetnodeid      Reset the NodeID next time the service is started.", MESH_AGENT_VERSION);
		return 0;
	}
	#endif
	*/
	
	#if defined(_LINKVM)
	if (argc > 1 && strcasecmp(argv[1], "-kvm0") == 0)
	{		
		void **parm = (void**)ILibMemory_Allocate(3 * sizeof(void*), 0, 0, NULL);
		parm[0] = kvm_serviceWriteSink;
		((int*)&(parm[2]))[0] = 0;

		HMODULE shCORE = LoadLibraryExA((LPCSTR)"Shcore.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
		DpiAwarenessFunc dpiAwareness = NULL;
		if (shCORE != NULL)
		{
			if ((dpiAwareness = (DpiAwarenessFunc)GetProcAddress(shCORE, (LPCSTR)"SetProcessDpiAwareness")) == NULL)
			{
				FreeLibrary(shCORE);
				shCORE = NULL;
			}
		}
		if (dpiAwareness != NULL)
		{
			dpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
			FreeLibrary(shCORE);
			shCORE = NULL;
		}
		else
		{
			SetProcessDPIAware();
		}

		kvm_server_mainloop((void*)parm);
		return 0;
	}
	else if (argc > 1 && strcasecmp(argv[1], "-kvm1") == 0)
	{
		void **parm = (void**)ILibMemory_Allocate(3 * sizeof(void*), 0, 0, NULL);
		parm[0] = kvm_serviceWriteSink;
		((int*)&(parm[2]))[0] = 1;

		HMODULE shCORE = LoadLibraryExA((LPCSTR)"Shcore.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
		DpiAwarenessFunc dpiAwareness = NULL;
		if (shCORE != NULL)
		{
			if ((dpiAwareness = (DpiAwarenessFunc)GetProcAddress(shCORE, (LPCSTR)"SetProcessDpiAwareness")) == NULL)
			{
				FreeLibrary(shCORE);
				shCORE = NULL;
			}
		}
		if (dpiAwareness != NULL)
		{
			dpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
			FreeLibrary(shCORE);
			shCORE = NULL;
		}
		else
		{
			SetProcessDPIAware();
		}


		kvm_server_mainloop((void*)parm);
		return 0;
	}
	#endif

#ifdef _MINCORE
	if (argc > 1 && strcasecmp(argv[1], "-signcheck") == 0)
	{
		// Check the signature of out own executable
		util_openssl_init();
		printf("%d", signcheck_verifysign(argv[0], 0));
		util_openssl_uninit();
		return 0;
	}
#else
	
	if (integratedJavaScript != NULL || (argc > 0 && strcasecmp(argv[0], "--slave") == 0) || (argc > 1 && ((strcasecmp(argv[1], "run") == 0) || (strcasecmp(argv[1], "connect") == 0) || (strcasecmp(argv[1], "--slave") == 0))))
	{
		// Run the mesh agent in console mode, since the agent is compiled for windows service, the KVM will not work right. This is only good for testing.
		SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); // Set SIGNAL on windows to listen for Ctrl-C

		__try
		{
			int capabilities = 0;
			if (argc > 1 && ((strcasecmp(argv[1], "connect") == 0))) { capabilities = MeshCommand_AuthInfo_CapabilitiesMask_TEMPORARY; }
			agent = MeshAgent_Create(capabilities);
			agent->meshCoreCtx_embeddedScript = integratedJavaScript;
			agent->meshCoreCtx_embeddedScriptLen = integragedJavaScriptLen;
			if (integratedJavaScript != NULL || (argc > 1 && (strcasecmp(argv[1], "run") == 0 || strcasecmp(argv[1], "connect") == 0))) { agent->runningAsConsole = 1; }
			MeshAgent_Start(agent, argc, argv);
			retCode = agent->exitCode;
			MeshAgent_Destroy(agent);
			agent = NULL;
		}
		__except (ILib_WindowsExceptionFilter(GetExceptionCode(), GetExceptionInformation(), &winException))
		{
			ILib_WindowsExceptionDebug(&winException);
		}
		return(retCode);
	}
	else if (argc > 1 && (strcasecmp(argv[1], "state") == 0))
	{
		// SERVICE_STOPPED				  1    The service is not running.
		// SERVICE_START_PENDING		  2    The service is starting.
		// SERVICE_STOP_PENDING			  3    The service is stopping.
		// SERVICE_RUNNING				  4    The service is running.
		// SERVICE_CONTINUE_PENDING		  5    The service continue is pending.
		// SERVICE_PAUSE_PENDING		  6    The service pause is pending.
		// SERVICE_PAUSED				  7    The service is paused.
		// SERVICE_NOT_INSTALLED		100    The service is not installed.
		int serviceState = GetServiceState(serviceFile);
		if (serviceState == 1) { printf("Stopped"); }
		else if (serviceState == 2) { printf("Start Pending"); }
		else if (serviceState == 3) { printf("Stop Pending"); }
		else if (serviceState == 4) { printf("Running"); }
		else if (serviceState == 5) { printf("Continue Pending"); }
		else if (serviceState == 6) { printf("Pause Pending"); }
		else if (serviceState == 7) { printf("Paused"); }
		else if (serviceState == 100) { printf("Not installed"); }
		return serviceState;
	}
	else if (argc > 1 && strcasecmp(argv[1], "-signcheck") == 0 && GetModuleFileNameA(NULL, str2, _MAX_PATH) > 5)
	{
		// Check the signature of out own executable
#ifndef MICROSTACK_NOTLS
		util_openssl_init();
		printf("%d", signcheck_verifysign(str2, 0));
		util_openssl_uninit();
#else
		printf("Cannot verify without OpenSSL support");
#endif
		return 0;
	}
#endif
	else if (argc > 1 && (strcasecmp(argv[1], "start") == 0 || strcasecmp(argv[1], "-start") == 0))
	{
		// Ask the service manager to launch the service
		int r = LaunchService(serviceFile);
		if (r == 0) { printf("Failed to start mesh agent"); }
		else if (r == 1) { printf("Started the mesh agent"); }
		else if (r == 2) { printf("Mesh agent already running"); }
	}
	else if (argc > 1 && (strcasecmp(argv[1], "stop") == 0 || strcasecmp(argv[1], "-stop") == 0))
	{
		// Ask the service manager to stop the service
		if (StopService(serviceFile) == 1) { printf("Stopped mesh agent"); } else { printf("Failed to stop mesh agent"); }
	}
	else if (argc > 1 && (strcasecmp(argv[1], "restart") == 0 || strcasecmp(argv[1], "-restart") == 0))
	{
		// Ask the service manager to stop and start the service
		StopService(serviceFile);
		{
			int r = LaunchService(serviceFile);
			if (r == 0) { printf("Failed to restart mesh agent"); }
			else if (r == 1) { printf("Restarted the mesh agent"); }
			else if (r == 2) { printf("Mesh agent failed to stop"); }
		}
	}
	else if (argc > 1 && strcasecmp(argv[1], "-install") == 0)
	{
		// Setup the service
		StopService(serviceFile);
		UninstallService(serviceFile);
		UninstallService(serviceFileOld);
		if (InstallService() == TRUE) { printf("Mesh agent installed"); } else { printf("Failed to install mesh agent"); }

#ifndef _MINCORE
		// Setup the Windows firewall
		if (GetModuleFileNameW(NULL, str, _MAX_PATH) > 5)
		{
			ClearWindowsFirewall(str);
			if (SetupWindowsFirewall(str) != 0)
			{
				#ifdef _DEBUG
				ILIBMESSAGE("Firewall rules added successfully");
				#endif
			}
			else
			{
				#ifdef _DEBUG
				ILIBMESSAGE("Unable to add firewall rules");
				#endif
			}
		}
#endif
	}
	else if (argc > 1 && ((strcasecmp(argv[1], "-remove") == 0) || (strcasecmp(argv[1], "-uninstall") == 0)))
	{
		// Ask the service manager to stop the service
		StopService(serviceFile);

		// Remove the service
		UninstallService(serviceFileOld);
		i = UninstallService(serviceFile);
		if (i == 0) { printf("Failed to uninstall mesh agent"); }
		else if (i == 1) { printf("Mesh agent uninstalled"); }
		else if (i == 2) { printf("Mesh agent still running"); }

#ifndef _MINCORE
		// Remove the MeshAgent registry keys
		RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2");
		RegDeleteKey(HKEY_CURRENT_USER, "Software\\Open Source\\MeshAgent2");

		// Cleanup the firewall rules
		if (GetModuleFileNameW( NULL, str, _MAX_PATH ) > 5)
		{
			if (ClearWindowsFirewall(str) != 0)
			{
				#ifdef _DEBUG
				ILIBMESSAGE("Firewall rules removed successfully");
				#endif
			}
			else
			{
				#ifdef _DEBUG
				ILIBMESSAGE("Unable to remove firewall rules");
				#endif
			}
		}
#endif
	}
#ifdef _MINCORE
	else if (argc > 1 && memcmp(argv[1], "-update:", 8) == 0)
	{
		// Attempt to copy our own exe over the original exe
		while (util_CopyFile(argv[0], argv[1] + 8, FALSE) == FALSE) { Sleep(5000); }

		// Attempt to start the updated service up again
		LaunchService();
	}
#endif
#ifndef _MINCORE
	else if (argc > 1 && memcmp(argv[1], "-update:", 8) == 0)
	{
		// Attempt to copy our own exe over the original exe
		while (util_CopyFile(argv[0], argv[1] + 8, FALSE) == FALSE) Sleep(5000);

		// Attempt to start the updated service up again
		LaunchService(serviceFile);
	}
	else if (argc > 1 && (strcasecmp(argv[1], "-netinfo") == 0))
	{
		char* data;
		int len = MeshInfo_GetSystemInformation(&data);
		if (len > 0) { printf(data); }
	}
	else if (argc > 1 && (strcasecmp(argv[1], "-fullinstall") == 0))
	{
		fullinstall( 0, proxyarg, (int)strnlen_s(proxyarg, _MAX_PATH), tagarg, (int)strnlen_s(tagarg, _MAX_PATH));
	}
	else if (argc > 1 && (strcasecmp(argv[1], "-fulluninstall") == 0))
	{
		fullinstall(1, NULL, 0, NULL, 0);
	}
	else if (argc > 1 && (strcasecmp(argv[1], "-setfirewall") == 0))
	{
		// Reset the firewall rules
		GetModuleFileNameW(NULL, str, _MAX_PATH);
		if (IsAdmin() == FALSE) { printf("Must run as administrator"); } else { ClearWindowsFirewall(str); SetupWindowsFirewall(str); printf("Done"); }
	}
	else if (argc > 1 && (strcasecmp(argv[1], "-clearfirewall") == 0))
	{
		// Clear the firewall rules
		GetModuleFileNameW(NULL, str, _MAX_PATH);
		if (IsAdmin() == FALSE) { printf("Must run as administrator"); } else { ClearWindowsFirewall(str); printf("Done"); }
	}
#endif
	else if (argc == 2 && (strcasecmp(argv[1], "-nodeidhex") == 0))
	{
		// Get the NodeID from the registry
		HKEY hKey;
		DWORD len = 0;
		char* strEx = NULL;
#ifndef _WIN64
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS )
#else
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS )
#endif
		{
			if (RegQueryValueExA(hKey, "NodeId", NULL, NULL, NULL, &len ) == ERROR_SUCCESS && len > 0)
			{
				if ((strEx = (char*)malloc(len)) == NULL) ILIBCRITICALEXIT(254);
				if (RegQueryValueExA(hKey, "NodeId", NULL, NULL, (LPBYTE)strEx, &len ) != ERROR_SUCCESS || len == 0) { free(strEx); strEx = NULL; len = 0;}
			}
			RegCloseKey(hKey);
		}
		if (strEx != NULL) printf(strEx); else printf("Not defined, start the mesh service to create a nodeid.");
		return 0;
	}
	else if (argc == 2 && (strcasecmp(argv[1], "-info") == 0))
	{
		// Display agent information from the registry
		HKEY hKey;
		DWORD len = 0;
		char* strEx = NULL;
#ifndef _WIN64
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS)
#else
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2", 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
#endif
		{
			// Display NodeId
			len = sizeof(ILibScratchPad2);
			if (RegQueryValueExA(hKey, "NodeId", NULL, NULL, (LPBYTE)ILibScratchPad2, &len) != ERROR_SUCCESS) { len = 0; }
			if (len == 0) printf("NodeId:    (none)"); else printf("NodeId:    %s", ILibScratchPad2);

			// Display MeshId
			len = sizeof(ILibScratchPad2);
			if (RegQueryValueExA(hKey, "MeshId", NULL, NULL, (LPBYTE)ILibScratchPad2, &len) != ERROR_SUCCESS) { len = 0; }
			if (len > 0) printf("\r\nMeshId:    %s", ILibScratchPad2);

			// Display AgentHash
			len = sizeof(ILibScratchPad2);
			if (RegQueryValueExA(hKey, "AgentHash", NULL, NULL, (LPBYTE)ILibScratchPad2, &len) != ERROR_SUCCESS) { len = 0; }
			if (len > 0) printf("\r\nAgentHash: %s", ILibScratchPad2);

			// Display MeshServerId
			len = sizeof(ILibScratchPad2);
			if (RegQueryValueExA(hKey, "MeshServerId", NULL, NULL, (LPBYTE)ILibScratchPad2, &len) != ERROR_SUCCESS) { len = 0; }
			if (len > 0) printf("\r\nServerId:  %s", ILibScratchPad2);

			// Display MeshServerUrl
			len = sizeof(ILibScratchPad2);
			if (RegQueryValueExA(hKey, "MeshServerUrl", NULL, NULL, (LPBYTE)ILibScratchPad2, &len) != ERROR_SUCCESS) { len = 0; }
			if (len > 0) printf("\r\nServerUrl: %s", ILibScratchPad2);

			// Display Proxy
			len = sizeof(ILibScratchPad2);
			if (RegQueryValueExA(hKey, "Proxy", NULL, NULL, (LPBYTE)ILibScratchPad2, &len) != ERROR_SUCCESS) { len = 0; }
			if (len > 0) printf("\r\nProxy:     %s", ILibScratchPad2);

			// Display Tag
			len = sizeof(ILibScratchPad2);
			if (RegQueryValueExA(hKey, "Tag", NULL, NULL, (LPBYTE)ILibScratchPad2, &len) != ERROR_SUCCESS) { len = 0; }
			if (len > 0) printf("\r\nTag:       %s", ILibScratchPad2);

			RegCloseKey(hKey);
		}
		return 0;
	}
	else if (argc == 2 && (strcasecmp(argv[1], "-resetnodeid") == 0))
	{
		// Set "resetnodeid" in registry
		HKEY hKey;
#ifndef _WIN64
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2", 0, KEY_WRITE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS )
#else
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Open Source\\MeshAgent2", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS )
#endif
		{
			i = 1;
			DWORD err = RegSetValueEx(hKey, "ResetNodeId", 0, REG_DWORD, (BYTE*)&i, (DWORD)4);
			if (err == ERROR_SUCCESS) { printf("NodeID will be reset next time the Mesh Agent service is started."); }
			RegCloseKey(hKey);
		}
		else
		{
			printf("Error writing to registry, try running as administrator.");
		}
		return 0;
	}
	else
	{
		UninstallService(serviceFileOld);
		if (argc > 1 && !strcmp(argv[1], "-recovery")==0)
		{
			// See if we need to run as a script engine
			if (argc >= 2 && ILibString_EndsWith(argv[1], -1, ".js", 3) != 0)
			{
				SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); // Set SIGNAL on windows to listen for Ctrl-C

				__try
				{
					agent = MeshAgent_Create(0);
					agent->runningAsConsole = 1;
					MeshAgent_Start(agent, argc, argv);
					MeshAgent_Destroy(agent);
					agent = NULL;
				}
				__except (ILib_WindowsExceptionFilter(GetExceptionCode(), GetExceptionInformation(), &winException))
				{
					ILib_WindowsExceptionDebug(&winException);
				}
			}
			else
			{
#ifdef _MINCORE
				printf("Mesh Agent available switches:\r\n  run               Start as a console agent.\r\n  connect           Start as a temporary console agent.\r\n  restart           Restart the service.\r\n  stop              Stop the service.\r\n  state             Display the running state of the service.\r\n  -signcheck        Perform self-check.\r\n  -install          Install the service from this location.\r\n  -uninstall        Remove the service from this location.\r\n  -nodeidhex        Return the current agent identifier.\r\n  -proxy:host:port  Specifiy an HTTPS proxy (after -fullinstall only).\r\n  -tag:xxx          Specifiy a agent tag  (after -fullinstall only).\r\n\r\n  -resetnodeid      Reset the NodeID next time the service is started.");
#else
				printf("Mesh Agent available switches:\r\n  run               Start as a console agent.\r\n  connect           Start as a temporary console agent.\r\n  start             Start the service.\r\n  restart           Restart the service.\r\n  stop              Stop the service.\r\n  state             Display the running state of the service.\r\n  -signcheck        Perform self-check.\r\n  -install          Install the service from this location.\r\n  -uninstall        Remove the service from this location.\r\n  -nodeidhex        Return the current agent identifier.\r\n  -fullinstall      Copy agent into program files, install and launch.\r\n  -fulluninstall    Stop agent and clean up the program files location.\r\n  -proxy:host:port  Specifiy an HTTPS proxy (after -fullinstall only).\r\n  -tag:xxx          Specifiy a agent tag  (after -fullinstall only).\r\n  -resetnodeid      Reset the NodeID next time the service is started.");
#endif
			}
		}
		else
		{
#ifndef _MINCORE
			if (RunService(argc, argv) == 0 && GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
			{
				FreeConsole();

				/*
				if (IsAdmin() == FALSE)
				{
					MessageBox(NULL, TEXT("Must run as administrator"), TEXT("Mesh Agent"), MB_OK | MB_ICONERROR);
				}
				else
				{
					DialogBox(NULL, MAKEINTRESOURCE(IDD_INSTALLDIALOG), NULL, DialogHandler);
				}
				*/
				DialogBox(NULL, MAKEINTRESOURCE(IDD_INSTALLDIALOG), NULL, DialogHandler);
			}
		}
#else
		RunService(argc, argv);
#endif
	}

	CoUninitialize();

	return 0;
}

char* getMshSettings(char* fileName, char* selfexe, char** meshname, char** meshid, char** serverid, char** serverurl, char** installFlags)
{
	char* importFile;
	int eq, importFileLen;
	parser_result *pr;
	parser_result_field *f;

	*meshname = *meshid = *serverid = *serverurl = *installFlags = NULL;
	importFileLen = ILibReadFileFromDiskEx(&importFile, fileName);
	if (importFile == NULL) {
		// Could not find the .msh file, see if there is one inside our own executable.
		FILE *tmpFile = NULL;
		char exeMeshPolicyGuid[] = { 0xB9, 0x96, 0x01, 0x58, 0x80, 0x54, 0x4A, 0x19, 0xB7, 0xF7, 0xE9, 0xBE, 0x44, 0x91, 0x4C, 0x19 };
		char tmpHash[16];

		fopen_s(&tmpFile, selfexe, "rb");
		if (tmpFile == NULL) { return NULL; } // Could not open our own executable

		fseek(tmpFile, -16, SEEK_END);
		ignore_result(fread(tmpHash, 1, 16, tmpFile)); // Read the GUID
		if (memcmp(tmpHash, exeMeshPolicyGuid, 16) == 0) { // If this is the Mesh policy file guid, we found a MSH file
														   // Found embedded MSH File
			fseek(tmpFile, -20, SEEK_CUR);
			if (fread((void*)&importFileLen, 1, 4, tmpFile) == 4) { // Read the length of the MSH file
				importFileLen = ntohl(importFileLen);
				if ((importFileLen >= 20000) || (importFileLen < 1)) { fclose(tmpFile); return NULL; }
				fseek(tmpFile, -4 - importFileLen, SEEK_CUR);
				if ((importFile = malloc(importFileLen + 1)) == NULL) { fclose(tmpFile); return NULL; }
				if (fread(importFile, 1, importFileLen, tmpFile) != importFileLen) { fclose(tmpFile); free(importFile); return NULL; }
				importFile[importFileLen] = 0;
			}
		}
		else {
			fclose(tmpFile);
			return NULL;
		}
		fclose(tmpFile);
	}

	pr = ILibParseString(importFile, 0, importFileLen, "\n", 1);
	f = pr->FirstResult;
	while (f != NULL) {
		f->datalength = ILibTrimString(&(f->data), f->datalength);
		if (f->data[0] != 35) { // Checking to see if this line is commented out
			eq = ILibString_IndexOf(f->data, f->datalength, "=", 1);
			if (eq > 0) {
				char *key, *val;
				int keyLen, valLen;

				key = f->data;
				keyLen = eq;
				key[keyLen] = 0;
				val = key + keyLen + 1;
				valLen = f->datalength - keyLen - 1;
				if (val[valLen - 1] == 13) { --valLen; }
				valLen = ILibTrimString(&val, valLen);
				val[valLen] = 0;

				if (keyLen == 8 && memcmp("MeshName", key, keyLen) == 0) { *meshname = val; }
				if (keyLen == 6 && memcmp("MeshID", key, keyLen) == 0) { *meshid = val; }
				if (keyLen == 8 && memcmp("ServerID", key, keyLen) == 0) { *serverid = val; }
				if (keyLen == 10 && memcmp("MeshServer", key, keyLen) == 0) { *serverurl = val; }
				if (keyLen == 12 && memcmp("InstallFlags", key, keyLen) == 0) { *installFlags = val; }
			}
		}
		f = f->NextResult;
	}
	ILibDestructParserResults(pr);
	return importFile;
}


#ifndef _MINCORE

// Start as a temporary mesh agent.
DWORD WINAPI StartTempAgent(_In_ LPVOID lpParameter)
{
	CONTEXT winException;
	char selfexe[_MAX_PATH];
	char *selfexe_ptr[] = { selfexe };
	WCHAR str[_MAX_PATH];
	size_t len;
	char *integratedJavaScript;
	int integragedJavaScriptLen;
	char setup1[_MAX_PATH];
	int setup1len;

	ILibDuktape_ScriptContainer_CheckEmbedded(&integratedJavaScript, &integragedJavaScriptLen);

	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

	// Get our own executable name
	if (GetModuleFileNameW(NULL, str, _MAX_PATH) > 5) { wcstombs_s(&len, selfexe, _MAX_PATH, str, _MAX_PATH); }

	// Setup proxy filenames
	if ((setup1len = (int)strnlen_s(selfexe, sizeof(selfexe))) >= 4) {
		memcpy_s(setup1, sizeof(setup1), selfexe, setup1len);
		memcpy_s(setup1 + (setup1len - 3), sizeof(setup1) - setup1len - 3, "proxy", 6);

		// Try to setup the proxy file
		WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyEx;
		if (WinHttpGetIEProxyConfigForCurrentUser(&proxyEx))
		{
			if (proxyEx.lpszProxy != NULL)
			{
				FILE *SourceFile = NULL;
				size_t len;
				if (wcstombs_s(&len, ILibScratchPad, 4095, proxyEx.lpszProxy, 2000) == 0)
				{
					char* ptr = strstr(ILibScratchPad, "https=");
					if (ptr != NULL)
					{
						char* ptr2 = strstr(ptr, ";");
						ptr += 6;
						if (ptr2 != NULL) ptr2[0] = 0;
					}
					else
					{
						ptr = ILibScratchPad;
					}
					fopen_s(&SourceFile, setup1, "wb");
					if (SourceFile != NULL)
					{
						if (fwrite(ptr, sizeof(char), strnlen_s(ptr, sizeof(ILibScratchPad)), SourceFile)) {}
						fclose(SourceFile);
					}
				}
				GlobalFree(proxyEx.lpszProxy);
			}

			// Release the rest of the proxy settings
			if (proxyEx.lpszAutoConfigUrl != NULL) GlobalFree(proxyEx.lpszAutoConfigUrl);
			if (proxyEx.lpszProxyBypass != NULL) GlobalFree(proxyEx.lpszProxyBypass);
		}
	}

	// Launch the temporary agent
	__try
	{
		agent = MeshAgent_Create(MeshCommand_AuthInfo_CapabilitiesMask_TEMPORARY);
		agent->meshCoreCtx_embeddedScript = integratedJavaScript;
		agent->meshCoreCtx_embeddedScriptLen = integragedJavaScriptLen;
		agent->runningAsConsole = 1;
		MeshAgent_Start(agent, 1, selfexe_ptr);
		//retCode = agent->exitCode;
		MeshAgent_Destroy(agent);
		agent = NULL;
	}
	__except (ILib_WindowsExceptionFilter(GetExceptionCode(), GetExceptionInformation(), &winException))
	{
		ILib_WindowsExceptionDebug(&winException);
	}

	CoUninitialize();
	return(0);
}

// Message handler for dialog box.
INT_PTR CALLBACK DialogHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	char *fileName = NULL, *meshname = NULL, *meshid = NULL, *serverid = NULL, *serverurl = NULL, *installFlags = NULL, *mshfile = NULL;

	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		{
			// Get the current service running state
			int r = GetServiceState(serviceFile);
			char* txt = "";
			char selfexe[_MAX_PATH];

			switch (r)
			{
			case 0:
				txt = "Error";
				break;
			case SERVICE_STOPPED: 
				txt = "Stopped";
				break;
			case SERVICE_START_PENDING: 
				txt = "Start Pending";
				break;
			case SERVICE_STOP_PENDING: 
				txt = "Stop Pending";
				break;
			case SERVICE_RUNNING: 
				txt = "Running";
				break;
			case SERVICE_CONTINUE_PENDING: 
				txt = "Continue Pending";
				break;
			case SERVICE_PAUSE_PENDING: 
				txt = "Pause Pending";
				break;
			case SERVICE_PAUSED: 
				txt = "Paused";
				break;
			case 100: 
				txt = "Not Installed";
				break;
			}
			SetWindowTextA( GetDlgItem( hDlg, IDC_STATUSTEXT ), txt);

			// Get current executable path
			GetModuleFileNameA(NULL, selfexe, MAX_PATH);
			fileName = MeshAgent_MakeAbsolutePath(selfexe, ".msh");

			{
				DWORD               dwSize = 0;
				BYTE                *pVersionInfo = NULL;
				VS_FIXEDFILEINFO    *pFileInfo = NULL;
				UINT                pLenFileInfo = 0;
				int major, minor, hotfix, other;

				if ((dwSize = GetFileVersionInfoSize(selfexe, NULL)))
				{					
					if ((pVersionInfo = malloc(dwSize)) == NULL) { ILIBCRITICALEXIT(254); }
					if (GetFileVersionInfo(selfexe, 0, dwSize, pVersionInfo))
					{
						if (VerQueryValue(pVersionInfo, TEXT("\\"), (LPVOID*)&pFileInfo, &pLenFileInfo))
						{
							// Display the version of this software
							major = (pFileInfo->dwFileVersionMS >> 16) & 0xffff;
							minor = (pFileInfo->dwFileVersionMS) & 0xffff;
							hotfix = (pFileInfo->dwFileVersionLS >> 16) & 0xffff;
							other = (pFileInfo->dwFileVersionLS) & 0xffff;
#ifdef _WIN64
							sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "v%d.%d.%d, 64bit", major, minor, hotfix);
#else
							sprintf_s(ILibScratchPad, sizeof(ILibScratchPad), "v%d.%d.%d", major, minor, hotfix);
#endif
							SetWindowTextA(GetDlgItem(hDlg, IDC_VERSIONTEXT), ILibScratchPad);
						}
					}
					free(pVersionInfo);
				}
			}

			if ((mshfile = getMshSettings(fileName, selfexe, &meshname, &meshid, &serverid, &serverurl, &installFlags)) != NULL)
			{
				// Set text in the dialog box
				int installFlagsInt = 0;
				WINDOWPLACEMENT lpwndpl;
				if (installFlags != NULL) { installFlagsInt = atoi(installFlags); }
				if (strnlen_s(meshid, 255) > 50) { meshid += 2; meshid[42] = 0; }
				if (strnlen_s(serverid, 255) > 50) { serverid[42] = 0; }
				SetWindowTextA(GetDlgItem(hDlg, IDC_POLICYTEXT), (meshid != NULL) ? meshname : "(None)");
				SetWindowTextA(GetDlgItem(hDlg, IDC_HASHTEXT), (meshid != NULL) ? meshid : "(None)");
				SetWindowTextA(GetDlgItem(hDlg, IDC_SERVERLOCATION), (serverurl != NULL) ? serverurl : "(None)");
				SetWindowTextA(GetDlgItem(hDlg, IDC_SERVERID), (serverid != NULL) ? serverid : "(None)");
				free(mshfile);
				if (meshid == NULL) { EnableWindow(GetDlgItem(hDlg, IDC_CONNECTBUTTON), FALSE); }
				if ((installFlagsInt & 3) == 1) {
					// Temporary Agent Only
					ShowWindow(GetDlgItem(hDlg, IDC_INSTALLBUTTON), SW_HIDE);
					ShowWindow(GetDlgItem(hDlg, IDC_UNINSTALLBUTTON), SW_HIDE);
					GetWindowPlacement(GetDlgItem(hDlg, IDC_INSTALLBUTTON), &lpwndpl);
					SetWindowPlacement(GetDlgItem(hDlg, IDC_CONNECTBUTTON), &lpwndpl);
				}  else if ((installFlagsInt & 3) == 2) {
					// Background Only
					ShowWindow(GetDlgItem(hDlg, IDC_CONNECTBUTTON), SW_HIDE);
				} else if ((installFlagsInt & 3) == 3) {
					// Uninstall only
					GetWindowPlacement(GetDlgItem(hDlg, IDC_INSTALLBUTTON), &lpwndpl);
					SetWindowPlacement(GetDlgItem(hDlg, IDC_UNINSTALLBUTTON), &lpwndpl);
					ShowWindow(GetDlgItem(hDlg, IDC_INSTALLBUTTON), SW_HIDE);
					ShowWindow(GetDlgItem(hDlg, IDC_CONNECTBUTTON), SW_HIDE);
				}
			}
			else
			{
				EnableWindow(GetDlgItem(hDlg, IDC_CONNECTBUTTON), FALSE);
			}

			return (INT_PTR)TRUE;
		}
	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));

#ifdef _DEBUG
			_CrtCheckMemory();
			_CrtDumpMemoryLeaks();
#endif

			return (INT_PTR)TRUE;
		}
		else if (LOWORD(wParam) == IDC_INSTALLBUTTON || LOWORD(wParam) == IDC_UNINSTALLBUTTON)
		{
			BOOL result = FALSE;

			EnableWindow( GetDlgItem( hDlg, IDC_INSTALLBUTTON ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDC_UNINSTALLBUTTON ), FALSE );
			EnableWindow( GetDlgItem( hDlg, IDCANCEL ), FALSE );

			if (IsAdmin() == TRUE)
			{
				// We are already administrator, just install/uninstall now.
				if (LOWORD(wParam) == IDC_INSTALLBUTTON) { fullinstall(0, NULL, 0, NULL, 0); } else { fullinstall(1, NULL, 0, NULL, 0); }
				result = TRUE;
			}
			else
			{
				// We need to request admin escalation
				if (LOWORD(wParam) == IDC_INSTALLBUTTON) { result = RunAsAdmin("-fullinstall"); } else { result = RunAsAdmin("-fulluninstall"); }
			}

			if (result)
			{
				EndDialog(hDlg, LOWORD(wParam));
			}
			else
			{
				EnableWindow(GetDlgItem(hDlg, IDC_INSTALLBUTTON), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_UNINSTALLBUTTON), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE);
			}

#ifdef _DEBUG
			_CrtCheckMemory();
			_CrtDumpMemoryLeaks();
#endif

			return (INT_PTR)TRUE;
		}
		else if (LOWORD(wParam) == IDC_CONNECTBUTTON) {
			EnableWindow(GetDlgItem(hDlg, IDC_INSTALLBUTTON), FALSE);
			EnableWindow(GetDlgItem(hDlg, IDC_UNINSTALLBUTTON), FALSE);
			EnableWindow(GetDlgItem(hDlg, IDC_CONNECTBUTTON), FALSE);
			SetWindowTextA(GetDlgItem(hDlg, IDC_STATUSTEXT), "Running as temporary agent");
			CreateThread(NULL, 0, &StartTempAgent, NULL, 0, NULL);
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

#endif

#ifdef _MINCORE
BOOL WINAPI AreFileApisANSI(void) { return FALSE; }
VOID WINAPI FatalAppExitA(_In_ UINT uAction, _In_ LPCSTR lpMessageText) {}
HANDLE WINAPI CreateSemaphoreW(_In_opt_  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, _In_ LONG lInitialCount, _In_ LONG lMaximumCount, _In_opt_ LPCWSTR lpName)
{
	return 0;
}
#endif
