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
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);
|
|
}
|
|
|