/*
*  OpenVPN-GUI -- A Windows GUI for OpenVPN.
*
*  Copyright (C) 2004 Mathias Sundman <mathias@nilings.se>
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program (see the file COPYING included with this
*  distribution); if not, write to the Free Software Foundation, Inc.,
*  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*  Parts of this sourcefile is taken from openvpnserv.c from the
*  OpenVPN source, with approval from the author, James Yonan
*  <jim@yonan.net>.
*/


#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <process.h>
#include "tray.h"
#include "main.h"
#include "openvpn.h"
#include "openvpn_monitor_process.h"
#include "openvpn-gui-res.h"
#include "options.h"
#include "scripts.h"
#include "viewlog.h"
#include "proxy.h"
#include "passphrase.h"
#include <richedit.h>

extern struct options o;

/*
* Creates a unique exit_event name based on the 
* config file number.
*/
int CreateExitEvent(int config)
{
	o.cnn[config].exit_event = NULL;
	if (o.oldversion == 1)
	{
		mysnprintf(o.cnn[config].exit_event_name, "openvpn_exit");
		o.cnn[config].exit_event = CreateEvent (NULL,
		TRUE, 
		FALSE, 
		o.cnn[config].exit_event_name);
		if (o.cnn[config].exit_event == NULL)
		{
			if (GetLastError() == ERROR_ACCESS_DENIED)
			{
				/* service mustn't be running, while using old version */ 
				ShowLocalizedMsg(GUI_NAME, ERR_STOP_SERV_ON_OLD_VERSION, "");
			}
			else
			{
				/* error creating exit event */ 
				ShowLocalizedMsg (GUI_NAME, ERR_CREATE_EVENT, o.cnn[config].exit_event_name);
			}
			return(false);
		}
	}
	else
	{
		mysnprintf(o.cnn[config].exit_event_name, "openvpngui_exit_event_%d",config);
		o.cnn[config].exit_event = CreateEvent (NULL,
		TRUE, 
		FALSE, 
		o.cnn[config].exit_event_name);
		if (o.cnn[config].exit_event == NULL)
		{
			/* error creating exit event */
			ShowLocalizedMsg (GUI_NAME, ERR_CREATE_EVENT, o.cnn[config].exit_event_name);
			return(false);
		}
	}

	return(true);
}


/*
* Set priority based on the registry or cmd-line value
*/
int SetProcessPriority(DWORD *priority)
{

	/* set process priority */
	*priority = NORMAL_PRIORITY_CLASS;
	if (!strcmp (o.priority_string, "IDLE_PRIORITY_CLASS"))
	*priority = IDLE_PRIORITY_CLASS;
	else if (!strcmp (o.priority_string, "BELOW_NORMAL_PRIORITY_CLASS"))
	*priority = BELOW_NORMAL_PRIORITY_CLASS;
	else if (!strcmp (o.priority_string, "NORMAL_PRIORITY_CLASS"))
	*priority = NORMAL_PRIORITY_CLASS;
	else if (!strcmp (o.priority_string, "ABOVE_NORMAL_PRIORITY_CLASS"))
	*priority = ABOVE_NORMAL_PRIORITY_CLASS;
	else if (!strcmp (o.priority_string, "HIGH_PRIORITY_CLASS"))
	*priority = HIGH_PRIORITY_CLASS;
	else
	{
		/* unknown priority */
		ShowLocalizedMsg (GUI_NAME, ERR_UNKNOWN_PRIORITY, o.priority_string);
		return (false);
	}

	return(true);
}


/*
* Launch an OpenVPN process
*/
int StartOpenVPN(int config)
{

	HANDLE hOutputReadTmp = NULL;
	HANDLE hOutputRead = NULL;
	HANDLE hOutputWrite = NULL;
	HANDLE hInputWriteTmp = NULL;
	HANDLE hInputRead = NULL;
	HANDLE hInputWrite = NULL;
	HANDLE hErrorWrite = NULL;

	HANDLE hThread; 
	DWORD IDThread; 
	DWORD priority;
	STARTUPINFO start_info;
	PROCESS_INFORMATION proc_info;
	SECURITY_ATTRIBUTES sa;
	SECURITY_DESCRIPTOR sd;
	char command_line[256];
	char proxy_string[100];
	char msg[200];
	int i, is_connected=0;

	extern HINSTANCE hInstance;

	CLEAR (start_info);
	CLEAR (proc_info);
	CLEAR (sa);
	CLEAR (sd);


	/* If oldversion, allow only ONE connection */
	if (o.oldversion == 1)
	{
		for (i=0; i < o.num_configs; i++) 
		{
			if ((o.cnn[i].connect_status != DISCONNECTED) &&
					(o.cnn[i].connect_status != DISCONNECTING))
			{
				is_connected=1;
				break;
			}
		}
		if (is_connected)
		{
			/* only one simultanious connection on old version */
			ShowLocalizedMsg(GUI_NAME, ERR_ONLY_ONE_CONN_OLD_VERSION, ""); 
			return(false);
		}
	}

	/* Warn if "log" or "log-append" option is found in config file */
	if ((ConfigFileOptionExist(config, "log ")) ||
			(ConfigFileOptionExist(config, "log-append ")))
	{
		TCHAR buf[1000];
		myLoadString(ERR_OPTION_LOG_IN_CONFIG);
		if (MessageBox(NULL, buf, GUI_NAME, MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING) != IDYES)
		return(false);
	}

	/* Clear connection unique vars */
	o.cnn[config].failed_psw = 0;
	CLEAR (o.cnn[config].ip);

	/* Create our exit event */
	if (!CreateExitEvent(config)) 
	return(false);

	/* set process priority */
	if (!SetProcessPriority(&priority))
	goto failed;

	/* Check that log append flag has a valid value */
	if ((o.append_string[0] != '0') && (o.append_string[0] != '1'))
	{
		/* append_log must be 0 or 1 */
		ShowLocalizedMsg (GUI_NAME, ERR_LOG_APPEND_BOOL, o.append_string);
		goto failed;
	}
	
	/* construct proxy string to append to command line */
	ConstructProxyCmdLine(proxy_string, sizeof(proxy_string));

	/* construct command line */
	if (o.oldversion == 1)
	{
		mysnprintf (command_line, "openvpn --config \"%s\" %s",
		o.cnn[config].config_file, proxy_string);
	}
	else
	{
		mysnprintf (command_line, "openvpn --service %s 0 --config \"%s\" %s",
		o.cnn[config].exit_event_name,
		o.cnn[config].config_file,
		proxy_string);
	}


	/* Make security attributes struct for logfile handle so it can
	be inherited. */
	sa.nLength = sizeof (sa);
	sa.lpSecurityDescriptor = &sd;
	sa.bInheritHandle = TRUE;
	if (!InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
	{
		/* Init Sec. Desc. failed */
		ShowLocalizedMsg (GUI_NAME, ERR_INIT_SEC_DESC, "");
		goto failed;
	}
	if (!SetSecurityDescriptorDacl (&sd, TRUE, NULL, FALSE))
	{
		/* set Dacl failed */
		ShowLocalizedMsg (GUI_NAME, ERR_SET_SEC_DESC_ACL, "");
		goto failed;
	}


	/* Create the child output pipe. */
	if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
	{
		/* CreatePipe failed. */
		ShowLocalizedMsg(GUI_NAME, ERR_CREATE_PIPE_OUTPUT, "");
		goto failed;
	}

	// Create a duplicate of the output write handle for the std error
	// write handle. This is necessary in case the child application
	// closes one of its std output handles.
	if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
				GetCurrentProcess(),&hErrorWrite,0,
				TRUE,DUPLICATE_SAME_ACCESS))
	{ 
		/* DuplicateHandle failed. */
		ShowLocalizedMsg(GUI_NAME, ERR_DUP_HANDLE_ERR_WRITE, "");
		goto failed;
	}

	// Create the child input pipe.
	if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
	{
		/* CreatePipe failed. */
		ShowLocalizedMsg(GUI_NAME, ERR_CREATE_PIPE_INPUT, "");
		goto failed;
	}

	// Create new output read handle and the input write handles. Set
	// the Properties to FALSE. Otherwise, the child inherits the
	// properties and, as a result, non-closeable handles to the pipes
	// are created.
	if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
				GetCurrentProcess(),
				&hOutputRead, // Address of new handle.
				0,FALSE, // Make it uninheritable.
				DUPLICATE_SAME_ACCESS))
	{
		/* Duplicate Handle failed. */
		ShowLocalizedMsg(GUI_NAME, ERR_DUP_HANDLE_OUTPUT_READ, "");
		goto failed;
	}

	if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
				GetCurrentProcess(),
				&hInputWrite, // Address of new handle.
				0,FALSE, // Make it uninheritable.
				DUPLICATE_SAME_ACCESS))
	{
		/* DuplicateHandle failed */
		ShowLocalizedMsg(GUI_NAME, ERR_DUP_HANDLE_INPUT_WRITE, "");
		goto failed;
	}

	/* Close inheritable copies of the handles */
	if (!CloseHandle(hOutputReadTmp) || !CloseHandle(hInputWriteTmp)) 
	{
		/* Close Handle failed */
		ShowLocalizedMsg(GUI_NAME, ERR_CLOSE_HANDLE_TMP, "");
		CloseHandle (o.cnn[config].exit_event);
		return(0); 
	}
	hOutputReadTmp=NULL;
	hInputWriteTmp=NULL;

	/* fill in STARTUPINFO struct */
	GetStartupInfo(&start_info);
	start_info.cb = sizeof(start_info);
	start_info.dwFlags = STARTF_USESTDHANDLES;
	start_info.hStdInput = hInputRead;
	start_info.hStdOutput = hOutputWrite;
	start_info.hStdError = hErrorWrite;

	/* Run Pre-connect script */
	RunPreconnectScript(config);

	/* create an OpenVPN process for one config file */
	if (!CreateProcess(o.exe_path,
				command_line,
				NULL,
				NULL,
				TRUE,
				priority | CREATE_NO_WINDOW,
				NULL,
				o.cnn[config].config_dir,
				&start_info,
				&proc_info))
	{
		/* CreateProcess failed */
		ShowLocalizedMsg (GUI_NAME, ERR_CREATE_PROCESS,
		o.exe_path,
		command_line,
		o.cnn[config].config_dir);
		goto failed;
	}


	/* close unneeded handles */
	Sleep (250); /* try to prevent race if we close logfile
				handle before child process DUPs it */

	if(!CloseHandle (proc_info.hThread) ||
			!CloseHandle (hOutputWrite) ||
			!CloseHandle (hInputRead) ||
			!CloseHandle (hErrorWrite))
	{
		/* CloseHandle failed */
		ShowLocalizedMsg (GUI_NAME, ERR_CLOSE_HANDLE, "");
		CloseHandle (o.cnn[config].exit_event);
		return(false);
	}
	hOutputWrite = NULL;
	hInputRead = NULL;
	hErrorWrite = NULL;

	/* Save StdIn and StdOut handles in our options struct */
	o.cnn[config].hStdIn = hInputWrite;
	o.cnn[config].hStdOut = hOutputRead;

	/* Save Process Handle */
	o.cnn[config].hProcess=proc_info.hProcess;


	/* Start Thread to show Status Dialog */
	hThread = CreateThread(NULL, 0, 
	(LPTHREAD_START_ROUTINE) ThreadOpenVPNStatus,
	(int *) config,  // pass config nr
	0, &IDThread); 
	if (hThread == NULL) 
	{
		/* CreateThread failed */
		ShowLocalizedMsg (GUI_NAME, ERR_CREATE_THREAD_STATUS, "");
		goto failed;
	}


	return(true); 

failed:
	if (o.cnn[config].exit_event) CloseHandle (o.cnn[config].exit_event);
	if (hOutputWrite) CloseHandle (hOutputWrite);
	if (hOutputRead) CloseHandle (hOutputRead);
	if (hInputWrite) CloseHandle (hInputWrite);
	if (hInputRead) CloseHandle (hInputRead);
	if (hErrorWrite) CloseHandle (hOutputWrite);
	return(false);

}


void StopOpenVPN(int config)
{
	int i;
	TCHAR buf[1000];

	o.cnn[config].connect_status = DISCONNECTING;

	if (o.cnn[config].exit_event) {
		/* Run Disconnect script */
		RunDisconnectScript(config, false);

		EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_DISCONNECT), FALSE);
		EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_RESTART), FALSE);
		SetMenuStatus(config, DISCONNECTING);
		myLoadString(INFO_STATE_WAIT_TERM);
		/* UserInfo: waiting for OpenVPN termination... */
		SetDlgItemText(o.cnn[config].hwndStatus, TEXT_STATUS, buf); 
		SetEvent(o.cnn[config].exit_event);
	}
}

void SuspendOpenVPN(int config)
{
	int i;
	TCHAR buf[1000];

	o.cnn[config].connect_status = SUSPENDING;
	o.cnn[config].restart = true;

	if (o.cnn[config].exit_event) {
		EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_DISCONNECT), FALSE);
		EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_RESTART), FALSE);
		SetMenuStatus(config, DISCONNECTING);
		myLoadString(INFO_STATE_WAIT_TERM);
		SetDlgItemText(o.cnn[config].hwndStatus, TEXT_STATUS, buf);
		SetEvent(o.cnn[config].exit_event);
	}
}


void StopAllOpenVPN()
{
	int i;

	for(i=0; i < o.num_configs; i++) {
		if(o.cnn[i].connect_status != DISCONNECTED)
		StopOpenVPN(i);
	}

	/* Wait for all connections to terminate (Max 5 sec) */
	for (i=0; i<20; i++, Sleep(250))
	if (CountConnectedState(DISCONNECTED) == o.num_configs) break;

}


BOOL CALLBACK StatusDialogFunc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
	BOOL Translated;
	HWND hwndLogWindow;
	RECT rect;
	CHARFORMAT charformat;
	HICON hIcon;
	int config;

	switch (msg) {

	case WM_INITDIALOG:
		/* Set Window Icon "DisConnected" */
		SetStatusWinIcon(hwndDlg, APP_ICON_CONNECTING);

		/* Create LogWindow */
		hwndLogWindow = CreateWindowEx (0, RICHEDIT_CLASS, NULL, 
		WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | \
		ES_SUNKEN | ES_LEFT | ES_MULTILINE | \
		ES_READONLY | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
		20, 25, 350, 160,		// Posision and Size
		hwndDlg,			// Parent window handle
		(HMENU) EDIT_LOG,		// hMenu
		o.hInstance,		// hInstance
		NULL);			// WM_CREATE lpParam

		
		if (!hwndLogWindow)
		{
			/* Create RichEd LogWindow Failed */
			ShowLocalizedMsg(GUI_NAME, ERR_CREATE_RICHED_LOGWINDOW, "");
			return FALSE;
		}

		/* Set font and fontsize of the LogWindow */
		charformat.cbSize = sizeof(CHARFORMAT); 
		charformat.dwMask = CFM_SIZE | CFM_FACE | CFM_BOLD | CFM_ITALIC | \
		CFM_UNDERLINE | CFM_STRIKEOUT | CFM_PROTECTED;
		charformat.dwEffects = 0;
		charformat.yHeight = 100;
		strcpy(charformat.szFaceName, "MS Sans Serif");
		if ((SendMessage(hwndLogWindow, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &charformat) && CFM_SIZE) == 0) {
			/* set size failed */
			ShowLocalizedMsg(GUI_NAME, ERR_SET_SIZE, "");
		}

		/* Set Size and Posision of controls */
		GetClientRect(hwndDlg, &rect);
		MoveWindow (hwndLogWindow, 20, 25, rect.right - 40, rect.bottom - 70, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, TEXT_STATUS), 20, 5, rect.right - 25, 15, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, ID_DISCONNECT), 20, rect.bottom - 30, 90, 23, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, ID_RESTART), 125, rect.bottom - 30, 90, 23, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, ID_HIDE), rect.right - 110, rect.bottom - 30, 90, 23, TRUE);

		/* Set focus on the LogWindow so it scrolls automatically */
		SetFocus(hwndLogWindow);

		return FALSE;

	case WM_SIZE:
		MoveWindow (GetDlgItem(hwndDlg, EDIT_LOG), 20, 25, LOWORD (lParam) - 40,
		HIWORD (lParam) - 70, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, ID_DISCONNECT), 20,
		HIWORD (lParam) - 30, 90, 23, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, ID_RESTART), 125,
		HIWORD (lParam) - 30, 90, 23, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, ID_HIDE), LOWORD (lParam) - 110,
		HIWORD (lParam) - 30, 90, 23, TRUE);
		MoveWindow (GetDlgItem(hwndDlg, TEXT_STATUS), 20, 5, LOWORD (lParam) - 25, 15, TRUE);
		InvalidateRect(hwndDlg, NULL, TRUE);
		return TRUE;

	case WM_COMMAND:
		config=GetDlgItemInt(hwndDlg, TEXT_CONFIG, &Translated, FALSE);
		switch (LOWORD(wParam)) {

		case ID_DISCONNECT:
			SetFocus(GetDlgItem(o.cnn[config].hwndStatus, EDIT_LOG));
			StopOpenVPN(config);
			return TRUE;

		case ID_HIDE:
			if (o.cnn[GetDlgItemInt(hwndDlg, TEXT_CONFIG, &Translated, FALSE)].connect_status != DISCONNECTED)
			{
				ShowWindow(hwndDlg, SW_HIDE);
			}
			else
			{
				DestroyWindow(hwndDlg);
			}
			return TRUE;
		case ID_RESTART:
			SetFocus(GetDlgItem(o.cnn[config].hwndStatus, EDIT_LOG));
			o.cnn[config].restart = true;
			StopOpenVPN(config);
			return TRUE; 
		}
		break;

	case WM_SHOWWINDOW:
		if (wParam == TRUE)
		{
			config=GetDlgItemInt(hwndDlg, TEXT_CONFIG, &Translated, FALSE);
			if (o.cnn[config].hwndStatus)
			SetFocus(GetDlgItem(o.cnn[config].hwndStatus, EDIT_LOG));
		}
		return FALSE;

	case WM_CLOSE:
		if (o.cnn[GetDlgItemInt(hwndDlg, TEXT_CONFIG, &Translated, FALSE)].connect_status != DISCONNECTED)
		{
			ShowWindow(hwndDlg, SW_HIDE);
		}
		else
		{
			DestroyWindow(hwndDlg);
		}
		return TRUE;

	case WM_DESTROY:
		PostQuitMessage(0);
		break; 
	}
	return FALSE;
}

void SetStatusWinIcon(HWND hwndDlg, int IconID)
{
	HICON hIcon;

	/* Set Window Icon */
	hIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IconID), 
	IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
	if (hIcon) {
		SendMessage(hwndDlg, WM_SETICON, (WPARAM) (ICON_SMALL), (LPARAM) (hIcon));
		SendMessage(hwndDlg, WM_SETICON, (WPARAM) (ICON_BIG), (LPARAM) (hIcon));  
	}
}

int AutoStartConnections()
{
	int i;

	for (i=0; i < o.num_configs; i++)
	{
		if (o.cnn[i].auto_connect)
		StartOpenVPN(i);
	}

	return(true);
}

int VerifyAutoConnections()
{
	int i,j;
	BOOL match;

	for (i=0; (o.auto_connect[i] != 0) && (i < MAX_CONFIGS); i++)
	{
		match = false;
		for (j=0; j < MAX_CONFIGS; j++)
		{
			if (strcasecmp(o.cnn[j].config_file, o.auto_connect[i]) == 0)
			{
				match=true;
				break;
			}
		}
		if (match == false)
		{
			/* autostart config not found */
			ShowLocalizedMsg(GUI_NAME, ERR_AUTOSTART_CONF_NOT_FOUND, o.auto_connect[i]);
			return false;
		}
	}

	return true;
}


int CheckVersion()
{
	HANDLE hOutputReadTmp = NULL;
	HANDLE hOutputRead = NULL;
	HANDLE hOutputWrite = NULL;
	HANDLE hInputWriteTmp = NULL;
	HANDLE hInputRead = NULL;
	HANDLE hInputWrite = NULL;
	HANDLE exit_event;
	HANDLE debug_event;

	STARTUPINFO start_info;
	PROCESS_INFORMATION proc_info;
	SECURITY_ATTRIBUTES sa;
	SECURITY_DESCRIPTOR sd;
	char command_line[256];
	char line[1024];
	char bin_path[MAX_PATH];
	char *p;
	int oldversion, i;

	CLEAR (start_info);
	CLEAR (proc_info);
	CLEAR (sa);
	CLEAR (sd);

	exit_event = CreateEvent (NULL, TRUE, FALSE, "openvpn_exit");
	if (exit_event == NULL)
	{
#ifdef DEBUG
		PrintErrorDebug((char*)"CreateEvent(openvpn_exit) failed.");
#endif
		if (GetLastError() == ERROR_ACCESS_DENIED)
		{
			/* Assume we're running OpenVPN 1.5/1.6 and the service is started. */
			o.oldversion=1;
			strncpy(o.connect_string, "Successful ARP Flush", sizeof(o.connect_string));
			return(true);
		}
		else
		{
			/* CreateEvent failed */
			ShowLocalizedMsg(GUI_NAME, ERR_VERSION_CREATE_EVENT, "");
			return(false);
		}
	}

#ifdef DEBUG
	PrintErrorDebug((char*)"CreateEvent(openvpn_exit) succeded.");
#endif

	/* construct command line */
	mysnprintf (command_line, "openvpn --version");

	/* construct bin path */
	strncpy(bin_path, o.exe_path, sizeof(bin_path));
	for (i=strlen(bin_path) - 1; i > 0; i--)
	if (bin_path[i] == '\\') break;
	bin_path[i] = '\0';

	/* Make security attributes struct for logfile handle so it can
	be inherited. */
	sa.nLength = sizeof (sa);
	sa.lpSecurityDescriptor = &sd;
	sa.bInheritHandle = TRUE;
	if (!InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
	{
		/* Init Sec. Desc. failed */
		ShowLocalizedMsg (GUI_NAME, ERR_INIT_SEC_DESC, "");
		return(0);
	}
	if (!SetSecurityDescriptorDacl (&sd, TRUE, NULL, FALSE))
	{
		/* Set Dacl failed */
		ShowLocalizedMsg (GUI_NAME, ERR_SET_SEC_DESC_ACL, "");
		return(0);
	}

	/* Create the child input pipe. */
	if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
	{
		/* create pipe failed */
		ShowLocalizedMsg(GUI_NAME, ERR_CREATE_PIPE_INPUT_READ, "");
		return(0);
	}

	/* Create the child output pipe. */
	if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
	{
		/* CreatePipe failed */
		ShowLocalizedMsg(GUI_NAME, ERR_CREATE_PIPE_OUTPUT, "");
		return(0);
	}

	if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
				GetCurrentProcess(),
				&hOutputRead, // Address of new handle.
				0,FALSE, // Make it uninheritable.
				DUPLICATE_SAME_ACCESS))
	{
		/* DuplicateHandle failed */
		ShowLocalizedMsg(GUI_NAME, ERR_DUP_HANDLE_OUTPUT_READ, "");
		return(0);
	}

	if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
				GetCurrentProcess(),
				&hInputWrite, // Address of new handle.
				0,FALSE, // Make it uninheritable.
				DUPLICATE_SAME_ACCESS))
	{
		/* DuplicateHandle failed */
		ShowLocalizedMsg(GUI_NAME, ERR_DUP_HANDLE_INPUT_WRITE, "");
		return(0);
	}


	/* Close inheritable copies of the handles */
	if (!CloseHandle(hOutputReadTmp) || !CloseHandle(hInputWriteTmp)) 
	{
		/* CloseHandle failed */
		ShowLocalizedMsg(GUI_NAME, ERR_CLOSE_HANDLE_TMP, "");
		return(0); 
	}

	/* fill in STARTUPINFO struct */
	GetStartupInfo(&start_info);
	start_info.cb = sizeof(start_info);
	start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
	start_info.wShowWindow = SW_HIDE;
	start_info.hStdInput = hInputRead;
	start_info.hStdOutput = hOutputWrite;
	start_info.hStdError = hOutputWrite;

	/* Start OpenVPN to check version */
	if (!CreateProcess(o.exe_path,
				command_line,
				NULL,
				NULL,
				TRUE,
				CREATE_NEW_CONSOLE,
				NULL,
				bin_path,
				&start_info,
				&proc_info))
	{
		/* CreateProcess failed */
		ShowLocalizedMsg (GUI_NAME, ERR_CREATE_PROCESS,
		o.exe_path,
		command_line,
		bin_path);
		return(0);
	}
#ifdef DEBUG
	else
	PrintDebug("CreateProcess succeded (exe=%s, cl=%s, bin_path=%s).", o.exe_path, command_line, bin_path);
#endif

	/* Default value for oldversion */
	oldversion=0;

	/* Default string to look for to report "Connected". */
	strncpy(o.connect_string, "Successful ARP Flush", sizeof(o.connect_string));

	if (ReadLineFromStdOut(hOutputRead, 0, line) == 1)
	{
#ifdef DEBUG
		PrintDebug("VersionString: %s", line);
#endif
		if (line[8] == '2') /* Majorversion = 2 */
		{
			if (line[10] == '0') /* Minorversion = 0 */
			{
				p=strstr(line, "beta");
				if (p != NULL)
				{
					if (p[5] == ' ') /* 2.0-beta1 - 2.0-beta9 */
					{
						if (p[4] >= '6') /* 2.0-beta6 - 2.0-beta9 */
						{
							oldversion=0;
						}
						else /* < 2.0-beta6 */
						{
							oldversion=1;
						}
					}
					else /* >= 2.0-beta10 */
					{
						if (strncmp(&p[6], "ms", 2) == 0) /* 2.0-betaXXms */
						strncpy(o.connect_string, "Initialization Sequence Completed",
						sizeof(o.connect_string));
						if ( !((p[4] == 1) && (p[5] == 0)) ) /* >= 2.0-beta11 */
						strncpy(o.connect_string, "Initialization Sequence Completed",
						sizeof(o.connect_string));
						
						oldversion=0;
					}
				}
				else /* 2.0 non-beta */
				{
					strncpy(o.connect_string, "Initialization Sequence Completed",
					sizeof(o.connect_string));
					oldversion=0;
				}
			}
			else /* > 2.0 */
			{
				strncpy(o.connect_string, "Initialization Sequence Completed",
				sizeof(o.connect_string));
				oldversion=0;
			}
		}
		else
		{
			if (line[8] == '1') /* Majorversion = 1 */
			{
				oldversion=1;
			}
			else /* Majorversion != (1 || 2) */
			{
				oldversion=0;
			}
		}
	}
	else return(0);

	o.oldversion = oldversion;


	if(!CloseHandle (proc_info.hThread) || !CloseHandle (hOutputWrite)
			|| !CloseHandle (hInputRead) || !CloseHandle(exit_event))
	{
		/* CloseHandle failed */
		ShowLocalizedMsg (GUI_NAME, ERR_CLOSE_HANDLE, "");
		return(0);
	}

	return(1); 
}

/* Return num of connections with Status = CheckVal */
int CountConnectedState(int CheckVal)
{
	int i;
	int count=0;

	for (i=0; i < o.num_configs; i++)
	{
		if (o.cnn[i].connect_status == CheckVal)
		count++;
	}

	return (count);
}

void CheckAndSetTrayIcon()
{

	/* Show green icon if service is running */
	if (o.service_running == SERVICE_CONNECTED)
	{
		SetTrayIcon(CONNECTED);
		return;
	}

	/* Change tray icon if no more connections is running */
	if (CountConnectedState(CONNECTED) != 0)
	SetTrayIcon(CONNECTED);
	else if ((CountConnectedState(CONNECTING) != 0) ||
			(o.service_running == SERVICE_CONNECTING))
	SetTrayIcon(CONNECTING);
	else if (CountConnectedState(RECONNECTING) != 0)
	SetTrayIcon(RECONNECTING);
	else
	SetTrayIcon(DISCONNECTED);
}

void ThreadOpenVPNStatus(int config) 
{
	char conn_name[200];
	HANDLE hThread; 
	DWORD IDThread; 
	char msg[200];
	MSG messages;

	/* Cut of extention from config filename. */
	strncpy(conn_name, o.cnn[config].config_file, sizeof(conn_name));
	conn_name[strlen(conn_name) - (strlen(o.ext_string)+1)]=0;

	if (o.cnn[config].restart)
	{
		/* UserInfo: Connecting */
		TCHAR buf[1000];
		myLoadString(INFO_STATE_RECONNECTING);
		SetDlgItemText(o.cnn[config].hwndStatus, TEXT_STATUS, buf); 
		SetStatusWinIcon(o.cnn[config].hwndStatus, APP_ICON_RECONNECTING);
		EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_DISCONNECT), TRUE);
		EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_RESTART), TRUE);
		SetFocus(GetDlgItem(o.cnn[config].hwndStatus, EDIT_LOG));
		o.cnn[config].restart = false;
	}
	else
	{
		/* Create and Show Status Dialog */  
		TCHAR buf[1000];
		if (!(o.cnn[config].hwndStatus = CreateDialog (o.hInstance, 
						MAKEINTRESOURCE (IDD_STATUS),
						NULL, (DLGPROC) StatusDialogFunc)))
		ExitThread(1);
		/* UserInfo: Connecting */
		myLoadString(INFO_STATE_CONNECTING);
		SetDlgItemText(o.cnn[config].hwndStatus, TEXT_STATUS, (LPCTSTR)buf); 
		SetDlgItemInt(o.cnn[config].hwndStatus, TEXT_CONFIG, (UINT)config, FALSE);
		myLoadString(INFO_CONNECTION_XXX);
		mysnprintf(msg, buf, conn_name);
		SetWindowText(o.cnn[config].hwndStatus, msg);

		if (o.silent_connection[0]=='0')
		ShowWindow(o.cnn[config].hwndStatus, SW_SHOW);
	}


	/* Start Thread to monitor our OpenVPN process */
	hThread = CreateThread(NULL, 0, 
	(LPTHREAD_START_ROUTINE) WatchOpenVPNProcess,
	(int *) config,  // pass config nr
	0, &IDThread); 
	if (hThread == NULL) 
	{
		/* CreateThread failed */
		ShowLocalizedMsg (GUI_NAME, ERR_CREATE_THREAD_READ_STDOUT, "");
		ExitThread(0);
	}

	/* Run the message loop. It will run until GetMessage() returns 0 */
	while (GetMessage (&messages, NULL, 0, 0))
	{
		if(!IsDialogMessage(o.cnn[config].hwndStatus, &messages))
		{
			TranslateMessage(&messages);
			DispatchMessage(&messages);
		}
	}

	ExitThread(0);
}

