Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2626 lines
68 KiB

/*++
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>
#include <Accctrl.h>
#include <Aclapi.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
)
/*++
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
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,
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;
}
typedef DWORD (*PRINTPROC)(HWND, HINSTANCE, LPCTSTR, UINT);
DWORD
NwrAddPrinterConnection(
IN LPWSTR Reserved,
IN LPWSTR pszPrinterName,
IN LPWSTR pszDriverName
)
/*++
Routine Description: Implements workaround to allow CSNW to support Point and Print
If PnP is enabled in registry, NwrAddPrinterConnection is called with driver
name read from NDS Queue object Description attribute in format:
"PnP Driver:Driver Friendly Name[@\\server\share\driver.inf]".
If driver inf file is specified it must be a Logo'd PnP driver, and must not
invoke UI. If not specified, driver name must be from %windir%\inf\ntprint.inf.
Thie routine purposely runs in the system context, not impersonating the user
in order to load the printer driver. To maintain administrative control
the feature requires a registry setting to enable PnP configuration, and
another key can be set to restrict PnP configuration to using only drivers
listed in ntprint.inf.
\\HKLM\System\CurrentControlSet\Control\Print\Providers\NetWare or Conpatible Network
DWORD Value: EnableUserAddPrinter = 1 enables PnP loading of printer drivers
( Default: 0 )
DWORD Value: RestrictToInboxDrivers = 1 forces loading from ntprint.inf only
( Default: 0 )
Arguments:
Reserved - RPC Handle not used
pszPrinterName - NDS Queue object
pszDriverName - Friendly name of printer driver, with optional driver location
Return Value:
--*/
{
DWORD err = 0;
PRINTER_INFO_2W * pPrinterInfo = NULL;
HANDLE hPrinter = NULL;
PSECURITY_DESCRIPTOR pSecDesc = NULL;
PACL pDacl = NULL;
LPTSTR pszProcessor = L"winprint";
NWWKSTA_PRINTER_CONTEXT phPrinter = NULL;
LPBYTE pDest = NULL;
DWORD dwBytesNeeded = 0;
BOOL bRet = FALSE;
LPWSTR pszInfPath = NULL;
PSID pEveryoneSID = NULL;
EXPLICIT_ACCESS ea[1];
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
WCHAR szPrintInf[MAX_PATH];
if (!pszPrinterName || (*pszPrinterName == 0))
return ERROR_INVALID_PARAMETER;
if (!pszDriverName || (*pszDriverName == 0))
return ERROR_INVALID_PARAMETER;
pszInfPath = wcschr(pszDriverName, L'@');
if (pszInfPath)
{
*pszInfPath = 0;
pszInfPath++;
}
else
{
szPrintInf[0] = 0;
err = GetSystemWindowsDirectory(szPrintInf, MAX_PATH);
lstrcat(szPrintInf, L"\\inf\\ntprint.inf");
pszInfPath = szPrintInf;
}
#if DBG
IF_DEBUG(PRINT)
{
KdPrint(("NWWKS:AddPrinterConnection - Printer=%ws, driver=%ws from %ws\n", pszPrinterName, pszDriverName, szPrintInf));
}
#endif
// Create a well-known SID for the Everyone group.
if(! AllocateAndInitializeSid( &SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pEveryoneSID) )
{
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = PRINTER_ALL_ACCESS;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance= OBJECT_INHERIT_ACE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;
// Create a new ACL that contains the new ACE.
err = SetEntriesInAcl(1, ea, NULL, &pDacl);
if (ERROR_SUCCESS != err) {
goto Cleanup;
}
// Initialize a security descriptor.
pSecDesc = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pSecDesc == NULL) {
goto Cleanup;
}
if (!InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)) {
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSecDesc,
TRUE, // fDaclPresent flag
pDacl,
FALSE)) // not a default DACL
{
goto Cleanup;
}
dwBytesNeeded = sizeof(PRINTER_INFO_2);
pPrinterInfo = (PRINTER_INFO_2 *) LocalAlloc(LPTR, dwBytesNeeded);
if (pPrinterInfo)
{
pPrinterInfo->pPrinterName = pszPrinterName;
pPrinterInfo->pPortName = pPrinterInfo->pPrinterName;
pPrinterInfo->pDriverName = pszDriverName;
pPrinterInfo->pPrintProcessor = pszProcessor;
pPrinterInfo->Attributes = PRINTER_ATTRIBUTE_NETWORK | PRINTER_ATTRIBUTE_LOCAL;
pPrinterInfo->pServerName = NULL;
pPrinterInfo->pShareName = NULL;
pPrinterInfo->Status = 0;
pPrinterInfo->cJobs = 0;
if (IsValidSecurityDescriptor(pSecDesc))
{
pPrinterInfo->pSecurityDescriptor = pSecDesc;
}
hPrinter = AddPrinter(NULL, 2, (LPBYTE) pPrinterInfo);
if (hPrinter == NULL)
{
if ( GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER )
{
WCHAR szCmdLine[1024];
_snwprintf(szCmdLine, sizeof(szCmdLine) / sizeof(WCHAR),
L"/ia /q /m \"%.200ws\" /h \"Intel\" /v \"Windows 2000 or XP\" /f \"%.768ws\"",
pPrinterInfo->pDriverName, pszInfPath);
#if DBG
IF_DEBUG(PRINT)
{
KdPrint(("AddPrinterConnection - attempting to load driver from cab with command\n %ws\n", szCmdLine));
}
#endif
{
HINSTANCE hPrintDll = LoadLibrary(L"printui.dll");
PRINTPROC pPrintFunc = NULL;
if (hPrintDll)
{
pPrintFunc = (PRINTPROC) GetProcAddress((HMODULE)hPrintDll, "PrintUIEntryW");
if (pPrintFunc)
{
HINSTANCE hInst = (HINSTANCE) GetModuleHandle(NULL);
HWND hParentWnd = GetDesktopWindow();
DWORD nRet = 0;
nRet = (pPrintFunc) (hParentWnd, hInst, szCmdLine, TRUE);
hPrinter = AddPrinter(NULL, 2, (LPBYTE) pPrinterInfo);
}
FreeLibrary(hPrintDll);
}
}
}
}
#if DBG
IF_DEBUG(PRINT)
{
KdPrint(("AddPrinter returned 0x%8.8X\n", hPrinter ));
}
#endif
}
Cleanup:
if (pEveryoneSID)
FreeSid(pEveryoneSID);
if (pDacl)
LocalFree(pDacl);
if (pSecDesc)
LocalFree(pSecDesc);
if (hPrinter)
ClosePrinter(hPrinter);
if (phPrinter)
ClosePrinter(phPrinter);
if (pPrinterInfo)
LocalFree(pPrinterInfo);
return (hPrinter) ? ERROR_SUCCESS : ERROR_ACCESS_DENIED;
}
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 to queue 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 != NO_ERROR)
{
err = NwCreateConnection( NULL,
lpRemoteName,
RESOURCETYPE_PRINT,
NULL,
NULL );
}
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);
}