1450 lines
33 KiB
C
1450 lines
33 KiB
C
/*++
|
||
|
||
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
|
||
|
||
|
||
|