summaryrefslogtreecommitdiffstats
path: root/private/nw/svcdlls/nwwks/server/spool.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/svcdlls/nwwks/server/spool.c')
-rw-r--r--private/nw/svcdlls/nwwks/server/spool.c2289
1 files changed, 2289 insertions, 0 deletions
diff --git a/private/nw/svcdlls/nwwks/server/spool.c b/private/nw/svcdlls/nwwks/server/spool.c
new file mode 100644
index 000000000..ab3ac4069
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/spool.c
@@ -0,0 +1,2289 @@
+/*++
+
+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);
+}
+