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.
7764 lines
250 KiB
7764 lines
250 KiB
/*++
|
|
|
|
Copyright (c) 1990 - 1996 Microsoft Corporation
|
|
All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
init.c
|
|
|
|
Abstract:
|
|
|
|
This module has all the initialization functions for the Local Print Provider
|
|
|
|
Author:
|
|
|
|
Dave Snipp (DaveSn) 15-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
Felix Maxa (amaxa) 18-Jun-2000
|
|
Modified SplCreateSpooler to special case cluster pIniSpooler
|
|
Modified LoadPrintProcessor to be able to copy the print processor
|
|
from the cluster disk
|
|
Added SplCreateSpoolerWorkerThread
|
|
ClusterAddDriversFromClusterDisk
|
|
ClusterAddVersionDrivers
|
|
ClusterAddOrUpdateDriverFromClusterDisk, all part of the DCR
|
|
regarding installing rpinter drivers on clusters
|
|
|
|
Adina Trufinescu (adinatru) 07-December 1998
|
|
Commented InitializePrintMonitor2 ;
|
|
Changed back to the old interface - InitializePrintMonitor - which is defined in localmon.c
|
|
|
|
|
|
Khaled Sedky (khaleds) 1-September 1998
|
|
Modified InitializePrintProcessor amd added LoadPrintProcessor
|
|
as a result of merging winprint and localspl
|
|
|
|
Steve Wilson (swilson) 1-November 1996
|
|
Added ShadowFile2 so spooler can delete crashing shadowfiles.
|
|
|
|
Muhunthan Sivapragasam (MuhuntS) 1-June-1995
|
|
Driver info 3 changes; Changes to use RegGetString, RegGetDword etc
|
|
|
|
Matthew A Felton (MattFe) 27-June-1994
|
|
pIniSpooler - allow other providers to call the spooler functions in LocalSpl
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#include <lm.h>
|
|
#include <winbasep.h>
|
|
#include <faxreg.h>
|
|
#include "clusspl.h"
|
|
#include "jobid.h"
|
|
#include "filepool.hxx"
|
|
|
|
MODULE_DEBUG_INIT( DBG_ERROR , DBG_ERROR );
|
|
|
|
UINT gcClusterIniSpooler = 0;
|
|
#if DBG
|
|
HANDLE ghbtClusterRef = 0;
|
|
#endif
|
|
|
|
MONITORREG gMonitorReg = {
|
|
sizeof( MONITORREG ),
|
|
&SplRegCreateKey,
|
|
&SplRegOpenKey,
|
|
&SplRegCloseKey,
|
|
&SplRegDeleteKey,
|
|
&SplRegEnumKey,
|
|
&SplRegQueryInfoKey,
|
|
&SplRegSetValue,
|
|
&SplRegDeleteValue,
|
|
&SplRegEnumValue,
|
|
&SplRegQueryValue
|
|
};
|
|
|
|
VOID
|
|
SplCreateSpoolerWorkerThread(
|
|
PVOID pv
|
|
);
|
|
|
|
DWORD
|
|
ClusterAddOrUpdateDriverFromClusterDisk(
|
|
HKEY hVersionKey,
|
|
LPCWSTR pszDriverName,
|
|
LPCWSTR pszEnvName,
|
|
LPCWSTR pszEnvDir,
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
BOOL
|
|
Old2NewShadow(
|
|
PSHADOWFILE pShadowFile1,
|
|
PSHADOWFILE_3 pShadowFile2,
|
|
DWORD *pnBytes
|
|
);
|
|
|
|
VOID
|
|
FreeIniVersion(
|
|
PINIVERSION pIniVersion
|
|
);
|
|
|
|
BOOL
|
|
NotIniSpooler(
|
|
BYTE *pMem
|
|
);
|
|
|
|
PINIDRIVER
|
|
GetDriverList(
|
|
HKEY hVersionKey,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion
|
|
);
|
|
|
|
PINIVERSION
|
|
GetVersionDrivers(
|
|
HKEY hDriversKey,
|
|
LPWSTR VersionName,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment
|
|
);
|
|
|
|
VOID
|
|
GetPrintSystemVersion(
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
VOID
|
|
InitializeSpoolerSettings(
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
VOID
|
|
WaitForSpoolerInitialization(
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
ValidateProductSuite(
|
|
PWSTR SuiteName
|
|
);
|
|
|
|
LPWSTR
|
|
FormatRegistryKeyForPrinter(
|
|
LPWSTR pSource, /* The string from which backslashes are to be added. */
|
|
LPWSTR pScratch /* Scratch buffer for the function to write in; */
|
|
); /* must be at least as long as pSource. */
|
|
|
|
#define MAX_LENGTH_DRIVERS_SHARE_REMARK 256
|
|
|
|
WCHAR *szSpoolDirectory = L"\\spool";
|
|
WCHAR *szPrintShareName = L""; /* No share for printers in product1 */
|
|
WCHAR *szPrintDirectory = L"\\printers";
|
|
WCHAR *szDriversDirectory = L"\\drivers";
|
|
WCHAR *gszNT4EMF = L"NT EMF 1.003";
|
|
WCHAR *gszNT5EMF = L"NT EMF 1.008";
|
|
|
|
|
|
SHARE_INFO_2 DriversShareInfo={NULL, /* Netname - initialized below */
|
|
STYPE_DISKTREE, /* Type of share */
|
|
NULL, /* Remark */
|
|
0, /* Default permissions */
|
|
SHI_USES_UNLIMITED, /* No users limit */
|
|
SHI_USES_UNLIMITED, /* Current uses (??) */
|
|
NULL, /* Path - initialized below */
|
|
NULL}; /* No password */
|
|
|
|
|
|
// WARNING
|
|
// Do not access these directly always go via pIniSpooler->pszRegistr...
|
|
// This will then work for multiple pIniSpoolers
|
|
|
|
PWCHAR ipszRoot = L"Print";
|
|
PWCHAR ipszRegistryRoot = L"System\\CurrentControlSet\\Control\\Print";
|
|
PWCHAR ipszRegistryPrinters = L"System\\CurrentControlSet\\Control\\Print\\Printers";
|
|
PWCHAR ipszRegSwPrinters = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers";
|
|
PWCHAR ipszRegistryMonitors = L"Monitors";
|
|
PWCHAR ipszRegistryMonitorsHKLM = L"\\System\\CurrentControlSet\\Control\\Print\\Monitors";
|
|
PWCHAR ipszRegistryEnvironments = L"System\\CurrentControlSet\\Control\\Print\\Environments";
|
|
PWCHAR ipszClusterDatabaseEnvironments = L"Environments";
|
|
PWCHAR ipszRegistryEventLog = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\Print";
|
|
PWCHAR ipszRegistryProviders = L"Providers";
|
|
PWCHAR ipszEventLogMsgFile = L"%SystemRoot%\\System32\\LocalSpl.dll";
|
|
PWCHAR ipszDriversShareName = L"print$";
|
|
PWCHAR ipszRegistryForms = L"System\\CurrentControlSet\\Control\\Print\\Forms";
|
|
PWCHAR ipszRegistryProductOptions = L"System\\CurrentControlSet\\Control\\ProductOptions";
|
|
PWCHAR ipszRegistryWin32Root = L"System\\CurrentControlSet\\Control\\Print\\Providers\\LanMan Print Services\\Servers";
|
|
PWCHAR ipszRegistryClusRepository = SPLREG_CLUSTER_LOCAL_ROOT_KEY;
|
|
|
|
WCHAR *szPrinterData = L"PrinterDriverData";
|
|
WCHAR *szConfigurationKey = L"Configuration File";
|
|
WCHAR *szDataFileKey = L"Data File";
|
|
WCHAR *szDriverVersion = L"Version";
|
|
WCHAR *szTempDir = L"TempDir";
|
|
WCHAR *szDriverAttributes = L"DriverAttributes";
|
|
WCHAR *szDriversKey = L"Drivers";
|
|
WCHAR *szPrintProcKey = L"Print Processors";
|
|
WCHAR *szPrintersKey = L"Printers";
|
|
WCHAR *szEnvironmentsKey = L"Environments";
|
|
WCHAR *szDirectory = L"Directory";
|
|
WCHAR *szDriverIni = L"Drivers.ini";
|
|
WCHAR *szDriverFile = L"Driver";
|
|
WCHAR *szDriverDir = L"DRIVERS";
|
|
WCHAR *szPrintProcDir = L"PRTPROCS";
|
|
WCHAR *szPrinterDir = L"PRINTERS";
|
|
WCHAR *szClusterPrinterDir= L"Spool";
|
|
WCHAR *szPrinterIni = L"\\printer.ini";
|
|
WCHAR *szAllSpools = L"\\*.SPL";
|
|
WCHAR *szNullPort = L"NULL";
|
|
WCHAR *szComma = L",";
|
|
WCHAR *szName = L"Name";
|
|
WCHAR *szShare = L"Share Name";
|
|
WCHAR *szPort = L"Port";
|
|
WCHAR *szPrintProcessor = L"Print Processor";
|
|
WCHAR *szDatatype = L"Datatype";
|
|
WCHAR *szDriver = L"Printer Driver";
|
|
WCHAR *szLocation = L"Location";
|
|
WCHAR *szDescription = L"Description";
|
|
WCHAR *szAttributes = L"Attributes";
|
|
WCHAR *szStatus = L"Status";
|
|
WCHAR *szPriority = L"Priority";
|
|
WCHAR *szDefaultPriority = L"Default Priority";
|
|
WCHAR *szUntilTime = L"UntilTime";
|
|
WCHAR *szStartTime = L"StartTime";
|
|
WCHAR *szParameters = L"Parameters";
|
|
WCHAR *szSepFile = L"Separator File";
|
|
WCHAR *szDevMode = L"Default DevMode";
|
|
WCHAR *szSecurity = L"Security";
|
|
WCHAR *szSpoolDir = L"SpoolDirectory";
|
|
WCHAR *szNetMsgDll = L"NETMSG.DLL";
|
|
WCHAR *szMajorVersion = L"MajorVersion";
|
|
WCHAR *szMinorVersion = L"MinorVersion";
|
|
WCHAR *szTimeLastChange = L"ChangeID";
|
|
WCHAR *szHelpFile = L"Help File";
|
|
WCHAR *szMonitor = L"Monitor";
|
|
WCHAR *szDependentFiles = L"Dependent Files";
|
|
WCHAR *szPreviousNames = L"Previous Names";
|
|
WCHAR *szDNSTimeout = L"dnsTimeout";
|
|
WCHAR *szTXTimeout = L"txTimeout";
|
|
WCHAR *szNTFaxDriver = FAX_DRIVER_NAME;
|
|
WCHAR *szObjectGUID = L"ObjectGUID";
|
|
WCHAR *szDsKeyUpdate = L"DsKeyUpdate";
|
|
WCHAR *szDsKeyUpdateForeground = L"DsKeyUpdateForeground";
|
|
WCHAR *szAction = L"Action";
|
|
WCHAR *szMfgName = L"Manufacturer";
|
|
WCHAR *szOEMUrl = L"OEM URL";
|
|
WCHAR *szHardwareID = L"HardwareID";
|
|
WCHAR *szProvider = L"Provider";
|
|
WCHAR *szDriverDate = L"DriverDate";
|
|
WCHAR *szLongVersion = L"DriverVersion";
|
|
WCHAR *szClusDrvTimeStamp = L"TimeStamp";
|
|
const WCHAR szDiscardTSJobs[] = L"DiscardTSJobs";
|
|
const WCHAR szDisableFilePooling[] = L"DisableServerFilePooling";
|
|
|
|
WCHAR *szRegistryRoot = L"System\\CurrentControlSet\\Control\\Print";
|
|
WCHAR *szEMFThrottle = L"EMFThrottle";
|
|
WCHAR *szFlushShadowFileBuffers = L"FlushShadowFileBuffers";
|
|
WCHAR *szPendingUpgrades = L"PendingUpgrades";
|
|
|
|
WCHAR *szPrintPublishPolicy = L"Software\\Policies\\Microsoft\\Windows NT\\Printers";
|
|
|
|
WCHAR *szClusterDriverRoot = L"PrinterDrivers";
|
|
WCHAR *szClusterNonAwareMonitors = L"OtherMonitors";
|
|
|
|
#if DBG
|
|
WCHAR *szDebugFlags = L"DebugFlags";
|
|
#endif
|
|
|
|
WCHAR *szEnvironment = LOCAL_ENVIRONMENT;
|
|
WCHAR *szWin95Environment = L"Windows 4.0";
|
|
const WCHAR gszCacheMasqPrinters[] = L"CacheMasqPrinters";
|
|
|
|
HANDLE hInst;
|
|
|
|
// Time before a job is assumed abandond and deleted during FastPrint
|
|
// operation
|
|
DWORD dwFastPrintWaitTimeout = FASTPRINT_WAIT_TIMEOUT;
|
|
DWORD dwSpoolerPriority = THREAD_PRIORITY_NORMAL;
|
|
DWORD dwPortThreadPriority = DEFAULT_PORT_THREAD_PRIORITY;
|
|
DWORD dwSchedulerThreadPriority = DEFAULT_SCHEDULER_THREAD_PRIORITY;
|
|
DWORD dwFastPrintThrottleTimeout = FASTPRINT_THROTTLE_TIMEOUT;
|
|
DWORD dwFastPrintSlowDownThreshold = FASTPRINT_SLOWDOWN_THRESHOLD;
|
|
DWORD dwServerThreadPriority = DEFAULT_SERVER_THREAD_PRIORITY;
|
|
DWORD dwEnableBroadcastSpoolerStatus = 0;
|
|
|
|
// NT 3.1 No Version ( Version 0 ) User Mode
|
|
// NT 3.5 and 3.51 Version 1 User Mode
|
|
// NT 4.0 Version 2 Kernel Mode
|
|
|
|
DWORD dwMajorVersion = SPOOLER_VERSION;
|
|
DWORD dwMinorVersion = 0;
|
|
|
|
// Unique Printer ID counter which increases monotonically. Wraps at 4G.
|
|
DWORD dwUniquePrinterSessionID = 0;
|
|
|
|
// Globals for EMF job scheduling
|
|
|
|
DWORD dwNumberOfEMFJobsRendering = 0;
|
|
BOOL bUseEMFScheduling = FALSE;
|
|
SIZE_T TotalMemoryForRendering = 0;
|
|
SIZE_T AvailMemoryForRendering = 0;
|
|
DWORD dwLastScheduleTime = 0;
|
|
|
|
PJOBDATA pWaitingList = NULL;
|
|
PJOBDATA pScheduleList = NULL;
|
|
|
|
DWORD dwFlushShadowFileBuffers = 0; // default for uninitialized
|
|
|
|
|
|
// Time to sleep if the LocalWritePrinter WritePort doesn't write any bytes
|
|
// but still returns success.
|
|
DWORD dwWritePrinterSleepTime = WRITE_PRINTER_SLEEP_TIME;
|
|
|
|
BOOL gbRemoteFax = TRUE;
|
|
|
|
BOOL Initialized = FALSE;
|
|
|
|
PINISPOOLER pLocalIniSpooler = NULL;
|
|
PINIENVIRONMENT pThisEnvironment = NULL;
|
|
|
|
#define POOL_TIMEOUT 120000 // 2 minutes
|
|
#define MAX_POOL_FILES 50
|
|
|
|
//
|
|
// Allowing remote connection policy.
|
|
//
|
|
ERemoteRPCEndPointPolicy gRemoteRPCEndPointPolicy;
|
|
|
|
//
|
|
// Global for KM Printers Blocking Policy
|
|
// by default it is
|
|
// 1 "blocked" for Server and
|
|
// 0 "not blocked" for Workstation
|
|
//
|
|
DWORD DefaultKMPrintersAreBlocked;
|
|
|
|
//
|
|
// Read from the registry if the HKLM\...\Print\ServerInstallTimeOut DWORD entry exists
|
|
// Otherwise default 5 mins.
|
|
//
|
|
DWORD gdwServerInstallTimeOut;
|
|
|
|
|
|
//
|
|
// 0 - Not upgrading, 1 - performing upgrade
|
|
//
|
|
|
|
DWORD dwUpgradeFlag = 0;
|
|
|
|
LPWSTR szRemoteDoc;
|
|
LPWSTR szLocalDoc;
|
|
LPWSTR szFastPrintTimeout;
|
|
LPWSTR szRaw = L"RAW";
|
|
|
|
|
|
PRINTPROVIDOR PrintProvidor = {LocalOpenPrinter,
|
|
LocalSetJob,
|
|
LocalGetJob,
|
|
LocalEnumJobs,
|
|
LocalAddPrinter,
|
|
SplDeletePrinter,
|
|
SplSetPrinter,
|
|
SplGetPrinter,
|
|
LocalEnumPrinters,
|
|
LocalAddPrinterDriver,
|
|
LocalEnumPrinterDrivers,
|
|
SplGetPrinterDriver,
|
|
LocalGetPrinterDriverDirectory,
|
|
LocalDeletePrinterDriver,
|
|
LocalAddPrintProcessor,
|
|
LocalEnumPrintProcessors,
|
|
LocalGetPrintProcessorDirectory,
|
|
LocalDeletePrintProcessor,
|
|
LocalEnumPrintProcessorDatatypes,
|
|
LocalStartDocPrinter,
|
|
LocalStartPagePrinter,
|
|
LocalWritePrinter,
|
|
LocalEndPagePrinter,
|
|
LocalAbortPrinter,
|
|
LocalReadPrinter,
|
|
LocalEndDocPrinter,
|
|
LocalAddJob,
|
|
LocalScheduleJob,
|
|
SplGetPrinterData,
|
|
SplSetPrinterData,
|
|
LocalWaitForPrinterChange,
|
|
SplClosePrinter,
|
|
SplAddForm,
|
|
SplDeleteForm,
|
|
SplGetForm,
|
|
SplSetForm,
|
|
SplEnumForms,
|
|
LocalEnumMonitors,
|
|
LocalEnumPorts,
|
|
LocalAddPort,
|
|
LocalConfigurePort,
|
|
LocalDeletePort,
|
|
LocalCreatePrinterIC,
|
|
LocalPlayGdiScriptOnPrinterIC,
|
|
LocalDeletePrinterIC,
|
|
LocalAddPrinterConnection,
|
|
LocalDeletePrinterConnection,
|
|
LocalPrinterMessageBox,
|
|
LocalAddMonitor,
|
|
LocalDeleteMonitor,
|
|
SplResetPrinter,
|
|
SplGetPrinterDriverEx,
|
|
LocalFindFirstPrinterChangeNotification,
|
|
LocalFindClosePrinterChangeNotification,
|
|
LocalAddPortEx,
|
|
NULL,
|
|
LocalRefreshPrinterChangeNotification,
|
|
LocalOpenPrinterEx,
|
|
LocalAddPrinterEx,
|
|
LocalSetPort,
|
|
SplEnumPrinterData,
|
|
SplDeletePrinterData,
|
|
SplClusterSplOpen,
|
|
SplClusterSplClose,
|
|
SplClusterSplIsAlive,
|
|
SplSetPrinterDataEx,
|
|
SplGetPrinterDataEx,
|
|
SplEnumPrinterDataEx,
|
|
SplEnumPrinterKey,
|
|
SplDeletePrinterDataEx,
|
|
SplDeletePrinterKey,
|
|
LocalSeekPrinter,
|
|
LocalDeletePrinterDriverEx,
|
|
LocalAddPerMachineConnection,
|
|
LocalDeletePerMachineConnection,
|
|
LocalEnumPerMachineConnections,
|
|
LocalXcvData,
|
|
LocalAddPrinterDriverEx,
|
|
SplReadPrinter,
|
|
LocalDriverUnloadComplete,
|
|
LocalGetSpoolFileHandle,
|
|
LocalCommitSpoolData,
|
|
LocalCloseSpoolFileHandle,
|
|
LocalFlushPrinter,
|
|
LocalSendRecvBidiData,
|
|
LocalAddDriverCatalog,
|
|
};
|
|
|
|
DWORD
|
|
FinalInitAfterRouterInitCompleteThread(
|
|
DWORD dwUpgrade
|
|
);
|
|
|
|
#if DBG
|
|
VOID
|
|
InitializeDebug(
|
|
PINISPOOLER pIniSpooler
|
|
);
|
|
|
|
PDBG_POINTERS
|
|
DbgSplGetPointers(
|
|
VOID
|
|
);
|
|
#endif
|
|
|
|
BOOL
|
|
DllMain(
|
|
HANDLE hModule,
|
|
DWORD dwReason,
|
|
LPVOID lpRes
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
|
|
switch(dwReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
DisableThreadLibraryCalls(hModule);
|
|
hInst = hModule;
|
|
|
|
bRet = InitializeLocalspl();
|
|
|
|
if (bRet)
|
|
{
|
|
bRet = LocalMonInit(hInst);
|
|
}
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH :
|
|
ShutdownPorts( pLocalIniSpooler );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return bRet;
|
|
|
|
UNREFERENCED_PARAMETER( lpRes );
|
|
}
|
|
|
|
|
|
BOOL
|
|
InitializeLocalspl(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
#if DBG
|
|
gpDbgPointers = DbgGetPointers();
|
|
|
|
if( gpDbgPointers ){
|
|
|
|
hcsSpoolerSection = gpDbgPointers->pfnAllocCritSec();
|
|
SPLASSERT( hcsSpoolerSection );
|
|
|
|
ghbtClusterRef = gpDbgPointers->pfnAllocBackTrace();
|
|
}
|
|
|
|
if( !hcsSpoolerSection ){
|
|
|
|
//
|
|
// Must be using the free version of spoolss.dll.
|
|
//
|
|
bRet = InitializeCriticalSectionAndSpinCount(&SpoolerSection, 0x80000000);
|
|
}
|
|
#else
|
|
bRet = InitializeCriticalSectionAndSpinCount(&SpoolerSection, 0x80000000);
|
|
#endif
|
|
|
|
return bRet;
|
|
}
|
|
|
|
VOID
|
|
SplDeleteSpoolerThread(
|
|
PVOID pv
|
|
)
|
|
{
|
|
PINIPORT pIniPort;
|
|
PINIPORT pIniPortNext;
|
|
|
|
PINIMONITOR pIniMonitor;
|
|
PINIMONITOR pIniMonitorNext;
|
|
PSHARE_INFO_2 pShareInfo;
|
|
|
|
PINISPOOLER pIniSpooler = (PINISPOOLER)pv;
|
|
|
|
//
|
|
// Remove the cache entry associated with this pIniSpooler. At this time the pIniSpooler
|
|
// is in pending deletion, so nobody is using it anymore.
|
|
//
|
|
CacheDeleteNode(pIniSpooler->pMachineName + 2);
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// Cleanup the port monitors.
|
|
//
|
|
ShutdownMonitors( pIniSpooler );
|
|
|
|
//
|
|
// Close Cluster Access Token
|
|
//
|
|
if (pIniSpooler->hClusterToken != INVALID_HANDLE_VALUE)
|
|
NtClose(pIniSpooler->hClusterToken);
|
|
|
|
|
|
//
|
|
// Delete All the Strings
|
|
//
|
|
|
|
FreeStructurePointers((LPBYTE)pIniSpooler, NULL, IniSpoolerOffsets);
|
|
|
|
DeleteShared( pIniSpooler );
|
|
|
|
//
|
|
// Run all of the environments down if this isn't the local ini-spoolers
|
|
// environment. This frees up the memory for all of the drivers and also
|
|
// handles the driver ref-counts.
|
|
//
|
|
if (pIniSpooler->pIniEnvironment != pLocalIniSpooler->pIniEnvironment && pIniSpooler->pIniEnvironment) {
|
|
|
|
PINIENVIRONMENT pIniEnvironment = NULL;
|
|
PINIENVIRONMENT pNextEnvironment = NULL;
|
|
|
|
for(pIniEnvironment = pIniSpooler->pIniEnvironment; pIniEnvironment; pIniEnvironment = pNextEnvironment) {
|
|
|
|
pNextEnvironment = pIniEnvironment->pNext;
|
|
|
|
FreeIniEnvironment(pIniEnvironment);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete ports and monitors.
|
|
//
|
|
// Note that there is no reference counting here. By the time
|
|
// we get here all jobs and printers should be deleted (otherwise
|
|
// the pIniSpooler reference count would be != 0). Therefore,
|
|
// even though we don't refcount ports and monitors, we should
|
|
// be ok.
|
|
//
|
|
|
|
//
|
|
// Remove all ports.
|
|
//
|
|
for( pIniPort = pIniSpooler->pIniPort;
|
|
pIniPort;
|
|
pIniPort = pIniPortNext ){
|
|
|
|
pIniPortNext = pIniPort->pNext;
|
|
|
|
if( !DeletePortEntry( pIniPort )){
|
|
DBGMSG( DBG_ERROR,
|
|
( "Unable to delete port %ws %x %x %d",
|
|
pIniPort->pName,
|
|
GetMonitorHandle(pIniPort),
|
|
pIniPort->Status,
|
|
pIniPort->cJobs ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove all the monitors.
|
|
//
|
|
for( pIniMonitor = pIniSpooler->pIniMonitor;
|
|
pIniMonitor;
|
|
pIniMonitor = pIniMonitorNext ){
|
|
|
|
pIniMonitorNext = pIniMonitor->pNext;
|
|
|
|
if( !pIniMonitor->cRef ){
|
|
|
|
FreeIniMonitor( pIniMonitor );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close cluster resource key handle.
|
|
//
|
|
if( pIniSpooler->hckRoot ){
|
|
SplRegCloseKey( pIniSpooler->hckRoot, pIniSpooler );
|
|
}
|
|
|
|
//
|
|
// Close cluster resource key handle.
|
|
//
|
|
if( pIniSpooler->hckPrinters ){
|
|
SplRegCloseKey( pIniSpooler->hckPrinters, pIniSpooler );
|
|
}
|
|
|
|
//
|
|
// Keep a counter of cluster pIniSpoolers.
|
|
//
|
|
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
|
|
--gcClusterIniSpooler;
|
|
}
|
|
|
|
//
|
|
// Free the shared bitmap and shared driver info.
|
|
//
|
|
vDeleteJobIdMap( pIniSpooler->hJobIdMap );
|
|
|
|
pShareInfo = (PSHARE_INFO_2)pIniSpooler->pDriversShareInfo;
|
|
FreeSplStr( pShareInfo->shi2_remark );
|
|
FreeSplStr( pShareInfo->shi2_path );
|
|
|
|
FreeSplMem( pIniSpooler->pDriversShareInfo );
|
|
|
|
|
|
LeaveSplSem();
|
|
|
|
//
|
|
// Shut down the file pool for this ini-spooler. It should not delete any of
|
|
// the files.
|
|
//
|
|
if (pIniSpooler->hFilePool != INVALID_HANDLE_VALUE) {
|
|
(VOID)DestroyFilePool(pIniSpooler->hFilePool, FALSE);
|
|
}
|
|
|
|
// Free this IniSpooler
|
|
|
|
FreeSplMem( pIniSpooler );
|
|
|
|
DBGMSG( DBG_WARN, ( "SplDeleteSpooler: Refcount 0 %x\n", pIniSpooler ));
|
|
}
|
|
|
|
BOOL
|
|
SplDeleteSpooler(
|
|
HANDLE hSpooler
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler = (PINISPOOLER) hSpooler;
|
|
BOOL bReturn = FALSE;
|
|
PINISPOOLER pCurrentIniSpooler = pLocalIniSpooler;
|
|
|
|
HANDLE hThread;
|
|
DWORD ThreadId;
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// Whoever calls this must have deleted all the object associated with
|
|
// this spooler, ie all printers etc, just make certain
|
|
//
|
|
|
|
if( pIniSpooler != pLocalIniSpooler ){
|
|
|
|
//
|
|
// Mark us as pending deletion.
|
|
//
|
|
pIniSpooler->SpoolerFlags |= SPL_PENDING_DELETION;
|
|
|
|
DBGMSG(DBG_CLUSTER, ("SplDeleteSpooler: Deleting %x\n cRef %u\n", pIniSpooler, pIniSpooler->cRef ));
|
|
|
|
//
|
|
// pIniPrinters now acquire a reference to pIniSpooler.
|
|
//
|
|
if( pIniSpooler->cRef == 0 ){
|
|
|
|
SPLASSERT( pIniSpooler->pIniPrinter == NULL );
|
|
|
|
//( pIniSpooler->pIniPort == NULL ) &&
|
|
//( pIniSpooler->pIniForm == NULL ) &&
|
|
//( pIniSpooler->pIniMonitor == NULL ) &&
|
|
//( pIniSpooler->pIniNetPrint == NULL ) &&
|
|
//( pIniSpooler->pSpool == NULL ))
|
|
|
|
|
|
//
|
|
// Take this Spooler Off the Linked List if it's on it.
|
|
//
|
|
|
|
while (( pCurrentIniSpooler->pIniNextSpooler != NULL ) &&
|
|
( pCurrentIniSpooler->pIniNextSpooler != pIniSpooler )) {
|
|
|
|
pCurrentIniSpooler = pCurrentIniSpooler->pIniNextSpooler;
|
|
|
|
}
|
|
|
|
//
|
|
// May not be on the linked list if it was removed earlier by
|
|
// clustering.
|
|
//
|
|
if( pCurrentIniSpooler->pIniNextSpooler ){
|
|
|
|
SPLASSERT( pCurrentIniSpooler->pIniNextSpooler == pIniSpooler );
|
|
pCurrentIniSpooler->pIniNextSpooler = pIniSpooler->pIniNextSpooler;
|
|
}
|
|
|
|
//
|
|
// Hack for port monitors.
|
|
//
|
|
// Some monitors will call ClosePrinter, which deletes the very
|
|
// last printer and allows the pIniSpooler to be destroyed.
|
|
// Unfortunately, we call back to the monitors to close themselves
|
|
// in the same thread, which the monitor does not support.
|
|
//
|
|
// Create a new thread and shut everything down.
|
|
//
|
|
if (hThread = CreateThread( NULL, 0,
|
|
(LPTHREAD_START_ROUTINE)SplDeleteSpoolerThread,
|
|
(PVOID)pIniSpooler,
|
|
0,
|
|
&ThreadId ))
|
|
{
|
|
CloseHandle(hThread);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Bug 54840
|
|
//
|
|
// What do we do if we can't create a thread to shut down?
|
|
// Sleep and retry?
|
|
//
|
|
DBGMSG(DBG_ERROR, ("Unable to create SplDeleteSpoolerThread\n"));
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
SplCloseSpooler(
|
|
HANDLE hSpooler
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler = (PINISPOOLER) hSpooler;
|
|
|
|
EnterSplSem();
|
|
|
|
if ((pIniSpooler == NULL) ||
|
|
(pIniSpooler == INVALID_HANDLE_VALUE) ||
|
|
(pIniSpooler == pLocalIniSpooler) ||
|
|
(pIniSpooler->signature != ISP_SIGNATURE) ||
|
|
(pIniSpooler->cRef == 0)) {
|
|
|
|
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
|
|
DBGMSG(DBG_WARNING, ("SplCloseSpooler InvalidHandle %x\n", pIniSpooler ));
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("SplCloseSpooler %x %ws cRef %d\n",pIniSpooler,
|
|
pIniSpooler->pMachineName,
|
|
pIniSpooler->cRef-1));
|
|
|
|
DECSPOOLERREF( pIniSpooler );
|
|
|
|
LeaveSplSem();
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SplRegCopyTree(
|
|
HKEY hDest,
|
|
HKEY hSrc
|
|
)
|
|
|
|
/*++
|
|
Function Description: Recursives copies every key and value from under hSrc to hDest
|
|
|
|
Parameters: hDest - destination key
|
|
hSrc - source key
|
|
|
|
Return Value: TRUE if successful; FALSE otherwise
|
|
--*/
|
|
|
|
{
|
|
BOOL bStatus = FALSE;
|
|
DWORD dwError, dwIndex, cbValueName, cbData, cbKeyName, dwType;
|
|
DWORD cKeys, cMaxValueNameLen, cMaxValueLen, cMaxKeyNameLen, cValues;
|
|
|
|
LPBYTE lpValueName = NULL, lpData = NULL, lpKeyName = NULL;
|
|
HKEY hSrcSubKey = NULL, hDestSubKey = NULL;
|
|
|
|
//
|
|
// Get the max key name length and value name length and data size for
|
|
// allocating the buffers
|
|
//
|
|
if (dwError = RegQueryInfoKey( hSrc, NULL, NULL, NULL,
|
|
&cKeys, &cMaxKeyNameLen, NULL,
|
|
&cValues, &cMaxValueNameLen,
|
|
&cMaxValueLen, NULL, NULL ))
|
|
{
|
|
SetLastError(dwError);
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Adjust for the NULL char
|
|
//
|
|
++cMaxValueNameLen;
|
|
++cMaxKeyNameLen;
|
|
|
|
//
|
|
// Allocate the buffers
|
|
//
|
|
lpValueName = AllocSplMem( cMaxValueNameLen * sizeof(WCHAR) );
|
|
lpData = AllocSplMem( cMaxValueLen );
|
|
lpKeyName = AllocSplMem( cMaxKeyNameLen * sizeof(WCHAR) );
|
|
|
|
if (!lpValueName || !lpData || !lpKeyName)
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Copy all the values in the current key
|
|
//
|
|
for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
|
|
{
|
|
cbData = cMaxValueLen;
|
|
cbValueName = cMaxValueNameLen;
|
|
|
|
//
|
|
// Retrieve the value name and the data
|
|
//
|
|
dwError = RegEnumValue( hSrc, dwIndex, (LPWSTR) lpValueName, &cbValueName,
|
|
NULL, &dwType, lpData, &cbData );
|
|
|
|
if (dwError)
|
|
{
|
|
SetLastError( dwError );
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Set the value in the destination
|
|
//
|
|
dwError = RegSetValueEx( hDest, (LPWSTR) lpValueName, 0, dwType,
|
|
lpData, cbData );
|
|
|
|
if (dwError)
|
|
{
|
|
SetLastError( dwError );
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Recursively copies all the subkeys
|
|
//
|
|
for (dwIndex = 0; dwIndex < cKeys; ++dwIndex)
|
|
{
|
|
cbKeyName = cMaxKeyNameLen;
|
|
|
|
//
|
|
// Retrieve the key name
|
|
//
|
|
dwError = RegEnumKeyEx( hSrc, dwIndex, (LPWSTR) lpKeyName, &cbKeyName,
|
|
NULL, NULL, NULL, NULL );
|
|
|
|
if (dwError)
|
|
{
|
|
SetLastError( dwError );
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Open the source subkey
|
|
//
|
|
if (dwError = RegOpenKeyEx( hSrc, (LPWSTR) lpKeyName, 0,
|
|
KEY_READ, &hSrcSubKey ))
|
|
{
|
|
SetLastError( dwError );
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Create the destination subkey
|
|
//
|
|
if (dwError = RegCreateKeyEx( hDest, (LPWSTR) lpKeyName, 0, NULL,
|
|
REG_OPTION_VOLATILE, KEY_READ | KEY_WRITE | DELETE,
|
|
NULL, &hDestSubKey, NULL ))
|
|
{
|
|
SetLastError( dwError );
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Copy the subkey tree
|
|
//
|
|
if (!SplRegCopyTree( hDestSubKey, hSrcSubKey ))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Close the registry handle
|
|
//
|
|
RegCloseKey( hDestSubKey );
|
|
RegCloseKey( hSrcSubKey );
|
|
|
|
hDestSubKey = NULL;
|
|
hSrcSubKey = NULL;
|
|
}
|
|
|
|
bStatus = TRUE;
|
|
|
|
CleanUp:
|
|
|
|
//
|
|
// Free allocated resources
|
|
//
|
|
if (lpValueName)
|
|
{
|
|
FreeSplMem( lpValueName );
|
|
}
|
|
if (lpData)
|
|
{
|
|
FreeSplMem( lpData );
|
|
}
|
|
if (lpKeyName)
|
|
{
|
|
FreeSplMem( lpKeyName );
|
|
}
|
|
|
|
//
|
|
// Close registry handles
|
|
//
|
|
if (hDestSubKey)
|
|
{
|
|
RegCloseKey( hDestSubKey );
|
|
}
|
|
if (hSrcSubKey)
|
|
{
|
|
RegCloseKey( hSrcSubKey );
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
VOID
|
|
MigratePrinterData()
|
|
|
|
/*++
|
|
Function Description: When the spooler starts up for the first time after upgrade,
|
|
the printer data is moved from HKLM\Software to HKLM\System
|
|
|
|
Parameters: None
|
|
|
|
Return Values: None
|
|
--*/
|
|
|
|
{
|
|
HKEY hSysPrinters = NULL, hSwPrinters = NULL;
|
|
|
|
//
|
|
// Migrate the data only immediately following upgrade
|
|
//
|
|
if (!dwUpgradeFlag)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Open the source and destination keys for the migration
|
|
//
|
|
if (( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
ipszRegSwPrinters,
|
|
0,
|
|
KEY_WRITE | KEY_READ | DELETE,
|
|
&hSwPrinters ) == ERROR_SUCCESS) &&
|
|
|
|
( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
ipszRegistryPrinters,
|
|
0,
|
|
KEY_WRITE | KEY_READ | DELETE,
|
|
&hSysPrinters ) == ERROR_SUCCESS) )
|
|
{
|
|
//
|
|
// Recursively copy the keys and the values from Software to System
|
|
//
|
|
SplRegCopyTree( hSysPrinters, hSwPrinters );
|
|
}
|
|
|
|
//
|
|
// Close the registry handles
|
|
//
|
|
if (hSwPrinters)
|
|
{
|
|
RegCloseKey( hSwPrinters );
|
|
}
|
|
if (hSysPrinters)
|
|
{
|
|
RegCloseKey( hSysPrinters );
|
|
}
|
|
|
|
//
|
|
// Delete the printers key from the software since it is no longer
|
|
// accessed by the spooler
|
|
//
|
|
RegDeleteKey( HKEY_LOCAL_MACHINE, ipszRegSwPrinters );
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
IsCCSetLinkedtoSoftwareHive (
|
|
PBOOL pbIsLinked
|
|
)
|
|
/*++
|
|
Function Description:
|
|
Checks to see if it is a link between SYSTEM hive and SOFTWARE hive
|
|
Only Nt Apis manage to do this.
|
|
Parameters:
|
|
OUT pbIsLinked - TRUE if there is a symbolic link between SYSTEM hive and SOFTWARE hive
|
|
Return Values:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING KeyName;
|
|
HANDLE KeyHandle;
|
|
|
|
*pbIsLinked = FALSE;
|
|
|
|
RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Print\\Printers");
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENLINK,
|
|
(HANDLE)NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Open CurrentControlSet\\Control\\Print\\Printers key
|
|
//
|
|
Status = NtOpenKey( (PHANDLE)(&KeyHandle),
|
|
MAXIMUM_ALLOWED,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
ULONG len;
|
|
UCHAR ValueBuffer[MAX_PATH];
|
|
UNICODE_STRING ValueName;
|
|
PKEY_VALUE_FULL_INFORMATION keyInfo;
|
|
|
|
RtlInitUnicodeString(&ValueName, L"SymbolicLinkValue");
|
|
|
|
//
|
|
// Query CurrentControlSet\\Control\\Print\\Printers for SymbolicLinkValue
|
|
//
|
|
Status = NtQueryValueKey(KeyHandle,
|
|
&ValueName,
|
|
KeyValueFullInformation,
|
|
ValueBuffer,
|
|
sizeof (ValueBuffer),
|
|
&len
|
|
);
|
|
if( NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// It's not enough that the value exists, it should be a REG_LINK value
|
|
//
|
|
keyInfo = ( PKEY_VALUE_FULL_INFORMATION ) ValueBuffer;
|
|
*pbIsLinked = ( keyInfo->Type == REG_LINK );
|
|
|
|
}
|
|
|
|
NtClose(KeyHandle);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
LinkControlSet (
|
|
LPCTSTR pszRegistryPrinters
|
|
)
|
|
/*++
|
|
Function Description:
|
|
Create a symbolic volatile link from SYSTEM hive to SOFTWARE hive
|
|
Parameters:
|
|
|
|
Return Values: ERROR_SUCCESS if succeeded
|
|
--*/
|
|
{
|
|
HKEY hCCSKey;
|
|
DWORD dwRet;
|
|
BOOL bIsLinked = FALSE;
|
|
PWCHAR pszRegistryPrintersFullLink = L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers";
|
|
|
|
dwRet = IsCCSetLinkedtoSoftwareHive(&bIsLinked);
|
|
|
|
//
|
|
// IsCCSetLinkedtoSoftwareHive returns NTSTATUS
|
|
// If the link is not there , IsCCSetLinkedtoSoftwareHive fails with STATUS_OBJECT_NAME_NOT_FOUND
|
|
// That's not an error.
|
|
//
|
|
if( NT_SUCCESS(dwRet) || dwRet == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
|
|
if (bIsLinked) {
|
|
|
|
dwRet = ERROR_SUCCESS;
|
|
|
|
}else{
|
|
|
|
dwRet = SplDeleteThisKey( HKEY_LOCAL_MACHINE,
|
|
NULL,
|
|
(LPWSTR)pszRegistryPrinters,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if( dwRet == ERROR_SUCCESS || dwRet == ERROR_FILE_NOT_FOUND) {
|
|
|
|
dwRet = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
|
|
pszRegistryPrinters,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE|REG_OPTION_CREATE_LINK,
|
|
KEY_WRITE | KEY_CREATE_LINK,
|
|
NULL,
|
|
&hCCSKey,
|
|
NULL);
|
|
|
|
if( dwRet == ERROR_SUCCESS )
|
|
{
|
|
dwRet = RegSetValueEx( hCCSKey,
|
|
_T("SymbolicLinkValue"),
|
|
0,
|
|
REG_LINK,
|
|
(CONST BYTE *)pszRegistryPrintersFullLink,
|
|
(_tcsclen(pszRegistryPrintersFullLink) * sizeof(WCHAR)));
|
|
|
|
|
|
RegCloseKey(hCCSKey);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
DWORD
|
|
BackupPrintersToSystemHive(
|
|
LPWSTR pszSwRegistryPrinters
|
|
)
|
|
/*++
|
|
Function Description:
|
|
Because the print registry data location was moved to SOFTWARE hive, we need to create
|
|
a symbolic registry link between the new location and the old one in SYSTEM hive.
|
|
We are doing this for applications that read directly from registry print data and
|
|
rely on the old location.
|
|
|
|
Parameters:
|
|
pszSwRegistryPrinters - the new printer data location under SOFTWARE hive
|
|
|
|
Return Values:
|
|
FALSE if the printer keys are not in SOFTWARE hive
|
|
Since this fuction's failure might stop spooler working,
|
|
Control set's cleanup and link failures are not considered fatal.
|
|
Only apps that access printer data directly will fail.
|
|
--*/
|
|
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwRet;
|
|
HKEY hSwPrinters = NULL;
|
|
|
|
//
|
|
// Check the existence of pszSwRegistryPrinters
|
|
//
|
|
dwRet = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
|
|
pszSwRegistryPrinters,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_WRITE | KEY_CREATE_LINK,
|
|
NULL,
|
|
&hSwPrinters,
|
|
NULL);
|
|
|
|
if ( dwRet != ERROR_SUCCESS ) {
|
|
goto End;
|
|
}
|
|
//
|
|
// Create a volatile link between current location in SOFTWARE hive and the old one in SYSTEM hive
|
|
// Because it is volatile, this link must be created after each reboot (every time when spooler starts)
|
|
// A failure at this level is not fatal since spooler doesn't rely on SYSTEM hive location anymore
|
|
//
|
|
dwRet = LinkControlSet(ipszRegistryPrinters);
|
|
|
|
End:
|
|
|
|
if ( hSwPrinters ){
|
|
|
|
RegCloseKey( hSwPrinters );
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CleanupDeletedPrinters (
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the printers in pending deletion for real if they have no more jobs and
|
|
if they are not referenced anymore
|
|
|
|
Arguments:
|
|
|
|
pIniSpooler - not null
|
|
|
|
Return Value:
|
|
|
|
BOOL - ignored
|
|
|
|
--*/
|
|
|
|
{
|
|
PINIPRINTER pIniPrinter;
|
|
BOOL bRet = FALSE;
|
|
|
|
if(pIniSpooler) {
|
|
|
|
pIniPrinter = pIniSpooler->pIniPrinter;
|
|
|
|
while (pIniPrinter) {
|
|
|
|
if (pIniPrinter->Status & PRINTER_PENDING_DELETION &&
|
|
!pIniPrinter->cJobs &&
|
|
!pIniPrinter->cRef ) {
|
|
|
|
DeletePrinterForReal(pIniPrinter, INIT_TIME);
|
|
|
|
// The link list will have changed underneath us
|
|
// DeletePrinterForReal leaves the Spooler CS
|
|
// Lets just loop through again from the beginning
|
|
|
|
pIniPrinter = pIniSpooler->pIniPrinter;
|
|
|
|
} else
|
|
|
|
pIniPrinter = pIniPrinter->pNext;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
SplCreateSpooler(
|
|
LPWSTR pMachineName,
|
|
DWORD Level,
|
|
PBYTE pSpooler,
|
|
LPBYTE pReserved
|
|
)
|
|
{
|
|
HANDLE hReturn = INVALID_HANDLE_VALUE;
|
|
PINISPOOLER pIniSpooler = NULL;
|
|
PSPOOLER_INFO_2 pSpoolerInfo2 = (PSPOOLER_INFO_2)pSpooler;
|
|
DWORD i;
|
|
WCHAR Buffer[MAX_PATH];
|
|
PSHARE_INFO_2 pShareInfo = NULL;
|
|
LONG Status;
|
|
HANDLE hToken = NULL;
|
|
DWORD dwRet;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if (!hToken)
|
|
{
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
// Validate Parameters
|
|
|
|
if ( pMachineName == NULL ) {
|
|
SetLastError( ERROR_INVALID_NAME );
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
if( Level == 1 &&
|
|
( pSpoolerInfo2->SpoolerFlags & SPL_CLUSTER_REG ||
|
|
!pSpoolerInfo2->pszRegistryRoot ||
|
|
!pSpoolerInfo2->pszRegistryPrinters )){
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
DBGMSG( DBG_TRACE, ("SplCreateSpooler %ws %d %x %x\n", pMachineName,
|
|
Level, pSpooler, pReserved ));
|
|
|
|
if( (pSpoolerInfo2->SpoolerFlags & (SPL_TYPE_LOCAL | SPL_PRINT)) &&
|
|
!(pSpoolerInfo2->SpoolerFlags & SPL_TYPE_CLUSTER) ){
|
|
|
|
if ( dwRet = (BackupPrintersToSystemHive( pSpoolerInfo2->pszRegistryPrinters )) != ERROR_SUCCESS ){
|
|
|
|
WCHAR szError[256];
|
|
|
|
StringCchPrintf(szError, COUNTOF(szError), L"%x", dwRet);
|
|
|
|
SplLogEvent(pIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_BACKUP_SPOOLER_REGISTRY,
|
|
TRUE,
|
|
szError,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
if (pLocalIniSpooler != NULL) {
|
|
|
|
pIniSpooler = FindSpooler( pMachineName, pSpoolerInfo2->SpoolerFlags );
|
|
|
|
if (pSpoolerInfo2->SpoolerFlags & SPL_OPEN_EXISTING_ONLY && !pIniSpooler) {
|
|
|
|
SetLastError( ERROR_FILE_NOT_FOUND );
|
|
goto SplCreateDone;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure we clear out a request to only open an existing inispooler.
|
|
// This is not a useful flag except for when we are searching for inispoolers.
|
|
//
|
|
pSpoolerInfo2->SpoolerFlags &= ~SPL_OPEN_EXISTING_ONLY;
|
|
|
|
if ( pIniSpooler == NULL ) {
|
|
|
|
pIniSpooler = AllocSplMem( sizeof(INISPOOLER) );
|
|
|
|
if (pIniSpooler == NULL ) {
|
|
DBGMSG( DBG_WARNING, ("Unable to allocate IniSpooler\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
pIniSpooler->signature = ISP_SIGNATURE;
|
|
INCSPOOLERREF( pIniSpooler );
|
|
|
|
pIniSpooler->hClusSplReady = NULL;
|
|
//
|
|
// Setup the job id map.
|
|
//
|
|
pIniSpooler->hJobIdMap = hCreateJobIdMap( 256 );
|
|
|
|
pIniSpooler->pMachineName = AllocSplStr( pMachineName );
|
|
|
|
if ( pIniSpooler->pMachineName == NULL ||
|
|
pIniSpooler->hJobIdMap == NULL ) {
|
|
|
|
DBGMSG( DBG_WARNING, ("Unable to allocate\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
//
|
|
// A cluster spooler owns its drivers, ports, pprocessors, etc. In order to manage those
|
|
// resources, the cluster spooler needs to have information about the driver letter of
|
|
// the cluster disk. Also the spooler needs to know its own cluster resource GUID
|
|
//
|
|
if( pSpoolerInfo2->SpoolerFlags & SPL_TYPE_CLUSTER )
|
|
{
|
|
pIniSpooler->pszClusResDriveLetter = AllocSplStr(pSpoolerInfo2->pszClusResDriveLetter);
|
|
pIniSpooler->pszClusResID = AllocSplStr(pSpoolerInfo2->pszClusResID);
|
|
|
|
if (!pIniSpooler->pszClusResDriveLetter || !pIniSpooler->pszClusResID)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
//
|
|
// When a node is upgraded, the resource dll writes a key in the registry. When the cluster spooler
|
|
// fails over for the first time on the node that was upgraded, then it will try to read that key
|
|
// in the registry. Then it will know if it has to do upgrade specific tasks, like upgrading its
|
|
// printer drivers.
|
|
//
|
|
Status = ClusterSplReadUpgradeKey(pIniSpooler->pszClusResID, &pIniSpooler->dwClusNodeUpgraded);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
SetLastError( Status );
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
DBGMSG(DBG_CLUSTER, ("SplCreateSpooler cluster ClusterUpgradeFlag %u\n", pIniSpooler->dwClusNodeUpgraded));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For a non cluster type spooler, these properties are meaningless.
|
|
//
|
|
pIniSpooler->pszClusResDriveLetter = NULL;
|
|
pIniSpooler->pszClusResID = NULL;
|
|
}
|
|
|
|
if (pSpoolerInfo2->pDir)
|
|
{
|
|
pIniSpooler->pDir = AllocSplStr( pSpoolerInfo2->pDir );
|
|
|
|
if (!pIniSpooler->pDir)
|
|
{
|
|
DBGMSG( DBG_WARNING, ("Unable to allocate pSpoolerInfo2-pDir\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
StringCchCopy(&Buffer[0], COUNTOF(Buffer), pIniSpooler->pDir);
|
|
}
|
|
else
|
|
{
|
|
i = GetSystemDirectory(Buffer, COUNTOF(Buffer));
|
|
|
|
StringCchCopy(&Buffer[i], COUNTOF(Buffer) - i, szSpoolDirectory);
|
|
|
|
if (pSpoolerInfo2->SpoolerFlags & SPL_TYPE_CLUSTER)
|
|
{
|
|
//
|
|
// For a cluster type spooler, the directory where it stores its driver files is of the form:
|
|
// pDir = C:\Windows\system32\spool\Drivers\spooler-resource-GUID
|
|
//
|
|
StrCatAlloc(&pIniSpooler->pDir,
|
|
Buffer,
|
|
szDriversDirectory,
|
|
L"\\",
|
|
pIniSpooler->pszClusResID,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For the local spooler, the directory where it stores its driver files is the following:
|
|
// pDir = C:\Windows\system32\spool\Drivers
|
|
//
|
|
StrCatAlloc(&pIniSpooler->pDir,
|
|
Buffer,
|
|
NULL);
|
|
}
|
|
|
|
if (!pIniSpooler->pDir)
|
|
{
|
|
DBGMSG( DBG_WARNING, ("Unable to Allocate pIniSpooler->pDir\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
}
|
|
|
|
//
|
|
// DriverShareInfo
|
|
//
|
|
pIniSpooler->pDriversShareInfo = AllocSplMem( sizeof( SHARE_INFO_2));
|
|
|
|
if ( pIniSpooler->pDriversShareInfo == NULL ) {
|
|
DBGMSG(DBG_WARNING, ("Unable to Alloc pIniSpooler->pDriversShareInfo\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
pShareInfo = (PSHARE_INFO_2)pIniSpooler->pDriversShareInfo;
|
|
|
|
if ( pIniSpooler->pDriversShareInfo == NULL )
|
|
goto SplCreateDone;
|
|
|
|
pShareInfo->shi2_netname = NULL;
|
|
pShareInfo->shi2_type = STYPE_DISKTREE;
|
|
pShareInfo->shi2_remark = NULL;
|
|
pShareInfo->shi2_permissions = 0;
|
|
pShareInfo->shi2_max_uses = SHI_USES_UNLIMITED;
|
|
pShareInfo->shi2_current_uses = SHI_USES_UNLIMITED;
|
|
pShareInfo->shi2_path = NULL;
|
|
pShareInfo->shi2_passwd = NULL;
|
|
|
|
//
|
|
// Find end of "<winnt>\system32\spool"
|
|
//
|
|
i = wcslen(Buffer);
|
|
|
|
//
|
|
// Make <winnt>\system32\spool\drivers
|
|
//
|
|
StringCchCopy(&Buffer[i], COUNTOF(Buffer) - i, szDriversDirectory);
|
|
|
|
pShareInfo->shi2_path = AllocSplStr(Buffer);
|
|
|
|
if ( pShareInfo->shi2_path == NULL ) {
|
|
DBGMSG( DBG_WARNING, ("Unable to alloc pShareInfo->shi2_path\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
pShareInfo->shi2_netname = ipszDriversShareName;
|
|
|
|
*Buffer = L'\0';
|
|
LoadString(hInst, IDS_PRINTER_DRIVERS, Buffer, (sizeof Buffer / sizeof *Buffer));
|
|
|
|
pShareInfo->shi2_remark = AllocSplStr(Buffer);
|
|
|
|
if ( pShareInfo->shi2_remark == NULL ) {
|
|
DBGMSG(DBG_WARNING, ("SplCreateSpooler Unable to allocate\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
pIniSpooler->pIniPrinter = NULL;
|
|
pIniSpooler->pIniEnvironment = NULL;
|
|
pIniSpooler->pIniNetPrint = NULL;
|
|
pIniSpooler->cNetPrinters = 0;
|
|
|
|
//
|
|
// No need to initialize shared resources.
|
|
//
|
|
pIniSpooler->pSpool = NULL;
|
|
pIniSpooler->pDefaultSpoolDir = NULL;
|
|
pIniSpooler->bEnableRetryPopups = FALSE;
|
|
pIniSpooler->dwRestartJobOnPoolTimeout = DEFAULT_JOB_RESTART_TIMEOUT_ON_POOL_ERROR;
|
|
pIniSpooler->bRestartJobOnPoolEnabled = TRUE;
|
|
|
|
|
|
if (( pSpoolerInfo2->pszRegistryMonitors == NULL ) &&
|
|
( pSpoolerInfo2->pszRegistryEnvironments == NULL ) &&
|
|
( pSpoolerInfo2->pszRegistryEventLog == NULL ) &&
|
|
( pSpoolerInfo2->pszRegistryProviders == NULL ) &&
|
|
( pSpoolerInfo2->pszEventLogMsgFile == NULL ) &&
|
|
( pSpoolerInfo2->pszRegistryForms == NULL ) &&
|
|
( pSpoolerInfo2->pszDriversShare == NULL )) {
|
|
|
|
DBGMSG( DBG_WARNING, ("SplCreateSpooler Invalid Parameters\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
if( !( pSpoolerInfo2->SpoolerFlags & SPL_CLUSTER_REG ) &&
|
|
( pSpoolerInfo2->pszRegistryPrinters == NULL )){
|
|
|
|
DBGMSG( DBG_WARNING, ("SplCreateSpooler Invalid RegistryPrinters\n"));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
if ( pSpoolerInfo2->pDefaultSpoolDir != NULL ) {
|
|
pIniSpooler->pDefaultSpoolDir = AllocSplStr( pSpoolerInfo2->pDefaultSpoolDir );
|
|
|
|
if ( pIniSpooler->pDefaultSpoolDir == NULL ) {
|
|
DBGMSG(DBG_WARNING, ("SplCreateSpooler Unable to allocate\n"));
|
|
goto SplCreateDone;
|
|
|
|
}
|
|
}
|
|
|
|
pIniSpooler->pszRegistryMonitors = AllocSplStr( pSpoolerInfo2->pszRegistryMonitors );
|
|
|
|
//
|
|
// The spooler stores data about environemnts, versions, drivers and print processors
|
|
// in the regsitry (or cluster data base). This data is accessed via pIniSpooler->
|
|
// pszRegistryEnvironemts
|
|
//
|
|
if (pSpoolerInfo2->SpoolerFlags & SPL_TYPE_CLUSTER)
|
|
{
|
|
//
|
|
// For a cluster spooler pIniSpooler->hckRoot maps to Parameters key of the spooler
|
|
// resource in the cluster database. pIniSpooler->pszRegistryEnvironments is a key
|
|
// called "Environemts" under hckRoot.
|
|
//
|
|
pIniSpooler->pszRegistryEnvironments = AllocSplStr(ipszClusterDatabaseEnvironments);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For local spooler pIniSpooler->pszRegistryEnvironments is the following string:
|
|
// System\CurrentControlSet\Control\Print\Environments. It is used relative to HKLM
|
|
//
|
|
pIniSpooler->pszRegistryEnvironments = AllocSplStr(!pLocalIniSpooler ? pSpoolerInfo2->pszRegistryEnvironments :
|
|
pLocalIniSpooler->pszRegistryEnvironments);
|
|
}
|
|
|
|
|
|
pIniSpooler->pszRegistryEventLog = AllocSplStr( pSpoolerInfo2->pszRegistryEventLog );
|
|
pIniSpooler->pszRegistryProviders = AllocSplStr( pSpoolerInfo2->pszRegistryProviders );
|
|
pIniSpooler->pszEventLogMsgFile = AllocSplStr( pSpoolerInfo2->pszEventLogMsgFile );
|
|
|
|
if (pSpoolerInfo2->SpoolerFlags & SPL_TYPE_CLUSTER)
|
|
{
|
|
//
|
|
// The driver share for a cluster spooler is of the form \\server\print$\spooler-resource-GUID
|
|
//
|
|
StrCatAlloc(&pIniSpooler->pszDriversShare,
|
|
pSpoolerInfo2->pszDriversShare,
|
|
L"\\",
|
|
pIniSpooler->pszClusResID,
|
|
szDriversDirectory,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The driver share for the local spooler is \\server\print$
|
|
//
|
|
StrCatAlloc(&pIniSpooler->pszDriversShare, pSpoolerInfo2->pszDriversShare, NULL);
|
|
}
|
|
|
|
pIniSpooler->pszRegistryForms = AllocSplStr( pSpoolerInfo2->pszRegistryForms ) ;
|
|
pIniSpooler->hClusterToken = INVALID_HANDLE_VALUE;
|
|
pIniSpooler->hFilePool = INVALID_HANDLE_VALUE;
|
|
|
|
if ( pIniSpooler->pszRegistryMonitors == NULL ||
|
|
pIniSpooler->pszRegistryEnvironments == NULL ||
|
|
pIniSpooler->pszRegistryEventLog == NULL ||
|
|
pIniSpooler->pszRegistryProviders == NULL ||
|
|
pIniSpooler->pszEventLogMsgFile == NULL ||
|
|
pIniSpooler->pszDriversShare == NULL ||
|
|
pIniSpooler->pszRegistryForms == NULL ) {
|
|
|
|
DBGMSG(DBG_WARNING, ("SplCreateSpooler Unable to allocate\n"));
|
|
goto SplCreateDone;
|
|
|
|
}
|
|
|
|
pIniSpooler->SpoolerFlags = pSpoolerInfo2->SpoolerFlags;
|
|
|
|
//
|
|
// Initialize the shared resources (pShared).
|
|
//
|
|
if( !InitializeShared( pIniSpooler )){
|
|
DBGMSG( DBG_WARN,
|
|
( "SplCreateSpooler: InitializeShared Failed %d\n",
|
|
GetLastError() ));
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
//
|
|
// Create the print share if necessary. This is always needed
|
|
// since the cluster printers are shared, while the local ones
|
|
// on this node aren't.
|
|
//
|
|
if(pIniSpooler->SpoolerFlags & SPL_ALWAYS_CREATE_DRIVER_SHARE ){
|
|
|
|
if( !AddPrintShare( pIniSpooler )){
|
|
goto SplCreateDone;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open and store the printer and root key from
|
|
// the resource registry.
|
|
//
|
|
|
|
if( pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG ){
|
|
|
|
SPLASSERT( Level == 2 );
|
|
|
|
// Set up the DS Cluster info. If we fail here, we can't publish printers, but let's
|
|
// not abort the cluster.
|
|
Status = InitializeDSClusterInfo(pIniSpooler, &hToken);
|
|
if (Status != ERROR_SUCCESS) {
|
|
DBGMSG(DBG_WARNING, ("InitializeDSClusterInfo FAILED: %d\n", Status));
|
|
}
|
|
|
|
pIniSpooler->hckRoot = OpenClusterParameterKey(
|
|
pSpoolerInfo2->pszResource );
|
|
|
|
if( !pIniSpooler->hckRoot ) {
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
Status = SplRegCreateKey( pIniSpooler->hckRoot,
|
|
szPrintersKey,
|
|
0,
|
|
KEY_WRITE | KEY_READ | DELETE,
|
|
NULL,
|
|
&pIniSpooler->hckPrinters,
|
|
NULL,
|
|
pIniSpooler );
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
SetLastError( Status );
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
} else {
|
|
|
|
DWORD dwDisposition;
|
|
|
|
Status = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
|
|
pSpoolerInfo2->pszRegistryRoot,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_WRITE | KEY_READ | DELETE,
|
|
NULL,
|
|
&pIniSpooler->hckRoot,
|
|
&dwDisposition );
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
SetLastError( Status );
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
Status = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
|
|
pSpoolerInfo2->pszRegistryPrinters,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_WRITE | KEY_READ | DELETE,
|
|
NULL,
|
|
&pIniSpooler->hckPrinters,
|
|
&dwDisposition );
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
SetLastError( Status );
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
pIniSpooler->pfnReadRegistryExtra = pSpoolerInfo2->pfnReadRegistryExtra;
|
|
pIniSpooler->pfnWriteRegistryExtra = pSpoolerInfo2->pfnWriteRegistryExtra;
|
|
pIniSpooler->pfnFreePrinterExtra = pSpoolerInfo2->pfnFreePrinterExtra;
|
|
|
|
// Success add to Linked List
|
|
|
|
if ( pLocalIniSpooler != NULL ) {
|
|
|
|
pIniSpooler->pIniNextSpooler = pLocalIniSpooler->pIniNextSpooler;
|
|
pLocalIniSpooler->pIniNextSpooler = pIniSpooler;
|
|
|
|
|
|
} else {
|
|
|
|
// First One is Always LocalSpl
|
|
|
|
pLocalIniSpooler = pIniSpooler;
|
|
pIniSpooler->pIniNextSpooler = NULL;
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// This function will update the global varaiable dwUpgradeFlag
|
|
//
|
|
QueryUpgradeFlag( pIniSpooler );
|
|
|
|
InitializeEventLogging( pIniSpooler );
|
|
|
|
//
|
|
// Only initialize forms if this is not a clustered spooler.
|
|
//
|
|
if( !( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER )){
|
|
InitializeForms( pIniSpooler );
|
|
}
|
|
|
|
//
|
|
// Originally ports were a per-machine (manual) resource. However,
|
|
// this has changed for clustering in 5.0, so that ports can be
|
|
// stored in the cluster registry. Note that monitors are still
|
|
// manual resources, since there isn't an easy way to install them
|
|
// on a remote machine.
|
|
//
|
|
|
|
BuildAllPorts( pIniSpooler );
|
|
|
|
InitializeSpoolerSettings( pIniSpooler );
|
|
|
|
if ( pIniSpooler == pLocalIniSpooler ) {
|
|
|
|
GetPrintSystemVersion( pIniSpooler );
|
|
|
|
BuildEnvironmentInfo( pIniSpooler );
|
|
|
|
if ( dwUpgradeFlag ) {
|
|
|
|
//
|
|
// The problem is that we have built-in forms, and
|
|
// custom forms (duplicates disallowed).On NT4, we
|
|
// may have a custom "A6" form. When we upgrade to NT5,
|
|
// and we have a new built-in from "A6." We need to
|
|
// rename the custom form to "A6 Custom," otherwise we'll
|
|
// have duplicates.
|
|
//
|
|
|
|
UpgradeForms(pIniSpooler);
|
|
}
|
|
|
|
|
|
} else if (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) {
|
|
|
|
HANDLE hThread;
|
|
DWORD dwThreadId;
|
|
DWORD dwError;
|
|
|
|
//
|
|
// The setup creates the registry strucutre for the local spooler:
|
|
// Environments {Windows NT x86, Windows IA64, etc}
|
|
// For a cluster spooler we need to create it ourselves in the
|
|
// cluster database
|
|
//
|
|
if ((dwError = CreateClusterSpoolerEnvironmentsStructure(pIniSpooler)) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Create all the environments, versions, drivers, processors strurctres
|
|
// This function always returns FALSE. We cannot take its return value
|
|
// into account.
|
|
//
|
|
BuildEnvironmentInfo(pIniSpooler);
|
|
|
|
//
|
|
// Now we launch a thread to do time consuming tasks that can
|
|
// be performed with the spooler on line. These inlude ungrading
|
|
// printer drivers, copying ICM profiles from the cluster disk etc.
|
|
//
|
|
// We need to bump the ref count so that the worker thread has
|
|
// a valid pIniSpooler. The worker thread will decref the pinispooler
|
|
// when it is done
|
|
//
|
|
INCSPOOLERREF(pIniSpooler);
|
|
|
|
//
|
|
// The event has manual reset and is not signaled.
|
|
//
|
|
pIniSpooler->hClusSplReady = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
//
|
|
// If the thread is created, then SplCreateSpoolerWorkerThread will
|
|
// close the hClusSplReady event handle.
|
|
//
|
|
if (pIniSpooler->hClusSplReady &&
|
|
(hThread = CreateThread(NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)SplCreateSpoolerWorkerThread,
|
|
(PVOID)pIniSpooler,
|
|
0,
|
|
&dwThreadId)))
|
|
{
|
|
CloseHandle(hThread);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Either CreateEvent or CreatreThread failed.
|
|
//
|
|
dwError = GetLastError();
|
|
|
|
if (pIniSpooler->hClusSplReady)
|
|
{
|
|
CloseHandle(pIniSpooler->hClusSplReady);
|
|
|
|
pIniSpooler->hClusSplReady = NULL;
|
|
}
|
|
|
|
DECSPOOLERREF(pIniSpooler);
|
|
|
|
DBGMSG(DBG_ERROR, ("Unable to create SplCreateSpoolerWorkerThread\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// An error occured
|
|
//
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(dwError);
|
|
goto SplCreateDone;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is the case of a network spooler. You get one of those when
|
|
// you make a true printer connection
|
|
//
|
|
pIniSpooler->pIniEnvironment = pLocalIniSpooler->pIniEnvironment;
|
|
}
|
|
|
|
//
|
|
// Read Printer Info from Registry (cluster databse for cluster spooler)
|
|
// There's no cleanup in any of this code--it doesn't free any allocated memory!
|
|
//
|
|
if( !BuildPrinterInfo( pIniSpooler, (BOOL) dwUpgradeFlag )){
|
|
goto SplCreateDone;
|
|
}
|
|
|
|
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
|
|
|
|
//
|
|
// Keep a counter of pIniSpoolers.
|
|
//
|
|
++gcClusterIniSpooler;
|
|
}
|
|
|
|
//
|
|
// We need to perform some costly initialization, so we increase the refcount
|
|
// on the pIniSpooler and will do the lengthy operations outside the global
|
|
// critical section
|
|
//
|
|
INCSPOOLERREF(pIniSpooler);
|
|
LeaveSplSem();
|
|
|
|
//
|
|
// GetDNSMachineName may fail, but that's okay. Just don't be surprised
|
|
// if pszFullMachineName is NULL.
|
|
//
|
|
GetDNSMachineName(pIniSpooler->pMachineName + 2, &pIniSpooler->pszFullMachineName);
|
|
|
|
if (Level == 2 && pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER)
|
|
{
|
|
LPWSTR *ppszIPAddresses = NULL;
|
|
DWORD cIPAddreeses = 0;
|
|
|
|
//
|
|
// The cluster resource calls us with level 2. In this case we want to add a node
|
|
// tothe cache, but we want the node to support the name and IP addresses we want.
|
|
// The resource tells us all the IP addresses the cluster spooler is dependent on.
|
|
//
|
|
if (BuildIPArrayFromCommaList(pSpoolerInfo2->pszAddress,
|
|
&ppszIPAddresses,
|
|
&cIPAddreeses) != S_OK ||
|
|
CacheCreateAndAddNodeWithIPAddresses(pIniSpooler->pMachineName + 2,
|
|
TRUE,
|
|
ppszIPAddresses,
|
|
cIPAddreeses) != S_OK)
|
|
{
|
|
EnterSplSem();
|
|
goto SplCreateDone;
|
|
}
|
|
}
|
|
else if (pIniSpooler->SpoolerFlags & (SPL_TYPE_CLUSTER | SPL_TYPE_LOCAL))
|
|
{
|
|
//
|
|
// We skip the \\ prepended to the server name. Cache nodes for cluster pIniSpooler
|
|
// are alwasy searched first. That is why in CacheCreateAndAddNode we indicate
|
|
// the type of the spooler.
|
|
//
|
|
if (CacheCreateAndAddNode(pIniSpooler->pMachineName + 2,
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER) != S_OK)
|
|
{
|
|
EnterSplSem();
|
|
goto SplCreateDone;
|
|
}
|
|
}
|
|
|
|
EnterSplSem();
|
|
DECSPOOLERREF(pIniSpooler);
|
|
|
|
} else {
|
|
|
|
INCSPOOLERREF( pIniSpooler );
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize the DS.
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_PRINT) {
|
|
InitializeDS(pIniSpooler);
|
|
}
|
|
|
|
hReturn = (HANDLE)pIniSpooler;
|
|
|
|
SplCreateDone:
|
|
|
|
//
|
|
// Check if an error occurred while creating the spooler.
|
|
//
|
|
if (hReturn == INVALID_HANDLE_VALUE && pIniSpooler)
|
|
{
|
|
//
|
|
// This will prevent leaking allocated fields
|
|
//
|
|
DECSPOOLERREF(pIniSpooler);
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
if ( !pIniSpooler )
|
|
{
|
|
if (!(pSpoolerInfo2->SpoolerFlags & SPL_OPEN_EXISTING_ONLY))
|
|
{
|
|
SplLogEvent(
|
|
NULL,
|
|
LOG_ERROR,
|
|
MSG_INIT_FAILED,
|
|
FALSE,
|
|
L"Spooler",
|
|
L"SplCreateSpooler",
|
|
L"Unknown Error",
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
if (hToken)
|
|
{
|
|
ImpersonatePrinterClient(hToken);
|
|
}
|
|
|
|
//
|
|
// Set the event that the cluster spooler is initialized
|
|
//
|
|
if (pIniSpooler &&
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
pIniSpooler->hClusSplReady)
|
|
{
|
|
SetEvent(pIniSpooler->hClusSplReady);
|
|
}
|
|
|
|
return hReturn;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
InitializePrintProvidor(
|
|
LPPRINTPROVIDOR pPrintProvidor,
|
|
DWORD cbPrintProvidor,
|
|
LPWSTR pFullRegistryPath
|
|
)
|
|
{
|
|
HANDLE hSchedulerThread;
|
|
HANDLE hFinalInitAfterRouterInitCompleteThread;
|
|
DWORD ThreadId;
|
|
BOOL bSucceeded = TRUE;
|
|
WCHAR Buffer[MAX_PATH];
|
|
DWORD i;
|
|
PINISPOOLER pIniSpooler = NULL;
|
|
static LPWSTR pMachineName = NULL;
|
|
static LPWSTR pszDriverShareName = NULL;
|
|
SPOOLER_INFO_1 SpoolerInfo1;
|
|
BOOL bInSem = FALSE;
|
|
|
|
try {
|
|
|
|
if (!InitializeWinSpoolDrv())
|
|
leave;
|
|
|
|
//
|
|
// Make sure sizes of structres are good
|
|
//
|
|
|
|
SPLASSERT( sizeof( PRINTER_INFO_STRESSW ) == sizeof ( PRINTER_INFO_STRESSA ) );
|
|
|
|
|
|
// !! LATER !!
|
|
// We could change this to succeed even on failure
|
|
// if we point all the routines to a function which returns failure
|
|
//
|
|
|
|
if (!InitializeNet())
|
|
leave;
|
|
|
|
//
|
|
// JobIdMap initialized when spooler created.
|
|
//
|
|
|
|
//
|
|
// Allocate LocalSpl Global IniSpooler
|
|
//
|
|
|
|
Buffer[0] = Buffer[1] = L'\\';
|
|
i = MAX_PATH-2;
|
|
OsVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if ( !GetComputerName(Buffer+2, &i) ||
|
|
!GetVersionEx((POSVERSIONINFO)&OsVersionInfoEx) ||
|
|
!GetVersionEx(&OsVersionInfo)) {
|
|
|
|
DBGMSG(DBG_WARNING, ("GetComputerName/OSVersionInfo failed.\n"));
|
|
leave;
|
|
}
|
|
|
|
pMachineName = AllocSplStr(Buffer);
|
|
if ( pMachineName == NULL )
|
|
leave;
|
|
|
|
pszDriverShareName = AllocSplStr(ipszDriversShareName); /* print$ */
|
|
|
|
if ( pszDriverShareName == NULL )
|
|
{
|
|
leave;
|
|
}
|
|
|
|
SpoolerInfo1.pszDriversShare = pszDriverShareName;
|
|
|
|
//
|
|
// Read value for Allow remote connection policy.
|
|
//
|
|
if (FAILED(GetServerPolicy(szRegisterSpoolerRemoteRpcEndPoint,
|
|
(ULONG*)(&gRemoteRPCEndPointPolicy))))
|
|
{
|
|
DBGMSG(DBG_ERROR, ("Failing to read the remote connections policy.\n"));
|
|
leave;
|
|
}
|
|
|
|
if (!InitializeSecurityStructures())
|
|
leave;
|
|
|
|
|
|
// Use Defaults
|
|
|
|
SpoolerInfo1.pDir = NULL;
|
|
SpoolerInfo1.pDefaultSpoolDir = NULL;
|
|
|
|
SpoolerInfo1.pszRegistryRoot = ipszRegistryRoot;
|
|
SpoolerInfo1.pszRegistryPrinters = ipszRegSwPrinters;
|
|
SpoolerInfo1.pszRegistryMonitors = ipszRegistryMonitorsHKLM;
|
|
SpoolerInfo1.pszRegistryEnvironments = ipszRegistryEnvironments;
|
|
SpoolerInfo1.pszRegistryEventLog = ipszRegistryEventLog;
|
|
SpoolerInfo1.pszRegistryProviders = ipszRegistryProviders;
|
|
SpoolerInfo1.pszEventLogMsgFile = ipszEventLogMsgFile;
|
|
SpoolerInfo1.pszRegistryForms = ipszRegistryForms;
|
|
|
|
SpoolerInfo1.SpoolerFlags = SPL_UPDATE_WININI_DEVICES |
|
|
SPL_PRINTER_CHANGES |
|
|
SPL_LOG_EVENTS |
|
|
SPL_FORMS_CHANGE |
|
|
SPL_BROADCAST_CHANGE |
|
|
SPL_SECURITY_CHECK |
|
|
SPL_OPEN_CREATE_PORTS |
|
|
SPL_FAIL_OPEN_PRINTERS_PENDING_DELETION |
|
|
SPL_REMOTE_HANDLE_CHECK |
|
|
SPL_PRINTER_DRIVER_EVENT |
|
|
SPL_SERVER_THREAD |
|
|
SPL_PRINT |
|
|
SPL_TYPE_LOCAL;
|
|
|
|
SpoolerInfo1.pfnReadRegistryExtra = NULL;
|
|
SpoolerInfo1.pfnWriteRegistryExtra = NULL;
|
|
SpoolerInfo1.pfnFreePrinterExtra = NULL;
|
|
|
|
pLocalIniSpooler = SplCreateSpooler( pMachineName,
|
|
1,
|
|
(PBYTE)&SpoolerInfo1,
|
|
NULL );
|
|
|
|
|
|
if ( pLocalIniSpooler == INVALID_HANDLE_VALUE ) {
|
|
DBGMSG( DBG_WARNING, ("InitializePrintProvidor Unable to allocate pLocalIniSpooler\n"));
|
|
leave;
|
|
}
|
|
|
|
pIniSpooler = pLocalIniSpooler;
|
|
|
|
#if DBG
|
|
InitializeDebug( pIniSpooler );
|
|
#endif
|
|
|
|
// !! LATER !!
|
|
// Why is this done inside critical section ?
|
|
|
|
|
|
EnterSplSem();
|
|
bInSem = TRUE;
|
|
|
|
if (!LoadString(hInst, IDS_REMOTE_DOC, Buffer, MAX_PATH))
|
|
leave;
|
|
|
|
szRemoteDoc = AllocSplStr( Buffer );
|
|
if ( szRemoteDoc == NULL )
|
|
leave;
|
|
|
|
if (!LoadString(hInst, IDS_LOCAL_DOC, Buffer, MAX_PATH))
|
|
leave;
|
|
|
|
szLocalDoc = AllocSplStr( Buffer );
|
|
if ( szLocalDoc == NULL )
|
|
leave;
|
|
|
|
if (!LoadString(hInst, IDS_FASTPRINT_TIMEOUT, Buffer, MAX_PATH))
|
|
leave;
|
|
|
|
szFastPrintTimeout = AllocSplStr( Buffer );
|
|
if ( szFastPrintTimeout == NULL )
|
|
leave;
|
|
|
|
SchedulerSignal = CreateEvent(NULL,
|
|
EVENT_RESET_AUTOMATIC,
|
|
EVENT_INITIAL_STATE_NOT_SIGNALED,
|
|
NULL);
|
|
|
|
PowerManagementSignal = CreateEvent(NULL,
|
|
EVENT_RESET_MANUAL,
|
|
EVENT_INITIAL_STATE_SIGNALED,
|
|
NULL);
|
|
|
|
hSchedulerThread = CreateThread( NULL,
|
|
INITIAL_STACK_COMMIT,
|
|
(LPTHREAD_START_ROUTINE)SchedulerThread,
|
|
pIniSpooler, 0, &ThreadId );
|
|
|
|
hFinalInitAfterRouterInitCompleteThread = CreateThread( NULL, INITIAL_STACK_COMMIT,
|
|
(LPTHREAD_START_ROUTINE)FinalInitAfterRouterInitCompleteThread,
|
|
(LPVOID)ULongToPtr(dwUpgradeFlag), 0, &ThreadId );
|
|
|
|
|
|
if (!SchedulerSignal || !PowerManagementSignal || !hSchedulerThread || !hFinalInitAfterRouterInitCompleteThread) {
|
|
|
|
DBGMSG( DBG_WARNING, ("Scheduler/FinalInitAfterRouterInitCompleteThread not initialised properly: Error %d\n", GetLastError()));
|
|
leave;
|
|
}
|
|
|
|
if ( !SetThreadPriority( hSchedulerThread, dwSchedulerThreadPriority ) ) {
|
|
|
|
DBGMSG( DBG_WARNING, ("Setting Scheduler thread priority failed %d\n", GetLastError()));
|
|
}
|
|
|
|
|
|
CloseHandle( hSchedulerThread );
|
|
CloseHandle( hFinalInitAfterRouterInitCompleteThread );
|
|
|
|
//
|
|
// Read online/offline status for local printers from current config
|
|
//
|
|
SplConfigChange();
|
|
|
|
CHECK_SCHEDULER();
|
|
|
|
CopyMemory( pPrintProvidor, &PrintProvidor, min(sizeof(PRINTPROVIDOR), cbPrintProvidor));
|
|
|
|
LeaveSplSem();
|
|
bInSem = FALSE;
|
|
|
|
CloseProfileUserMapping(); // !!! We should be able to get rid of this
|
|
|
|
//
|
|
// Get the default value for DefaultKMPrintersAreBlocked. It depends
|
|
// what type of OS is running. If we cannot identify the type of OS,
|
|
// the default is set to "blocked"
|
|
//
|
|
DefaultKMPrintersAreBlocked = GetDefaultForKMPrintersBlockedPolicy();
|
|
|
|
gdwServerInstallTimeOut = GetServerInstallTimeOut();
|
|
|
|
Initialized = TRUE;
|
|
|
|
|
|
} finally {
|
|
|
|
if ( bInSem ) {
|
|
LeaveSplSem();
|
|
}
|
|
|
|
}
|
|
|
|
SplOutSem();
|
|
|
|
return Initialized;
|
|
}
|
|
|
|
|
|
PINIPORT
|
|
CreatePortEntry(
|
|
LPWSTR pPortName,
|
|
PINIMONITOR pIniMonitor,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
DWORD cb;
|
|
PINIPORT pIniPort = NULL;
|
|
HANDLE hPort = NULL;
|
|
BOOL bPlaceHolder = FALSE;
|
|
BOOL bInitCS = FALSE;
|
|
|
|
//
|
|
// This is a placeholder if there is no monitor and later if there is no
|
|
// partial print provider.
|
|
//
|
|
bPlaceHolder = pIniMonitor == NULL;
|
|
|
|
SplInSem();
|
|
|
|
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
if (!pPortName || !*pPortName || wcslen(pPortName) >= MAX_PATH) {
|
|
|
|
SetLastError(ERROR_UNKNOWN_PORT);
|
|
return NULL;
|
|
}
|
|
|
|
if (!pIniMonitor) {
|
|
|
|
/* Don't bother validating the port if we aren't initialised.
|
|
* It must be valid, since we wrote it in the registry.
|
|
* This fixes the problem of attempting to open a network
|
|
* printer before the redirector has initialised,
|
|
* and the problem of access denied because we're currently
|
|
* in the system's context.
|
|
*/
|
|
if (Initialized) {
|
|
|
|
//
|
|
// !! Warning !!
|
|
//
|
|
// Watch for deadlock:
|
|
//
|
|
// spoolss!OpenPrinterPortW -> RPC to self printer port
|
|
// localspl!CreatePortEntry
|
|
// localspl!ValidatePortTokenList
|
|
// localspl!SetPrinterPorts
|
|
// localspl!LocalSetPrinter
|
|
// spoolss!SetPrinterW
|
|
// spoolss!RpcSetPrinter
|
|
// spoolss!winspool_RpcSetPrinter
|
|
//
|
|
|
|
//
|
|
// If we can't open the port then fail the call since this
|
|
// spooler did not know this name before.
|
|
//
|
|
|
|
LeaveSplSem();
|
|
if ( !OpenPrinterPortW(pPortName, &hPort, NULL) ){
|
|
EnterSplSem();
|
|
goto Cleanup;
|
|
}
|
|
else {
|
|
|
|
bPlaceHolder = FALSE;
|
|
ClosePrinter(hPort);
|
|
}
|
|
|
|
EnterSplSem();
|
|
}
|
|
}
|
|
|
|
cb = sizeof(INIPORT) + wcslen(pPortName)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
|
|
if (pIniPort=AllocSplMem(cb)) {
|
|
|
|
StringCbCopy((LPWSTR)(pIniPort+1), cb - sizeof(*pIniPort), pPortName);
|
|
|
|
pIniPort->pName = (LPWSTR)(pIniPort+1);
|
|
pIniPort->signature = IPO_SIGNATURE;
|
|
pIniPort->pIniMonitor = pIniMonitor;
|
|
pIniPort->IdleTime = GetTickCount() - 1;
|
|
pIniPort->bIdleTimeValid = FALSE;
|
|
pIniPort->ErrorTime = 0;
|
|
pIniPort->hErrorEvent = NULL;
|
|
pIniPort->InCriticalSection = 0;
|
|
|
|
pIniPort->hPortThreadRunning = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if ( !pIniPort->hPortThreadRunning ) {
|
|
goto Cleanup;
|
|
}
|
|
if (pIniMonitor) {
|
|
pIniPort->Status |= PP_MONITOR;
|
|
}
|
|
|
|
if (bPlaceHolder) {
|
|
pIniPort->Status |= PP_PLACEHOLDER;
|
|
}
|
|
|
|
if (!(bInitCS = InitializeCriticalSectionAndSpinCount(&(pIniPort->MonitorCS),
|
|
0x80000000)))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
LinkPortToSpooler( pIniPort, pIniSpooler );
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( pIniPort && (!pIniPort->hPortThreadRunning || !bInitCS) ) {
|
|
DeletePortEntry(pIniPort);
|
|
}
|
|
return pIniPort;
|
|
}
|
|
|
|
BOOL
|
|
DeletePortEntry(
|
|
PINIPORT pIniPort
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free pIniPort resources then delete it. If the pIniPort is on
|
|
a pIniSpooler's linked list, remove it too.
|
|
|
|
Arguments:
|
|
|
|
pIniPort - Port to delete. May or may not be on a pIniSpooler.
|
|
|
|
Return Value:
|
|
|
|
TRUE - deleted
|
|
FALSE - not deleted (may be in use).
|
|
|
|
--*/
|
|
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
|
|
SplInSem();
|
|
|
|
SPLASSERT ( ( pIniPort != NULL) || ( pIniPort->signature == IPO_SIGNATURE) );
|
|
|
|
//
|
|
// We had better already closed the port monitor.
|
|
//
|
|
SPLASSERT( !GetMonitorHandle(pIniPort) &&
|
|
!(pIniPort->Status & PP_THREADRUNNING) &&
|
|
!pIniPort->cJobs);
|
|
|
|
if (pIniPort->cRef) {
|
|
pIniPort->Status |= PP_DELETING;
|
|
return FALSE;
|
|
}
|
|
|
|
pIniSpooler = pIniPort->pIniSpooler;
|
|
|
|
//
|
|
// If currently linked to a pIniSpooler, delink it.
|
|
//
|
|
if( pIniSpooler ){
|
|
|
|
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
DelinkPortFromSpooler( pIniPort, pIniSpooler );
|
|
}
|
|
|
|
if (pIniPort->ppIniPrinter)
|
|
FreeSplMem(pIniPort->ppIniPrinter);
|
|
|
|
|
|
CloseHandle(pIniPort->hPortThreadRunning);
|
|
|
|
DeleteCriticalSection(&pIniPort->MonitorCS);
|
|
|
|
FreeSplMem(pIniPort);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
FreeIniMonitor(
|
|
PINIMONITOR pIniMonitor
|
|
)
|
|
{
|
|
if( pIniMonitor ){
|
|
|
|
FreeSplStr( pIniMonitor->pMonitorDll );
|
|
|
|
if( pIniMonitor->hModule ){
|
|
FreeLibrary( pIniMonitor->hModule );
|
|
}
|
|
|
|
if( pIniMonitor->pMonitorInit ){
|
|
|
|
FreeSplStr( (LPWSTR)pIniMonitor->pMonitorInit->pszServerName );
|
|
|
|
if( pIniMonitor->pMonitorInit->hckRegistryRoot ){
|
|
SplRegCloseKey( pIniMonitor->pMonitorInit->hckRegistryRoot,
|
|
pIniMonitor->pIniSpooler );
|
|
}
|
|
|
|
FreeSplMem( pIniMonitor->pMonitorInit );
|
|
}
|
|
|
|
FreeSplMem( pIniMonitor );
|
|
}
|
|
}
|
|
|
|
#ifdef _SPL_CLUST
|
|
LPMONITOR2
|
|
InitializePrintMonitor2(
|
|
PMONITORINIT pMonitorInit,
|
|
PHANDLE phMonitor
|
|
)
|
|
{
|
|
return(LocalMonInitializePrintMonitor2(pMonitorInit,phMonitor));
|
|
}
|
|
#endif
|
|
|
|
PINIMONITOR
|
|
CreateMonitorEntry(
|
|
LPWSTR pMonitorDll,
|
|
LPWSTR pMonitorName,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Valid pIniMonitor - This means everything worked out fine.
|
|
|
|
NULL - This means the monitor DLL was found, but the initialisation routine
|
|
returned FALSE. This is non-fatal, as the monitor may need the
|
|
system to reboot before it can run properly.
|
|
|
|
-1 - This means the monitor DLL or the initialization routine was not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR szRegistryRoot[MAX_PATH];
|
|
DWORD cb, cbNeeded, cReturned, dwRetVal;
|
|
PPORT_INFO_1 pPorts, pPort;
|
|
PINIMONITOR pIniMonitor;
|
|
UINT uOldErrMode;
|
|
PMONITOR2 (*pfnInitializePrintMonitor2)(PMONITORINIT, PHANDLE) = NULL;
|
|
PINIMONITOR pReturnValue = (PINIMONITOR)-1;
|
|
|
|
HANDLE hKeyOut;
|
|
LPWSTR pszPathOut;
|
|
|
|
SPLASSERT( (pIniSpooler != NULL) || (pIniSpooler->signature == ISP_SIGNATURE));
|
|
SplInSem();
|
|
|
|
cb = sizeof(INIMONITOR) + wcslen(pMonitorName)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
pIniMonitor = AllocSplMem(cb);
|
|
|
|
if( !pIniMonitor ){
|
|
goto Fail;
|
|
}
|
|
|
|
StringCbCopy((LPWSTR)(pIniMonitor+1), cb - sizeof(*pIniMonitor), pMonitorName);
|
|
|
|
pIniMonitor->pName = (LPWSTR)(pIniMonitor+1);
|
|
pIniMonitor->signature = IMO_SIGNATURE;
|
|
pIniMonitor->pMonitorDll = AllocSplStr(pMonitorDll);
|
|
|
|
pIniMonitor->pIniSpooler = pIniSpooler;
|
|
|
|
if( !pIniMonitor->pMonitorDll ){
|
|
goto Fail;
|
|
}
|
|
|
|
//
|
|
// Load the library, but don't show any hard error popups if it's an
|
|
// invalid binary.
|
|
//
|
|
INCSPOOLERREF( pIniSpooler );
|
|
LeaveSplSem();
|
|
uOldErrMode = SetErrorMode( SEM_FAILCRITICALERRORS );
|
|
pIniMonitor->hModule = LoadLibrary(pMonitorDll);
|
|
SetErrorMode( uOldErrMode );
|
|
EnterSplSem();
|
|
DECSPOOLERREF( pIniSpooler );
|
|
|
|
if (!pIniMonitor->hModule) {
|
|
|
|
DBGMSG(DBG_WARNING, ("CreateMonitorEntry( %ws, %ws ) LoadLibrary failed %d\n",
|
|
pMonitorDll ? pMonitorDll : L"(NULL)",
|
|
pMonitorName ? pMonitorName : L"(NULL)",
|
|
GetLastError()));
|
|
goto Fail;
|
|
}
|
|
|
|
GetRegistryLocation( pIniSpooler->hckRoot,
|
|
pIniSpooler->pszRegistryMonitors,
|
|
&hKeyOut,
|
|
&pszPathOut );
|
|
|
|
|
|
dwRetVal = StrNCatBuff( szRegistryRoot,
|
|
COUNTOF(szRegistryRoot),
|
|
pszPathOut,
|
|
L"\\",
|
|
pMonitorName,
|
|
NULL );
|
|
|
|
if (dwRetVal != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(ERROR_INVALID_PRINT_MONITOR);
|
|
goto Fail;
|
|
}
|
|
//
|
|
// Try calling the entry points in the following order:
|
|
// InitializePrintMonitor2 (used for clustering),
|
|
// InitializePrintMonitor,
|
|
// InitializeMonitorEx,
|
|
// InitializeMonitor
|
|
//
|
|
|
|
(FARPROC)pfnInitializePrintMonitor2 = GetProcAddress(
|
|
pIniMonitor->hModule,
|
|
"InitializePrintMonitor2" );
|
|
|
|
if( !pfnInitializePrintMonitor2 ){
|
|
|
|
//
|
|
// If this is clustered spooler, then only InitializePrintMonitor2
|
|
// monitors are supported.
|
|
//
|
|
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
|
|
goto Fail;
|
|
}
|
|
|
|
//
|
|
// Add the parth to the Monitor name here.
|
|
//
|
|
|
|
pReturnValue = InitializeDMonitor( pIniMonitor,
|
|
szRegistryRoot );
|
|
|
|
if( pReturnValue == NULL ||
|
|
pReturnValue == (PINIMONITOR)-1 ){
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
} else {
|
|
|
|
PMONITORINIT pMonitorInit;
|
|
DWORD Status;
|
|
PMONITOR2 pMonitor2 = NULL;
|
|
|
|
INCSPOOLERREF( pIniSpooler );
|
|
LeaveSplSem();
|
|
|
|
//
|
|
// kKeyOut must either be not HKLM, or it must not be a cluster.
|
|
// If it is both a cluster and also uses HKLM, then we have an error.
|
|
// This should never happen because only win32spl uses an absolute
|
|
// path.
|
|
//
|
|
SPLASSERT( (hKeyOut != HKEY_LOCAL_MACHINE) ||
|
|
!(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ));
|
|
|
|
pMonitorInit = (PMONITORINIT)AllocSplMem( sizeof( MONITORINIT ));
|
|
|
|
if( !pMonitorInit ){
|
|
goto FailOutsideSem;
|
|
}
|
|
|
|
pMonitorInit->pszServerName = AllocSplStr( pIniSpooler->pMachineName );
|
|
|
|
if( !pMonitorInit->pszServerName ){
|
|
goto FailOutsideSem;
|
|
}
|
|
|
|
pMonitorInit->cbSize = sizeof( MONITORINIT );
|
|
pMonitorInit->hSpooler = (HANDLE)pIniSpooler;
|
|
pMonitorInit->pMonitorReg = &gMonitorReg;
|
|
pMonitorInit->bLocal = ( pIniSpooler == pLocalIniSpooler );
|
|
|
|
pIniMonitor->pMonitorInit = pMonitorInit;
|
|
|
|
Status = SplRegCreateKey( hKeyOut,
|
|
szRegistryRoot,
|
|
0,
|
|
KEY_READ | KEY_WRITE | DELETE,
|
|
NULL,
|
|
&pMonitorInit->hckRegistryRoot,
|
|
NULL,
|
|
pIniSpooler );
|
|
//
|
|
// If we can't create the hck root key, then fail
|
|
// the call. We should log an event here too.
|
|
//
|
|
if( Status == ERROR_SUCCESS ){
|
|
|
|
pMonitor2 = (*pfnInitializePrintMonitor2)(
|
|
pMonitorInit,
|
|
&pIniMonitor->hMonitor );
|
|
|
|
if( pMonitor2 ){
|
|
|
|
DBGMSG( DBG_TRACE,
|
|
( "CreateMonitorEntry: opened %x %x on %x\n",
|
|
pIniMonitor, pIniMonitor->hMonitor, pIniSpooler ));
|
|
|
|
//
|
|
// Succeeded, copy over the pMonitor2 structure into
|
|
// pIniMonitor->Monitor2.
|
|
//
|
|
CopyMemory((LPBYTE)&pIniMonitor->Monitor2, (LPBYTE)pMonitor2, min(pMonitor2->cbSize, sizeof(MONITOR2)));
|
|
|
|
//
|
|
// Check if the monitor2 supports Shutdown.
|
|
//
|
|
// Raid#: 193150 - Accept any size Monitor2 as long as it supports Shutdown
|
|
//
|
|
if( !pIniMonitor->Monitor2.pfnShutdown ){
|
|
|
|
DBGMSG( DBG_ERROR,
|
|
( "Invalid print monitor %ws (no shutdown)\n",
|
|
pMonitorName ));
|
|
SetLastError(ERROR_INVALID_PRINT_MONITOR);
|
|
DECSPOOLERREF( pIniSpooler );
|
|
goto FailOutsideSem;
|
|
}
|
|
|
|
//
|
|
// Initialize an uplevel monitor for downlevel support.
|
|
//
|
|
InitializeUMonitor( pIniMonitor );
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "CreateMonitorEntry: InitializePrintMonitor2 failed %d\n",
|
|
GetLastError() ));
|
|
}
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARN,
|
|
( "CreateMonitorEntry: Unable to create hckRoot "TSTR"\n",
|
|
pMonitorName ));
|
|
}
|
|
|
|
EnterSplSem();
|
|
DECSPOOLERREF( pIniSpooler );
|
|
|
|
if( !pMonitor2 ){
|
|
goto Fail;
|
|
}
|
|
|
|
pIniMonitor->bUplevel = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check if the monitor supports essential functions
|
|
//
|
|
if ( (!pIniMonitor->Monitor2.pfnOpenPort &&
|
|
!pIniMonitor->Monitor2.pfnOpenPortEx) ||
|
|
!pIniMonitor->Monitor2.pfnClosePort ||
|
|
!pIniMonitor->Monitor2.pfnStartDocPort ||
|
|
!pIniMonitor->Monitor2.pfnWritePort ||
|
|
!pIniMonitor->Monitor2.pfnReadPort ||
|
|
!pIniMonitor->Monitor2.pfnEndDocPort ) {
|
|
|
|
DBGMSG(DBG_ERROR, ("Invalid print monitor %ws\n", pMonitorName));
|
|
SetLastError(ERROR_INVALID_PRINT_MONITOR);
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
if (FindMonitor(pMonitorName, pIniSpooler)) {
|
|
|
|
SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
|
|
goto Fail;
|
|
}
|
|
|
|
|
|
if ((pIniMonitor->Monitor2.pfnEnumPorts) &&
|
|
!(*pIniMonitor->Monitor2.pfnEnumPorts)(
|
|
pIniMonitor->hMonitor,
|
|
NULL,
|
|
1,
|
|
NULL,
|
|
0,
|
|
&cbNeeded,
|
|
&cReturned)) {
|
|
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
if (pPorts = AllocSplMem(cbNeeded)) {
|
|
pPort = pPorts;
|
|
if ((*pIniMonitor->Monitor2.pfnEnumPorts)(
|
|
pIniMonitor->hMonitor,
|
|
NULL,
|
|
1,
|
|
(LPBYTE)pPorts,
|
|
cbNeeded,
|
|
&cbNeeded,
|
|
&cReturned)) {
|
|
|
|
while (cReturned--) {
|
|
CreatePortEntry(pPort->pName,
|
|
pIniMonitor,
|
|
pIniSpooler);
|
|
pPort++;
|
|
}
|
|
}
|
|
FreeSplMem(pPorts);
|
|
}
|
|
}
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("CreateMonitorEntry( %ws, %ws, %ws ) returning %x\n",
|
|
pMonitorDll ? pMonitorDll : L"(NULL)",
|
|
pMonitorName ? pMonitorName : L"(NULL)",
|
|
szRegistryRoot, pIniMonitor));
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// Success, link it up.
|
|
//
|
|
|
|
pIniMonitor->pNext = pIniSpooler->pIniMonitor;
|
|
pIniSpooler->pIniMonitor = pIniMonitor;
|
|
|
|
return pIniMonitor;
|
|
|
|
FailOutsideSem:
|
|
|
|
EnterSplSem();
|
|
|
|
Fail:
|
|
|
|
FreeIniMonitor( pIniMonitor );
|
|
|
|
return pReturnValue;
|
|
}
|
|
|
|
BOOL
|
|
BuildAllPorts(
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
DWORD cchData, cbDll, cMonitors;
|
|
WCHAR Dll[MAX_PATH];
|
|
WCHAR MonitorName[MAX_PATH];
|
|
WCHAR RegistryPath[MAX_PATH];
|
|
HKEY hKey, hKey1, hKeyOut;
|
|
LPWSTR pszPathOut;
|
|
LONG Status;
|
|
PINIMONITOR pReturnValue = (PINIMONITOR)-1;
|
|
|
|
PINISPOOLER pIniSpoolerMonitor;
|
|
|
|
//
|
|
// For pLocalIniSpooler or clustered spooler, read the monitors out
|
|
// of HKLM (the same monitors used in pLocalIniMonitor). This is because
|
|
// you install a monitor for each node, then that monitor is initialized
|
|
// for the local spooler and all clustered spoolers.
|
|
//
|
|
// You install monitors on the node, not on specific cluster groups.
|
|
//
|
|
if( pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL ){
|
|
pIniSpoolerMonitor = pLocalIniSpooler;
|
|
} else {
|
|
pIniSpoolerMonitor = pIniSpooler;
|
|
}
|
|
|
|
GetRegistryLocation( pIniSpoolerMonitor->hckRoot,
|
|
pIniSpooler->pszRegistryMonitors,
|
|
&hKeyOut,
|
|
&pszPathOut );
|
|
|
|
Status = RegOpenKeyEx( hKeyOut,
|
|
pszPathOut,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
cMonitors=0;
|
|
cchData = COUNTOF( MonitorName );
|
|
|
|
while (RegEnumKeyEx(hKey, cMonitors, MonitorName, &cchData, NULL, NULL,
|
|
NULL, NULL) == ERROR_SUCCESS) {
|
|
|
|
DBGMSG(DBG_TRACE, ("Found monitor %ws\n", MonitorName));
|
|
|
|
if (RegOpenKeyEx(hKey, MonitorName, 0, KEY_READ, &hKey1)
|
|
== ERROR_SUCCESS) {
|
|
|
|
cbDll = sizeof(Dll);
|
|
|
|
if (RegQueryValueEx(hKey1, L"Driver", NULL, NULL,
|
|
(LPBYTE)Dll, &cbDll)
|
|
== ERROR_SUCCESS) {
|
|
|
|
CreateMonitorEntry(Dll, MonitorName, pIniSpooler);
|
|
}
|
|
|
|
RegCloseKey(hKey1);
|
|
}
|
|
|
|
cMonitors++;
|
|
cchData = COUNTOF( MonitorName );
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
Current Directory == <NT directory>\system32\spool\printers
|
|
pFindFileData->cFileName == 0
|
|
*/
|
|
|
|
BOOL
|
|
BuildPrinterInfo(
|
|
PINISPOOLER pIniSpooler,
|
|
BOOL UpdateChangeID
|
|
)
|
|
{
|
|
WCHAR PrinterName[MAX_PRINTER_NAME];
|
|
WCHAR szData[MAX_PATH];
|
|
WCHAR szDefaultPrinterDirectory[MAX_PATH];
|
|
DWORD cbData, i;
|
|
DWORD cbSecurity, dwLastError;
|
|
DWORD cPrinters, Type;
|
|
HKEY hPrinterKey;
|
|
PINIPRINTER pIniPrinter;
|
|
PINIPORT pIniPort;
|
|
LONG Status;
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
PKEYDATA pKeyData = NULL;
|
|
BOOL bUpdateRegistryForThisPrinter = UpdateChangeID;
|
|
BOOL bWriteDirectory = FALSE;
|
|
BOOL bAllocMem = FALSE;
|
|
BOOL bNoPorts = FALSE;
|
|
LPWSTR szPortData;
|
|
|
|
|
|
//
|
|
// Has user specified Default Spool Directory ?
|
|
//
|
|
|
|
cbData = sizeof( szData );
|
|
*szData = (WCHAR)0;
|
|
|
|
Status = SplRegQueryValue( pIniSpooler->hckPrinters,
|
|
SPLREG_DEFAULT_SPOOL_DIRECTORY,
|
|
NULL,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) { // found a value, so verify the directory
|
|
if (!(pIniSpooler->pDefaultSpoolDir = AllocSplStr( szData ))) // Copies szData to pDefaultSpoolDir
|
|
return FALSE;
|
|
} else {
|
|
bWriteDirectory = TRUE; // No registry directory, so create one
|
|
}
|
|
|
|
// Copy pDefaultSpoolDir to szDefaultPrinterDirectory
|
|
GetPrinterDirectory(NULL, FALSE, szDefaultPrinterDirectory, COUNTOF(szDefaultPrinterDirectory), pIniSpooler);
|
|
|
|
if (!pIniSpooler->pDefaultSpoolDir)
|
|
return FALSE;
|
|
|
|
|
|
// Create the directory with the proper security, or fail trying
|
|
|
|
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SecurityAttributes.lpSecurityDescriptor = CreateEverybodySecurityDescriptor();
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
//
|
|
// CreateDirectory limits the length of a directory to 248 (MAX_PATH-12)
|
|
// characters. By calculating the length, we ensure that an escape sequence
|
|
// will not lead to the creation of a directory with a name longer than 248
|
|
//
|
|
if (wcslen(szDefaultPrinterDirectory) > MAX_PATH - 12 ||
|
|
!CreateDirectory(szDefaultPrinterDirectory, &SecurityAttributes)) {
|
|
|
|
// Failed to create the directory? Back to factory default
|
|
|
|
bWriteDirectory = TRUE;
|
|
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS) {
|
|
|
|
//
|
|
// In the clustered case, just fail.
|
|
//
|
|
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
|
|
return FALSE;
|
|
}
|
|
|
|
DBGMSG(DBG_WARNING, ("Failed to create DefaultSpoolDirectory %ws\n", szDefaultPrinterDirectory));
|
|
FreeSplStr(pIniSpooler->pDefaultSpoolDir);
|
|
|
|
pIniSpooler->pDefaultSpoolDir = NULL; // This tells GetPrinterDirectory to alloc pDefaultSpoolDir
|
|
GetPrinterDirectory(NULL, FALSE, szDefaultPrinterDirectory, COUNTOF(szDefaultPrinterDirectory), pIniSpooler);
|
|
|
|
if (!pIniSpooler->pDefaultSpoolDir)
|
|
return FALSE;
|
|
|
|
Status = CreateDirectory(szDefaultPrinterDirectory, &SecurityAttributes);
|
|
|
|
if (Status != ERROR_SUCCESS && Status != ERROR_ALREADY_EXISTS) {
|
|
DBGMSG(DBG_WARNING, ("Failed to create DefaultSpoolDirectory %ws\n", szDefaultPrinterDirectory));
|
|
FreeSplStr(pIniSpooler->pDefaultSpoolDir);
|
|
pIniSpooler->pDefaultSpoolDir = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalFree(SecurityAttributes.lpSecurityDescriptor);
|
|
|
|
if (bWriteDirectory) {
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_DEFAULT_SPOOL_DIRECTORY,
|
|
REG_SZ,
|
|
(LPBYTE) pIniSpooler->pDefaultSpoolDir,
|
|
wcslen(pIniSpooler->pDefaultSpoolDir)*sizeof(WCHAR) + sizeof(WCHAR));
|
|
}
|
|
|
|
cPrinters=0;
|
|
cbData = COUNTOF(PrinterName);
|
|
|
|
while( SplRegEnumKey( pIniSpooler->hckPrinters,
|
|
cPrinters,
|
|
PrinterName,
|
|
&cbData,
|
|
NULL,
|
|
pIniSpooler ) == ERROR_SUCCESS) {
|
|
|
|
DBGMSG(DBG_TRACE, ("Found printer %ws\n", PrinterName));
|
|
|
|
if( SplRegCreateKey( pIniSpooler->hckPrinters,
|
|
PrinterName,
|
|
0,
|
|
KEY_READ,
|
|
NULL,
|
|
&hPrinterKey,
|
|
NULL,
|
|
pIniSpooler ) == ERROR_SUCCESS ){
|
|
|
|
if ( pIniPrinter = AllocSplMem(sizeof(INIPRINTER) )) {
|
|
|
|
//
|
|
// Reference count the pIniSpooler.
|
|
//
|
|
INCSPOOLERREF( pIniSpooler );
|
|
|
|
pIniPrinter->signature = IP_SIGNATURE;
|
|
GetSystemTime( &pIniPrinter->stUpTime );
|
|
|
|
// Give the printer a unique session ID to pass around in notifications
|
|
pIniPrinter->dwUniqueSessionID = dwUniquePrinterSessionID++;
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue(hPrinterKey,
|
|
szName,
|
|
NULL,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler) == ERROR_SUCCESS)
|
|
|
|
pIniPrinter->pName = AllocSplStr(szData);
|
|
|
|
//
|
|
// Get Spool Directory for this printer
|
|
//
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szSpoolDir,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler) == ERROR_SUCCESS) {
|
|
|
|
if ( *szData != (WCHAR)0 ) {
|
|
|
|
pIniPrinter->pSpoolDir = AllocSplStr(szData);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Get ObjectGUID for this printer
|
|
//
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szObjectGUID,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler) == ERROR_SUCCESS) {
|
|
|
|
if ( *szData != (WCHAR)0 ) {
|
|
pIniPrinter->pszObjectGUID = AllocSplStr(szData);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Get DsKeyUpdate and DsKeyUpdateForeground for this printer
|
|
//
|
|
|
|
cbData = sizeof(pIniPrinter->DsKeyUpdate );
|
|
SplRegQueryValue( hPrinterKey,
|
|
szDsKeyUpdate,
|
|
&Type,
|
|
(LPBYTE) &pIniPrinter->DsKeyUpdate,
|
|
&cbData,
|
|
pIniSpooler);
|
|
|
|
cbData = sizeof(pIniPrinter->DsKeyUpdateForeground );
|
|
SplRegQueryValue( hPrinterKey,
|
|
szDsKeyUpdateForeground,
|
|
&Type,
|
|
(LPBYTE) &pIniPrinter->DsKeyUpdateForeground,
|
|
&cbData,
|
|
pIniSpooler);
|
|
|
|
if ( !(pIniSpooler->SpoolerFlags & SPL_TYPE_CACHE) ) {
|
|
|
|
// We make sure that DsKeyUpdateForeground is consistent
|
|
// when the spooler startsup. Otherwise DsKeyUpdateForeground might be
|
|
// set without dwAction being set and the printer will always be in the
|
|
// IO_PENDING state.
|
|
if (pIniPrinter->DsKeyUpdateForeground & (DS_KEY_PUBLISH | DS_KEY_REPUBLISH | DS_KEY_UNPUBLISH)) {
|
|
if (pIniPrinter->DsKeyUpdateForeground & DS_KEY_PUBLISH) {
|
|
pIniPrinter->dwAction |= DSPRINT_PUBLISH;
|
|
} else if (pIniPrinter->DsKeyUpdateForeground & DS_KEY_REPUBLISH) {
|
|
pIniPrinter->dwAction |= DSPRINT_REPUBLISH;
|
|
} else if (pIniPrinter->DsKeyUpdateForeground & DS_KEY_UNPUBLISH) {
|
|
pIniPrinter->dwAction |= DSPRINT_UNPUBLISH;
|
|
}
|
|
|
|
pIniPrinter->DsKeyUpdateForeground &= ~(DS_KEY_PUBLISH | DS_KEY_REPUBLISH | DS_KEY_UNPUBLISH);
|
|
|
|
} else {
|
|
pIniPrinter->DsKeyUpdateForeground = 0;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// For connections, dwAction is read from registry. It is updated by
|
|
// caching code with the value on the server.
|
|
//
|
|
cbData = sizeof(pIniPrinter->dwAction);
|
|
SplRegQueryValue( hPrinterKey,
|
|
szAction,
|
|
&Type,
|
|
(LPBYTE) &pIniPrinter->dwAction,
|
|
&cbData,
|
|
pIniSpooler);
|
|
}
|
|
|
|
// Make Certain this Printers Printer directory exists
|
|
// with correct security
|
|
|
|
if ((pIniPrinter->pSpoolDir) &&
|
|
(wcscmp(pIniPrinter->pSpoolDir, szDefaultPrinterDirectory) != 0)) {
|
|
|
|
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SecurityAttributes.lpSecurityDescriptor = CreateEverybodySecurityDescriptor();
|
|
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
|
|
if (!CreateDirectory(pIniPrinter->pSpoolDir, &SecurityAttributes)) {
|
|
|
|
// Failed to Create the Directory, revert back
|
|
// to the default
|
|
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS) {
|
|
DBGMSG(DBG_WARNING, ("Could not create printer spool directory %ws %d\n",
|
|
pIniPrinter->pSpoolDir, GetLastError() ));
|
|
pIniPrinter->pSpoolDir = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
LocalFree(SecurityAttributes.lpSecurityDescriptor);
|
|
}
|
|
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szShare,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler) == ERROR_SUCCESS)
|
|
|
|
pIniPrinter->pShareName = AllocSplStr(szData);
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
dwLastError = SplRegQueryValue( hPrinterKey,
|
|
szPort,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if ((dwLastError == ERROR_MORE_DATA) &&
|
|
(szPortData = AllocSplMem(cbData)))
|
|
{
|
|
bAllocMem = TRUE;
|
|
dwLastError = SplRegQueryValue( hPrinterKey,
|
|
szPort,
|
|
&Type,
|
|
(LPBYTE)szPortData,
|
|
&cbData,
|
|
pIniSpooler );
|
|
}
|
|
else
|
|
{
|
|
bAllocMem = FALSE;
|
|
szPortData = szData;
|
|
}
|
|
|
|
if (dwLastError == ERROR_SUCCESS)
|
|
{
|
|
if (pKeyData = CreateTokenList(szPortData)) {
|
|
|
|
if (!ValidatePortTokenList( pKeyData, pIniSpooler, TRUE, &bNoPorts)) {
|
|
|
|
LogFatalPortError(pIniSpooler, pIniPrinter->pName);
|
|
|
|
FreePortTokenList(pKeyData);
|
|
pKeyData = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If there are no ports on the printer, just log
|
|
// a warning message, but only for pooled printers.
|
|
//
|
|
if (bNoPorts && pKeyData->cTokens > 1) {
|
|
|
|
SplLogEvent( pIniSpooler,
|
|
LOG_WARNING,
|
|
MSG_NO_PORT_FOUND_FOR_PRINTER,
|
|
TRUE,
|
|
pIniPrinter->pName,
|
|
szPortData,
|
|
NULL );
|
|
}
|
|
|
|
pIniPrinter->ppIniPorts = AllocSplMem(pKeyData->cTokens * sizeof(PINIPORT));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bAllocMem)
|
|
{
|
|
FreeSplMem(szPortData);
|
|
szPortData = NULL;
|
|
}
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szPrintProcessor,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler ) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// We are trying to find the environment relative to the pIniSpooler.
|
|
// The local spooler and cluster spoolers do not share the same
|
|
// environment strucutres anymore
|
|
//
|
|
PINIENVIRONMENT pIniEnv;
|
|
|
|
if (pIniEnv = FindEnvironment(szEnvironment, pIniSpooler))
|
|
{
|
|
pIniPrinter->pIniPrintProc = FindPrintProc(szData, pIniEnv);
|
|
}
|
|
}
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szDatatype,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler ) == ERROR_SUCCESS)
|
|
|
|
pIniPrinter->pDatatype = AllocSplStr(szData);
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szDriver,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler ) == ERROR_SUCCESS) {
|
|
|
|
pIniPrinter->pIniDriver = (PINIDRIVER)FindLocalDriver(pIniSpooler, szData);
|
|
|
|
if (!pIniPrinter->pIniDriver)
|
|
{
|
|
//
|
|
// The hosting node of the cluster spooler was upgraded to Whistler.
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
pIniSpooler->dwClusNodeUpgraded &&
|
|
|
|
//
|
|
// The driver was not found in the cluster spooler, not on the cluster disk.
|
|
// We attempt to install the driver from the local spooler to our cluster
|
|
// spooler. This will get the driver files on the cluster disk.
|
|
//
|
|
AddLocalDriverToClusterSpooler(szData, pIniSpooler) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Search again for the driver that must have been added
|
|
//
|
|
pIniPrinter->pIniDriver = (PINIDRIVER)FindLocalDriver(pIniSpooler, szData);
|
|
}
|
|
}
|
|
|
|
if (!pIniPrinter->pIniDriver)
|
|
{
|
|
SplLogEvent(pLocalIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_NO_DRIVER_FOUND_FOR_PRINTER,
|
|
TRUE,
|
|
pIniPrinter->pName,
|
|
szData,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szLocation,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData,
|
|
pIniSpooler ) == ERROR_SUCCESS)
|
|
|
|
pIniPrinter->pLocation = AllocSplStr(szData);
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szDescription,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData, pIniSpooler ) == ERROR_SUCCESS)
|
|
|
|
pIniPrinter->pComment = AllocSplStr(szData);
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szParameters,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData, pIniSpooler ) == ERROR_SUCCESS)
|
|
|
|
pIniPrinter->pParameters = AllocSplStr(szData);
|
|
|
|
cbData = sizeof(szData);
|
|
*szData = (WCHAR)0;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szSepFile,
|
|
&Type,
|
|
(LPBYTE)szData,
|
|
&cbData, pIniSpooler ) == ERROR_SUCCESS)
|
|
|
|
pIniPrinter->pSepFile = AllocSplStr(szData);
|
|
|
|
cbData = sizeof(pIniPrinter->Attributes);
|
|
|
|
SplRegQueryValue( hPrinterKey,
|
|
szAttributes,
|
|
NULL,
|
|
(LPBYTE)&pIniPrinter->Attributes,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
cbData = sizeof(pIniPrinter->Status);
|
|
|
|
Status = SplRegQueryValue( hPrinterKey,
|
|
szStatus,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->Status,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
pIniPrinter->Status |= PRINTER_FROM_REG;
|
|
|
|
if ( Status == ERROR_SUCCESS ) {
|
|
|
|
pIniPrinter->Status &= ( PRINTER_PAUSED |
|
|
PRINTER_PENDING_DELETION |
|
|
PRINTER_ZOMBIE_OBJECT |
|
|
PRINTER_FROM_REG |
|
|
PRINTER_OK |
|
|
PRINTER_PENDING_CREATION );
|
|
|
|
} else {
|
|
|
|
pIniPrinter->Status |= PRINTER_PENDING_CREATION ;
|
|
|
|
}
|
|
|
|
// Half formed printers should be deleted
|
|
// before they cause us trouble
|
|
|
|
if ( pIniPrinter->Status & PRINTER_PENDING_CREATION ) {
|
|
|
|
pIniPrinter->Status |= PRINTER_PENDING_DELETION ;
|
|
|
|
}
|
|
|
|
|
|
|
|
cbData = sizeof(pIniPrinter->Priority);
|
|
|
|
SplRegQueryValue( hPrinterKey,
|
|
szPriority,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->Priority,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
cbData = sizeof(pIniPrinter->DefaultPriority);
|
|
|
|
SplRegQueryValue( hPrinterKey,
|
|
szDefaultPriority,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->DefaultPriority,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
cbData = sizeof(pIniPrinter->UntilTime);
|
|
|
|
SplRegQueryValue( hPrinterKey,
|
|
szUntilTime,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->UntilTime,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
cbData = sizeof(pIniPrinter->StartTime);
|
|
|
|
SplRegQueryValue( hPrinterKey,
|
|
szStartTime,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->StartTime,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
cbData = sizeof(pIniPrinter->dnsTimeout);
|
|
|
|
if ( SplRegQueryValue( hPrinterKey,
|
|
szDNSTimeout,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->dnsTimeout,
|
|
&cbData,
|
|
pIniSpooler ) != ERROR_SUCCESS ) {
|
|
|
|
pIniPrinter->dnsTimeout = DEFAULT_DNS_TIMEOUT;
|
|
}
|
|
|
|
cbData = sizeof(pIniPrinter->txTimeout);
|
|
|
|
if ( SplRegQueryValue( hPrinterKey,
|
|
szTXTimeout,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->txTimeout,
|
|
&cbData,
|
|
pIniSpooler ) != ERROR_SUCCESS ) {
|
|
|
|
pIniPrinter->txTimeout = DEFAULT_TX_TIMEOUT;
|
|
}
|
|
|
|
cbData = sizeof( pIniPrinter->cChangeID ) ;
|
|
|
|
if ( SplRegQueryValue( hPrinterKey,
|
|
szTimeLastChange,
|
|
&Type,
|
|
(LPBYTE)&pIniPrinter->cChangeID,
|
|
&cbData,
|
|
pIniSpooler ) != ERROR_SUCCESS ) {
|
|
|
|
// Current Registry Doesn't have a UniqueID
|
|
// Make sure one gets written
|
|
|
|
bUpdateRegistryForThisPrinter = TRUE;
|
|
|
|
}
|
|
|
|
pIniPrinter->dwPrivateFlag = 0;
|
|
pIniPrinter->cbDevMode = 0;
|
|
pIniPrinter->pDevMode = NULL;
|
|
|
|
if (SplRegQueryValue( hPrinterKey,
|
|
szDevMode,
|
|
&Type,
|
|
NULL,
|
|
&pIniPrinter->cbDevMode,
|
|
pIniSpooler ) == ERROR_SUCCESS) {
|
|
|
|
if (pIniPrinter->cbDevMode) {
|
|
|
|
pIniPrinter->pDevMode = AllocSplMem(pIniPrinter->cbDevMode);
|
|
|
|
SplRegQueryValue( hPrinterKey,
|
|
szDevMode,
|
|
&Type,
|
|
(LPBYTE)pIniPrinter->pDevMode,
|
|
&pIniPrinter->cbDevMode,
|
|
pIniSpooler );
|
|
}
|
|
}
|
|
|
|
//
|
|
// A Provider might want to Read Extra Data from Registry
|
|
//
|
|
|
|
|
|
if ( pIniSpooler->pfnReadRegistryExtra != NULL ) {
|
|
|
|
pIniPrinter->pExtraData = (LPBYTE)(*pIniSpooler->pfnReadRegistryExtra)(hPrinterKey);
|
|
|
|
}
|
|
|
|
/* SECURITY */
|
|
|
|
Status = SplRegQueryValue( hPrinterKey,
|
|
szSecurity,
|
|
NULL,
|
|
NULL,
|
|
&cbSecurity,
|
|
pIniSpooler );
|
|
|
|
if ((Status == ERROR_MORE_DATA) || (Status == ERROR_SUCCESS)) {
|
|
|
|
/* Use the process' heap to allocate security descriptors,
|
|
* so that they can be passed to the security API, which
|
|
* may need to reallocate them.
|
|
*/
|
|
if (pIniPrinter->pSecurityDescriptor =
|
|
LocalAlloc(0, cbSecurity)) {
|
|
|
|
if (Status = SplRegQueryValue( hPrinterKey,
|
|
szSecurity,
|
|
NULL,
|
|
pIniPrinter->pSecurityDescriptor,
|
|
&cbSecurity,
|
|
pIniSpooler ) != ERROR_SUCCESS) {
|
|
|
|
LocalFree(pIniPrinter->pSecurityDescriptor);
|
|
|
|
pIniPrinter->pSecurityDescriptor = NULL;
|
|
|
|
DBGMSG( DBG_WARNING,
|
|
( "RegQueryValue returned %d on Permissions for %ws (%ws)\n",
|
|
Status,
|
|
pIniPrinter->pName ?
|
|
pIniPrinter->pName :
|
|
szNull,
|
|
PrinterName) );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
pIniPrinter->pSecurityDescriptor = NULL;
|
|
|
|
DBGMSG( DBG_WARNING,
|
|
( "RegQueryValue (2) returned %d on Permissions for %ws (%ws)\n",
|
|
Status,
|
|
pIniPrinter->pName ?
|
|
pIniPrinter->pName :
|
|
szNull,
|
|
PrinterName) );
|
|
}
|
|
|
|
pIniPrinter->MasqCache.bThreadRunning = FALSE;
|
|
pIniPrinter->MasqCache.cJobs = 0;
|
|
pIniPrinter->MasqCache.Status = 0;
|
|
pIniPrinter->MasqCache.dwError = ERROR_SUCCESS;
|
|
|
|
/* END SECURITY */
|
|
|
|
if ( pIniPrinter->pName &&
|
|
pIniPrinter->pShareName &&
|
|
pKeyData &&
|
|
pIniPrinter->ppIniPorts &&
|
|
pIniPrinter->pIniPrintProc &&
|
|
pIniPrinter->pIniDriver &&
|
|
pIniPrinter->pLocation &&
|
|
pIniPrinter->pComment &&
|
|
pIniPrinter->pSecurityDescriptor
|
|
#if DBG
|
|
&& ( IsValidSecurityDescriptor (pIniPrinter->pSecurityDescriptor)
|
|
? TRUE
|
|
: (DBGMSG( DBG_SECURITY,
|
|
( "The security descriptor for %ws (%ws) is invalid\n",
|
|
pIniPrinter->pName ?
|
|
pIniPrinter->pName :
|
|
szNull,
|
|
PrinterName)), /* (sequential evaluation) */
|
|
FALSE) )
|
|
#endif /* DBG */
|
|
) {
|
|
|
|
|
|
pIniPrinter->pIniFirstJob = pIniPrinter->pIniLastJob = NULL;
|
|
|
|
pIniPrinter->pIniPrintProc->cRef++;
|
|
|
|
INCDRIVERREF( pIniPrinter->pIniDriver );
|
|
|
|
for (i=0; i<pKeyData->cTokens; i++) {
|
|
|
|
pIniPort = (PINIPORT)pKeyData->pTokens[i];
|
|
pIniPrinter->ppIniPorts[i] = pIniPort;
|
|
|
|
pIniPort->ppIniPrinter =
|
|
|
|
ReallocSplMem(pIniPort->ppIniPrinter,
|
|
pIniPort->cPrinters *
|
|
sizeof(pIniPort->ppIniPrinter),
|
|
(pIniPort->cPrinters+1) *
|
|
sizeof(pIniPort->ppIniPrinter));
|
|
|
|
if (!pIniPort->ppIniPrinter) {
|
|
DBGMSG(DBG_WARNING, ("Failed to allocate memory for printer info\n." ));
|
|
}
|
|
|
|
pIniPort->ppIniPrinter[pIniPort->cPrinters] =
|
|
pIniPrinter;
|
|
|
|
//
|
|
// With the new monitors localspl does the
|
|
// redirection for LPT, COM ports
|
|
//
|
|
if ( !pIniPort->cPrinters++ )
|
|
CreateRedirectionThread(pIniPort);
|
|
|
|
}
|
|
|
|
|
|
pIniPrinter->cPorts = pKeyData->cTokens;
|
|
pIniPrinter->Priority =
|
|
pIniPrinter->Priority ? pIniPrinter->Priority
|
|
: DEF_PRIORITY;
|
|
|
|
if ((pIniPrinter->Attributes &
|
|
(PRINTER_ATTRIBUTE_QUEUED | PRINTER_ATTRIBUTE_DIRECT)) ==
|
|
(PRINTER_ATTRIBUTE_QUEUED | PRINTER_ATTRIBUTE_DIRECT))
|
|
|
|
pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_DIRECT;
|
|
|
|
//
|
|
// I f we're upgrading, fix the NT4 bug that broke Masc printers.
|
|
// UpdateChangeID is passed into us as dwUpgradeFlag, so it determines
|
|
// if we're in an upgrade state.
|
|
//
|
|
// The rules: Printer name starts with \\
|
|
// Port Name starts with \\
|
|
// It's on a local IniSpooler.
|
|
// The NETWORK and LOCAL bits are not set.
|
|
//
|
|
// If all the rules are met, set the NETWORK and LOCAL bits so the printer
|
|
// will behave properly.
|
|
//
|
|
if ( UpdateChangeID && (wcslen(pIniPrinter->pName) > 2) && pIniPrinter->cPorts &&
|
|
(*pIniPrinter->ppIniPorts)->pName && (wcslen((*pIniPrinter->ppIniPorts)->pName) > 2) &&
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL)
|
|
{
|
|
WCHAR * pNameStr = pIniPrinter->pName;
|
|
WCHAR * pPortStr = (*pIniPrinter->ppIniPorts)->pName;
|
|
DWORD MascAttr = (PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL);
|
|
|
|
if ((pNameStr[0] == L'\\') && (pNameStr[1] == L'\\') &&
|
|
(pPortStr[0] == L'\\') && (pPortStr[1] == L'\\') &&
|
|
((pIniPrinter->Attributes & MascAttr) == 0))
|
|
{
|
|
pIniPrinter->Attributes |= MascAttr;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If there were no ports for the printer, we set the state to
|
|
// work offline, but not if this was a masque printer. (In
|
|
// which case PRINTER_ATTRIBUTE_NETWORK is also set).
|
|
//
|
|
if (bNoPorts && !(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_NETWORK)) {
|
|
pIniPrinter->Attributes |= PRINTER_ATTRIBUTE_WORK_OFFLINE;
|
|
}
|
|
|
|
pIniPrinter->pNext = pIniSpooler->pIniPrinter;
|
|
|
|
pIniPrinter->pIniSpooler = pIniSpooler;
|
|
|
|
pIniSpooler->pIniPrinter = pIniPrinter;
|
|
|
|
if ( bUpdateRegistryForThisPrinter ) {
|
|
|
|
UpdatePrinterIni( pIniPrinter , UPDATE_CHANGEID );
|
|
bUpdateRegistryForThisPrinter = UpdateChangeID;
|
|
}
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARNING,
|
|
( "Initialization of printer failed:\
|
|
\n\tpPrinterName:\t%ws\
|
|
\n\tKeyName:\t%ws\
|
|
\n\tpShareName:\t%ws\
|
|
\n\tpKeyData:\t%08x\
|
|
\n\tpIniPrintProc:\t%08x",
|
|
pIniPrinter->pName ? pIniPrinter->pName : szNull,
|
|
PrinterName,
|
|
pIniPrinter->pShareName ? pIniPrinter->pShareName : szNull ,
|
|
pKeyData,
|
|
pIniPrinter->pIniPrintProc ) );
|
|
|
|
/* Do this in two lumps, because otherwise NTSD might crash.
|
|
* (Raid bug #10650)
|
|
*/
|
|
DBGMSG( DBG_WARNING,
|
|
( " \n\tpIniDriver:\t%08x\
|
|
\n\tpLocation:\t%ws\
|
|
\n\tpComment:\t%ws\
|
|
\n\tpSecurity:\t%08x\
|
|
\n\tStatus:\t\t%08x %s\n\n",
|
|
pIniPrinter->pIniDriver,
|
|
pIniPrinter->pLocation ? pIniPrinter->pLocation : szNull,
|
|
pIniPrinter->pComment ? pIniPrinter->pComment : szNull,
|
|
pIniPrinter->pSecurityDescriptor,
|
|
pIniPrinter->Status,
|
|
( pIniPrinter->Status & PRINTER_PENDING_DELETION
|
|
? "Pending deletion" : "" ) ) );
|
|
|
|
FreeStructurePointers((LPBYTE)pIniPrinter,
|
|
NULL,
|
|
IniPrinterOffsets);
|
|
|
|
if (pIniPrinter->pSecurityDescriptor) {
|
|
LocalFree(pIniPrinter->pSecurityDescriptor);
|
|
pIniPrinter->pSecurityDescriptor = NULL;
|
|
}
|
|
|
|
if (( pIniSpooler->pfnFreePrinterExtra != NULL ) &&
|
|
( pIniPrinter->pExtraData != NULL )) {
|
|
|
|
(*pIniSpooler->pfnFreePrinterExtra)( pIniPrinter->pExtraData );
|
|
|
|
}
|
|
|
|
//
|
|
// Reference count the pIniSpooler.
|
|
//
|
|
DECSPOOLERREF( pIniSpooler );
|
|
|
|
FreeSplMem(pIniPrinter);
|
|
}
|
|
|
|
FreePortTokenList(pKeyData);
|
|
pKeyData = NULL;
|
|
}
|
|
SplRegCloseKey( hPrinterKey, pIniSpooler );
|
|
}
|
|
|
|
cPrinters++;
|
|
|
|
cbData = COUNTOF(PrinterName);
|
|
}
|
|
|
|
if ( pIniSpooler->SpoolerFlags & SPL_TYPE_LOCAL) {
|
|
|
|
WCHAR szFilename[MAX_PATH];
|
|
HRESULT RetVal;
|
|
|
|
szFilename[0] = L'\0';
|
|
|
|
//
|
|
// FP Change
|
|
// Initialize the File pool.
|
|
//
|
|
if (pIniSpooler->hFilePool == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (GetPrinterDirectory(NULL, FALSE, szFilename, MAX_PATH, pIniSpooler))
|
|
{
|
|
RetVal = CreateFilePool(
|
|
&pIniSpooler->hFilePool,
|
|
szFilename,
|
|
L"FP",
|
|
L".SPL",
|
|
L".SHD",
|
|
POOL_TIMEOUT,
|
|
MAX_POOL_FILES
|
|
);
|
|
if (FAILED(RetVal))
|
|
{
|
|
DBGMSG( DBG_WARN,
|
|
( "SplCreateSpooler: Initialization of FilePool Failed %x\n",
|
|
RetVal ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGMSG( DBG_WARN, ("CreateFilePool: GetPrinterDirectory Failed\n"));
|
|
}
|
|
}
|
|
|
|
// Read .SHD/.SPL files from common printer directory
|
|
ProcessShadowJobs( NULL, pIniSpooler );
|
|
|
|
// If any printer has a separate Printer directory process them
|
|
// also
|
|
|
|
if( GetPrinterDirectory(NULL, FALSE, szData, COUNTOF(szData), pIniSpooler) ) {
|
|
|
|
for ( pIniPrinter = pIniSpooler->pIniPrinter;
|
|
pIniPrinter;
|
|
pIniPrinter = pIniPrinter->pNext ) {
|
|
|
|
if ((pIniPrinter->pSpoolDir != NULL) &&
|
|
(_wcsicmp(szData, pIniPrinter->pSpoolDir) != 0)) {
|
|
|
|
ProcessShadowJobs(pIniPrinter, pIniSpooler);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UpdateReferencesToChainedJobs( pIniSpooler );
|
|
|
|
// Finally, go through all Printers looking for PENDING_DELETION
|
|
// if there are no jobs for that Printer, then we can delete it now
|
|
|
|
CleanupDeletedPrinters(pIniSpooler);
|
|
|
|
DBGMSG( DBG_TRACE, ("BuildPrinterInfo returned\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* InitializePrintProcessor
|
|
*
|
|
* Allocates and initialises an INIPRINTPROC structure for the specified
|
|
* print processor and environment.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* hLibrary - Handle to a previously loaded library ,
|
|
*
|
|
* pIniEnvironment - Data structure for the requested environment
|
|
* The pIniPrintProc field is initialised with the chain of print
|
|
* processor structures
|
|
*
|
|
* pPrintProcessorName - The Print Processor name e.g. WinPrint
|
|
*
|
|
* pDLLName - The DLL name, e.g. WINPRINT
|
|
*
|
|
* Returns:
|
|
*
|
|
* The allocated PiniPrintProc if no error was detected, otherwise FALSE.
|
|
*
|
|
*
|
|
*/
|
|
PINIPRINTPROC
|
|
InitializePrintProcessor(
|
|
HINSTANCE hLibrary,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
LPWSTR pPrintProcessorName,
|
|
LPWSTR pDLLName
|
|
)
|
|
{
|
|
DWORD cb, cbNeeded, cReturned;
|
|
PINIPRINTPROC pIniPrintProc;
|
|
BOOL rc;
|
|
DWORD Error;
|
|
SIZE_T cchString = 0;
|
|
|
|
DBGMSG(DBG_TRACE, ("InitializePrintProcessor( %08x, %08x ,%ws, %ws)\n",
|
|
hLibrary, pPrintProcessorName, pPrintProcessorName, pDLLName));
|
|
|
|
|
|
cb = sizeof(INIPRINTPROC) +
|
|
wcslen(pPrintProcessorName)*sizeof(WCHAR) +
|
|
sizeof(WCHAR) +
|
|
wcslen(pDLLName)*sizeof(WCHAR) +
|
|
sizeof(WCHAR);
|
|
|
|
if (!(pIniPrintProc = (PINIPRINTPROC)AllocSplMem(cb))) {
|
|
|
|
DBGMSG(DBG_WARNING, ("Failed to allocate %d bytes for print processor\n.", cb));
|
|
return NULL;
|
|
}
|
|
|
|
__try {
|
|
|
|
InitializeCriticalSection(&pIniPrintProc->CriticalSection);
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
FreeSplMem(pIniPrintProc);
|
|
SetLastError(GetExceptionCode());
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Typical strings used to build the full path of the DLL:
|
|
*
|
|
* pPathName = C:\NT\SYSTEM32\SPOOL\PRTPROCS
|
|
* pEnvironment = W32X86
|
|
* pDLLName = WINPRINT.DLL
|
|
*/
|
|
|
|
pIniPrintProc->hLibrary = hLibrary;
|
|
|
|
if (!pIniPrintProc->hLibrary) {
|
|
|
|
DeleteCriticalSection(&pIniPrintProc->CriticalSection);
|
|
FreeSplMem(pIniPrintProc);
|
|
DBGMSG(DBG_WARNING, ("Failed to LoadLibrary(%ws)\n", pDLLName));
|
|
return NULL;
|
|
}
|
|
|
|
pIniPrintProc->EnumDatatypes = (pfnEnumDatatypes) GetProcAddress(pIniPrintProc->hLibrary,
|
|
"EnumPrintProcessorDatatypesW");
|
|
|
|
if (!pIniPrintProc->EnumDatatypes) {
|
|
|
|
DBGMSG(DBG_WARNING, ("Failed to GetProcAddress(EnumDatatypes)\n"));
|
|
DeleteCriticalSection(&pIniPrintProc->CriticalSection);
|
|
FreeLibrary(pIniPrintProc->hLibrary);
|
|
FreeSplMem(pIniPrintProc);
|
|
return NULL;
|
|
}
|
|
|
|
rc = (*pIniPrintProc->EnumDatatypes)(NULL, pPrintProcessorName, 1, NULL, 0, &cbNeeded, &cReturned);
|
|
|
|
if (!rc && ((Error = GetLastError()) == ERROR_INSUFFICIENT_BUFFER)) {
|
|
|
|
pIniPrintProc->cbDatatypes = cbNeeded;
|
|
|
|
if (!(pIniPrintProc->pDatatypes = AllocSplMem(cbNeeded))) {
|
|
|
|
DBGMSG(DBG_WARNING, ("Failed to allocate %d bytes for print proc datatypes\n.", cbNeeded));
|
|
DeleteCriticalSection(&pIniPrintProc->CriticalSection);
|
|
FreeLibrary(pIniPrintProc->hLibrary);
|
|
FreeSplMem(pIniPrintProc);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(*pIniPrintProc->EnumDatatypes)(NULL, pPrintProcessorName, 1,
|
|
(LPBYTE)pIniPrintProc->pDatatypes,
|
|
cbNeeded, &cbNeeded,
|
|
&pIniPrintProc->cDatatypes)) {
|
|
|
|
Error = GetLastError();
|
|
DBGMSG(DBG_WARNING, ("EnumPrintProcessorDatatypes(%ws) failed: Error %d\n",
|
|
pPrintProcessorName, Error));
|
|
}
|
|
|
|
} else if(rc) {
|
|
|
|
DBGMSG(DBG_WARNING, ("EnumPrintProcessorDatatypes(%ws) returned no data\n",
|
|
pPrintProcessorName));
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING, ("EnumPrintProcessorDatatypes(%ws) failed: Error %d\n",
|
|
pPrintProcessorName, Error));
|
|
}
|
|
|
|
pIniPrintProc->Install = (pfnInstallPrintProcessor) GetProcAddress(pIniPrintProc->hLibrary,
|
|
"InstallPrintProcessor");
|
|
|
|
pIniPrintProc->Open = (pfnOpenPrintProcessor) GetProcAddress(pIniPrintProc->hLibrary,
|
|
"OpenPrintProcessor");
|
|
|
|
pIniPrintProc->Print = (pfnPrintDocOnPrintProcessor) GetProcAddress(pIniPrintProc->hLibrary,
|
|
"PrintDocumentOnPrintProcessor");
|
|
|
|
pIniPrintProc->Close = (pfnClosePrintProcessor) GetProcAddress(pIniPrintProc->hLibrary,
|
|
"ClosePrintProcessor");
|
|
|
|
pIniPrintProc->Control = (pfnControlPrintProcessor) GetProcAddress(pIniPrintProc->hLibrary,
|
|
"ControlPrintProcessor");
|
|
|
|
pIniPrintProc->GetPrintProcCaps = (pfnGetPrintProcCaps) GetProcAddress(pIniPrintProc->hLibrary,
|
|
"GetPrintProcessorCapabilities");
|
|
|
|
/* pName and pDLLName are contiguous with the INIPRINTPROC structure:
|
|
*/
|
|
pIniPrintProc->pName = (LPWSTR)(pIniPrintProc+1);
|
|
|
|
StringCbCopy(pIniPrintProc->pName, cb - sizeof(*pIniPrintProc), pPrintProcessorName);
|
|
|
|
cchString = wcslen(pIniPrintProc->pName) + 1;
|
|
|
|
pIniPrintProc->pDLLName = (LPWSTR)(pIniPrintProc->pName + cchString);
|
|
|
|
StringCbCopy(pIniPrintProc->pDLLName, cb - sizeof(*pIniPrintProc) - cchString*sizeof(WCHAR), pDLLName);
|
|
|
|
pIniPrintProc->signature = IPP_SIGNATURE;
|
|
|
|
pIniPrintProc->pNext = pIniEnvironment->pIniPrintProc;
|
|
|
|
pIniEnvironment->pIniPrintProc = pIniPrintProc;
|
|
|
|
return pIniPrintProc;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
InitializeLocalPrintProcessor
|
|
|
|
Routine Description:
|
|
|
|
We start up the local print processor, we need to bump the reference count
|
|
on it library instance so that the cleanup code does not accidentally
|
|
unload localspl.dll while it is running.
|
|
|
|
Arguments:
|
|
|
|
pIniEnvironment - The environment to add the print processor to.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT.
|
|
|
|
--*/
|
|
HRESULT
|
|
InitializeLocalPrintProcessor(
|
|
IN PINIENVIRONMENT pIniEnvironment
|
|
)
|
|
{
|
|
HRESULT hRetval = E_FAIL;
|
|
HINSTANCE hLocalSpl = NULL;
|
|
WCHAR szFilename[MAX_PATH];
|
|
|
|
hRetval = pIniEnvironment ? S_OK : HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
|
|
if (SUCCEEDED(hRetval))
|
|
{
|
|
hRetval = GetModuleFileName(hInst, szFilename, COUNTOF(szFilename)) ? S_OK : GetLastErrorAsHResult();
|
|
}
|
|
|
|
if (SUCCEEDED(hRetval))
|
|
{
|
|
hLocalSpl = LoadLibrary(szFilename);
|
|
|
|
hRetval = hLocalSpl ? S_OK : GetLastErrorAsHResult();
|
|
}
|
|
|
|
if (SUCCEEDED(hRetval))
|
|
{
|
|
hRetval = InitializePrintProcessor(hLocalSpl, pIniEnvironment, L"WinPrint", L"localspl.dll") ? S_OK : GetLastErrorAsHResult();
|
|
|
|
if (SUCCEEDED(hRetval))
|
|
{
|
|
hLocalSpl = NULL;
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hLocalSpl);
|
|
|
|
return hRetval;
|
|
}
|
|
|
|
|
|
/* LoadPrintProcessor
|
|
*
|
|
* Loads the DLL for the required Print Processor and then calls
|
|
* InitializePrintProcessor for the necesary allocation and
|
|
* initialization of an INIPRINTPROC structure for the specified
|
|
* print processor and environment.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pIniEnvironment - Data structure for the requested environment
|
|
* The pIniPrintProc field is initialised with the chain of print
|
|
* processor structures
|
|
*
|
|
* pPrintProcessorName - The Print Processor name e.g. WinPrint
|
|
*
|
|
* pDLLName - The DLL name, e.g. WINPRINT
|
|
*
|
|
* pInitSpooler
|
|
*
|
|
* Returns:
|
|
*
|
|
* PINIPRINTPROC if no error was detected, otherwise NULL.
|
|
*
|
|
*
|
|
*/
|
|
PINIPRINTPROC
|
|
LoadPrintProcessor(
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
LPWSTR pPrintProcessorName,
|
|
LPWSTR pDLLName,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
WCHAR string[MAX_PATH];
|
|
DWORD dwOldErrMode = 0;
|
|
HINSTANCE hLibrary;
|
|
DWORD MinorVersion = 0;
|
|
DWORD MajorVersion = 0;
|
|
PINIPRINTPROC pIniProc;
|
|
|
|
|
|
DBGMSG(DBG_TRACE, ("LoadPrintProcessor( %08x, %ws, %ws )\n", pIniEnvironment, pPrintProcessorName, pDLLName));
|
|
|
|
|
|
/* Originally:
|
|
* Typical strings used to build the full path of the DLL:
|
|
*
|
|
* pPathName = C:\NT\SYSTEM32\SPOOL\PRTPROCS
|
|
* pEnvironment = W32X86
|
|
* pDLLName = WINPRINT.DLL
|
|
* But after merging winprint and localspl , e.g. of setting
|
|
* pPathName = C:\NT\SYSTEM32
|
|
* pDllName = LOCALSPL.DLL
|
|
*/
|
|
|
|
if( StrNCatBuff ( string,
|
|
COUNTOF(string),
|
|
pIniSpooler->pDir,
|
|
L"\\PRTPROCS\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pDLLName,
|
|
NULL) != ERROR_SUCCESS) {
|
|
|
|
SetLastError(ERROR_BAD_PATHNAME);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
dwOldErrMode = SetErrorMode( SEM_FAILCRITICALERRORS );
|
|
|
|
hLibrary = LoadLibrary(string);
|
|
|
|
//
|
|
// We are a cluster spooler and we cannot find the library for a print
|
|
// processor. We will try to copy the print processor from the cluster.
|
|
// disk.
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
!hLibrary &&
|
|
GetLastError() == ERROR_MOD_NOT_FOUND)
|
|
{
|
|
WCHAR szSourceFile[MAX_PATH];
|
|
WCHAR szDestDir[MAX_PATH];
|
|
DWORD dwError;
|
|
|
|
DBGMSG(DBG_CLUSTER, ("LoadPrintProcessor File not found "TSTR"\n", string));
|
|
|
|
if ((dwError = StrNCatBuff(szDestDir,
|
|
COUNTOF(szDestDir),
|
|
pIniSpooler->pDir,
|
|
L"\\PRTPROCS\\",
|
|
pIniEnvironment->pDirectory,
|
|
NULL)) == ERROR_SUCCESS &&
|
|
(dwError = StrNCatBuff(szSourceFile,
|
|
COUNTOF(szSourceFile),
|
|
pIniSpooler->pszClusResDriveLetter,
|
|
L"\\",
|
|
szClusterDriverRoot,
|
|
L"\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pDLLName,
|
|
NULL)) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Make sure the destination directory exists
|
|
//
|
|
CreateCompleteDirectory(szDestDir);
|
|
|
|
//
|
|
// Try to copy the print proc file from the cluster disk
|
|
//
|
|
if (CopyFile(szSourceFile, string, FALSE) &&
|
|
(hLibrary = LoadLibrary(string)))
|
|
{
|
|
DBGMSG(DBG_CLUSTER, ("LoadPrintProc copied "TSTR" to "TSTR"\n", szSourceFile, string));
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hLibrary)
|
|
{
|
|
if (!GetBinaryVersion(string, &MajorVersion, &MinorVersion))
|
|
{
|
|
DBGMSG(DBG_ERROR, ("GetBinaryVersion failed. Error %u\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
SetErrorMode( dwOldErrMode ); /* Restore error mode */
|
|
|
|
pIniProc = InitializePrintProcessor(hLibrary,
|
|
pIniEnvironment,
|
|
pPrintProcessorName,
|
|
pDLLName);
|
|
|
|
if (pIniProc)
|
|
{
|
|
pIniProc->FileMajorVersion = MajorVersion;
|
|
pIniProc->FileMinorVersion = MinorVersion;
|
|
}
|
|
|
|
return pIniProc;
|
|
}
|
|
|
|
|
|
/*
|
|
Current Directory == c:\winspool\drivers
|
|
pFindFileData->cFileName == win32.x86
|
|
*/
|
|
|
|
|
|
/* BuildEnvironmentInfo
|
|
*
|
|
*
|
|
* The registry tree for Environments is as follows:
|
|
*
|
|
* Print
|
|
* ³
|
|
* ÃÄ Environments
|
|
* ³ ³
|
|
* ³ ÃÄ Windows NT x86
|
|
* ³ ³ ³
|
|
* ³ ³ ÃÄ Drivers
|
|
* ³ ³ ³ ³
|
|
* ³ ³ ³ ÀÄ Agfa Compugraphic Genics (e.g.)
|
|
* ³ ³ ³
|
|
* ³ ³ ³ :
|
|
* ³ ³ ³ :
|
|
* ³ ³ ³
|
|
* ³ ³ ÀÄ Print Processors
|
|
* ³ ³ ³
|
|
* ³ ³ ÀÄ WINPRINT : WINPRINT.DLL (e.g.)
|
|
* ³ ³
|
|
* ³ ³ :
|
|
* ³ ³ :
|
|
* ³ ³
|
|
* ³ ÀÄ Windows NT R4000
|
|
* ³
|
|
* ÀÄ Printers
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
BOOL
|
|
BuildEnvironmentInfo(
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
WCHAR Environment[MAX_PATH];
|
|
WCHAR szData[MAX_PATH];
|
|
DWORD cbData, cb;
|
|
DWORD cchBuffer = COUNTOF(Environment);
|
|
DWORD cEnvironments=0, Type;
|
|
HKEY hEnvironmentsKey, hEnvironmentKey;
|
|
LPWSTR pDirectory;
|
|
PINIENVIRONMENT pIniEnvironment;
|
|
LONG Status;
|
|
|
|
//
|
|
// The local spooler and cluster spooler have each different places
|
|
// where they store information about environments
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_CLUSTER_REG)
|
|
{
|
|
Status = SplRegOpenKey(pIniSpooler->hckRoot,
|
|
pIniSpooler->pszRegistryEnvironments,
|
|
KEY_READ,
|
|
&hEnvironmentsKey,
|
|
pIniSpooler);
|
|
}
|
|
else
|
|
{
|
|
Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, pIniSpooler->pszRegistryEnvironments, 0, KEY_READ, &hEnvironmentsKey);
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
DBGMSG(DBG_CLUSTER, ("RegOpenKey of %ws Failed: Error = %d\n", szEnvironmentsKey, Status));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Enumerate the subkeys of "Environment".
|
|
// This will give us "Windows NT x86", "Windows NT R4000", * and maybe others:
|
|
//
|
|
while (SplRegEnumKey(hEnvironmentsKey, cEnvironments, Environment, &cchBuffer, NULL, pIniSpooler) == ERROR_SUCCESS) {
|
|
|
|
DBGMSG(DBG_CLUSTER, ("Found environment "TSTR"\n", Environment));
|
|
|
|
//
|
|
// For each environment found, create or open the key:
|
|
//
|
|
if (SplRegCreateKey(hEnvironmentsKey, Environment, 0, KEY_READ, NULL, &hEnvironmentKey, NULL, pIniSpooler) == ERROR_SUCCESS) {
|
|
|
|
cbData = sizeof(szData);
|
|
|
|
pDirectory = NULL;
|
|
|
|
//
|
|
// Find the name of the directory associated with this environment,
|
|
// e.g. "Windows NT x86" -> "W32X86"
|
|
// "Windows NT R4000" -> "W32MIPS"
|
|
//
|
|
if (RegGetString(hEnvironmentKey, szDirectory, &pDirectory, &cbData, &Status, TRUE, pIniSpooler)) {
|
|
|
|
DBGMSG(DBG_CLUSTER, ("BuildEnvInfo pDirectory "TSTR"\n", pDirectory));
|
|
}
|
|
|
|
cb = sizeof(INIENVIRONMENT) + wcslen(Environment)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
|
|
if (pDirectory && (pIniEnvironment=AllocSplMem(cb))) {
|
|
|
|
StringCbCopy((LPWSTR)(pIniEnvironment+1), cb - sizeof(*pIniEnvironment), Environment);
|
|
|
|
pIniEnvironment->pName = (LPWSTR)(pIniEnvironment+1);
|
|
pIniEnvironment->signature = IE_SIGNATURE;
|
|
pIniEnvironment->pDirectory = pDirectory;
|
|
pIniEnvironment->pNext = pIniSpooler->pIniEnvironment;
|
|
pIniSpooler->pIniEnvironment = pIniEnvironment;
|
|
pIniEnvironment->pIniVersion = NULL;
|
|
pIniEnvironment->pIniPrintProc = NULL;
|
|
pIniEnvironment->pIniSpooler = pIniSpooler;
|
|
if(!_wcsicmp(Environment,LOCAL_ENVIRONMENT)) {
|
|
|
|
(VOID)InitializeLocalPrintProcessor(pIniEnvironment);
|
|
}
|
|
|
|
BuildDriverInfo(hEnvironmentKey, pIniEnvironment, pIniSpooler);
|
|
BuildPrintProcInfo (hEnvironmentKey, pIniEnvironment, pIniSpooler);
|
|
|
|
DBGMSG(DBG_TRACE, ("Data for environment %ws created:\
|
|
\n\tpDirectory: %ws\n",
|
|
Environment,
|
|
pDirectory));
|
|
}
|
|
|
|
SplRegCloseKey(hEnvironmentKey, pIniSpooler);
|
|
}
|
|
|
|
cEnvironments++;
|
|
|
|
cchBuffer = COUNTOF(Environment);
|
|
}
|
|
|
|
SplRegCloseKey(hEnvironmentsKey, pIniSpooler);
|
|
|
|
if (!(pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER))
|
|
{
|
|
pThisEnvironment = FindEnvironment(szEnvironment, pIniSpooler);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BuildDriverInfo(
|
|
HKEY hKeyEnvironment,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates driver and version ini structures based on environment.
|
|
|
|
Arguments:
|
|
|
|
hKeyEnvironment - Registry key specifying environment.
|
|
|
|
pIniEnvironment - Structure for environemnt. Will be initialized
|
|
to hold pIniVersions and pIniDrivers.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success,
|
|
False - Failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR szVersionName[MAX_PATH];
|
|
DWORD cchBuffer;
|
|
DWORD cVersion;
|
|
HKEY hDriversKey;
|
|
DWORD Status;
|
|
PINIVERSION pIniVersionList, pIniVersion;
|
|
|
|
Status = SplRegCreateKey(hKeyEnvironment,szDriversKey, 0, KEY_READ, NULL, &hDriversKey, NULL, pIniSpooler);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
DBGMSG(DBG_ERROR, ("RegOpenKeyEx of %ws failed: Error = %d\n", szDriversKey, Status));
|
|
return FALSE;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE,("RegCreateKeyEx succeeded in BuildDriverInfo\n"));
|
|
|
|
for( pIniVersionList = NULL, cVersion = 0;
|
|
|
|
cchBuffer = COUNTOF( szVersionName ),
|
|
SplRegEnumKey(hDriversKey, cVersion, szVersionName, &cchBuffer, NULL, pIniSpooler) == ERROR_SUCCESS;
|
|
|
|
cVersion++ ){
|
|
|
|
DBGMSG(DBG_TRACE,("Version found %ws\n", szVersionName));
|
|
|
|
//
|
|
// If it isn't a version -- remember we look for current
|
|
// drivers before we upgrade, just move on.
|
|
//
|
|
if (_wcsnicmp(szVersionName, L"Version-", 8)) {
|
|
continue;
|
|
}
|
|
|
|
pIniVersion = GetVersionDrivers( hDriversKey,
|
|
szVersionName,
|
|
pIniSpooler,
|
|
pIniEnvironment );
|
|
|
|
if( pIniVersion ){
|
|
InsertVersionList( &pIniVersionList, pIniVersion );
|
|
}
|
|
}
|
|
SplRegCloseKey(hDriversKey, pIniSpooler);
|
|
pIniEnvironment->pIniVersion = pIniVersionList;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* BuildPrintProcInfo
|
|
*
|
|
* Opens the printproc subkey for the specified environment and enumerates
|
|
* the print processors listed.
|
|
*
|
|
* For each print processor found, calls InitializePrintProcessor to allocate
|
|
* and inintialize a data structure.
|
|
*
|
|
* This function was adapted to use SplReg functions. Those functions are
|
|
* cluster aware.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* hKeyEnvironment - The key for the specified environment,
|
|
* used for Registry API calls.
|
|
*
|
|
* pIniEnvironment - Data structure for the environment.
|
|
* The pIniPrintProc field will be initialised to contain a chain
|
|
* of one or more print processors enumerated from the registry.
|
|
*
|
|
* Return:
|
|
*
|
|
* TRUE if operation was successful, otherwise FALSE
|
|
*/
|
|
BOOL
|
|
BuildPrintProcInfo(
|
|
HKEY hKeyEnvironment,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
WCHAR PrintProcName[MAX_PATH];
|
|
WCHAR DLLName[MAX_PATH];
|
|
DWORD cchBuffer, cbDLLName;
|
|
DWORD cPrintProcs = 0;
|
|
HKEY hPrintProcKey, hPrintProc;
|
|
DWORD Status;
|
|
PINIPRINTPROC pIniPrintProc;
|
|
|
|
if ((Status = SplRegOpenKey(hKeyEnvironment,
|
|
szPrintProcKey,
|
|
KEY_READ,
|
|
&hPrintProcKey,
|
|
pIniSpooler)) == ERROR_SUCCESS)
|
|
{
|
|
cchBuffer = COUNTOF(PrintProcName);
|
|
|
|
while (SplRegEnumKey(hPrintProcKey,
|
|
cPrintProcs,
|
|
(LPTSTR)PrintProcName,
|
|
&cchBuffer,
|
|
NULL,
|
|
pIniSpooler) == ERROR_SUCCESS)
|
|
{
|
|
DBGMSG(DBG_TRACE, ("BuildPrintProcInfo Print processor found: %ws\n", PrintProcName));
|
|
|
|
if (SplRegOpenKey(hPrintProcKey,
|
|
PrintProcName,
|
|
KEY_READ,
|
|
&hPrintProc,
|
|
pIniSpooler) == ERROR_SUCCESS)
|
|
{
|
|
cbDLLName = sizeof(DLLName);
|
|
|
|
if (SplRegQueryValue(hPrintProc,
|
|
szDriverFile,
|
|
NULL,
|
|
(LPBYTE)DLLName,
|
|
&cbDLLName,
|
|
pIniSpooler) == ERROR_SUCCESS)
|
|
{
|
|
pIniPrintProc = LoadPrintProcessor(pIniEnvironment,
|
|
PrintProcName,
|
|
DLLName,
|
|
pIniSpooler);
|
|
}
|
|
|
|
SplRegCloseKey(hPrintProc, pIniSpooler);
|
|
}
|
|
|
|
//
|
|
// Don't delete the key !! If winprint.dll was corrupt,
|
|
// then we nuke it and we are hosed since there is no UI
|
|
// to add print procs.
|
|
// We can afford to be a little slow on init, since we only
|
|
// do it once.
|
|
//
|
|
cchBuffer = COUNTOF(PrintProcName);
|
|
cPrintProcs++;
|
|
}
|
|
|
|
SplRegCloseKey(hPrintProcKey, pIniSpooler);
|
|
|
|
DBGMSG(DBG_TRACE, ("End of print processor initialization.\n"));
|
|
|
|
} else {
|
|
|
|
DBGMSG (DBG_WARNING, ("SplRegOpenKey failed: Error = %d\n", Status));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define SetOffset(Dest, Source, End) \
|
|
if (Source) { \
|
|
Dest=End; \
|
|
End+=wcslen(Source)+1; \
|
|
}
|
|
|
|
#define SetPointer(struc, off) \
|
|
if (struc->off) { \
|
|
struc->off += (ULONG_PTR)struc/sizeof(*struc->off); \
|
|
}
|
|
|
|
#define WriteString(hFile, pStr) \
|
|
if (pStr) {\
|
|
rc = WriteFile(hFile, pStr, wcslen(pStr)*sizeof(WCHAR) + \
|
|
sizeof(WCHAR), &BytesWritten, NULL); \
|
|
if (!rc) { \
|
|
DBGMSG(DBG_WARNING, ("WriteShadowJob: WriteFile failed %d\n", \
|
|
GetLastError())); \
|
|
} \
|
|
}
|
|
|
|
#define AddSize(pStr, dwSize) \
|
|
if (pStr) { \
|
|
dwSize = dwSize + (wcslen(pStr) + 1)*sizeof(WCHAR); \
|
|
}
|
|
|
|
VOID
|
|
__inline
|
|
CopyString(
|
|
IN OUT BYTE *pBuffer,
|
|
IN OUT ULONG_PTR *pdwOffset,
|
|
IN OUT SIZE_T *pcbRemaining,
|
|
IN PCWSTR pStr)
|
|
{
|
|
if (pStr) {
|
|
|
|
PWSTR pszEnd = 0;
|
|
|
|
StrCbCopyMultipleStr((LPWSTR)(pBuffer + *pdwOffset), *pcbRemaining, pStr, &pszEnd, pcbRemaining);
|
|
|
|
*pdwOffset = (pszEnd - (LPWSTR)pBuffer) * sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
WriteShadowJob(
|
|
IN PINIJOB pIniJob,
|
|
IN BOOL bLeaveCS
|
|
)
|
|
{
|
|
BOOL bAllocBuffer = FALSE;
|
|
BOOL bRet = FALSE;
|
|
BOOL bFileCreated = FALSE;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hImpersonationToken = INVALID_HANDLE_VALUE;
|
|
DWORD BytesWritten, cbSize, dwType, dwData, dwcbData;
|
|
SIZE_T cbRemaining = 0;
|
|
ULONG_PTR dwOffset, cb;
|
|
SHADOWFILE_3 ShadowFile = {0};
|
|
LPWSTR pEnd;
|
|
WCHAR szFileName[MAX_PATH];
|
|
BYTE ShdFileBuffer[MAX_STATIC_ALLOC];
|
|
LPBYTE pBuffer;
|
|
HKEY hPrintRegKey = NULL;
|
|
BOOL UsePools = TRUE;
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// Only update if this is not a direct job and the spooler requests it.
|
|
// Also don't update if the shadow file has been deleted at some other point.
|
|
// This check must be performed in the CS else the FilePool starts leaking
|
|
// jobs.
|
|
//
|
|
if ( (pIniJob->Status & JOB_DIRECT) ||
|
|
(pIniJob->pIniPrinter->pIniSpooler->SpoolerFlags
|
|
& SPL_NO_UPDATE_JOBSHD) ||
|
|
(pIniJob->Status & JOB_SHADOW_DELETED) ) {
|
|
|
|
bRet = TRUE;
|
|
|
|
//
|
|
// Setting this to FALSE prevents us reentering the CS accidentally.
|
|
//
|
|
bLeaveCS = FALSE;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (bLeaveCS) {
|
|
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
}
|
|
|
|
//
|
|
// FP Change
|
|
// if we don't have a handle to a filepool item, we
|
|
// revert to the old methods.
|
|
//
|
|
if (pIniJob->hFileItem == INVALID_HANDLE_VALUE)
|
|
{
|
|
UsePools = FALSE;
|
|
}
|
|
|
|
|
|
if (!UsePools)
|
|
{
|
|
GetFullNameFromId(pIniJob->pIniPrinter, pIniJob->JobId, FALSE, szFileName, COUNTOF(szFileName), FALSE);
|
|
}
|
|
|
|
hImpersonationToken = RevertToPrinterSelf();
|
|
|
|
if (UsePools)
|
|
{
|
|
HRESULT RetVal = S_OK;
|
|
//
|
|
// FP Change
|
|
// We Get a write handle from the pool for the shadow files and
|
|
// truncate it for use.
|
|
//
|
|
RetVal = GetWriterFromHandle(pIniJob->hFileItem, &hFile, FALSE);
|
|
|
|
if (SUCCEEDED(RetVal))
|
|
{
|
|
//
|
|
// Even if we can't set the file pointer, we have signalled to the
|
|
// file pool that a writer is busy with this file pool object.
|
|
//
|
|
bFileCreated = TRUE;
|
|
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
|
|
{
|
|
DBGMSG( DBG_WARNING,
|
|
( "WriteShadowJob Failed to set File pointer. Error %d\n", GetLastError() ));
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGMSG( DBG_WARNING,
|
|
( "WriteShadowJob Failed to get File Handle from Pool Item. Error %x\n", RetVal ));
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Open file in Cached IO. Big performance gain.
|
|
//
|
|
hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ,
|
|
NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
bFileCreated = TRUE;
|
|
}
|
|
}
|
|
|
|
bRet = ImpersonatePrinterClient(hImpersonationToken);
|
|
|
|
if(!bRet)
|
|
{
|
|
DBGMSG( DBG_WARNING,
|
|
( "Failed ot impersonate client \n" ));
|
|
|
|
goto CleanUp;
|
|
|
|
}
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE ) {
|
|
|
|
DBGMSG( DBG_WARNING,
|
|
( "WriteShadowJob failed to open shadow file "TSTR"\n Error %d\n",
|
|
szFileName, GetLastError() ));
|
|
|
|
bRet = FALSE;
|
|
|
|
goto CleanUp;
|
|
|
|
}
|
|
|
|
//
|
|
// We need to be able to tell if a shadow file was saved in .NET or in a previous OS
|
|
// version before an upgrade.
|
|
//
|
|
ShadowFile.signature = SF_SIGNATURE_3_DOTNET;
|
|
ShadowFile.cbSize = sizeof( SHADOWFILE_3 );
|
|
ShadowFile.Version = SF_VERSION_3;
|
|
ShadowFile.Status = pIniJob->Status;
|
|
ShadowFile.JobId = pIniJob->JobId;
|
|
ShadowFile.Priority = pIniJob->Priority;
|
|
ShadowFile.Submitted = pIniJob->Submitted;
|
|
ShadowFile.StartTime = pIniJob->StartTime;
|
|
ShadowFile.UntilTime = pIniJob->UntilTime;
|
|
ShadowFile.Size = pIniJob->Size;
|
|
ShadowFile.dwValidSize = pIniJob->dwValidSize;
|
|
ShadowFile.cPages = pIniJob->cPages;
|
|
ShadowFile.dwReboots = pIniJob->dwReboots;
|
|
if(pIniJob->pSecurityDescriptor)
|
|
ShadowFile.cbSecurityDescriptor=GetSecurityDescriptorLength(
|
|
pIniJob->pSecurityDescriptor);
|
|
|
|
pEnd=(LPWSTR)sizeof(ShadowFile);
|
|
|
|
if (pIniJob->pDevMode) {
|
|
ShadowFile.pDevMode=(LPDEVMODE)pEnd;
|
|
cb = pIniJob->pDevMode->dmSize + pIniJob->pDevMode->dmDriverExtra;
|
|
cb = ALIGN_UP(cb,ULONG_PTR);
|
|
cb /= sizeof(WCHAR);
|
|
pEnd += cb;
|
|
}
|
|
|
|
if (pIniJob->pSecurityDescriptor) {
|
|
ShadowFile.pSecurityDescriptor=(PSECURITY_DESCRIPTOR)pEnd;
|
|
cb = ShadowFile.cbSecurityDescriptor;
|
|
cb = ALIGN_UP(cb,ULONG_PTR);
|
|
cb /= sizeof(WCHAR);
|
|
pEnd += cb;
|
|
}
|
|
|
|
ShadowFile.NextJobId = pIniJob->NextJobId;
|
|
|
|
SetOffset( ShadowFile.pNotify, pIniJob->pNotify, pEnd );
|
|
SetOffset( ShadowFile.pUser, pIniJob->pUser, pEnd );
|
|
SetOffset( ShadowFile.pDocument, pIniJob->pDocument, pEnd );
|
|
SetOffset( ShadowFile.pOutputFile, pIniJob->pOutputFile, pEnd );
|
|
SetOffset( ShadowFile.pPrinterName, pIniJob->pIniPrinter->pName, pEnd );
|
|
SetOffset( ShadowFile.pDriverName, pIniJob->pIniDriver->pName, pEnd );
|
|
SetOffset( ShadowFile.pPrintProcName, pIniJob->pIniPrintProc->pName, pEnd );
|
|
SetOffset( ShadowFile.pDatatype, pIniJob->pDatatype, pEnd );
|
|
SetOffset( ShadowFile.pParameters, pIniJob->pParameters, pEnd );
|
|
SetOffset( ShadowFile.pMachineName, pIniJob->pMachineName, pEnd );
|
|
|
|
cbSize = (DWORD)ALIGN_UP(pEnd, ULONG_PTR);
|
|
|
|
if (cbSize > MAX_STATIC_ALLOC) {
|
|
|
|
if (!(pBuffer = (LPBYTE) AllocSplMem(cbSize))) {
|
|
|
|
DBGMSG( DBG_WARNING, ("WriteShadowJob: Memory Allocation failed %d\n", GetLastError()));
|
|
|
|
bRet = FALSE;
|
|
|
|
goto CleanUp;
|
|
}
|
|
bAllocBuffer = TRUE;
|
|
|
|
cbRemaining = cbSize;
|
|
|
|
} else {
|
|
|
|
pBuffer = (LPBYTE) ShdFileBuffer;
|
|
|
|
cbRemaining = COUNTOF(ShdFileBuffer);
|
|
}
|
|
|
|
|
|
//
|
|
// Copy SHADOWFILE_3 and data pointed thru it, into the buffer
|
|
//
|
|
|
|
dwOffset = 0;
|
|
|
|
CopyMemory(pBuffer + dwOffset, &ShadowFile, sizeof(SHADOWFILE_3));
|
|
dwOffset += sizeof(SHADOWFILE_3);
|
|
|
|
if (pIniJob->pDevMode) {
|
|
|
|
CopyMemory(pBuffer + dwOffset, pIniJob->pDevMode, pIniJob->pDevMode->dmSize + pIniJob->pDevMode->dmDriverExtra);
|
|
dwOffset += (pIniJob->pDevMode->dmSize + pIniJob->pDevMode->dmDriverExtra);
|
|
dwOffset = ALIGN_UP(dwOffset,ULONG_PTR);
|
|
}
|
|
|
|
if (pIniJob->pSecurityDescriptor) {
|
|
|
|
CopyMemory(pBuffer + dwOffset, pIniJob->pSecurityDescriptor, ShadowFile.cbSecurityDescriptor);
|
|
dwOffset += ShadowFile.cbSecurityDescriptor;
|
|
dwOffset = ALIGN_UP(dwOffset,ULONG_PTR);
|
|
}
|
|
|
|
//
|
|
// CopyString is defined at the start of the function
|
|
//
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pNotify);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pUser);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pDocument);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pOutputFile);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pIniPrinter->pName);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pIniDriver->pName);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pIniPrintProc->pName);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pDatatype);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pParameters);
|
|
CopyString(pBuffer, &dwOffset, &cbRemaining, pIniJob->pMachineName);
|
|
|
|
//
|
|
// Copy the structure into the Shadow file. Buffers need not be buffered since the
|
|
// file is opened in WRITE_THROUGH mode.
|
|
//
|
|
bRet = WriteFile( hFile, pBuffer, cbSize, &BytesWritten, NULL);
|
|
|
|
//
|
|
// Flush the file buffers if the corresponding flag is set in the registry
|
|
//
|
|
if (dwFlushShadowFileBuffers == 0) {
|
|
|
|
// Avoid repeated initializations
|
|
dwFlushShadowFileBuffers = 2;
|
|
|
|
// flag has to be initialized from the registry
|
|
dwcbData = sizeof(DWORD);
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
szRegistryRoot,
|
|
0,
|
|
KEY_READ,
|
|
&hPrintRegKey) == ERROR_SUCCESS) {
|
|
|
|
if (RegQueryValueEx(hPrintRegKey,
|
|
szFlushShadowFileBuffers,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) &dwData,
|
|
&dwcbData) == ERROR_SUCCESS) {
|
|
|
|
if (dwData == 1) {
|
|
// Flush the shadow file buffers
|
|
dwFlushShadowFileBuffers = 1;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hPrintRegKey);
|
|
}
|
|
}
|
|
|
|
if (dwFlushShadowFileBuffers == 1) {
|
|
bRet = FlushFileBuffers(hFile);
|
|
}
|
|
|
|
if (!bRet) {
|
|
|
|
DBGMSG( DBG_WARNING, ("WriteShadowJob: WriteFile failed %d\n", GetLastError()));
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if (bAllocBuffer) {
|
|
FreeSplMem(pBuffer);
|
|
}
|
|
|
|
if (!UsePools && hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
//
|
|
// FP Change
|
|
// Only close the file if it's a non-pooled file.
|
|
//
|
|
if (!CloseHandle(hFile)) {
|
|
DBGMSG(DBG_WARNING, ("WriteShadowJob CloseHandle failed %d %d\n",
|
|
hFile, GetLastError()));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reenter the CS if we were asked to leave it.
|
|
//
|
|
if (bLeaveCS) {
|
|
|
|
EnterSplSem();
|
|
}
|
|
|
|
//
|
|
// We can be called just before the shadow file was deleted (or sent back to
|
|
// the file pool) and then either recreate the shadow file on the disk or
|
|
// potentially leak a file pool handle. The final DeleteJob when the reference
|
|
// count is zero will see that the shadow file has already been deleted and will
|
|
// not attempt to clean it up. So, we remove the JOB_SHADOW_DELETED bit here to
|
|
// ensure that this will not happen.
|
|
//
|
|
if (bFileCreated) {
|
|
|
|
InterlockedAnd((LONG*)&(pIniJob->Status), ~JOB_SHADOW_DELETED);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#undef CopyString
|
|
|
|
#undef AddSize
|
|
|
|
|
|
VOID
|
|
ProcessShadowJobs(
|
|
PINIPRINTER pIniPrinter,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
WCHAR wczPrintDirAllSpools[MAX_PATH];
|
|
WCHAR wczPrinterDirectory[MAX_PATH];
|
|
HANDLE fFile;
|
|
BOOL b;
|
|
PWIN32_FIND_DATA pFindFileData;
|
|
PINIJOB pIniJob;
|
|
UINT ErrorMode;
|
|
|
|
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
//
|
|
// Don't Process Shadow Jobs during Upgrade
|
|
//
|
|
|
|
if ( dwUpgradeFlag != 0 || !( pIniSpooler->SpoolerFlags & SPL_PRINT )) {
|
|
|
|
return;
|
|
}
|
|
|
|
ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
if( GetPrinterDirectory(pIniPrinter,
|
|
FALSE,
|
|
wczPrintDirAllSpools,
|
|
(COUNTOF(wczPrintDirAllSpools) - COUNTOF(szAllSpools)),
|
|
pIniSpooler) &&
|
|
|
|
GetPrinterDirectory(pIniPrinter,
|
|
FALSE,
|
|
wczPrinterDirectory,
|
|
COUNTOF(wczPrinterDirectory),
|
|
pIniSpooler) ) {
|
|
|
|
StringCchCat(wczPrintDirAllSpools, COUNTOF(wczPrintDirAllSpools), szAllSpools);
|
|
|
|
if ( pFindFileData = AllocSplMem(sizeof(WIN32_FIND_DATA) )) {
|
|
|
|
fFile = FindFirstFile( wczPrintDirAllSpools, pFindFileData );
|
|
|
|
if ( fFile != (HANDLE)-1 ) {
|
|
|
|
b=TRUE;
|
|
|
|
while( b ) {
|
|
|
|
if ( !(pFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
ReadShadowJob(wczPrinterDirectory, pFindFileData, pIniSpooler);
|
|
}
|
|
|
|
b = FindNextFile(fFile, pFindFileData);
|
|
}
|
|
|
|
FindClose( fFile );
|
|
|
|
}
|
|
|
|
FreeSplMem( pFindFileData );
|
|
}
|
|
}
|
|
|
|
SetErrorMode(ErrorMode);
|
|
}
|
|
|
|
|
|
|
|
#define CheckPointer( strptr ) \
|
|
if( strptr ) { \
|
|
if( (ULONG_PTR)(strptr + wcslen(strptr) + 1) > (ULONG_PTR)pEnd ) { \
|
|
bRet = FALSE; \
|
|
goto BailOut; \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// make sure all pointers contain embedded data bounded within the pShadowFile (not passed the end).
|
|
//
|
|
BOOL
|
|
CheckAllPointers(
|
|
PSHADOWFILE_3 pShadowFile,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
LPBYTE pEnd = (LPBYTE)pShadowFile + dwSize;
|
|
BOOL bRet = TRUE;
|
|
|
|
try {
|
|
|
|
CheckPointer(pShadowFile->pDatatype);
|
|
CheckPointer(pShadowFile->pNotify);
|
|
CheckPointer(pShadowFile->pUser);
|
|
CheckPointer(pShadowFile->pDocument);
|
|
CheckPointer(pShadowFile->pOutputFile);
|
|
CheckPointer(pShadowFile->pPrinterName);
|
|
CheckPointer(pShadowFile->pDriverName);
|
|
CheckPointer(pShadowFile->pPrintProcName);
|
|
CheckPointer(pShadowFile->pParameters);
|
|
CheckPointer(pShadowFile->pMachineName);
|
|
|
|
// Now check the rest of the two data structures
|
|
if( (ULONG_PTR)pShadowFile->pSecurityDescriptor + pShadowFile->cbSecurityDescriptor > (ULONG_PTR)pEnd ) {
|
|
bRet = FALSE;
|
|
goto BailOut;
|
|
}
|
|
|
|
if( pShadowFile->pDevMode ) {
|
|
|
|
bRet = ((ULONG_PTR)pShadowFile->pDevMode < (ULONG_PTR)pEnd) ?
|
|
BoolFromHResult(SplIsValidDevmodeW((PDEVMODE)pShadowFile->pDevMode,
|
|
(ULONG_PTR)pEnd - (ULONG_PTR)pShadowFile->pDevMode)) :
|
|
FALSE;
|
|
}
|
|
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
bRet = FALSE;
|
|
}
|
|
|
|
BailOut:
|
|
return bRet;
|
|
}
|
|
|
|
#undef CheckPointer
|
|
|
|
|
|
PINIJOB
|
|
ReadShadowJob(
|
|
LPWSTR szDir,
|
|
PWIN32_FIND_DATA pFindFileData,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads a *.spl/*.shd file and partially validates the file.
|
|
|
|
Arguments:
|
|
|
|
szDir -- pointer to spool directory string
|
|
|
|
pFindFileData -- found file data (spl file)
|
|
|
|
pIniSpooler -- spooler the *.spl belongs to
|
|
|
|
Return Value:
|
|
|
|
Allocated pIniJob.
|
|
|
|
Notes:
|
|
|
|
Warning: Changing the format of SHADOWFILE requires modifying the
|
|
data integrity checks performed here!
|
|
|
|
If the shadow file structure size is grown, then when reading,
|
|
you must check the old sizes before touching memory. Current layout is:
|
|
|
|
| DWORD | ... | String | DWORD | DWORD | StringData | StringData |
|
|
*--------------------------------------*
|
|
^ This is the SHADOWFILE_3 structure.
|
|
|
|
If you grow it, then the next field will point to StringData, and
|
|
won't be valid--you can't touch it since you'll corrupt the string.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hFileSpl = INVALID_HANDLE_VALUE;
|
|
DWORD BytesRead;
|
|
PSHADOWFILE_3 pShadowFile3 = NULL;
|
|
PSHADOWFILE_3 pShadowFile = NULL;
|
|
PINIJOB pIniJob;
|
|
DWORD cb,i;
|
|
WCHAR szFileName[MAX_PATH];
|
|
LPWSTR pExt;
|
|
BOOL rc;
|
|
LPWSTR pFileSpec;
|
|
DWORD nFileSizeLow;
|
|
|
|
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
if (!BoolFromStatus(StrNCatBuff(szFileName, COUNTOF(szFileName), szDir, L"\\", pFindFileData->cFileName, NULL))) {
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
hFileSpl = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
if (hFileSpl == INVALID_HANDLE_VALUE) {
|
|
DBGMSG(DBG_WARNING, ("ReadShadowJob CreateFile( %ws ) failed: LastError = %d\n",
|
|
szFileName, GetLastError()));
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
CharUpper(szFileName);
|
|
pExt = wcsstr(szFileName, L".SPL");
|
|
|
|
if (!pExt)
|
|
goto Fail;
|
|
|
|
pExt[2] = L'H';
|
|
pExt[3] = L'D';
|
|
|
|
hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DBGMSG(DBG_WARNING, ("ReadShadowJob CreateFile( %ws ) failed: LastError = %d\n",
|
|
szFileName, GetLastError()));
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
nFileSizeLow = GetFileSize(hFile, NULL);
|
|
|
|
if (nFileSizeLow == 0xffffffff)
|
|
{
|
|
DBGMSG(DBG_WARNING, ("ReadShadowJob GetFileSize( %ws ) failed: LastError = %d\n",
|
|
szFileName, GetLastError()));
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
|
|
if ( nFileSizeLow < sizeof( SHADOWFILE ) ||
|
|
!(pShadowFile=AllocSplMem(nFileSizeLow))) {
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
rc = ReadFile(hFile, pShadowFile, nFileSizeLow, &BytesRead, NULL);
|
|
|
|
// If Shadow file is old style, then convert it to new
|
|
if (rc && (BytesRead == nFileSizeLow) &&
|
|
( pShadowFile->signature == SF_SIGNATURE ||
|
|
pShadowFile->signature == SF_SIGNATURE_2 )) {
|
|
|
|
BOOL bStatus;
|
|
|
|
if (!(pShadowFile3 = AllocSplMem(nFileSizeLow +
|
|
sizeof(SHADOWFILE_3) - sizeof(SHADOWFILE))) ) {
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
bStatus = Old2NewShadow((PSHADOWFILE)pShadowFile, pShadowFile3, &BytesRead);
|
|
nFileSizeLow = BytesRead; // This is used in CheckAllPointers, below
|
|
FreeSplMem(pShadowFile);
|
|
pShadowFile = pShadowFile3;
|
|
|
|
if( !bStatus ){
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initial size of SF_3/SF_3_DOTNET must include pMachineName.
|
|
//
|
|
if (!rc ||
|
|
(pShadowFile->signature != SF_SIGNATURE_3 && pShadowFile->signature != SF_SIGNATURE_3_DOTNET) ||
|
|
(BytesRead != nFileSizeLow) ||
|
|
(BytesRead < pShadowFile->cbSize ) ||
|
|
(BytesRead < sizeof( SHADOWFILE_3 )) ||
|
|
(pShadowFile->Status & (JOB_SPOOLING | JOB_PENDING_DELETION))) {
|
|
|
|
DBGMSG(DBG_WARNING, ( "Error reading shadow job:\
|
|
\n\tReadFile returned %d: Error %d\
|
|
\n\tsignature = %08x\
|
|
\n\tBytes read = %d; expected %d\
|
|
\n\tFile size = %d; expected %d\
|
|
\n\tStatus = %08x %s\n",
|
|
rc, ( rc ? 0 : GetLastError() ),
|
|
pShadowFile->signature,
|
|
BytesRead, nFileSizeLow,
|
|
sizeof(*pShadowFile), pShadowFile->Size,
|
|
pShadowFile->Status,
|
|
( (pShadowFile->Status & JOB_SPOOLING) ?
|
|
"Job is spooling!" : "" ) ) );
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
if (!CloseHandle(hFile)) {
|
|
DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", hFileSpl, GetLastError()));
|
|
}
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
if (!CloseHandle(hFileSpl)) {
|
|
DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", hFileSpl, GetLastError()));
|
|
}
|
|
hFileSpl = INVALID_HANDLE_VALUE;
|
|
|
|
// Check number of reboots on this file & delete if too many
|
|
if (pShadowFile->dwReboots > 1) {
|
|
DBGMSG(DBG_WARNING, ("Corrupt shadow file %ws\n", szFileName));
|
|
|
|
if ( pShadowFile->pDocument && pShadowFile->pDriverName ) {
|
|
SplLogEvent(pIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_BAD_JOB,
|
|
FALSE,
|
|
pShadowFile->pDocument + (ULONG_PTR)pShadowFile/sizeof(*pShadowFile->pDocument),
|
|
pShadowFile->pDriverName + (ULONG_PTR)pShadowFile/sizeof(*pShadowFile->pDriverName),
|
|
NULL);
|
|
}
|
|
goto Fail;
|
|
}
|
|
|
|
//
|
|
// If the job is a TS Job, and the registry printer policies DiscardTSJobs
|
|
// key is set, we just discard it.
|
|
//
|
|
if ((pShadowFile->Status & JOB_TS) && (pIniSpooler->dwSpoolerSettings & SPOOLER_DISCARDTSJOBS)) {
|
|
goto Fail;
|
|
}
|
|
|
|
if (pIniJob = AllocSplMem(sizeof(INIJOB))) {
|
|
|
|
pIniJob->signature = IJ_SIGNATURE;
|
|
pIniJob->Status = pShadowFile->Status & (JOB_PAUSED | JOB_REMOTE | JOB_PRINTED | JOB_COMPLETE );
|
|
pIniJob->JobId = pShadowFile->JobId;
|
|
pIniJob->Priority = pShadowFile->Priority;
|
|
pIniJob->Submitted = pShadowFile->Submitted;
|
|
pIniJob->StartTime = pShadowFile->StartTime;
|
|
pIniJob->UntilTime = pShadowFile->UntilTime;
|
|
pIniJob->Size = pShadowFile->Size;
|
|
pIniJob->dwValidSize = pShadowFile->dwValidSize;
|
|
pIniJob->cPages = pShadowFile->cPages;
|
|
pIniJob->cbPrinted = 0;
|
|
pIniJob->NextJobId = pShadowFile->NextJobId;
|
|
pIniJob->dwReboots = pShadowFile->dwReboots;
|
|
|
|
pIniJob->dwJobNumberOfPagesPerSide = 0;
|
|
pIniJob->dwDrvNumberOfPagesPerSide = 0;
|
|
pIniJob->cLogicalPages = 0;
|
|
pIniJob->cLogicalPagesPrinted = 0;
|
|
|
|
pIniJob->WaitForWrite = NULL;
|
|
pIniJob->WaitForRead = NULL;
|
|
pIniJob->hWriteFile = INVALID_HANDLE_VALUE;
|
|
|
|
// Additional fields for SeekPrinter.
|
|
pIniJob->WaitForSeek = NULL;
|
|
pIniJob->bWaitForEnd = FALSE;
|
|
pIniJob->bWaitForSeek = FALSE;
|
|
pIniJob->liFileSeekPosn.u.HighPart = 0;
|
|
pIniJob->liFileSeekPosn.u.LowPart = 0;
|
|
|
|
SetPointer(pShadowFile, pDatatype);
|
|
SetPointer(pShadowFile, pNotify);
|
|
SetPointer(pShadowFile, pUser);
|
|
SetPointer(pShadowFile, pDocument);
|
|
SetPointer(pShadowFile, pOutputFile);
|
|
SetPointer(pShadowFile, pPrinterName);
|
|
SetPointer(pShadowFile, pDriverName);
|
|
SetPointer(pShadowFile, pPrintProcName);
|
|
SetPointer(pShadowFile, pParameters);
|
|
SetPointer(pShadowFile, pMachineName);
|
|
|
|
if( (pShadowFile->cbSecurityDescriptor > 0) && pShadowFile->pSecurityDescriptor )
|
|
pShadowFile->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)((LPBYTE)pShadowFile +
|
|
(ULONG_PTR)pShadowFile->pSecurityDescriptor);
|
|
|
|
if (pShadowFile->pDevMode)
|
|
pShadowFile->pDevMode = (LPDEVMODEW)((LPBYTE)pShadowFile +
|
|
(ULONG_PTR)pShadowFile->pDevMode);
|
|
|
|
|
|
// check the length of the embedded strings as well as DevMode and Security structs.
|
|
if( !CheckAllPointers( pShadowFile, nFileSizeLow )) {
|
|
DBGMSG( DBG_WARNING, ("CheckAllPointers() failed; bad shadow file %ws\n", pFindFileData->cFileName ));
|
|
|
|
FreeSplMem(pIniJob);
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
//
|
|
// Discard any jobs which were NT JNL 1.000 since the fonts might not
|
|
// be correct
|
|
|
|
if ( pShadowFile->pDatatype != NULL ) {
|
|
if (!lstrcmpi( pShadowFile->pDatatype, L"NT JNL 1.000" )) {
|
|
|
|
DBGMSG(DBG_WARNING, ("Deleteing job Datatype %ws %ws %ws\n",
|
|
pShadowFile->pDatatype,
|
|
pFindFileData->cFileName, szFileName));
|
|
FreeSplMem(pIniJob);
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
pIniJob->pIniDriver = (PINIDRIVER)FindLocalDriver(pIniSpooler, pShadowFile->pDriverName);
|
|
|
|
if ((pIniJob->pIniPrinter = FindPrinter(pShadowFile->pPrinterName,pIniSpooler)) &&
|
|
pIniJob->pIniDriver &&
|
|
(pIniJob->pIniPrintProc = FindPrintProc(pShadowFile->pPrintProcName, FindEnvironment(szEnvironment, pIniSpooler)))) {
|
|
|
|
|
|
// Notice that MaxJobId is really the number of job slots in the pJobIdMap, so
|
|
// the maximum job id we can allow is (MaxJobId - 1).
|
|
if (pIniJob->JobId >= MaxJobId( pIniSpooler->hJobIdMap )) {
|
|
// If the job id is too huge (i.e. from a corrupt file) then we might allocate
|
|
// too much unnecessary memory for the JobIdMap!
|
|
// Notice we need to ask for (JobId+1) number of slots in the map!.
|
|
if( !ReallocJobIdMap( pIniSpooler->hJobIdMap,
|
|
pIniJob->JobId + 1 )) {
|
|
|
|
// probably a bad job id, dump the job!
|
|
DBGMSG( DBG_WARNING, ("Failed to alloc JobIdMap in ShadowFile %ws for JobId %d\n", pFindFileData->cFileName, pIniJob->JobId ));
|
|
|
|
FreeSplMem(pIniJob);
|
|
|
|
goto Fail;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if( bBitOn( pIniSpooler->hJobIdMap, pIniJob->JobId )) {
|
|
|
|
// A bad job id from a corrupt shadowfile; dump the job!
|
|
DBGMSG( DBG_WARNING, ("Duplicate Job Id in ShadowFile %ws for JobId %d\n", pFindFileData->cFileName, pIniJob->JobId ));
|
|
|
|
FreeSplMem(pIniJob);
|
|
|
|
goto Fail;
|
|
}
|
|
}
|
|
|
|
SPLASSERT( pIniSpooler->hJobIdMap != NULL );
|
|
vMarkOn( pIniSpooler->hJobIdMap, pIniJob->JobId);
|
|
|
|
pIniJob->pIniPrinter->cJobs++;
|
|
pIniJob->pIniPrinter->cTotalJobs++;
|
|
|
|
INCDRIVERREF( pIniJob->pIniDriver );
|
|
|
|
pIniJob->pIniPrintProc->cRef++;
|
|
pIniJob->pIniPort = NULL;
|
|
|
|
|
|
if (pShadowFile->pSecurityDescriptor)
|
|
{
|
|
if (pIniJob->pSecurityDescriptor = LocalAlloc(LPTR, pShadowFile->cbSecurityDescriptor))
|
|
{
|
|
memcpy(pIniJob->pSecurityDescriptor, pShadowFile->pSecurityDescriptor, pShadowFile->cbSecurityDescriptor);
|
|
|
|
if (pShadowFile->signature != SF_SIGNATURE_3_DOTNET && !GrantJobReadPermissionToLocalSystem(&pIniJob->pSecurityDescriptor))
|
|
{
|
|
LocalFree(pIniJob->pSecurityDescriptor);
|
|
|
|
pIniJob->pSecurityDescriptor = NULL;
|
|
|
|
goto Fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGMSG(DBG_WARNING, ("Failed to alloc ini job security descriptor.\n"));
|
|
}
|
|
}
|
|
|
|
if (pShadowFile->pDevMode) {
|
|
|
|
cb=pShadowFile->pDevMode->dmSize +
|
|
pShadowFile->pDevMode->dmDriverExtra;
|
|
if (pIniJob->pDevMode=AllocSplMem(cb))
|
|
memcpy(pIniJob->pDevMode, pShadowFile->pDevMode, cb);
|
|
else
|
|
DBGMSG(DBG_WARNING, ("Failed to alloc ini job devmode.\n"));
|
|
}
|
|
|
|
pIniJob->pNotify = AllocSplStr( pShadowFile->pNotify);
|
|
pIniJob->pUser = AllocSplStr( pShadowFile->pUser);
|
|
pIniJob->pDocument = AllocSplStr( pShadowFile->pDocument);
|
|
pIniJob->pOutputFile = AllocSplStr( pShadowFile->pOutputFile);
|
|
pIniJob->pDatatype = AllocSplStr( pShadowFile->pDatatype);
|
|
pIniJob->pParameters = AllocSplStr( pShadowFile->pParameters);
|
|
|
|
if( pShadowFile->pMachineName ){
|
|
pIniJob->pMachineName = AllocSplStr( pShadowFile->pMachineName );
|
|
} else {
|
|
pIniJob->pMachineName = AllocSplStr( pIniSpooler->pMachineName );
|
|
}
|
|
|
|
//
|
|
// FP Change
|
|
// Add the files to the File pool if :-
|
|
// a. We dont want to KeepPrintedJobs for the printer,
|
|
// b. the printer does not have its own spool directory; or
|
|
// c. SpoolerSettings do not have filepooling disabled.
|
|
//
|
|
if ( !(pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS) &&
|
|
pIniJob->pIniPrinter->pSpoolDir == NULL &&
|
|
!(pIniSpooler->dwSpoolerSettings & SPOOLER_NOFILEPOOLING))
|
|
{
|
|
pIniJob->pszSplFileName = AllocSplStr( szFileName );
|
|
|
|
if ((pIniJob->pIniPrinter->pIniSpooler->hFilePool != INVALID_HANDLE_VALUE)
|
|
&& pIniJob->pszSplFileName && SUCCEEDED(ConvertFileExt(pIniJob->pszSplFileName, L".SHD", L".SPL")))
|
|
{
|
|
if (FAILED(GetFileItemHandle(
|
|
pIniJob->pIniPrinter->pIniSpooler->hFilePool,
|
|
&pIniJob->hFileItem,
|
|
pIniJob->pszSplFileName )))
|
|
{
|
|
pIniJob->hFileItem = INVALID_HANDLE_VALUE;
|
|
FreeSplStr(pIniJob->pszSplFileName);
|
|
pIniJob->pszSplFileName = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FreeSplStr(pIniJob->pszSplFileName);
|
|
pIniJob->pszSplFileName = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pIniJob->pszSplFileName = NULL;
|
|
pIniJob->hFileItem = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
pIniJob->pIniNextJob = NULL;
|
|
pIniJob->pStatus = NULL;
|
|
|
|
if (pIniJob->pIniPrevJob = pIniJob->pIniPrinter->pIniLastJob)
|
|
pIniJob->pIniPrevJob->pIniNextJob=pIniJob;
|
|
|
|
if (!pIniJob->pIniPrinter->pIniFirstJob)
|
|
pIniJob->pIniPrinter->pIniFirstJob = pIniJob;
|
|
|
|
pIniJob->pIniPrinter->pIniLastJob=pIniJob;
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARNING, ("Failed to find printer %ws\n",pShadowFile->pPrinterName));
|
|
|
|
FreeSplMem(pIniJob);
|
|
|
|
goto Fail;
|
|
}
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING, ("Failed to allocate ini job.\n"));
|
|
}
|
|
|
|
FreeSplMem( pShadowFile );
|
|
|
|
return pIniJob;
|
|
|
|
Fail:
|
|
|
|
if (pShadowFile) {
|
|
FreeSplMem(pShadowFile);
|
|
}
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE && !CloseHandle(hFile)) {
|
|
DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", hFile, GetLastError()));
|
|
}
|
|
if (hFileSpl != INVALID_HANDLE_VALUE && !CloseHandle(hFileSpl)) {
|
|
DBGMSG(DBG_WARNING, ("CloseHandle failed %d %d\n", hFileSpl, GetLastError()));
|
|
}
|
|
|
|
DeleteFile(szFileName);
|
|
|
|
pFileSpec = wcsrchr(szFileName, L'\\');
|
|
|
|
if (pFileSpec) {
|
|
|
|
pFileSpec++;
|
|
|
|
StringCchCopy(pFileSpec, COUNTOF(szFileName) - (pFileSpec - szFileName), pFindFileData->cFileName);
|
|
}
|
|
|
|
DeleteFile(szFileName);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
GetDiscardTSJobsSettings(
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
//
|
|
// If the Printer Policy to Discard TS Jobs is set we shall
|
|
// discard the jobs on spooler restart as a security precaution
|
|
// otherwise the normal behaviour is that we keep the jobs.
|
|
// However this by design causes a job to print on the TS port
|
|
// irrespective of the current user or printer.
|
|
//
|
|
DWORD RegValue = 0;
|
|
DWORD RegValueSize = sizeof(RegValue);
|
|
HKEY RegKey = NULL;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD RegValueType;
|
|
|
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
szPrintPublishPolicy,
|
|
0,
|
|
KEY_READ,
|
|
&RegKey );
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwError = RegQueryValueEx( RegKey,
|
|
szDiscardTSJobs,
|
|
NULL,
|
|
(LPDWORD) &RegValueType,
|
|
(LPBYTE) &RegValue,
|
|
&RegValueSize );
|
|
|
|
if ( RegValueType == REG_DWORD && dwError == ERROR_SUCCESS && RegValue > 0)
|
|
{
|
|
pIniSpooler->dwSpoolerSettings |= SPOOLER_DISCARDTSJOBS;
|
|
}
|
|
}
|
|
|
|
if (RegKey)
|
|
{
|
|
RegCloseKey( RegKey );
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
Old2NewShadow(
|
|
PSHADOWFILE pShadowFile1,
|
|
PSHADOWFILE_3 pShadowFile3,
|
|
DWORD *pnBytes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts an original format *.shd file to a new format (version 2).
|
|
|
|
Arguments:
|
|
|
|
pShadowFile1 -- pointer to version 1 shadow file
|
|
|
|
pShadowFile2 -- pointer to version 2 shadow file
|
|
|
|
*pnBytes -- pointer to number of bytes read from version 1 shadow file. On
|
|
return, pnBytes contains the number of bytes in the version 2 shadow file.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
|
|
Author: Steve Wilson (NT)
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD cbOld;
|
|
DWORD cbDiff;
|
|
|
|
switch( pShadowFile1->signature ){
|
|
case SF_SIGNATURE:
|
|
cbOld = sizeof( SHADOWFILE );
|
|
cbDiff = sizeof( SHADOWFILE_3 ) - sizeof( SHADOWFILE );
|
|
break;
|
|
case SF_SIGNATURE_2:
|
|
cbOld = sizeof ( SHADOWFILE_2 );
|
|
cbDiff = sizeof( SHADOWFILE_3 ) - sizeof( SHADOWFILE_2 );
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
if( *pnBytes < cbOld ){
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy everything except signature.
|
|
//
|
|
MoveMemory((PVOID)(&pShadowFile3->Status),
|
|
(PVOID)(&pShadowFile1->Status),
|
|
cbOld - sizeof( pShadowFile1->signature ));
|
|
|
|
//
|
|
// Now update signature and size.
|
|
//
|
|
pShadowFile3->signature = SF_SIGNATURE_3_DOTNET;
|
|
pShadowFile3->cbSize = *pnBytes + cbDiff;
|
|
|
|
//
|
|
// Move strings.
|
|
//
|
|
MoveMemory((PVOID)(pShadowFile3 + 1),
|
|
((PBYTE)pShadowFile1) + cbOld,
|
|
*pnBytes - cbOld );
|
|
|
|
pShadowFile3->pNotify += pShadowFile1->pNotify ? cbDiff/sizeof *pShadowFile1->pNotify : 0;
|
|
pShadowFile3->pUser += pShadowFile1->pUser ? cbDiff/sizeof *pShadowFile1->pUser : 0;
|
|
pShadowFile3->pDocument += pShadowFile1->pDocument ? cbDiff/sizeof *pShadowFile3->pDocument : 0;
|
|
pShadowFile3->pOutputFile += pShadowFile1->pOutputFile ? cbDiff/sizeof *pShadowFile3->pOutputFile : 0;
|
|
pShadowFile3->pPrinterName += pShadowFile1->pPrinterName ? cbDiff/sizeof *pShadowFile3->pPrinterName : 0;
|
|
pShadowFile3->pDriverName += pShadowFile1->pDriverName ? cbDiff/sizeof *pShadowFile3->pDriverName : 0;
|
|
pShadowFile3->pPrintProcName += pShadowFile1->pPrintProcName ? cbDiff/sizeof *pShadowFile3->pPrintProcName : 0;
|
|
pShadowFile3->pDatatype += pShadowFile1->pDatatype ? cbDiff/sizeof *pShadowFile3->pDatatype : 0;
|
|
pShadowFile3->pParameters += pShadowFile1->pParameters ? cbDiff/sizeof *pShadowFile3->pParameters : 0;
|
|
|
|
pShadowFile3->pDevMode = (PDEVMODE) (pShadowFile1->pDevMode ?
|
|
(ULONG_PTR) pShadowFile1->pDevMode + cbDiff : 0);
|
|
|
|
pShadowFile3->pSecurityDescriptor = (PSECURITY_DESCRIPTOR) (pShadowFile1->pSecurityDescriptor ?
|
|
(ULONG_PTR) pShadowFile1->pSecurityDescriptor + cbDiff : 0);
|
|
|
|
pShadowFile3->Version = SF_VERSION_3;
|
|
|
|
//
|
|
// The first shadow file didn't have dwReboots.
|
|
//
|
|
if( pShadowFile1->signature == SF_SIGNATURE ){
|
|
pShadowFile3->dwReboots = 0;
|
|
}
|
|
|
|
pShadowFile3->pMachineName = NULL;
|
|
|
|
*pnBytes += cbDiff;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PINIVERSION
|
|
GetVersionDrivers(
|
|
HKEY hDriversKey,
|
|
LPWSTR szVersionName,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment
|
|
)
|
|
{
|
|
HKEY hVersionKey;
|
|
WCHAR szDirectoryValue[MAX_PATH];
|
|
PINIDRIVER pIniDriver;
|
|
DWORD cMajorVersion, cMinorVersion;
|
|
DWORD cbData;
|
|
DWORD Type;
|
|
PINIVERSION pIniVersion = NULL;
|
|
|
|
if (SplRegOpenKey(hDriversKey, szVersionName, KEY_READ, &hVersionKey, pIniSpooler) != ERROR_SUCCESS)
|
|
{
|
|
DBGMSG(DBG_TRACE, ("GetVersionDrivers SplRegOpenKey on "TSTR" failed\n", szVersionName));
|
|
return NULL;
|
|
}
|
|
|
|
cbData = sizeof(szDirectoryValue);
|
|
|
|
if (SplRegQueryValue(hVersionKey, szDirectory, &Type, (LPBYTE)szDirectoryValue, &cbData, pIniSpooler)!=ERROR_SUCCESS)
|
|
{
|
|
DBGMSG(DBG_TRACE, ("Couldn't query for directory in version structure\n"));
|
|
goto Done;
|
|
}
|
|
|
|
cbData = sizeof(DWORD);
|
|
|
|
if (SplRegQueryValue(hVersionKey, szMajorVersion, &Type, (LPBYTE)&cMajorVersion, &cbData, pIniSpooler)!=ERROR_SUCCESS)
|
|
{
|
|
DBGMSG(DBG_TRACE, ("Couldn't query for major version in version structure\n"));
|
|
goto Done;
|
|
}
|
|
|
|
cbData = sizeof(DWORD);
|
|
|
|
if (SplRegQueryValue(hVersionKey, szMinorVersion, &Type, (LPBYTE)&cMinorVersion, &cbData, pIniSpooler)!=ERROR_SUCCESS)
|
|
{
|
|
DBGMSG(DBG_TRACE, ("Couldn't query for minor version in version structure\n"));
|
|
goto Done;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("Got all information to build the version entry\n"));
|
|
|
|
//
|
|
// Now build the version node structure.
|
|
//
|
|
pIniVersion = AllocSplMem(sizeof(INIVERSION));
|
|
|
|
if( pIniVersion ){
|
|
|
|
pIniVersion->signature = IV_SIGNATURE;
|
|
pIniVersion->pName = AllocSplStr(szVersionName);
|
|
pIniVersion->szDirectory = AllocSplStr(szDirectoryValue);
|
|
pIniVersion->cMajorVersion = cMajorVersion;
|
|
pIniVersion->cMinorVersion = cMinorVersion;
|
|
pIniVersion->pDrvRefCnt = NULL;
|
|
|
|
if (!pIniVersion->pName || !pIniVersion->szDirectory) {
|
|
FreeIniVersion(pIniVersion);
|
|
pIniVersion = NULL;
|
|
} else {
|
|
|
|
pIniDriver = GetDriverList(hVersionKey,
|
|
pIniSpooler,
|
|
pIniEnvironment,
|
|
pIniVersion);
|
|
|
|
pIniVersion->pIniDriver = pIniDriver;
|
|
|
|
while (pIniDriver) {
|
|
if (!UpdateDriverFileRefCnt(pIniEnvironment,pIniVersion,pIniDriver,NULL,0,TRUE)) {
|
|
FreeIniVersion(pIniVersion);
|
|
pIniVersion = NULL;
|
|
break;
|
|
}
|
|
pIniDriver = pIniDriver->pNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Done:
|
|
SplRegCloseKey(hVersionKey, pIniSpooler);
|
|
return pIniVersion;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
FreeIniDriver
|
|
|
|
Routine Description:
|
|
|
|
This handles the memory in an inidriver after first decrementing the driver
|
|
ref-count correctly.
|
|
|
|
Arguments:
|
|
|
|
pIniEnvironment - The environment of the driver.
|
|
pIniVersion - The version of the driver.
|
|
pIniDriver - The driver to delete.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
FreeIniDriver(
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion,
|
|
IN PINIDRIVER pIniDriver
|
|
)
|
|
{
|
|
if (pIniEnvironment && pIniVersion && pIniDriver)
|
|
{
|
|
//
|
|
// This is to reverse the ref-count for when the spooler is first created.
|
|
//
|
|
UpdateDriverFileRefCnt(pIniEnvironment, pIniVersion, pIniDriver, NULL, 0, FALSE);
|
|
|
|
//
|
|
// The monitors will be deleted shortly. So we don't need to worry about
|
|
// the language monitors.
|
|
//
|
|
FreeStructurePointers((LPBYTE) pIniDriver, NULL, IniDriverOffsets);
|
|
FreeSplMem(pIniDriver);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
FreeIniVersion
|
|
|
|
Routine Description:
|
|
|
|
This frees all the memory in an ini-version without handling either the
|
|
drivers or the driver ref-counts in it.
|
|
|
|
Arguments:
|
|
|
|
pIniVersion - The version to delete.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
FreeIniVersion(
|
|
IN PINIVERSION pIniVersion
|
|
)
|
|
{
|
|
PDRVREFCNT pdrc,pdrctemp;
|
|
|
|
FreeSplStr( pIniVersion->pName );
|
|
FreeSplStr( pIniVersion->szDirectory );
|
|
|
|
pdrc = pIniVersion->pDrvRefCnt;
|
|
|
|
while (pdrc) {
|
|
FreeSplStr(pdrc->szDrvFileName);
|
|
pdrctemp = pdrc->pNext;
|
|
FreeSplMem(pdrc);
|
|
pdrc = pdrctemp;
|
|
}
|
|
|
|
FreeSplMem( pIniVersion );
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
DeleteIniVersion
|
|
|
|
Routine Description:
|
|
|
|
This runs all of the drivers in an iniVersion and then calls FreeIniVersion
|
|
to free the contents of the iniversion.
|
|
|
|
Arguments:
|
|
|
|
pIniEnvironment - The environment used for handling driver ref-counts.
|
|
pIniVersion - The version to delete.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
DeleteIniVersion(
|
|
IN PINIENVIRONMENT pIniEnvironment,
|
|
IN PINIVERSION pIniVersion
|
|
)
|
|
{
|
|
if (pIniVersion && pIniEnvironment)
|
|
{
|
|
PINIDRIVER pIniDriver = NULL;
|
|
PINIDRIVER pNextIniDriver = NULL;
|
|
|
|
for(pIniDriver = pIniVersion->pIniDriver; pIniDriver; pIniDriver = pNextIniDriver)
|
|
{
|
|
pNextIniDriver = pIniDriver->pNext;
|
|
|
|
FreeIniDriver(pIniEnvironment, pIniVersion, pIniDriver);
|
|
}
|
|
|
|
FreeIniVersion(pIniVersion);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
FreeIniEnvironment
|
|
|
|
Routine Description:
|
|
|
|
This runs all of the ini-versions in an environvironment
|
|
and then deletes the environment.
|
|
|
|
Arguments:
|
|
|
|
pIniEnvironment - The environment to free.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
FreeIniEnvironment(
|
|
IN PINIENVIRONMENT pIniEnvironment
|
|
)
|
|
{
|
|
if (pIniEnvironment)
|
|
{
|
|
PINIVERSION pIniVersion = NULL;
|
|
PINIVERSION pNextIniVersion = NULL;
|
|
|
|
for(pIniVersion = pIniEnvironment->pIniVersion; pIniVersion; pIniVersion = pNextIniVersion)
|
|
{
|
|
pNextIniVersion = pIniVersion->pNext;
|
|
|
|
DeleteIniVersion(pIniEnvironment, pIniVersion);
|
|
}
|
|
|
|
FreeIniPrintProc(pIniEnvironment->pIniPrintProc);
|
|
FreeStructurePointers((LPBYTE)pIniEnvironment, NULL, IniEnvironmentOffsets);
|
|
FreeSplMem(pIniEnvironment);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
FreeIniPrintProc
|
|
|
|
Routine Description:
|
|
|
|
This deletes all of the print processor fields.
|
|
|
|
Arguments:
|
|
|
|
pIniPrintProc - The print processor to delete.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
FreeIniPrintProc(
|
|
IN PINIPRINTPROC pIniPrintProc
|
|
)
|
|
{
|
|
if (pIniPrintProc)
|
|
{
|
|
FreeLibrary(pIniPrintProc->hLibrary);
|
|
DeleteCriticalSection(&pIniPrintProc->CriticalSection);
|
|
FreeStructurePointers((LPBYTE)pIniPrintProc, NULL, IniPrintProcOffsets);
|
|
FreeSplMem(pIniPrintProc);
|
|
}
|
|
}
|
|
|
|
PINIDRIVER
|
|
GetDriverList(
|
|
HKEY hVersionKey,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion
|
|
)
|
|
{
|
|
PINIDRIVER pIniDriverList = NULL;
|
|
DWORD cDrivers = 0;
|
|
PINIDRIVER pIniDriver;
|
|
WCHAR DriverName[MAX_PATH];
|
|
DWORD cchBuffer =0;
|
|
|
|
pIniDriverList = NULL;
|
|
|
|
cchBuffer = COUNTOF(DriverName);
|
|
|
|
while (SplRegEnumKey(hVersionKey, cDrivers++, DriverName, &cchBuffer, NULL, pIniSpooler) == ERROR_SUCCESS)
|
|
{
|
|
cchBuffer = COUNTOF(DriverName);
|
|
|
|
DBGMSG(DBG_TRACE, ("Found a driver - "TSTR"\n", DriverName));
|
|
|
|
pIniDriver = GetDriver(hVersionKey, DriverName, pIniSpooler, pIniEnvironment, pIniVersion);
|
|
|
|
if (pIniDriver != NULL)
|
|
{
|
|
pIniDriver->pNext = pIniDriverList;
|
|
pIniDriverList = pIniDriver;
|
|
}
|
|
|
|
//
|
|
// On a cluster, a driver may have changed while the cluster spooler
|
|
// was hosted by another node. Here we check if we need to update or
|
|
// add a new driver
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
ClusterCheckDriverChanged(hVersionKey,
|
|
DriverName,
|
|
pIniEnvironment->pName,
|
|
pIniVersion->pName,
|
|
pIniSpooler))
|
|
{
|
|
DWORD dwError;
|
|
|
|
//
|
|
// Add or update the driver.
|
|
//
|
|
LeaveSplSem();
|
|
|
|
if ((dwError = ClusterAddOrUpdateDriverFromClusterDisk(hVersionKey,
|
|
DriverName,
|
|
pIniEnvironment->pName,
|
|
pIniEnvironment->pDirectory,
|
|
pIniSpooler)) != ERROR_SUCCESS)
|
|
|
|
{
|
|
WCHAR szError[20];
|
|
|
|
DBGMSG(DBG_CLUSTER, ("GetDriverList failed to add/update driver "TSTR". Win32 error %u\n",
|
|
DriverName, dwError));
|
|
|
|
StringCchPrintf(szError, COUNTOF(szError), L"%u", dwError);
|
|
|
|
SplLogEvent(pIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_CANT_ADD_UPDATE_CLUSTER_DRIVER,
|
|
FALSE,
|
|
DriverName,
|
|
pIniSpooler->pMachineName,
|
|
szError,
|
|
NULL);
|
|
}
|
|
|
|
EnterSplSem();
|
|
}
|
|
}
|
|
|
|
return pIniDriverList;
|
|
}
|
|
|
|
|
|
PINIDRIVER
|
|
GetDriver(
|
|
HKEY hVersionKey,
|
|
LPWSTR DriverName,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIENVIRONMENT pIniEnvironment,
|
|
PINIVERSION pIniVersion
|
|
)
|
|
{
|
|
HKEY hDriverKey = NULL;
|
|
DWORD Type;
|
|
WCHAR szData[MAX_PATH];
|
|
WCHAR szTempDir[MAX_PATH];
|
|
DWORD cbData;
|
|
DWORD Version;
|
|
DWORD DriverAttributes;
|
|
LPWSTR pConfigFile, pDataFile, pDriver;
|
|
LPWSTR pHelpFile, pMonitorName, pDefaultDataType, pDependentFiles, pTemp;
|
|
LPWSTR pDriverName, pszzPreviousNames;
|
|
LPWSTR pszMfgName, pszOEMUrl, pszHardwareID, pszProvider;
|
|
FILETIME DriverDate;
|
|
DWORDLONG DriverVersion;
|
|
PINIDRIVER pIniDriver = NULL;
|
|
DWORD cb, cLen, cchDependentFiles = 0, cchPreviousNames = 0;
|
|
DWORD dwTempDir, dwLastError = ERROR_SUCCESS;
|
|
|
|
pDriverName = pConfigFile = pDataFile = pDriver = pHelpFile = pTemp = NULL;
|
|
pMonitorName = pDefaultDataType = pDependentFiles = pszzPreviousNames = NULL;
|
|
pszMfgName = pszOEMUrl = pszHardwareID = pszProvider = NULL;
|
|
|
|
if ((dwLastError = SplRegOpenKey(hVersionKey, DriverName, KEY_READ, &hDriverKey, pIniSpooler)) != ERROR_SUCCESS) {
|
|
goto Fail;
|
|
}
|
|
else {
|
|
|
|
if ( !(pDriverName=AllocSplStr(DriverName)) ) {
|
|
dwLastError = GetLastError();
|
|
goto Fail;
|
|
}
|
|
|
|
RegGetString( hDriverKey, szConfigurationKey, &pConfigFile, &cLen, &dwLastError, TRUE, pIniSpooler );
|
|
if (!pConfigFile) {
|
|
goto Fail;
|
|
}
|
|
|
|
RegGetString( hDriverKey, szDataFileKey, &pDataFile, &cLen, &dwLastError, TRUE, pIniSpooler );
|
|
if ( !pDataFile ) {
|
|
goto Fail;
|
|
}
|
|
|
|
RegGetString( hDriverKey, szDriverFile, &pDriver, &cLen, &dwLastError, TRUE, pIniSpooler );
|
|
if ( !pDriver ) {
|
|
goto Fail;
|
|
}
|
|
|
|
RegGetString( hDriverKey, szHelpFile, &pHelpFile, &cLen, &dwLastError, FALSE, pIniSpooler );
|
|
|
|
RegGetString( hDriverKey, szMonitor, &pMonitorName, &cLen, &dwLastError, FALSE, pIniSpooler );
|
|
|
|
RegGetString( hDriverKey, szDatatype, &pDefaultDataType, &cLen, &dwLastError, FALSE, pIniSpooler );
|
|
|
|
RegGetMultiSzString( hDriverKey, szDependentFiles, &pDependentFiles, &cchDependentFiles, &dwLastError, FALSE, pIniSpooler );
|
|
|
|
RegGetMultiSzString( hDriverKey, szPreviousNames, &pszzPreviousNames, &cchPreviousNames, &dwLastError, FALSE, pIniSpooler );
|
|
|
|
RegGetString( hDriverKey, szMfgName, &pszMfgName, &cLen, &dwLastError, FALSE, pIniSpooler );
|
|
|
|
RegGetString( hDriverKey, szOEMUrl, &pszOEMUrl, &cLen, &dwLastError, FALSE, pIniSpooler );
|
|
|
|
RegGetString( hDriverKey, szHardwareID, &pszHardwareID, &cLen, &dwLastError, TRUE, pIniSpooler );
|
|
|
|
RegGetString( hDriverKey, szProvider, &pszProvider, &cLen, &dwLastError, TRUE, pIniSpooler );
|
|
|
|
cbData = sizeof(DriverDate);
|
|
if (SplRegQueryValue(hDriverKey, szDriverDate, NULL, (LPBYTE)&DriverDate, &cbData, pIniSpooler)!=ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// don't leave the data uninitialized
|
|
//
|
|
DriverDate.dwLowDateTime = DriverDate.dwHighDateTime = 0;
|
|
}
|
|
|
|
cbData = sizeof(DriverVersion);
|
|
if (SplRegQueryValue(hDriverKey, szLongVersion, NULL, (LPBYTE)&DriverVersion, &cbData, pIniSpooler)!=ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// don't leave the data uninitialized
|
|
//
|
|
DriverVersion = 0;
|
|
}
|
|
|
|
// Retrieve the version number
|
|
cbData = sizeof(DWORD);
|
|
if (SplRegQueryValue(hDriverKey, szDriverAttributes, &Type, (LPBYTE)&DriverAttributes, &cbData, pIniSpooler) != ERROR_SUCCESS)
|
|
{
|
|
DriverAttributes = 0;
|
|
}
|
|
|
|
// Retrieve the version number
|
|
cbData = sizeof(DWORD);
|
|
if (SplRegQueryValue(hDriverKey, szDriverVersion, &Type, (LPBYTE)&Version, &cbData, pIniSpooler) != ERROR_SUCCESS)
|
|
{
|
|
Version = 0;
|
|
}
|
|
|
|
// Retrieve the TempDir number
|
|
cbData = sizeof(DWORD);
|
|
if (SplRegQueryValue(hDriverKey, szTempDir, &Type, (LPBYTE)&dwTempDir, &cbData, pIniSpooler) != ERROR_SUCCESS)
|
|
{
|
|
dwTempDir = 0;
|
|
}
|
|
|
|
// After REBOOT temp directories are deleted. So check for the presence of the
|
|
// directory on spooler startup.
|
|
if (dwTempDir && pIniEnvironment && pIniVersion)
|
|
{
|
|
StringCchPrintf(szTempDir, COUNTOF(szTempDir), L"%d", dwTempDir);
|
|
|
|
if(StrNCatBuff(szData,
|
|
COUNTOF(szData),
|
|
pIniSpooler->pDir,
|
|
L"\\drivers\\",
|
|
pIniEnvironment->pDirectory,
|
|
L"\\",
|
|
pIniVersion->szDirectory,
|
|
L"\\",
|
|
szTempDir,
|
|
NULL) == ERROR_SUCCESS)
|
|
{
|
|
if (!DirectoryExists(szData))
|
|
{
|
|
// Files must have been moved in Reboot, reset dwTempDir to 0
|
|
dwTempDir = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
SplRegCloseKey(hDriverKey, pIniSpooler);
|
|
hDriverKey = NULL;
|
|
}
|
|
|
|
//
|
|
// Win95 driver needs every file as a dependent file for point and print.
|
|
// For others we eliminate duplicates
|
|
//
|
|
if ( pIniEnvironment && _wcsicmp(pIniEnvironment->pName, szWin95Environment) ) {
|
|
|
|
pTemp = pDependentFiles;
|
|
pDependentFiles = NULL;
|
|
|
|
if ( !BuildTrueDependentFileField(pDriver,
|
|
pDataFile,
|
|
pConfigFile,
|
|
pHelpFile,
|
|
pTemp,
|
|
&pDependentFiles) )
|
|
goto Fail;
|
|
|
|
FreeSplMem(pTemp);
|
|
for ( pTemp = pDependentFiles ; pTemp && *pTemp ;
|
|
pTemp += wcslen(pTemp) + 1 )
|
|
;
|
|
|
|
if ( pTemp )
|
|
cchDependentFiles = (DWORD) (pTemp - pDependentFiles + 1);
|
|
else
|
|
cchDependentFiles = 0;
|
|
|
|
pTemp = NULL;
|
|
}
|
|
|
|
cb = sizeof( INIDRIVER );
|
|
|
|
if ( pIniDriver = AllocSplMem( cb )) {
|
|
|
|
pIniDriver->signature = ID_SIGNATURE;
|
|
pIniDriver->pName = pDriverName;
|
|
pIniDriver->pDriverFile = pDriver;
|
|
pIniDriver->pDataFile = pDataFile;
|
|
pIniDriver->pConfigFile = pConfigFile;
|
|
pIniDriver->cVersion = Version;
|
|
pIniDriver->pHelpFile = pHelpFile;
|
|
pIniDriver->pMonitorName = pMonitorName;
|
|
pIniDriver->pDefaultDataType = pDefaultDataType;
|
|
pIniDriver->pDependentFiles = pDependentFiles;
|
|
pIniDriver->cchDependentFiles = cchDependentFiles;
|
|
pIniDriver->pszzPreviousNames = pszzPreviousNames;
|
|
pIniDriver->cchPreviousNames = cchPreviousNames;
|
|
pIniDriver->dwTempDir = dwTempDir;
|
|
pIniDriver->pszMfgName = pszMfgName;
|
|
pIniDriver->pszOEMUrl = pszOEMUrl;
|
|
pIniDriver->pszHardwareID = pszHardwareID;
|
|
pIniDriver->pszProvider = pszProvider;
|
|
pIniDriver->dwlDriverVersion = DriverVersion;
|
|
pIniDriver->ftDriverDate = DriverDate;
|
|
pIniDriver->dwDriverAttributes = DriverAttributes;
|
|
|
|
DBGMSG( DBG_TRACE, ("Data for driver %ws created:\
|
|
\n\tpDriverFile:\t%ws\
|
|
\n\tpDataFile:\t%ws\
|
|
\n\tpConfigFile:\t%ws\n\n",
|
|
pDriverName, pDriver, pDataFile, pConfigFile));
|
|
|
|
if ( pIniDriver->pMonitorName && *pIniDriver->pMonitorName ) {
|
|
|
|
//
|
|
// Don't we add ref the monitor here?
|
|
//
|
|
pIniDriver->pIniLangMonitor = FindMonitor(pIniDriver->pMonitorName, pIniSpooler);
|
|
|
|
//
|
|
// Cluster spoolers do not have keep their own lists of language monitors.
|
|
// This is because most language monitors are not cluster aware. Therefore,
|
|
// cluster spooler share language monitors with the local spooler.
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
!pIniDriver->pIniLangMonitor &&
|
|
pLocalIniSpooler)
|
|
{
|
|
//
|
|
// We try to find the langauge monitor off the local spooler
|
|
//
|
|
pIniDriver->pIniLangMonitor = FindMonitor(pIniDriver->pMonitorName, pLocalIniSpooler);
|
|
}
|
|
|
|
if (!pIniDriver->pIniLangMonitor)
|
|
{
|
|
DBGMSG(DBG_TRACE, ("Can't find print monitor %ws\n", pIniDriver->pMonitorName));
|
|
}
|
|
|
|
}
|
|
|
|
return pIniDriver;
|
|
}
|
|
|
|
|
|
Fail:
|
|
|
|
FreeSplStr( pDriverName );
|
|
FreeSplStr( pConfigFile );
|
|
FreeSplStr( pDataFile );
|
|
FreeSplStr( pHelpFile );
|
|
FreeSplStr( pMonitorName );
|
|
FreeSplStr( pDefaultDataType );
|
|
FreeSplStr( pDependentFiles );
|
|
FreeSplStr( pszzPreviousNames );
|
|
FreeSplStr( pDriver );
|
|
FreeSplStr( pTemp);
|
|
FreeSplStr( pszMfgName);
|
|
FreeSplStr( pszOEMUrl);
|
|
FreeSplStr( pszProvider);
|
|
FreeSplStr( pszHardwareID);
|
|
|
|
if( hDriverKey ) {
|
|
SplRegCloseKey(hDriverKey, pIniSpooler);
|
|
}
|
|
|
|
SetLastError( dwLastError );
|
|
return NULL;
|
|
}
|
|
|
|
PINIDRIVER
|
|
FindLocalDriver(
|
|
PINISPOOLER pIniSpooler,
|
|
LPWSTR pz
|
|
)
|
|
{
|
|
PINIVERSION pIniVersion;
|
|
|
|
if ( !pz || !*pz ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// During Upgrade we load any driver so we have a valid printer, which might not be able to boot.
|
|
//
|
|
return FindCompatibleDriver(GetLocalArchEnv(pIniSpooler),
|
|
&pIniVersion,
|
|
pz,
|
|
dwMajorVersion,
|
|
dwUpgradeFlag);
|
|
}
|
|
|
|
|
|
BOOL
|
|
FindLocalDriverAndVersion(
|
|
PINISPOOLER pIniSpooler,
|
|
LPWSTR pz,
|
|
PINIDRIVER *ppIniDriver,
|
|
PINIVERSION *ppIniVersion
|
|
)
|
|
{
|
|
if ( !pz || !*pz || !ppIniDriver || !ppIniVersion) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// During Upgrade we load any driver so we have a valid printer, which might not be able to boot.
|
|
//
|
|
*ppIniDriver = FindCompatibleDriver( GetLocalArchEnv(pIniSpooler),
|
|
ppIniVersion,
|
|
pz,
|
|
dwMajorVersion,
|
|
dwUpgradeFlag );
|
|
|
|
if ( !*ppIniDriver || !*ppIniVersion ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#if DBG
|
|
VOID
|
|
InitializeDebug(
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
DWORD Status;
|
|
HKEY hKey = pIniSpooler->hckRoot;
|
|
DWORD cbData;
|
|
INT TimeOut = 60;
|
|
|
|
cbData = sizeof(DWORD);
|
|
|
|
Status = SplRegQueryValue( hKey,
|
|
szDebugFlags,
|
|
NULL,
|
|
(LPBYTE)&MODULE_DEBUG,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
// Wait until someone turns off the Pause Flag
|
|
|
|
if ( Status != NO_ERROR )
|
|
return;
|
|
|
|
while ( MODULE_DEBUG & DBG_PAUSE ) {
|
|
Sleep(1*1000);
|
|
if ( TimeOut-- == 0)
|
|
break;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("DebugFlags %x\n", MODULE_DEBUG));
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
VOID
|
|
GetPrintSystemVersion(
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
DWORD Status;
|
|
HKEY hKey;
|
|
DWORD cbData;
|
|
|
|
hKey = pIniSpooler->hckRoot;
|
|
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, szMinorVersion, NULL, NULL,
|
|
(LPBYTE)&dwMinorVersion, &cbData);
|
|
DBGMSG(DBG_TRACE, ("This Minor Version - %d\n", dwMinorVersion));
|
|
|
|
|
|
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"FastPrintWaitTimeout", NULL, NULL,
|
|
(LPBYTE)&dwFastPrintWaitTimeout, &cbData);
|
|
DBGMSG(DBG_TRACE, ("dwFastPrintWaitTimeout - %d\n", dwFastPrintWaitTimeout));
|
|
|
|
|
|
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"FastPrintThrottleTimeout", NULL, NULL,
|
|
(LPBYTE)&dwFastPrintThrottleTimeout, &cbData);
|
|
DBGMSG(DBG_TRACE, ("dwFastPrintThrottleTimeout - %d\n", dwFastPrintThrottleTimeout));
|
|
|
|
|
|
|
|
// If the values look invalid use Defaults
|
|
|
|
if (( dwFastPrintThrottleTimeout == 0) ||
|
|
( dwFastPrintWaitTimeout < dwFastPrintThrottleTimeout)) {
|
|
|
|
DBGMSG( DBG_WARNING, ("Bad timeout values FastPrintThrottleTimeout %d FastPrintWaitTimeout %d using defaults\n",
|
|
dwFastPrintThrottleTimeout, dwFastPrintWaitTimeout));
|
|
|
|
dwFastPrintThrottleTimeout = FASTPRINT_THROTTLE_TIMEOUT;
|
|
dwFastPrintWaitTimeout = FASTPRINT_WAIT_TIMEOUT;
|
|
|
|
}
|
|
|
|
// Calculate a reasonable Threshold based on the two timeouts
|
|
|
|
dwFastPrintSlowDownThreshold = dwFastPrintWaitTimeout / dwFastPrintThrottleTimeout;
|
|
|
|
|
|
// FastPrintSlowDownThreshold
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"FastPrintSlowDownThreshold", NULL, NULL,
|
|
(LPBYTE)&dwFastPrintSlowDownThreshold, &cbData);
|
|
DBGMSG(DBG_TRACE, ("dwFastPrintSlowDownThreshold - %d\n", dwFastPrintSlowDownThreshold));
|
|
|
|
// PortThreadPriority
|
|
cbData = sizeof dwPortThreadPriority;
|
|
Status = RegQueryValueEx(hKey, SPLREG_PORT_THREAD_PRIORITY, NULL, NULL,
|
|
(LPBYTE)&dwPortThreadPriority, &cbData);
|
|
|
|
if (Status != ERROR_SUCCESS ||
|
|
(dwPortThreadPriority != THREAD_PRIORITY_LOWEST &&
|
|
dwPortThreadPriority != THREAD_PRIORITY_BELOW_NORMAL &&
|
|
dwPortThreadPriority != THREAD_PRIORITY_NORMAL &&
|
|
dwPortThreadPriority != THREAD_PRIORITY_ABOVE_NORMAL &&
|
|
dwPortThreadPriority != THREAD_PRIORITY_HIGHEST)) {
|
|
|
|
dwPortThreadPriority = DEFAULT_PORT_THREAD_PRIORITY;
|
|
|
|
SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_PORT_THREAD_PRIORITY,
|
|
REG_DWORD,
|
|
(LPBYTE) &dwPortThreadPriority,
|
|
sizeof dwPortThreadPriority
|
|
);
|
|
}
|
|
DBGMSG(DBG_TRACE, ("dwPortThreadPriority - %d\n", dwPortThreadPriority));
|
|
|
|
|
|
// SchedulerThreadPriority
|
|
cbData = sizeof dwSchedulerThreadPriority;
|
|
Status = RegQueryValueEx(hKey, SPLREG_SCHEDULER_THREAD_PRIORITY, NULL, NULL,
|
|
(LPBYTE)&dwSchedulerThreadPriority, &cbData);
|
|
|
|
if (Status != ERROR_SUCCESS ||
|
|
(dwSchedulerThreadPriority != THREAD_PRIORITY_LOWEST &&
|
|
dwSchedulerThreadPriority != THREAD_PRIORITY_BELOW_NORMAL &&
|
|
dwSchedulerThreadPriority != THREAD_PRIORITY_NORMAL &&
|
|
dwSchedulerThreadPriority != THREAD_PRIORITY_ABOVE_NORMAL &&
|
|
dwSchedulerThreadPriority != THREAD_PRIORITY_HIGHEST)) {
|
|
|
|
dwSchedulerThreadPriority = DEFAULT_SCHEDULER_THREAD_PRIORITY;
|
|
|
|
SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_SCHEDULER_THREAD_PRIORITY,
|
|
REG_DWORD,
|
|
(LPBYTE) &dwSchedulerThreadPriority,
|
|
sizeof dwSchedulerThreadPriority
|
|
);
|
|
}
|
|
DBGMSG(DBG_TRACE, ("dwSchedulerThreadPriority - %d\n", dwSchedulerThreadPriority));
|
|
|
|
// WritePrinterSleepTime
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"WritePrinterSleepTime", NULL, NULL,
|
|
(LPBYTE)&dwWritePrinterSleepTime, &cbData);
|
|
DBGMSG(DBG_TRACE, ("dwWritePrinterSleepTime - %d\n", dwWritePrinterSleepTime));
|
|
|
|
// ServerThreadPriority
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"ServerThreadPriority", NULL, NULL,
|
|
(LPBYTE)&dwServerThreadPriority, &cbData);
|
|
DBGMSG(DBG_TRACE, ("dwServerThreadPriority - %d\n", dwServerThreadPriority));
|
|
|
|
// ServerThreadTimeout
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"ServerThreadTimeout", NULL, NULL,
|
|
(LPBYTE)&ServerThreadTimeout, &cbData);
|
|
DBGMSG(DBG_TRACE, ("ServerThreadTimeout - %d\n", ServerThreadTimeout));
|
|
|
|
|
|
// EnableBroadcastSpoolerStatus
|
|
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"EnableBroadcastSpoolerStatus", NULL, NULL,
|
|
(LPBYTE)&dwEnableBroadcastSpoolerStatus, &cbData);
|
|
DBGMSG(DBG_TRACE, ("EnableBroadcastSpoolerStatus - %d\n",
|
|
dwEnableBroadcastSpoolerStatus ));
|
|
|
|
// NetPrinterDecayPeriod
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"NetPrinterDecayPeriod", NULL, NULL,
|
|
(LPBYTE)&NetPrinterDecayPeriod, &cbData);
|
|
DBGMSG(DBG_TRACE, ("NetPrinterDecayPeriod - %d\n", NetPrinterDecayPeriod));
|
|
|
|
|
|
// RefreshTimesPerDecayPeriod
|
|
cbData = sizeof(DWORD);
|
|
RegQueryValueEx(hKey, L"RefreshTimesPerDecayPeriod", NULL, NULL,
|
|
(LPBYTE)&RefreshTimesPerDecayPeriod, &cbData);
|
|
DBGMSG(DBG_TRACE, ("RefreshTimesPerDecayPeriod - %d\n", RefreshTimesPerDecayPeriod));
|
|
|
|
if ( RefreshTimesPerDecayPeriod == 0 ) {
|
|
|
|
RefreshTimesPerDecayPeriod = DEFAULT_REFRESH_TIMES_PER_DECAY_PERIOD;
|
|
}
|
|
|
|
// BrowsePrintWorkstations
|
|
cbData = sizeof( BrowsePrintWorkstations );
|
|
RegQueryValueEx( hKey, L"BrowsePrintWorkstations", NULL, NULL, (LPBYTE)&BrowsePrintWorkstations, &cbData );
|
|
|
|
DBGMSG( DBG_TRACE, ("BrowsePrintWorkstations - %d\n", BrowsePrintWorkstations ));
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
AllowFaxSharing
|
|
|
|
Routine Description:
|
|
|
|
This routine looks at the sku to determine whether we should allowing sharing
|
|
out and printing to a fax driver.
|
|
|
|
We don't allow sharing the fax printer on Personal & Professional.
|
|
We also don't allow sharing of the fax printer on a Web Blade.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE - We can share out the fax driver.
|
|
|
|
--*/
|
|
BOOL
|
|
AllowFaxSharing(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL bAllowFaxSharing = TRUE;
|
|
|
|
DWORDLONG dwlConditionMask = 0;
|
|
OSVERSIONINFOEX osvi = { 0 };
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
|
|
osvi.wProductType = VER_NT_WORKSTATION;
|
|
VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_LESS_EQUAL);
|
|
|
|
//
|
|
// We allow sharing if it isn't WORKSTATION or PRO.
|
|
//
|
|
bAllowFaxSharing = !VerifyVersionInfo(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
|
|
|
|
if (bAllowFaxSharing)
|
|
{
|
|
ZeroMemory(&osvi, sizeof(osvi));
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
osvi.wSuiteMask = VER_SUITE_BLADE;
|
|
|
|
VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_OR);
|
|
|
|
//
|
|
// We allow sharing if it isn't a web blade.
|
|
//
|
|
bAllowFaxSharing = !VerifyVersionInfo(&osvi, VER_SUITENAME, dwlConditionMask);
|
|
}
|
|
|
|
return bAllowFaxSharing;
|
|
}
|
|
|
|
VOID
|
|
InitializeSpoolerSettings(
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
HKEY hKey;
|
|
HKEY hKeyProvider;
|
|
DWORD cbData;
|
|
DWORD dwLastError;
|
|
DWORD Status;
|
|
DWORD CacheMasqPrinters;
|
|
|
|
DWORDLONG dwlConditionMask = 0;
|
|
OSVERSIONINFOEX osvi;
|
|
|
|
hKey = pIniSpooler->hckRoot;
|
|
|
|
//
|
|
// BeepEnabled
|
|
//
|
|
cbData = sizeof pIniSpooler->dwBeepEnabled;
|
|
Status = SplRegQueryValue(hKey,
|
|
SPLREG_BEEP_ENABLED,
|
|
NULL,
|
|
(LPBYTE)&pIniSpooler->dwBeepEnabled,
|
|
&cbData,
|
|
pIniSpooler);
|
|
|
|
if (Status!=ERROR_SUCCESS) {
|
|
DBGMSG(DBG_TRACE, ("BeepEnabled - SplRegQueryValue failed with error %u\n", Status));
|
|
}
|
|
|
|
pIniSpooler->dwBeepEnabled = !!pIniSpooler->dwBeepEnabled;
|
|
|
|
SetPrinterDataServer(pIniSpooler,
|
|
SPLREG_BEEP_ENABLED,
|
|
REG_DWORD,
|
|
(LPBYTE) &pIniSpooler->dwBeepEnabled,
|
|
sizeof pIniSpooler->dwBeepEnabled);
|
|
|
|
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
|
|
|
|
//
|
|
// Restart job time.
|
|
//
|
|
cbData = sizeof( pIniSpooler->dwJobCompletionTimeout );
|
|
Status = SplRegQueryValue( hKey,
|
|
L"JobCompletionTimeout",
|
|
NULL,
|
|
(LPBYTE)&pIniSpooler->dwJobCompletionTimeout,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
pIniSpooler->dwJobCompletionTimeout = DEFAULT_JOB_COMPLETION_TIMEOUT;
|
|
}
|
|
|
|
DBGMSG( DBG_TRACE, ("JobCompletionTimeout - %d\n", pIniSpooler->dwJobCompletionTimeout ));
|
|
}
|
|
|
|
//
|
|
// Retrieve whether we want to cache masq printer settings, default is FALSE.
|
|
//
|
|
cbData = sizeof(CacheMasqPrinters);
|
|
|
|
Status = SplRegQueryValue(hKey,
|
|
gszCacheMasqPrinters,
|
|
NULL,
|
|
(BYTE *)&CacheMasqPrinters,
|
|
&cbData,
|
|
pIniSpooler);
|
|
|
|
//
|
|
// We only set the bit for caching masq printers if CacbeMasqPrinters is
|
|
// non-NULL.
|
|
//
|
|
if (Status == ERROR_SUCCESS && CacheMasqPrinters != 0) {
|
|
|
|
pIniSpooler->dwSpoolerSettings |= SPOOLER_CACHEMASQPRINTERS;
|
|
}
|
|
|
|
//
|
|
// Some Folks like the NT FAX Service Don't want people to be able
|
|
// to remotely print with specific printer drivers.
|
|
//
|
|
if (!AllowFaxSharing()) {
|
|
|
|
pIniSpooler->pNoRemotePrintDrivers = AllocSplStr(szNTFaxDriver);
|
|
pIniSpooler->cchNoRemotePrintDrivers = wcslen(szNTFaxDriver) + 1;
|
|
gbRemoteFax = FALSE;
|
|
}
|
|
|
|
//
|
|
// If this is embedded NT, then allow masq printers to get non-RAW
|
|
// jobs. This is for Xerox.
|
|
//
|
|
dwlConditionMask = 0;
|
|
ZeroMemory(&osvi, sizeof(osvi));
|
|
|
|
osvi.wSuiteMask = VER_SUITE_EMBEDDEDNT;
|
|
VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_OR);
|
|
|
|
if (VerifyVersionInfo( &osvi,
|
|
VER_SUITENAME,
|
|
dwlConditionMask)) {
|
|
|
|
pIniSpooler->SpoolerFlags |= SPL_NON_RAW_TO_MASQ_PRINTERS;
|
|
}
|
|
|
|
Status = SplRegCreateKey( pIniSpooler->hckRoot,
|
|
pIniSpooler->pszRegistryProviders,
|
|
0,
|
|
KEY_READ,
|
|
NULL,
|
|
&hKeyProvider,
|
|
NULL,
|
|
pIniSpooler );
|
|
|
|
if( Status == NO_ERROR ){
|
|
|
|
DWORD Flags;
|
|
|
|
// NonRawToMasqPrinters
|
|
|
|
cbData = sizeof( Flags );
|
|
Status = SplRegQueryValue( hKeyProvider,
|
|
SPLREG_NON_RAW_TO_MASQ_PRINTERS,
|
|
NULL,
|
|
(LPBYTE)&Flags,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
if (Flags) {
|
|
pIniSpooler->SpoolerFlags |= SPL_NON_RAW_TO_MASQ_PRINTERS;
|
|
}
|
|
}
|
|
|
|
// EventLog
|
|
|
|
cbData = sizeof( Flags );
|
|
Status = SplRegQueryValue( hKeyProvider,
|
|
SPLREG_EVENT_LOG,
|
|
NULL,
|
|
(LPBYTE)&Flags,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
pIniSpooler->dwEventLogging = Flags;
|
|
|
|
} else {
|
|
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_EVENT_LOG,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->dwEventLogging,
|
|
sizeof( pIniSpooler->dwEventLogging ));
|
|
}
|
|
|
|
// NetPopup
|
|
|
|
cbData = sizeof( Flags );
|
|
Status = SplRegQueryValue( hKeyProvider,
|
|
SPLREG_NET_POPUP,
|
|
NULL,
|
|
(LPBYTE)&Flags,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
pIniSpooler->bEnableNetPopups = !!Flags;
|
|
|
|
if (Flags != 1 && Flags != 0) {
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_NET_POPUP,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bEnableNetPopups,
|
|
sizeof( pIniSpooler->bEnableNetPopups ));
|
|
}
|
|
} else {
|
|
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_NET_POPUP,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bEnableNetPopups,
|
|
sizeof( pIniSpooler->bEnableNetPopups ));
|
|
}
|
|
|
|
// NetPopupToComputer
|
|
|
|
cbData = sizeof( Flags );
|
|
Status = SplRegQueryValue( hKeyProvider,
|
|
SPLREG_NET_POPUP_TO_COMPUTER,
|
|
NULL,
|
|
(LPBYTE)&Flags,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
pIniSpooler->bEnableNetPopupToComputer = !!Flags;
|
|
|
|
if (Flags != 1 && Flags != 0) {
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_NET_POPUP_TO_COMPUTER,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bEnableNetPopupToComputer,
|
|
sizeof( pIniSpooler->bEnableNetPopupToComputer ));
|
|
}
|
|
} else {
|
|
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_NET_POPUP_TO_COMPUTER,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bEnableNetPopupToComputer,
|
|
sizeof( pIniSpooler->bEnableNetPopupToComputer ));
|
|
}
|
|
|
|
// RetryPopup
|
|
|
|
cbData = sizeof( Flags );
|
|
Status = SplRegQueryValue( hKeyProvider,
|
|
SPLREG_RETRY_POPUP,
|
|
NULL,
|
|
(LPBYTE)&Flags,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
pIniSpooler->bEnableRetryPopups = !!Flags;
|
|
|
|
if (Flags != 1 && Flags != 0) {
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_RETRY_POPUP,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bEnableRetryPopups,
|
|
sizeof( pIniSpooler->bEnableRetryPopups ));
|
|
}
|
|
} else {
|
|
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_RETRY_POPUP,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bEnableRetryPopups,
|
|
sizeof( pIniSpooler->bEnableRetryPopups ));
|
|
}
|
|
|
|
// RestartJobOnPoolError
|
|
|
|
cbData = sizeof( Flags );
|
|
Status = SplRegQueryValue( hKeyProvider,
|
|
SPLREG_RESTART_JOB_ON_POOL_ERROR,
|
|
NULL,
|
|
(LPBYTE)&Flags,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
pIniSpooler->dwRestartJobOnPoolTimeout = Flags;
|
|
|
|
} else {
|
|
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_RESTART_JOB_ON_POOL_ERROR,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->dwRestartJobOnPoolTimeout,
|
|
sizeof( pIniSpooler->dwRestartJobOnPoolTimeout ));
|
|
}
|
|
|
|
// RestartJobOnPoolEnabled
|
|
cbData = sizeof( Flags );
|
|
Status = SplRegQueryValue( hKeyProvider,
|
|
SPLREG_RESTART_JOB_ON_POOL_ENABLED,
|
|
NULL,
|
|
(LPBYTE)&Flags,
|
|
&cbData,
|
|
pIniSpooler );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
pIniSpooler->bRestartJobOnPoolEnabled = !!Flags;
|
|
|
|
if (Flags != 1 && Flags != 0) {
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_RESTART_JOB_ON_POOL_ENABLED,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bRestartJobOnPoolEnabled,
|
|
sizeof( pIniSpooler->bRestartJobOnPoolEnabled ));
|
|
}
|
|
} else {
|
|
|
|
Status = SetPrinterDataServer( pIniSpooler,
|
|
SPLREG_RESTART_JOB_ON_POOL_ENABLED,
|
|
REG_DWORD,
|
|
(LPBYTE)&pIniSpooler->bRestartJobOnPoolEnabled,
|
|
sizeof( pIniSpooler->bRestartJobOnPoolEnabled ));
|
|
}
|
|
|
|
SplRegCloseKey( hKeyProvider, pIniSpooler );
|
|
}
|
|
GetDiscardTSJobsSettings( pIniSpooler );
|
|
GetServerFilePoolSettings( pIniSpooler );
|
|
}
|
|
|
|
VOID
|
|
GetServerFilePoolSettings(
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
//
|
|
// We read the registry key DisableServerFilePooling from
|
|
// HKLM\System\CurrentControlSet\Control\Print once on spooler startup.
|
|
// If the value is set, filepooling for the server is disabled.
|
|
//
|
|
DWORD RegValue = 0;
|
|
DWORD RegValueSize = sizeof(RegValue);
|
|
HKEY RegKey = NULL;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD RegValueType;
|
|
|
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
ipszRegistryRoot,
|
|
0,
|
|
KEY_READ,
|
|
&RegKey );
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
dwError = RegQueryValueEx( RegKey,
|
|
szDisableFilePooling,
|
|
NULL,
|
|
(LPDWORD) &RegValueType,
|
|
(LPBYTE) &RegValue,
|
|
&RegValueSize );
|
|
|
|
if ( RegValueType == REG_DWORD && dwError == ERROR_SUCCESS && RegValue > 0)
|
|
{
|
|
pIniSpooler->dwSpoolerSettings |= SPOOLER_NOFILEPOOLING;
|
|
}
|
|
}
|
|
|
|
if (RegKey)
|
|
{
|
|
RegCloseKey( RegKey );
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
FinalInitAfterRouterInitComplete(
|
|
DWORD dwUpgrade,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This thread does LocalSpl initialization that has to happen after
|
|
the router has completely initialized.
|
|
|
|
There are 2 jobs:-
|
|
Upgrading Printer Driver Data
|
|
Sharing Printers
|
|
|
|
Ensures that printers are shared. This case occurs when the spooler
|
|
service not running on startup (and the server is), and then the
|
|
user starts the spooler.
|
|
|
|
We also get the benefit of closing down any invalid printer handles
|
|
(in the server).
|
|
|
|
Arguments:
|
|
|
|
dwUpgrade != 0 upgrade printer driver data.
|
|
|
|
Return Value:
|
|
|
|
DWORD - ignored
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwPort;
|
|
PINIPORT pIniPort;
|
|
PINIMONITOR pIniLangMonitor;
|
|
PINIPRINTER pIniPrinter;
|
|
PINIPRINTER pIniPrinterNext;
|
|
|
|
// Do Not share all the printers during an Upgrade.
|
|
|
|
if ( dwUpgrade ) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
WaitForSpoolerInitialization();
|
|
|
|
// Try pending driver upgrades on spooler startup
|
|
PendingDriverUpgrades(NULL);
|
|
|
|
EnterSplSem();
|
|
|
|
// Delete printers in pending deletion state with no jobs
|
|
CleanupDeletedPrinters(pIniSpooler);
|
|
|
|
|
|
//
|
|
// Re-share all shared printers.
|
|
//
|
|
|
|
for( pIniPrinter = pIniSpooler->pIniPrinter;
|
|
pIniPrinter;
|
|
pIniPrinter = pIniPrinterNext ) {
|
|
|
|
if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) {
|
|
//
|
|
// Up the ref count to prevent deletion
|
|
//
|
|
INCPRINTERREF( pIniPrinter );
|
|
|
|
//
|
|
// Unshare it first. We are doing it both to close all handles in the
|
|
// server ( for the case when the printer is shared ) or to just unshare
|
|
// the printer if the policy says so ( we cleared the attribute above )
|
|
//
|
|
ShareThisPrinter( pIniPrinter,
|
|
pIniPrinter->pShareName,
|
|
FALSE );
|
|
|
|
//
|
|
// We are checking if the printer is still shared ( the attribute could have been
|
|
// cleared as a result of the remote connctions policy being off or , if that's
|
|
// not the case, as a result of leaving the Spooler semaphore in ShareThisPrinter)
|
|
//
|
|
if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) {
|
|
|
|
BOOL bReturn;
|
|
|
|
//
|
|
// Now share it again.
|
|
//
|
|
bReturn = ShareThisPrinter( pIniPrinter,
|
|
pIniPrinter->pShareName,
|
|
TRUE );
|
|
|
|
if( !bReturn ){
|
|
|
|
DWORD rc = GetLastError();
|
|
|
|
if( rc != NERR_ServerNotStarted &&
|
|
rc != NERR_DuplicateShare ){
|
|
|
|
WCHAR szError[256];
|
|
|
|
DBGMSG( DBG_WARNING,
|
|
( "NetShareAdd failed %lx\n", rc));
|
|
|
|
StringCchPrintf(szError, COUNTOF(szError), L"+ %d", rc);
|
|
|
|
SplLogEvent( pIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_SHARE_FAILED,
|
|
TRUE,
|
|
szError,
|
|
pIniPrinter->pName,
|
|
pIniPrinter->pShareName,
|
|
NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
DECPRINTERREF( pIniPrinter );
|
|
pIniPrinterNext = pIniPrinter->pNext;
|
|
|
|
|
|
} else {
|
|
|
|
//
|
|
// The unshared case.
|
|
//
|
|
pIniPrinterNext = pIniPrinter->pNext;
|
|
}
|
|
|
|
INCPRINTERREF(pIniPrinter);
|
|
|
|
if (pIniPrinterNext)
|
|
INCPRINTERREF(pIniPrinterNext);
|
|
|
|
for ( dwPort = 0 ; dwPort < pIniPrinter->cPorts ; ++dwPort ) {
|
|
|
|
pIniPort = pIniPrinter->ppIniPorts[dwPort];
|
|
|
|
//
|
|
// Bidi monitor can inform spooler of errors. First
|
|
// printer will keep the port at the beginning
|
|
//
|
|
if ( pIniPort->ppIniPrinter[0] != pIniPrinter )
|
|
continue;
|
|
|
|
if ( !pIniPort->hMonitorHandle && dwUpgradeFlag == 0 ) {
|
|
|
|
LPTSTR pszPrinter;
|
|
TCHAR szFullPrinter[ MAX_UNC_PRINTER_NAME ];
|
|
|
|
if( pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ){
|
|
|
|
pszPrinter = szFullPrinter;
|
|
StringCchPrintf(szFullPrinter,
|
|
COUNTOF(szFullPrinter),
|
|
L"%ws\\%ws",
|
|
pIniSpooler->pMachineName,
|
|
pIniPrinter->pName);
|
|
} else {
|
|
|
|
pszPrinter = pIniPrinter->pName;
|
|
}
|
|
|
|
if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI )
|
|
pIniLangMonitor = pIniPrinter->pIniDriver->pIniLangMonitor;
|
|
else
|
|
pIniLangMonitor = NULL;
|
|
|
|
OpenMonitorPort(pIniPort,
|
|
pIniLangMonitor,
|
|
pszPrinter);
|
|
ReleaseMonitorPort(pIniPort);
|
|
|
|
}
|
|
}
|
|
|
|
if (pIniPrinterNext)
|
|
DECPRINTERREF(pIniPrinterNext);
|
|
|
|
DECPRINTERREF(pIniPrinter);
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DWORD
|
|
FinalInitAfterRouterInitCompleteThread(
|
|
DWORD dwUpgrade
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Async thread called when initializing provider.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
return FinalInitAfterRouterInitComplete( dwUpgrade, pLocalIniSpooler );
|
|
}
|
|
|
|
// DEBUG PURPOSE ONLY - - returns TRUE if pMem is an IniSpooler, FALSE otherwise
|
|
BOOL
|
|
NotIniSpooler(
|
|
BYTE *pMem
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
|
|
for (pIniSpooler = pLocalIniSpooler ; pIniSpooler ; pIniSpooler = pIniSpooler->pIniNextSpooler)
|
|
if (pIniSpooler == (PINISPOOLER) pMem)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
ValidateProductSuite(
|
|
PWSTR pszSuiteName
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LONG Rslt;
|
|
HKEY hKey = NULL;
|
|
DWORD Type = 0;
|
|
DWORD Size = 0;
|
|
PWSTR pszProductSuite = NULL;
|
|
PWSTR psz;
|
|
|
|
|
|
Rslt = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
ipszRegistryProductOptions,
|
|
0,
|
|
KEY_READ,
|
|
&hKey);
|
|
|
|
if (Rslt != ERROR_SUCCESS)
|
|
goto exit;
|
|
|
|
Rslt = RegQueryValueEx(
|
|
hKey,
|
|
L"ProductSuite",
|
|
NULL,
|
|
&Type,
|
|
NULL,
|
|
&Size
|
|
);
|
|
if (!Size || Rslt != ERROR_SUCCESS)
|
|
goto exit;
|
|
|
|
pszProductSuite = AllocSplMem(Size);
|
|
if (!pszProductSuite) {
|
|
goto exit;
|
|
}
|
|
|
|
Rslt = RegQueryValueEx(
|
|
hKey,
|
|
L"ProductSuite",
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE) pszProductSuite,
|
|
&Size
|
|
);
|
|
if (Rslt != ERROR_SUCCESS || Type != REG_MULTI_SZ)
|
|
goto exit;
|
|
|
|
for(psz = pszProductSuite ; *psz && wcscmp(psz, pszSuiteName) ; psz += wcslen(psz) + 1)
|
|
;
|
|
if (*psz)
|
|
bRet = TRUE;
|
|
|
|
exit:
|
|
|
|
FreeSplMem(pszProductSuite);
|
|
|
|
if (hKey)
|
|
RegCloseKey(hKey);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
ClusterAddOrUpdateDriverFromClusterDisk
|
|
|
|
Routine Description:
|
|
|
|
Takes in a driver key and a cluster type pIniSpooler. It will add
|
|
a driver from the cluster disk to the cluster spooler. If the driver
|
|
already exists in the list of drivers, then it will attempt to upgrade
|
|
it.
|
|
|
|
Arguments:
|
|
|
|
hKeyVersion - key to Ex. "Environments\Windows NT x86\Drivers\Version-3"
|
|
pszDriverName - name a a driver
|
|
pszEnvName - environemnt of the driver
|
|
pszenvDir - directory for the driver files on the disk (Ex. w32x86)
|
|
pIniSpooler - cluster type pIniSpooler
|
|
|
|
Return Value:
|
|
|
|
Win32 error code
|
|
|
|
--*/
|
|
DWORD
|
|
ClusterAddOrUpdateDriverFromClusterDisk(
|
|
IN HKEY hKeyVersion,
|
|
IN LPCWSTR pszDriverName,
|
|
IN LPCWSTR pszEnvName,
|
|
IN LPCWSTR pszEnvDir,
|
|
IN PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
LPWSTR pszzPathDepFiles = NULL;
|
|
DRIVER_INFO_6 Drv = {0};
|
|
HKEY hDrvKey = NULL;
|
|
WCHAR szVerPath[10] = {0};
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
WCHAR szData[MAX_PATH];
|
|
WCHAR szPathConfigFile[MAX_PATH];
|
|
WCHAR szPathDataFile[MAX_PATH];
|
|
WCHAR szPathDriverFile[MAX_PATH];
|
|
WCHAR szPathHelpFile[MAX_PATH];
|
|
DWORD cbData;
|
|
DWORD cLen;
|
|
|
|
//
|
|
// Open the driver's key
|
|
//
|
|
if ((dwError = SplRegOpenKey(hKeyVersion,
|
|
pszDriverName,
|
|
KEY_READ,
|
|
&hDrvKey,
|
|
pIniSpooler)) == ERROR_SUCCESS &&
|
|
(dwError = (Drv.pName = AllocSplStr(pszDriverName)) ? ERROR_SUCCESS : GetLastError()) == ERROR_SUCCESS &&
|
|
RegGetString(hDrvKey, szConfigurationKey, &Drv.pConfigFile, &cLen, &dwError, TRUE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szDataFileKey, &Drv.pDataFile, &cLen, &dwError, TRUE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szDriverFile, &Drv.pDriverPath, &cLen, &dwError, TRUE, pIniSpooler) &&
|
|
(dwError = Drv.pConfigFile &&
|
|
Drv.pDataFile &&
|
|
Drv.pDriverPath ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER) == ERROR_SUCCESS &&
|
|
RegGetString(hDrvKey, szHelpFile, &Drv.pHelpFile, &cLen, &dwError, FALSE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szMonitor, &Drv.pMonitorName, &cLen, &dwError, FALSE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szDatatype, &Drv.pDefaultDataType, &cLen, &dwError, FALSE, pIniSpooler) &&
|
|
RegGetMultiSzString(hDrvKey, szDependentFiles, &Drv.pDependentFiles, &cLen, &dwError, FALSE, pIniSpooler) &&
|
|
RegGetMultiSzString(hDrvKey, szPreviousNames, &Drv.pszzPreviousNames,&cLen, &dwError, FALSE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szMfgName, &Drv.pszMfgName, &cLen, &dwError, FALSE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szOEMUrl, &Drv.pszOEMUrl, &cLen, &dwError, FALSE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szHardwareID, &Drv.pszHardwareID, &cLen, &dwError, TRUE, pIniSpooler) &&
|
|
RegGetString(hDrvKey, szProvider, &Drv.pszProvider, &cLen, &dwError, TRUE, pIniSpooler) &&
|
|
(dwError = ClusterFindLanguageMonitor(Drv.pMonitorName, pszEnvName, pIniSpooler)) == ERROR_SUCCESS)
|
|
{
|
|
cbData = sizeof(Drv.ftDriverDate);
|
|
SplRegQueryValue(hDrvKey,
|
|
szDriverDate,
|
|
NULL,
|
|
(LPBYTE)&Drv.ftDriverDate,
|
|
&cbData,
|
|
pIniSpooler);
|
|
|
|
cbData = sizeof(Drv.dwlDriverVersion);
|
|
SplRegQueryValue(hDrvKey,
|
|
szLongVersion,
|
|
NULL,
|
|
(LPBYTE)&Drv.dwlDriverVersion,
|
|
&cbData,
|
|
pIniSpooler);
|
|
|
|
cbData = sizeof(Drv.cVersion);
|
|
SplRegQueryValue(hDrvKey,
|
|
szDriverVersion,
|
|
NULL,
|
|
(LPBYTE)&Drv.cVersion,
|
|
&cbData,
|
|
pIniSpooler);
|
|
|
|
//
|
|
// We need the matching version <-> directory on disk
|
|
// Ex. Version-3 <-> 3
|
|
//
|
|
StringCchPrintf(szVerPath, COUNTOF(szVerPath), L"%u", Drv.cVersion);
|
|
|
|
//
|
|
// Get fully qualified driver file paths. We will do an add printer driver
|
|
// without using the scratch directory. So the files have to be fully
|
|
// qualified
|
|
//
|
|
if ((dwError = StrNCatBuff(szPathDriverFile,
|
|
COUNTOF(szPathDriverFile),
|
|
pIniSpooler->pszClusResDriveLetter, L"\\",
|
|
szClusterDriverRoot, L"\\",
|
|
pszEnvDir, L"\\",
|
|
szVerPath, L"\\",
|
|
Drv.pDriverPath,
|
|
NULL)) == ERROR_SUCCESS &&
|
|
(dwError = StrNCatBuff(szPathDataFile,
|
|
COUNTOF(szPathDataFile),
|
|
pIniSpooler->pszClusResDriveLetter, L"\\",
|
|
szClusterDriverRoot, L"\\",
|
|
pszEnvDir, L"\\",
|
|
szVerPath, L"\\",
|
|
Drv.pDataFile,
|
|
NULL)) == ERROR_SUCCESS &&
|
|
(dwError = StrNCatBuff(szPathConfigFile,
|
|
COUNTOF(szPathConfigFile),
|
|
pIniSpooler->pszClusResDriveLetter, L"\\",
|
|
szClusterDriverRoot, L"\\",
|
|
pszEnvDir, L"\\",
|
|
szVerPath, L"\\",
|
|
Drv.pConfigFile,
|
|
NULL)) == ERROR_SUCCESS &&
|
|
(dwError = StrNCatBuff(szPathHelpFile,
|
|
COUNTOF(szPathHelpFile),
|
|
pIniSpooler->pszClusResDriveLetter, L"\\",
|
|
szClusterDriverRoot, L"\\",
|
|
pszEnvDir, L"\\",
|
|
szVerPath, L"\\",
|
|
Drv.pHelpFile,
|
|
NULL)) == ERROR_SUCCESS &&
|
|
(dwError = StrNCatBuff(szData,
|
|
COUNTOF(szData),
|
|
pIniSpooler->pszClusResDriveLetter, L"\\",
|
|
szClusterDriverRoot, L"\\",
|
|
pszEnvDir, L"\\",
|
|
szVerPath, L"\\",
|
|
NULL)) == ERROR_SUCCESS &&
|
|
(dwError = StrCatPrefixMsz(szData,
|
|
Drv.pDependentFiles,
|
|
&pszzPathDepFiles)) == ERROR_SUCCESS)
|
|
{
|
|
LPWSTR pszTempDriver = Drv.pDriverPath;
|
|
LPWSTR pszTempData = Drv.pDataFile;
|
|
LPWSTR pszTempConfig = Drv.pConfigFile;
|
|
LPWSTR pszTempHelp = Drv.pHelpFile;
|
|
LPWSTR pszTempDep = Drv.pDependentFiles;
|
|
|
|
DBGMSG(DBG_CLUSTER, ("ClusterAddOrUpDrv szPathDriverFile = "TSTR"\n", szPathDriverFile));
|
|
DBGMSG(DBG_CLUSTER, ("ClusterAddOrUpDrv szPathDataFile = "TSTR"\n", szPathDataFile));
|
|
DBGMSG(DBG_CLUSTER, ("ClusterAddOrUpDrv szPathConfigFile = "TSTR"\n", szPathConfigFile));
|
|
DBGMSG(DBG_CLUSTER, ("ClusterAddOrUpDrv szPathHelpFile = "TSTR"\n", szPathHelpFile));
|
|
|
|
Drv.pDriverPath = szPathDriverFile;
|
|
Drv.pEnvironment = (LPWSTR)pszEnvName;
|
|
Drv.pDataFile = szPathDataFile;
|
|
Drv.pConfigFile = szPathConfigFile;
|
|
Drv.pHelpFile = szPathHelpFile;
|
|
Drv.pDependentFiles = pszzPathDepFiles;
|
|
|
|
if (!SplAddPrinterDriverEx(NULL,
|
|
6,
|
|
(LPBYTE)&Drv,
|
|
APD_COPY_NEW_FILES | APD_DONT_COPY_FILES_TO_CLUSTER,
|
|
pIniSpooler,
|
|
FALSE,
|
|
DO_NOT_IMPERSONATE_USER))
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
|
|
//
|
|
// Restore pointers
|
|
//
|
|
Drv.pDriverPath = pszTempDriver;
|
|
Drv.pConfigFile = pszTempConfig;
|
|
Drv.pDataFile = pszTempData;
|
|
Drv.pHelpFile = pszTempHelp;
|
|
Drv.pDependentFiles = pszTempDep;
|
|
}
|
|
}
|
|
|
|
FreeSplStr(Drv.pName);
|
|
FreeSplStr(Drv.pDriverPath);
|
|
FreeSplStr(Drv.pConfigFile);
|
|
FreeSplStr(Drv.pDataFile);
|
|
FreeSplStr(Drv.pHelpFile);
|
|
FreeSplStr(Drv.pMonitorName);
|
|
FreeSplStr(Drv.pDefaultDataType);
|
|
FreeSplStr(Drv.pDependentFiles);
|
|
FreeSplStr(Drv.pszzPreviousNames);
|
|
FreeSplStr(Drv.pszMfgName);
|
|
FreeSplStr(Drv.pszOEMUrl);
|
|
FreeSplStr(Drv.pszProvider);
|
|
FreeSplStr(Drv.pszHardwareID);
|
|
FreeSplStr(pszzPathDepFiles);
|
|
|
|
if (hDrvKey)
|
|
{
|
|
SplRegCloseKey(hDrvKey, pIniSpooler);
|
|
}
|
|
|
|
DBGMSG(DBG_CLUSTER, ("ClusterAddOrUpdateDriverFromClusterDisk returns Win32 error %u\n", dwError));
|
|
|
|
return dwError;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
SplCreateSpoolerWorkerThread
|
|
|
|
Routine Description:
|
|
|
|
This routine will be launched in a separate thread to perform time consuming
|
|
initialization as part of SplCreateSpooler when the spooler is a cluster spooler.
|
|
Tasks that it will do include copying down ICM profiles from the cluster disk.
|
|
The caller needs to AddRef the pIniSpooler so that it doesn't become invalid
|
|
(deleted) while we are using it.
|
|
|
|
This function closes the hClusSplReady event handle.
|
|
|
|
Arguments:
|
|
|
|
PINISPOOLER pIniSpooler
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
VOID
|
|
SplCreateSpoolerWorkerThread(
|
|
IN PVOID pv
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
WCHAR szDir[MAX_PATH];
|
|
|
|
pIniSpooler = (PINISPOOLER)pv;
|
|
|
|
if (pIniSpooler &&
|
|
pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER &&
|
|
pIniSpooler->hClusSplReady)
|
|
{
|
|
HANDLE hSplReady = pIniSpooler->hClusSplReady;
|
|
|
|
//
|
|
// Waiting for the creating function (SplCreateSpooler) to terminate
|
|
//
|
|
WaitForSingleObject(pIniSpooler->hClusSplReady, INFINITE);
|
|
|
|
EnterSplSem();
|
|
|
|
pIniSpooler->hClusSplReady = NULL;
|
|
|
|
LeaveSplSem();
|
|
|
|
//
|
|
// We use hSplReady so we do not hold the critical section while doing CloseHandle
|
|
//
|
|
CloseHandle(hSplReady);
|
|
|
|
CopyICMFromClusterDiskToLocalDisk(pIniSpooler);
|
|
|
|
//
|
|
// If the node was upgraded, we need to upgrade the print drivers
|
|
// We cannot load ntprint and printui. So we create a process and
|
|
// call an entry point in ntprint. That one will enumerate all the
|
|
// cluster drivers and will try to upgrade them based on the new cab.
|
|
//
|
|
if (pIniSpooler->dwClusNodeUpgraded)
|
|
{
|
|
DWORD dwError;
|
|
DWORD dwCode = 0;
|
|
LPWSTR pszCommand = NULL;
|
|
LPWSTR pszExe = NULL;
|
|
|
|
//
|
|
// We need to pass as argument the name of the cluster spooler
|
|
//
|
|
if ((dwError = StrCatSystemPath(L"rundll32.exe",
|
|
kSystemDir,
|
|
&pszExe)) == ERROR_SUCCESS &&
|
|
(dwError = StrCatAlloc(&pszCommand,
|
|
L"rundll32.exe ntprint.dll,PSetupUpgradeClusterDrivers ",
|
|
pIniSpooler->pMachineName,
|
|
NULL)) == ERROR_SUCCESS &&
|
|
(dwError = RunProcess(pszExe, pszCommand, INFINITE, &dwCode)) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// dwCode is the return code of the function PSetupUpgradeClusterDrivers in ntprint,
|
|
// executed inside the rundll32 process
|
|
//
|
|
if (dwCode == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// We upgraded all the printer drivers, now we delete the key from the registry
|
|
// so we don't go though upgrading printer drivers again next time when the
|
|
// cluster group comes online on this node
|
|
//
|
|
ClusterSplDeleteUpgradeKey(pIniSpooler->pszClusResID);
|
|
}
|
|
else
|
|
{
|
|
DBGMSG(DBG_WARN, ("Error upgrading cluster drivers! dwCode %u\n", dwCode));
|
|
}
|
|
}
|
|
|
|
FreeSplMem(pszCommand);
|
|
FreeSplMem(pszExe);
|
|
|
|
DBGMSG(DBG_CLUSTER, ("SplCreateSpoolerWorkerThread dwError %u dwCode %u\n", dwError, dwCode));
|
|
}
|
|
|
|
//
|
|
// Set resource private property ClusterDriverDirectry. This will be used by the
|
|
// ResDll to perform clean up when the spooler is deleted
|
|
//
|
|
if (StrNCatBuff(szDir,
|
|
COUNTOF(szDir),
|
|
pIniSpooler->pszClusResDriveLetter,
|
|
L"\\",
|
|
szClusterDriverRoot,
|
|
NULL) == ERROR_SUCCESS)
|
|
{
|
|
SplRegSetValue(pIniSpooler->hckRoot,
|
|
SPLREG_CLUSTER_DRIVER_DIRECTORY,
|
|
REG_SZ,
|
|
(LPBYTE)szDir,
|
|
(wcslen(szDir) + 1) * sizeof(WCHAR),
|
|
pIniSpooler);
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
DECSPOOLERREF(pIniSpooler);
|
|
|
|
LeaveSplSem();
|
|
|
|
DBGMSG(DBG_CLUSTER, ("SplCreateSpoolerWorkerThread terminates pIniSpooler->cRef %u\n", pIniSpooler->cRef));
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
LogFatalPortError
|
|
|
|
Routine Description:
|
|
|
|
This routine logs a message when a printer cannot be brought up because its
|
|
ports are missing.
|
|
|
|
Arguments:
|
|
|
|
pszName - The name of the printer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
LogFatalPortError(
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PCWSTR pszName
|
|
)
|
|
{
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
WCHAR szError[40] = {0};
|
|
|
|
LastError = GetLastError();
|
|
|
|
StringCchPrintf(szError, COUNTOF(szError), L"%u (0x%x)", LastError, LastError);
|
|
|
|
SplLogEvent(pIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_PORT_INITIALIZATION_ERROR,
|
|
FALSE,
|
|
(PWSTR)pszName,
|
|
szError,
|
|
NULL);
|
|
}
|
|
|