|
|
/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
spool.c
Abstract:
This module contains the Netware print provider.
Author:
Yi-Hsin Sung (yihsins) 15-May-1993
Revision History:
--*/
#include <stdio.h>
#include <stdlib.h>
#include <nw.h>
#include <nwreg.h>
#include <nwpkstr.h>
#include <splutil.h>
#include <queue.h>
#include <nwmisc.h>
//------------------------------------------------------------------
//
// Local Definitions
//
//------------------------------------------------------------------
#define NW_SIGNATURE 0x574E /* "NW" is the signature */
#define SPOOL_STATUS_STARTDOC 0x00000001
#define SPOOL_STATUS_ADDJOB 0x00000002
#define SPOOL_STATUS_ABORT 0x00000003
#define PRINTER_CHANGE_VALID 0x55770F07
#define PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE 10000
#define PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE 1000
#define REG_TIMEOUT_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters"
#define REG_TIMEOUT_VALUE_NAME L"PrintNotifyTimeout"
#define NDS_MAX_NAME_CHARS 256
#define NDS_MAX_NAME_SIZE ( NDS_MAX_NAME_CHARS * 2 )
//
// Printer structure
//
typedef struct _NWPRINTER { LPWSTR pszServer; // Server Name
LPWSTR pszQueue; // Queue Name
LPWSTR pszUncConnection; // UNC Connection Name
// (only present if NDS print queue
DWORD nQueueId; // Queue Id
struct _NWPRINTER *pNextPrinter; // Points to the next printer
struct _NWSPOOL *pSpoolList; // Points to the list of open handles
} NWPRINTER, *PNWPRINTER;
//
// Handle structure
//
typedef struct _NWSPOOL { DWORD nSignature; // Signature
DWORD errOpenPrinter; // OpenPrinter API will always return
// success on known printers. This will
// contain the error that we get
// if something went wrong in the API.
PNWPRINTER pPrinter; // Points to the corresponding printer
HANDLE hServer; // Opened handle to the server
struct _NWSPOOL *pNextSpool; // Points to the next handle
DWORD nStatus; // Status
DWORD nJobNumber; // StartDocPrinter/AddJob: Job Number
HANDLE hChangeEvent; // WaitForPrinterChange: event to wait on
DWORD nWaitFlags; // WaitForPrinterChange: flags to wait on
DWORD nChangeFlags; // Changes that occurred to the printer
} NWSPOOL, *PNWSPOOL;
//------------------------------------------------------------------
//
// Global Variables
//
//------------------------------------------------------------------
// Stores the timeout value used in WaitForPrinterChange ( in milliseconds )
STATIC DWORD NwTimeOutValue = PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE;
// Points to the link list of printers
STATIC PNWPRINTER NwPrinterList = NULL;
//------------------------------------------------------------------
//
// Local Function Prototypes
//
//------------------------------------------------------------------
VOID NwSetPrinterChange( IN PNWSPOOL pSpool, IN DWORD nFlags );
PNWPRINTER NwFindPrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue );
DWORD NwCreatePrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue, OUT PNWPRINTER *ppPrinter, OUT PHANDLE phServer );
VOID NwRemovePrinterEntry( IN PNWPRINTER pPrinter );
LPWSTR NwGetUncObjectName( IN LPWSTR ContainerName );
VOID NwInitializePrintProvider( VOID ) /*++
Routine Description:
This routine initializes the server side print provider when the workstation service starts up.
Arguments:
None.
Return Value:
--*/ { DWORD err; HKEY hkey; DWORD dwTemp; DWORD dwSize = sizeof( dwTemp );
//
// Read the time out value from the registry.
// We will ignore all errors since we can always have a default time out.
// The default will be used if the key does not exist.
//
err = RegOpenKeyExW( HKEY_LOCAL_MACHINE, REG_TIMEOUT_PATH, 0, KEY_READ, &hkey );
if ( !err ) { err = RegQueryValueExW( hkey, REG_TIMEOUT_VALUE_NAME, NULL, NULL, (LPBYTE) &dwTemp, &dwSize );
if ( !err ) { NwTimeOutValue = dwTemp;
//
// tommye - bug 139469 - removed
// if (NwTimeOutValue >= 0) because NwtimeOutValue is a DWORD
//
// Use the minimum timeout value if the
// value set in the registry is too small.
//
if (NwTimeOutValue <= PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE) { NwTimeOutValue = PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE; } }
RegCloseKey( hkey ); }
}
VOID NwTerminatePrintProvider( VOID ) /*++
Routine Description:
This routine cleans up the server side print provider when the workstation service shut downs.
Arguments:
None.
Return Value:
--*/ { PNWPRINTER pPrinter, pNext; PNWSPOOL pSpool, pNextSpool;
for ( pPrinter = NwPrinterList; pPrinter; pPrinter = pNext ) { pNext = pPrinter->pNextPrinter;
pPrinter->pNextPrinter = NULL;
for ( pSpool = pPrinter->pSpoolList; pSpool; pSpool = pNextSpool ) { pNextSpool = pSpool->pNextSpool; if ( pSpool->hChangeEvent ) CloseHandle( pSpool->hChangeEvent ); (VOID) NtClose( pSpool->hServer );
//
// Free all memory associated with the context handle
//
FreeNwSplMem( pSpool, sizeof( NWSPOOL) ); }
pPrinter->pSpoolList = NULL; FreeNwSplStr( pPrinter->pszServer ); FreeNwSplStr( pPrinter->pszQueue ); if ( pPrinter->pszUncConnection ) { (void) NwrDeleteConnection( NULL, pPrinter->pszUncConnection, FALSE ); FreeNwSplStr( pPrinter->pszUncConnection ); } FreeNwSplMem( pPrinter, sizeof( NWPRINTER)); }
NwPrinterList = NULL; NwTimeOutValue = PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE; }
DWORD NwrOpenPrinter( IN LPWSTR Reserved, IN LPWSTR pszPrinterName, IN DWORD fKnownPrinter, OUT LPNWWKSTA_PRINTER_CONTEXT phPrinter ) /*++
Routine Description:
This routine retrieves a handle identifying the specified printer.
Arguments:
Reserved - Unused pszPrinterName - Name of the printer fKnownPrinter - TRUE if we have successfully opened the printer before, FALSE otherwise. phPrinter - Receives the handle that identifies the given printer
Return Value:
--*/ { DWORD err; PNWSPOOL pSpool = NULL; LPWSTR pszServer = NULL; LPWSTR pszQueue = NULL; PNWPRINTER pPrinter = NULL; BOOL fImpersonate = FALSE ; HANDLE hServer; BOOL isPrinterNameValid;
UNREFERENCED_PARAMETER( Reserved );
if ( pszPrinterName[0] == L' ' && pszPrinterName[1] == L'\\' && pszPrinterName[2] == L'\\' ) { if ( (pszServer = AllocNwSplStr( pszPrinterName + 1 )) == NULL ) return ERROR_NOT_ENOUGH_MEMORY; isPrinterNameValid = ValidateUNCName( pszPrinterName + 1 ); } else { if ( (pszServer = AllocNwSplStr( pszPrinterName )) == NULL ) return ERROR_NOT_ENOUGH_MEMORY; isPrinterNameValid = ValidateUNCName( pszPrinterName ); }
CharUpperW( pszServer ); // convert in place
//
// ValidatePrinterName
//
if ( ( !isPrinterNameValid ) || ( (pszQueue = wcschr( pszServer + 2, L'\\')) == NULL ) || ( pszQueue == (pszServer + 2) ) || ( *(pszQueue + 1) == L'\0' ) ) { FreeNwSplStr( pszServer ); return ERROR_INVALID_NAME; }
*pszQueue = L'\0'; // put a '\0' in place of '\\'
pszQueue++; // Get past the '\0'
if ( !(pSpool = AllocNwSplMem( LMEM_ZEROINIT, sizeof( NWSPOOL)))) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; }
//
// Impersonate the client
//
if ((err = NwImpersonateClient()) != NO_ERROR) { goto ErrorExit; } fImpersonate = TRUE ;
EnterCriticalSection( &NwPrintCritSec );
if ((err = NwCreatePrinterEntry( pszServer, pszQueue, &pPrinter, &hServer))) { if ( !fKnownPrinter ) { LeaveCriticalSection( &NwPrintCritSec ); goto ErrorExit; } }
//
// Construct the print queue context handle to give back to the caller
//
pSpool->nSignature = NW_SIGNATURE; pSpool->errOpenPrinter = err;
pSpool->hServer = hServer; pSpool->nStatus = 0; pSpool->nJobNumber = 0; pSpool->hChangeEvent= NULL; pSpool->nWaitFlags = 0; pSpool->nChangeFlags= 0;
if ( !err ) { pSpool->pPrinter = pPrinter; pSpool->pNextSpool = pPrinter->pSpoolList; pPrinter->pSpoolList= pSpool; } else { pSpool->pPrinter = NULL; pSpool->pNextSpool = NULL; }
// We know about this printer before but failed to retrieve
// it this time. Clean up the error and return successfully.
// The error code is stored in the handle above which
// will be returned on subsequent calls using this
// dummy handle.
err = NO_ERROR;
LeaveCriticalSection( &NwPrintCritSec );
ErrorExit:
if (fImpersonate) (void) NwRevertToSelf() ;
if ( err ) { if ( pSpool ) FreeNwSplMem( pSpool, sizeof( NWSPOOL) ); } else { *phPrinter = (NWWKSTA_PRINTER_CONTEXT) pSpool; }
//
// Free up all allocated memories
//
*(pszServer + wcslen( pszServer)) = L'\\'; FreeNwSplStr( pszServer );
return err;
}
DWORD NwrClosePrinter( IN OUT LPNWWKSTA_PRINTER_CONTEXT phPrinter ) /*++
Routine Description:
This routine closes the given printer object.
Arguments:
phPrinter - Handle of the printer object
Return Value:
--*/ { PNWSPOOL pSpool = (PNWSPOOL) *phPrinter; PNWPRINTER pPrinter; PNWSPOOL pCur, pPrev = NULL;
if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) return ERROR_INVALID_HANDLE;
//
// If OpenPrinter failed, then this is a dummy handle.
// We just need to free up the memory.
//
if ( pSpool->errOpenPrinter ) { //
// invalidate the signature, but leave a recognizable value
//
pSpool->nSignature += 1 ; FreeNwSplMem( pSpool, sizeof( NWSPOOL) ); *phPrinter = NULL; return NO_ERROR; }
pPrinter = pSpool->pPrinter; ASSERT( pPrinter );
//
// Call EndDocPrinter if the user has not already done so
//
if ( pSpool->nStatus == SPOOL_STATUS_STARTDOC ) { (void) NwrEndDocPrinter( *phPrinter ); } else if ( pSpool->nStatus == SPOOL_STATUS_ADDJOB ) { (void) NwrScheduleJob( *phPrinter, pSpool->nJobNumber ); }
if ( pSpool->hChangeEvent ) CloseHandle( pSpool->hChangeEvent );
pSpool->hChangeEvent = NULL; pSpool->nChangeFlags = 0; (VOID) NtClose( pSpool->hServer );
EnterCriticalSection( &NwPrintCritSec );
for ( pCur = pPrinter->pSpoolList; pCur; pPrev = pCur, pCur = pCur->pNextSpool ) { if ( pCur == pSpool ) { if ( pPrev ) pPrev->pNextSpool = pCur->pNextSpool; else pPrinter->pSpoolList = pCur->pNextSpool; break; }
}
ASSERT( pCur );
if ( pPrinter->pSpoolList == NULL ) { #if DBG
IF_DEBUG(PRINT) { KdPrint(("*************DELETED PRINTER ENTRY: %ws\\%ws\n\n", pPrinter->pszServer, pPrinter->pszQueue )); } #endif
NwRemovePrinterEntry( pPrinter ); }
LeaveCriticalSection( &NwPrintCritSec );
//
// invalidate the signature, but leave a recognizable value
//
pSpool->nSignature += 1 ;
pSpool->pNextSpool = NULL; pSpool->pPrinter = NULL;
//
// Free all memory associated with the context handle
//
FreeNwSplMem( pSpool, sizeof( NWSPOOL) );
//
// indicate to RPC we are done
//
*phPrinter = NULL;
return NO_ERROR; }
DWORD NwrGetPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwLevel, IN OUT LPBYTE pbPrinter, IN DWORD cbBuf, OUT LPDWORD pcbNeeded ) /*++
Routine Description:
The routine retrieves information about the given printer.
Arguments:
hPrinter - Handle of the printer dwLevel - Specifies the level of the structure to which pbPrinter points. pbPrinter - Points to a buffer that receives the PRINTER_INFO object. cbBuf - Size, in bytes of the array pbPrinter points to. pcbNeeded - Points to a value which specifies the number of bytes copied if the function succeeds or the number of bytes required if cbBuf was too small.
Return Value:
--*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter;
LPBYTE pbEnd = pbPrinter + cbBuf; BOOL fFitInBuffer; DWORD_PTR *pOffsets;
if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { return ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; } else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) && ( dwLevel != 3 )) { return ERROR_INVALID_LEVEL; }
if ( !pbPrinter ) { if ( cbBuf == 0 ) { //
// Calculate size needed
//
pPrinter = pSpool->pPrinter; ASSERT( pPrinter );
if ( dwLevel == 1 ) { *pcbNeeded = sizeof( PRINTER_INFO_1W ) + ( wcslen( pPrinter->pszServer ) + wcslen( pPrinter->pszQueue ) + 2 ) * sizeof( WCHAR ); } else if ( dwLevel == 2 ) { *pcbNeeded = sizeof( PRINTER_INFO_2W ) + ( 2*wcslen( pPrinter->pszServer ) + 2*wcslen( pPrinter->pszQueue ) + 4 ) * sizeof( WCHAR ); } else // Level == 3
{ PRINTER_INFO_3 *pPrinterInfo3 = (PRINTER_INFO_3 *) pbPrinter;
*pcbNeeded = sizeof( PRINTER_INFO_3 ); } return ERROR_INSUFFICIENT_BUFFER; } else return ERROR_INVALID_PARAMETER; }
pPrinter = pSpool->pPrinter; ASSERT( pPrinter );
if ( dwLevel == 1 ) { PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) pbPrinter; LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_1W );
//
// Calculate size needed
//
*pcbNeeded = sizeof( PRINTER_INFO_1W ) + ( wcslen( pPrinter->pszServer ) + wcslen( pPrinter->pszQueue ) + 2 ) * sizeof( WCHAR );
if ( cbBuf < *pcbNeeded ) return ERROR_INSUFFICIENT_BUFFER;
pOffsets = PrinterInfo1Offsets;
//
// Fill in the structure
//
pPrinterInfo1->Flags = PRINTER_ENUM_REMOTE | PRINTER_ENUM_NAME; pPrinterInfo1->pComment = NULL;
fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszServer, wcslen( pPrinter->pszServer ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pDescription );
ASSERT( fFitInBuffer );
fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszQueue, wcslen( pPrinter->pszQueue ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pName );
ASSERT( fFitInBuffer );
} else if ( dwLevel == 2 ) { DWORD err; BYTE nQueueStatus; BYTE nNumJobs; PRINTER_INFO_2W *pPrinterInfo2 = (PRINTER_INFO_2W *) pbPrinter; LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_2W );
//
// Check if the buffer is big enough to hold all the data
//
*pcbNeeded = sizeof( PRINTER_INFO_2W ) + ( 2*wcslen( pPrinter->pszServer ) + 2*wcslen( pPrinter->pszQueue ) + 4 ) * sizeof( WCHAR );
if ( cbBuf < *pcbNeeded ) return ERROR_INSUFFICIENT_BUFFER;
pOffsets = PrinterInfo2Offsets;
err = NwReadQueueCurrentStatus( pSpool->hServer, pPrinter->nQueueId, &nQueueStatus, &nNumJobs );
if ( err ) return err;
pPrinterInfo2->Status = (nQueueStatus & 0x05)? PRINTER_STATUS_PAUSED : 0; pPrinterInfo2->cJobs = nNumJobs;
fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszServer, wcslen( pPrinter->pszServer ), (LPCWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo2->pServerName );
ASSERT( fFitInBuffer );
pbEnd -= ( wcslen( pPrinter->pszQueue) + 1 ) * sizeof( WCHAR ); wcscpy( (LPWSTR) pbEnd, pPrinter->pszQueue ); pbEnd -= ( wcslen( pPrinter->pszServer) + 1 ) * sizeof( WCHAR ); wcscpy( (LPWSTR) pbEnd, pPrinter->pszServer ); *(pbEnd + wcslen( pPrinter->pszServer )*sizeof(WCHAR))= L'\\'; pPrinterInfo2->pPrinterName = (LPWSTR) pbEnd;
fFitInBuffer = NwlibCopyStringToBuffer( pPrinter->pszQueue, wcslen( pPrinter->pszQueue ), (LPCWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo2->pShareName );
ASSERT( fFitInBuffer );
pPrinterInfo2->pPortName = NULL; pPrinterInfo2->pDriverName = NULL; pPrinterInfo2->pComment = NULL; pPrinterInfo2->pLocation = NULL; pPrinterInfo2->pDevMode = NULL; pPrinterInfo2->pSepFile = NULL; pPrinterInfo2->pPrintProcessor = NULL; pPrinterInfo2->pDatatype = NULL; pPrinterInfo2->pParameters = NULL; pPrinterInfo2->pSecurityDescriptor = NULL; pPrinterInfo2->Attributes = PRINTER_ATTRIBUTE_QUEUED; pPrinterInfo2->Priority = 0; pPrinterInfo2->DefaultPriority = 0; pPrinterInfo2->StartTime = 0; pPrinterInfo2->UntilTime = 0; pPrinterInfo2->AveragePPM = 0; } else // Level == 3
{ PRINTER_INFO_3 *pPrinterInfo3 = (PRINTER_INFO_3 *) pbPrinter;
*pcbNeeded = sizeof( PRINTER_INFO_3 );
if ( cbBuf < *pcbNeeded ) return ERROR_INSUFFICIENT_BUFFER;
pOffsets = PrinterInfo3Offsets; pPrinterInfo3->pSecurityDescriptor = NULL; }
MarshallDownStructure( pbPrinter, pOffsets, pbPrinter ); return NO_ERROR; }
DWORD NwrSetPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwCommand ) /*++
Routine Description:
The routine sets information about the given printer.
Arguments:
hPrinter - Handle of the printer dwCommand - Specifies the new printer state
Return Value:
--*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; DWORD err = NO_ERROR; PNWPRINTER pPrinter;
if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { return ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; }
pPrinter = pSpool->pPrinter; ASSERT( pPrinter );
switch ( dwCommand ) { case PRINTER_CONTROL_PAUSE: case PRINTER_CONTROL_RESUME: { BYTE nQueueStatus = 0; BYTE nNumJobs;
//
// Get the original queue status so that we don't overwrite
// some of the bits.
//
err = NwReadQueueCurrentStatus( pSpool->hServer, pPrinter->nQueueId, &nQueueStatus, &nNumJobs );
if ( !err ) { //
// Clear the pause bits, and leave the rest alone.
//
nQueueStatus &= ~0x05; }
if ( dwCommand == PRINTER_CONTROL_PAUSE ) { nQueueStatus |= 0x04; }
err = NwSetQueueCurrentStatus( pSpool->hServer, pPrinter->nQueueId, nQueueStatus ); if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER ); break; }
case PRINTER_CONTROL_PURGE:
err = NwRemoveAllJobsFromQueue( pSpool->hServer, pPrinter->nQueueId ); if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER | PRINTER_CHANGE_DELETE_JOB ); break;
default: //
// dwCommand is 0 so that means
// some properties of the printer has changed.
// We will ignore the properties that
// are being modified since most properties
// are stored in the registry by spooler.
// All we need to do is to signal WaitForPrinterChange to
// return so that print manager will refresh its data.
//
ASSERT( dwCommand == 0 ); NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER ); break; }
return err; }
DWORD NwrEnumPrinters( IN LPWSTR Reserved, IN LPWSTR pszName, IN OUT LPBYTE pbPrinter, IN DWORD cbBuf, OUT LPDWORD pcbNeeded, OUT LPDWORD pcReturned ) /*++
Routine Description:
This routine enumerates the available providers, servers, printers depending on the given pszName.
Arguments:
Reserved - Unused pszName - The name of the container object pbPrinter - Points to the array to receive the PRINTER_INFO objects cbBuf - Size, in bytes of pbPrinter pcbNeeded - Count of bytes needed pcReturned - Count of PRINTER_INFO objects
Return Value:
--*/ { PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) pbPrinter;
*pcbNeeded = 0; *pcReturned = 0;
if ( ( cbBuf != 0 ) && !pbPrinter ) { return ERROR_INVALID_PARAMETER; }
if ( !pszName ) // Enumerate the provider name
{ BOOL fFitInBuffer; LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_1W ); LPBYTE pbEnd = pbPrinter + cbBuf;
*pcbNeeded = sizeof( PRINTER_INFO_1W ) + ( 2 * wcslen( NwProviderName ) + + 2) * sizeof(WCHAR);
if ( *pcbNeeded > cbBuf ) return ERROR_INSUFFICIENT_BUFFER;
pPrinterInfo1->Flags = PRINTER_ENUM_ICON1 | PRINTER_ENUM_CONTAINER | PRINTER_ENUM_EXPAND; pPrinterInfo1->pComment = NULL;
fFitInBuffer = NwlibCopyStringToBuffer( NwProviderName, wcslen( NwProviderName ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pDescription );
ASSERT( fFitInBuffer );
fFitInBuffer = NwlibCopyStringToBuffer( NwProviderName, wcslen( NwProviderName ), (LPWSTR) pbFixedEnd, (LPWSTR *) &pbEnd, &pPrinterInfo1->pName );
ASSERT( fFitInBuffer );
MarshallDownStructure( pbPrinter, PrinterInfo1Offsets, pbPrinter ); *pcReturned = 1; }
else if ( pszName && *pszName ) { DWORD err; LPWSTR pszFullName; LPWSTR pszServer; NWWKSTA_CONTEXT_HANDLE handle; BYTE bTemp = 0; LPBYTE pbTempBuf = pbPrinter ? pbPrinter : &bTemp;
if ( (pszFullName = LocalAlloc( 0, (wcslen( pszName ) + 1) * sizeof(WCHAR) ) ) == NULL ) return ERROR_NOT_ENOUGH_MEMORY;
wcscpy( pszFullName, pszName ); pszServer = wcschr( pszFullName, L'!');
if ( pszServer ) *pszServer++ = 0;
if ( lstrcmpiW( pszFullName, NwProviderName ) ) { LocalFree( pszFullName ); return ERROR_INVALID_NAME; }
if ( !pszServer ) // Enumerate servers
{ LocalFree( pszFullName );
err = NwOpenEnumPrintServers( &handle );
if ( err != NO_ERROR ) { return err; }
err = NwrEnum( handle, (DWORD_PTR) -1, pbTempBuf, cbBuf, pcbNeeded, pcReturned );
if ( err != NO_ERROR ) { NwrCloseEnum( &handle ); return err; }
err = NwrCloseEnum( &handle );
if ( err != NO_ERROR ) { return err; } } else // Enumerate NDS sub-trees or print queues
{ LPWSTR tempStrPtr = pszServer; DWORD dwClassType = 0;
if ( tempStrPtr[0] == L'\\' && tempStrPtr[1] == L'\\' && tempStrPtr[2] == L' ' ) tempStrPtr = &tempStrPtr[1];
err = NwrOpenEnumNdsSubTrees_Print( NULL, tempStrPtr, &dwClassType, &handle );
if ( err == ERROR_NETWORK_ACCESS_DENIED && dwClassType == CLASS_TYPE_NCP_SERVER ) { // An error code from the above NwOpenEnumNdsSubTrees could have
// failed because the object was a server, which cannot be enumerated
// with the NDS tree APIs. If so we try to get the print queues with the
// regular NW APIs.
tempStrPtr = NwGetUncObjectName( tempStrPtr );
err = NwOpenEnumPrintQueues( tempStrPtr, &handle );
if ( err != NO_ERROR ) { LocalFree( pszFullName ); return err; } }
if ( err != NO_ERROR ) { // An error code from the above NwOpenEnumNdsSubTrees could have
// failed because the object was not a part of an NDS tree.
// So we try to get the print queues with the regular NW APIs.
err = NwOpenEnumPrintQueues( tempStrPtr, &handle );
if ( err != NO_ERROR ) { LocalFree( pszFullName ); return err; } }
//
// Get rid of the allocated temp buffer that we've been using
// indirectly through tempStrPtr and pszServer.
//
LocalFree( pszFullName );
err = NwrEnum( handle, 0xFFFFFFFF, pbTempBuf, cbBuf, pcbNeeded, pcReturned );
if ( err != NO_ERROR ) { NwrCloseEnum( &handle ); return err; }
err = NwrCloseEnum( &handle );
if ( err != NO_ERROR ) { return err; } } }
return NO_ERROR; }
DWORD NwrStartDocPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN LPWSTR pszDocument, IN LPWSTR pszUser, IN DWORD PrintOptions, //Multi-User Addition
IN DWORD fGateway ) /*++
Routine Description:
This routine informs the print spooler that a document is to be spooled for printing.
Arguments:
hPrinter - Handle of the printer pszDocument - Name of the document to be printed pszUser - Name of the user submitting the print job fGateway - TRUE if it is gateway printing
Return Value:
--*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
if ( !pSpool || (pSpool->nSignature != NW_SIGNATURE) ) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( pSpool->nStatus != 0 ) { err = ERROR_INVALID_PARAMETER; } else { //
// Get pSpool->nJobNumber from CreateQueueJobAndFile
//
PNWPRINTER pPrinter = pSpool->pPrinter; WORD nJobNumber = 0;
ASSERT( pPrinter ); err = NwCreateQueueJobAndFile( pSpool->hServer, pPrinter->nQueueId, pszDocument, pszUser, fGateway, PrintOptions, //Multi-User addition
pPrinter->pszQueue, &nJobNumber );
if ( !err ) { pSpool->nJobNumber = nJobNumber; pSpool->nStatus = SPOOL_STATUS_STARTDOC; NwSetPrinterChange( pSpool, PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER ); } }
return err; }
DWORD NwrWritePrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN LPBYTE pBuf, IN DWORD cbBuf, OUT LPDWORD pcbWritten ) /*++
Routine Description:
This routine informs the print spooler that the specified data should be written to the given printer.
Arguments:
hPrinter - Handle of the printer object pBuf - Address of array that contains printer data cbBuf - Size, in bytes of pBuf pcbWritten - Receives the number of bytes actually written to the printer
Return Value:
--*/ { DWORD err = NO_ERROR; PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE)) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( pSpool->nStatus != SPOOL_STATUS_STARTDOC ) { err = ERROR_INVALID_PARAMETER; } else { NTSTATUS ntstatus; IO_STATUS_BLOCK IoStatusBlock; PNWPRINTER pPrinter = pSpool->pPrinter;
ASSERT( pPrinter ); ntstatus = NtWriteFile( pSpool->hServer, NULL, NULL, NULL, &IoStatusBlock, pBuf, cbBuf, NULL, NULL );
if ( NT_SUCCESS(ntstatus)) ntstatus = IoStatusBlock.Status;
if ( NT_SUCCESS(ntstatus) ) { *pcbWritten = (DWORD) IoStatusBlock.Information; NwSetPrinterChange( pSpool, PRINTER_CHANGE_WRITE_JOB ); } else { KdPrint(("NWWORKSTATION: NtWriteFile failed 0x%08lx\n", ntstatus)); *pcbWritten = 0; err = RtlNtStatusToDosError( ntstatus ); } }
return err; }
DWORD NwrAbortPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter ) /*++
Routine Description:
This routine deletes a printer's spool file if the printer is configured for spooling.
Arguments:
hPrinter - Handle of the printer object
Return Value:
--*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( pSpool->nStatus != SPOOL_STATUS_STARTDOC ) { err = ERROR_INVALID_PARAMETER; } else { PNWPRINTER pPrinter = pSpool->pPrinter;
ASSERT( pPrinter ); err = NwRemoveJobFromQueue( pSpool->hServer, pPrinter->nQueueId, (WORD) pSpool->nJobNumber );
if ( !err ) { pSpool->nJobNumber = 0; pSpool->nStatus = SPOOL_STATUS_ABORT; NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB ); } }
return err; }
DWORD NwrEndDocPrinter( IN NWWKSTA_PRINTER_CONTEXT hPrinter ) /*++
Routine Description:
This routine ends the print job for the given printer.
Arguments:
hPrinter - Handle of the printer object
Return Value:
--*/ { DWORD err = NO_ERROR; PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { err = ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( ( pSpool->nStatus != SPOOL_STATUS_STARTDOC ) && ( pSpool->nStatus != SPOOL_STATUS_ABORT ) ) { err = ERROR_INVALID_PARAMETER; } else { PNWPRINTER pPrinter = pSpool->pPrinter;
ASSERT( pPrinter );
if ( pSpool->nStatus == SPOOL_STATUS_STARTDOC ) { err = NwCloseFileAndStartQueueJob( pSpool->hServer, pPrinter->nQueueId, (WORD) pSpool->nJobNumber );
if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB ); }
if ( !err ) { pSpool->nJobNumber = 0; pSpool->nStatus = 0; } }
return err; }
DWORD NwrGetJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwJobId, IN DWORD dwLevel, IN OUT LPBYTE pbJob, IN DWORD cbBuf, OUT LPDWORD pcbNeeded ) /*++
Routine Description:
Arguments:
hPrinter - Handle of the printer dwJobId - dwLevel - pbJob - cbBuf - pcbNeeded -
Return Value:
--*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { err = ERROR_INVALID_HANDLE; } // allow NULL for bpJob if cbBuf is 0.
// Relies on NwGetQueueJobInfo to properly handle NULL pointer in request to fill pcbNeeded
else if ( (cbBuf != 0) && ( !pbJob ) ) { err = ERROR_INVALID_PARAMETER; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if (( dwLevel != 1 ) && ( dwLevel != 2 )) { err = ERROR_INVALID_LEVEL; } else { DWORD nPrinterLen; LPWSTR pszPrinter; LPBYTE FixedPortion = pbJob; LPWSTR EndOfVariableData = (LPWSTR) (pbJob + cbBuf); PNWPRINTER pPrinter = pSpool->pPrinter;
ASSERT( pPrinter );
pszPrinter = AllocNwSplMem( LMEM_ZEROINIT, nPrinterLen = ( wcslen( pPrinter->pszServer) + wcslen( pPrinter->pszQueue) + 2) * sizeof(WCHAR));
if ( pszPrinter == NULL ) return ERROR_NOT_ENOUGH_MEMORY;
wcscpy( pszPrinter, pPrinter->pszServer ); wcscat( pszPrinter, L"\\" ); wcscat( pszPrinter, pPrinter->pszQueue );
*pcbNeeded = 0; err = NwGetQueueJobInfo( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId, pszPrinter, dwLevel, &FixedPortion, &EndOfVariableData, pcbNeeded );
FreeNwSplMem( pszPrinter, nPrinterLen );
if ( !err ) { switch( dwLevel ) { case 1: MarshallDownStructure( pbJob, JobInfo1Offsets, pbJob ); break;
case 2: MarshallDownStructure( pbJob, JobInfo2Offsets, pbJob ); break;
default: ASSERT( FALSE ); break; } }
}
return err; }
DWORD NwrEnumJobs( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwFirstJob, IN DWORD dwNoJobs, IN DWORD dwLevel, IN OUT LPBYTE pbJob, IN DWORD cbBuf, OUT LPDWORD pcbNeeded, OUT LPDWORD pcReturned ) /*++
Routine Description:
Arguments:
hPrinter - Handle of the printer dwFirstJob - dwNoJobs - dwLevel - pbJob - cbBuf - pcbNeeded - pcReturned -
Return Value:
--*/ { DWORD err; PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { err = ERROR_INVALID_HANDLE; } // allow NULL for bpJob if cbBuf is 0.
// Relies on NwGetQueueJobInfo to properly handle NULL pointer in request to fill pcbNeeded
else if ( (cbBuf != 0) && ( !pbJob ) ) { err = ERROR_INVALID_PARAMETER; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) ) { err = ERROR_INVALID_LEVEL; } else { PNWPRINTER pPrinter = pSpool->pPrinter; LPWSTR pszPrinter; DWORD nPrinterLen;
ASSERT( pPrinter ); pszPrinter = AllocNwSplMem( LMEM_ZEROINIT, nPrinterLen = ( wcslen( pPrinter->pszServer ) + wcslen( pPrinter->pszQueue) + 2) * sizeof(WCHAR));
if ( pszPrinter == NULL ) return ERROR_NOT_ENOUGH_MEMORY;
wcscpy( pszPrinter, pPrinter->pszServer ); wcscat( pszPrinter, L"\\" ); wcscat( pszPrinter, pPrinter->pszQueue );
err = NwGetQueueJobs( pSpool->hServer, pPrinter->nQueueId, pszPrinter, dwFirstJob, dwNoJobs, dwLevel, pbJob, cbBuf, pcbNeeded, pcReturned );
FreeNwSplMem( pszPrinter, nPrinterLen );
if ( !err ) { DWORD_PTR *pOffsets; DWORD cbStruct; DWORD cReturned = *pcReturned; LPBYTE pbBuffer = pbJob;
switch( dwLevel ) { case 1: pOffsets = JobInfo1Offsets; cbStruct = sizeof( JOB_INFO_1W ); break;
case 2: pOffsets = JobInfo2Offsets; cbStruct = sizeof( JOB_INFO_2W ); break;
default: ASSERT( FALSE ); break; }
while ( cReturned-- ) { MarshallDownStructure( pbBuffer, pOffsets, pbJob ); pbBuffer += cbStruct; } } }
return err; }
DWORD NwrSetJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwJobId, IN DWORD dwLevel, IN PNW_JOB_INFO pNwJobInfo, IN DWORD dwCommand ) /*++
Routine Description:
Arguments:
hPrinter - Handle of the printer dwJobId - dwLevel - pNwJobInfo- dwCommand -
Return Value:
--*/ { DWORD err = NO_ERROR; PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter;
if ( !pSpool || pSpool->nSignature != NW_SIGNATURE ) { err = ERROR_INVALID_HANDLE; } else if ( ( dwLevel != 0 ) && ( !pNwJobInfo ) ) { err = ERROR_INVALID_PARAMETER; } else if ( pSpool->errOpenPrinter ) { err = pSpool->errOpenPrinter; } else if ( ( dwLevel != 0 ) && ( dwLevel != 1 ) && ( dwLevel != 2 ) ) { err = ERROR_INVALID_LEVEL; }
if ( err ) return err;
pPrinter = pSpool->pPrinter; ASSERT( pPrinter );
if ( ( dwCommand == JOB_CONTROL_CANCEL ) || ( dwCommand == JOB_CONTROL_DELETE ) ) { err = NwRemoveJobFromQueue( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId );
if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_DELETE_JOB | PRINTER_CHANGE_SET_PRINTER );
// Since the job is removed, we don't need to change other
// information about it.
} else { if ( dwLevel != 0 ) { if ( pNwJobInfo->nPosition != JOB_POSITION_UNSPECIFIED ) { err = NwChangeQueueJobPosition( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId, (BYTE) pNwJobInfo->nPosition ); } }
if ( ( !err ) && ( dwCommand == JOB_CONTROL_RESTART )) { err = ERROR_NOT_SUPPORTED; } else if ( !err ) { err = NwChangeQueueJobEntry( pSpool->hServer, pPrinter->nQueueId, (WORD) dwJobId, dwCommand, pNwJobInfo ); }
if ( !err ) NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB ); }
return err; }
DWORD NwrAddJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, OUT LPADDJOB_INFO_1W pAddInfo1, IN DWORD cbBuf, OUT LPDWORD pcbNeeded ) /*++
Routine Description:
Arguments:
hPrinter - Handle of the printer. pAddInfo1 - Output buffer to hold ADDJOB_INFO_1W structure. cbBuf - Output buffer size in bytes. pcbNeeded - Required output buffer size in bytes.
Return Value:
--*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter;
if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { return ERROR_INVALID_HANDLE; }
if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; }
if ( pSpool->nStatus != 0 ) { return ERROR_INVALID_PARAMETER; }
pPrinter = pSpool->pPrinter; ASSERT( pPrinter );
*pcbNeeded = sizeof(ADDJOB_INFO_1W) + (wcslen(pPrinter->pszServer) + wcslen(pPrinter->pszQueue) + 2) * sizeof(WCHAR);
if (cbBuf < *pcbNeeded) { return ERROR_INSUFFICIENT_BUFFER; }
//
// Write UNC path name into the output buffer.
//
// dfergus 19 Apr 2001 - 348006
// DWORD cast
pAddInfo1->Path = (LPWSTR) ((DWORD) pAddInfo1 + sizeof(ADDJOB_INFO_1W)); //
wcscpy(pAddInfo1->Path, pPrinter->pszServer); wcscat(pAddInfo1->Path, L"\\" ); wcscat(pAddInfo1->Path, pPrinter->pszQueue);
//
// Return special job id value which the client (winspool.drv) looks
// for and does an FSCTL call to our redirector to get the real
// job id. We cannot return a real job id at this point because
// the CreateQueueJobAndFile NCP is not issue until the client opens
// the UNC name we return in this API.
//
pAddInfo1->JobId = (DWORD) -1;
//
// Save context information
//
pSpool->nJobNumber = pAddInfo1->JobId; pSpool->nStatus = SPOOL_STATUS_ADDJOB;
#if DBG
IF_DEBUG(PRINT) { KdPrint(("NWWORKSTATION: NwrAddJob Path=%ws, JobId=%lu, BytesNeeded=%lu\n", pAddInfo1->Path, pAddInfo1->JobId, *pcbNeeded)); } #endif
NwSetPrinterChange( pSpool, PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER );
return NO_ERROR; }
DWORD NwrScheduleJob( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN DWORD dwJobId ) /*++
Routine Description:
Arguments:
hPrinter - Handle of the printer dwJobId - Job identification number
Return Value:
--*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; PNWPRINTER pPrinter;
if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { return ERROR_INVALID_HANDLE; }
if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; }
if (pSpool->nStatus != SPOOL_STATUS_ADDJOB) { return ERROR_INVALID_PARAMETER; }
pPrinter = pSpool->pPrinter; ASSERT( pPrinter );
pSpool->nJobNumber = 0; pSpool->nStatus = 0;
NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB );
return NO_ERROR; }
DWORD NwrWaitForPrinterChange( IN NWWKSTA_PRINTER_CONTEXT hPrinter, IN OUT LPDWORD pdwFlags ) /*++
Routine Description:
Arguments:
hPrinter - Handle of the printer pdwFlags -
Return Value:
--*/ { PNWSPOOL pSpool = (PNWSPOOL) hPrinter; HANDLE hChangeEvent = NULL; DWORD nRetVal; HANDLE ahWaitEvents[2]; DWORD err = NO_ERROR;
if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) { return ERROR_INVALID_HANDLE; } else if ( pSpool->errOpenPrinter ) { return pSpool->errOpenPrinter; } else if ( pSpool->hChangeEvent ) { return ERROR_ALREADY_WAITING; } else if ( !(*pdwFlags & PRINTER_CHANGE_VALID )) { return ERROR_INVALID_PARAMETER; }
if ( pSpool->nChangeFlags & *pdwFlags ) { //
// There is a change since we last called
//
*pdwFlags &= pSpool->nChangeFlags;
EnterCriticalSection( &NwPrintCritSec ); pSpool->nChangeFlags = 0; LeaveCriticalSection( &NwPrintCritSec );
return NO_ERROR; }
hChangeEvent = CreateEvent( NULL, FALSE, // automatic reset
FALSE, // initial state not signalled
NULL );
if ( !hChangeEvent ) { KdPrint(("WaitForPrinterChange: CreateEvent failed with error %d\n", GetLastError() )); return GetLastError(); }
pSpool->nWaitFlags = *pdwFlags;
EnterCriticalSection( &NwPrintCritSec ); pSpool->hChangeEvent = hChangeEvent; pSpool->nChangeFlags = 0; LeaveCriticalSection( &NwPrintCritSec );
ahWaitEvents[0] = pSpool->hChangeEvent; ahWaitEvents[1] = NwDoneEvent;
nRetVal = WaitForMultipleObjects( 2, // Two events to wait for
ahWaitEvents, FALSE, // Wait for one to signal
NwTimeOutValue );
switch ( nRetVal ) { case WAIT_FAILED: err = GetLastError(); break;
case WAIT_TIMEOUT: case WAIT_OBJECT_0 + 1: // treats service stopping as timeout
*pdwFlags |= PRINTER_CHANGE_TIMEOUT; break;
case WAIT_OBJECT_0: *pdwFlags &= pSpool->nChangeFlags; break;
default: KdPrint(("WaitForPrinterChange: WaitForMultipleObjects returned with %d\n", nRetVal )); *pdwFlags |= PRINTER_CHANGE_TIMEOUT; break; }
if ( ( !err ) && ( nRetVal != WAIT_OBJECT_0 + 1 ) ) { pSpool->nWaitFlags = 0;
EnterCriticalSection( &NwPrintCritSec ); pSpool->nChangeFlags = 0; pSpool->hChangeEvent = NULL; LeaveCriticalSection( &NwPrintCritSec ); }
if ( !CloseHandle( hChangeEvent ) ) { KdPrint(("WaitForPrinterChange: CloseHandle failed with error %d\n", GetLastError())); }
return err; }
VOID NwSetPrinterChange( PNWSPOOL pSpool, DWORD nFlags ) { PNWPRINTER pPrinter = pSpool->pPrinter; PNWSPOOL pCurSpool = pSpool;
EnterCriticalSection( &NwPrintCritSec );
do {
if ( pCurSpool->nWaitFlags & nFlags ) { pCurSpool->nChangeFlags |= nFlags;
if ( pCurSpool->hChangeEvent ) { SetEvent( pCurSpool->hChangeEvent ); pCurSpool->hChangeEvent = NULL; } }
pCurSpool = pCurSpool->pNextSpool; if ( pCurSpool == NULL ) pCurSpool = pPrinter->pSpoolList;
} while ( pCurSpool && (pCurSpool != pSpool) );
LeaveCriticalSection( &NwPrintCritSec ); }
PNWPRINTER NwFindPrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue ) { PNWPRINTER pPrinter = NULL;
//
// Check to see if we already have the given printer in our printer
// link list. If yes, return the printer.
//
for ( pPrinter = NwPrinterList; pPrinter; pPrinter = pPrinter->pNextPrinter) { if ( ( lstrcmpiW( pPrinter->pszServer, pszServer ) == 0 ) && ( lstrcmpiW( pPrinter->pszQueue, pszQueue ) == 0 ) ) { return pPrinter; } }
return NULL; }
DWORD NwCreatePrinterEntry( IN LPWSTR pszServer, IN LPWSTR pszQueue, OUT PNWPRINTER *ppPrinter, OUT PHANDLE phServer ) { DWORD err = NO_ERROR; DWORD nQueueId = 0; HANDLE TreeHandle = NULL; UNICODE_STRING TreeName; PNWPRINTER pNwPrinter = NULL; BOOL fCreatedNWConnection = FALSE;
LPWSTR lpRemoteName = NULL; DWORD dwBufSize = ( wcslen(pszServer) + wcslen(pszQueue) + 2 ) * sizeof(WCHAR);
lpRemoteName = (LPWSTR) AllocNwSplMem( LMEM_ZEROINIT, dwBufSize );
if ( lpRemoteName == NULL ) return ERROR_NOT_ENOUGH_MEMORY;
wcscpy( lpRemoteName, pszServer ); wcscat( lpRemoteName, L"\\" ); wcscat( lpRemoteName, pszQueue );
*ppPrinter = NULL; *phServer = NULL;
//
// See if we already know about this print queue.
//
pNwPrinter = NwFindPrinterEntry( pszServer, pszQueue );
/* Changing to get queue status to verify access instead
if ( pNwPrinter == NULL ) { // We don't know about this NetWare print queue. We need to see if
// we are authorized to use this queue. If so, then go ahead
// and continue to open printer. Otherwise, fail with not
// authorized error code.
err = NwCreateConnection( NULL, lpRemoteName, RESOURCETYPE_PRINT, NULL, NULL );
if ( err != NO_ERROR ) { if ( ( err == ERROR_INVALID_PASSWORD ) || ( err == ERROR_ACCESS_DENIED ) || ( err == ERROR_NO_SUCH_USER ) ) { err = ERROR_ACCESS_DENIED; }
FreeNwSplMem( lpRemoteName, dwBufSize );
return err; }
fCreatedNWConnection = TRUE; } */
//
// See if pszServer is really a NDS tree name, if so call
// NwNdsGetQueueInformation to get the QueueId and possible referred
// server for which we open handle.
//
RtlInitUnicodeString( &TreeName, pszServer + 2 );
err = NwNdsOpenTreeHandle( &TreeName, &TreeHandle );
if ( err == NO_ERROR ) { NTSTATUS ntstatus; WCHAR szRefServer[NDS_MAX_NAME_CHARS]; UNICODE_STRING ObjectName; UNICODE_STRING QueuePath;
ObjectName.Buffer = szRefServer; ObjectName.MaximumLength = NDS_MAX_NAME_CHARS; ObjectName.Length = 0;
RtlInitUnicodeString( &QueuePath, pszQueue );
ntstatus = NwNdsGetQueueInformation( TreeHandle, &QueuePath, &ObjectName, &nQueueId );
if ( TreeHandle ) { CloseHandle( TreeHandle ); TreeHandle = NULL; }
if ( ntstatus ) { err = RtlNtStatusToDosError( ntstatus ); goto ErrorExit; }
//
// If we got a referred server, it's name would look like:
// "CN=SERVER.OU=DEV.O=MICROSOFT" . . . Convert it to "C\\SERVER"
//
if ( ObjectName.Length > 0 ) { WORD i; LPWSTR EndOfServerName = NULL;
//
// First convert the referred server name to
// "C\\SERVER.OU=DEV.O=MICROSOFT"
//
szRefServer[1] = L'\\'; szRefServer[2] = L'\\';
//
// Put a NULL terminator at the first '.'
//
EndOfServerName = wcschr( szRefServer + 3, L'.' ); if (EndOfServerName) *EndOfServerName = L'\0';
//
// pszServer now equals the referred server "C\\SERVER"
//
//
// Get the handle of the referred server skipping the 'C' character.
//
err = NwAttachToNetwareServer( szRefServer + 1, phServer); } } else // Not an NDS tree, so get handle of server.
{
err = NwAttachToNetwareServer( pszServer, phServer);
if ( err == NO_ERROR ) { if ( err = NwGetQueueId( *phServer, pszQueue, &nQueueId)) err = ERROR_INVALID_NAME; } }
if ( ( err == ERROR_INVALID_PASSWORD ) || ( err == ERROR_ACCESS_DENIED ) || ( err == ERROR_NO_SUCH_USER ) ) { err = ERROR_ACCESS_DENIED; goto ErrorExit; } else if ( err != NO_ERROR ) { err = ERROR_INVALID_NAME; goto ErrorExit; }
//
// Test to see if there already was a entry for this print queue. If so,
// we can now return with NO_ERROR since pNwPrinter and phServer are
// now set.
//
if ( pNwPrinter ) { if ( lpRemoteName ) { FreeNwSplMem( lpRemoteName, dwBufSize ); }
*ppPrinter = pNwPrinter;
return NO_ERROR; }
//
// The printer entry was not found in our list of printers in the
// call to NwFindPrinterEntry. So, we must create one.
//
// First, verify access rights
else { BYTE nQueueStatus; BYTE nJobCount;
err = NwReadQueueCurrentStatus(*phServer, nQueueId, &nQueueStatus, &nJobCount);
if ( ( err == ERROR_INVALID_PASSWORD ) || ( err == ERROR_ACCESS_DENIED ) || ( err == ERROR_NO_SUCH_USER ) ) { err = ERROR_ACCESS_DENIED; goto ErrorExit; } else if ( err != NO_ERROR ) { err = ERROR_INVALID_NAME; goto ErrorExit; }
}
if ( *ppPrinter = AllocNwSplMem( LMEM_ZEROINIT, sizeof(NWPRINTER) )) { if ( !( (*ppPrinter)->pszServer = AllocNwSplStr( pszServer )) ) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } else if ( !( (*ppPrinter)->pszQueue = AllocNwSplStr( pszQueue ))) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; }
if ( fCreatedNWConnection ) { if ( !( (*ppPrinter)->pszUncConnection = AllocNwSplStr( lpRemoteName )) ) { err = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; }
FreeNwSplMem( lpRemoteName, dwBufSize ); lpRemoteName = NULL; } else { (*ppPrinter)->pszUncConnection = NULL; }
#if DBG
IF_DEBUG(PRINT) { KdPrint(("*************CREATED PRINTER ENTRY: %ws\\%ws\n\n", (*ppPrinter)->pszServer, (*ppPrinter)->pszQueue )); } #endif
(*ppPrinter)->nQueueId = nQueueId; (*ppPrinter)->pSpoolList = NULL; (*ppPrinter)->pNextPrinter = NwPrinterList; NwPrinterList = *ppPrinter;
err = NO_ERROR; } else { err = ERROR_NOT_ENOUGH_MEMORY; }
if ( err == NO_ERROR ) return err;
ErrorExit:
if ( *phServer ) { (VOID) NtClose( *phServer ); *phServer = NULL; }
if ( *ppPrinter ) { if ( (*ppPrinter)->pszServer ) { FreeNwSplStr( (*ppPrinter)->pszServer ); }
if ( (*ppPrinter)->pszQueue ) { FreeNwSplStr( (*ppPrinter)->pszQueue ); }
if ( (*ppPrinter)->pszUncConnection ) { (void) NwrDeleteConnection( NULL, (*ppPrinter)->pszUncConnection, FALSE ); FreeNwSplStr( (*ppPrinter)->pszUncConnection ); }
FreeNwSplMem( *ppPrinter, sizeof( NWPRINTER)); *ppPrinter = NULL; }
if ( lpRemoteName ) { FreeNwSplMem( lpRemoteName, dwBufSize ); }
return err; }
VOID NwRemovePrinterEntry( IN PNWPRINTER pPrinter ) { PNWPRINTER pCur, pPrev = NULL;
ASSERT( pPrinter->pSpoolList == NULL ); pPrinter->pSpoolList = NULL;
for ( pCur = NwPrinterList; pCur; pPrev = pCur, pCur = pCur->pNextPrinter ) { if ( pCur == pPrinter ) { if ( pPrev ) pPrev->pNextPrinter = pCur->pNextPrinter; else NwPrinterList = pCur->pNextPrinter; break; } }
ASSERT( pCur );
pPrinter->pNextPrinter = NULL; FreeNwSplStr( pPrinter->pszServer ); FreeNwSplStr( pPrinter->pszQueue ); if ( pPrinter->pszUncConnection ) { (void) NwrDeleteConnection( NULL, pPrinter->pszUncConnection, FALSE ); FreeNwSplStr( pPrinter->pszUncConnection ); } FreeNwSplMem( pPrinter, sizeof( NWPRINTER)); }
VOID NWWKSTA_PRINTER_CONTEXT_rundown( IN NWWKSTA_PRINTER_CONTEXT hPrinter ) /*++
Routine Description:
This function is called by RPC when a client terminates with an opened handle. This allows us to clean up and deallocate any context data associated with the handle.
Arguments:
hPrinter - Supplies the opened handle
Return Value:
None.
--*/ { (void) NwrClosePrinter(&hPrinter); }
LPWSTR NwGetUncObjectName( IN LPWSTR ContainerName ) { WORD length = 2; WORD totalLength = (WORD) wcslen( ContainerName );
if ( totalLength < 2 ) return 0;
while ( length < totalLength ) { if ( ContainerName[length] == L'.' ) ContainerName[length] = L'\0';
length++; }
length = 2;
while ( length < totalLength && ContainerName[length] != L'\\' ) { length++; }
ContainerName[length + 2] = L'\\'; ContainerName[length + 3] = L'\\';
return (ContainerName + length + 2); }
|