//  Copyright (C) 2007 Ultr@VNC Team Members. All Rights Reserved.
//  Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
//  This file is part of the VNC system.
//
//  The VNC system 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; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//
// If the source code for the VNC system is not available from the place 
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.

// WinVNC.cpp

// 24/11/97		WEZ

// WinMain and main WndProc for the new version of WinVNC

////////////////////////////
// System headers
#include "stdhdrs.h"

#include "mmsystem.h"

////////////////////////////
// Custom headers
#include "VSocket.h"
#include "WinVNC.h"

#include "vncServer.h"
#include "vncMenu.h"
#include "vncInstHandler.h"
#include "vncService.h"
///unload driver
#include "vncOSVersion.h"
#include "videodriver.h"
//#define CRASH_ENABLED
#ifdef CRASH_ENABLED
	#ifndef _CRASH_RPT_
	#include "crashrpt.h"
	#pragma comment(lib, "crashrpt.lib")
	#endif
#endif

#define LOCALIZATION_MESSAGES   // ACT: full declaration instead on extern ones
#include "localization.h" // Act : add localization on messages



// Application instance and name
HINSTANCE	hAppInstance;
const char	*szAppName = "WinVNC";
DWORD		mainthreadId;
BOOL		AllowMulti=false;
BOOL		DisableMultiWarning=false;
BOOL		fRunningFromExternalService=false;

// sf@2007 - New shutdown order handling stuff (with uvnc_service)
bool			fShutdownOrdered = false;
static HANDLE		hShutdownEvent;
MMRESULT			mmRes;

void WRITETOLOG(char *szText, int size, DWORD *byteswritten, void *);


// WinMain parses the command line and either calls the main App
// routine or, under NT, the main service routine.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	SetOSVersion();
	setbuf(stderr, 0);

        //ACT: Load all messages from ressource file
        Load_Localization(hInstance) ;
        //ACT: end

	// Configure the log file, in case one is required
//		Beep(1000,100);
//		Beep(2000,100);
//		Beep(3000,100);
//		Beep(4000,100);

	char WORKDIR[MAX_PATH];
	if (GetModuleFileName(NULL, WORKDIR, MAX_PATH))
		{
		char* p = strrchr(WORKDIR, '\\');
		if (p == NULL) return 0;
		*p = '\0';
		}
	strcat(WORKDIR,"\\");
	strcat(WORKDIR,"WinVNC.log");

	vnclog.SetFile(WORKDIR, true);
	vnclog.SetMode(2);
	vnclog.SetLevel(10);
	vnclog.Print(LL_STATE, VNCLOG("sockets initialised pre %s\n"),szCmdLine);

#ifdef _DEBUG
	{
		// Get current flag
		int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );

		// Turn on leak-checking bit
		tmpFlag |= _CRTDBG_LEAK_CHECK_DF;

		// Set flag to the new value
		_CrtSetDbgFlag( tmpFlag );
	}
#endif

	// Save the application instance and main thread id
//	Beep(1000,500);
	hAppInstance = hInstance;
	mainthreadId = GetCurrentThreadId();
//	Beep(1000,500);

	// Initialise the VSocket system
	VSocketSystem socksys;
	if (!socksys.Initialised())
	{
		MessageBox(NULL, sz_ID_FAILED_INIT, szAppName, MB_OK);
		return 0;
	}
	vnclog.Print(LL_STATE, VNCLOG("sockets initialised %s\n"),szCmdLine);


	// Make the command-line lowercase and parse it
	int i;
	for (i = 0; i < strlen(szCmdLine); i++)
	{
		szCmdLine[i] = tolower(szCmdLine[i]);
	}

	//MessageBox(NULL, sz_ID_FAILED_INIT, szCmdLine, MB_OK);
	//char szMsg[255];
	// wsprintf(szMsg, "***** DBG - WinMain CommandLine  %s\n", szCmdLine);
	//vnclog.Print(LL_INTINFO, szMsg); 

	BOOL argfound = FALSE;
	for (i = 0; i < strlen(szCmdLine); i++)
	{
		if (szCmdLine[i] <= ' ')
			continue;
		argfound = TRUE;

		// Now check for command-line arguments
		if (strncmp(&szCmdLine[i], winvncRunServiceHelper, strlen(winvncRunServiceHelper)) == 0)
		{
			// NB : This flag MUST be parsed BEFORE "-service", otherwise it will match
			// the wrong option!  (This code should really be replaced with a simple
			// parser machine and parse-table...)

			// Run the WinVNC Service Helper app
			// Seperate service this is not longer needed, just disable it to avoid unwanted effects
			//vncService::PostUserHelperMessage();
			return 0;
		}
		if (strncmp(&szCmdLine[i], winvncRunService, strlen(winvncRunService)) == 0)
		{
			// Run WinVNC as a service
			// Seperate service this is not longer needed, just disable it to avoid unwanted effects
			//return vncService::WinVNCServiceMain();
			return 0;
		}

		if (strncmp(&szCmdLine[i], winvncRunAsUserApp, strlen(winvncRunAsUserApp)) == 0)
		{
			// WinVNC is being run as a user-level program
			return WinVNCAppMain();
		}

		if (strncmp(&szCmdLine[i], "windesk",  strlen("windesk")) == 0)
		{
			fRunningFromExternalService = true;
			vncService::RunningFromExternalService(true); 
			return WinVNCAppMain();
		}

		if (strncmp(&szCmdLine[i], "defaultdesk",  strlen("defaultdesk")) == 0)
		{
//			Beep(500,5000);
			return 0;
		}

		if (strncmp(&szCmdLine[i], winvncAllowMulti, strlen(winvncAllowMulti)) == 0)
		{
			// WinVNC is being run as a user-level program
			AllowMulti=true;
			i+=strlen(winvncAllowMulti);
			continue;
		}

		if (strncmp(&szCmdLine[i], winvncDisableMultiWarning, strlen(winvncDisableMultiWarning)) == 0)
		{
			// WinVNC is being run as a user-level program
			DisableMultiWarning=true;
			i+=strlen(winvncDisableMultiWarning);
			vnclog.SetFile("WinVNCfus.log", false);

			return WinVNCAppMain();

			continue;
		}

		if (strncmp(&szCmdLine[i], winvncInstallService, strlen(winvncInstallService)) == 0)
		{
			// Install WinVNC as a service
			// Seperate service this is not longer needed, just disable it to avoid unwanted effects
			/*vncService::InstallService();
			i+=strlen(winvncInstallService);
			continue;*/
			return 0;
		}

		if (strncmp(&szCmdLine[i], winvncInstallServices, strlen(winvncInstallServices)) == 0)
		{
			// Install WinVNC as a service
			// Seperate service this is not longer needed, just disable it to avoid unwanted effects
			/*
			vncService::InstallService(1);
			i+=strlen(winvncInstallServices);
			continue;*/
			return 0;
		}

		if (strncmp(&szCmdLine[i], winvncReinstallService, strlen(winvncReinstallService)) == 0)
		{
			// Silently remove WinVNC, then re-install it
			// Seperate service this is not longer needed, just disable it to avoid unwanted effects
			/*
			vncService::ReinstallService();
			i+=strlen(winvncReinstallService);
			continue;
			*/
			return 0;
		}
		if (strncmp(&szCmdLine[i], winvncRemoveService, strlen(winvncRemoveService)) == 0)
		{
			// Remove the WinVNC service
			// Seperate service this is not longer needed, just disable it to avoid unwanted effects
			/*
			vncService::RemoveService();
			i+=strlen(winvncRemoveService);
			continue;
			*/
			return 0;
		}
		if (strncmp(&szCmdLine[i], winvncShowProperties, strlen(winvncShowProperties)) == 0)
		{
			// Show the Properties dialog of an existing instance of WinVNC
			vncService::ShowProperties();
			i+=strlen(winvncShowProperties);
			continue;
		}
		if (strncmp(&szCmdLine[i], winvncShowDefaultProperties, strlen(winvncShowDefaultProperties)) == 0)
		{
			// Show the Properties dialog of an existing instance of WinVNC
			vncService::ShowDefaultProperties();
			i+=strlen(winvncShowDefaultProperties);
			continue;
		}
		if (strncmp(&szCmdLine[i], winvncShowAbout, strlen(winvncShowAbout)) == 0)
		{
			// Show the About dialog of an existing instance of WinVNC
			vncService::ShowAboutBox();
			i+=strlen(winvncShowAbout);
			continue;
		}
		if (strncmp(&szCmdLine[i], winvncKillRunningCopy, strlen(winvncKillRunningCopy)) == 0)
		{
			// Kill any already running copy of WinVNC
			vncService::KillRunningCopy();
			i+=strlen(winvncKillRunningCopy);
			continue;
		}
		if (strncmp(&szCmdLine[i], winvncAutoReconnect, strlen(winvncAutoReconnect)) == 0)
		{
			// Note that this "autoreconnect" param MUST be BEFORE the "connect" one
			// on the command line !
			// wa@2005 -- added support for the AutoReconnectId
			i+=strlen(winvncAutoReconnect);

			int start, end;
			char* pszId = NULL;
			start = i;
			// skip any spaces and grab the parameter
			while (szCmdLine[start] <= ' ' && szCmdLine[start] != 0) start++;
			
			if ( strncmp( &szCmdLine[start], winvncAutoReconnectId, strlen(winvncAutoReconnectId) ) == 0 )
			{
				end = start;
				while (szCmdLine[end] > ' ') end++;

				pszId = new char[ end - start + 1 ];
				if (pszId != 0) 
				{
					strncpy( pszId, &(szCmdLine[start]), end - start );
					pszId[ end - start ] = 0;
					pszId = _strupr( pszId );
				}

				i += strlen( pszId );
			}// end of condition we found the ID: parameter
			
			// NOTE:  id must be NULL or the ID:???? (pointer will get deleted when message is processed)
			vncService::PostAddAutoConnectClient( pszId );

			continue;
		}
		/*
		if (strncmp(&szCmdLine[i], winvncFTNoUserImpersonation, strlen(winvncFTNoUserImpersonation)) == 0)
		{
			// We disable User Impersonnation for FT thread
			// (so even a non loged user can access the whole FileSystem)
			vncService::PostAddNewClient(998, 998); // sf@2005 - I still hate to do that ;)
			i+=strlen(winvncFTNoUserImpersonation);
			continue;
		}
		*/
		if (strncmp(&szCmdLine[i], winvncAddNewClient, strlen(winvncAddNewClient)) == 0)
		{
			// Add a new client to an existing copy of winvnc
			i+=strlen(winvncAddNewClient);

			// First, we have to parse the command line to get the filename to use
			int start, end;
			start=i;
			while (szCmdLine[start] <= ' ' && szCmdLine[start] != 0) start++;
			end = start;
			while (szCmdLine[end] > ' ') end++;

			// Was there a hostname (and optionally a port number) given?
			if (end-start > 0)
			{
				char *name = new char[end-start+1];
				if (name != 0) {
					strncpy(name, &(szCmdLine[start]), end-start);
					name[end-start] = 0;

					int port = INCOMING_PORT_OFFSET;
					char *portp = strchr(name, ':');
					if (portp) {
						*portp++ = '\0';
						if (*portp == ':') {
							port = atoi(++portp);	// Port number after "::"
						} else {
							port = atoi(portp);	// Display number after ":"
						}
					}
					vnclog.Print(LL_STATE, VNCLOG("test... %s %d\n"),name,port);
					VCard32 address = VSocket::Resolve(name);
					delete [] name;
					if (address != 0) {
						// Post the IP address to the server

						vncService::PostAddNewClient(address, port);
					}
				}
				i=end;
				continue;
			}
			else 
			{
				// Tell the server to show the Add New Client dialog
				vncService::PostAddNewClient(0, 0);
			}
			continue;
		}

		// Either the user gave the -help option or there is something odd on the cmd-line!

		// Show the usage dialog
		MessageBox(NULL, winvncUsageText, sz_ID_WINVNC_USAGE, MB_OK | MB_ICONINFORMATION);
		break;
	};

	// If no arguments were given then just run
	if (!argfound)
	{
		return WinVNCAppMain();
	}

	return 0;
}


// rdv&sf@2007 - New TrayIcon impuDEsktop/impersonation thread stuff
// Todo: cleanup
HINSTANCE hInst_;
HWND hwnd_;
HANDLE Token_;
HANDLE process_;

// Todo: use same security.cpp function instead
DWORD GetCurrentUserToken_()
{
	HWND tray = FindWindow(("Shell_TrayWnd"), 0);
	if (!tray)
		return 0;
	
	DWORD processId = 0;
	GetWindowThreadProcessId(tray, &processId);
	if (!processId)
		return 0;
	
	process_ = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
	if (!process_)
		return 0;
	
	OpenProcessToken(process_, MAXIMUM_ALLOWED, &Token_);
	return 2;
	
}

// Todo: use same security.cpp function instead
bool ImpersonateCurrentUser_()
{
  SetLastError(0);
  process_=0;
  Token_=NULL;
  if (GetCurrentUserToken_()==0)
  {
	 vnclog.Print(LL_INTERR, VNCLOG("!GetCurrentUserToken_ \n"));
     return false;
  }
  bool test=ImpersonateLoggedOnUser(Token_);
  if (test==1) vnclog.Print(LL_INTERR, VNCLOG("ImpersonateLoggedOnUser OK \n"));
  if (process_) CloseHandle(process_);
  if (Token_) CloseHandle(Token_);
  return test;
}


DWORD WINAPI imp_desktop_thread(LPVOID lpParam)
{
	vncServer *server = (vncServer *)lpParam;
	HDESK desktop;
	//vnclog.Print(LL_INTERR, VNCLOG("SelectDesktop \n"));
	//vnclog.Print(LL_INTERR, VNCLOG("OpenInputdesktop2 NULL\n"));
	desktop = OpenInputDesktop(0, FALSE,
								DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
								DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
								DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
								DESKTOP_SWITCHDESKTOP | GENERIC_WRITE
								);

	if (desktop == NULL)
		vnclog.Print(LL_INTERR, VNCLOG("OpenInputdesktop Error \n"));
	else 
		vnclog.Print(LL_INTERR, VNCLOG("OpenInputdesktop OK\n"));

	HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
	DWORD dummy;

	char new_name[256];

	if (!GetUserObjectInformation(desktop, UOI_NAME, &new_name, 256, &dummy))
	{
		vnclog.Print(LL_INTERR, VNCLOG("!GetUserObjectInformation \n"));
	}

	vnclog.Print(LL_INTERR, VNCLOG("SelectHDESK to %s (%x) from %x\n"), new_name, desktop, old_desktop);

	if (!SetThreadDesktop(desktop))
	{
		vnclog.Print(LL_INTERR, VNCLOG("SelectHDESK:!SetThreadDesktop \n"));
	}

	if (!CloseDesktop(old_desktop))
		vnclog.Print(LL_INTERR, VNCLOG("SelectHDESK failed to close old desktop %x (Err=%d)\n"), old_desktop, GetLastError());

//	ImpersonateCurrentUser_();

	char m_username[200];
	HWINSTA station = GetProcessWindowStation();
	if (station != NULL)
	{
		DWORD usersize;
		GetUserObjectInformation(station, UOI_USER_SID, NULL, 0, &usersize);
		DWORD  dwErrorCode = GetLastError();
		SetLastError(0);
		if (usersize != 0)
		{
			DWORD length = usersize;
			if (GetUserName(m_username, &length) == 0)
			{
				UINT error = GetLastError();
				if (error == ERROR_NOT_LOGGED_ON)
				{
				}
				else
				{
					vnclog.Print(LL_INTERR, VNCLOG("getusername error %d\n"), GetLastError());
					return FALSE;
				}
			}
		}
	}
    vnclog.Print(LL_INTERR, VNCLOG("Username %s \n"),m_username);

	// Create tray icon and menu
	vncMenu *menu = new vncMenu(server);
	if (menu == NULL)
	{
		vnclog.Print(LL_INTERR, VNCLOG("failed to create tray menu\n"));
		PostQuitMessage(0);
	}

	MSG msg;
	while (GetMessage(&msg,0,0,0) != 0 && !fShutdownOrdered)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	// sf@2007 - Close all (vncMenu,tray icon, connections...)
	menu->Shutdown();

	if (menu != NULL)
		delete menu;

	//vnclog.Print(LL_INTERR, VNCLOG("GetMessage stop \n"));
	CloseDesktop(desktop);
//	RevertToSelf();
	return 0;

}


// sf@2007 - For now we use a mmtimer to test the shutdown event periodically
// Maybe there's a less rude method...
void CALLBACK fpTimer(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
	// vnclog.Print(LL_INTERR, VNCLOG("****************** SDTimer tic\n"));
	DWORD result=WaitForSingleObject(hShutdownEvent, 0);
	if (WAIT_OBJECT_0==result)
	{
		ResetEvent(hShutdownEvent);
		fShutdownOrdered = true;
		vnclog.Print(LL_INTERR, VNCLOG("****************** WaitForSingleObject - Shutdown server\n"));
	}
}

void InitSDTimer()
{
	if (mmRes != -1) return;
	vnclog.Print(LL_INTERR, VNCLOG("****************** Init SDTimer\n"));
	mmRes = timeSetEvent( 2000, 0, fpTimer, NULL, TIME_PERIODIC );
}


void KillSDTimer()
{
	vnclog.Print(LL_INTERR, VNCLOG("****************** Kill SDTimer\n"));
	timeKillEvent(mmRes);
	mmRes = -1;
}



// This is the main routine for WinVNC when running as an application
// (under Windows 95 or Windows NT)
// Under NT, WinVNC can also run as a service.  The WinVNCServerMain routine,
// defined in the vncService header, is used instead when running as a service.


int WinVNCAppMain()
{
	SetOSVersion();
	vnclog.Print(LL_INTINFO, VNCLOG("***** DBG - WinVNCAPPMain\n"));
#ifdef CRASH_ENABLED
	LPVOID lpvState = Install(NULL,  "rudi.de.vos@skynet.be", "UltraVnc");
#endif

	// Set this process to be the last application to be shut down.
	// Check for previous instances of WinVNC!
	vncInstHandler *instancehan=new vncInstHandler;
	
	if (!instancehan->Init())
	{
		if (!AllowMulti)
		{
			// We don't allow multiple instances!
			if (!DisableMultiWarning) MessageBox(NULL, sz_ID_ANOTHER_INST, szAppName, MB_OK);
			return 0;
		}
	}

	//vnclog.Print(LL_INTINFO, VNCLOG("***** DBG - Previous instance checked - Trying to create server\n"));
	// CREATE SERVER
	vncServer server;

	// Set the name and port number
	server.SetName(szAppName);
	vnclog.Print(LL_STATE, VNCLOG("server created ok\n"));
	///uninstall driver before cont
	
	// sf@2007 - Set Application0 special mode
	server.RunningFromExternalService(fRunningFromExternalService);

	// sf@2007 - New impersonation thread stuff for tray icon & menu

	/* 
	// Create tray icon & menu if we're running as an app
	vncMenu *menu = new vncMenu(&server);
	if (menu == NULL)
	{
		vnclog.Print(LL_INTERR, VNCLOG("failed to create tray menu\n"));
		PostQuitMessage(0);
	}
	*/
	/*
	MSG msg;
	while (GetMessage(&msg, NULL, 0,0) ) {
		//vnclog.Print(LL_INTINFO, VNCLOG("Message %d received\n"), msg.message);

		TranslateMessage(&msg);  // convert key ups and downs to chars
		DispatchMessage(&msg);
	}
	vnclog.Print(LL_STATE, VNCLOG("shutting down server\n"));
	*/

	// Subscribe to shutdown event
	hShutdownEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "Global\\SessionEvent");
	ResetEvent(hShutdownEvent);
	vnclog.Print(LL_STATE, VNCLOG("***************** SDEvent created \n"));
	// Create the timer that looks periodicaly for shutdown event
	mmRes = -1;
	InitSDTimer();

	//int nn = 10;
	while (/*nn-- > 0 &&*/ !fShutdownOrdered)
	{
		//vnclog.Print(LL_STATE, VNCLOG("################## Creating Imp Thread : %d \n"), nn);

		HANDLE threadHandle;
		DWORD dwTId;
		threadHandle = CreateThread(NULL, 0, imp_desktop_thread, &server, 0, &dwTId);

		WaitForSingleObject( threadHandle, INFINITE );
		CloseHandle(threadHandle);
		vnclog.Print(LL_STATE, VNCLOG("################## Closing Imp Thread\n"));
	}

	/*
	if (menu != NULL)
		delete menu;
	*/

	if (instancehan!=NULL)
		delete instancehan;

	vnclog.Print(LL_STATE, VNCLOG("################## SHUTING DOWN SERVER ####################\n"));
	return 1;
	//return msg.wParam;
};
