/*++ 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