/*****************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/

// Services.cpp - utility functions implementing service creation/deletion
#include "stdafx.h"
#include "DriverServices.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#ifdef NT5
BOOL FW_EXPORTED_API RestartDriver(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData,DWORD dwState /*=DICS_PROPCHANGE*/)
{
/*
	SP_PROPCHANGE_PARAMS pcp = { 0 };
	pcp.ClassInstallHeader.cbSize = sizeof pcp.ClassInstallHeader;
	pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
//	pcp.StateChange = DICS_PROPCHANGE;
	pcp.StateChange = dwState;
	pcp.Scope = DICS_FLAG_GLOBAL;

	if (SetupDiSetClassInstallParams(DeviceInfoSet, DeviceInfoData, 
		&pcp.ClassInstallHeader, sizeof pcp))
	{

		SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,DeviceInfoSet,DeviceInfoData);

	}
*/
	SP_DEVINSTALL_PARAMS deip = {0};
	deip.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
	BOOL bResult = SetupDiGetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &deip);
	if (bResult)
	{
		deip.FlagsEx |= DI_FLAGSEX_PROPCHANGE_PENDING;
		bResult = SetupDiSetDeviceInstallParams(DeviceInfoSet, DeviceInfoData, &deip);
	}

	return bResult;
}


// Install a Device for a given Device Info set for a given class
// and driver. If successful, returns the handle to the device instance under
// HKLM\CCS\Control\Class\<ClassGuid>
// The class installer is called to install the device. 
// hDevInfo - device info set
// lpszClass - pointer to null terminated class name
// lpszHardwareID - pointer to null terminated Hardware ID
// lpszRegPath - path to where data for the registry is stored. Can be NULL
// lpszParentDevice - name of the parent device that the installed service
//                    will use. Can be NULL
// szInstance - reference that will get the installed device instance.
static BOOL InstallDevice(const HDEVINFO hDevInfo,LPCTSTR lpszClass,LPCTSTR lpszHardwareID, 
				   LPCTSTR lpszRegPath,LPCTSTR lpszParentDevice,CString& szInstance)
{
	BOOL bSuccess = FALSE;
	DWORD dwsize;
	GUID guid;
	HDEVINFO hDevInfoSet = hDevInfo;
	SP_DEVINFO_DATA sp;
	sp.cbSize = sizeof(SP_DEVINFO_DATA);
	ASSERT(hDevInfo != INVALID_HANDLE_VALUE);
	ASSERT(lpszClass != NULL);
	ASSERT(lpszHardwareID != NULL);
	try
	{
		// get the GUID for the class 
		AffirmThrow(SetupDiClassGuidsFromName(lpszClass,&guid,1,&dwsize));
		// create a new device instance in our NET device info set
		// This will create a unique 4 digit identifier under the
		// enum\root\net key
		AffirmThrow(SetupDiCreateDeviceInfo(hDevInfoSet,lpszClass,&guid,
					NULL,(HWND)NULL,DICD_GENERATE_ID,&sp));
		// register the device with the plug & play manager
		AffirmThrow(SetupDiRegisterDeviceInfo(hDevInfoSet,&sp,0,NULL,NULL,NULL));
		// set the Hardware ID field of the device instance. This 
		// corresponds to what's in the target INF file. The string needs
		// to be terminated with 2 zeros. The length of the string including
		// the 2 zeros needs to be passed.
		size_t cbBuffSize = strlen(lpszHardwareID)+2;
		BYTE* pBuffer = new BYTE[cbBuffSize];
		ZeroMemory(pBuffer,cbBuffSize);
		strcpy((char*)pBuffer,lpszHardwareID);
		AffirmThrow(SetupDiSetDeviceRegistryProperty(hDevInfoSet,&sp,
				SPDRP_HARDWAREID,pBuffer,DWORD(cbBuffSize)));
		delete pBuffer;
		// Build up a list of drivers which correspond to the HARDWARE id
		// set above. In our case there should be only 1 element
		AffirmThrow(SetupDiBuildDriverInfoList(hDevInfoSet,&sp,SPDIT_COMPATDRIVER));

		// select that element
		SP_DRVINFO_DATA spdrv;
		DWORD index=0;
		spdrv.cbSize = sizeof(spdrv);
		AffirmThrow(SetupDiEnumDriverInfo(hDevInfoSet,&sp,SPDIT_COMPATDRIVER,
				index,&spdrv));
		// select the device that we just created along with the driver
		// that applies to it
		AffirmThrow(SetupDiSetSelectedDriver(hDevInfoSet,&sp,&spdrv));
		// make sure our coinstaller entry is registered.
		AffirmThrow(SetupDiRegisterCoDeviceInstallers(hDevInfoSet,&sp));
		// we want to do an installation without starting up the driver
		SP_DEVINSTALL_PARAMS spdip = {0};
		spdip.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
		AffirmThrow(SetupDiGetDeviceInstallParams(hDevInfoSet,&sp,&spdip));
		// spdip.Flags |= DI_NEEDRESTART;
		AffirmThrow(SetupDiSetDeviceInstallParams(hDevInfoSet,&sp,&spdip));
		TRACE("\n --- BEFORE CALLING CLASS INSTALLER --- \n");

		// call the class installer and let it do all the work.
		AffirmThrow(SetupDiCallClassInstaller(DIF_INSTALLDEVICE,hDevInfoSet,&sp));

		TRACE("\n --- BEFORE CALLING GET CLASS PROPERTY --- \n");
		// the device has been installed successfully. Get its instance id
		// under class\control\<NETGUID> 
		DWORD dwReqSize;
		BYTE Buffer[256];
		AffirmThrow(SetupDiGetDeviceRegistryProperty(hDevInfoSet,&sp,
						SPDRP_DRIVER,&dwReqSize,
						(PBYTE)&Buffer,sizeof(Buffer),NULL));

		szInstance = Buffer;
		// now that the device has been installed, write into its registry location
		// 
		CString szTemp(DXB_REGCLASS_VALUE);
		szTemp += Buffer;
		szTemp += _T("\\");
		szTemp += DXB_REGKEY_PARAMETERS;
		HKEY hkey;
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,szTemp,0,KEY_ALL_ACCESS,&hkey) == ERROR_SUCCESS)
		{
			// extract only the device instance #.
			szTemp = szTemp.Right(4);
			RegWriteStrValue(hkey,DXB_REGVAL_NDIS_ADAPTER_INSTANCE,szTemp);
			RegWriteIntValue(hkey,DXB_REGVAL_SPOOF,DXB_REGVAL_SPOOF_OFF);
			if (lpszRegPath)
				RegWriteStrValue(hkey,DXB_REGVAL_REGPATH,lpszRegPath);
			if (lpszParentDevice)
				RegWriteStrValue(hkey,DXB_REGVAL_ADAPTER,lpszParentDevice);

		}
		bSuccess = TRUE;
	}
	catch(CAffirmException ae)
	{
		DWORD err = GetLastError();
		LPVOID lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			err,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL 
			);
		AfxMessageBox((LPCTSTR)lpMsgBuf,MB_OK | MB_ICONINFORMATION);
		// Free the buffer.
		LocalFree( lpMsgBuf );

	}


	return bSuccess;
}

/*
// function that finishes off where InstallDevice left off.
// hDevInfo - handle to device info set. Should match the one passed to InstallDevice
// psp - pointer to SP_DEVINFO_DATA struct. This should be the same as that returned
// from InstallDevice.
// For explanation, read comments on InstallDevice.
static BOOL InstallDevice2(const HDEVINFO hDevInfo,PSP_DEVINFO_DATA psp)
{
	SP_DEVINSTALL_PARAMS spdip= {0};
	spdip.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
	BOOL bResult = FALSE;
	if (SetupDiGetDeviceInstallParams(hDevInfo,psp,&spdip))
	{
		TRACE(" -- obtained Install Params -- \n");
		spdip.Flags &= ~DI_NEEDRESTART;
		if (SetupDiSetDeviceInstallParams(hDevInfo,psp,&spdip))
		{
			TRACE(" -- turning of Need restart flag -- \n");
			RestartDriver(hDevInfo,psp,DICS_PROPCHANGE);
			bResult = TRUE;
		}
	}
	return bResult;

}
*/
// Enumerate all devices within the set, and match their instance ID against the passed in
// id. If a match is found, return TRUE
// inputs: 
// hDevInfo - handle to device set to which device belongs
// szInstanceId - instance Id under HKLM\CCS\Control\Class\<ClassGuid>
// psp - ptr to SP_DEVINFO_DATA structure. This will be filled in with relevant data
// once a device is found
BOOL FW_EXPORTED_API FindDevice(const HDEVINFO hDevInfo,const CString& szInstanceId,PSP_DEVINFO_DATA psp)
{
	ASSERT(hDevInfo);
	ASSERT(szInstanceId);
	ASSERT(!szInstanceId.IsEmpty());
	ASSERT(psp);

	DWORD index = 0;

	// start the enumeration process
	while(SetupDiEnumDeviceInfo(hDevInfo,index,psp))
	{
		// read the driver parameter for this device and get its instance id
		TCHAR buffer[128]; 
		DWORD dwReqSize;
		BOOL bResult = SetupDiGetDeviceRegistryProperty(hDevInfo,psp,
						SPDRP_DRIVER,&dwReqSize,
						(PBYTE)&buffer,sizeof(buffer),NULL);

		if (bResult)
		{
			CString szInstance(buffer);
			TRACE("SrcInst = %s,DstInst = %s \n",szInstanceId,szInstance);
			if (szInstance.CompareNoCase(szInstanceId) == 0)
			{
				return TRUE;
			}
		}
		++index;
	}
	return FALSE;

}

// create a device info set for a given device class
// Callers of this function MUST call SetupDiDestroyDeviceInfoList after
// they are done using the HDEVINFO returned from this function.
HDEVINFO FW_EXPORTED_API GetDevInfo(LPCTSTR lpszClassName)
{
	ASSERT(lpszClassName);
	// create a new DeviceInfoSet for the Class name
	DWORD dwSize;
	GUID guid;

	// get the GUID for the NET class device
	if (!SetupDiClassGuidsFromName(lpszClassName,&guid,1,&dwSize))
	{
		TRACE0("Create::Failed GUID call\n");
		return INVALID_HANDLE_VALUE;
	}

   // Returns all devices including disabled ones
   return(SetupDiGetClassDevs(&guid,NULL,NULL,DIGCF_PROFILE));
}

// install a service (if it hasn't been installed) and instantiate a
// new instance.
// lpszHdwId - the hardware ID of an INF file. This file must be present in
//             %windr%\inf directory.
// lpszRegPath - a Registry Path to data in the registry which the newly
//             - installed service can point to. Can be NULL
// lpszParentDevice - Name of the lower level driver that this service may
//                  - communicate with. Can be NULL
// szInstance - on return will contain the <guid>\<xxxx> path under 
//              HKLM\CurrentControlSet\Control\Class
// bPortClass - set to TRUE if dealing with PORT instead of NET devices
// 
BOOL FW_EXPORTED_API CreateService(LPCTSTR lpszHdwId,LPCTSTR lpszRegPath,
								   LPCTSTR lpszParentDevice,
								   CString& szInstance,
								   BOOL bPortClass/*=FALSE*/)
{
	BOOL bSuccess = FALSE;

	try
	{
		AffirmThrow(lpszHdwId != NULL);
		HDEVINFO hDeviceInfo = GetDevInfo((!bPortClass)?NETCLASS:PORTCLASS);
		AffirmThrow(hDeviceInfo != INVALID_HANDLE_VALUE);
		BOOL bResult = InstallDevice(hDeviceInfo,(!bPortClass)?NETCLASS:PORTCLASS,
			lpszHdwId,lpszRegPath,lpszParentDevice,szInstance);
		SetupDiDestroyDeviceInfoList(hDeviceInfo);
		AffirmThrow(bResult);
		
		bSuccess = TRUE;
	}
	catch( CAffirmException ae )
	{
	}
	return bSuccess;
		
}

// delete the service instance. This instance must match the instance
// returned
// remove a device from a given device set and with a instance Id that exists under the
// HKLM\CCS\Control\Class\<classguid> key. 
// Use the class installer for the device set to remove the device.
// Doing this operation, marks the instance key as volatile. The key will actually be removed
// once the system is restarted. To provide further safety, the spoof flag is enabled
// This can serve as a marker in case the system is not rebooted immediately.
BOOL FW_EXPORTED_API DeleteService(LPCTSTR lpszServiceInstance,BOOL bPortClass/*=FALSE*/)
{
	BOOL bSuccess = FALSE;
	try
	{
		AffirmThrow(lpszServiceInstance != NULL);
		if (!bPortClass)
			TRACE("DeleteService::NETCLASS\n");
		HDEVINFO hDeviceInfo = GetDevInfo((!bPortClass)?NETCLASS:PORTCLASS);
		AffirmThrow(hDeviceInfo != INVALID_HANDLE_VALUE);
		SP_DEVINFO_DATA spd;
		spd.cbSize = sizeof(SP_DEVINFO_DATA);

		if (FindDevice(hDeviceInfo,lpszServiceInstance,&spd))
		{
			// Turn the spoof flag back on
			CString szkey(DXB_REGCLASS_VALUE);
			szkey += lpszServiceInstance;
			szkey += _T("\\");
			szkey += DXB_REGKEY_PARAMETERS;
			HKEY hkey;
			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,szkey,0,KEY_WRITE,&hkey) == ERROR_SUCCESS)
			{
				RegWriteIntValue(hkey,DXB_REGVAL_SPOOF,DXB_REGVAL_SPOOF_ON);
				RegCloseKey(hkey);
			}

			TRACE("DeleteService::Before calling DIF_REMOVE function\n");
			// call the class installer to remove the device
			bSuccess = SetupDiCallClassInstaller(DIF_REMOVE,hDeviceInfo,&spd);
			if (bSuccess)
				TRACE("DeleteService::DIF_REMOVE Successful\n");

		}
		

		SetupDiDestroyDeviceInfoList(hDeviceInfo);
	}
	catch( CAffirmException ae )
	{
	}
	return bSuccess;

}

// issue a driver restart message to the service whose instance is passed in.
// this instance must be the same as received from InstallService
BOOL FW_EXPORTED_API RestartService(LPCTSTR lpszServiceInstance,BOOL bPortClass/*=FALSE*/)
{
	BOOL bSuccess = FALSE;
	ASSERT(lpszServiceInstance);

	try
	{
		HDEVINFO hDeviceInfo = GetDevInfo((!bPortClass)?NETCLASS:PORTCLASS);
		AffirmThrow(hDeviceInfo != INVALID_HANDLE_VALUE);
		SP_DEVINFO_DATA spd;
		spd.cbSize = sizeof(SP_DEVINFO_DATA);
		if (FindDevice(hDeviceInfo,lpszServiceInstance,&spd))
		{
			RestartDriver(hDeviceInfo,&spd,DICS_PROPCHANGE);
			bSuccess = TRUE;
		}
		SetupDiDestroyDeviceInfoList(hDeviceInfo);
	}
	catch( CAffirmException ae )
	{
	}
	return bSuccess;

}

#endif	
