/*++
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
);
DWORD
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;
// Use the minimum timeout value if the
// value set in the registry is too small.
if ( NwTimeOutValue >= 0
&& 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));
}
}
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;
UNREFERENCED_PARAMETER( Reserved );
if ( (pszServer = AllocNwSplStr( pszPrinterName )) == NULL )
return ERROR_NOT_ENOUGH_MEMORY;
CharUpperW( pszServer ); // convert in place
//
// ValidatePrinterName
//
if ( ( !ValidateUNCName( pszPrinterName ) )
|| ( (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 *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;
}
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 ( !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;
WCHAR szFullName[MAX_PATH];
LPWSTR pszServer;
NWWKSTA_CONTEXT_HANDLE handle;
wcscpy( szFullName, pszName );
pszServer = wcschr( szFullName, L'!');
if ( pszServer )
*pszServer++ = 0;
if ( lstrcmpiW( szFullName, NwProviderName ) )
return ERROR_INVALID_NAME;
if ( !pszServer ) // Enumerate servers
{
err = NwOpenEnumPrintServers( &handle );
if ( err != NO_ERROR )
{
return err;
}
err = NwrEnum( handle,
0xFFFFFFFF,
pbPrinter,
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.
// BUGBUG - Temporary hack to browse past CN=<server> in an NDS tree.
tempStrPtr = (LPWSTR) NwGetUncObjectName( tempStrPtr );
err = NwOpenEnumPrintQueues( tempStrPtr, &handle );
if ( err != NO_ERROR )
{
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 )
{
return err;
}
}
err = NwrEnum( handle,
0xFFFFFFFF,
pbPrinter,
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 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;
ASSERT( pPrinter );
err = NwCreateQueueJobAndFile( pSpool->hServer,
pPrinter->nQueueId,
pszDocument,
pszUser,
fGateway,
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 = 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;
}
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) ((DWORD) 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;
}
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 *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 ( 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 )
{
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.
//
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 )) // BUGBUG: do we need this?
{
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 );
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 ) )
{
// BUGBUG err = ERROR_INVALID_PASSWORD;
err = ERROR_ACCESS_DENIED;
}
FreeNwSplMem( lpRemoteName, dwBufSize );
if ( TreeHandle )
CloseHandle( TreeHandle );
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 ) )
{
// BUGBUG err = ERROR_INVALID_PASSWORD;
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.
//
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);
}
DWORD
NwGetUncObjectName(
IN LPWSTR ContainerName
)
{
WORD length = 2;
WORD totalLength = 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 (DWORD) (ContainerName + length + 2);
}