Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

909 lines
25 KiB

/*****************************************************************************\
* MODULE: gencab.c
*
* The module contains routines for generating a self-extracting CAB file
* for the IExpress utility. This relies on the cdfCreate and infCreate
* to create the objects for the IExpress process.
*
*
* Copyright (C) 1996-1997 Microsoft Corporation
* Copyright (C) 1996-1997 Hewlett Packard
*
* History:
* 22-Nov-1996 <chriswil> Created.
*
\*****************************************************************************/
#include "pch.h"
#include "spool.h"
#include <initguid.h> // To define the IIS Metabase GUIDs used in this file. Every other file defines GUIDs as extern, except here where we actually define them in initguid.h.
#include <iadmw.h> // Interface header
#include <iiscnfg.h> // MD_ & IIS_MD_ defines
#define CAB_TIMEOUT 1000
/*****************************************************************************\
* Critical Section Handling
*
\*****************************************************************************/
DWORD gdwCritOwner = 0;
VOID cab_enter_crit(VOID)
{
EnterCriticalSection(&g_csGenCab);
gdwCritOwner = GetCurrentThreadId();
}
VOID cab_leave_crit(VOID)
{
gdwCritOwner = 0;
LeaveCriticalSection(&g_csGenCab);
}
VOID cab_check_crit(VOID)
{
DWORD dwCurrent = GetCurrentThreadId();
SPLASSERT((dwCurrent == gdwCritOwner));
}
#define EnterCABCrit() cab_enter_crit()
#define LeaveCABCrit() cab_leave_crit()
#define CheckCABCrit() cab_check_crit()
/*****************************************************************************\
* cab_SetClientSecurity (Local Routine)
*
* This routine sets the thread security so that it has max-privileges.
*
\*****************************************************************************/
HANDLE cab_SetClientSecurity(VOID)
{
HANDLE hToken;
HANDLE hThread = GetCurrentThread();
if (OpenThreadToken(hThread, TOKEN_IMPERSONATE, TRUE, &hToken)) {
if (SetThreadToken(&hThread, NULL)) {
return hToken;
}
CloseHandle(hToken);
}
return NULL;
}
/*****************************************************************************\
* cab_ResetClientSecurity (Local Routine)
*
* This routine resets the client-security to the token passed in. This
* is called in order to set the thread to the original security attributes.
*
\*****************************************************************************/
BOOL cab_ResetClientSecurity(
HANDLE hToken)
{
BOOL bRet;
bRet = SetThreadToken(NULL, hToken);
CloseHandle(hToken);
return bRet;
}
/*****************************************************************************\
* cab_iexpress_sync (Local Routine)
*
* This routine sychronizes the process and won't return until the process
* is done generating the CAB executable.
*
\*****************************************************************************/
BOOL cab_iexpress_sync(
HANDLE hProcess)
{
DWORD dwObj;
DWORD dwExitCode;
while (TRUE) {
dwObj = WaitForSingleObject(hProcess, INFINITE);
// Look for the exit type.
//
switch (dwObj) {
// The process handle triggered the wait. Let's get the
// exit-code and return whether the success. Otherwise,
// drop through and return the failure.
//
case WAIT_OBJECT_0:
GetExitCodeProcess(hProcess, &dwExitCode);
if (dwExitCode == 0)
return TRUE;
// Something failed in the call. We failed.
//
case WAIT_FAILED:
return FALSE;
}
}
}
/*****************************************************************************\
* cab_force_delete_file
*
* Reset a files attributes before deleting it. We copied them to the temp dir
* so we can delete them no matter what.
*
\*****************************************************************************/
VOID cab_force_delete_file(
LPCTSTR lpszFileName)
{
SetFileAttributes( lpszFileName, FILE_ATTRIBUTE_NORMAL );
DeleteFile( lpszFileName );
}
/*****************************************************************************\
* cab_cleanup_files
*
* Cleanup any files left-over in our generation process. Particularly,
* this will remove cab-files.
*
\*****************************************************************************/
VOID cab_cleanup_files(
LPCTSTR lpszDstPath)
{
TCHAR szAllExt[MIN_CAB_BUFFER];
HANDLE hFind;
DWORD idx;
DWORD cItems;
LPTSTR lpszCurDir;
WIN32_FIND_DATA wfd;
static LPCTSTR s_szFiles[] = {
g_szDotInf,
g_szDotBin,
g_szDotCat
};
// Put us into the destination directory where we
// want to cleanup the files.
//
if (lpszCurDir = genGetCurDir()) {
SetCurrentDirectory(lpszDstPath);
// Delete the temporary dat-file used in building
// the cdf-file
//
cab_force_delete_file(g_szDatFile);
// Delete certain extension files.
//
cItems = sizeof(s_szFiles) / sizeof(s_szFiles[0]);
for (idx = 0; idx < cItems; idx++) {
wsprintf(szAllExt, TEXT("*%s"), s_szFiles[idx]);
hFind = FindFirstFile(szAllExt, &wfd);
if (hFind && (hFind != INVALID_HANDLE_VALUE)) {
do {
cab_force_delete_file(wfd.cFileName);
} while (FindNextFile(hFind, &wfd));
FindClose(hFind);
}
}
SetCurrentDirectory(lpszCurDir);
genGFree(lpszCurDir, genGSize(lpszCurDir));
}
return;
}
/*****************************************************************************\
* cab_rename_cab
*
* Renames the .CAB to the dstname.
*
\*****************************************************************************/
BOOL cab_rename_cab(
LPCTSTR lpszDstName)
{
LPTSTR lpszSrc;
LPTSTR lpszDst;
BOOL bRet = FALSE;
if (lpszSrc = genBuildFileName(NULL, lpszDstName, g_szDotCab)) {
if (lpszDst = genBuildFileName(NULL, lpszDstName, g_szDotIpp)) {
bRet = MoveFileEx(lpszSrc, lpszDst, MOVEFILE_REPLACE_EXISTING);
genGFree(lpszDst, genGSize(lpszDst));
}
genGFree(lpszSrc, genGSize(lpszSrc));
}
return bRet;
}
/*****************************************************************************\
* cab_iexpress_exec (Local Routine)
*
* This exec's the IExpress utility to generate the self-extracting CAB
* file.
*
\*****************************************************************************/
BOOL cab_iexpress_exec(
LPCTSTR lpszCabDir,
LPCTSTR lpszDstName,
LPCTSTR lpszSedFile)
{
PROCESS_INFORMATION pi;
STARTUPINFO sti;
LPTSTR lpszCmd;
DWORD cbSize;
LPTSTR lpszOldDir;
BOOL bSuccess = FALSE;
TCHAR szWindowDir[MAX_PATH];
DWORD dwWinDirLen = 0;
if (dwWinDirLen = GetSystemWindowsDirectory (szWindowDir, MAX_PATH))
{
if (szWindowDir[dwWinDirLen - 1] == TEXT ('\\'))
{
szWindowDir[dwWinDirLen - 1] = 0;
}
// Calculate enough space to hold the command-line arguments.
//
cbSize = (dwWinDirLen + lstrlen(lpszSedFile) + lstrlen(g_szCabCmd) + 1) * sizeof(TCHAR);
// Allocate the command-line for the create-process call.
//
if (lpszCmd = (LPTSTR)genGAlloc(cbSize)) {
// Initialize startup-info fields.
//
memset(&sti, 0, sizeof(STARTUPINFO));
sti.cb = sizeof(STARTUPINFO);
// Build the command-line string that exec's IExpress.
//
wsprintf(lpszCmd, g_szCabCmd, szWindowDir, lpszSedFile);
// Change the directory to the cab/sed directory. It
// appears that IExpress requires this to generate.
//
if (lpszOldDir = genGetCurDir()) {
SetCurrentDirectory(lpszCabDir);
// Exec the process.
//
if (EXEC_PROCESS(lpszCmd, &sti, &pi)) {
CloseHandle(pi.hThread);
// This will wait until the process if finished generating
// the file. The return from this indicates whether the
// generation succeeded or not.
//
if (cab_iexpress_sync(pi.hProcess))
bSuccess = cab_rename_cab(lpszDstName);
CloseHandle(pi.hProcess);
}
// Restore our current-directory and free up any strings.
//
SetCurrentDirectory(lpszOldDir);
genGFree(lpszOldDir, genGSize(lpszOldDir));
}
genGFree(lpszCmd, cbSize);
}
}
return bSuccess;
}
/*****************************************************************************\
* cab_get_modtime
*
* Returns the modified-time of the CAB file. This can be used to determine
* whether this is out of date with other files.
*
\*****************************************************************************/
BOOL cab_get_modtime(
LPTSTR lpszOutPath,
LPFILETIME lpft)
{
HANDLE hFile;
BOOL bRet = FALSE;
// Open the file for reading.
//
hFile = gen_OpenFileRead(lpszOutPath);
// If the file exists and was opened, go get the time.
//
if (hFile && (hFile != INVALID_HANDLE_VALUE)) {
bRet = GetFileTime(hFile, NULL, NULL, lpft);
CloseHandle(hFile);
}
return bRet;
}
/*****************************************************************************\
* cab_get_drvname (Local Routine)
*
* This routine attempts to open the printer, and retrieve the printer
* driver-name associated with the FriendlyName. This returns to pointers
* to a driver and a share-name.
*
\*****************************************************************************/
BOOL cab_get_drvname(
LPCTSTR lpszFriendlyName,
LPTSTR* lpszDrvName,
LPTSTR* lpszShrName,
DWORD idxPlt
)
{
HANDLE hPrinter;
LPPRINTER_INFO_2 lppi;
DWORD cbBuf;
DWORD cbNeed;
BOOL bRet = FALSE;
// Initialize the pointers for the fail-case.
//
*lpszShrName = NULL;
*lpszDrvName = NULL;
// Open the printer and use the handle to query the printer for
// the driver-name.
//
if (OpenPrinter((LPTSTR)lpszFriendlyName, &hPrinter, NULL)) {
// First let's see how big our buffer will need to
// be in order to hold the PRINTER_INFO_2.
//
cbBuf = 0;
GetPrinter(hPrinter, PRT_LEV_2, NULL, 0, &cbBuf);
// Allocate storage for holding the print-info structure.
//
if (cbBuf && (lppi = (LPPRINTER_INFO_2)genGAlloc(cbBuf))) {
if (GetPrinter(hPrinter, PRT_LEV_2, (LPBYTE)lppi, cbBuf, &cbNeed)) {
//If this is a Win9X client make sure the driver name is correct
if ( genIsWin9X(idxPlt) )
{
// Call get printer driver to get the actual driver name for Win9X
DWORD rc, cbNeeded;
GetPrinterDriver( hPrinter, (LPTSTR) genStrCliEnvironment(idxPlt), 3,
NULL, 0, &cbNeeded);
rc = GetLastError();
if ( rc == ERROR_INSUFFICIENT_BUFFER )
{
LPBYTE pData;
DWORD dwSize = cbNeeded;
if ( pData = (LPBYTE) genGAlloc(cbNeeded) )
{
if ( GetPrinterDriver( hPrinter, (LPTSTR) genStrCliEnvironment(idxPlt), 3,
pData, dwSize, &cbNeeded) )
{
PDRIVER_INFO_3 pDriver = (PDRIVER_INFO_3) pData;
*lpszDrvName = genGAllocStr(pDriver->pName);
}
genGFree( pData, dwSize );
}
}
else
*lpszDrvName = genGAllocStr(lppi->pDriverName);
}
else
*lpszDrvName = genGAllocStr(lppi->pDriverName);
if ( *lpszDrvName ) {
if (*lpszShrName = genGAllocStr(lppi->pShareName)) {
bRet = TRUE;
} else {
genGFree(*lpszDrvName, genGSize(*lpszDrvName));
*lpszDrvName = NULL;
}
}
}
genGFree(lppi, cbBuf);
}
ClosePrinter(hPrinter);
}
return bRet;
}
/*****************************************************************************\
* cab_get_dstname
*
* Returns the then name of the destination-files. The name built
* incorporates the platform/version so that it is not duplicated with
* any other files.
*
* <Share ChkSum><Arch><Ver>.webpnp
*
* i.e. <share chksum>AXP2 - NT Alpha 4.0
* <share chksum>A863 - NT x86 5.0
* <share chksum>AXP3 - NT Alpha 5.0
* <share chksum>W9X0 - Win9X
*
\*****************************************************************************/
LPTSTR cab_get_dstname(
DWORD idxPlt,
DWORD idxVer,
LPCTSTR lpszShr)
{
int cch;
int cchBuf;
LPCTSTR lpszPlt;
LPTSTR lpszName = NULL;
// Get the platform requested for the client.
//
if (lpszPlt = genStrCliCab(idxPlt)) {
cchBuf = lstrlen(lpszPlt) + MIN_CAB_BUFFER;
// Build the cabname according to platform and version.
//
if (lpszName = (LPTSTR)genGAlloc(cchBuf * sizeof(TCHAR))) {
cch = wsprintf(lpszName, g_szCabName, genChkSum(lpszShr), lpszPlt, idxVer);
SPLASSERT((cch < cchBuf));
}
}
return lpszName;
}
/*****************************************************************************\
* cab_get_name
*
* Returns the name of the returned-file.
*
\*****************************************************************************/
VOID cab_get_name(
LPCTSTR lpszDstName,
LPCTSTR lpszShrName,
LPTSTR lpszName)
{
DWORD cch;
cch = wsprintf(lpszName, TEXT("/printers/%s/%s%s"), g_szPrtCabs, lpszDstName, g_szDotIpp);
SPLASSERT((cch < MAX_PATH));
return;
}
/*****************************************************************************\
* cab_get_webdir
*
* Returns the virtual-root-directory where the cab-files are to be generated
* and stored.
*
\*****************************************************************************/
LPTSTR cab_get_webdir(VOID)
{
BOOL bRet = FALSE;
HRESULT hr; // com error status
METADATA_HANDLE hMeta = NULL; // handle to metabase
CComPtr<IMSAdminBase> pIMeta; // ATL smart ptr
DWORD dwMDRequiredDataLen;
METADATA_RECORD mr;
HANDLE hToken = NULL;
LPTSTR lpszDir = NULL;
TCHAR szBuf[MAX_CAB_BUFFER];
// Need to revert to our service credential to be able to read IIS Metabase.
hToken = RevertToPrinterSelf();
// Create a instance of the metabase object
hr=::CoCreateInstance(CLSID_MSAdminBase,
NULL,
CLSCTX_ALL,
IID_IMSAdminBase,
(void **)&pIMeta);
if( SUCCEEDED( hr )) {
// open key to ROOT on website #1 (default)
hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE,
g_szMetabasePath,
METADATA_PERMISSION_READ,
CAB_TIMEOUT,
&hMeta);
if( SUCCEEDED( hr )) {
mr.dwMDIdentifier = MD_VR_PATH;
mr.dwMDAttributes = 0;
mr.dwMDUserType = IIS_MD_UT_FILE;
mr.dwMDDataType = STRING_METADATA;
mr.dwMDDataLen = sizeof(szBuf);
mr.pbMDData = reinterpret_cast<unsigned char *>(szBuf);
hr = pIMeta->GetData( hMeta, L"", &mr, &dwMDRequiredDataLen );
pIMeta->CloseKey( hMeta );
if( SUCCEEDED( hr ))
{
lpszDir = genBuildFileName(szBuf, g_szPrtCabs, NULL);
}
}
}
if (hToken)
{
ImpersonatePrinterClient(hToken);
}
return lpszDir;
}
/*****************************************************************************\
* cab_get_dstpath
*
* Returns a directory string where the cabinet files are to be generated.
*
\*****************************************************************************/
LPTSTR cab_get_dstpath(VOID)
{
HANDLE hDir;
LPTSTR lpszDir;
// First, we need to find the virtual-directory-root the cab files
// are located.
//
if (lpszDir = cab_get_webdir()) {
// Make sure the directory exists, or we can create it.
//
hDir = gen_OpenDirectory(lpszDir);
if (hDir && (hDir != INVALID_HANDLE_VALUE)) {
CloseHandle(hDir);
} else {
if (CreateDirectory(lpszDir, NULL) == FALSE) {
genGFree(lpszDir, genGSize(lpszDir));
lpszDir = NULL;
}
}
}
return lpszDir;
}
/*****************************************************************************\
* GenerateCAB
*
* Main entry-point for generating the CAB file.
*
* Parameters
* ----------
* lpszFriendlyName - Name of printer (shared-name).
* lpszPortName - Name of output port.
* dwCliInfo - Client platform/architecture/version information.
* lpszOutName - This is returned to the caller specifying the output.
*
\*****************************************************************************/
DWORD GenerateCAB(
LPCTSTR lpszFriendlyName,
LPCTSTR lpszPortName,
DWORD dwCliInfo,
LPTSTR lpszOutName,
BOOL bSecure)
{
INFGENPARM infParm;
HANDLE hToken;
HANDLE hsed;
HANDLE hinf;
FILETIME ftSED;
FILETIME ftCAB;
DWORD idxVer;
DWORD idxPlt;
BOOL bSed;
BOOL bCab;
LPTSTR lpszFullName;
LPTSTR lpszDrvName = NULL;
LPTSTR lpszShrName = NULL;
LPTSTR lpszDstName = NULL;
LPTSTR lpszDstPath = NULL;
LPTSTR lpszFrnName = NULL;
LPCTSTR lpszSedFile;
DWORD dwRet = HSE_STATUS_ERROR;
DWORD dwFailure = ERROR_SUCCESS;
// Initialize the security-token so that we will have
// max-privileges during the file-creation.
//
if ((hToken = cab_SetClientSecurity()) == NULL) {
DBGMSG(DBG_ERROR, ("GenerateCab : Access Denied"));
goto RetCabDone;
}
// Get the platform and version of the client (map to an index
// into tables). Validate the indexes to assure the information
// is valid.
//
idxPlt = genIdxCliPlatform(dwCliInfo);
idxVer = genIdxCliVersion(dwCliInfo);
#ifdef WIN95TST
// WORK : ChrisWil
if (idxPlt == IDX_W9X) {
lstrcpy(lpszOutName, TEXT("/hplaserj/cab_w9x0.webpnp"));
return HSE_STATUS_SUCCESS;
}
#endif
if ((idxPlt == IDX_UNKNOWN) || (idxVer == IDX_UNKNOWN)) {
dwFailure = ERROR_BAD_ENVIRONMENT;
DBGMSG(DBG_ERROR, ("GenerateCab : Unsupported client architecture/version"));
goto RetCabDone;
}
// Get the directory where the cab-files will be placed.
//
if ((lpszDstPath = cab_get_dstpath()) == NULL)
goto RetCabDone;
// Build a cluster-capable friendly-name.
//
if ((lpszFrnName = genFrnName(lpszFriendlyName)) == NULL)
goto RetCabDone;
// Get the driver information about the friendly-name.
//
if (cab_get_drvname(lpszFrnName, &lpszDrvName, &lpszShrName, idxPlt) == FALSE)
goto RetCabDone;
// Get the destination-name.
//
if ((lpszDstName = cab_get_dstname(idxPlt, idxVer, lpszShrName)) == NULL)
goto RetCabDone;
// Fill in the inf-input-parameters. These values should be
// validated at this point.
//
infParm.lpszFriendlyName = lpszFrnName;
infParm.lpszShareName = lpszShrName;
infParm.lpszPortName = lpszPortName;
infParm.idxPlt = idxPlt;
infParm.idxVer = idxVer;
infParm.dwCliInfo = dwCliInfo;
infParm.lpszDrvName = lpszDrvName;
infParm.lpszDstName = lpszDstName;
infParm.lpszDstPath = lpszDstPath;
// Grab the critical-section while we deal with generating
// the files for the driver-install.
//
EnterCABCrit();
// Call to have the INF/CDF files generated. If
// all goes well, then the SED file can be generated
// using the INF as input.
//
if (hinf = infCreate(&infParm)) {
// We created an INF object. Now process the INF
if ( infProcess(hinf) ) {
// Got good INF so now work on CDF
if (hsed = cdfCreate(hinf, bSecure)) {
// We created a CDF object. Now process the CDF
if ( cdfProcess(hsed) ) {
// Allocate a string representing the full-path-name of
// the generated executable.
//
lpszFullName = genBuildFileName(lpszDstPath, lpszDstName, g_szDotIpp);
if (lpszFullName) {
// Get the name of the directive file that we'll be using
// to lauch the iexpress-package with. Do not delete this
// pointer as it is handled by the SED object.
//
if (lpszSedFile = cdfGetName(hsed)) {
// Retrieve modified dates so that we can determine whether
// to generate a new-CAB or return an existing one. If the
// calls return FALSE, the we can assume a file doesn't
// exist, so we'll generate an new one anyway.
//
bSed = cdfGetModTime(hsed, &ftSED);
bCab = cab_get_modtime(lpszFullName, &ftCAB);
// Get the name of the Cab-File that will be
// generated (or returned). This is relative to the
// wwwroot path and is in the form /share/printer.
//
cab_get_name(lpszDstName, lpszShrName, lpszOutName);
// If the bCabMod is TRUE (meaning we have a CAB), and the
// SED is not newer than the CAB, then we really only need
// to return the existing CAB. Otherwise, some files
// must have changed.
//
if ((bSed && bCab) && (CompareFileTime(&ftSED, &ftCAB) <= 0)) {
goto RetCabName;
} else {
if (cab_iexpress_exec(lpszDstPath, lpszDstName, lpszSedFile)) {
RetCabName:
dwRet = HSE_STATUS_SUCCESS;
}
else
dwFailure = ERROR_CANNOT_MAKE;
}
}
else // If cdfGetName
dwFailure = GetLastError();
genGFree(lpszFullName, genGSize(lpszFullName));
}
else // If lpszFullName
dwFailure = GetLastError();
}
else // If cdfProcess()
dwFailure = cdfGetError(hsed); // Get saved error
cdfCleanUpSourceFiles(hinf); // Even if cdfProcess fails it might have
// partialy generated some temporary files
cdfDestroy(hsed);
}
else // If cdfCreate
dwFailure = GetLastError();
}
else // If infProcess()
dwFailure = infGetError(hinf); // Get saved error
infDestroy(hinf);
}
else // If infCreate
dwFailure = GetLastError();
// Cleanup the files generated during processing.
//
cab_cleanup_files(lpszDstPath);
RetCabDone:
// If something failed but we don't know what yet.. Get the error
if ( (dwRet == HSE_STATUS_ERROR) && ( dwFailure == ERROR_SUCCESS ) )
dwFailure = GetLastError();
// Free our strings allocated through this scope.
//
if (lpszFrnName)
genGFree(lpszFrnName, genGSize(lpszFrnName));
if (lpszDrvName)
genGFree(lpszDrvName, genGSize(lpszDrvName));
if (lpszDstName)
genGFree(lpszDstName, genGSize(lpszDstName));
if (lpszShrName)
genGFree(lpszShrName, genGSize(lpszShrName));
if (lpszDstPath)
genGFree(lpszDstPath, genGSize(lpszDstPath));
if (hToken)
cab_ResetClientSecurity(hToken);
LeaveCABCrit();
if (dwFailure != ERROR_SUCCESS)
SetLastError(dwFailure);
return dwRet;
}