windows-nt/Source/XPSP1/NT/printscan/print/drivers/usermode/driverui/prnevent.c
2020-09-26 16:20:57 +08:00

1450 lines
33 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
prnevent.c
Abstract:
This file handles the DrvPrinterEvent spooler API.
Environment:
Win32 subsystem, DriverUI module, user mode
Revision History:
08/30/00 -fengy-
Added DrvDocumentEvent support.
02/13/97 -davidx-
Implement OEM plugin support.
02/06/97 -davidx-
Rewrote it to use common data management functions.
02/04/97 -davidx-
Reorganize driver UI to separate ps and uni DLLs.
07/17/96 -amandan-
Modified for common UI and shared binary data
05/20/96 -davidx-
Created it.
--*/
#include "precomp.h"
//
// DOCUMENTEVENT_QUERYFILTER is introduced in Whistler.
// We need to define it if it's not defined (on Win2K)
// so our code can be built with Win2K DDK.
//
#ifndef DOCUMENTEVENT_QUERYFILTER
#define DOCUMENTEVENT_QUERYFILTER 14
#endif
//
// Private APIs exported by the spooler for printer drivers and port monitors.
// These must be kept in sync with winsplp.h.
//
typedef HANDLE (*LPREVERTTOPRINTERSELF)(VOID);
typedef BOOL (*LPIMPERSONATEPRINTERCLIENT)(HANDLE);
//
// Forward and external function declarations
//
BOOL BInitOrUpgradePrinterProperties(PCOMMONINFO);
VOID DeleteFontIntallerFile(HANDLE);
PTSTR
GetBinaryFileName(
PTSTR ptstrDataFileName
)
/*++
Routine Description:
This function generates a binary file name from the data file name.
Arguments:
ptstrDataFileName specifies the data file name
Return Value:
TRUE for success and FALSE for failure
Note:
--*/
{
INT iLen;
PTSTR ptstrFileName, ptstrExtension;
PTSTR ptstrBinaryExt, ptstrFileExt;
ptstrFileName = ptstrExtension = NULL;
#ifdef UNIDRV
ptstrBinaryExt = BUD_FILENAME_EXT;
ptstrFileExt = GPD_FILENAME_EXT;
#else
ptstrBinaryExt = BPD_FILENAME_EXT;
ptstrFileExt = PPD_FILENAME_EXT;
#endif
iLen = _tcslen(ptstrDataFileName);
if ((ptstrExtension = _tcsrchr(ptstrDataFileName, TEXT('.'))) == NULL ||
_tcsicmp(ptstrExtension, ptstrFileExt) != EQUAL_STRING)
{
ptstrExtension = ptstrDataFileName + iLen;
iLen += _tcslen(ptstrBinaryExt);
}
if (ptstrFileName = MemAlloc((iLen + 1) * sizeof(TCHAR)))
{
_tcscpy(ptstrFileName, ptstrDataFileName);
_tcscpy(ptstrFileName + (ptstrExtension - ptstrDataFileName), ptstrBinaryExt);
}
return ptstrFileName;
}
BOOL
DrvDriverEvent(
DWORD dwDriverEvent,
DWORD dwLevel,
LPBYTE pDriverInfo,
LPARAM lParam
)
/*++
Routine Description:
This function handles the DrvDriverEvent spooler API
Arguments:
dwDriverEvent specifies the event
dwLevel level of DRIVER_INFO_*
pDriverInfo pointer to DRIVER_INFO_*
lParam event specific parameters.
Return Value:
TRUE for success and FALSE for failure
Note:
--*/
{
BOOL bResult = TRUE;
#ifndef WINNT_40
PDRIVER_INFO_3 pDriverInfo3 = (PDRIVER_INFO_3)pDriverInfo;
PTSTR ptstrFileName;
if (pDriverInfo3 == NULL || pDriverInfo3->pDataFile == NULL)
return FALSE;
switch (dwDriverEvent)
{
case DRIVER_EVENT_INITIALIZE:
break;
case DRIVER_EVENT_DELETE:
//
// Need to delete the binary data generated by the parsers
//
ptstrFileName = GetBinaryFileName(pDriverInfo3->pDataFile);
if (ptstrFileName)
{
DeleteFile(ptstrFileName);
MemFree(ptstrFileName);
}
}
//
// Call the OEM to handle DrvDriverEvent
//
{
PFN_OEMDriverEvent pfnOEMDriverEvent;
POEM_PLUGINS pOemPlugins;
POEM_PLUGIN_ENTRY pOemEntry;
DWORD dwOemCount;
if (! (pOemPlugins = PGetOemPluginInfo(NULL, pDriverInfo3->pConfigFile, pDriverInfo3)) ||
! BLoadOEMPluginModules(pOemPlugins))
{
ERR(("DrvDriverEvent, Cannot load OEM plugins: %d\n", GetLastError()));
if (pOemPlugins)
{
VFreeOemPluginInfo(pOemPlugins);
}
return FALSE;
}
dwOemCount = pOemPlugins->dwCount;
pOemEntry = pOemPlugins->aPlugins;
//
// call OEMDriverEvent entrypoint for each plugin
//
for (; dwOemCount--; pOemEntry++)
{
if (pOemEntry->hInstance == NULL)
continue;
if (HAS_COM_INTERFACE(pOemEntry))
{
HRESULT hr;
hr = HComOEMDriverEvent(pOemEntry,
dwDriverEvent,
dwLevel,
pDriverInfo,
lParam);
if (hr == E_NOTIMPL)
continue;
bResult = SUCCEEDED(hr);
}
else
{
if ((pfnOEMDriverEvent = GET_OEM_ENTRYPOINT(pOemEntry, OEMDriverEvent)) &&
!pfnOEMDriverEvent(dwDriverEvent, dwLevel, pDriverInfo, lParam))
{
ERR(("OEMDriverEvent failed for '%ws': %d\n",
CURRENT_OEM_MODULE_NAME(pOemEntry),
GetLastError()));
bResult = FALSE;
}
}
}
if (pOemPlugins)
VFreeOemPluginInfo(pOemPlugins);
}
#endif // WINNT_40
return bResult;
}
/*++
Routine Name:
DrvDocumentEvent
Routine Description:
Handle certain events associated with printing a document.
Although our core driver doesn't do anything for any events,
this function allows OEM plugins to add their event handling.
Arguments:
hPrinter - printer handle
hdc - device contect handle
iEsc - escape code identifying the event to be handled
cbIn - size in bytes of the array pointed to by pbIn
pbIn - pointer to a ULONG array, whose usage depends on iEsc
cbOut - only used as cbOutput parameter for ExtEscape
pbOut - pointer to an output buffer, whose usage depends on iEsc
Return Value:
DOCUMENTEVENT_FAILURE - iEsc event is supported but a failure occurred
DOCUMENTEVENT_SUCCESS - iEsc event is handled successfully
DOCUMENTEVENT_UNSUPPORTED - iEsc event is not supported
Last Error:
None
--*/
INT
DrvDocumentEvent(
HANDLE hPrinter,
HDC hdc,
int iEsc,
ULONG cbIn,
#ifdef WINNT_40
PULONG pbIn,
#else
PVOID pbIn,
#endif
ULONG cbOut,
#ifdef WINNT_40
PULONG pbOut
#else
PVOID pbOut
#endif
)
{
POEM_PLUGINS pOemPlugins = NULL;
PDRIVER_INFO_3 pDriverInfo3 = NULL;
INT iReturn;
if ((pDriverInfo3 = MyGetPrinterDriver(hPrinter, NULL, 3)) == NULL)
{
ERR(("Cannot get printer driver info: %d\n", GetLastError()));
iReturn = DOCUMENTEVENT_FAILURE;
goto docevent_exit;
}
if (!(pOemPlugins = PGetOemPluginInfo(hPrinter,
pDriverInfo3->pConfigFile,
pDriverInfo3)) ||
!BLoadOEMPluginModules(pOemPlugins))
{
//
// Note that BLoadOEMPluginModules always returns TRUE, even in
// the case when plugin(s) can't be loaded, which is treated
// as there is no plugin(s).
//
ERR(("Cannot get OEM plugin info: %d\n", GetLastError()));
iReturn = DOCUMENTEVENT_FAILURE;
goto docevent_exit;
}
if (pOemPlugins->dwCount)
{
POEM_PLUGIN_ENTRY pOemEntry = pOemPlugins->aPlugins;
DWORD cOemCount = pOemPlugins->dwCount;
INT iResult;
BOOL bOEMDocEventOK = FALSE;
for ( ; cOemCount--; pOemEntry++)
{
HRESULT hr;
if (pOemEntry->hInstance == NULL ||
!HAS_COM_INTERFACE(pOemEntry))
{
continue;
}
hr = HComOEMDocumentEvent(pOemEntry,
hPrinter,
hdc,
iEsc,
cbIn,
(PVOID)pbIn,
cbOut,
(PVOID)pbOut,
&iResult);
if (SUCCEEDED(hr))
{
bOEMDocEventOK = TRUE;
#ifndef WINNT_40
//
// DOCUMENTEVENT_QUERYFILTER is introduced in Whistler.
//
if (iEsc == DOCUMENTEVENT_QUERYFILTER)
{
//
// At most one plugin is allowed to handle the event
// DOCUMENTEVENT_QUERYFILTER, and the filter it specifies
// will be used by spooler.
//
// For all other events, we will call every plugin so
// each will have the chance to perform its tasks.
//
break;
}
#endif // !WINNT_40
}
}
if (bOEMDocEventOK)
{
//
// At least one plugin handled the event successfully, so
// use the return value specified by the plugin(s).
//
iReturn = iResult;
}
else
{
//
// None of the plugins handled the event successfully.
//
iReturn = DOCUMENTEVENT_UNSUPPORTED;
}
}
else
{
//
// There is no plugin.
//
iReturn = DOCUMENTEVENT_UNSUPPORTED;
}
docevent_exit:
if (pDriverInfo3)
{
MemFree(pDriverInfo3);
}
if (pOemPlugins)
{
VFreeOemPluginInfo(pOemPlugins);
}
//
// If there is no plugin, or none of the plugins handles DocumentEvent
// successfully, we return DOCUMENTEVENT_UNSUPPORTED since our driver
// doesn't do anything for DrvDocumentEvent. When spooler sees this
// return value for DOCUMENTEVENT_CREATEDCPRE, it will decide not to
// make any more event calls to the driver.
//
// If the event is handled successfully by the plugins, we will return
// the return value specified by the plugin(s).
//
return iReturn;
}
BOOL
DrvPrinterEvent(
LPWSTR pPrinterName,
INT DriverEvent,
DWORD Flags,
LPARAM lParam
)
/*++
Routine Description:
This function handles the DrvPrinterEvent spooler API
Arguments:
pPrinterName name of device
DriverEvent specifies the event
Flags bits flag
lParam event specific parameters.
Return Value:
TRUE for success and FALSE for failure
Note:
--*/
{
LPREVERTTOPRINTERSELF pRevertToPrinterSelf;
LPIMPERSONATEPRINTERCLIENT pImpersonatePrinterClient;
HINSTANCE hSpoolss = NULL;
HANDLE hToken = NULL;
PCOMMONINFO pci = NULL;
HANDLE hPrinter = NULL;
BOOL bResult = TRUE;
CACHEDFILE CachedFile;
VERBOSE(("Entering DrvPrinterEvent: %d ...\n", DriverEvent));
switch (DriverEvent)
{
case PRINTER_EVENT_CACHE_REFRESH:
//
// Open a handle to the printer connection
//
if (! OpenPrinter(pPrinterName, &hPrinter, NULL))
{
ERR(("OpenPrinter '%ws' failed: %d\n", pPrinterName, GetLastError()));
hPrinter = NULL;
break;
}
//
// Prepare to copy cached driver files from the server, if any
//
#ifdef PSCRIPT
_BPrepareToCopyCachedFile(hPrinter, &CachedFile, REGVAL_NTFFILENAME);
#else
_BPrepareToCopyCachedFile(hPrinter, &CachedFile, REGVAL_FONTFILENAME);
#endif
//
// Load spoolss.dll and get address of functions:
// RevertToPrinterSelf - switch to spooler's security context
// ImpersonatePrinterClient - switch to current user's security context
//
if (! (hSpoolss = LoadLibrary(TEXT("spoolss.dll"))) ||
! (pRevertToPrinterSelf = (LPREVERTTOPRINTERSELF)
GetProcAddress(hSpoolss, "RevertToPrinterSelf")) ||
! (pImpersonatePrinterClient = (LPIMPERSONATEPRINTERCLIENT)
GetProcAddress(hSpoolss, "ImpersonatePrinterClient")))
{
ERR(("Couldn't load spoolss.dll: %d\n", GetLastError()));
if (hSpoolss != NULL)
FreeLibrary(hSpoolss);
_VDisposeCachedFileInfo(&CachedFile);
break;
}
//
// Switch to spooler security context so that we can create
// binary printer description data file in the driver directory
//
// When we call to load raw printer description data, the parser
// will check its cache. If no binary data file exists or existing
// binary data file is out of date, the parser will regenerate
// an up-to-date binary data file.
//
hToken = pRevertToPrinterSelf();
pci = PLoadCommonInfo(hPrinter, pPrinterName, 0);
//
// Copy cached driver file from the server
//
_BCopyCachedFile(pci, &CachedFile);
_VDisposeCachedFileInfo(&CachedFile);
if (hToken)
{
if (!(bResult = pImpersonatePrinterClient(hToken)))
{
ERR(("PrinterEvent-ImpersonatePrinterClient failed: %d\n", GetLastError()));
}
}
FreeLibrary(hSpoolss);
break;
case PRINTER_EVENT_INITIALIZE:
//
// Open a printer with administrator privilege, and
// process OEM plugin configuration information
//
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_ADMIN|FLAG_INIT_PRINTER);
if (pci == NULL)
break;
//
// Initialize default printer-sticky properties in registry
// Add printer forms to the spooler's forms database
//
(VOID) BInitOrUpgradePrinterProperties(pci);
#ifndef WINNT_40
VNotifyDSOfUpdate(pci->hPrinter);
#endif // !WINNT_40
break;
case PRINTER_EVENT_ADD_CONNECTION:
//
// Fix the bug where when NT5 client connects to NT4 server, the server registry
// doesn't have the REGVAL_INIDATA entry. Calling PLoadCommonInfo with
// FLAG_PROCESS_INIFILE will write REGVAL_INIDATA to NT4 registry.
//
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_ADMIN|FLAG_PROCESS_INIFILE);
break;
#ifdef UNIDRV
case PRINTER_EVENT_DELETE:
case PRINTER_EVENT_DELETE_CONNECTION:
//
// Delete font installer file
//
//
// Open a handle to the printer
//
if (! OpenPrinter(pPrinterName, &hPrinter, NULL))
{
ERR(("OpenPrinter '%ws' failed: %d\n", pPrinterName, GetLastError()));
hPrinter = NULL;
break;
}
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL);
DeleteFontIntallerFile(hPrinter);
break;
#endif
default:
pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL);
break;
}
if (pci != NULL)
{
if (bResult)
{
PFN_OEMPrinterEvent pfnOEMPrinterEvent;
//
// call OEMPrinterEvent entrypoint for each plugin
//
FOREACH_OEMPLUGIN_LOOP(pci)
if (HAS_COM_INTERFACE(pOemEntry))
{
HRESULT hr;
hr = HComOEMPrinterEvent(pOemEntry,
pPrinterName,
DriverEvent,
Flags,
lParam);
if (hr == E_NOTIMPL)
continue;
bResult = SUCCEEDED(hr);
}
else
{
if ((pfnOEMPrinterEvent = GET_OEM_ENTRYPOINT(pOemEntry, OEMPrinterEvent)) &&
!pfnOEMPrinterEvent(pPrinterName, DriverEvent, Flags, lParam))
{
ERR(("OEMPrinterEvent failed for '%ws': %d\n",
CURRENT_OEM_MODULE_NAME(pOemEntry),
GetLastError()));
bResult = FALSE;
}
}
END_OEMPLUGIN_LOOP
}
VFreeCommonInfo(pci);
}
else
bResult = FALSE;
if (hPrinter != NULL)
ClosePrinter(hPrinter);
return (bResult);
}
BOOL
BUpgradePrivateFlags(
PCOMMONINFO pci
)
/*++
Routine Description:
Upgrade pPrinterData->dwFlags if necessary
Arguments:
pci - Points to basic printer info
Return Value:
TRUE if successful, FALSE if there is an error
--*/
{
#if 0
DWORD dwFlag, dwIndex, dwSelection, dwFeatureIndex;
PFEATURE pFeature;
PPAGEPROTECT pPageProtect;
//
// UniDriver specific upgrade steps
//
if (BGetPrinterDataDWord(pci->hPrinter, REGVAL_PAGE_PROTECTION, &dwFlag) &&
(pFeature = GET_PREDEFINED_FEATURE(pci->pUIInfo, GID_PAGEPROTECTION)) &&
BCombineCommonInfoOptionsArray(pci))
{
if (dwFlag & DXF_PAGEPROT)
dwSelection = PAGEPRO_ON;
else
dwSelection = PAGEPRO_OFF;
pPageProtect = PGetIndexedOption(pci->pUIInfo, pFeature, 0);
for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++, pPageProtect++)
{
if (dwSelection == pPageProtect->dwPageProtectID)
break;
}
if (dwIndex == pFeature->Options.dwCount)
dwIndex = pFeature->dwDefaultOptIndex;
dwFeatureIndex = GET_INDEX_FROM_FEATURE(pci->pUIInfo, pFeature);
pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex = (BYTE)dwIndex;
SeparateOptionArray(pci->pRawData,
pci->pCombinedOptions,
pci->pPrinterData->aOptions,
MAX_PRINTER_OPTIONS,
MODE_PRINTER_STICKY
);
}
#endif
return TRUE;
}
BOOL
BInitOrUpgradePrinterData(
PCOMMONINFO pci
)
/*++
Routine Description:
Initialize the driver's printer-sticky property data
or upgrade it to current version if it already exists
Arguments:
pci - Points to basic printer info
Return Value:
TRUE if successful, FALSE if there is an error
--*/
{
DWORD dwSize;
BOOL bResult = TRUE;
//
// If the printer property data already exists in the registry
// and it's at least as big as the current PRINTERDATA, then
// we assume it's ok and there is no need to upgrade it.
//
if (!BGetPrinterDataDWord(pci->hPrinter, REGVAL_PRINTER_DATA_SIZE, &dwSize) ||
dwSize < sizeof(PRINTERDATA))
{
//
// Otherwise, upgrade the existing printer property data in the registry
// or save a copy of the default printer property data to registry.
//
bResult = BFillCommonInfoPrinterData(pci) &&
BUpgradePrivateFlags(pci) &&
BSavePrinterProperties(pci->hPrinter, pci->pRawData,
pci->pPrinterData, sizeof(PRINTERDATA));
}
return bResult;
}
BOOL
BAddOrUpgradePrinterForms(
PCOMMONINFO pci
)
/*++
Routine Description:
Add printer specific forms to the spooler's forms database
Arguments:
pci - Points to basic printer info
Return Value:
TRUE if successful, FALSE if there is an error
--*/
{
PPAGESIZE pPageSize;
FORM_INFO_1 FormInfo1;
DWORD dwIndex, dwChecksum32, dwForm;
PFEATURE pFeature;
WCHAR awchBuf[CCHPAPERNAME];
//
// If forms has already been added and printer description
// data hasn't changed, we don't need to do anything
//
if (BGetPrinterDataDWord(pci->hPrinter, REGVAL_FORMS_ADDED, &dwChecksum32) &&
dwChecksum32 == pci->pRawData->dwChecksum32)
{
return TRUE;
}
if (pci->pSplForms == NULL)
pci->pSplForms = MyEnumForms(pci->hPrinter, 1, &pci->dwSplForms);
//
// Get pointer to PageSize feature
//
if ((pFeature = GET_PREDEFINED_FEATURE(pci->pUIInfo, GID_PAGESIZE)) == NULL)
{
WARNING(("No paper size supported\n"));
return FALSE;
}
ZeroMemory(&FormInfo1, sizeof(FormInfo1));
FormInfo1.Flags = FORM_PRINTER;
FormInfo1.pName = awchBuf;
//
// Go through each printer form
//
for (dwIndex=0; dwIndex < pFeature->Options.dwCount; dwIndex++)
{
pPageSize = PGetIndexedOption(pci->pUIInfo, pFeature, dwIndex);
ASSERT(pPageSize != NULL);
//
// Ignore the custom page size option
//
if (pPageSize->dwPaperSizeID == DMPAPER_USER ||
pPageSize->dwPaperSizeID == DMPAPER_CUSTOMSIZE)
{
continue;
}
if (pPageSize->szPaperSize.cx <= 0 ||
pPageSize->szPaperSize.cy <= 0)
{
ERR(("Paper size is too small\n"));
continue;
}
if (! LOAD_STRING_PAGESIZE_NAME(pci, pPageSize, awchBuf, CCHPAPERNAME))
{
ERR(("Cannot get paper name\n"));
continue;
}
//
// Check if the paper name is already in the forms database.
// If it's already in the database as a
//
for (dwForm=0; dwForm < pci->dwSplForms; dwForm++)
{
if (pci->pSplForms[dwForm].Flags == FORM_USER &&
wcscmp(pci->pSplForms[dwForm].pName, awchBuf) == EQUAL_STRING)
{
VERBOSE(("Delete user/driver rdefined form: %ws\n", awchBuf));
DeleteForm(pci->hPrinter, awchBuf);
}
}
//
// Page size:
// remember that FORM_INFO_1 uses micron units while
// PAGESIZE.szPaperSize are in Master units.
//
FormInfo1.Size.cx = MASTER_UNIT_TO_MICRON(pPageSize->szPaperSize.cx,
pci->pUIInfo->ptMasterUnits.x);
FormInfo1.Size.cy = MASTER_UNIT_TO_MICRON(pPageSize->szPaperSize.cy,
pci->pUIInfo->ptMasterUnits.y);
//
// Imageable area:
// for driver-defined forms, all margins should be set to 0.
//
FormInfo1.ImageableArea.left =
FormInfo1.ImageableArea.top = 0;
FormInfo1.ImageableArea.right = FormInfo1.Size.cx;
FormInfo1.ImageableArea.bottom = FormInfo1.Size.cy;
//
// We'll try to add the form first. If that fails,
// we assume the form is already there and try to
// update the form with the new info.
//
(VOID) AddForm(pci->hPrinter, 1, (PBYTE) &FormInfo1);
}
(VOID) BSetPrinterDataDWord(pci->hPrinter,
REGVAL_FORMS_ADDED,
pci->pRawData->dwChecksum32);
return TRUE;
}
BOOL
BInitOrUpgradePrinterProperties(
PCOMMONINFO pci
)
/*++
Routine Description:
Initialize or upgrade printer property information in the registry
Arguments:
pci - Points to basic printer info
Return Value:
TRUE if successful, FALSE if there is an error
--*/
{
BOOL bResult;
//
// Handle PRINTERDATA structure in registry
//
bResult = BInitOrUpgradePrinterData(pci);
//
// Handle driver-defined forms in the spooler's database
//
if (! BAddOrUpgradePrinterForms(pci))
bResult = FALSE;
#ifdef PSCRIPT
//
// pscript specific initializations
//
// Save model-specific NTF filename in registry for NT4 compatibility
if (! BUpdateModelNtfFilename(pci))
bResult = FALSE;
#ifdef WINNT_40
// Also save the current user locale too.
if (! BUpdateVMErrorMessageID(pci))
bResult = FALSE;
#endif // WINNT_40
#endif // PSCRIPT
return bResult;
}
PWSTR
PGetFileDirectory(
PWSTR pServerName
)
/*++
Routine Description:
Get the name of the directory used by the font downloader
to store NTF information about downloaded fonts
Arguments:
pServerName - Name of the print server
Return Value:
Pointer to the directory used for storing NTF information
about downloaded fonts, NULL if there is an error
--*/
{
PWSTR p, pDir = NULL;
DWORD cbNeeded;
static WCHAR wszDir[] = FONTDIR;
//
// Get the printer driver directory path
//
if (GetPrinterDriverDirectory(pServerName, NULL, 1, NULL, 0, &cbNeeded) ||
GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
(pDir = MemAlloc(cbNeeded + sizeof(wszDir))) == NULL ||
!GetPrinterDriverDirectory(pServerName, NULL, 1, (PBYTE) pDir, cbNeeded, &cbNeeded))
{
ERR(("GetPrinterDriverDirectory failed: %d\n", GetLastError()));
MemFree(pDir);
return NULL;
}
//
// Replace the last component of the directory path (which should be w32...)
// with \psfont\
//
if (p = wcsrchr(pDir, TEXT(PATH_SEPARATOR)))
wcscpy(p, wszDir);
else
{
WARNING(("Driver directory is not fully-qualified: %ws\n", pDir));
wcscat(pDir, wszDir);
}
return pDir;
}
PWSTR
PConcatFilename(
PWSTR pDir,
PWSTR pFilename
)
{
PWSTR pBasename;
//
// Strip any directory prefix from the input filename
//
if (pBasename = wcsrchr(pFilename, TEXT(PATH_SEPARATOR)))
pBasename++;
else
pBasename = pFilename;
//
// Concatenate the input directory with the base filename
//
if (! (pFilename = MemAlloc(SIZE_OF_STRING(pDir) + SIZE_OF_STRING(pBasename))))
{
ERR(("Memory allocation failed\n"));
return NULL;
}
wcscpy(pFilename, pDir);
wcscat(pFilename, pBasename);
return pFilename;
}
BOOL
_BPrepareToCopyCachedFile(
HANDLE hPrinter,
PCACHEDFILE pCachedFile,
PWSTR pRegKey
)
/*++
Routine Description:
Prepare to copy files from the server during a printer-connection
cache refresh event
Arguments:
hPrinter - Handle to the printer connection
pCachedFile - Buffer to store information about cached file
Return Value:
TRUE if successful, FALSE if there is an error
Note:
We assume this function is called from within the spooler process
and with current user's security context. Specifically, we must
be able to access the server's print$ share at this point.
--*/
{
PPRINTER_INFO_2 pPrinterInfo2 = NULL;
PWSTR pRemoteFilename;
DWORD dwSize;
ZeroMemory(pCachedFile, sizeof(CACHEDFILE));
pCachedFile->hRemoteFile = INVALID_HANDLE_VALUE;
//
// Find out the name of the file to copy
//
#if !defined(PSCRIPT)
pCachedFile->pFilename = PtstrGetPrinterDataString(hPrinter, pRegKey, &dwSize);
#else
return TRUE;
#endif
if (pCachedFile->pFilename == NULL || *pCachedFile->pFilename == NUL)
return TRUE;
//
// Get the remote NTF filename on the server
//
// NOTE: We're really like to use level 4 here. But due to bug in the
// spooler, GetPrinter level 4 doesn't work for printer connections.
//
if (! (pPrinterInfo2 = MyGetPrinter(hPrinter, 2)) ||
! pPrinterInfo2->pServerName ||
! (pCachedFile->pRemoteDir = PGetFileDirectory(pPrinterInfo2->pServerName)) ||
! (pCachedFile->pLocalDir = PGetFileDirectory(NULL)) ||
! (pRemoteFilename = PConcatFilename(pCachedFile->pRemoteDir, pCachedFile->pFilename)))
{
goto exit_prepare_copyfile;
}
pCachedFile->hRemoteFile = CreateFile(pRemoteFilename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
MemFree(pRemoteFilename);
exit_prepare_copyfile:
MemFree(pPrinterInfo2);
if (pCachedFile->hRemoteFile == INVALID_HANDLE_VALUE)
{
ERR(("Couldn't open remote NTF/FontInfo file: %d\n", GetLastError()));
_VDisposeCachedFileInfo(pCachedFile);
}
return (pCachedFile->hRemoteFile != INVALID_HANDLE_VALUE);
}
BOOL
_BCopyCachedFile(
PCOMMONINFO pci,
PCACHEDFILE pCachedFile
)
/*++
Routine Description:
Copy files from the server during printer-connection cache refresh event
Arguments:
pci - Points to basic printer information
pCachedFile - Points to information about cached file
Return Value:
TRUE if successful, FALSE if there is an error
Note:
We assume this function is called from within the spooler process
and with system's security context. Specifically, we must
be able to write into local machines' printer driver directory.
--*/
#define BUFFER_SIZE 4096
{
HANDLE hLocalFile;
PWSTR pLocalFilename = NULL;
PVOID pBuffer = NULL;
BOOL bResult = FALSE;
DWORD dwCount;
//
// We don't have any file to copy
//
if (pCachedFile->hRemoteFile == INVALID_HANDLE_VALUE)
return TRUE;
//
// Get the name for the local copy of the NTF file
// and allocate temporary buffer
//
ASSERT(BUFFER_SIZE >= MAX_PATH * sizeof(WCHAR));
if (! (pLocalFilename = PConcatFilename(pCachedFile->pLocalDir, pCachedFile->pFilename)) ||
! (pBuffer = MemAlloc(BUFFER_SIZE)))
{
goto exit_copyfile;
}
// Make sure the local directory is created
(VOID) CreateDirectory(pCachedFile->pLocalDir, NULL);
for (dwCount=0; dwCount < 2; dwCount++)
{
hLocalFile = CreateFile(pLocalFilename,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (hLocalFile != INVALID_HANDLE_VALUE)
break;
if (dwCount == 0)
{
//
// If this is our first try, then attempt to move
// the existing file to a temporary file and set
// it to be delete-on-reboot.
//
#ifdef PSCRIPT
if (! GetTempFileName(pCachedFile->pLocalDir, L"NTF", 0, pBuffer) ||
#else
if (! GetTempFileName(pCachedFile->pLocalDir, L"FON", 0, pBuffer) ||
#endif
! MoveFileEx(pLocalFilename, pBuffer, MOVEFILE_REPLACE_EXISTING) ||
! MoveFileEx(pBuffer, NULL, MOVEFILE_DELAY_UNTIL_REBOOT))
{
break;
}
}
}
if (hLocalFile != INVALID_HANDLE_VALUE)
{
while (ReadFile(pCachedFile->hRemoteFile, pBuffer, BUFFER_SIZE, &dwCount, NULL))
{
//
// Have we reached end-of-file?
//
if (dwCount == 0)
{
bResult = TRUE;
break;
}
if (! WriteFile(hLocalFile, pBuffer, dwCount, &dwCount, NULL))
break;
}
CloseHandle(hLocalFile);
//
// If file copying failed, be sure to delete the temporary file
//
if (! bResult)
DeleteFile(pLocalFilename);
}
exit_copyfile:
MemFree(pLocalFilename);
MemFree(pBuffer);
if (! bResult)
ERR(("Couldn't copy remote NTF/FontInfo file: %d\n", GetLastError()));
return bResult;
}
VOID
_VDisposeCachedFileInfo(
PCACHEDFILE pCachedFile
)
/*++
Routine Description:
Clean up after copying files from the server
during printer-connection cache refresh
Arguments:
pCachedFile - Points to information about cached file
Return Value:
NONE
--*/
{
if (pCachedFile->hRemoteFile != INVALID_HANDLE_VALUE)
CloseHandle(pCachedFile->hRemoteFile);
MemFree(pCachedFile->pFilename);
MemFree(pCachedFile->pRemoteDir);
MemFree(pCachedFile->pLocalDir);
ZeroMemory(pCachedFile, sizeof(CACHEDFILE));
pCachedFile->hRemoteFile = INVALID_HANDLE_VALUE;
}
#ifdef UNIDRV
VOID
DeleteFontIntallerFile(
HANDLE hPrinter
)
/*++
Routine Description:
Delete font installer file when printer is deleted
Arguments:
hPrinter - Handle to printer
Return Value:
NONE
--*/
{
PWSTR pFilename;
PWSTR pLocalDir;
PWSTR pLocalFilename = NULL;
pFilename = PtstrGetPrinterDataString(hPrinter, REGVAL_FONTFILENAME, NULL);
if (!pFilename || !*pFilename)
return;
if (!(pLocalDir = PGetFileDirectory(NULL)))
goto exit_deletefile;
if (!(pLocalFilename = PConcatFilename(pLocalDir, pFilename)))
goto exit_deletefile;
DeleteFile(pLocalFilename);
exit_deletefile:
MemFree(pFilename);
MemFree(pLocalDir);
MemFree(pLocalFilename);
return;
}
#endif