windows-nt/Source/XPSP1/NT/printscan/print/spooler/splsetup/srvinst.cxx
2020-09-26 16:20:57 +08:00

729 lines
17 KiB
C++

/*++
Copyright (c) 1995-97 Microsoft Corporation
All rights reserved.
Module Name:
SrvInst.c
Purpose:
Server side install code. This code will be called from a process created by the spooler to do a
"server" side install of a printer driver.
Author:
Patrick Vine (pvine) - 22 March 2000
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "srvinst.hxx"
const TCHAR gcszNTPrint[] = _TEXT("inf\\ntprint.inf");
const TCHAR gcszPrintKey[] = _TEXT("SYSTEM\\CurrentControlSet\\Control\\Print");
const TCHAR gcszTimeOut[] = _TEXT("ServerInstallTimeOut");
const TCHAR gcSpace = _TEXT(' ');
#define DEFAULT_MAX_TIMEOUT 300000 // 5 minute timeout.
/*++
Routine Name:
ServerInstall
Routine Description:
Server side install code to be called by a process created by spooler.
Arguments:
hwnd - Window handle of stub window.
hInstance, - Rundll instance handle.
pszCmdLine - Pointer to command line.
nCmdShow - Show command value always TRUE.
Return Value:
Returns the last error code. This can be read by the spooler by getting the return code from the process.
--*/
DWORD
ServerInstallW(
IN HWND hwnd,
IN HINSTANCE hInstance,
IN LPCTSTR pszCmdLine,
IN UINT nCmdShow
)
{
CServerInstall Installer;
if( Installer.ParseCommand( (LPTSTR)pszCmdLine) && Installer.OpenPipe())
{
if( Installer.GetInstallParameters() )
Installer.InstallDriver();
Installer.SendError();
Installer.ClosePipe();
}
return Installer.GetLastError();
}
////////////////////////////////////////////////////////////////////////////////
//
// Method definitions for CServerInstall Class.
//
////////////////////////////////////////////////////////////////////////////////
CServerInstall::
CServerInstall() : _dwLastError(ERROR_SUCCESS),
_tsDriverName(),
_tsInf(),
_tsSource(),
_tsFlags(),
_tsPipeName(),
_hPipe(INVALID_HANDLE_VALUE)
{
SetMaxTimeOut();
}
CServerInstall::
~CServerInstall()
{
ClosePipe();
}
void
CServerInstall::
SetMaxTimeOut()
{
HKEY hKey;
DWORD dwDummy;
DWORD dwSize = sizeof(_dwMaxTimeOut);
_dwMaxTimeOut = DEFAULT_MAX_TIMEOUT;
if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, gcszPrintKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS )
{
if(RegQueryValueEx( hKey, gcszTimeOut, 0, &dwDummy, (LPBYTE)&_dwMaxTimeOut, &dwSize ) != ERROR_SUCCESS)
{
_dwMaxTimeOut = DEFAULT_MAX_TIMEOUT;
}
RegCloseKey( hKey );
}
}
BOOL
CServerInstall::
InstallDriver()
{
if( SetInfDir() &&
bValidateSourcePath() &&
DriverNotInstalled() )
{
_dwLastError = ::InstallDriverSilently(_tsInf, _tsDriverName, _tsSource);
}
return (_dwLastError == ERROR_SUCCESS);
}
/*++
Parameter structure:
1st word : Flags = default == 0 for now
if flags = 0
2nd word : Pipe name to open
--*/
BOOL
CServerInstall::
ParseCommand( LPTSTR pszCommandStr )
{
TCHAR * pTemp;
DWORD dwCount = 0;
//
// If we don't have a valid command string
//
if( !pszCommandStr || !*pszCommandStr )
{
_dwLastError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Lets grab the flags field
//
pTemp = _tcschr( pszCommandStr, gcSpace );
if( !pTemp )
{
//
// No flags field, fail.
//
_dwLastError = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
*(pTemp++) = 0;
if( !_tsFlags.bUpdate( pszCommandStr ))
{
_dwLastError = ::GetLastError();
goto Cleanup;
}
//
// Currently we only have one case - so we don't need to worry about the
// flags nor branch on them. however we may want to in the future.
//
//
// The rest of the command line is the pipe's name.
//
if( !_tsPipeName.bUpdate( pTemp ))
_dwLastError = ::GetLastError();
Cleanup:
return (_dwLastError == ERROR_SUCCESS);
}
BOOL
CServerInstall::
GetInstallParameters()
{
if(!GetOneParam( &_tsDriverName ))
goto Done;
if( _tsDriverName.bEmpty() )
{
_dwLastError = ERROR_INVALID_PARAMETER;
goto Done;
}
if(!GetOneParam( &_tsInf ))
goto Done;
GetOneParam( &_tsSource );
Done:
return (_dwLastError == ERROR_SUCCESS);
}
//
// Read the size of the string to follow from the pipe.
// Then reads the string and places it in the TString passed.
//
BOOL
CServerInstall::
GetOneParam( TString * tString )
{
DWORD dwSize = 0;
DWORD dwRet = 0;
LPTSTR pszString = NULL;
LPVOID pData = NULL;
if( !ReadOverlapped( _hPipe, &dwSize, sizeof(dwSize), &dwRet ))
goto Done;
if( dwSize == 0 )
goto Done;
//
// The data that we're receiving will be WCHARs as spooler only works with UNICODE.
//
if(!(pData = LocalAllocMem((dwSize + 1)*sizeof(WCHAR))))
{
_dwLastError = ::GetLastError();
goto Done;
}
if( !ReadOverlapped( _hPipe, pData, dwSize*sizeof(WCHAR), &dwRet ) )
goto Done;
if( dwRet != dwSize*sizeof(WCHAR) )
_dwLastError = ERROR_INVALID_PARAMETER;
//
// Because ntprint compiles to both unicode and ansi we need to do this conversion.
// The string coming in will be unicode as it comes from spooler which only
// uses wchars.
//
#ifdef UNICODE
pszString = (LPTSTR)pData;
pData = NULL;
#else
//Get the length of the unicode string
dwSize = WideCharToMultiByte( CP_ACP, 0, (LPWSTR)pData, -1, NULL, 0, NULL, NULL );
//Create the TCHAR string of the same length
if( !(pszString = (LPTSTR)LocalAllocMem((dwSize + 1)*sizeof(TCHAR))))
goto Done;
//Convert the string from unicode to ansi.
if( !WideCharToMultiByte( CP_ACP, 0, (LPWSTR)pData, -1, pszString, dwSize, NULL, NULL ) )
{
_dwLastError = ::GetLastError();
}
#endif
Done:
if( !tString->bUpdate( pszString ))
_dwLastError = ::GetLastError();
if( pData )
LocalFreeMem( pData );
if( pszString )
LocalFreeMem( pszString );
return (_dwLastError == ERROR_SUCCESS);
}
DWORD
CServerInstall::
GetLastError()
{
SetLastError(_dwLastError);
return _dwLastError;
}
BOOL
CServerInstall::
SetInfDir()
{
if( _tsInf.bEmpty() )
{
SetInfToNTPRINTDir();
}
else
{
//
// The inf name must be fully qualified.
//
TCHAR szFullInfName[MAX_PATH];
LPTSTR pszDummy;
DWORD dwLength = GetFullPathName( _tsInf, COUNTOF( szFullInfName ), szFullInfName, &pszDummy );
if( !dwLength || !_tsInf.bUpdate( szFullInfName ))
_dwLastError = ::GetLastError();
}
return (_dwLastError == ERROR_SUCCESS);
}
//
// Returns: TRUE if SUCCESS, FALSE otherwise
//
// Sets the _stInf string to contain %windir%\inf\ntprint.inf
//
BOOL
CServerInstall::
SetInfToNTPRINTDir()
{
UINT uiSize = 0;
UINT uiAllocSize = 0;
PTCHAR pData = NULL;
TCHAR szNTPrintInf[MAX_PATH];
_dwLastError = ERROR_INVALID_DATA;
//
// Get %windir%
// If the return is 0 - the call failed.
// If the return is greater than MAX_PATH we want to fail as something has managed to change
// the system dir to longer than MAX_PATH which is invalid.
//
uiSize = GetSystemWindowsDirectory( szNTPrintInf, COUNTOF(szNTPrintInf) );
if( !uiSize || uiSize > COUNTOF(szNTPrintInf) )
goto Cleanup;
//
// If we don't end in a \ then add one.
//
pData = &szNTPrintInf[ _tcslen(szNTPrintInf) ];
if( *pData != _TEXT('\\') )
*(pData++) = _TEXT('\\');
*(pData) = 0;
uiSize = _tcslen( szNTPrintInf ) + _tcslen( gcszNTPrint ) + 1;
//
// If what we've got sums up to a longer string than the allowable length MAX_PATH - fail
//
if( uiSize > COUNTOF(szNTPrintInf) )
goto Cleanup;
//
// Copy the inf\ntprint.inf string onto the end of the %windir%\ string.
//
_tcscpy( pData, gcszNTPrint );
_dwLastError = ERROR_SUCCESS;
Cleanup:
if( _dwLastError != ERROR_SUCCESS && szNTPrintInf )
{
//
// Got here due to some error. Get what the called function set the last error to.
// If the function set a success, set some error code.
//
if( (_dwLastError = ::GetLastError()) == ERROR_SUCCESS )
_dwLastError = ERROR_INVALID_DATA;
szNTPrintInf[0] = 0;
}
if( !_tsInf.bUpdate( szNTPrintInf ) )
_dwLastError = ::GetLastError();
return (_dwLastError == ERROR_SUCCESS);
}
BOOL
CServerInstall::
bValidateSourcePath(
)
{
if( !_tsSource.bEmpty() &&
!(GetFileAttributes( (LPCTSTR)_tsSource ) & FILE_ATTRIBUTE_DIRECTORY) )
{
_dwLastError = ERROR_DIRECTORY;
}
return (_dwLastError == ERROR_SUCCESS);
}
BOOL
CServerInstall::
OpenPipe()
{
if( !_tsPipeName.bEmpty() )
{
_hPipe = CreateFile( _tsPipeName,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL );
if( _hPipe == INVALID_HANDLE_VALUE )
_dwLastError = ::GetLastError();
}
else
_dwLastError = ERROR_INVALID_HANDLE;
return (_dwLastError == ERROR_SUCCESS);
}
BOOL
CServerInstall::
SendError()
{
DWORD dwDontCare;
return (WriteOverlapped( _hPipe, &_dwLastError, sizeof(_dwLastError), &dwDontCare ));
}
BOOL
CServerInstall::
ClosePipe()
{
BOOL bRet = TRUE;
if( _hPipe != INVALID_HANDLE_VALUE )
{
bRet = CloseHandle( _hPipe );
_hPipe = INVALID_HANDLE_VALUE;
}
return bRet;
}
BOOL
CServerInstall::
ReadOverlapped( HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead )
{
if( hFile != INVALID_HANDLE_VALUE )
{
OVERLAPPED Ov;
ZeroMemory( &Ov,sizeof(Ov));
if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
{
_dwLastError = ::GetLastError();
goto Cleanup;
}
if( !ReadFile( hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &Ov ) &&
::GetLastError() != ERROR_IO_PENDING )
{
_dwLastError = ::GetLastError();
goto Cleanup;
}
if( WaitForSingleObject(Ov.hEvent, _dwMaxTimeOut) == WAIT_TIMEOUT )
{
CancelIo(hFile);
WaitForSingleObject(Ov.hEvent, INFINITE);
}
if( !GetOverlappedResult(hFile, &Ov, lpNumberOfBytesRead, FALSE) )
_dwLastError = ::GetLastError();
Cleanup:
if ( Ov.hEvent )
CloseHandle(Ov.hEvent);
}
else
_dwLastError = ERROR_INVALID_HANDLE;
return (_dwLastError == ERROR_SUCCESS);
}
BOOL
CServerInstall::
WriteOverlapped( HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead )
{
if( hFile != INVALID_HANDLE_VALUE )
{
OVERLAPPED Ov;
ZeroMemory( &Ov,sizeof(Ov));
if ( !(Ov.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) )
{
_dwLastError = ::GetLastError();
goto Cleanup;
}
if( !WriteFile( hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, &Ov ) &&
::GetLastError() != ERROR_IO_PENDING )
{
_dwLastError = ::GetLastError();
goto Cleanup;
}
if( WaitForSingleObject(Ov.hEvent, _dwMaxTimeOut) == WAIT_TIMEOUT )
{
CancelIo(hFile);
WaitForSingleObject(Ov.hEvent, INFINITE);
}
if( !GetOverlappedResult(hFile, &Ov, lpNumberOfBytesRead, FALSE) )
_dwLastError = ::GetLastError();
Cleanup:
if ( Ov.hEvent )
CloseHandle(Ov.hEvent);
}
else
_dwLastError = ERROR_INVALID_HANDLE;
return (_dwLastError == ERROR_SUCCESS);
}
/*+
This function enumerates the drivers and finds if there is one of the same name currently installed.
If there is then open the inf to install with and verify that the inf's version date is newer than the
already installed driver.
Returns: TRUE - if anything fails or the installed date isn't newer than the inf date.
FALSE - only if the driver is installed AND it's date is newer than the inf's date.
-*/
BOOL
CServerInstall::
DriverNotInstalled()
{
LPCTSTR pszKey = _TEXT("DriverVer");
LPTSTR pszEntry = NULL;
LPDRIVER_INFO_6 pDriverInfo6 = NULL;
LPBYTE pBuf = NULL;
PSP_INF_INFORMATION pInfo = NULL;
SYSTEMTIME Time = {0};
BOOL bRet = TRUE;
DWORD dwLength,
dwRet,
dwIndex;
TCHAR *pTemp,
*pTemp2;
if(!EnumPrinterDrivers( NULL, PlatformEnv[MyPlatform].pszName, 6, pBuf, 0, &dwLength, &dwRet ))
{
if( ::GetLastError() != ERROR_INSUFFICIENT_BUFFER )
return TRUE;
if( (pBuf = (LPBYTE) AllocMem( dwLength )) == NULL ||
!EnumPrinterDrivers( NULL, PlatformEnv[MyPlatform].pszName, 6, pBuf, dwLength, &dwLength, &dwRet ))
{
_dwLastError = ::GetLastError();
goto Cleanup;
}
}
else
{
//
// Only way this could succeed is if no drivers installed.
//
_dwLastError = ERROR_UNKNOWN_PRINTER_DRIVER;
return TRUE;
}
for( dwIndex = 0, pDriverInfo6 = (LPDRIVER_INFO_6)pBuf; dwIndex < dwRet; dwIndex++, pDriverInfo6++ )
{
if( _tcscmp( pDriverInfo6->pName, (LPCTSTR)_tsDriverName ) == 0 )
break;
}
if(dwIndex >= dwRet)
{
//
// Driver not found
//
_dwLastError = ERROR_UNKNOWN_PRINTER_DRIVER;
goto Cleanup;
}
//
// The driver has been found... Open up inf and look at it's date.
//
//
// Firstly get the size that will be needed for pInfo.
//
if( !SetupGetInfInformation( (LPCTSTR)_tsInf, INFINFO_INF_NAME_IS_ABSOLUTE, pInfo, 0, &dwLength ) )
{
_dwLastError = ::GetLastError();
goto Cleanup;
}
//
// Alloc pInfo and fill it.
//
if( (pInfo = (PSP_INF_INFORMATION) AllocMem( dwLength )) != NULL &&
SetupGetInfInformation( (LPCTSTR)_tsInf, INFINFO_INF_NAME_IS_ABSOLUTE, pInfo, dwLength, &dwLength ) )
{
//
// Get the size of the date string
//
if( SetupQueryInfVersionInformation( pInfo, 0, pszKey, pszEntry, 0, &dwLength ))
{
//
// Alloc pszEntry and fill it.
//
if( (pszEntry = (LPTSTR) AllocMem( dwLength*sizeof(TCHAR) )) != NULL &&
SetupQueryInfVersionInformation( pInfo, 0, pszKey, pszEntry, dwLength, &dwLength ))
{
//
// Now convert the date string into a SYSTEMTIME
// Date is of the form 03/22/2000
//
// Get the month - 03 part
//
if( (pTemp = _tcschr( pszEntry, _TEXT('/'))) != NULL )
{
*pTemp++ = 0;
Time.wMonth = (WORD)_ttoi( pszEntry );
pTemp2 = pTemp;
//
// Get the day - 22 part
//
if( (pTemp = _tcschr( pTemp2, _TEXT('/'))) != NULL )
{
*pTemp++ = 0;
Time.wDay = (WORD)_ttoi( pTemp2 );
pTemp2 = pTemp;
//
// Get the year - 2000 part
//
pTemp = _tcschr( pTemp2, _TEXT('/'));
if( pTemp )
*pTemp = 0;
Time.wYear = (WORD)_ttoi( pTemp2 );
}
else
_dwLastError = ERROR_INVALID_PARAMETER;
}
else
_dwLastError = ERROR_INVALID_PARAMETER;
}
else
_dwLastError = ::GetLastError();
}
else
_dwLastError = ::GetLastError();
}
else
_dwLastError = ::GetLastError();
//
// If we got all the way to filling in the year, we may have something useful...
//
if( Time.wYear )
{
FILETIME ftTime = {0};
if(SystemTimeToFileTime( &Time, &ftTime ))
{
//
// If the inf time is more recent than what is installed,
// reinstall, otherwise don't
//
if( CompareFileTime(&ftTime, &pDriverInfo6->ftDriverDate) < 1 )
{
bRet = FALSE;
}
}
//
// Getting here and return TRUE or FALSE is still a successful call.
//
_dwLastError = ERROR_SUCCESS;
}
else
_dwLastError = ERROR_INVALID_PARAMETER;
Cleanup:
if( pBuf )
FreeMem( pBuf );
if( pInfo )
FreeMem( pInfo );
if( pszEntry )
FreeMem( pszEntry );
return bRet;
}