windows-nt/Source/XPSP1/NT/ds/netapi/dosprint/dosprtw.c

2197 lines
63 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991-1993 Microsoft Corporation
Module Name:
DosPrtW.c
Abstract:
This module provides the UNICODE mapping layer from the old DosPrint APIs
to the new all singing all dancing beautiful Print APIs. (The ANSI
mapping layer is in DosPrint.c)
Author:
Dave Snipp (DaveSn) 26-Apr-1991
Revision History:
09-Jul-1992 JohnRo
Created this file (from DaveSn's DosPrint.c) for RAID 10324: net print
vs. UNICODE.
05-Oct-1992 JohnRo
RAID 3556: DosPrintQGetInfo(from downlevel) level 3, rc=124. (4&5 too.)
RAID 3580: lmsvcs.exe: access violation from OS/2 DosPrintJobGetInfo.
RAID 8333: view printer queues hangs DOS LM enh client.
Make sure data type in job level 1 is null terminated.
Fixed job submitted times.
Fixed error code if GlobalAlloc fails.
Fixed memory leak in DosPrintQGetInfoW.
Fixed DosPrintQEnumW level 5 array bug.
Fixed DosPrintJobEnumW levels 2 and 3.
25-Nov-1992 JohnRo
RAID 1661: downlevel to NT DosPrintDestEnum not supported.
Added code to track down empty queue name.
Quiet normal debug output.
Avoid const vs. volatile compiler warnings.
Avoid other new compiler warnings.
08-Feb-1993 JohnRo
RAID 10164: Data misalignment error during XsDosPrintQGetInfo().
22-Mar-1993 JohnRo
RAID 2974: NET PRINT says NT printer is held when it isn't.
11-May-1993 JohnRo
RAID 9942: fix queue name in job info level 3.
14-May-1993 JohnRo
RAID 9961: DosPrintDestEnum returns NO_ERROR to downlevel but
pcReturned=0; should return NERR_DestNotFound.
Fixed data type returned from PrjInfoFixedSizeW().
18-May-1993 JohnRo
DosPrintQGetInfoW underestimates number of bytes needed.
Use NetpKdPrint() where possible.
Made changes suggested by PC-LINT.
04-Jun-1993 JohnRo
RAID 10222: DosPrintQEnumW returns ERROR_INVALID_USER_BUFFER
when queue is empty.
Made changes suggested by PC-LINT 5.0
08-Jul-1993 JohnRo
RAID 15509: GetJob() API sometimes returned TRUE, even on error case.
Also added some >64KB checks.
Added some assert checks...
13-Jul-1993 JohnRo
Intermittent empty print queue (was buggy after some MyEnumJobs calls).
29-Mar-1995 AlbertT
Support for pause/resume/purge printer queue added.
SetJobInfo 1 comment field (translated into document name) support
added so that chicago clients can set the doc name.
--*/
#ifndef UNICODE
#error "RxPrint APIs assume RxRemoteApi uses wide characters."
#endif
#define NOMINMAX
#define NOSERVICE // Avoid <winsvc.h> vs. <lmsvc.h> conflicts.
#include <windows.h>
//#include <lm.h>
#include <netdebug.h>
#include <string.h>
#include <align.h>
#ifdef _WINSPOOL_
#error "Include of winspool.h moved, make sure it doesn't get UNICODE."
#endif
#undef UNICODE
#undef TEXT
#define TEXT(quote) quote
#include <winspool.h>
#undef TEXT
#define TEXT(quote) __TEXT(quote)
#define UNICODE
#ifndef _WINSPOOL_
#error "Oops, winspool.h changed, make sure this code is still OK."
#endif
#include <dosprint.h>
#include <dosprtp.h> // CommandALocalJob(), etc.
#include <lmapibuf.h> // NetApiBufferFree(), etc.
#include <lmerr.h> // NO_ERROR, NERR_, and ERROR_ equates.
#include <lmshare.h> // LPSHARE_INFO_1, STYPE_ equates, etc.
#include <prefix.h> // PREFIX_ equates.
#include <stddef.h> // offsetof().
#include <timelib.h> // NetpSystemTimeToGmtTime().
#include <tstring.h> // WCSSIZE(), NetpNCopy{type}To{type}.
#include <wchar.h> // wsclen(), wcscpy(), etc.
#include "myspool.h"
#define STR_CONV_SIZE(psz) ( (strlen(psz)+1) * sizeof(WCHAR) )
// NULL_STR_CONV_SIZE: Compute size needed for converted string, which
// is possibly a null pointer but downlevel really wants ptr to null char.
#define NULL_STR_CONV_SIZE(psz) ( (psz) ? STR_CONV_SIZE(psz) : sizeof(WCHAR) )
#define ARRAY_END ((DWORD) -1)
#define MAX_WORD ( (WORD) (~0) )
#define MY_PROTOCOL_LIMIT_ERROR ERROR_NOT_ENOUGH_MEMORY
#define WIN95_DRIVER_SHARE "\\print$\\WIN40\\0"
VOID
NetpSetJobCountForQueue(
IN DWORD QueueLevel,
IN OUT LPVOID Queue,
IN BOOL HasUnicodeStrings,
IN DWORD JobCount
);
DBGSTATIC LPWSTR
PackAnsiStringsToW(
LPSTR *pSource,
LPBYTE pDest,
CONST DWORD *DestOffsets,
LPWSTR pEnd
)
{
// Make sure our end pointer is WCHAR aligned or we'll fault later
ROUND_DOWN_POINTER( pEnd, ALIGN_WCHAR );
while (*DestOffsets != ARRAY_END) {
if (*pSource) {
pEnd-=(strlen(*pSource) + 1);
// Copy the string and convert chars while we're at it.
NetpCopyStrToWStr(pEnd, *pSource);
*(LPWSTR *)(pDest+*DestOffsets) = pEnd;
} else {
--pEnd; // need 1 char for this.
*pEnd = L'\0';
*(LPWSTR *)(pDest+*DestOffsets) = pEnd;
}
pSource++;
DestOffsets++;
}
return pEnd;
}
DBGSTATIC DWORD
PrjInfoFixedSizeW(
IN DWORD Level // assumed valid
)
{
switch (Level) {
case 0:
return sizeof(WORD); // job number.
case 1:
return (sizeof(PRJINFOW));
case 2:
return (sizeof(PRJINFO2W));
case 3:
return (sizeof(PRJINFO3W));
default:
NetpAssert( FALSE );
return (0);
}
/*NOTREACHED*/
}
DBGSTATIC DWORD
GetPrjInfoSizeW(
IN DWORD Level,
IN LPJOB_INFO_2 pJob,
IN LPCWSTR QueueNameW
)
{
NetpAssert( pJob != NULL );
switch (Level) {
case 0:
return sizeof(WORD); // job number.
case 1:
return sizeof(PRJINFOW) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pParameters) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pStatus) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ); // fake pszComment
case 2:
return sizeof(PRJINFO2W) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pUserName) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ) + // fake pszComment
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) );
case 3:
NetpAssert( QueueNameW != NULL );
return sizeof(PRJINFO3W) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pUserName) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ) + // fake pszComment
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDocument) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pNotifyName) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDatatype) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pParameters) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pStatus) ) +
WCSSIZE( QueueNameW ) + // pszQueue
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pPrintProcessor) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pParameters) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pDriverName) ) +
NULL_STR_CONV_SIZE( (LPSTR) (pJob->pPrinterName) );
default:
NetpKdPrint(( PREFIX_DOSPRINT
"GetPrjInfoSizeW: invalid level!\n" ));
return 0;
}
/*NOTREACHED*/
}
// Print job info string table (for level 1).
DBGSTATIC CONST DWORD PrjInfo1StringsW[]={
offsetof(PRJINFOW, pszParms),
offsetof(PRJINFOW, pszStatus),
offsetof(PRJINFOW, pszComment),
ARRAY_END};
// Print job info string table (for level 2).
DBGSTATIC CONST DWORD PrjInfo2StringsW[]={
offsetof(PRJINFO2W, pszUserName),
offsetof(PRJINFO2W, pszComment),
offsetof(PRJINFO2W, pszDocument),
(DWORD) -1};
// Print job info string table (for items which level 3 has on top of level 2).
DBGSTATIC CONST DWORD PrjInfo3StringsW[]={
offsetof(PRJINFO3W, pszNotifyName),
offsetof(PRJINFO3W, pszDataType),
offsetof(PRJINFO3W, pszParms),
offsetof(PRJINFO3W, pszStatus),
offsetof(PRJINFO3W, pszQProcName),
offsetof(PRJINFO3W, pszQProcParms),
offsetof(PRJINFO3W, pszDriverName),
offsetof(PRJINFO3W, pszPrinterName),
(DWORD) -1};
DBGSTATIC LPWSTR
CopyJobToPrjInfoW(
IN DWORD Level,
IN LPJOB_INFO_2 pJob,
IN LPCWSTR QueueNameW,
OUT PBYTE pBuffer,
IN OUT LPWSTR pEnd
)
{
LPSTR *pSourceStrings;
NET_API_STATUS rc;
NetpAssert( pBuffer != NULL );
NetpAssert( pEnd != NULL );
NetpAssert( pJob != NULL );
switch (Level) {
case 0:
{
PWORD pJobIds = (PWORD) pBuffer;
*pJobIds = (WORD)pJob->JobId;
}
break;
case 1:
{
LPSTR SourceStrings[sizeof(PrjInfo1StringsW)/sizeof(DWORD)];
PPRJINFOW pPrjInfo = (LPVOID) pBuffer;
pSourceStrings=SourceStrings;
*pSourceStrings++ = (LPSTR) (pJob->pParameters);
*pSourceStrings++ = (LPSTR) (pJob->pStatus);
*pSourceStrings++ = (LPSTR) (pJob->pDocument); // fake pszComment
pEnd = PackAnsiStringsToW(
SourceStrings,
(LPBYTE) (LPVOID) pPrjInfo,
PrjInfo1StringsW,
pEnd);
pPrjInfo->uJobId = (WORD)pJob->JobId;
if (pJob->pUserName)
(VOID) NetpNCopyStrToWStr(
(LPWSTR) (pPrjInfo->szUserName),
(LPSTR) (pJob->pUserName),
LM20_UNLEN+1);
else
pPrjInfo->szUserName[0] = L'\0';
if (pJob->pNotifyName)
(VOID) NetpNCopyStrToWStr(
(LPWSTR) (pPrjInfo->szNotifyName),
(LPSTR) (pJob->pNotifyName),
LM20_CNLEN+1);
else
pPrjInfo->szNotifyName[0] = L'\0';
if (pJob->pDatatype) {
(VOID) NetpNCopyStrToWStr(
(LPWSTR) (pPrjInfo->szDataType),
(LPSTR) (pJob->pDatatype),
DTLEN+1);
pPrjInfo->szDataType[DTLEN] = L'\0';
} else {
pPrjInfo->szDataType[0] = L'\0';
}
pPrjInfo->uPosition = (WORD)pJob->Position;
pPrjInfo->fsStatus = PrjStatusFromJobStatus( pJob->Status );
rc = NetpSystemTimeToGmtTime(
&pJob->Submitted,
&pPrjInfo->ulSubmitted );
NetpAssert( rc == NO_ERROR );
pPrjInfo->ulSize = pJob->Size;
}
break;
case 2: /*FALLTHROUGH*/
case 3:
{
PPRJINFO2W pPrjInfo = (LPVOID) pBuffer;
LPSTR SourceStrings[sizeof(PrjInfo2StringsW)/sizeof(DWORD)];
pSourceStrings=SourceStrings;
*pSourceStrings++ = (LPSTR) (pJob->pUserName);
*pSourceStrings++ = (LPSTR) (pJob->pDocument); // fake pszComment
*pSourceStrings++ = (LPSTR) (pJob->pDocument);
pEnd = PackAnsiStringsToW(
SourceStrings,
(LPBYTE) (LPVOID) pPrjInfo,
PrjInfo2StringsW,
pEnd);
pPrjInfo->uJobId = (WORD)pJob->JobId;
pPrjInfo->uPriority = (WORD)pJob->Priority;
pPrjInfo->uPosition = (WORD)pJob->Position;
pPrjInfo->fsStatus = PrjStatusFromJobStatus( pJob->Status );
rc = NetpSystemTimeToGmtTime(
&pJob->Submitted,
&pPrjInfo->ulSubmitted );
NetpAssert( rc == NO_ERROR );
pPrjInfo->ulSize = pJob->Size;
}
if (Level == 3) {
PPRJINFO3W pPrjInfo = (LPVOID) pBuffer;
LPSTR SourceStrings[sizeof(PrjInfo3StringsW)/sizeof(DWORD)];
//
// Copy queue name first, as it is already right char set.
//
NetpAssert( QueueNameW != NULL );
pEnd-=(wcslen(QueueNameW) + 1);
(VOID) wcscpy(pEnd, QueueNameW);
pPrjInfo->pszQueue = pEnd;
//
// Copy and convert other strings.
//
pSourceStrings=SourceStrings;
*pSourceStrings++ = (LPSTR) (pJob->pNotifyName);
*pSourceStrings++ = (LPSTR) (pJob->pDatatype);
*pSourceStrings++ = (LPSTR) (pJob->pParameters);
*pSourceStrings++ = (LPSTR) (pJob->pStatus);
*pSourceStrings++ = (LPSTR) (pJob->pPrintProcessor);
*pSourceStrings++ = (LPSTR) (pJob->pParameters);
*pSourceStrings++ = (LPSTR) (pJob->pDriverName);
*pSourceStrings++ = (LPSTR) (pJob->pPrinterName);
pEnd = PackAnsiStringsToW(
SourceStrings,
(LPBYTE) (LPVOID) pPrjInfo,
PrjInfo3StringsW,
pEnd);
pPrjInfo->pDriverData = NULL;
}
break;
default:
NetpKdPrint(( PREFIX_DOSPRINT
"CopyJobToPrjInfoW: invalid level!\n" ));
}
return pEnd;
}
DBGSTATIC DWORD
GetPrqInfoSizeW(
IN DWORD Level,
IN LPCWSTR QueueNameW,
IN LPPRINTER_INFO_2 pPrinter
)
{
NetpAssert( QueueNameW != NULL );
NetpAssert( (*QueueNameW) != L'\0' );
switch (Level) {
case 0:
return ( (LM20_QNLEN+1) * sizeof(WCHAR) );
case 1: /*FALLTHROUGH*/
case 2:
return sizeof(PRQINFOW) +
NULL_STR_CONV_SIZE( pPrinter->pSepFile ) +
NULL_STR_CONV_SIZE( pPrinter->pPrintProcessor ) +
NULL_STR_CONV_SIZE( pPrinter->pPortName ) +
NULL_STR_CONV_SIZE( pPrinter->pParameters ) +
NULL_STR_CONV_SIZE( pPrinter->pComment );
case 3: /*FALLTHROUGH*/
case 4:
NetpAssert( QueueNameW != NULL );
return sizeof(PRQINFO3W) +
WCSSIZE( QueueNameW ) + // pszName
NULL_STR_CONV_SIZE( pPrinter->pSepFile ) +
NULL_STR_CONV_SIZE( pPrinter->pPrintProcessor ) +
NULL_STR_CONV_SIZE( pPrinter->pParameters ) +
NULL_STR_CONV_SIZE( pPrinter->pComment ) +
NULL_STR_CONV_SIZE( pPrinter->pPortName ) +
NULL_STR_CONV_SIZE( pPrinter->pDriverName );
case 5:
NetpAssert( QueueNameW != NULL );
return sizeof(LPWSTR) +
WCSSIZE( QueueNameW ); // pszName
default:
NetpKdPrint(( PREFIX_DOSPRINT
"GetPrqInfoSizeW: invalid level!\n" ));
}
return 0;
}
DBGSTATIC DWORD
GetDrvInfoSizeW(
IN DWORD Level,
IN LPDRIVER_INFO_3A pDriverInfo3,
IN LPCSTR pUNCSharePath,
OUT LPDWORD pdwDependentFileCount
)
{
LPSTR psz;
DWORD dwSize;
switch (Level) {
case 52:
dwSize = sizeof(PRQINFO52W) +
NULL_STR_CONV_SIZE(pDriverInfo3->pName) +
NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pDriverPath)) +
NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pDataFile)) +
NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pConfigFile)) +
NULL_STR_CONV_SIZE(GetFileNameA(pDriverInfo3->pHelpFile)) +
NULL_STR_CONV_SIZE(pDriverInfo3->pDefaultDataType) +
NULL_STR_CONV_SIZE(pDriverInfo3->pMonitorName) +
NULL_STR_CONV_SIZE(pUNCSharePath);
*pdwDependentFileCount = 0;
for ( psz = pDriverInfo3->pDependentFiles;
psz && *psz ; psz += strlen(psz) + 1 ) {
dwSize += NULL_STR_CONV_SIZE(GetDependentFileNameA(psz));
(*pdwDependentFileCount)++;
}
//
// For the '\0's
//
dwSize += (MAX_DEPENDENT_FILES-*pdwDependentFileCount)*sizeof(WCHAR);
return dwSize;
default:
NetpKdPrint(( PREFIX_DOSPRINT "GetDrvInfoSizeW: invalid level!\n" ));
}
return 0;
}
DBGSTATIC DWORD
PrqInfoFixedSizeW(
IN DWORD Level // assumed valid
)
{
switch (Level) {
case 0:
return ( (LM20_QNLEN+1) * sizeof(WCHAR) );
case 1: /*FALLTHROUGH*/
case 2:
return (sizeof(PRQINFOW));
case 3: /*FALLTHROUGH*/
case 4:
return (sizeof(PRQINFO3W));
case 5:
return (sizeof(LPWSTR));
default:
NetpAssert( FALSE ); // Level should be valid!
return (0);
}
/*NOTREACHED*/
}
// String table for Q levels 1,2
DBGSTATIC CONST DWORD PrqInfo1StringsW[]={
offsetof(PRQINFOW, pszSepFile),
offsetof(PRQINFOW, pszPrProc),
offsetof(PRQINFOW, pszDestinations),
offsetof(PRQINFOW, pszParms),
offsetof(PRQINFOW, pszComment),
ARRAY_END};
// String table for Q levels 3,4.
DBGSTATIC CONST DWORD PrqInfo3StringsW[]={
offsetof(PRQINFO3W, pszSepFile),
offsetof(PRQINFO3W, pszPrProc),
offsetof(PRQINFO3W, pszParms),
offsetof(PRQINFO3W, pszComment),
offsetof(PRQINFO3W, pszPrinters),
offsetof(PRQINFO3W, pszDriverName),
(DWORD) -1};
// Print driver info3 string table (for level 52)
DBGSTATIC CONST DWORD PrqInfo52StringsW[]={
offsetof(PRQINFO52W, pszModelName),
offsetof(PRQINFO52W, pszDriverName),
offsetof(PRQINFO52W, pszDataFileName),
offsetof(PRQINFO52W, pszMonitorName),
offsetof(PRQINFO52W, pszDriverPath),
offsetof(PRQINFO52W, pszDefaultDataType),
offsetof(PRQINFO52W, pszHelpFile),
offsetof(PRQINFO52W, pszConfigFile),
offsetof(PRQINFO52W, pszDependentNames[0]),
offsetof(PRQINFO52W, pszDependentNames[1]),
offsetof(PRQINFO52W, pszDependentNames[2]),
offsetof(PRQINFO52W, pszDependentNames[3]),
offsetof(PRQINFO52W, pszDependentNames[4]),
offsetof(PRQINFO52W, pszDependentNames[5]),
offsetof(PRQINFO52W, pszDependentNames[6]),
offsetof(PRQINFO52W, pszDependentNames[7]),
offsetof(PRQINFO52W, pszDependentNames[8]),
offsetof(PRQINFO52W, pszDependentNames[9]),
offsetof(PRQINFO52W, pszDependentNames[10]),
offsetof(PRQINFO52W, pszDependentNames[11]),
offsetof(PRQINFO52W, pszDependentNames[12]),
offsetof(PRQINFO52W, pszDependentNames[13]),
offsetof(PRQINFO52W, pszDependentNames[14]),
offsetof(PRQINFO52W, pszDependentNames[15]),
offsetof(PRQINFO52W, pszDependentNames[16]),
offsetof(PRQINFO52W, pszDependentNames[17]),
offsetof(PRQINFO52W, pszDependentNames[18]),
offsetof(PRQINFO52W, pszDependentNames[19]),
offsetof(PRQINFO52W, pszDependentNames[20]),
offsetof(PRQINFO52W, pszDependentNames[21]),
offsetof(PRQINFO52W, pszDependentNames[22]),
offsetof(PRQINFO52W, pszDependentNames[23]),
offsetof(PRQINFO52W, pszDependentNames[24]),
offsetof(PRQINFO52W, pszDependentNames[25]),
offsetof(PRQINFO52W, pszDependentNames[26]),
offsetof(PRQINFO52W, pszDependentNames[27]),
offsetof(PRQINFO52W, pszDependentNames[28]),
offsetof(PRQINFO52W, pszDependentNames[29]),
offsetof(PRQINFO52W, pszDependentNames[30]),
offsetof(PRQINFO52W, pszDependentNames[31]),
offsetof(PRQINFO52W, pszDependentNames[32]),
offsetof(PRQINFO52W, pszDependentNames[33]),
offsetof(PRQINFO52W, pszDependentNames[34]),
offsetof(PRQINFO52W, pszDependentNames[35]),
offsetof(PRQINFO52W, pszDependentNames[36]),
offsetof(PRQINFO52W, pszDependentNames[37]),
offsetof(PRQINFO52W, pszDependentNames[38]),
offsetof(PRQINFO52W, pszDependentNames[39]),
offsetof(PRQINFO52W, pszDependentNames[40]),
offsetof(PRQINFO52W, pszDependentNames[41]),
offsetof(PRQINFO52W, pszDependentNames[42]),
offsetof(PRQINFO52W, pszDependentNames[43]),
offsetof(PRQINFO52W, pszDependentNames[44]),
offsetof(PRQINFO52W, pszDependentNames[45]),
offsetof(PRQINFO52W, pszDependentNames[46]),
offsetof(PRQINFO52W, pszDependentNames[47]),
offsetof(PRQINFO52W, pszDependentNames[48]),
offsetof(PRQINFO52W, pszDependentNames[49]),
offsetof(PRQINFO52W, pszDependentNames[50]),
offsetof(PRQINFO52W, pszDependentNames[51]),
offsetof(PRQINFO52W, pszDependentNames[52]),
offsetof(PRQINFO52W, pszDependentNames[53]),
offsetof(PRQINFO52W, pszDependentNames[54]),
offsetof(PRQINFO52W, pszDependentNames[55]),
offsetof(PRQINFO52W, pszDependentNames[56]),
offsetof(PRQINFO52W, pszDependentNames[57]),
offsetof(PRQINFO52W, pszDependentNames[58]),
offsetof(PRQINFO52W, pszDependentNames[59]),
offsetof(PRQINFO52W, pszDependentNames[60]),
offsetof(PRQINFO52W, pszDependentNames[61]),
offsetof(PRQINFO52W, pszDependentNames[62]),
offsetof(PRQINFO52W, pszDependentNames[63]),
(DWORD) -1};
DBGSTATIC LPWSTR
CopyPrinterToPrqInfoW(
IN LPPRINTER_INFO_2 pPrinter,
IN DWORD Level,
OUT LPBYTE pBuffer,
IN LPCWSTR QueueNameW,
OUT LPWSTR pEnd
)
{
LPSTR *pSourceStrings;
NetpAssert( pEnd != NULL );
NetpAssert( QueueNameW != NULL );
NetpAssert( (*QueueNameW) != L'\0' );
switch (Level) {
case 0:
(VOID) wcsncpy(
(LPWSTR) (LPVOID) pBuffer,
QueueNameW,
LM20_QNLEN);
break;
case 1: /*FALLTHROUGH*/
case 2:
{
LPSTR SourceStrings[sizeof(PrqInfo1StringsW)/sizeof(DWORD)];
PPRQINFOW pPrqInfo = (LPVOID) pBuffer;
pSourceStrings=SourceStrings;
*pSourceStrings++ = pPrinter->pSepFile;
*pSourceStrings++ = pPrinter->pPrintProcessor;
*pSourceStrings++ = pPrinter->pPortName;
*pSourceStrings++ = pPrinter->pParameters;
*pSourceStrings++ = pPrinter->pComment;
pEnd = PackAnsiStringsToW(
SourceStrings,
(LPBYTE) (LPVOID) pPrqInfo,
PrqInfo1StringsW,
pEnd);
NetpAssert( QueueNameW != NULL );
(VOID) wcsncpy(
pPrqInfo->szName, // dest
QueueNameW, // src
LM20_QNLEN); // char count
pPrqInfo->szName[LM20_QNLEN] = (USHORT)0;
pPrqInfo->uPriority = (WORD)pPrinter->Priority;
pPrqInfo->uStartTime = (WORD)pPrinter->StartTime;
pPrqInfo->uUntilTime = (WORD)pPrinter->UntilTime;
pPrqInfo->fsStatus = PrqStatusFromPrinterStatus( pPrinter->Status );
pPrqInfo->cJobs = (WORD)pPrinter->cJobs;
}
break;
case 3: /*FALLTHROUGH*/
case 4:
{
LPSTR SourceStrings[sizeof(PrqInfo3StringsW)/sizeof(DWORD)];
PPRQINFO3W pPrqInfo = (LPVOID) pBuffer;
//
// Copy queue name first, as it is already right char set.
//
NetpAssert( QueueNameW != NULL );
pEnd-=(wcslen(QueueNameW) + 1);
(VOID) wcscpy(pEnd, QueueNameW);
pPrqInfo->pszName = pEnd;
//
// Copy and convert other strings.
//
pSourceStrings=SourceStrings;
*pSourceStrings++ = pPrinter->pSepFile;
*pSourceStrings++ = pPrinter->pPrintProcessor;
*pSourceStrings++ = pPrinter->pParameters;
*pSourceStrings++ = pPrinter->pComment;
*pSourceStrings++ = pPrinter->pPortName; // pszPrinters
*pSourceStrings++ = pPrinter->pDriverName;
pEnd = PackAnsiStringsToW(
SourceStrings,
(LPBYTE) (LPVOID) pPrqInfo,
PrqInfo3StringsW,
pEnd);
pPrqInfo->uPriority = (WORD)pPrinter->Priority;
pPrqInfo->uStartTime = (WORD)pPrinter->StartTime;
pPrqInfo->uUntilTime = (WORD)pPrinter->UntilTime;
pPrqInfo->fsStatus = PrqStatusFromPrinterStatus( pPrinter->Status );
pPrqInfo->cJobs = (WORD)pPrinter->cJobs;
pPrqInfo->pDriverData = NULL;
// Note: if level is 4, caller will add array of jobs after this.
break;
}
case 5:
NetpAssert( QueueNameW != NULL );
pEnd -= (wcslen( QueueNameW ) + 1);
* (LPWSTR *) pBuffer = pEnd;
(VOID) wcscpy(
pEnd, // dest
QueueNameW ); // src
break;
default:
NetpKdPrint(( PREFIX_DOSPRINT
"CopyPrinterToPrqInfoW: invalid level!\n" ));
}
return pEnd;
}
DBGSTATIC LPWSTR
CopyDriverToPrqInfoW(
IN LPDRIVER_INFO_3A pDriver3,
IN DWORD dwDependentFileCount,
IN LPSTR pUNCSharePath,
IN DWORD Level,
OUT LPBYTE pBuffer,
OUT LPWSTR pEnd
)
{
LPSTR *pSourceStrings;
LPSTR psz;
NetpAssert( pEnd != NULL );
NetpAssert(MAX_DEPENDENT_FILES == 64);
switch (Level) {
case 52:
{
PPRQINFO52W pPrqInfo = (LPVOID) pBuffer;
LPSTR SourceStrings[sizeof(PrqInfo52StringsW)/sizeof(DWORD)];
ZeroMemory((LPBYTE)SourceStrings, sizeof(SourceStrings));
pSourceStrings=SourceStrings;
*pSourceStrings++ = pDriver3->pName;
*pSourceStrings++ = GetFileNameA(pDriver3->pDriverPath);
*pSourceStrings++ = GetFileNameA(pDriver3->pDataFile);
*pSourceStrings++ = GetFileNameA(pDriver3->pMonitorName);
*pSourceStrings++ = pUNCSharePath;
*pSourceStrings++ = GetFileNameA(pDriver3->pDefaultDataType);
*pSourceStrings++ = GetFileNameA(pDriver3->pHelpFile);
*pSourceStrings++ = GetFileNameA(pDriver3->pConfigFile);
for ( psz = pDriver3->pDependentFiles ;
psz && *psz ; psz += strlen(psz) + 1 ) {
*pSourceStrings++ = GetDependentFileNameA(psz);
}
pEnd = PackAnsiStringsToW(
SourceStrings,
(LPBYTE) (LPVOID)pPrqInfo,
PrqInfo52StringsW,
pEnd);
pPrqInfo->uVersion = (WORD)pDriver3->cVersion;
pPrqInfo->cDependentNames = (WORD)dwDependentFileCount;
}
break;
default:
NetpKdPrint(( PREFIX_DOSPRINT
"CopyPrinterToPrqInfoW: invalid level!\n" ));
}
return pEnd;
}
DBGSTATIC NET_API_STATUS
ComputeSpaceNeededForJobs(
IN LPCWSTR QueueNameW,
IN DWORD QLevel,
IN HANDLE PrinterHandle,
OUT LPDWORD pcbNeeded
)
{
NET_API_STATUS ApiStatus;
DWORD cJobs;
DWORD cbJobs;
DWORD cbNeeded = 0;
DWORD JobLevel;
LPJOB_INFO_2 pJob = NULL;
LPJOB_INFO_2 pJobs = NULL;
NetpAssert( (QLevel==2) || (QLevel==4) );
NetpAssert( QueueNameW != NULL );
if (QLevel==2) {
JobLevel = 1;
} else {
JobLevel = 2;
}
if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, NULL, 0, &cbJobs, &cJobs)) {
ApiStatus = (NET_API_STATUS) GetLastError();
if (ApiStatus == ERROR_INSUFFICIENT_BUFFER) {
pJobs = (LPVOID) GlobalAlloc(GMEM_FIXED, cbJobs);
if (pJobs == NULL) {
ApiStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
} else {
NetpKdPrint(( PREFIX_DOSPRINT
"ComputeSpaceNeededForJobs: got error " FORMAT_API_STATUS
" from MyEnumJobs(first).\n", ApiStatus ));
goto Cleanup;
}
}
if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, (LPBYTE)pJobs, cbJobs,
&cbJobs, &cJobs)) {
ApiStatus = (NET_API_STATUS) GetLastError();
NetpAssert( ApiStatus != ERROR_INSUFFICIENT_BUFFER );
NetpKdPrint(( PREFIX_DOSPRINT
"ComputeSpaceNeededForJobs: got error " FORMAT_API_STATUS
" from MyEnumJobs(second)\n", ApiStatus ));
goto Cleanup;
}
if (cJobs == 0) {
cbNeeded = 0;
ApiStatus = NO_ERROR;
goto Cleanup;
}
if (pJobs == NULL) {
NetpKdPrint(( PREFIX_DOSPRINT
"ComputeSpaceNeededForJobs: never allocated array!\n" ));
ApiStatus = NERR_InternalError;
goto Cleanup;
}
pJob=pJobs;
while (cJobs--) {
cbNeeded+=GetPrjInfoSizeW(JobLevel, pJob++, QueueNameW);
}
*pcbNeeded=(WORD)cbNeeded; // final byte count for this queue's jobs.
ApiStatus = NO_ERROR;
Cleanup:
if (pJobs != NULL) {
(VOID) GlobalFree(pJobs);
}
*pcbNeeded = cbNeeded; // final byte count for this queue's jobs.
return (ApiStatus);
} // ComputeSpaceNeededForJobs
DBGSTATIC NET_API_STATUS
AppendJobsToPrqW(
IN LPCWSTR QueueNameW,
IN DWORD QLevel,
IN HANDLE PrinterHandle,
OUT LPBYTE pbBuf,
IN DWORD cbBuf,
IN LPVOID pEnd,
OUT LPVOID * pNewEnd,
OUT LPDWORD pcbNeeded,
OUT LPDWORD pcReturned,
IN BOOL AllowPartialData
)
{
DWORD cJobs;
DWORD cbJobs;
DWORD cbNeeded = 0;
DWORD cbPrj;
DWORD JobLevel;
DWORD rc;
DWORD JobSize;
DWORD BytesLeft;
DWORD JobsStored;
LPJOB_INFO_2 pJob = NULL;
LPJOB_INFO_2 pJobs = NULL;
NetpAssert( (QLevel==2) || (QLevel==4) );
NetpAssert( QueueNameW != NULL );
if (QLevel==2) {
cbPrj = sizeof(PRJINFOW);
JobLevel = 1;
} else {
cbPrj = sizeof(PRJINFO2W);
JobLevel = 2;
}
if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, NULL, 0, &cbJobs, pcReturned)) {
rc = GetLastError();
if (rc == ERROR_INSUFFICIENT_BUFFER) {
pJobs = (LPVOID) GlobalAlloc(GMEM_FIXED, cbJobs);
if (pJobs == NULL) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
} else {
NetpKdPrint(( PREFIX_DOSPRINT
"AppendJobsToPrqW: got error " FORMAT_API_STATUS
" from MyEnumJobs(first)\n", rc ));
goto Cleanup;
}
}
if (!MyEnumJobs(PrinterHandle, 0, (DWORD) -1, 2, (LPBYTE)pJobs, cbJobs,
&cbJobs, pcReturned)) {
rc = GetLastError();
NetpAssert( rc != ERROR_INSUFFICIENT_BUFFER );
NetpKdPrint(( PREFIX_DOSPRINT
"AppendJobsToPrqW: got error " FORMAT_API_STATUS
" from MyEnumJobs(second)\n", rc ));
goto Cleanup;
}
if (*pcReturned == 0) {
cbNeeded = 0;
rc = NO_ERROR;
goto Cleanup;
}
if (pJobs == NULL) {
NetpKdPrint(( PREFIX_DOSPRINT
"AppendJobsToPrqW: never allocated array!\n" ));
rc = NERR_InternalError;
goto Cleanup;
}
cJobs = *pcReturned;
pJob=pJobs;
while (cJobs--)
cbNeeded+=GetPrjInfoSizeW(JobLevel, pJob++, QueueNameW);
*pcbNeeded = cbNeeded; // final byte count for this queue's jobs.
if (cbNeeded <= cbBuf) {
cJobs = *pcReturned;
pJob=pJobs;
while (cJobs--) {
pEnd = CopyJobToPrjInfoW(JobLevel, pJob++, QueueNameW,
pbBuf,
pEnd);
pbBuf += cbPrj; // Note: Wasn't DWORD aligned
}
rc = NO_ERROR;
} else {
//
// See if the user wants to receive as much data as we can fit.
//
if( AllowPartialData == TRUE ) {
cJobs = *pcReturned;
pJob = pJobs;
JobsStored = 0;
BytesLeft = cbBuf;
while( cJobs-- ) {
JobSize = GetPrjInfoSizeW( JobLevel,
pJob,
QueueNameW );
if( JobSize <= BytesLeft ) {
//
// This job will fit. Add it in.
//
pEnd = CopyJobToPrjInfoW( JobLevel,
pJob++,
QueueNameW,
pbBuf,
pEnd );
pbBuf += cbPrj; // Note: Wasn't DWORD aligned
BytesLeft -= JobSize;
JobsStored++;
} else {
//
// The buffer is full.
//
break;
}
}
if( JobsStored != 0 ) {
//
// Return what we were able to store.
//
*pcReturned = JobsStored;
rc = NO_ERROR;
} else {
rc = NERR_BufTooSmall;
}
} else {
rc = NERR_BufTooSmall;
}
}
Cleanup:
if (pJobs != NULL) {
(VOID) GlobalFree(pJobs);
}
*pcbNeeded = cbNeeded; // final byte count for this queue's jobs.
if (pNewEnd != NULL) {
*pNewEnd = pEnd;
}
return (rc);
}
SPLERR SPLENTRY DosPrintQGetInfoW(
LPWSTR pszServer,
LPWSTR pszQueueName,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
PUSHORT pcbNeeded
)
{
DWORD cJobsReturned;
LPWSTR pEnd;
DWORD rc;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
LPPRINTER_INFO_2 pPrinter = NULL;
LPDRIVER_INFO_3A pDriver = NULL;
CHAR szDriverDir[MAX_PATH];
DWORD cbNeeded = 0, dwDependentFileCount;
DWORD cbNeededForJobs;
if (pszServer && *pszServer) {
rc = RxPrintQGetInfo(pszServer, pszQueueName, uLevel, pbBuf,
cbBuf, &cbNeeded);
if (cbNeeded > MAX_WORD) {
rc = MY_PROTOCOL_LIMIT_ERROR;
goto Cleanup;
}
*pcbNeeded = (USHORT)cbNeeded;
goto Cleanup;
}
*pcbNeeded = 0; // in case an error occurs.
if ( !NetpIsPrintQLevelValid( uLevel, FALSE ) ) {
rc = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if ( (pszQueueName==NULL) || ((*pszQueueName)==L'\0') ) {
rc = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
if ( !MyOpenPrinterW( pszQueueName, &hPrinter, NULL) ) {
rc = GetLastError();
if ( rc == ERROR_INVALID_PRINTER_NAME )
rc = NERR_QNotFound;
goto Cleanup;
}
//
// Level 52 is meant for point and print from a Windows 95 clients
// can't use with other clients since no environment info is passed
//
if ( uLevel == 52 ) {
cbNeeded = sizeof(szDriverDir)-2;
szDriverDir[0] = szDriverDir[1] = '\\';
if ( !GetComputerNameA(szDriverDir+2, &cbNeeded) ) {
rc = GetLastError();
goto Cleanup;
}
if ( strlen(szDriverDir) + strlen(WIN95_DRIVER_SHARE) + 1
> sizeof(szDriverDir) ) {
rc = ERROR_NOT_ENOUGH_MEMORY;
NetpAssert( rc != NO_ERROR ); // Always break
goto Cleanup;
}
strcat(szDriverDir, WIN95_DRIVER_SHARE);
(VOID)MyGetPrinterDriver(hPrinter, WIN95_ENVIRONMENT, 3,
NULL, 0, &cbNeeded);
rc = GetLastError();
if ( rc != ERROR_INSUFFICIENT_BUFFER )
goto Cleanup;
pDriver = (LPVOID) GlobalAlloc(GMEM_FIXED, cbNeeded);
if ( !pDriver ) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
if ( !MyGetPrinterDriver(hPrinter, WIN95_ENVIRONMENT, 3,
(LPVOID)pDriver, cbNeeded, &cbNeeded) ) {
rc = GetLastError();
goto Cleanup;
}
cbNeeded=GetDrvInfoSizeW(uLevel, pDriver,
szDriverDir, &dwDependentFileCount);
if ( dwDependentFileCount > MAX_DEPENDENT_FILES ) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
} else {
if (!MyGetPrinter(hPrinter, 2, NULL, 0, &cbNeeded)) {
rc = GetLastError();
if (rc == ERROR_INSUFFICIENT_BUFFER) {
pPrinter = (LPVOID) GlobalAlloc(GMEM_FIXED, cbNeeded);
if (pPrinter == NULL) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
} else {
goto Cleanup;
}
}
if (!MyGetPrinter(hPrinter, 2, (LPBYTE)pPrinter, cbNeeded, &cbNeeded)) {
rc = GetLastError();
goto Cleanup;
}
// How much for just the queue structure and its strings?
cbNeeded=GetPrqInfoSizeW(uLevel, pszQueueName, pPrinter);
}
if (cbNeeded > MAX_WORD) {
rc = MY_PROTOCOL_LIMIT_ERROR;
goto Cleanup;
}
*pcbNeeded = (WORD)cbNeeded; // Tell caller the size (so far).
//
// Build the queue structure itself.
//
if (cbNeeded <= (DWORD) cbBuf) {
if ( uLevel == 52 ) {
ZeroMemory(pbBuf, cbNeeded);
pEnd = CopyDriverToPrqInfoW(pDriver, dwDependentFileCount,
szDriverDir, uLevel, pbBuf,
(LPWSTR) (pbBuf+cbBuf) );
} else {
pEnd = CopyPrinterToPrqInfoW(pPrinter, uLevel, pbBuf, pszQueueName,
(LPWSTR) (pbBuf+cbBuf) );
}
} else {
//
// Too small. Well, need to find total size before we can tell caller.
//
if ( (uLevel==2) || (uLevel==4) ) {
rc = ComputeSpaceNeededForJobs(
pszQueueName,
uLevel, // Q info level
hPrinter,
& cbNeededForJobs );
if (rc != NO_ERROR) {
goto Cleanup;
}
cbNeeded += cbNeededForJobs;
}
if (cbNeeded > MAX_WORD) {
rc = MY_PROTOCOL_LIMIT_ERROR;
goto Cleanup;
}
rc = NERR_BufTooSmall;
goto Cleanup;
}
//
// Append jobs if necessary.
//
if ( (uLevel==2) || (uLevel==4) ) {
DWORD cbPrq = PrqInfoFixedSizeW( uLevel );
rc = AppendJobsToPrqW(
pszQueueName,
uLevel, // Q info level
hPrinter,
pbBuf + cbPrq, // put first job here
cbBuf - cbNeeded, // bytes avail for jobs
pEnd, // str area
NULL, // don't need new pEnd
& cbNeededForJobs,
& cJobsReturned,
cbBuf == MAX_WORD ? TRUE : FALSE ); // If the buffer is at its max, get what we can.
if( cbNeeded + cbNeededForJobs > MAX_WORD ) {
*pcbNeeded = MAX_WORD;
} else {
*pcbNeeded = (USHORT) (cbNeeded + cbNeededForJobs);
}
//
// Update job count in queue structure, as it may be out of date.
//
NetpSetJobCountForQueue(
uLevel, // queue info level
pbBuf, // queue structure to update
TRUE, // yes, UNICODE strings
cJobsReturned ); // actual job count
if (rc != NO_ERROR) {
goto Cleanup;
}
}
rc = NO_ERROR;
Cleanup:
if (hPrinter != INVALID_HANDLE_VALUE) {
(VOID) MyClosePrinter( hPrinter );
}
if (pPrinter) {
(VOID) GlobalFree( pPrinter );
}
if (pDriver) {
(VOID) GlobalFree( pDriver );
}
return rc;
}
SPLERR SPLENTRY DosPrintJobGetInfoW(
LPWSTR pszServer,
BOOL bRemote,
WORD uJobId,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
PUSHORT pcbNeeded
)
{
DWORD cb;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
LPSTR QueueNameA = NULL;
LPWSTR QueueNameW = NULL;
LPJOB_INFO_2 pJob = NULL;
LPWSTR pEnd;
DWORD rc;
DWORD cbNeeded = 0;
if (bRemote) {
rc = RxPrintJobGetInfo(pszServer, uJobId, uLevel, pbBuf,
cbBuf, &cbNeeded);
*pcbNeeded = (USHORT)cbNeeded;
return rc;
}
*pcbNeeded = 0; // in case an error occurs.
if ( !NetpIsPrintJobLevelValid( uLevel, FALSE ) ) {
rc = ERROR_INVALID_LEVEL;
goto Cleanup;
}
//
// The 3.51 spooler has been changed to accept Get/SetJobs on the
// local server handle. We will still do security checks against
// the Job's security descriptor. This also avoids the costly
// FindLocalJob() call.
//
if (!MyOpenPrinterW( pszServer, &hPrinter, NULL)) {
rc = GetLastError();
NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: "
"MyOpenPrinter( NULL, &hPrinter, NULL ) failed"
FORMAT_API_STATUS "\n", rc ));
hPrinter = INVALID_HANDLE_VALUE;
goto Cleanup;
}
NetpAssert( hPrinter != INVALID_HANDLE_VALUE );
//
// Note: this should really call MyGetJobW, since it looks
// like the code later thunks from ansi back to unicode.
//
if (!MyGetJobA(hPrinter, uJobId, 2, NULL, 0, &cb)) {
rc=GetLastError();
NetpAssert( rc != NO_ERROR );
if (rc == ERROR_INSUFFICIENT_BUFFER) {
pJob = (LPVOID) GlobalAlloc(GMEM_FIXED, cb);
if (pJob == NULL) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
if ( !MyGetJobA(hPrinter, uJobId, 2, (LPBYTE)pJob, cb, &cb) ) {
rc=GetLastError();
NetpAssert( rc != NO_ERROR );
goto Cleanup;
}
} else {
if (rc == ERROR_INVALID_PARAMETER) {
rc = NERR_JobNotFound;
}
goto Cleanup; // Job deleted? Not enough mem?
}
}
if (pJob == NULL) {
NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobGetInfoW: "
"*** STILL INVALID RESULT FROM MyGetJob, pJob IS NULL!\n" ));
rc = NERR_InternalError;
goto Cleanup;
}
NetpAssert( pJob != NULL );
NetpAssert( pJob->pPrinterName != NULL );
QueueNameA = FindQueueNameInPrinterNameA(
(pJob->pPrinterName) );
NetpAssert( QueueNameA != NULL );
QueueNameW = NetpAllocWStrFromStr( QueueNameA );
if (QueueNameW == NULL) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
NetpAssert( QueueNameW != NULL );
cb=GetPrjInfoSizeW(uLevel, pJob, QueueNameW);
*pcbNeeded=(WORD)cb;
if (cb > (DWORD) cbBuf) {
rc = NERR_BufTooSmall;
goto Cleanup;
}
pEnd = (LPVOID) (pbBuf+cbBuf);
(VOID) CopyJobToPrjInfoW(uLevel, pJob, QueueNameW, pbBuf, pEnd);
rc = NO_ERROR;
Cleanup:
if (hPrinter != INVALID_HANDLE_VALUE) {
(VOID) MyClosePrinter( hPrinter );
}
if (pJob != NULL) {
(VOID) GlobalFree( pJob );
}
if (QueueNameW != NULL) {
(VOID) NetApiBufferFree( QueueNameW );
}
return (rc);
}
SPLERR SPLENTRY DosPrintJobDelW(
LPWSTR pszServer,
BOOL bRemote,
WORD uJobId
)
{
if (bRemote)
return RxPrintJobDel(pszServer, uJobId);
return (CommandALocalJobA(NULL, pszServer, NULL, uJobId, 0, NULL, JOB_CONTROL_CANCEL ) );
}
SPLERR SPLENTRY DosPrintJobContinueW(
LPWSTR pszServer,
BOOL bRemote,
WORD uJobId
)
{
if (bRemote)
return RxPrintJobContinue(pszServer, uJobId);
return (CommandALocalJobA(NULL, pszServer, NULL, uJobId, 0, NULL, JOB_CONTROL_RESUME ) );
}
SPLERR SPLENTRY DosPrintJobPauseW(
LPWSTR pszServer,
BOOL bRemote,
WORD uJobId
)
{
if (bRemote)
return RxPrintJobPause(pszServer, uJobId);
return (CommandALocalJobA(NULL, pszServer, NULL, uJobId, 0, NULL, JOB_CONTROL_PAUSE ) );
}
SPLERR SPLENTRY DosPrintJobEnumW(
LPWSTR pszServer,
LPWSTR pszQueueName,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
PWORD pcReturned,
PWORD pcTotal
)
{
DWORD cbPrinter;
LPJOB_INFO_2 pJob = NULL;
LPJOB_INFO_2 pJobs;
DWORD cb, cbJobs, cReturned, cJobs;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
LPWSTR pEnd;
DWORD rc;
DWORD cTotal;
if (pszServer && *pszServer) {
rc = RxPrintJobEnum(pszServer, pszQueueName, uLevel, pbBuf,
cbBuf, &cReturned, &cTotal);
*pcReturned = (WORD)cReturned;
*pcTotal = (WORD)cTotal;
goto Cleanup;
}
*pcReturned=0;
*pcTotal = 0;
if ( !NetpIsPrintJobLevelValid( uLevel, FALSE ) ) {
rc = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if (!MyOpenPrinterW( pszQueueName, &hPrinter, NULL)) {
rc = GetLastError();
goto Cleanup;
}
NetpAssert( hPrinter != INVALID_HANDLE_VALUE );
if (!MyEnumJobs(hPrinter, 0, (DWORD) -1, 2, NULL, 0, &cbJobs, &cReturned)) {
rc = GetLastError();
NetpAssert( rc != NO_ERROR );
if (rc == ERROR_INSUFFICIENT_BUFFER) {
if (pJob = (LPVOID) GlobalAlloc(GMEM_FIXED, cbJobs)) {
if (!MyEnumJobs(hPrinter, 0, (DWORD) -1, 2, (LPBYTE)pJob, cbJobs,
&cbJobs, &cReturned)) {
rc = GetLastError();
NetpAssert( rc != NO_ERROR );
NetpAssert( rc != ERROR_INSUFFICIENT_BUFFER );
NetpKdPrint(( PREFIX_DOSPRINT
"DosPrintJobEnumW: got error " FORMAT_API_STATUS
" from MyEnumJobs(first)\n", rc ));
goto Cleanup;
}
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
} else {
NetpKdPrint(( PREFIX_DOSPRINT
"DosPrintJobEnumW: got error " FORMAT_API_STATUS
" from MyEnumJobs(first)\n", rc ));
goto Cleanup;
}
}
if (cReturned == 0) {
*pcReturned = 0;
*pcTotal = 0;
rc = NO_ERROR;
goto Cleanup;
}
if (pJob == NULL) {
NetpKdPrint(( PREFIX_DOSPRINT
"DosPrintJobEnumW: never allocated array!\n" ));
rc = NERR_InternalError;
goto Cleanup;
}
*pcTotal = (WORD)cReturned;
cb=0;
cJobs=cReturned;
pJobs=pJob;
while (cJobs--)
cb+=GetPrjInfoSizeW(uLevel, pJobs++, pszQueueName);
if (cb <= (DWORD) cbBuf) {
DWORD cbFixedPortion = PrjInfoFixedSizeW( uLevel );
NetpAssert( cbFixedPortion != 0 ); // level already checked!
pEnd = (LPWSTR)(pbBuf+cbBuf);
cJobs=cReturned;
pJobs=pJob;
while (cJobs--) {
pEnd = CopyJobToPrjInfoW(uLevel, pJobs++,
pszQueueName,
pbBuf, pEnd);
pbBuf += cbFixedPortion;
}
*pcReturned = (WORD)cReturned;
rc = NO_ERROR;
} else {
rc = NERR_BufTooSmall;
goto Cleanup;
}
Cleanup:
if (hPrinter != INVALID_HANDLE_VALUE) {
(VOID) MyClosePrinter( hPrinter );
}
if (pJob != NULL) {
(VOID) GlobalFree( pJob );
}
return rc;
}
SPLERR SPLENTRY
DosPrintDestEnumW(
IN LPWSTR pszServer OPTIONAL,
IN WORD uLevel,
OUT PBYTE pbBuf,
IN WORD cbBuf,
OUT PUSHORT pcReturned,
OUT PUSHORT pcTotal
)
{
DWORD cReturned=0, cTotal=0, rc;
if (pszServer && *pszServer) {
rc = RxPrintDestEnum(pszServer, uLevel, pbBuf, cbBuf,
&cReturned, &cTotal);
*pcReturned = (USHORT)cReturned;
*pcTotal = (USHORT)cTotal;
return rc;
}
// Stub for local dest enum - no entries, dest not found.
*pcReturned = 0;
*pcTotal = 0;
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintDestControlW(
LPWSTR pszServer,
LPWSTR pszDevName,
WORD uControl
)
{
if (pszServer && *pszServer)
return RxPrintDestControl(pszServer, pszDevName, uControl);
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintDestGetInfoW(
LPWSTR pszServer,
LPWSTR pszName,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
PUSHORT pcbNeeded
)
{
DWORD cbNeeded = 0, rc;
if (pszServer && *pszServer) {
rc = RxPrintDestGetInfo(pszServer, pszName, uLevel, pbBuf,
cbBuf, &cbNeeded);
*pcbNeeded = (USHORT)cbNeeded;
return rc;
}
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintDestAddW(
LPWSTR pszServer,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf
)
{
if (pszServer && *pszServer)
return RxPrintDestAdd(pszServer, uLevel, pbBuf, cbBuf);
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintDestSetInfoW(
LPWSTR pszServer,
LPWSTR pszName,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
WORD uParmNum
)
{
if (pszServer && *pszServer)
return RxPrintDestSetInfo(pszServer, pszName, uLevel, pbBuf,
cbBuf, uParmNum);
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintDestDelW(
LPWSTR pszServer,
LPWSTR pszPrinterName
)
{
if (pszServer && *pszServer)
return RxPrintDestDel(pszServer, pszPrinterName);
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintQEnumW(
LPWSTR pszServer,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
PUSHORT pcReturned,
PUSHORT pcTotal
)
{
DWORD cJobsReturned;
DWORD Total, cbNeeded, rc;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
DWORD i;
DWORD JobFixedEntrySize = 0;
DWORD JobLevel;
LPSHARE_INFO_1 pShareInfo = NULL;
DWORD cbPrinter;
LPPRINTER_INFO_2 pPrinter = NULL;
BOOL BufferTooSmall=FALSE;
DWORD cReturned = 0;
DWORD cTotal = 0;
#if DBG
LPVOID OutputBufferStart = pbBuf;
#endif
LPVOID pEnd;
DWORD SharesRead;
if ( !NetpIsPrintQLevelValid( uLevel, FALSE ) ) {
rc = ERROR_INVALID_LEVEL;
goto Cleanup;
}
if (pszServer && *pszServer) {
rc = RxPrintQEnum(pszServer, uLevel, pbBuf, cbBuf, &cReturned, &cTotal);
*pcReturned = (USHORT)cReturned;
*pcTotal = (USHORT)cTotal;
goto Cleanup;
}
*pcReturned = 0;
*pcTotal = 0;
rc=NetShareEnum(
NULL,
1,
(LPBYTE *)(LPVOID)&pShareInfo,
MAX_PREFERRED_LENGTH,
&SharesRead,
&Total,
NULL);
if (rc != NO_ERROR) {
NetpKdPrint((PREFIX_DOSPRINT "DosPrintQEnumW: NetShareEnum returned "
FORMAT_API_STATUS "\n", rc));
goto Cleanup;
}
pEnd = (pbBuf + cbBuf);
if (uLevel==2) {
JobLevel = 1;
JobFixedEntrySize = PrjInfoFixedSizeW( JobLevel );
} else if (uLevel == 4) {
JobLevel = 2;
JobFixedEntrySize = PrjInfoFixedSizeW( JobLevel );
}
for (i=0; i<SharesRead; i++) {
if (pShareInfo[i].shi1_type != STYPE_PRINTQ) {
continue;
}
NetpAssert( pShareInfo[i].shi1_netname != NULL );
NetpAssert( (*pShareInfo[i].shi1_netname) != L'\0' );
if (STRLEN( pShareInfo[i].shi1_netname ) > LM20_QNLEN) {
continue;
}
if ( !MyOpenPrinterW(pShareInfo[i].shi1_netname, &hPrinter, NULL)) {
rc = (NET_API_STATUS) GetLastError();
NetpKdPrint(( PREFIX_DOSPRINT
"DosPrintQEnumW: MyOpenPrinter failed, status "
FORMAT_API_STATUS ".\n", rc ));
NetpAssert( rc != NO_ERROR );
goto Cleanup;
}
NetpAssert( hPrinter != INVALID_HANDLE_VALUE );
if (!MyGetPrinter(hPrinter, 2, NULL, 0, &cbPrinter)) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
rc = (NET_API_STATUS) GetLastError();
NetpKdPrint(( PREFIX_DOSPRINT
"DosPrintQEnumW: MyGetPrinter(first) failed, status "
FORMAT_API_STATUS ".\n", rc ));
NetpAssert( rc != NO_ERROR );
goto Cleanup;
}
}
NetpAssert( cbPrinter != 0 );
pPrinter = (LPVOID) GlobalAlloc(GMEM_FIXED, cbPrinter);
if (pPrinter == NULL) {
rc = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
if ( !MyGetPrinter(hPrinter, 2, (LPBYTE)pPrinter,
cbPrinter, &cbPrinter)) {
rc = (NET_API_STATUS) GetLastError();
NetpKdPrint(( PREFIX_DOSPRINT
"DosPrintQEnumW: MyGetPrinter(second) failed, status "
FORMAT_API_STATUS ".\n", rc ));
NetpAssert( rc != NO_ERROR );
goto Cleanup;
}
cbNeeded=GetPrqInfoSizeW(uLevel,
pShareInfo[i].shi1_netname, // Q nam
pPrinter);
NetpAssert( cbNeeded > 0 );
NetpAssert( cbNeeded <= (DWORD) MAX_WORD );
if ( (!BufferTooSmall) && ((DWORD)cbBuf >= cbNeeded) ) {
LPVOID pbQueue = pbBuf;
//
// Handle queue structure itself.
//
pEnd = CopyPrinterToPrqInfoW(pPrinter,
uLevel,
pbBuf,
pShareInfo[i].shi1_netname,
pEnd);
pbBuf += PrqInfoFixedSizeW( uLevel );
cbBuf -= (WORD) cbNeeded;
//
// Append job structures if needed.
//
if ( (uLevel==2) || (uLevel==4) ) { // info level includes jobs
NetpAssert( pbBuf < (LPBYTE) pEnd );
rc = AppendJobsToPrqW(
pShareInfo[i].shi1_netname,
uLevel, // Q info level
hPrinter,
pbBuf, // first job here
cbBuf, // bytes avail
pEnd, // str area
& pEnd, // set new end ptr
& cbNeeded,
& cJobsReturned,
FALSE ); // Only accept all the data.
if (rc == NERR_BufTooSmall) {
BufferTooSmall = TRUE; // continue, as we need pcTotal...
} else if (rc != NO_ERROR) {
goto Cleanup;
} else { // Must be NO_ERROR.
NetpAssert( cbNeeded <= (DWORD) MAX_WORD );
NetpAssert( pbBuf < (LPBYTE) pEnd );
NetpAssert( JobFixedEntrySize !=0 );
pbBuf += (JobFixedEntrySize * cJobsReturned);
cbBuf -= (WORD) cbNeeded;
(*pcReturned)++;
// Correct possible out of date
// job count in queue structure.
NetpSetJobCountForQueue(
uLevel,
pbQueue,
TRUE, // yes, UNICODE strs
cJobsReturned );
}
} else { // info level does not include jobs
(*pcReturned)++;
}
} else { // not enough mem for Q struct
BufferTooSmall = TRUE;
// Continue, as we want to compute pcTotal for subsequent queues.
}
(*pcTotal)++;
NetpAssert( pPrinter != NULL );
(VOID) GlobalFree(pPrinter);
pPrinter = NULL;
NetpAssert( hPrinter != INVALID_HANDLE_VALUE );
(VOID) MyClosePrinter(hPrinter);
hPrinter = INVALID_HANDLE_VALUE;
} // for each share
Cleanup:
if (hPrinter != INVALID_HANDLE_VALUE) {
(VOID) MyClosePrinter( hPrinter );
}
if (pPrinter != NULL) {
(VOID) GlobalFree( pPrinter );
}
if (pShareInfo != NULL) {
(VOID) NetApiBufferFree(pShareInfo);
}
if (BufferTooSmall) {
rc = NERR_BufTooSmall;
}
return (rc);
}
SPLERR SPLENTRY DosPrintQSetInfoW(
LPWSTR pszServer,
LPWSTR pszQueueName,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
WORD uParmNum
)
{
if (pszServer && *pszServer)
return RxPrintQSetInfo(pszServer, pszQueueName, uLevel, pbBuf,
cbBuf, uParmNum);
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintQPauseW(
LPWSTR pszServer,
LPWSTR pszQueueName
)
{
if (pszServer && *pszServer)
return RxPrintQPause(pszServer, pszQueueName);
return (CommandALocalPrinterW( pszQueueName, PRINTER_CONTROL_PAUSE ) );
}
SPLERR SPLENTRY DosPrintQContinueW(
LPWSTR pszServer,
LPWSTR pszQueueName
)
{
if (pszServer && *pszServer)
return RxPrintQContinue(pszServer, pszQueueName);
return (CommandALocalPrinterW( pszQueueName, PRINTER_CONTROL_RESUME ) );
}
SPLERR SPLENTRY DosPrintQPurgeW(
LPWSTR pszServer,
LPWSTR pszQueueName
)
{
if (pszServer && *pszServer)
return RxPrintQPurge(pszServer, pszQueueName);
return (CommandALocalPrinterW( pszQueueName, PRINTER_CONTROL_PURGE ) );
}
SPLERR SPLENTRY DosPrintQAddW(
LPWSTR pszServer,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf
)
{
if (pszServer && *pszServer)
return RxPrintQAdd(pszServer, uLevel, pbBuf, cbBuf);
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintQDelW(
LPWSTR pszServer,
LPWSTR pszQueueName
)
{
if (pszServer && *pszServer)
return RxPrintQDel(pszServer, pszQueueName);
return ERROR_NOT_SUPPORTED;
}
SPLERR SPLENTRY DosPrintJobSetInfoW(
LPWSTR pszServer,
BOOL bRemote,
WORD uJobId,
WORD uLevel,
PBYTE pbBuf,
WORD cbBuf,
WORD uParmNum
)
{
if (bRemote)
return RxPrintJobSetInfo(pszServer, uJobId, uLevel, pbBuf,
cbBuf, uParmNum);
//
// Hack for Chicago: support Level 1, ParmNum 0xb so that jobs
// are set with the comment field.
//
if (uLevel == 1 && uParmNum == PRJ_COMMENT_PARMNUM) {
HANDLE hPrinter = INVALID_HANDLE_VALUE;
CHAR szDocument[MAX_PATH];
PJOB_INFO_1 pJob = NULL;
DWORD cbJob;
SPLERR rc;
//
// Allocate maximum size of JOB_INFO_1A. Later, this
// should be moved into the spooler's header file.
//
cbJob = sizeof(JOB_INFO_1) + 6 * MAX_PATH;
pJob = (PJOB_INFO_1) GlobalAlloc(GMEM_FIXED, cbJob);
if (pJob == NULL) {
rc = GetLastError();
goto Cleanup;
}
//
// The 3.51 spooler has been changed to accept Get/SetJobs on the
// local server handle. We will still do security checks against
// the Job's security descriptor. This also avoids the costly
// FindLocalJob() call.
//
if (!MyOpenPrinterW( pszServer, &hPrinter, NULL)) {
rc = GetLastError();
NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: "
"MyOpenPrinter( NULL, &hPrinter, NULL ) failed"
FORMAT_API_STATUS "\n", rc ));
hPrinter = INVALID_HANDLE_VALUE;
goto Cleanup;
}
NetpAssert( hPrinter != INVALID_HANDLE_VALUE );
//
// We need to get a copy of the old job info. Later, the
// spooler should be changed to allow "don't change" values.
//
if (!MyGetJobA( hPrinter, uJobId, 1, (PBYTE)pJob, cbJob, &cbJob )) {
rc = GetLastError();
NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: "
"MyGetJob failed" FORMAT_API_STATUS "\n", rc ));
goto Cleanup;
}
//
// Put in new document name.
//
NetpNCopyWStrToStr( szDocument,
(LPWSTR)pbBuf,
sizeof( szDocument ) / sizeof( szDocument[0] ));
pJob->pDocument = szDocument;
//
// Don't try and change the position, since this requires
// admin access (and isn't necessary).
//
pJob->Position = JOB_POSITION_UNSPECIFIED;
rc = CommandALocalJobA( hPrinter, NULL, NULL, uJobId, 1, (PBYTE)pJob, 0 );
if (rc) {
NetpKdPrint((PREFIX_DOSPRINT "DosPrintJobSetInfoW: "
"CommandALocalJobA failed " FORMAT_API_STATUS "\n", rc ));
}
Cleanup:
if (pJob) {
GlobalFree( pJob );
}
if (hPrinter != INVALID_HANDLE_VALUE) {
MyClosePrinter( hPrinter );
}
return rc;
}
return ERROR_NOT_SUPPORTED;
}
VOID
NetpSetJobCountForQueue(
IN DWORD QueueLevel,
IN OUT LPVOID Queue,
IN BOOL HasUnicodeStrings,
IN DWORD JobCount
)
{
NetpAssert( NetpIsPrintQLevelValid( QueueLevel, FALSE ) );
NetpAssert( Queue != NULL );
if (QueueLevel == 2) {
if (HasUnicodeStrings) {
PPRQINFOW pq = Queue;
pq->cJobs = (WORD) JobCount;
} else {
PPRQINFOA pq = Queue;
pq->cJobs = (WORD) JobCount;
}
} else if (QueueLevel == 4) {
if (HasUnicodeStrings) {
PPRQINFO3W pq = Queue;
pq->cJobs = (WORD) JobCount;
} else {
PPRQINFO3A pq = Queue;
pq->cJobs = (WORD) JobCount;
}
} else {
NetpAssert( FALSE ); // Should never get here!
}
} // NetpSetJobCountForQueue