/*****************************************************************************
* Copyright (c) 2000-2001 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
*
*****************************************************************************/

// UpdateInfo.cpp: implementation of the CUpdateInfo class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "customapp.h"
#include "UpdateInfo.h"
#include <atlbase.h> // Registry class
#include <newdev.h> // Update API
#include <setupapi.h> // INF copy API
#include <winsvc.h> // SCM

// Get the dynamically changing name of the coinstaller dll
#include "..\..\inc\dxbbuildnumber.h"

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

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CUpdateInfo::CUpdateInfo()
{
   m_nMajor = 0;
   m_nMinor = 0;
   m_nBuildNumber = 0;
}

CUpdateInfo::~CUpdateInfo()
{

}

BOOL CUpdateInfo::MoveInfFiles(void)
{
	char source[MAX_PATH], destination[MAX_PATH];
	char soinf[MAX_PATH], destinf[MAX_PATH], destpnf[MAX_PATH];
	char data[MAX_PATH], windir[MAX_PATH], counter[64];
	HINF handle;
	INFCONTEXT context;
	int i;
	BOOL flag;
	DWORD DirError;
	char *section = "version";
	char *key = "CatalogFile";
	char *catalogfile	= WANLINKS_CAT_FILE;
	BOOL Success = FALSE;

	// Get the windows inf directory, create a digi specific subdir to copy the INF files to
	if ( 0 != GetWindowsDirectory(windir, sizeof(windir)) )
	{
		strcpy(source, windir);
		strcat(source, "\\inf\\");
		strcpy(destination, source);
		strcat(destination, WANLINKS_DXB_NAME_TEXT);

		CreateDirectory(destination,NULL);
		DirError = GetLastError();
		if( ( DirError == ERROR_SUCCESS) || ( DirError == ERROR_ALREADY_EXISTS ))
		{
			strcat(destination, "\\");
			TCHAR * pszInfFiles[] = { WANLINKS_INF_COPY_LIST };

			for (i=0; i<ElementCount(pszInfFiles); i++)
			{
				CString SrcInf,DstInf;
				SrcInf.Format("%s%s",source,pszInfFiles[i]);
				DstInf.Format("%s%s",destination,pszInfFiles[i]);
				if (MoveFileEx(SrcInf,DstInf,MOVEFILE_REPLACE_EXISTING))
					// At least one file was moved...
					Success = TRUE;
			}

			for(i=0;;i++)
			{
				flag = FALSE;
				strcpy(soinf, source);
				strcat(soinf, "oem");
				itoa(i,counter,10);
				strcat(soinf,counter);
				strcat(soinf, ".inf");

				handle = SetupOpenInfFile( soinf, NULL, INF_STYLE_WIN4, NULL);

				if (handle == INVALID_HANDLE_VALUE)
					break;

				flag = SetupFindFirstLine(handle, section, key, &context);
				if(flag)
				{
					SetupGetStringField( &context, 1, data, sizeof(data), NULL);
					SetupCloseInfFile(handle);
					if(!strcmp(catalogfile,data))
					{
						strcpy(destinf, destination);
						strcat(destinf, "oem");
						strcat(destinf, counter);
						strcpy(destpnf, source);
						strcat(destpnf, "oem");
						strcat(destpnf, counter);
						strcat(destpnf,".pnf");
						DeleteFile(destpnf);
						strcat(destinf, ".inf");
						if (MoveFileEx(soinf, destinf, MOVEFILE_REPLACE_EXISTING))
						{
							// At least one file was moved...
							Success = TRUE;
						}
					}
				}
				else
					SetupCloseInfFile(handle);
			}	
		}
	}
	return Success;
}

DWORD CUpdateInfo::CopyInfFiles(CString SourceDir)
{
   DWORD Status = NO_ERROR;

   TCHAR * pszDistribInfFiles[] = {WANLINKS_INF_COPY_LIST};

   CString SourceFile;
   int nIndex;
   for (nIndex = 0; nIndex < ElementCount(pszDistribInfFiles); nIndex++)
   {
      SourceFile.Format("%s\\%s", SourceDir, pszDistribInfFiles[nIndex]);
      if(!SetupCopyOEMInf( SourceFile,
                           NULL,
                           SPOST_PATH,
                           0,
                           NULL,
                           0,
                           NULL,
                           NULL)
                           )
      {
         Status = GetLastError();
         TRACE("Fail: SetupCopyOEMInf: %s\nLast error:%d", SourceFile, Status);
         break;
      }
   }

   return Status;
}

DWORD CUpdateInfo::CopyDistributionFiles(CString DestinationDir)
{
   DWORD Status = NO_ERROR;

   TCHAR * pszDistribFiles[] = {

#ifdef _DEBUG
      // MFC DLLs, only needed for debug, the current free version is guaranteed to be on the system
      WANLINKS_DBG_MFC_COPY_LIST
#endif
      // catalog file
      WANLINKS_CAT_FILE_COPY_LIST
      // readme
      WANLINKS_README_COPY_LIST
      // Drivers
      WANLINKS_DRIVER_COPY_LIST
      // FEPs
      WANLINKS_BIN_COPY_LIST
      // Coinstaller and Config DLLs
      WANLINKS_DLL_COPY_LIST
      // Help Files
      WANLINKS_CHM_COPY_LIST
      // Diags
      WANLINKS_DIAG_COPY_LIST
      // PAM
      WANLINKS_PAM_COPY_LIST
      // Common list of inf files...
      WANLINKS_INF_COPY_LIST
   };

   CString Dest;
   int nIndex;
   for (nIndex = 0; nIndex < ElementCount(pszDistribFiles); nIndex++)
   {
      // don't overwrite the existing file
      Dest.Format("%s\\%s", DestinationDir, pszDistribFiles[nIndex]);
      TRACE("Copying %s\n",Dest);
	   if (!::CopyFile(pszDistribFiles[nIndex],Dest,TRUE))
	   {
		   Status = GetLastError();

		   LPVOID lpMsgBuf;
		   FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
			   NULL,
			   Status,
			   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			   (LPTSTR) &lpMsgBuf,
			   0,
			   NULL );
		   AfxMessageBox((LPCTSTR)lpMsgBuf,MB_OK | MB_ICONINFORMATION);
		   // Free the buffer.
		   LocalFree( lpMsgBuf );

         break; // Stop it...
	   }
   }

   if (NO_ERROR == Status)
   {
      MoveInfFiles(); // get rid of any that are already on the system
      Status = CopyInfFiles(DestinationDir);
   }

   return Status;
}

BOOL CUpdateInfo::DevMgrRunning(void)
{
   CString MMCMainFrameClassName, AppName;
   BOOL Status = FALSE;

   MMCMainFrameClassName.LoadString(IDS_MMCMainFrameClass);

   // Device manager may have our prop page loaded...
   AppName.LoadString(IDS_DeviceManagerWndTitle);
   if (NULL != FindWindow(MMCMainFrameClassName, AppName))
   {
      TRACE("Device Manager is running...\n");
      Status = TRUE;
   }

   // Computer Management may have the device manager loaded...
   AppName.LoadString(IDS_ComputerManagementWndTitle);
   if (NULL != FindWindow(MMCMainFrameClassName, AppName))
   {
      TRACE("Computer Management is running...\n");
      Status = TRUE;
   }

   return Status;
}

BOOL CUpdateInfo::ExistingDriverNewer(void)
{
   // Return true if the installed driver is newer
   BOOL fNewer;
   CRegKey RegKey;
   m_nMajor = 0; // Invalid
   m_nMinor = 0; // Invalid
   m_nBuildNumber = 0; // Invalid

   // If we can't open the key, we know it's an older driver...
   if (ERROR_SUCCESS == RegKey.Open(HKEY_LOCAL_MACHINE, DXB_REGKEY_CONFIG_DATA))
   {
      RegKey.QueryValue(m_nMajor, DXB_REGVAL_MAJOR);
      RegKey.QueryValue(m_nMinor, DXB_REGVAL_MINOR);
      RegKey.QueryValue(m_nBuildNumber, DXB_REGVAL_BUILD);
   }

   fNewer = (  (DXBVER_VERSION_MAJOR < m_nMajor) ||
               ( (DXBVER_VERSION_MAJOR == m_nMajor) && (DXBVER_VERSION_MINOR < m_nMinor) ) ||
               ( (DXBVER_VERSION_MAJOR == m_nMajor) && (DXBVER_VERSION_MINOR == m_nMinor) && (DXBVER_BUILDNUMBER < m_nBuildNumber) )
      );
   return fNewer;
}

BOOL CUpdateInfo::UpdateDriverVersion(void)
{
   BOOL fSuccess;
   CRegKey RegKey;
   if (fSuccess = (ERROR_SUCCESS == RegKey.Create(HKEY_LOCAL_MACHINE, DXB_REGKEY_CONFIG_DATA)))
   {
      fSuccess &= (ERROR_SUCCESS == RegKey.SetValue(DXBVER_VERSION_MAJOR, DXB_REGVAL_MAJOR));
      fSuccess &= (ERROR_SUCCESS == RegKey.SetValue((ULONG)DXBVER_VERSION_MINOR, DXB_REGVAL_MINOR));
      fSuccess &= (ERROR_SUCCESS == RegKey.SetValue(DXBVER_BUILDNUMBER, DXB_REGVAL_BUILD));

	  //
	  // Cast the SetValue (ULONG) because the function is overloaded.  So when the value of the version number
	  // is set to 0 the compiler can not tell which function the call is intended for.  So I will let the
	  // compiler know what to do and how to do it.  Because I am master of the code.
	  //
   }
   return fSuccess;
}

static BOOL
IsNetHwId(TCHAR * szId)
{
   BOOL Result = FALSE;

#if defined(DATAFIRE_RAS_BUILD) || defined(DATAFIRE_SYNC_BUILD)
   DWORD index;
   
   TCHAR * Ids[] =
   {
      WANLINKS_ID_LIST
   };

   for (index = 0; index < ElementCount(Ids); index++)
   {
      if (0 == strcmp(szId, Ids[index]))
      {
         Result = TRUE;
         break;
      }
   }
#endif

   return Result;
}

static BOOL
IsMpsHwId(TCHAR * szId)
{
   BOOL Result = FALSE;

#if defined(ACCELEPORT_RAS_BUILD) || defined(ACCELEPORT_XP_BUILD)
   DWORD index;

   TCHAR * Ids[] =
   {
      WANLINKS_ID_LIST
   };

   for (index = 0; index < ElementCount(Ids); index++)
   {
      if (0 == strcmp(szId, Ids[index]))
      {
         Result = TRUE;
         break;
      }
   }
#endif

   return Result;
}

static BOOL
DisableMatchingDrivers(GUID * pGuid)
{
   // We need to use setup APIs to find the adapters...
	SP_DEVINFO_DATA spd;
   TCHAR szHwId[MAX_PATH];
	DWORD dwHwIdSize, index = 0, nBoards = 0;
	HDEVINFO hDeviceInfo = NULL;

   if (INVALID_HANDLE_VALUE != (hDeviceInfo = SetupDiGetClassDevs(pGuid,NULL,NULL,DIGCF_PROFILE)))
   {
   	spd.cbSize = sizeof(SP_DEVINFO_DATA);

	   // start the enumeration process
	   while(SetupDiEnumDeviceInfo(hDeviceInfo,index,&spd))
	   {
		   if (SetupDiGetDeviceRegistryProperty(hDeviceInfo,&spd,SPDRP_HARDWAREID,&dwHwIdSize,(PBYTE)szHwId,sizeof(szHwId),NULL))
         {
            // HW Id has rev info too... Need to strip this off before the compare
            szHwId[sizeof(PNPHWID) - sizeof(TCHAR)] = NULL;
            TRACE("Checking SPDRP_HARDWAREID:%s\n",szHwId);
            // Check against the PCI ID lists and if we're one of the chosen ones, stop the driver
            if (IsNetHwId(szHwId) || IsMpsHwId(szHwId))
            {
               // Stop the driver
               SP_PROPCHANGE_PARAMS pcp = { 0 };
               pcp.ClassInstallHeader.cbSize = sizeof pcp.ClassInstallHeader;
               pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
               pcp.StateChange = DICS_DISABLE;
               pcp.Scope = DICS_FLAG_GLOBAL;
               TRACE("Getting device info:%s\n",szHwId);
               if (SetupDiSetClassInstallParams(hDeviceInfo, &spd, &pcp.ClassInstallHeader, sizeof pcp))
               {
                  TRACE("Stopping:%s\n",szHwId);
                  if (SetupDiCallClassInstaller( DIF_PROPERTYCHANGE, hDeviceInfo, &spd))
                  {
                     TRACE("Stopped:%s\n",szHwId);
                     nBoards++;
                  }
               }
            }
            ++index; // Is there another?
	      }
      }
      SetupDiDestroyDeviceInfoList(hDeviceInfo);

   }

   TRACE("Stopped %d boards\n",nBoards);
   return (0 != nBoards);
}

BOOL CUpdateInfo::DisableDrivers(void)
{
   GUID NetGuid, MpsGuid;
   DWORD dwGuidSize;
   BOOL fSuccess = FALSE;

   // Only concerned with the adapters at this point
   if (SetupDiClassGuidsFromName(NETCLASS,&NetGuid,1,&dwGuidSize))
   {
      fSuccess |= DisableMatchingDrivers(&NetGuid);
   }

   if (SetupDiClassGuidsFromName(MPSCLASS,&MpsGuid,1,&dwGuidSize))
   {
      fSuccess |= DisableMatchingDrivers(&MpsGuid);
   }

   return fSuccess;
}

BOOL CUpdateInfo::UpdateExistingAdapters(void)
{
   // Return value is TRUE if at least one device was upgraded successfully
   // If at least one device was updated, the driver footprint on the system is current
   BOOL fDriverInstalled = FALSE;

   static const struct
   {
      TCHAR * pszId;
      TCHAR * pszInf;
   }

   InfIds[] =
   {
      WANLINKS_ID_AND_INF_LIST
      // Order is important, the client INFs need to be last
      WANLINKS_CLIENT_ID_AND_INF_LIST
   };

   int nIndex;
   CString Msg;
   TCHAR szFullInfPath[MAX_PATH];
   LPTSTR pFileOffset;
   DWORD Status = NO_ERROR;
   BOOL bRebootRequired = FALSE;


   // We need to check if any drivers from this set are already running, if so, disable
   DisableDrivers();
   // We don't want the system to use the old INFs
   // get rid of any that are already on the system that have our .cat file
   MoveInfFiles();

   for (nIndex = 0; nIndex < ElementCount(InfIds); nIndex++)
   {
      if ( 0 != GetFullPathName( InfIds[nIndex].pszInf,
                                 MAX_PATH,
                                 &szFullInfPath[0],
                                 &pFileOffset))
      {
         Msg.Format("ID %s\nINF %s\n", InfIds[nIndex].pszId, &szFullInfPath[0]);

         if (  UpdateDriverForPlugAndPlayDevices(  NULL,
                                                   InfIds[nIndex].pszId,
                                                   &szFullInfPath[0],
                                                   0, // INSTALLFLAG_FORCE if we want to...
                                                   &bRebootRequired))
         {
            fDriverInstalled = TRUE; // At least one adapter's driver was updated, the new driver is on the system
            TRACE("Success " + Msg);
         }
         else
         {
            CString ErrorMsg;
            Status = GetLastError();
            switch (Status)
            {
            case ERROR_FILE_NOT_FOUND:
               ErrorMsg = "The FullInfPath does not exist. ";
               break;
            case ERROR_NO_SUCH_DEVINST:
               ErrorMsg = " The HardwareId does not match any device on the machine. ";
               break;
            case ERROR_INVALID_FLAGS:
               ErrorMsg = " InstallFlags does not contain an accepted value. ";
               break;
            case NO_ERROR:
               ErrorMsg = " The routine found a match for the HardwareId but the specified driver was not better than the current driver and the caller did not specify the INSTALLFLAG_FORCE flag. ";
               break;
            case ERROR_DI_BAD_PATH:
               ErrorMsg = "ERROR_DI_BAD_PATH ";
               break;

            default:
               ErrorMsg.Format("LastError:%#x",Status);
               break;
            }

            TRACE(ErrorMsg + Msg);
         }
      }
      else
      {

      }
   }

   // Any driver that ended up being upgraded is now started again...

   return fDriverInstalled;
}

BOOL CUpdateInfo::InstallPamA(void)
{
   CString msg, Dest;
	DWORD LastError, Try, nIndex;
   BOOL ServiceExists = FALSE, ServiceStarted = FALSE;
   TCHAR szSystem32Dir[MAX_PATH] = {0};
   SERVICE_STATUS SvcStatus = {0};
	SC_HANDLE hService = NULL;
   TCHAR * pszDistribFiles[] = {
      WANLINKS_PAM_COPY_LIST
   };

   // The Service Control Manager API is used to control the PAM agent...
   SC_HANDLE hScManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
	if ( hScManager == NULL )
	{
      msg.Format( "OpenSCManager failed, GetLastError 0x%x\n", GetLastError() );
      TRACE(msg);
		goto Exit;
	}

   // Stop the DigiPamA service if it exists and is already running
	hService = OpenService(hScManager, WANLINKS_PAMA_SERVICE_NAME_TEXT, SERVICE_ALL_ACCESS);
   if (NULL != hService)
   {
      // We need it to be stopped before we can copy...
      if ( !QueryServiceStatus(hService, &SvcStatus) )
      {
         goto Exit; // Error, we found the service but couldn't query it!
      }

      TRACE("[DgUpgrade] Queried the PAMA service\n");
      if (SvcStatus.dwCurrentState != SERVICE_STOPPED)
      {
         ServiceStarted = TRUE;
         if ( !ControlService(hService, SERVICE_CONTROL_STOP, &SvcStatus) )
	      {
            goto Exit; // Error, we need to be able to do this!
         }

         // We'll wait up to 30 seconds
		   for (Try = 0; Try < 30; Try++)
         {
            if (  QueryServiceStatus(hService, &SvcStatus) &&
                  (SvcStatus.dwCurrentState == SERVICE_STOPPED) )
            {
               TRACE("[DgUpgrade] Stopped!\n");
               ServiceStarted = FALSE; // Same as stopped :-)
               break;
            }
            TRACE(".");
			   Sleep(1000);
		   }
         TRACE("\n");
      }
   }

   if (ServiceStarted)
   {
		TRACE("[DgUpgrade] Couldn't stop existing service.\n");
      ServiceStarted = FALSE; // really means we didn't get the new service in and started
                              // because the old service couldn't be stopped
      goto Exit;
   }

   if (0 == GetWindowsDirectory(szSystem32Dir, sizeof szSystem32Dir))
   {
      goto Exit; // Error, we need this for sure!
   }

   // Copy the service and shim DLL to the system
   for (nIndex = 0; nIndex < ElementCount(pszDistribFiles); nIndex++)
   {
      // overwrite the existing file, we've already done the preliminary version checking
      Dest.Format("%s\\System32\\%s", szSystem32Dir, pszDistribFiles[nIndex]);

      // In some cases the file will be read only...
      if (!SetFileAttributes(Dest,FILE_ATTRIBUTE_NORMAL))
      {
         TRACE("Couldn't set file attributes for %s, LastError:0x%x\n", Dest, GetLastError());
      }

      TRACE("[DgUpgrade] Attempting to copy: %s\n", Dest);
	   if (!CopyFile(pszDistribFiles[nIndex],Dest,FALSE))
	   {
		   LastError = GetLastError();
		   LPVOID lpMsgBuf;
		   FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
			   NULL,
			   LastError,
			   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			   (LPTSTR) &lpMsgBuf,
			   0,
			   NULL );
		   AfxMessageBox((LPCTSTR)lpMsgBuf,MB_OK | MB_ICONINFORMATION);
		   // Free the buffer.
		   LocalFree( lpMsgBuf );

         TRACE("[DgUpgrade] copy failure: %s\n", Dest);
         goto Exit; // Stop it...
	   }
   }

   // Is the PAMA service already installed?
   if (NULL == hService)
   {
      // No, install it now
	   hService = CreateService( hScManager,
                                 WANLINKS_PAMA_SERVICE_NAME_TEXT,
                                 WANLINKS_PAMA_SERVICE_NAME_TEXT,
                                 SERVICE_ALL_ACCESS,
                                 SERVICE_WIN32_OWN_PROCESS,
                                 SERVICE_AUTO_START,
                                 SERVICE_ERROR_IGNORE,
                                 WANLINKS_PAMA_BINARY_PATH_TEXT,
                                 NULL, NULL, NULL, NULL, NULL );
	   if ( (hService == NULL) && (ERROR_SERVICE_EXISTS != (LastError = GetLastError())) )
	   {
  		   TRACE( "[DgUpgrade] CreateService: Failed: GetLastError: %d\n", LastError);
		   goto Exit;
	   }
   }

   ServiceStarted = StartService(hService, 0, NULL);
	LastError = (!ServiceStarted) ? GetLastError() : ERROR_SUCCESS;

	if (ServiceStarted || (LastError == ERROR_SERVICE_ALREADY_RUNNING))
	{
		TRACE("%s has been succeedfully started\n", WANLINKS_PAMA_SERVICE_NAME_TEXT);
	}
	else
	{
		TRACE("%s StartService() failed!  System error: %d\n", WANLINKS_PAMA_SERVICE_NAME_TEXT, LastError);
	}

Exit:
   if ( hService != NULL )
      CloseServiceHandle( hService );
   if ( hScManager != NULL )
      CloseServiceHandle( hScManager );

   return ServiceStarted;

}