2599 lines
67 KiB
C
2599 lines
67 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 - 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
printer.c
|
|
|
|
Abstract:
|
|
|
|
This module provides all the public exported APIs relating to Printer
|
|
management for the Local Print Providor
|
|
|
|
SplAddPrinter
|
|
LocalAddPrinter
|
|
SplDeletePrinter
|
|
SplResetPrinter
|
|
|
|
Author:
|
|
|
|
Dave Snipp (DaveSn) 15-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
Matthew A Felton (Mattfe) 27-June-1994
|
|
Allow Multiple pIniSpoolers
|
|
|
|
MattFe Jan5 Cleanup SplAddPrinter & UpdatePrinterIni
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
#define PRINTER_NO_CONTROL 0x00
|
|
|
|
extern WCHAR *szNull;
|
|
|
|
WCHAR *szIniDevices = L"devices";
|
|
WCHAR *szIniPrinterPorts = L"PrinterPorts";
|
|
DWORD NetPrinterDecayPeriod = 1000*60*60; // 1 hour
|
|
DWORD FirstAddNetPrinterTickCount = 0;
|
|
|
|
|
|
extern GENERIC_MAPPING GenericMapping[SPOOLER_OBJECT_COUNT];
|
|
extern PINIENVIRONMENT pThisEnvironment;
|
|
|
|
|
|
BOOL
|
|
CopyRegistryKeys(
|
|
HKEY hSourceParentKey,
|
|
LPWSTR szSourceKey,
|
|
HKEY hDestParentKey,
|
|
LPWSTR szDestKey
|
|
);
|
|
|
|
VOID
|
|
FixDevModeDeviceName(
|
|
LPWSTR pPrinterName,
|
|
PDEVMODE pDevMode,
|
|
DWORD cbDevMode
|
|
);
|
|
|
|
VOID
|
|
CheckAndUpdatePrinterRegAll(
|
|
PINISPOOLER pIniSpooler,
|
|
LPWSTR pszPrinterName,
|
|
LPWSTR pszPort,
|
|
BOOL bDelete
|
|
)
|
|
{
|
|
// Print Providers if they are simulating network connections
|
|
// will have the Win.INI setting taken care of by the router
|
|
// so don't do they update if they request it.
|
|
|
|
if ( pIniSpooler->SpoolerFlags & SPL_UPDATE_WININI_DEVICES ) {
|
|
|
|
UpdatePrinterRegAll( pszPrinterName, pszPort, bDelete );
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
ValidatePrinterAttributes(
|
|
DWORD SourceAttributes,
|
|
BOOL bSettableOnly
|
|
)
|
|
{
|
|
//
|
|
// Use only valid attributes.
|
|
//
|
|
DWORD TargetAttributes = SourceAttributes & PRINTER_ATTRIBUTE_VALID;
|
|
|
|
if ( !bSettableOnly ) {
|
|
|
|
if( SourceAttributes & PRINTER_ATTRIBUTE_LOCAL )
|
|
TargetAttributes |= PRINTER_ATTRIBUTE_LOCAL;
|
|
|
|
/* Don't accept PRINTER_ATTRIBUTE_NETWORK
|
|
* unless the PRINTER_ATTRIBUTE_LOCAL bit is set also.
|
|
* This is a special case of a local printer masquerading
|
|
* as a network printer.
|
|
* Otherwise PRINTER_ATTRIBUTE_NETWORK should be set only
|
|
* by win32spl.
|
|
*/
|
|
if( ( SourceAttributes & PRINTER_ATTRIBUTE_NETWORK )
|
|
&&( SourceAttributes & PRINTER_ATTRIBUTE_LOCAL ) )
|
|
TargetAttributes |= PRINTER_ATTRIBUTE_NETWORK;
|
|
}
|
|
|
|
/* If both queued and direct, knock out direct:
|
|
*/
|
|
if((TargetAttributes &
|
|
(PRINTER_ATTRIBUTE_QUEUED | PRINTER_ATTRIBUTE_DIRECT)) ==
|
|
(PRINTER_ATTRIBUTE_QUEUED | PRINTER_ATTRIBUTE_DIRECT)) {
|
|
TargetAttributes &= ~PRINTER_ATTRIBUTE_DIRECT;
|
|
}
|
|
|
|
/* If both direct and keep-printed-jobs, knock out keep-printed-jobs
|
|
*/
|
|
if((TargetAttributes &
|
|
(PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS | PRINTER_ATTRIBUTE_DIRECT)) ==
|
|
(PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS | PRINTER_ATTRIBUTE_DIRECT)) {
|
|
TargetAttributes &= ~PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS;
|
|
}
|
|
|
|
return TargetAttributes;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CreatePrinterEntry(
|
|
LPPRINTER_INFO_2 pPrinter,
|
|
PINIPRINTER pIniPrinter,
|
|
PBOOL pAccessSystemSecurity
|
|
)
|
|
{
|
|
if( !( pIniPrinter->pSecurityDescriptor =
|
|
CreatePrinterSecurityDescriptor( pPrinter->pSecurityDescriptor ) )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
*pAccessSystemSecurity = FALSE;
|
|
|
|
pIniPrinter->signature = IP_SIGNATURE;
|
|
|
|
pIniPrinter->pName = AllocSplStr(pPrinter->pPrinterName);
|
|
|
|
if (!pIniPrinter->pName) {
|
|
DBGMSG(DBG_WARNING, ("CreatePrinterEntry: Could not allocate PrinterName string\n" ));
|
|
return FALSE;
|
|
}
|
|
|
|
if (pPrinter->pShareName) {
|
|
|
|
pIniPrinter->pShareName = AllocSplStr(pPrinter->pShareName);
|
|
|
|
} else {
|
|
|
|
pIniPrinter->pShareName = NULL;
|
|
|
|
}
|
|
|
|
if (pPrinter->pDatatype) {
|
|
|
|
pIniPrinter->pDatatype = AllocSplStr(pPrinter->pDatatype);
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
//
|
|
// Error: the datatype should never be NULL
|
|
// point.
|
|
//
|
|
LogEvent( pIniPrinter->pIniSpooler,
|
|
LOG_ERROR,
|
|
MSG_SHARE_FAILED,
|
|
L"CreatePrinterEntry",
|
|
pIniPrinter->pName ?
|
|
pIniPrinter->pName :
|
|
L"(Nonep)",
|
|
pIniPrinter->pShareName ?
|
|
pIniPrinter->pShareName :
|
|
L"(Nones)",
|
|
L"NULL datatype",
|
|
NULL);
|
|
#endif
|
|
|
|
pIniPrinter->pDatatype = NULL;
|
|
}
|
|
|
|
//
|
|
// Bugbug, what if any of these allocations were to fail ?
|
|
// We'd have an invalid printer created.
|
|
//
|
|
|
|
|
|
pIniPrinter->Priority = pPrinter->Priority ? pPrinter->Priority
|
|
: DEF_PRIORITY;
|
|
|
|
pIniPrinter->Attributes = ValidatePrinterAttributes(pPrinter->Attributes,
|
|
FALSE);
|
|
|
|
pIniPrinter->StartTime = pPrinter->StartTime;
|
|
pIniPrinter->UntilTime = pPrinter->UntilTime;
|
|
|
|
pIniPrinter->pParameters = AllocSplStr(pPrinter->pParameters);
|
|
|
|
pIniPrinter->pSepFile = AllocSplStr(pPrinter->pSepFile);
|
|
|
|
pIniPrinter->pComment = AllocSplStr(pPrinter->pComment);
|
|
|
|
pIniPrinter->pLocation = AllocSplStr(pPrinter->pLocation);
|
|
|
|
if (pPrinter->pDevMode) {
|
|
|
|
pIniPrinter->cbDevMode = pPrinter->pDevMode->dmSize +
|
|
pPrinter->pDevMode->dmDriverExtra;
|
|
|
|
if (pIniPrinter->pDevMode = AllocSplMem(pIniPrinter->cbDevMode)) {
|
|
|
|
memcpy(pIniPrinter->pDevMode,
|
|
pPrinter->pDevMode,
|
|
pIniPrinter->cbDevMode);
|
|
|
|
FixDevModeDeviceName( pIniPrinter->pName,
|
|
pIniPrinter->pDevMode,
|
|
pIniPrinter->cbDevMode );
|
|
}
|
|
|
|
} else {
|
|
|
|
pIniPrinter->cbDevMode = 0;
|
|
pIniPrinter->pDevMode = NULL;
|
|
}
|
|
|
|
pIniPrinter->DefaultPriority = pPrinter->DefaultPriority;
|
|
|
|
pIniPrinter->pIniFirstJob = pIniPrinter->pIniLastJob = NULL;
|
|
|
|
pIniPrinter->cJobs = pIniPrinter->AveragePPM = 0;
|
|
|
|
pIniPrinter->GenerateOnClose = 0;
|
|
|
|
// At present no API can set this up, the user has to use the
|
|
// registry. LATER we should enhance the API to take this.
|
|
|
|
pIniPrinter->pSpoolDir = NULL;
|
|
|
|
// Initialize Status Information
|
|
|
|
pIniPrinter->cTotalJobs = 0;
|
|
pIniPrinter->cTotalBytes.LowPart = 0;
|
|
pIniPrinter->cTotalBytes.HighPart = 0;
|
|
GetSystemTime(&pIniPrinter->stUpTime);
|
|
pIniPrinter->MaxcRef = 0;
|
|
pIniPrinter->cTotalPagesPrinted = 0;
|
|
pIniPrinter->cSpooling = 0;
|
|
pIniPrinter->cMaxSpooling = 0;
|
|
pIniPrinter->cErrorOutOfPaper = 0;
|
|
pIniPrinter->cErrorNotReady = 0;
|
|
pIniPrinter->cJobError = 0;
|
|
|
|
//
|
|
// Start from a Semi Random Number
|
|
// That way if someone deletes and creates a printer of
|
|
// the same name it is unlickly to have the same unique ID
|
|
|
|
pIniPrinter->cChangeID = GetTickCount();
|
|
|
|
if (pIniPrinter->cChangeID == 0 )
|
|
pIniPrinter->cChangeID++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
UpdateWinIni(
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
PINIPORT pIniPort;
|
|
DWORD i;
|
|
BOOL bGenerateNetId = FALSE;
|
|
LPWSTR pszPort;
|
|
|
|
SplInSem();
|
|
|
|
if( !( pIniPrinter->pIniSpooler->SpoolerFlags & SPL_UPDATE_WININI_DEVICES )){
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Update win.ini for Win16 compatibility
|
|
//
|
|
if ( pIniPrinter->Status & PRINTER_PENDING_DELETION ) {
|
|
|
|
CheckAndUpdatePrinterRegAll( pIniPrinter->pIniSpooler,
|
|
pIniPrinter->pName,
|
|
NULL,
|
|
UPDATE_REG_DELETE );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Initialize in case there are no ports that match this printer.
|
|
//
|
|
pszPort = szNullPort;
|
|
|
|
for( pIniPort = pIniPrinter->pIniSpooler->pIniPort;
|
|
pIniPort;
|
|
pIniPort = pIniPort->pNext ){
|
|
|
|
for ( i = 0; i < pIniPort->cPrinters; i++ ) {
|
|
|
|
if ( pIniPort->ppIniPrinter[i] == pIniPrinter ) {
|
|
|
|
//
|
|
// UpdatePrinterRegAll will automatically
|
|
// convert "\\server\share" or ports with
|
|
// spaces to Nexx:
|
|
//
|
|
pszPort = pIniPort->pName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CheckAndUpdatePrinterRegAll( pIniPrinter->pIniSpooler,
|
|
pIniPrinter->pName,
|
|
pszPort,
|
|
UPDATE_REG_CHANGE );
|
|
}
|
|
|
|
BroadcastChange( pIniPrinter->pIniSpooler,
|
|
WM_WININICHANGE,
|
|
PR_JOBSTATUS,
|
|
(LPARAM)szIniDevices);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DeletePrinterIni(
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
HKEY hPrinterRootKey=NULL, hPrinterKey=NULL;
|
|
DWORD Status;
|
|
LPWSTR pKeyName;
|
|
WCHAR scratch[MAX_PATH];
|
|
HANDLE hToken;
|
|
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
Status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pIniSpooler->pszRegistryPrinters, 0,
|
|
KEY_WRITE, &hPrinterRootKey );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
pKeyName = RemoveBackslashesForRegistryKey ( pIniPrinter->pName, scratch );
|
|
|
|
Status = RegOpenKeyEx( hPrinterRootKey, pKeyName, 0,
|
|
KEY_WRITE, &hPrinterKey );
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
Status = RegDeleteKey( hPrinterKey, szPrinterData );
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
|
|
DBGMSG(DBG_WARNING, ("DeletePrinterIni: RegDeleteKey returns %ld\n", Status ));
|
|
}
|
|
|
|
RegCloseKey(hPrinterKey);
|
|
}
|
|
|
|
Status = RegDeleteKey(hPrinterRootKey, pKeyName);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
DBGMSG(DBG_WARNING, ("DeletePrinter: RegDeleteKey <Key itself> returns %ld\n", Status ));
|
|
|
|
}
|
|
|
|
RegCloseKey(hPrinterRootKey);
|
|
|
|
ImpersonatePrinterClient(hToken);
|
|
|
|
|
|
return (Status == ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
PrinterCreateKey(
|
|
HKEY hKey,
|
|
LPWSTR pSubKey,
|
|
PHKEY phkResult,
|
|
PDWORD pdwLastError
|
|
)
|
|
{
|
|
BOOL bReturnValue;
|
|
DWORD Status;
|
|
|
|
Status = RegCreateKeyEx( hKey,
|
|
pSubKey,
|
|
0, NULL, 0,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
phkResult,
|
|
NULL);
|
|
|
|
if ( Status != ERROR_SUCCESS ) {
|
|
|
|
DBGMSG( DBG_WARNING, ( "PrinterCreateKey: RegCreateKeyEx %ws error %d\n", pSubKey, Status ));
|
|
|
|
*pdwLastError = Status;
|
|
bReturnValue = FALSE;
|
|
|
|
} else {
|
|
|
|
bReturnValue = TRUE;
|
|
}
|
|
|
|
return bReturnValue;
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
UpdatePrinterIni(
|
|
PINIPRINTER pIniPrinter,
|
|
DWORD dwChangeID
|
|
)
|
|
{
|
|
HKEY hPrinterRootKey = NULL, hPrinterKey = NULL;
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
|
|
WCHAR StackTempString[MAX_PRINTER_NAME];
|
|
LPWSTR pKeyName;
|
|
HANDLE hToken;
|
|
DWORD dwTickCount;
|
|
BOOL bReturnValue;
|
|
DWORD cbData;
|
|
DWORD cbNeeded;
|
|
LPWSTR pszPorts;
|
|
|
|
try {
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
if ( hToken == FALSE ) {
|
|
|
|
DBGMSG( DBG_WARNING, ("UpdatePrinterIni failed RevertToPrinterSelf %x\n", GetLastError() ));
|
|
}
|
|
|
|
|
|
if ( !PrinterCreateKey( HKEY_LOCAL_MACHINE,
|
|
pIniSpooler->pszRegistryPrinters,
|
|
&hPrinterRootKey,
|
|
&dwLastError )) {
|
|
leave;
|
|
}
|
|
|
|
|
|
pKeyName = RemoveBackslashesForRegistryKey( pIniPrinter->pName, StackTempString );
|
|
|
|
if ( !PrinterCreateKey( hPrinterRootKey,
|
|
pKeyName,
|
|
&hPrinterKey,
|
|
&dwLastError )) {
|
|
|
|
leave;
|
|
}
|
|
|
|
|
|
if ( dwChangeID != KEEP_CHANGEID ) {
|
|
|
|
// WorkStation Caching requires a Unique ID so that they can quickly
|
|
// tell if their Cache is up to date.
|
|
|
|
dwTickCount = GetTickCount();
|
|
|
|
// Ensure Uniqueness
|
|
|
|
if ( dwTickCount == 0 )
|
|
dwTickCount++;
|
|
|
|
if ( pIniPrinter->cChangeID == dwTickCount )
|
|
dwTickCount++;
|
|
|
|
pIniPrinter->cChangeID = dwTickCount;
|
|
RegSetDWord( hPrinterKey, szTimeLastChange, pIniPrinter->cChangeID, &dwLastError );
|
|
|
|
}
|
|
|
|
if ( dwChangeID != CHANGEID_ONLY ) {
|
|
|
|
RegSetDWord( hPrinterKey, szStatus, pIniPrinter->Status, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szName, pIniPrinter->pName, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szShare, pIniPrinter->pShareName, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szPrintProcessor, pIniPrinter->pIniPrintProc->pName, &dwLastError );
|
|
|
|
if ( !( pIniPrinter->Status & PRINTER_PENDING_DELETION )) {
|
|
|
|
SPLASSERT( pIniPrinter->pDatatype != NULL );
|
|
}
|
|
|
|
RegSetString( hPrinterKey, szDatatype, pIniPrinter->pDatatype, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szParameters, pIniPrinter->pParameters, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szDescription, pIniPrinter->pComment, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szDriver, pIniPrinter->pIniDriver->pName, &dwLastError );
|
|
|
|
if (pIniPrinter->pDevMode) {
|
|
|
|
cbData = pIniPrinter->cbDevMode;
|
|
|
|
} else {
|
|
|
|
cbData = 0;
|
|
}
|
|
|
|
RegSetBinaryData( hPrinterKey, szDevMode, (LPBYTE)pIniPrinter->pDevMode, cbData, &dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szPriority, pIniPrinter->Priority, &dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szDefaultPriority, pIniPrinter->DefaultPriority, &dwLastError );
|
|
|
|
RegSetDWord(hPrinterKey, szStartTime, pIniPrinter->StartTime, &dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szUntilTime, pIniPrinter->UntilTime, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szSepFile, pIniPrinter->pSepFile, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szLocation, pIniPrinter->pLocation, &dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szAttributes, pIniPrinter->Attributes, &dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szTXTimeout, pIniPrinter->txTimeout, &dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szDNSTimeout, pIniPrinter->dnsTimeout, &dwLastError );
|
|
|
|
if (pIniPrinter->pSecurityDescriptor) {
|
|
|
|
cbData = GetSecurityDescriptorLength( pIniPrinter->pSecurityDescriptor );
|
|
|
|
} else {
|
|
|
|
cbData = 0;
|
|
}
|
|
|
|
RegSetBinaryData( hPrinterKey, szSecurity, pIniPrinter->pSecurityDescriptor, cbData, &dwLastError );
|
|
|
|
RegSetString( hPrinterKey, szSpoolDir, pIniPrinter->pSpoolDir, &dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szTotalJobs, pIniPrinter->cTotalJobs, &dwLastError );
|
|
|
|
RegSetBinaryData( hPrinterKey, szTotalBytes, (LPBYTE)&pIniPrinter->cTotalBytes, sizeof(pIniPrinter->cTotalBytes),&dwLastError );
|
|
|
|
RegSetDWord( hPrinterKey, szTotalPages, pIniPrinter->cTotalPagesPrinted, &dwLastError );
|
|
|
|
cbNeeded = 0;
|
|
GetPrinterPorts( pIniPrinter, 0, &cbNeeded);
|
|
|
|
if (!(pszPorts = AllocSplMem(cbNeeded))) {
|
|
dwLastError = GetLastError();
|
|
leave;
|
|
}
|
|
|
|
GetPrinterPorts(pIniPrinter, pszPorts, &cbNeeded);
|
|
|
|
RegSetString( hPrinterKey, szPort, pszPorts, &dwLastError );
|
|
|
|
FreeSplMem(pszPorts);
|
|
|
|
//
|
|
// A Provider might want to Write Extra Data from Registry
|
|
//
|
|
if ( pIniSpooler->pfnWriteRegistryExtra != NULL ) {
|
|
|
|
if ( !(*pIniSpooler->pfnWriteRegistryExtra)(pIniPrinter->pName, hPrinterKey, pIniPrinter->pExtraData)) {
|
|
dwLastError = GetLastError();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make Sure we have an Open DriverData Key handle
|
|
//
|
|
if ( pIniPrinter->hPrinterDataKey == NULL ) {
|
|
|
|
if ( !PrinterCreateKey( hPrinterKey,
|
|
szPrinterData,
|
|
&pIniPrinter->hPrinterDataKey,
|
|
&dwLastError )) {
|
|
|
|
leave;
|
|
}
|
|
}
|
|
|
|
if ( ( pIniPrinter->Status & PRINTER_PENDING_CREATION ) &&
|
|
( dwLastError == ERROR_SUCCESS ) ) {
|
|
|
|
pIniPrinter->Status &= ~PRINTER_PENDING_CREATION;
|
|
|
|
RegSetDWord( hPrinterKey, szStatus, pIniPrinter->Status, &dwLastError );
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
if ( hPrinterKey )
|
|
RegCloseKey(hPrinterKey);
|
|
|
|
if ( hPrinterRootKey )
|
|
RegCloseKey( hPrinterRootKey );
|
|
|
|
if ( hToken )
|
|
ImpersonatePrinterClient( hToken );
|
|
|
|
}
|
|
|
|
if ( dwLastError != ERROR_SUCCESS ) {
|
|
|
|
SetLastError( dwLastError );
|
|
bReturnValue = FALSE;
|
|
|
|
} else {
|
|
|
|
bReturnValue = TRUE;
|
|
}
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
RemoveOldNetPrinters(
|
|
PPRINTER_INFO_1 pPrinterInfo1
|
|
)
|
|
{
|
|
PININETPRINT *ppIniNetPrint = &pLocalIniSpooler->pIniNetPrint;
|
|
PININETPRINT pIniNetPrint;
|
|
DWORD TickCount;
|
|
|
|
|
|
TickCount = GetTickCount();
|
|
|
|
//
|
|
// Browse Information only becomes valid after this print server has been
|
|
// up for the NetPrinterDecayPeriod.
|
|
//
|
|
|
|
if (( bNetInfoReady == FALSE ) &&
|
|
(( TickCount - FirstAddNetPrinterTickCount ) > NetPrinterDecayPeriod )) {
|
|
|
|
DBGMSG( DBG_TRACE, ("RemoveOldNetPrinters has a valid browse list\n" ));
|
|
|
|
bNetInfoReady = TRUE;
|
|
}
|
|
|
|
|
|
while (*ppIniNetPrint) {
|
|
|
|
|
|
//
|
|
// If either the Tickcount has expired OR we want to delete this specific NetPrinter
|
|
// ( because its no longer shared ).
|
|
//
|
|
|
|
if ( (( TickCount - (*ppIniNetPrint)->TickCount ) > NetPrinterDecayPeriod + TEN_MINUTES ) ||
|
|
|
|
( pPrinterInfo1 != NULL &&
|
|
pPrinterInfo1->Flags & PRINTER_ATTRIBUTE_NETWORK &&
|
|
!(pPrinterInfo1->Flags & PRINTER_ATTRIBUTE_SHARED ) &&
|
|
_wcsicmp( pPrinterInfo1->pName, (*ppIniNetPrint)->pName ) == STRINGS_ARE_EQUAL)) {
|
|
|
|
pIniNetPrint = *ppIniNetPrint;
|
|
|
|
DBGMSG( DBG_TRACE, ("RemoveOldNetPrinters removing %ws not heard for %d millisconds\n",
|
|
pIniNetPrint->pName, ( TickCount - (*ppIniNetPrint)->TickCount ) ));
|
|
|
|
|
|
*ppIniNetPrint = pIniNetPrint->pNext;
|
|
|
|
FreeSplStr( pIniNetPrint->pDescription );
|
|
FreeSplStr( pIniNetPrint->pComment );
|
|
FreeSplMem( pIniNetPrint );
|
|
}
|
|
|
|
if ( *ppIniNetPrint )
|
|
ppIniNetPrint = &(*ppIniNetPrint)->pNext;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE
|
|
AddNetPrinter(
|
|
LPBYTE pPrinterInfo,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Net Printers are created by remote machines calling AddPrinter( Level = 1, Printer_info_1 )
|
|
( see server.c ). They are used for browsing, someone can call EnumPrinters and ask to get
|
|
back our browse list - ie all our net printers.
|
|
|
|
The printers in this list are decayed out after 1 hour ( default ).
|
|
|
|
See return value comment.
|
|
|
|
Note client\winspool.c AddPrinterW doesn't allow PRINTER_INFO_1 ( NET printers ), so this can
|
|
only come from system components.
|
|
|
|
Arguments:
|
|
|
|
pPrinterInfo - Point to a PRINTER_INFO_1 structure to add
|
|
|
|
Return Value:
|
|
|
|
NULL - it doesn't return a printer handle.
|
|
LastError = ERROR_SUCCESS, or error code ( like out of memory ).
|
|
|
|
NOTE before NT 3.51 it returned a printer handle of type PRINTER_HANDLE_NET, but since the
|
|
only use of this handle was to close it ( which burnt up cpu / net traffic and RPC binding
|
|
handles, we return a NULL handle now to make it more efficient. Apps ( Server.c ) if it
|
|
cares could call GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPRINTER_INFO_1 pPrinterInfo1 = (PPRINTER_INFO_1)pPrinterInfo;
|
|
PININETPRINT pIniNetPrint = NULL;
|
|
PININETPRINT *ppScan;
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// Validate PRINTER_INFO_1
|
|
// At minimum it must have a PrinterName.
|
|
|
|
if ( pPrinterInfo1->pName == NULL ) {
|
|
|
|
DBGMSG( DBG_WARN, ("AddNetPrinter pPrinterInfo1->pName == NULL failed\n"));
|
|
SetLastError( ERROR_INVALID_NAME );
|
|
return NULL;
|
|
}
|
|
|
|
if ( FirstAddNetPrinterTickCount == 0 ) {
|
|
|
|
FirstAddNetPrinterTickCount = GetTickCount();
|
|
}
|
|
|
|
//
|
|
// Decay out of the browse list any old printers
|
|
//
|
|
|
|
RemoveOldNetPrinters( pPrinterInfo1 );
|
|
|
|
|
|
//
|
|
// Do Not Add and printer which is no longer shared.
|
|
//
|
|
|
|
if ( pPrinterInfo1->Flags & PRINTER_ATTRIBUTE_NETWORK &&
|
|
!( pPrinterInfo1->Flags & PRINTER_ATTRIBUTE_SHARED )) {
|
|
|
|
SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// See if we already have this printer
|
|
//
|
|
|
|
pIniNetPrint = pIniSpooler->pIniNetPrint;
|
|
|
|
while ( pIniNetPrint &&
|
|
pIniNetPrint->pName &&
|
|
lstrcmpi( pPrinterInfo1->pName, pIniNetPrint->pName )) {
|
|
|
|
pIniNetPrint = pIniNetPrint->pNext;
|
|
}
|
|
|
|
|
|
//
|
|
// If we didn't find this printer already Create one
|
|
//
|
|
|
|
if ( pIniNetPrint == NULL && ( pIniNetPrint = AllocSplMem( sizeof(ININETPRINT) )) ) {
|
|
|
|
pIniNetPrint->signature = IN_SIGNATURE;
|
|
pIniNetPrint->pName = AllocSplStr( pPrinterInfo1->pName );
|
|
pIniNetPrint->pDescription = AllocSplStr( pPrinterInfo1->pDescription );
|
|
pIniNetPrint->pComment = AllocSplStr( pPrinterInfo1->pComment );
|
|
|
|
// Did Any of the above allocations fail ?
|
|
|
|
if ( pIniNetPrint->pName == NULL ||
|
|
( pPrinterInfo1->pDescription != NULL && pIniNetPrint->pDescription == NULL ) ||
|
|
( pPrinterInfo1->pComment != NULL && pIniNetPrint->pComment == NULL ) ) {
|
|
|
|
// Failed - CleanUp
|
|
|
|
FreeSplStr( pIniNetPrint->pComment );
|
|
FreeSplStr( pIniNetPrint->pDescription );
|
|
FreeSplStr( pIniNetPrint->pName );
|
|
FreeSplMem( pIniNetPrint );
|
|
pIniNetPrint = NULL;
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_TRACE, ("AddNetPrinter(%ws) NEW\n", pPrinterInfo1->pName ));
|
|
|
|
ppScan = &pIniSpooler->pIniNetPrint;
|
|
|
|
// Scan through the current known printers, and insert the new one
|
|
// in alphabetical order
|
|
// BUGBUG - Why Alphabetical order ? More CPU cycles to burn ?
|
|
|
|
while( *ppScan && (lstrcmp((*ppScan)->pName, pIniNetPrint->pName) < 0)) {
|
|
ppScan = &(*ppScan)->pNext;
|
|
}
|
|
|
|
pIniNetPrint->pNext = *ppScan;
|
|
*ppScan = pIniNetPrint;
|
|
}
|
|
|
|
} else if ( pIniNetPrint != NULL ) {
|
|
|
|
DBGMSG( DBG_TRACE, ("AddNetPrinter(%ws) elapsed since last notified %d milliseconds\n", pIniNetPrint->pName, ( GetTickCount() - pIniNetPrint->TickCount ) ));
|
|
}
|
|
|
|
|
|
if ( pIniNetPrint ) {
|
|
|
|
// Tickle the TickCount so this printer sticks around in the browse list
|
|
|
|
pIniNetPrint->TickCount = GetTickCount();
|
|
|
|
// Have to set some error code or RPC thinks ERROR_SUCCESS is good.
|
|
|
|
SetLastError( ERROR_PRINTER_ALREADY_EXISTS );
|
|
|
|
pIniSpooler->cAddNetPrinters++; // Status Only
|
|
}
|
|
|
|
Done:
|
|
|
|
SPLASSERT( GetLastError() != ERROR_SUCCESS);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If the token list item is one of our ports, replace it with a pointer
|
|
// to an INIPORT.
|
|
// If it is a port that is provided by another providor, keep the string
|
|
// Anybody who calls this function should check if it is one of our ports
|
|
// by checking the signature of the pointer:
|
|
// if (pIniPort->signature == IPO_SIGNATURE)
|
|
|
|
BOOL
|
|
ValidatePortTokenList(
|
|
PKEYDATA pKeyData,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
PINIPORT pIniPort;
|
|
DWORD i;
|
|
BOOL rc=TRUE;
|
|
|
|
if (!pKeyData) {
|
|
SetLastError(ERROR_UNKNOWN_PORT);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i=0; i<pKeyData->cTokens; i++) {
|
|
|
|
pIniPort = FindPort(pKeyData->pTokens[i]);
|
|
|
|
// For Partial Print Providers
|
|
// Create a Port on the fly without checking its validity.
|
|
|
|
if ((!pIniPort) &&
|
|
( pIniSpooler->SpoolerFlags & SPL_OPEN_CREATE_PORTS )) {
|
|
|
|
pIniPort = CreatePortEntry(pKeyData->pTokens[i], NULL, pIniSpooler);
|
|
}
|
|
|
|
if (!pIniPort)
|
|
rc = FALSE;
|
|
else
|
|
pKeyData->pTokens[i] = (LPWSTR)pIniPort;
|
|
}
|
|
|
|
if ( rc ) {
|
|
|
|
for ( i = 0 ; i < pKeyData->cTokens ; ++i ) {
|
|
|
|
pIniPort = (PINIPORT)pKeyData->pTokens[i];
|
|
INCPORTREF(pIniPort);
|
|
}
|
|
|
|
pKeyData->bFixPortRef = TRUE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ValidatePrinterName(
|
|
LPWSTR pszNewName,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIPRINTER pIniPrinter,
|
|
LPWSTR *ppszLocalName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validates a printer name. Printer and share names exist in the same
|
|
namespace, so validation is done against printer, share names.
|
|
|
|
Arguments:
|
|
|
|
pszNewName - printer name specified
|
|
|
|
pIniSpooler - Spooler that owns printer
|
|
|
|
pIniPrinter - could be null if the printer is getting created
|
|
|
|
ppszLocalName - on success returns local name
|
|
(\\servername stripped off if necessary).
|
|
|
|
Return Value:
|
|
|
|
DWORD error code.
|
|
|
|
History:
|
|
|
|
MuhuntS (Muhunthan Sivapragasam) July 95
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT cchMachineNameLength;
|
|
PINIPRINTER pIniTempPrinter, pIniNextPrinter;
|
|
LPWSTR pszLocalNameTmp;
|
|
|
|
if ( !pszNewName || !*pszNewName || wcschr( pszNewName, *szComma ) ) {
|
|
return ERROR_INVALID_PRINTER_NAME;
|
|
}
|
|
|
|
cchMachineNameLength = wcslen( pIniSpooler->pMachineName );
|
|
|
|
if ((!_wcsnicmp( pIniSpooler->pMachineName,
|
|
pszNewName,
|
|
cchMachineNameLength)) &&
|
|
pszNewName[cchMachineNameLength] == L'\\') {
|
|
|
|
pszLocalNameTmp = pszNewName + cchMachineNameLength + 1;
|
|
} else {
|
|
|
|
pszLocalNameTmp = pszNewName;
|
|
}
|
|
|
|
//
|
|
// Limit PrinterNames to MAX_PATH length
|
|
//
|
|
|
|
if ( wcslen( pszLocalNameTmp ) > MAX_PRINTER_NAME ) {
|
|
return ERROR_INVALID_PRINTER_NAME;
|
|
}
|
|
|
|
//
|
|
// Now validate that printer names are unique. Printer names and
|
|
// share names reside in the same namespace (see net\dosprint\dosprtw.c).
|
|
//
|
|
for( pIniTempPrinter = pIniSpooler->pIniPrinter;
|
|
pIniTempPrinter;
|
|
pIniTempPrinter = pIniNextPrinter ){
|
|
|
|
//
|
|
// Get the next printer now in case we delete the current
|
|
// one in DeletePrinterCheck.
|
|
//
|
|
pIniNextPrinter = pIniTempPrinter->pNext;
|
|
|
|
//
|
|
// Skip ourselves, if we are pssed in.
|
|
//
|
|
if( pIniTempPrinter == pIniPrinter ){
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Disallow common Printer/Share names.
|
|
//
|
|
if( !lstrcmpi( pszLocalNameTmp, pIniTempPrinter->pName ) ||
|
|
( pIniTempPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED &&
|
|
!lstrcmpi( pszLocalNameTmp, pIniTempPrinter->pShareName ))){
|
|
|
|
if( !DeletePrinterCheck( pIniTempPrinter )){
|
|
|
|
return ERROR_PRINTER_ALREADY_EXISTS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Success, now update ppszLocalName from pszLocalNameTmp.
|
|
//
|
|
*ppszLocalName = pszLocalNameTmp;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD
|
|
ValidatePrinterShareName(
|
|
LPWSTR pszNewShareName,
|
|
PINISPOOLER pIniSpooler,
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validates the printer share name. Printer and share names exist in the
|
|
same namespace, so validation is done against printer, share names.
|
|
|
|
Arguments:
|
|
|
|
pszNewShareName - share name specified
|
|
|
|
pIniSpooler - Spooler that owns printer
|
|
|
|
pIniPrinter - could be null if the printer is getting created
|
|
|
|
Return Value:
|
|
|
|
DWORD error code.
|
|
|
|
History:
|
|
|
|
MuhuntS (Muhunthan Sivapragasam) July 95
|
|
|
|
--*/
|
|
|
|
{
|
|
PINIPRINTER pIniTempPrinter, pIniNextPrinter;
|
|
|
|
if ( !pszNewShareName || !*pszNewShareName ) {
|
|
|
|
return ERROR_INVALID_SHARENAME;
|
|
}
|
|
|
|
//
|
|
// Now validate that share names are unique. Share names and printer names
|
|
// reside in the same namespace (see net\dosprint\dosprtw.c).
|
|
//
|
|
for( pIniTempPrinter = pIniSpooler->pIniPrinter;
|
|
pIniTempPrinter;
|
|
pIniTempPrinter = pIniNextPrinter ) {
|
|
|
|
//
|
|
// Get the next printer now in case we delete the current
|
|
// one in DeletePrinterCheck.
|
|
//
|
|
pIniNextPrinter = pIniTempPrinter->pNext;
|
|
|
|
//
|
|
// Skip ourselves, if we are pssed in.
|
|
//
|
|
if( pIniTempPrinter == pIniPrinter ){
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check our share name now.
|
|
//
|
|
if( !lstrcmpi(pszNewShareName, pIniTempPrinter->pName) ||
|
|
( pIniTempPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED &&
|
|
!lstrcmpi(pszNewShareName, pIniTempPrinter->pShareName)) ) {
|
|
|
|
if( !DeletePrinterCheck( pIniTempPrinter )){
|
|
|
|
return ERROR_INVALID_SHARENAME;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD
|
|
ValidatePrinterInfo(
|
|
IN PPRINTER_INFO_2 pPrinter,
|
|
IN PINISPOOLER pIniSpooler,
|
|
IN PINIPRINTER pIniPrinter OPTIONAL,
|
|
OUT LPWSTR* ppszLocalName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validates that printer names/share do not collide. (Both printer and
|
|
share names exist in the same namespace.)
|
|
|
|
Note: Later, we should remove all this DeletePrinterCheck. As people
|
|
decrement ref counts, they should DeletePrinterCheck themselves (or
|
|
have it built into the decrement).
|
|
|
|
Arguments:
|
|
|
|
pPrinter - PrinterInfo2 structure to validate.
|
|
|
|
pIniSpooler - Spooler that owns printer
|
|
|
|
pIniPrinter - If printer already exists, don't check against itself.
|
|
|
|
ppszLocalName - Returned pointer to string buffer in pPrinter;
|
|
indicates local name (\\servername stripped off if necessary).
|
|
|
|
Valid only on SUCCESS return code.
|
|
|
|
Return Value:
|
|
|
|
DWORD error code.
|
|
|
|
--*/
|
|
{
|
|
LPWSTR pszNewLocalName;
|
|
DWORD dwLastError;
|
|
UINT cchMachineNameLength;
|
|
|
|
if( !CheckSepFile( pPrinter->pSepFile )) {
|
|
return ERROR_INVALID_SEPARATOR_FILE;
|
|
}
|
|
|
|
if( pPrinter->Priority != NO_PRIORITY &&
|
|
( pPrinter->Priority > MAX_PRIORITY ||
|
|
pPrinter->Priority < MIN_PRIORITY )){
|
|
|
|
return ERROR_INVALID_PRIORITY;
|
|
}
|
|
|
|
if( pPrinter->StartTime >= ONEDAY || pPrinter->UntilTime >= ONEDAY){
|
|
|
|
return ERROR_INVALID_TIME;
|
|
}
|
|
|
|
if ( dwLastError = ValidatePrinterName(pPrinter->pPrinterName,
|
|
pIniSpooler,
|
|
pIniPrinter,
|
|
&pszNewLocalName) ) {
|
|
|
|
return dwLastError;
|
|
}
|
|
|
|
if ( pPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) {
|
|
|
|
if ( dwLastError = ValidatePrinterShareName(pPrinter->pShareName,
|
|
pIniSpooler,
|
|
pIniPrinter) ) {
|
|
|
|
return dwLastError;
|
|
}
|
|
}
|
|
|
|
if( ppszLocalName ){
|
|
|
|
*ppszLocalName = pszNewLocalName;
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE
|
|
LocalAddPrinter(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pPrinterInfo
|
|
)
|
|
{
|
|
return SplAddPrinter(pName, Level, pPrinterInfo,
|
|
pLocalIniSpooler, NULL, NULL, 0);
|
|
}
|
|
|
|
|
|
HANDLE
|
|
LocalAddPrinterEx(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pPrinterInfo,
|
|
LPBYTE pSplClientInfo,
|
|
DWORD dwSplClientLevel
|
|
)
|
|
{
|
|
return SplAddPrinter(pName, Level, pPrinterInfo,
|
|
pLocalIniSpooler, NULL, pSplClientInfo,
|
|
dwSplClientLevel);
|
|
}
|
|
|
|
|
|
HANDLE
|
|
SplAddPrinter(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pPrinterInfo,
|
|
PINISPOOLER pIniSpooler,
|
|
LPBYTE pExtraData,
|
|
LPBYTE pSplClientInfo,
|
|
DWORD dwSplClientInfoLevel
|
|
)
|
|
{
|
|
PINIDRIVER pIniDriver = NULL;
|
|
PINIPRINTPROC pIniPrintProc;
|
|
PINIPRINTER pIniPrinter = NULL;
|
|
PINIPORT pIniPort;
|
|
PPRINTER_INFO_2 pPrinter=(PPRINTER_INFO_2)pPrinterInfo;
|
|
DWORD cbIniPrinter = sizeof(INIPRINTER);
|
|
BOOL bSucceeded = TRUE;
|
|
PKEYDATA pKeyData = NULL;
|
|
DWORD i;
|
|
HANDLE hPrinter = NULL;
|
|
DWORD TypeofHandle = PRINTER_HANDLE_PRINTER;
|
|
PRINTER_DEFAULTS Defaults;
|
|
PINIPORT pIniNetPort = NULL;
|
|
HANDLE hPort = NULL;
|
|
BOOL bAccessSystemSecurity = FALSE;
|
|
DWORD AccessRequested = 0;
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
PDEVMODE pNewDevMode = NULL;
|
|
PINIMONITOR pIniLangMonitor;
|
|
|
|
|
|
// Quick Check Outside Critical Section
|
|
// Since it is common for the ServerThread to call
|
|
// AddPrinter Level 1, which we need to continue
|
|
// to route to other Print Providers
|
|
|
|
if (!MyName( pName, pIniSpooler )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
EnterSplSem();
|
|
|
|
// PRINTER_INFO_1 is only used by printer browsing to replicate
|
|
// data between different print servers.
|
|
// Thus we add a Net printer for level 1.
|
|
|
|
if ( Level == 1 ) {
|
|
|
|
hPrinter = AddNetPrinter( pPrinterInfo, pIniSpooler );
|
|
leave;
|
|
}
|
|
|
|
|
|
if ( !ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
|
|
SERVER_ACCESS_ADMINISTER,
|
|
NULL, pIniSpooler )) {
|
|
leave;
|
|
}
|
|
|
|
if ( dwLastError = ValidatePrinterInfo( pPrinter,
|
|
pIniSpooler,
|
|
NULL,
|
|
NULL )){
|
|
|
|
leave;
|
|
}
|
|
|
|
if (!( pIniDriver = FindLocalDriver( pPrinter->pDriverName ) )) {
|
|
|
|
dwLastError = ERROR_UNKNOWN_PRINTER_DRIVER;
|
|
leave;
|
|
}
|
|
|
|
if (!(pIniPrintProc = FindLocalPrintProc(pPrinter->pPrintProcessor))) {
|
|
|
|
dwLastError = ERROR_UNKNOWN_PRINTPROCESSOR;
|
|
leave;
|
|
}
|
|
|
|
if ( pPrinter->pDatatype && *pPrinter->pDatatype &&
|
|
!FindDatatype(pIniPrintProc, pPrinter->pDatatype) ) {
|
|
|
|
dwLastError = ERROR_INVALID_DATATYPE;
|
|
leave;
|
|
}
|
|
|
|
if (!(pKeyData = CreateTokenList(pPrinter->pPortName))) {
|
|
|
|
dwLastError = ERROR_UNKNOWN_PORT;
|
|
leave;
|
|
}
|
|
|
|
if ( !IsInteractiveUser() ) {
|
|
|
|
TypeofHandle |= PRINTER_HANDLE_REMOTE;
|
|
}
|
|
|
|
if ( !ValidatePortTokenList( pKeyData, pIniSpooler ) ) {
|
|
|
|
leave;
|
|
}
|
|
|
|
DBGMSG(DBG_TRACE, ("AddPrinter(%ws)\n", pPrinter->pPrinterName ?
|
|
pPrinter->pPrinterName : L"NULL"));
|
|
|
|
//
|
|
// Set up defaults for CreatePrinterHandle.
|
|
// If we create a printer we have Administer access to it:
|
|
//
|
|
Defaults.pDatatype = NULL;
|
|
Defaults.pDevMode = NULL;
|
|
Defaults.DesiredAccess = PRINTER_ALL_ACCESS;
|
|
|
|
pIniPrinter = (PINIPRINTER)AllocSplMem( cbIniPrinter );
|
|
|
|
if ( pIniPrinter == NULL ) {
|
|
leave;
|
|
}
|
|
|
|
pIniPrinter->signature = IP_SIGNATURE;
|
|
pIniPrinter->Status |= PRINTER_PENDING_CREATION;
|
|
pIniPrinter->pExtraData = pExtraData;
|
|
pIniPrinter->pIniSpooler = pIniSpooler;
|
|
|
|
pIniDriver->cRef++;
|
|
pIniPrinter->pIniDriver = pIniDriver;
|
|
|
|
pIniPrintProc->cRef++;
|
|
pIniPrinter->pIniPrintProc = pIniPrintProc;
|
|
|
|
pIniPrinter->dnsTimeout = DEFAULT_DNS_TIMEOUT;
|
|
pIniPrinter->txTimeout = DEFAULT_TX_TIMEOUT;
|
|
|
|
|
|
INCPRINTERREF( pIniPrinter );
|
|
|
|
if ( !CreatePrinterEntry(pPrinter, pIniPrinter, &bAccessSystemSecurity)) {
|
|
|
|
leave;
|
|
}
|
|
|
|
pIniPrinter->ppIniPorts = AllocSplMem(pKeyData->cTokens * sizeof(INIPORT));
|
|
|
|
if ( !pIniPrinter->ppIniPorts ) {
|
|
|
|
leave;
|
|
}
|
|
|
|
if (!pIniPrinter->pDatatype) {
|
|
|
|
pIniPrinter->pDatatype = AllocSplStr(*((LPWSTR *)pIniPrinter->pIniPrintProc->pDatatypes));
|
|
|
|
if ( pIniPrinter->pDatatype == NULL )
|
|
leave;
|
|
}
|
|
|
|
// Add this printer to the global list for this machine
|
|
|
|
SplInSem();
|
|
pIniPrinter->pNext = pIniSpooler->pIniPrinter;
|
|
pIniSpooler->pIniPrinter = pIniPrinter;
|
|
|
|
|
|
pIniPrinter->Attributes = ValidatePrinterAttributes(pPrinter->Attributes,
|
|
FALSE);
|
|
|
|
//
|
|
// When a printer is created we will enable bidi by default
|
|
//
|
|
pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_ENABLE_BIDI;
|
|
if ( pIniPrinter->pIniDriver->pIniLangMonitor ) {
|
|
|
|
pIniPrinter->Attributes |= PRINTER_ATTRIBUTE_ENABLE_BIDI;
|
|
}
|
|
|
|
|
|
for ( i = 0; i < pKeyData->cTokens; i++ ) {
|
|
|
|
pIniPort = (PINIPORT)pKeyData->pTokens[i];
|
|
|
|
pIniPrinter->ppIniPorts[i] = pIniPort;
|
|
|
|
if ( !AddIniPrinterToIniPort( pIniPort, pIniPrinter ) ) {
|
|
leave;
|
|
}
|
|
|
|
// If there isn't a monitor for this port,
|
|
// it's a network printer.
|
|
// Make sure we can get a handle for it.
|
|
// This will attempt to open only the first one
|
|
// it finds. Any others will be ignored.
|
|
|
|
if (!(pIniPort->Status & PP_MONITOR) && !hPort) {
|
|
|
|
if( bSucceeded = OpenPrinterPortW(pIniPort->pName,
|
|
&hPort, NULL)) {
|
|
|
|
// Store the address of the INIPORT structure
|
|
// that refers to the network share.
|
|
// This should correspond to pIniPort in any
|
|
// handles opened on this printer.
|
|
// Only the first INIPORT in the linked list
|
|
// is a valid network port.
|
|
|
|
pIniNetPort = pIniPort;
|
|
pIniPrinter->pIniNetPort = pIniNetPort;
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("SplAddPrinter OpenPrinterPort( %ws ) failed: Error %d\n",
|
|
pIniPort->pName,
|
|
GetLastError()));
|
|
leave;
|
|
}
|
|
|
|
} else if ( !pIniPort->hPort ) {
|
|
|
|
if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI )
|
|
pIniLangMonitor = pIniPrinter->pIniDriver->pIniLangMonitor;
|
|
else
|
|
pIniLangMonitor = NULL;
|
|
|
|
OpenMonitorPort(pIniPort,
|
|
pIniLangMonitor,
|
|
pIniPrinter->pName,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
pIniPrinter->cPorts = pKeyData->cTokens;
|
|
|
|
if ( !UpdateWinIni( pIniPrinter ) ) {
|
|
|
|
leave;
|
|
}
|
|
|
|
|
|
if (bAccessSystemSecurity) {
|
|
|
|
Defaults.DesiredAccess |= ACCESS_SYSTEM_SECURITY;
|
|
}
|
|
|
|
AccessRequested = Defaults.DesiredAccess;
|
|
|
|
SplInSem();
|
|
hPrinter = CreatePrinterHandle( pIniPrinter->pName,
|
|
pIniPrinter,
|
|
pIniPort,
|
|
pIniNetPort,
|
|
NULL,
|
|
TypeofHandle,
|
|
hPort,
|
|
&Defaults,
|
|
pIniSpooler,
|
|
AccessRequested,
|
|
pSplClientInfo,
|
|
dwSplClientInfoLevel,
|
|
INVALID_HANDLE_VALUE );
|
|
|
|
if ( hPrinter == NULL ) {
|
|
leave;
|
|
}
|
|
|
|
|
|
if ( !UpdatePrinterIni( pIniPrinter, UPDATE_CHANGEID )) {
|
|
|
|
dwLastError = GetLastError();
|
|
|
|
SplClosePrinter( hPrinter );
|
|
hPrinter = NULL;
|
|
leave;
|
|
}
|
|
|
|
|
|
if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) {
|
|
|
|
INC_PRINTER_ZOMBIE_REF(pIniPrinter);
|
|
|
|
//
|
|
// NOTE ShareThisPrinter will leave critical section and the
|
|
// server will call the spooler back again to OpenPrinter this
|
|
// printer. So this printer MUST be fully created at the point
|
|
// it is shared, so that Open can succeed.
|
|
//
|
|
bSucceeded = ShareThisPrinter(pIniPrinter,
|
|
pIniPrinter->pShareName,
|
|
TRUE
|
|
);
|
|
|
|
DEC_PRINTER_ZOMBIE_REF(pIniPrinter);
|
|
|
|
if ( !bSucceeded ) {
|
|
|
|
//
|
|
// We do not want to delete the existing share in DeletePrinterIni
|
|
//
|
|
pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_SHARED;
|
|
DBGMSG( DBG_WARNING, ("LocalAddPrinter: %ws share failed %ws error %d\n",
|
|
pIniPrinter->pName,
|
|
pIniPrinter->pShareName,
|
|
GetLastError() ));
|
|
|
|
|
|
//
|
|
// With PRINTER_PENDING_CREATION turned on we will Delete this printer.
|
|
//
|
|
|
|
pIniPrinter->Status |= PRINTER_PENDING_CREATION;
|
|
|
|
dwLastError = GetLastError();
|
|
|
|
SPLASSERT( hPrinter );
|
|
SplClosePrinter( hPrinter );
|
|
hPrinter = NULL;
|
|
leave;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
pIniPrinter->Status |= PRINTER_OK;
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// If no devmode is given get driver default, if a devmode is given
|
|
// convert it to current version
|
|
//
|
|
if ( pIniSpooler == pLocalIniSpooler ) {
|
|
|
|
pNewDevMode = ConvertDevModeToSpecifiedVersion(pIniPrinter,
|
|
pIniPrinter->pDevMode,
|
|
NULL,
|
|
NULL,
|
|
CURRENT_VERSION);
|
|
|
|
//
|
|
// If call is remote we must convert devmode before setting it
|
|
//
|
|
if ( pNewDevMode || (TypeofHandle & PRINTER_HANDLE_REMOTE) ) {
|
|
|
|
FreeSplMem(pIniPrinter->pDevMode);
|
|
pIniPrinter->pDevMode = pNewDevMode;
|
|
if ( pNewDevMode ) {
|
|
|
|
pIniPrinter->cbDevMode = pNewDevMode->dmSize
|
|
+ pNewDevMode->dmDriverExtra;
|
|
} else {
|
|
|
|
pIniPrinter->cbDevMode = 0;
|
|
}
|
|
pNewDevMode = NULL;
|
|
}
|
|
}
|
|
|
|
if ( pIniPrinter->pDevMode ) {
|
|
|
|
//
|
|
// Fix up the DEVMODE.dmDeviceName field.
|
|
//
|
|
FixDevModeDeviceName(pIniPrinter->pName,
|
|
pIniPrinter->pDevMode,
|
|
pIniPrinter->cbDevMode);
|
|
}
|
|
|
|
//
|
|
// We need to write the new devmode to the registry
|
|
//
|
|
if ( !UpdatePrinterIni(pIniPrinter, UPDATE_CHANGEID) ) {
|
|
|
|
DBGMSG(DBG_ERROR,
|
|
("SplAddPrinter: UpdatePrinterIni failed after devmode conversion\n"));
|
|
}
|
|
|
|
|
|
// From this point on we don't care if any of these fail
|
|
// we still keep the created printer.
|
|
|
|
LogEvent( pIniSpooler,
|
|
LOG_WARNING,
|
|
MSG_PRINTER_CREATED,
|
|
pIniPrinter->pName,
|
|
NULL );
|
|
|
|
SetPrinterChange(pIniPrinter,
|
|
NULL,
|
|
NVPrinterAll,
|
|
PRINTER_CHANGE_ADD_PRINTER,
|
|
pIniSpooler);
|
|
|
|
|
|
} finally {
|
|
|
|
SplInSem();
|
|
|
|
if ( hPrinter == NULL ) {
|
|
|
|
// FAILURE CLEAN-UP
|
|
|
|
// If a subroutine we called failed
|
|
// then we should save its error incase it is
|
|
// altered during cleanup.
|
|
|
|
if ( dwLastError == ERROR_SUCCESS ) {
|
|
dwLastError = GetLastError();
|
|
}
|
|
|
|
if ( pIniPrinter == NULL ) {
|
|
|
|
// Allow a Print Provider to free its ExtraData
|
|
// associated with this printer.
|
|
|
|
if (( pIniSpooler->pfnFreePrinterExtra != NULL ) &&
|
|
( pExtraData != NULL )) {
|
|
|
|
(*pIniSpooler->pfnFreePrinterExtra)( pExtraData );
|
|
|
|
}
|
|
|
|
} else if ( pIniPrinter->Status & PRINTER_PENDING_CREATION ) {
|
|
|
|
DECPRINTERREF( pIniPrinter );
|
|
|
|
InternalDeletePrinter( pIniPrinter );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Success
|
|
|
|
if ( pIniPrinter ) {
|
|
|
|
DECPRINTERREF( pIniPrinter );
|
|
}
|
|
}
|
|
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
|
|
FreePortTokenList(pKeyData);
|
|
FreeSplMem(pNewDevMode);
|
|
|
|
if ( hPrinter == NULL && Level != 1 ) {
|
|
|
|
DBGMSG(DBG_WARNING, ("SplAddPrinter failed error %d\n", dwLastError ));
|
|
SPLASSERT(dwLastError);
|
|
SetLastError ( dwLastError );
|
|
|
|
} else if ( hPrinter ) {
|
|
|
|
SPLASSERT( hPrinter && Level != 1 );
|
|
|
|
//
|
|
// Printer Drivers Want to Initialize
|
|
// so call them on Success
|
|
|
|
PrinterDriverEvent( pIniPrinter, PRINTER_EVENT_INITIALIZE, (LPARAM)NULL );
|
|
|
|
}
|
|
|
|
DBGMSG( DBG_TRACE, ("AddPrinter returned handle %x\n", hPrinter ));
|
|
}
|
|
|
|
return hPrinter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
RemovePrinterFromAllPorts(
|
|
PINIPORT pIniPort,
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
|
|
DWORD i,j;
|
|
PINIPRINTER *ppIniPrinter;
|
|
|
|
// Go through all the ports that this printer is connected to,
|
|
// and remove the references to this printer:
|
|
|
|
while (pIniPort) {
|
|
|
|
for (i = 0; i < pIniPort->cPrinters; i++ ) {
|
|
|
|
if ( pIniPort->ppIniPrinter[i] == pIniPrinter ) {
|
|
|
|
DBGMSG( DBG_TRACE, ("RemovePrinterFromAllPorts pIniPrinter %x %ws pIniPort %x %ws\n",
|
|
pIniPrinter, pIniPrinter->pName,
|
|
pIniPort, pIniPort->pName ));
|
|
|
|
for (j = i+1; j < pIniPort->cPrinters; j++) {
|
|
pIniPort->ppIniPrinter[j-1] = pIniPort->ppIniPrinter[j];
|
|
}
|
|
|
|
ppIniPrinter = RESIZEPORTPRINTERS(pIniPort, -1);
|
|
if ( ppIniPrinter != NULL ) {
|
|
pIniPort->ppIniPrinter = ppIniPrinter;
|
|
}
|
|
|
|
if ( !--pIniPort->cPrinters )
|
|
RemoveDeviceName(pIniPort);
|
|
|
|
}
|
|
}
|
|
pIniPort = pIniPort->pNext;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
CloseMonitorsRestartOrphanJobs(
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
PINIPORT pIniPort;
|
|
DWORD i;
|
|
BOOL bFound;
|
|
|
|
SplInSem();
|
|
|
|
for ( pIniPort = pIniPrinter->pIniSpooler->pIniPort;
|
|
pIniPort != NULL;
|
|
pIniPort = pIniPort->pNext ) {
|
|
|
|
if ( pIniPort->pIniJob != NULL &&
|
|
pIniPort->pIniJob->pIniPrinter == pIniPrinter ) {
|
|
|
|
|
|
// If this printer is no longer associated with this port
|
|
// then restart that job.
|
|
|
|
for ( i = 0, bFound = FALSE;
|
|
i < pIniPort->cPrinters;
|
|
i++) {
|
|
|
|
if (pIniPort->ppIniPrinter[i] == pIniPrinter) {
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( !bFound ) {
|
|
|
|
DBGMSG( DBG_WARNING, ("CloseMonitorsRestartOrphanJobs Restarting JobId %d\n", pIniPort->pIniJob->JobId ));
|
|
RestartJob( pIniPort->pIniJob );
|
|
}
|
|
}
|
|
|
|
if ( !pIniPort->cPrinters &&
|
|
!(pIniPort->Status & PP_THREADRUNNING) ) {
|
|
|
|
CloseMonitorPort(pIniPort, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This really does delete the printer.
|
|
// It should be called only when the printer has no open handles
|
|
// and no jobs waiting to print
|
|
|
|
BOOL
|
|
DeletePrinterForReal(
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
PINIPRINTER *ppIniPrinter;
|
|
PINIPORT pIniPort;
|
|
DWORD i,j;
|
|
PINISPOOLER pIniSpooler;
|
|
LPWSTR pComma;
|
|
DWORD Status;
|
|
|
|
SplInSem();
|
|
SPLASSERT( pIniPrinter->pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
pIniSpooler = pIniPrinter->pIniSpooler;
|
|
|
|
if ( pIniPrinter->pName != NULL ) {
|
|
|
|
DBGMSG( DBG_TRACE, ("Deleting %ws for real\n", pIniPrinter->pName ));
|
|
}
|
|
|
|
CheckAndUpdatePrinterRegAll( pIniSpooler,
|
|
pIniPrinter->pName,
|
|
NULL,
|
|
UPDATE_REG_DELETE );
|
|
|
|
//
|
|
// Close The Old Printer Data Handle, since it points to the old Registry Entry
|
|
//
|
|
|
|
if ( pIniPrinter->hPrinterDataKey != NULL ) {
|
|
|
|
Status = RegCloseKey( pIniPrinter->hPrinterDataKey );
|
|
|
|
if ( Status != ERROR_SUCCESS ) {
|
|
|
|
DBGMSG( DBG_WARNING, ("DeletePrinterForReal RegCloseKey pIniPrinter %x hPrinterDataKey %x error %d",
|
|
pIniPrinter, pIniPrinter->hPrinterDataKey, Status ));
|
|
}
|
|
}
|
|
|
|
|
|
DeletePrinterIni( pIniPrinter );
|
|
|
|
// Take this IniPrinter off the list of printers for
|
|
// this IniSpooler
|
|
|
|
SplInSem();
|
|
ppIniPrinter = &pIniSpooler->pIniPrinter;
|
|
|
|
while (*ppIniPrinter && *ppIniPrinter != pIniPrinter) {
|
|
ppIniPrinter = &(*ppIniPrinter)->pNext;
|
|
}
|
|
|
|
if (*ppIniPrinter)
|
|
*ppIniPrinter = pIniPrinter->pNext;
|
|
|
|
//
|
|
// Decrement useage counts for Print Processor & Driver
|
|
//
|
|
|
|
if ( pIniPrinter->pIniPrintProc )
|
|
pIniPrinter->pIniPrintProc->cRef--;
|
|
|
|
if ( pIniPrinter->pIniDriver )
|
|
pIniPrinter->pIniDriver->cRef--;
|
|
|
|
|
|
|
|
pIniPort = pIniSpooler->pIniPort;
|
|
|
|
RemovePrinterFromAllPorts( pIniPort, pIniPrinter );
|
|
|
|
CloseMonitorsRestartOrphanJobs( pIniPrinter );
|
|
|
|
DeletePrinterSecurity( pIniPrinter );
|
|
|
|
// When the printer is Zombied it gets a trailing comma
|
|
// Concatingated with the name ( see job.c deleteprintercheck ).
|
|
// Remove trailing , from printer name before we log it as deleted.
|
|
|
|
if ( pIniPrinter->pName != NULL ) {
|
|
|
|
pComma = wcsrchr( pIniPrinter->pName, *szComma );
|
|
|
|
if ( pComma != NULL ) {
|
|
|
|
*pComma = 0;
|
|
}
|
|
|
|
LogEvent( pIniSpooler,
|
|
LOG_WARNING,
|
|
MSG_PRINTER_DELETED,
|
|
pIniPrinter->pName,
|
|
NULL );
|
|
}
|
|
|
|
|
|
FreeStructurePointers((LPBYTE) pIniPrinter, NULL, IniPrinterOffsets);
|
|
|
|
//
|
|
// Allow a Print Provider to free its ExtraData
|
|
// associated with this printer.
|
|
//
|
|
|
|
if (( pIniSpooler->pfnFreePrinterExtra != NULL ) &&
|
|
( pIniPrinter->pExtraData != NULL )) {
|
|
|
|
(*pIniSpooler->pfnFreePrinterExtra)( pIniPrinter->pExtraData );
|
|
}
|
|
|
|
FreeSplMem( pIniPrinter );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
InternalDeletePrinter(
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
BOOL dwRet = FALSE;
|
|
|
|
SPLASSERT( pIniPrinter->signature == IP_SIGNATURE );
|
|
SPLASSERT( pIniPrinter->pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
//
|
|
// This Might be a partially created printer that has no name
|
|
//
|
|
|
|
if ( pIniPrinter->pName != NULL ) {
|
|
|
|
DBGMSG(DBG_TRACE, ("LocalDeletePrinter: %ws pending deletion: references = %d; jobs = %d\n",
|
|
pIniPrinter->pName, pIniPrinter->cRef, pIniPrinter->cJobs));
|
|
|
|
LogEvent( pIniPrinter->pIniSpooler, LOG_WARNING, MSG_PRINTER_DELETION_PENDING,
|
|
pIniPrinter->pName, NULL );
|
|
}
|
|
|
|
|
|
pIniPrinter->Status |= PRINTER_PENDING_DELETION;
|
|
|
|
if (!(pIniPrinter->Status & PRINTER_PENDING_CREATION)) {
|
|
|
|
SetPrinterChange(pIniPrinter,
|
|
NULL,
|
|
NVPrinterStatus,
|
|
PRINTER_CHANGE_DELETE_PRINTER,
|
|
pIniPrinter->pIniSpooler );
|
|
}
|
|
|
|
INC_PRINTER_ZOMBIE_REF( pIniPrinter );
|
|
|
|
if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) {
|
|
|
|
dwRet = ShareThisPrinter(pIniPrinter, pIniPrinter->pShareName, FALSE);
|
|
|
|
if (!dwRet) {
|
|
|
|
pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_SHARED;
|
|
pIniPrinter->Status |= PRINTER_WAS_SHARED;
|
|
CreateServerThread( pIniPrinter->pIniSpooler );
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARNING, ("LocalDeletePrinter: Unsharing this printer failed %ws\n", pIniPrinter->pName));
|
|
}
|
|
}
|
|
|
|
DEC_PRINTER_ZOMBIE_REF( pIniPrinter );
|
|
|
|
|
|
// The printer doesn't get deleted until ClosePrinter is called
|
|
// on the last remaining handle.
|
|
|
|
UpdatePrinterIni( pIniPrinter, UPDATE_CHANGEID );
|
|
|
|
UpdateWinIni( pIniPrinter );
|
|
|
|
DeletePrinterCheck( pIniPrinter );
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SplDeletePrinter(
|
|
HANDLE hPrinter
|
|
)
|
|
{
|
|
PINIPRINTER pIniPrinter;
|
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
|
DWORD LastError = 0;
|
|
PINISPOOLER pIniSpooler;
|
|
|
|
EnterSplSem();
|
|
|
|
pIniSpooler = pSpool->pIniSpooler;
|
|
|
|
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
|
|
if ( ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER) ) {
|
|
|
|
pIniPrinter = pSpool->pIniPrinter;
|
|
|
|
if ( !AccessGranted(SPOOLER_OBJECT_PRINTER,
|
|
DELETE, pSpool) ) {
|
|
|
|
LastError = ERROR_ACCESS_DENIED;
|
|
|
|
} else if (pIniPrinter->cJobs && (pIniPrinter->Status & PRINTER_PAUSED)) {
|
|
|
|
// Don't allow a printer to be deleted that is paused and has
|
|
// jobs waiting, otherwise it'll never get deleted:
|
|
|
|
LastError = ERROR_PRINTER_HAS_JOBS_QUEUED;
|
|
|
|
} else {
|
|
|
|
InternalDeletePrinter( pIniPrinter );
|
|
(VOID) ObjectDeleteAuditAlarm( szSpooler, pSpool, pSpool->GenerateOnClose );
|
|
|
|
|
|
}
|
|
|
|
} else
|
|
LastError = ERROR_INVALID_HANDLE;
|
|
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
|
|
if (LastError) {
|
|
SetLastError(LastError);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
PurgePrinter(
|
|
PINIPRINTER pIniPrinter
|
|
)
|
|
{
|
|
PINIJOB pIniJob;
|
|
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
|
|
|
|
SplInSem();
|
|
|
|
while (pIniJob = pIniPrinter->pIniFirstJob) {
|
|
|
|
while (pIniJob) {
|
|
|
|
if ( (pIniJob->cRef == 0) || !(pIniJob->Status & JOB_PENDING_DELETION)) {
|
|
|
|
// this job is going to be deleted
|
|
|
|
DBGMSG(DBG_TRACE, ("Job Address 0x%.8x Job Status 0x%.8x\n", pIniJob, pIniJob->Status));
|
|
break;
|
|
}
|
|
pIniJob = pIniJob->pIniNextJob;
|
|
}
|
|
|
|
// This job needs to be deleted
|
|
|
|
if (pIniJob) {
|
|
pIniJob->Status &= ~JOB_RESTART;
|
|
DeleteJob(pIniJob,NO_BROADCAST);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
// When purging a printer we don't want to generate a spooler information
|
|
// message for each job being deleted becuase a printer might have a very
|
|
// large number of jobs being purged would lead to a large number of
|
|
// of unnessary and time consuming messages being generated.
|
|
// Since this is a information only message it shouldn't cause any problems
|
|
// Also Win 3.1 didn't have purge printer functionality and the printman
|
|
// generated this message on Win 3.1
|
|
|
|
BroadcastChange( pIniSpooler,WM_SPOOLERSTATUS, PR_JOBSTATUS, (LPARAM)0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SetPrinterPorts(
|
|
PSPOOL pSpool, // Caller's printer handle. May be NULL.
|
|
PINIPRINTER pIniPrinter,
|
|
PKEYDATA pKeyData
|
|
)
|
|
{
|
|
DWORD i,j;
|
|
PINIPORT pIniPort;
|
|
PINIPORT pIniNetPort = NULL;
|
|
BOOL bReturnValue = TRUE;
|
|
PINIPRINTER *ppIniPrinter;
|
|
|
|
|
|
/* Find a port that doesn't have a monitor.
|
|
* (This will choose the last one, if there is one.)
|
|
*/
|
|
for (i=0; i<pKeyData->cTokens; i++) {
|
|
|
|
pIniPort=(PINIPORT)pKeyData->pTokens[i];
|
|
|
|
if (!(pIniPort->Status & PP_MONITOR))
|
|
pIniNetPort = pIniPort;
|
|
}
|
|
|
|
|
|
/* If we found a port with no monitor,
|
|
* check that it hasn't changed from what we had before.
|
|
* If it has, we must close the old handle, if there was one,
|
|
* and open up a new one:
|
|
*/
|
|
if (pSpool) {
|
|
|
|
BOOL NewNetPort = FALSE;
|
|
|
|
if (pSpool->pIniNetPort) {
|
|
|
|
/* There was a net port previously for this handle:
|
|
*/
|
|
if (!pIniNetPort ||
|
|
(NewNetPort = wcscmp(pSpool->pIniNetPort->pName,
|
|
pIniNetPort->pName))) {
|
|
|
|
DBGMSG(DBG_INFO,
|
|
("Network port for %ws changed from %ws to %ws\n",
|
|
pIniPrinter->pName,
|
|
pSpool->pIniNetPort->pName,
|
|
pIniNetPort ? pIniNetPort->pName : L"NULL"));
|
|
|
|
/* We still have an open handle on the old port:
|
|
*/
|
|
if (pSpool->hPort == INVALID_PORT_HANDLE) {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("Port connection with invalid handle closing\n"));
|
|
|
|
pSpool->OpenPortError = NO_ERROR;
|
|
|
|
} else if (!ClosePrinter(pSpool->hPort)) {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("ClosePrinter( %ws ) failed: Error %d\n",
|
|
pIniNetPort ? pIniNetPort->pName : L"NULL",
|
|
GetLastError()));
|
|
}
|
|
|
|
pSpool->hPort = NULL;
|
|
pSpool->pIniNetPort = NULL;
|
|
}
|
|
|
|
} else if (pIniNetPort) {
|
|
|
|
NewNetPort = TRUE;
|
|
|
|
DBGMSG(DBG_INFO,
|
|
("Network port for %ws changed from NULL to %ws\n",
|
|
pIniPrinter->pName,
|
|
pIniNetPort->pName));
|
|
}
|
|
|
|
if (NewNetPort) {
|
|
|
|
/* Open the new port. This should succeed,
|
|
* since we only just opened it to validate
|
|
* the port entry.
|
|
*/
|
|
pSpool->pIniNetPort = pIniNetPort;
|
|
|
|
if (!OpenPrinterPortW(pIniNetPort->pName, &pSpool->hPort, NULL)) {
|
|
|
|
DWORD Error = GetLastError();
|
|
|
|
if ( Error == ERROR_INVALID_NAME ||
|
|
Error == ERROR_INVALID_PRINTER_NAME ||
|
|
Error == ERROR_INVALID_PARAMETER )
|
|
|
|
SetLastError(ERROR_UNKNOWN_PORT);
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("Oops, OpenPrinterPort( %ws ) just failed: The error was %d\n",
|
|
pIniNetPort->pName, Error));
|
|
bReturnValue = FALSE;
|
|
goto Cleanup;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SPLASSERT( pIniPrinter != NULL );
|
|
SPLASSERT( pIniPrinter->signature == IP_SIGNATURE );
|
|
SPLASSERT( pIniPrinter->pIniSpooler != NULL );
|
|
SPLASSERT( pIniPrinter->pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
|
|
pIniPort = pIniPrinter->pIniSpooler->pIniPort;
|
|
|
|
RemovePrinterFromAllPorts(pIniPort, pIniPrinter);
|
|
|
|
// Go through all the ports that this printer is connected to,
|
|
// and add new references to this printer:
|
|
|
|
for (i = 0; i < pKeyData->cTokens; i++ ) {
|
|
|
|
pIniPort = (PINIPORT)pKeyData->pTokens[i];
|
|
|
|
if ( !AddIniPrinterToIniPort( pIniPort, pIniPrinter ) ) {
|
|
bReturnValue = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
CloseMonitorsRestartOrphanJobs( pIniPrinter );
|
|
|
|
Cleanup:
|
|
return bReturnValue;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SplResetPrinter(
|
|
HANDLE hPrinter,
|
|
LPPRINTER_DEFAULTS pDefaults
|
|
)
|
|
{
|
|
PSPOOL pSpool=(PSPOOL)hPrinter;
|
|
PINIPRINTER pIniPrinter;
|
|
PDEVMODE pSourceDevMode = NULL;
|
|
BOOL bFreeSourceDevMode = FALSE;
|
|
DWORD cbSize = 0;
|
|
|
|
DBGMSG(DBG_TRACE, ("ResetPrinter( %08x )\n", hPrinter));
|
|
|
|
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( pDefaults == NULL ) {
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
pIniPrinter = pSpool->pIniPrinter;
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// If pDefaults->pDevMode == -1 then we want to set default devmode in the IniPrinter to pSpool
|
|
//
|
|
if ( (pSpool->TypeofHandle & PRINTER_HANDLE_3XCLIENT) &&
|
|
pDefaults->pDevMode &&
|
|
pDefaults->pDevMode == (LPDEVMODE)-1 ) {
|
|
|
|
pSourceDevMode = ConvertDevModeToSpecifiedVersion(pIniPrinter,
|
|
pIniPrinter->pDevMode,
|
|
NULL,
|
|
NULL,
|
|
NT3X_VERSION);
|
|
|
|
if ( !pSourceDevMode )
|
|
goto Cleanup;
|
|
|
|
bFreeSourceDevMode = TRUE;
|
|
}
|
|
|
|
if (pDefaults->pDatatype) {
|
|
|
|
if (pDefaults->pDatatype == (LPWSTR)-1) {
|
|
|
|
if (pSpool->pIniPrinter->pDatatype) {
|
|
ReallocSplStr(&pSpool->pDatatype, pSpool->pIniPrinter->pDatatype);
|
|
}else {
|
|
DBGMSG(DBG_WARNING, ("LocalResetPrinter: pSpool->pIniPrinter->pDatatype is NULL\n"));
|
|
}
|
|
}else {
|
|
ReallocSplStr(&pSpool->pDatatype, pDefaults->pDatatype);
|
|
}
|
|
pSpool->pIniPrintProc->cRef--;
|
|
pSpool->pIniPrintProc = FindDatatype(pIniPrinter->pIniPrintProc,
|
|
pSpool->pDatatype);
|
|
pSpool->pIniPrintProc->cRef++;
|
|
|
|
}else {
|
|
DBGMSG(DBG_TRACE,("LocalResetPrinter: Not resetting the pDatatype field\n"));
|
|
}
|
|
|
|
if (pDefaults->pDevMode) {
|
|
|
|
if ( pDefaults->pDevMode == (LPDEVMODE)-1 && !pSourceDevMode ) {
|
|
if (pSpool->pIniPrinter->pDevMode) {
|
|
cbSize = pSpool->pIniPrinter->pDevMode->dmSize +
|
|
pSpool->pIniPrinter->pDevMode->dmDriverExtra;
|
|
pSourceDevMode = pSpool->pIniPrinter->pDevMode;
|
|
} else {
|
|
DBGMSG(DBG_TRACE, ("LocalResetPrinter: pSpool->pIniPrinter->pDevMode is NULL\n"));
|
|
}
|
|
} else {
|
|
cbSize = pDefaults->pDevMode->dmSize +
|
|
pDefaults->pDevMode->dmDriverExtra;
|
|
pSourceDevMode = pDefaults->pDevMode;
|
|
}
|
|
|
|
|
|
if ( pSourceDevMode && cbSize) {
|
|
if (pSpool->pDevMode)
|
|
FreeSplMem(pSpool->pDevMode);
|
|
if (pSpool->pDevMode = AllocSplMem(cbSize)) {
|
|
memcpy(pSpool->pDevMode, pSourceDevMode, cbSize);
|
|
} else {
|
|
DBGMSG(DBG_WARNING, ("LocalResetPrinter: AllocSplMem failed - setting pSpool->pDevMode to NULL\n"));
|
|
pSpool->pDevMode = NULL;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
DBGMSG(DBG_TRACE, ("LocalResetPrinter: Not resetting the pDevMode field\n"));
|
|
}
|
|
|
|
Cleanup:
|
|
LeaveSplSem();
|
|
|
|
if ( bFreeSourceDevMode )
|
|
FreeSplMem(pSourceDevMode);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CopyPrinterIni(
|
|
PINIPRINTER pIniPrinter,
|
|
LPWSTR pNewName
|
|
)
|
|
{
|
|
HKEY hPrinterRootKey=NULL, hPrinterKey=NULL;
|
|
DWORD Status;
|
|
LPWSTR pSourceKeyName, pDestKeyName;
|
|
WCHAR pSrcScratch[MAX_PATH];
|
|
WCHAR pDestScratch[MAX_PATH];
|
|
HANDLE hToken;
|
|
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
|
|
DWORD dwLastError;
|
|
BOOL bReturnValue = TRUE;
|
|
|
|
SPLASSERT( pIniSpooler != NULL);
|
|
SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
Status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
pIniSpooler->pszRegistryPrinters,
|
|
0,
|
|
KEY_WRITE,
|
|
&hPrinterRootKey );
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
ImpersonatePrinterClient(hToken);
|
|
return(FALSE);
|
|
}
|
|
|
|
pSourceKeyName = RemoveBackslashesForRegistryKey( pIniPrinter->pName, pSrcScratch );
|
|
pDestKeyName = RemoveBackslashesForRegistryKey( pNewName, pDestScratch );
|
|
|
|
CopyRegistryKeys( hPrinterRootKey, pSourceKeyName, hPrinterRootKey, pDestKeyName );
|
|
|
|
//
|
|
// Close The Old Printer Data Handle, since it points to the old Registry Entry
|
|
//
|
|
|
|
if ( pIniPrinter->hPrinterDataKey != NULL ) {
|
|
|
|
Status = RegCloseKey( pIniPrinter->hPrinterDataKey );
|
|
|
|
if ( Status == ERROR_SUCCESS ) {
|
|
|
|
pIniPrinter->hPrinterDataKey = NULL;
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARNING, ("CopyPrinterIni RegCloseKey pIniPrinter %x hPrinterDataKey %x error %d",
|
|
pIniPrinter, pIniPrinter->hPrinterDataKey, Status ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make Sure we have an Open DriverData Key handle
|
|
//
|
|
|
|
if ( PrinterCreateKey( hPrinterRootKey,
|
|
pDestKeyName,
|
|
&hPrinterKey,
|
|
&dwLastError )) {
|
|
|
|
if ( !PrinterCreateKey( hPrinterKey,
|
|
szPrinterData,
|
|
&pIniPrinter->hPrinterDataKey,
|
|
&dwLastError )) {
|
|
|
|
DBGMSG( DBG_WARNING, ("CopyPrinterIni failed to open PrinterData key %d\n", dwLastError ));
|
|
|
|
SetLastError( dwLastError );
|
|
pIniPrinter->hPrinterDataKey = NULL;
|
|
bReturnValue = FALSE;
|
|
}
|
|
|
|
RegCloseKey( hPrinterKey );
|
|
|
|
} else {
|
|
|
|
DBGMSG( DBG_WARNING, ("CopyPrinterIni failed to open Printer Key pIniPrinter %x pName %ws error %d\n", pIniPrinter, pDestKeyName, dwLastError ));
|
|
|
|
SetLastError( dwLastError);
|
|
bReturnValue = FALSE;
|
|
}
|
|
|
|
RegCloseKey( hPrinterRootKey );
|
|
|
|
ImpersonatePrinterClient( hToken );
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
VOID
|
|
FixDevModeDeviceName(
|
|
LPWSTR pPrinterName,
|
|
PDEVMODE pDevMode,
|
|
DWORD cbDevMode)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fixes up the dmDeviceName field of the DevMode to be the same
|
|
as the printer name.
|
|
|
|
Arguments:
|
|
|
|
pPrinterName - Name of the printer (qualified with server for remote)
|
|
|
|
pDevMode - DevMode to fix up
|
|
|
|
cbDevMode - byte count of devmode.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD cbDeviceMax;
|
|
DWORD cchDeviceStrLenMax;
|
|
//
|
|
// Compute the maximum length of the device name string
|
|
// this is the min of the structure and allocated space.
|
|
//
|
|
cbDeviceMax = (cbDevMode < sizeof(pDevMode->dmDeviceName)) ?
|
|
cbDevMode :
|
|
sizeof(pDevMode->dmDeviceName);
|
|
|
|
cchDeviceStrLenMax = (cbDeviceMax / sizeof(pDevMode->dmDeviceName[0])) -1;
|
|
|
|
//
|
|
// !! LATER !!
|
|
//
|
|
// Put in DBG code to debug print if the device name is truncated.
|
|
//
|
|
wcsncpy(pDevMode->dmDeviceName,
|
|
pPrinterName,
|
|
cchDeviceStrLenMax);
|
|
|
|
//
|
|
// Ensure NULL termination.
|
|
//
|
|
pDevMode->dmDeviceName[cchDeviceStrLenMax] = 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CopyPrinterDevModeToIniPrinter(
|
|
PINIPRINTER pIniPrinter,
|
|
PDEVMODE pDevMode)
|
|
{
|
|
BOOL bReturn = TRUE;
|
|
DWORD dwInSize = 0;
|
|
DWORD dwCurSize = 0;
|
|
PINISPOOLER pIniSpooler = pIniPrinter->pIniSpooler;
|
|
WCHAR PrinterName[ MAX_UNC_PRINTER_NAME ];
|
|
|
|
if (pDevMode) {
|
|
|
|
dwInSize = pDevMode->dmSize + pDevMode->dmDriverExtra;
|
|
if (pIniPrinter->pDevMode) {
|
|
|
|
//
|
|
// Detect if the devmodes are identical
|
|
// if they are, no need to copy or send devmode.
|
|
// (Skip the device name though!)
|
|
//
|
|
dwCurSize = pIniPrinter->pDevMode->dmSize
|
|
+ pIniPrinter->pDevMode->dmDriverExtra;
|
|
|
|
if (dwInSize == dwCurSize) {
|
|
|
|
if (dwInSize > sizeof(pDevMode->dmDeviceName)) {
|
|
|
|
if (!memcmp(&pDevMode->dmSpecVersion,
|
|
&pIniPrinter->pDevMode->dmSpecVersion,
|
|
dwCurSize - sizeof(pDevMode->dmDeviceName))) {
|
|
|
|
//
|
|
// No need to copy this devmode because its identical
|
|
// to what we already have.
|
|
//
|
|
DBGMSG(DBG_TRACE,("Identical DevModes, no update\n"));
|
|
bReturn = FALSE;
|
|
|
|
goto FixupName;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the devmode which we already have.
|
|
//
|
|
FreeSplMem(pIniPrinter->pDevMode);
|
|
}
|
|
|
|
pIniPrinter->cbDevMode = pDevMode->dmSize +
|
|
pDevMode->dmDriverExtra;
|
|
|
|
if (pIniPrinter->pDevMode = AllocSplMem(pIniPrinter->cbDevMode)) {
|
|
|
|
memcpy(pIniPrinter->pDevMode,
|
|
pDevMode,
|
|
pIniPrinter->cbDevMode);
|
|
|
|
//
|
|
// Prepend the machine name if this is not localspl
|
|
//
|
|
|
|
if ( pIniSpooler != pLocalIniSpooler ) {
|
|
|
|
// For Non Local Printers prepend the Machine Name
|
|
|
|
wsprintf( PrinterName, L"%ws\\%ws", pIniSpooler->pMachineName, pIniPrinter->pName );
|
|
|
|
} else {
|
|
|
|
wsprintf( PrinterName, L"%ws", pIniPrinter->pName );
|
|
|
|
}
|
|
|
|
BroadcastChange( pIniSpooler,WM_DEVMODECHANGE, 0, (LPARAM)PrinterName);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// No old, no new, so no change.
|
|
//
|
|
if (!pIniPrinter->pDevMode)
|
|
return FALSE;
|
|
}
|
|
|
|
FixupName:
|
|
|
|
if (pIniPrinter->pDevMode) {
|
|
|
|
//
|
|
// Fix up the DEVMODE.dmDeviceName field.
|
|
//
|
|
FixDevModeDeviceName(pIniPrinter->pName,
|
|
pIniPrinter->pDevMode,
|
|
pIniPrinter->cbDevMode);
|
|
}
|
|
return bReturn;
|
|
}
|