/////////////////////////////////////////////////////////////////////////////
//  Copyright (C) 2002 Ultr@VNC. All Rights Reserved.
//
//  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; 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 program is not available from the place from
// which you received this file, check 
// http://ultravnc.sourceforge.net/
//
////////////////////////////////////////////////////////////////////////////
//
// IF YOU USE THIS GPL SOURCE CODE TO MAKE YOUR DSM PLUGIN, PLEASE ADD YOUR
// COPYRIGHT TO THE TOP OF THIS FILE AND SHORTLY DESCRIBE/EXPLAIN THE 
// MODIFICATIONS YOU'VE MADE. THANKS.
//
// IF YOU DON'T USE THIS CODE AS A BASE FOR YOUR PLUGIN, THE HEADER ABOVE AND
// ULTR@VNC COPYRIGHT SHOULDN'T BE FOUND IN YOUR PLUGIN SOURCE CODE.
//
////////////////////////////////////////////////////////////////////////////
//
// Testplugin is a sample that shows the rules that a DSM Plugin for UltraVNC
// must follow (Fonctions to export, their signatures, design constraints...)
//
// WARNING: As it is still beta, some rules might change in the near future
//
//
// WARNING : In order to avoid multithreading issues with your DSM Plugin, it is recommended
// (not to say mandatory...) to use 2 distincts contexts (buffers, structs, objects, keys on so on)
// for "Tranformation" and "Restoration" operations. 
// Actually, 2 distinct mutexes are used at DSMPlugin.cpp level (one in the TransformBuffer 
// function, and the other in the RestoreBuffer function) so Transform and Restore events
// may occur at the same time.
// 
//////////////////////////////////////////////////////////////////////////////

#include "TestPlugin.h"

HINSTANCE hInstance;
PLUGINSTRUCT* pPlugin = NULL;  // struct (or class instance) that handles all Plugin params.
							   // Given as an example.
 
// Internal Plugin vars, depending on what it does
char  szExternalKey[255];	// To store the password/key transmitted via SetParams() by UltraVNC apps
char  szLoaderType[32];     // To store the type of application that has loaded the plugin 
BYTE* pLocalTransBuffer = NULL; // Local Transformation buffer (freed on VNC demand)
BYTE* pLocalRestBuffer = NULL;  // Local Restoration buffer (freed on VNC demand)
int   nLocalTransBufferSize = 0;
int   nLocalRestBufferSize = 0;
//


// Plugin Description
// Please use the following format (with ',' (comma) as separator)
// Name-Description,Author,Date,Version,FileName-Comment
// For the version, we recommend the following format: x.y.z
// The other fields (Name-Description, Author, Date, FileName-Comment) are format free (don't use ',' in them, of course)
#define PLUGIN_DESCRIPTION  "TestPlugin,Sam,Nov 21 2002,1.0.0,TestPlugin.dsm"



// ----------------------------------------------------------------------
//
// A VNC DSM Plugin MUST export (extern "C" (__cdecl)) all the following
// functions (same names, same signatures)
//
// For return values, the rule is:
//    < 0, 0, and NULL pointer mean Error
//    > 0 and pointer != NULL mean Ok
//
// ----------------------------------------------------------------------
//

// Returns the ID string of the Plugin
//
TESTPLUGIN_API char* Description(void)
{
	return PLUGIN_DESCRIPTION;
}


//
// Initialize the plugin and all its internals
// Return -1 if error
//
TESTPLUGIN_API int Startup(void)
{
	// Init everything
	memset(szExternalKey, 0, sizeof(szExternalKey));
	// Create threads if any
	return 1;
}


//
// Stop and Clean up the plugin 
// Return -1 if error
// 
TESTPLUGIN_API int Shutdown(void)
{
	// Terminate Threads if any
	// Cleanup everything
	return 1;
}


//
// Stop and Clean up the plugin 
// Return -1 if error
// 
TESTPLUGIN_API int Reset(void)
{
	// Reset the plugin (buffers, keys, whatever that 
	// requires to be reset between 2 connections

	return 1;
}


//
// Set the plugin params (Key or password )
// If several params are needed, they can be transmitted separated with ',' (comma)
// then translated if necessary. They also can be taken from the internal Plugin config
// 
// WARNING: The plugin is responsible for implementing necessary GUI or File/Registry reading
// to acquire additionnal parameters and to ensure their persistence if necessary.
// Same thing for events/errors logging.
// 
// This function can be called 2 times, both from vncviewer and WinVNC:
// 
// 1.If the user clicks on the Plugin's "config" button in vncviewer and WinVNC dialog boxes
//   In this case this function is called with hVNC != 0 (CASE 1)
//
//   -> szParams is a string formatted as follow: "Part1,Part2"
//   Part1 = "NoPassword"
//   Part2 = type of application that has loaded the plugin
//     "viewer"     : for vncviewer
//     "server-svc" : for WinVNC run as a service
//     "server-app" : for WINVNC run as an application
//
//   -> The Plugin Config dialog box is displayed if any.
// 
// 2.When then plugin is Inited from VNC viewer or Server, right after Startup() call (CASE 2);
//   In this case, this function is called with hVNC = 0 and
//   szParams is a string formatted as follows: "part1,Part2"
//   Part1 = The VNC password, if required by the GetParams() function return value
//   Part2 = type of application that has loaded the plugin
//      "viewer"     : for vncviewer
//      "server-svc" : for WinVNC run as a service
//      "server-app" : for WINVNC run as an application
//   (this info can be used for application/environnement dependent
//    operations (config saving...))
//   
TESTPLUGIN_API int SetParams(HWND hVNC, char* szParams)
{
	// CASE 1
	// Get the environnement (szLoaderType) value that is always sent from 
	// VNC viewer or server
	MyStrToken(szLoaderType, szParams, 2, ',');

	// If hVNC != 0, display for instance the Plugin Config Dialog box 
	if (hVNC)
	{
		// Display the Plugin Config dialog box
		DoDialog();
	}

	// CASE 2: 
	// Use szParams to setup the Plugin.
	// In this example Plugin, the externalkey is not used but we store it anyway.for demo.
	// (it corresponds to the VNC password as we require it in the GetParams() function below)
	MyStrToken(szExternalKey, szParams, 1, ',');

	return 1;
}


//
// Return the current plugin params
// As the plugin is basically a blackbox, VNC doesn't need to know 
// the Plugin parameters.
// But we use this method to know if the plugin needs the VNC password
// as a parameter to do its job correctly (for login step).
// Thus this function is called once before the SetParams() function is called
//  - Return "VNCPasswordNeeded" if VNC password must be transmitted by the UltraVNC app
//  - Return any other Plugin parameters value otherwise (not used in WinVNC & vncviewer for now)
TESTPLUGIN_API char* GetParams(void)
{
	if (strlen(szExternalKey) > 0)
		return szExternalKey; // Return the already stored externalkey params
	else
		return "VNCPasswordNeeded";
	    // return "IveGotAllINeedThanks";
}

// 
// TransformBuffer function
//
// Transform the data given in pDataBuffer then return the pointer on the allocated 
// buffer containing the resulting data.
// The length of the resulting data is given by pnTransformedDataLen
//
TESTPLUGIN_API BYTE* TransformBuffer(BYTE* pDataBuffer, int nDataLen, int* pnTransformedDataLen)
{
	BYTE* pTransBuffer = CheckLocalTransBufferSize(GiveTransDataLen(nDataLen));
	if (pTransBuffer == NULL)
	{
		*pnTransformedDataLen = -1;
		return NULL;
	}

	// Do the actual data padding/transformation/utilization or whatever here
	// In this Sample plugin we do nothing.
	// We just copy the incoming data into the destination buffer, without modifiying it.
	int i;
	for (i = 0; i < nDataLen; i++)
	{
		pTransBuffer[i] = pDataBuffer[i];
	}

	// return the transformed data length
	*pnTransformedDataLen = GiveTransDataLen(nDataLen);

	return pTransBuffer; // Actually, pTransBuffer = pLocalTransBuffer
}


//
// RestoreBuffer function
//
// This function has a 2 mandatory behaviors:
//
// 1. If pRestoredDataBuffer is NULL, the function must return the pointer to current
//    LocalRestBuffer that is going to receive the Transformed data to restore
//    from VNC viewer/server's socket.
//    This buffer must be of the size of transformed data, calculated from nDataLen
//    and this size must be given back in pnRestoredDataLen.
//
// 2. If pRestoredDataBuffer != NULL, it is the destination buffer that is going to receive
//    the restored data. So the function must restore the data that is currently in the
//    local pLocalRestBuffer (nDataLen long) and put the result in pRestoredDataBuffer.
//    The length of the resulting data is given back in pnTransformedDataLen
//
// Explanation: When VNC viewer/server wants to restore some data, it does the following:
// - Calls RestoreBuffer with NULL to get the buffer (and its length) to store incoming transformed data
// - Reads incoming transformed data from socket directly into the buffer given (and of given length)
// - Calls RestoreBuffer again to actually restore data into the given destination buffer.
// This way the copies of data buffers are reduced to the minimum.
// 
TESTPLUGIN_API BYTE* RestoreBuffer(BYTE* pRestoredDataBuffer, int nDataLen, int* pnRestoredDataLen)
{
	// If given buffer is NULL, allocate necessary space here and return the pointer.
	// Additionaly, calculate the resulting length based on nDataLen and return it at the same time.
	if (pRestoredDataBuffer == NULL)
	{
		// Give the size of the transformed data buffer, based on the original data length
		*pnRestoredDataLen = GiveTransDataLen(nDataLen);
		// Ensure the pLocalRestBuffer that receive transformed data is big enought
		BYTE* pBuffer = CheckLocalRestBufferSize(*pnRestoredDataLen);
		return pBuffer; // Actually pBuffer = pLocalRestBuffer
	}

	// If we reach this point, pLocalTransBuffer must contain the transformed data to restore
	// Do the actual data padding/transformation/utilization or whatever here
	// In this Sample plugin we do nothing.
	// We just copy data into the destination buffer, without modifiying it.
	// WARNING: don't use the *pnRestoredDataLen value here, as its value can be
	// modified between the 2 calls to this function.
	// Instead use the GiveTransDataLen() and GiveRestDataLen() functions.
	int i;
	for (i = 0; i < nDataLen; i++)
	{
		pRestoredDataBuffer[i] = pLocalRestBuffer[i];
	}

	// return the resulting data length
	*pnRestoredDataLen = GiveRestDataLen(nDataLen);

	return pLocalRestBuffer;
}


//
// Free the DataBuffer and TransBuffer than have been allocated
// in TransformBuffer and RestoreBuffer, using the method adapted
// to the used allocation method.
//
TESTPLUGIN_API void FreeBuffer(BYTE* pBuffer)
{
	if (pBuffer != NULL) 
		free(pBuffer);

	return;
}


// -----------------------------------------------------------------
// End of functions that must be exported
// -----------------------------------------------------------------
 


// -----------------------------------------------------------------
//  Plugin internal Config Dialog Box Sample
// -----------------------------------------------------------------


// Move the given window to the centre of the screen 
// and bring it to the top.
void CentreWindow(HWND hwnd)
{
	RECT winrect, workrect;
	
	// Find how large the desktop work area is
	SystemParametersInfo(SPI_GETWORKAREA, 0, &workrect, 0);
	int workwidth = workrect.right -  workrect.left;
	int workheight = workrect.bottom - workrect.top;
	
	// And how big the window is
	GetWindowRect(hwnd, &winrect);
	int winwidth = winrect.right - winrect.left;
	int winheight = winrect.bottom - winrect.top;
	// Make sure it's not bigger than the work area
	winwidth = min(winwidth, workwidth);
	winheight = min(winheight, workheight);

	// Now centre it
	SetWindowPos(hwnd, 
		HWND_TOP,
		workrect.left + (workwidth-winwidth) / 2,
		workrect.top + (workheight-winheight) / 2,
		winwidth, winheight, 
		SWP_SHOWWINDOW);
	SetForegroundWindow(hwnd);
}


//
// Display the Plugin Config Dialog box
//
int DoDialog(void)
{
 	return DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIG_DIALOG), 
		NULL, (DLGPROC) ConfigDlgProc, (LONG) pPlugin);
}


//
// Config Dialog box callback
//
BOOL CALLBACK ConfigDlgProc(HWND hwnd,  UINT uMsg,  WPARAM wParam, LPARAM lParam )
{
	PLUGINSTRUCT* _this = (PLUGINSTRUCT*) GetWindowLong(hwnd, GWL_USERDATA);

	switch (uMsg)
	{
	case WM_INITDIALOG:
		{
			// Init the various fields with the saved values if they exist.
			CentreWindow(hwnd);
            return TRUE;
		}

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDOK:
			// Save the parameters in an ini file or registry for instance/
			// szLoaderType (environnement value) param can be used if necessary.
			return TRUE;

		case IDCANCEL:
			EndDialog(hwnd, FALSE);
			return TRUE;
		}
		break;

	case WM_DESTROY:
		EndDialog(hwnd, FALSE);
		return TRUE;
	}
	return 0;
}





// -----------------------------------------------------------------
// Others internal functions, some depending on what the Plugin does
// -----------------------------------------------------------------


//
//
//
BOOL MyStrToken(LPSTR szToken, LPSTR lpString, int nTokenNum, char cSep)
{
	int i = 1;
	while (i < nTokenNum)
	{
		while ( *lpString && (*lpString != cSep) &&(*lpString != '\0'))
		{
			lpString++;
		}
		i++;
		lpString++;
	}
	while ((*lpString != cSep) && (*lpString != '\0'))
	{
		*szToken = *lpString;
		szToken++;
		lpString++;
	}
	*szToken = '\0' ;
	if (( ! *lpString ) || (! *szToken)) return NULL;
	return FALSE;
}


//
// Calculate the len of the data after Transformation and return it. 
// 
// MANDATORY: The calculation must be possible by
// ONLY knowing the source data length ! (=> forget compression algos...)
//
int GiveTransDataLen(int nDataLen)
{
	int nTransDataLen = nDataLen; // In this example, the datalen remains unchanged

	return nTransDataLen;
}

//
// Calculate the len of the data after Restauration and return it. 
// 
// MANDATORY: The calculation must be possible by
// ONLY knowing the source data length ! (=> forget compression algos...)
//
int GiveRestDataLen(int nDataLen)
{
	int nRestDataLen = nDataLen; // In this example, the datalen remains unchanged

	return nRestDataLen;
}

//
// Allocate more space for the local transformation buffer if necessary
// and returns the pointer to this buffer
//
BYTE* CheckLocalTransBufferSize(int nBufferSize)
{
	if (nLocalTransBufferSize >= nBufferSize) return pLocalTransBuffer;

	BYTE *pNewBuffer = (BYTE *) malloc (nBufferSize + 256);
	if (pNewBuffer == NULL) 
	{
		return NULL;
	}
	if (pLocalTransBuffer != NULL)
		free(pLocalTransBuffer);

	pLocalTransBuffer = pNewBuffer;
	nLocalTransBufferSize = nBufferSize + 256;

	memset(pLocalTransBuffer, 0, nLocalTransBufferSize);

	return pLocalTransBuffer;
}


//
// Allocate more space for the local restoration buffer if necessary
// and returns the pointer to this buffer
//
BYTE* CheckLocalRestBufferSize(int nBufferSize)
{
	if (nLocalRestBufferSize >= nBufferSize) return pLocalRestBuffer;

	BYTE *pNewBuffer = (BYTE *) malloc (nBufferSize + 256);
	if (pNewBuffer == NULL) 
	{
		return NULL;
	}
	if (pLocalRestBuffer != NULL)
		free(pLocalRestBuffer);

	pLocalRestBuffer = pNewBuffer;
	nLocalRestBufferSize = nBufferSize + 256;

	memset(pLocalRestBuffer, 0, nLocalRestBufferSize);

	return pLocalRestBuffer;
}



//
// DLL Main Entry point  
// 
BOOL WINAPI DllMain( HINSTANCE hInst, 
                       DWORD  dwReason, 
                       LPVOID lpReserved
					 )
{
    switch (dwReason)
	{
		case DLL_PROCESS_ATTACH:
			hInstance = hInst;
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}

