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.
2313 lines
65 KiB
2313 lines
65 KiB
/*++
|
|
|
|
Copyright (c) 1990 - 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
getprn.c
|
|
|
|
Abstract:
|
|
|
|
This module provides all the public exported APIs relating to Printer
|
|
management for the Local Print Providor
|
|
|
|
SplGetPrinter
|
|
LocalEnumPrinters
|
|
|
|
Author:
|
|
|
|
Dave Snipp (DaveSn) 15-Mar-1991
|
|
Steve Wilson (SWilson) - Dec 1996 Added GetPrinter Level 7
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#define NOMINMAX
|
|
|
|
#include <precomp.h>
|
|
#include <offsets.h>
|
|
|
|
|
|
WCHAR *szNull = L"";
|
|
WCHAR *szPrintProvidorName = L"Windows NT Local Print Providor";
|
|
WCHAR *szPrintProvidorDescription=L"Windows NT Local Printers";
|
|
WCHAR *szPrintProvidorComment=L"Locally connected Printers";
|
|
|
|
WCHAR *gszDrvConvert = L",DrvConvert";
|
|
|
|
#define Nulwcslen(psz) ((psz) ? wcslen(psz)*sizeof(WCHAR)+sizeof(WCHAR) : 0)
|
|
|
|
#define PRINTER_STATUS_INTERNAL 0
|
|
#define PRINTER_STATUS_EXTERNAL 1
|
|
|
|
DWORD SettablePrinterStatusMappings[] = {
|
|
|
|
// INTERNAL: EXTERNAL:
|
|
|
|
PRINTER_OFFLINE, PRINTER_STATUS_OFFLINE,
|
|
PRINTER_PAPEROUT, PRINTER_STATUS_PAPER_OUT,
|
|
PRINTER_PAPER_JAM, PRINTER_STATUS_PAPER_JAM,
|
|
PRINTER_MANUAL_FEED, PRINTER_STATUS_MANUAL_FEED,
|
|
PRINTER_PAPER_PROBLEM, PRINTER_STATUS_PAPER_PROBLEM,
|
|
PRINTER_IO_ACTIVE, PRINTER_STATUS_IO_ACTIVE,
|
|
PRINTER_BUSY, PRINTER_STATUS_BUSY,
|
|
PRINTER_PRINTING, PRINTER_STATUS_PRINTING,
|
|
PRINTER_OUTPUT_BIN_FULL, PRINTER_STATUS_OUTPUT_BIN_FULL,
|
|
PRINTER_NOT_AVAILABLE, PRINTER_STATUS_NOT_AVAILABLE,
|
|
PRINTER_WAITING, PRINTER_STATUS_WAITING,
|
|
PRINTER_PROCESSING, PRINTER_STATUS_PROCESSING,
|
|
PRINTER_INITIALIZING, PRINTER_STATUS_INITIALIZING,
|
|
PRINTER_WARMING_UP, PRINTER_STATUS_WARMING_UP,
|
|
PRINTER_TONER_LOW, PRINTER_STATUS_TONER_LOW,
|
|
PRINTER_NO_TONER, PRINTER_STATUS_NO_TONER,
|
|
PRINTER_PAGE_PUNT, PRINTER_STATUS_PAGE_PUNT,
|
|
PRINTER_USER_INTERVENTION, PRINTER_STATUS_USER_INTERVENTION,
|
|
PRINTER_OUT_OF_MEMORY, PRINTER_STATUS_OUT_OF_MEMORY,
|
|
PRINTER_DOOR_OPEN, PRINTER_STATUS_DOOR_OPEN,
|
|
PRINTER_SERVER_UNKNOWN, PRINTER_STATUS_SERVER_UNKNOWN,
|
|
PRINTER_POWER_SAVE, PRINTER_STATUS_POWER_SAVE,
|
|
0, 0
|
|
};
|
|
|
|
DWORD ReadablePrinterStatusMappings[] = {
|
|
|
|
// INTERNAL: EXTERNAL:
|
|
|
|
PRINTER_PAUSED, PRINTER_STATUS_PAUSED,
|
|
PRINTER_PENDING_DELETION, PRINTER_STATUS_PENDING_DELETION,
|
|
|
|
PRINTER_OFFLINE, PRINTER_STATUS_OFFLINE,
|
|
PRINTER_PAPEROUT, PRINTER_STATUS_PAPER_OUT,
|
|
PRINTER_PAPER_JAM, PRINTER_STATUS_PAPER_JAM,
|
|
PRINTER_MANUAL_FEED, PRINTER_STATUS_MANUAL_FEED,
|
|
PRINTER_PAPER_PROBLEM, PRINTER_STATUS_PAPER_PROBLEM,
|
|
PRINTER_IO_ACTIVE, PRINTER_STATUS_IO_ACTIVE,
|
|
PRINTER_BUSY, PRINTER_STATUS_BUSY,
|
|
PRINTER_PRINTING, PRINTER_STATUS_PRINTING,
|
|
PRINTER_OUTPUT_BIN_FULL, PRINTER_STATUS_OUTPUT_BIN_FULL,
|
|
PRINTER_NOT_AVAILABLE, PRINTER_STATUS_NOT_AVAILABLE,
|
|
PRINTER_WAITING, PRINTER_STATUS_WAITING,
|
|
PRINTER_PROCESSING, PRINTER_STATUS_PROCESSING,
|
|
PRINTER_INITIALIZING, PRINTER_STATUS_INITIALIZING,
|
|
PRINTER_WARMING_UP, PRINTER_STATUS_WARMING_UP,
|
|
PRINTER_TONER_LOW, PRINTER_STATUS_TONER_LOW,
|
|
PRINTER_NO_TONER, PRINTER_STATUS_NO_TONER,
|
|
PRINTER_PAGE_PUNT, PRINTER_STATUS_PAGE_PUNT,
|
|
PRINTER_USER_INTERVENTION, PRINTER_STATUS_USER_INTERVENTION,
|
|
PRINTER_OUT_OF_MEMORY, PRINTER_STATUS_OUT_OF_MEMORY,
|
|
PRINTER_DOOR_OPEN, PRINTER_STATUS_DOOR_OPEN,
|
|
PRINTER_SERVER_UNKNOWN, PRINTER_STATUS_SERVER_UNKNOWN,
|
|
PRINTER_POWER_SAVE, PRINTER_STATUS_POWER_SAVE,
|
|
|
|
0, 0
|
|
};
|
|
|
|
DWORD
|
|
MapPrinterStatus(
|
|
DWORD Type,
|
|
DWORD SourceStatus)
|
|
{
|
|
DWORD TargetStatus;
|
|
PDWORD pMappings;
|
|
INT MapFrom;
|
|
INT MapTo;
|
|
|
|
if (Type == MAP_READABLE) {
|
|
|
|
MapFrom = PRINTER_STATUS_INTERNAL;
|
|
MapTo = PRINTER_STATUS_EXTERNAL;
|
|
|
|
pMappings = ReadablePrinterStatusMappings;
|
|
|
|
} else {
|
|
|
|
MapFrom = PRINTER_STATUS_EXTERNAL;
|
|
MapTo = PRINTER_STATUS_INTERNAL;
|
|
|
|
pMappings = SettablePrinterStatusMappings;
|
|
}
|
|
|
|
TargetStatus = 0;
|
|
|
|
while(*pMappings) {
|
|
|
|
if (SourceStatus & pMappings[MapFrom])
|
|
TargetStatus |= pMappings[MapTo];
|
|
|
|
pMappings += 2;
|
|
}
|
|
|
|
return TargetStatus;
|
|
}
|
|
|
|
DWORD
|
|
GetIniNetPrintSize(
|
|
PININETPRINT pIniNetPrint
|
|
)
|
|
{
|
|
return sizeof(PRINTER_INFO_1) +
|
|
wcslen(pIniNetPrint->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
Nulwcslen(pIniNetPrint->pDescription) +
|
|
Nulwcslen(pIniNetPrint->pComment);
|
|
}
|
|
|
|
DWORD
|
|
GetPrinterSize(
|
|
PINIPRINTER pIniPrinter,
|
|
DWORD Level,
|
|
DWORD Flags,
|
|
LPWSTR lpRemote,
|
|
LPDEVMODE pDevMode
|
|
)
|
|
{
|
|
DWORD cb;
|
|
DWORD cbNeeded;
|
|
LPWSTR pszPorts;
|
|
|
|
switch (Level) {
|
|
|
|
case STRESSINFOLEVEL:
|
|
cb = sizeof(PRINTER_INFO_STRESS) +
|
|
wcslen(pIniPrinter->pName)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
|
|
if( lpRemote ){
|
|
|
|
//
|
|
// Allocate space for ServerName "\\foobar" and the prefix
|
|
// for PrinterName "\\foobar\." The rest of PrinterName
|
|
// is allocated above.
|
|
//
|
|
// ServerName + NULL + ServerName +'\'
|
|
//
|
|
cb += 2 * wcslen(lpRemote) * sizeof(WCHAR) +
|
|
sizeof(WCHAR) + sizeof(WCHAR);
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
cb = sizeof(PRINTER_INFO_4) +
|
|
wcslen(pIniPrinter->pName)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
|
|
if( lpRemote ){
|
|
cb += 2 * wcslen(lpRemote) * sizeof(WCHAR) +
|
|
sizeof(WCHAR) + sizeof(WCHAR);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
|
|
//
|
|
// Local:
|
|
//
|
|
// "pName,pDriver,pLocation"
|
|
// "pName"
|
|
// "pComment"
|
|
//
|
|
// Remote:
|
|
//
|
|
// "pMachine\pName,pDriver,<pLocation>"
|
|
// "pMachine\pName"
|
|
// "pComment"
|
|
//
|
|
|
|
//
|
|
// Mandatory items, plus NULLs for _all_ strings.
|
|
// 2 * PrinterName +
|
|
// DriverName +
|
|
// 2 commas, 3 NULL terminators.
|
|
//
|
|
cb = 2 * wcslen( pIniPrinter->pName ) +
|
|
wcslen( pIniPrinter->pIniDriver->pName ) +
|
|
2 + 3;
|
|
//
|
|
// Add items that may be NULL.
|
|
//
|
|
|
|
if( pIniPrinter->pLocation ){
|
|
cb += wcslen( pIniPrinter->pLocation );
|
|
}
|
|
|
|
if( pIniPrinter->pComment ){
|
|
cb += wcslen( pIniPrinter->pComment );
|
|
}
|
|
|
|
//
|
|
// Remote case adds prefix.
|
|
// 2 * ( MachineName + BackSlash )
|
|
//
|
|
if( lpRemote ){
|
|
cb += 2 * ( wcslen( lpRemote ) + 1 );
|
|
}
|
|
|
|
//
|
|
// cb was a char count, convert to byte count.
|
|
//
|
|
cb *= sizeof( WCHAR );
|
|
cb += sizeof( PRINTER_INFO_1 );
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
cbNeeded = 0;
|
|
GetPrinterPorts(pIniPrinter, 0, &cbNeeded);
|
|
|
|
cb = sizeof(PRINTER_INFO_2) +
|
|
wcslen(pIniPrinter->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
Nulwcslen(pIniPrinter->pShareName) +
|
|
cbNeeded +
|
|
wcslen(pIniPrinter->pIniDriver->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
Nulwcslen(pIniPrinter->pComment) +
|
|
Nulwcslen(pIniPrinter->pLocation) +
|
|
Nulwcslen(pIniPrinter->pSepFile) +
|
|
wcslen(pIniPrinter->pIniPrintProc->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
Nulwcslen(pIniPrinter->pDatatype) +
|
|
Nulwcslen(pIniPrinter->pParameters);
|
|
|
|
if( lpRemote ){
|
|
cb += 2 * wcslen(lpRemote) * sizeof(WCHAR) +
|
|
sizeof(WCHAR) + sizeof(WCHAR);
|
|
}
|
|
|
|
if (pDevMode) {
|
|
|
|
cb += pDevMode->dmSize + pDevMode->dmDriverExtra;
|
|
cb = (cb + sizeof(ULONG_PTR)-1) & ~(sizeof(ULONG_PTR)-1);
|
|
}
|
|
|
|
if (pIniPrinter->pSecurityDescriptor) {
|
|
|
|
cb += GetSecurityDescriptorLength(pIniPrinter->pSecurityDescriptor);
|
|
cb = (cb + sizeof(ULONG_PTR)-1) & ~(sizeof(ULONG_PTR)-1);
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
cb = sizeof(PRINTER_INFO_3);
|
|
cb += GetSecurityDescriptorLength(pIniPrinter->pSecurityDescriptor);
|
|
cb = (cb + sizeof(ULONG_PTR)-1) & ~(sizeof(ULONG_PTR)-1);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
cbNeeded = 0;
|
|
GetPrinterPorts(pIniPrinter, 0, &cbNeeded);
|
|
|
|
cb = sizeof(PRINTER_INFO_5) +
|
|
wcslen(pIniPrinter->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
cbNeeded;
|
|
|
|
//
|
|
// Allocate space for just the PrinterName prefix:
|
|
// "\\server\."
|
|
//
|
|
if( lpRemote ){
|
|
cb += wcslen(lpRemote) * sizeof(WCHAR) +
|
|
sizeof(WCHAR);
|
|
}
|
|
break;
|
|
|
|
case 6:
|
|
cb = sizeof(PRINTER_INFO_6);
|
|
break;
|
|
|
|
case 7:
|
|
cb = sizeof(PRINTER_INFO_7);
|
|
cb += pIniPrinter->pszObjectGUID ? (wcslen(pIniPrinter->pszObjectGUID) + 1)*sizeof(WCHAR) : 0;
|
|
break;
|
|
|
|
default:
|
|
cb = 0;
|
|
break;
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
LPBYTE
|
|
CopyIniNetPrintToPrinter(
|
|
PININETPRINT pIniNetPrint,
|
|
LPBYTE pPrinterInfo,
|
|
LPBYTE pEnd
|
|
)
|
|
{
|
|
LPWSTR SourceStrings[sizeof(PRINTER_INFO_1)/sizeof(LPWSTR)];
|
|
LPWSTR *pSourceStrings=SourceStrings;
|
|
PPRINTER_INFO_1 pPrinterInfo1 = (PPRINTER_INFO_1)pPrinterInfo;
|
|
|
|
*pSourceStrings++=pIniNetPrint->pDescription;
|
|
*pSourceStrings++=pIniNetPrint->pName;
|
|
*pSourceStrings++=pIniNetPrint->pComment;
|
|
|
|
pEnd = PackStrings(SourceStrings, pPrinterInfo, PrinterInfo1Strings, pEnd);
|
|
|
|
pPrinterInfo1->Flags = PRINTER_ENUM_NAME;
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
/* CopyIniPrinterSecurityDescriptor
|
|
*
|
|
* Copies the security descriptor for the printer to the buffer provided
|
|
* on a call to GetPrinter. The portions of the security descriptor which
|
|
* will be copied are determined by the accesses granted when the printer
|
|
* was opened. If it was opened with both READ_CONTROL and ACCESS_SYSTEM_SECURITY,
|
|
* all of the security descriptor will be made available. Otherwise a
|
|
* partial descriptor is built containing those portions to which the caller
|
|
* has access.
|
|
*
|
|
* Parameters
|
|
*
|
|
* pIniPrinter - Spooler's private structure for this printer.
|
|
*
|
|
* Level - Should be 2 or 3. Any other will cause AV.
|
|
*
|
|
* pPrinterInfo - Pointer to the buffer to receive the PRINTER_INFO_*
|
|
* structure. The pSecurityDescriptor field will be filled in with
|
|
* a pointer to the security descriptor.
|
|
*
|
|
* pEnd - Current position in the buffer to receive the data.
|
|
* This will be decremented to point to the next free bit of the
|
|
* buffer and will be returned.
|
|
*
|
|
* GrantedAccess - An access mask used to determine how much of the
|
|
* security descriptor the caller has access to.
|
|
*
|
|
* Returns
|
|
*
|
|
* Updated position in the buffer.
|
|
*
|
|
* NULL if an error occurred copying the security descriptor.
|
|
* It is assumed that no other errors are possible.
|
|
*
|
|
*/
|
|
LPBYTE
|
|
CopyIniPrinterSecurityDescriptor(
|
|
PINIPRINTER pIniPrinter,
|
|
DWORD Level,
|
|
LPBYTE pPrinterInfo,
|
|
LPBYTE pEnd,
|
|
ACCESS_MASK GrantedAccess
|
|
)
|
|
{
|
|
PSECURITY_DESCRIPTOR pPartialSecurityDescriptor = NULL;
|
|
DWORD SecurityDescriptorLength = 0;
|
|
PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
|
|
PSECURITY_DESCRIPTOR *ppSecurityDescriptorCopy;
|
|
BOOL ErrorOccurred = FALSE;
|
|
|
|
if(!(AreAllAccessesGranted(GrantedAccess,
|
|
READ_CONTROL | ACCESS_SYSTEM_SECURITY)))
|
|
{
|
|
/* Caller doesn't have full access, so we'll have to build
|
|
* a partial descriptor:
|
|
*/
|
|
if(!BuildPartialSecurityDescriptor(GrantedAccess,
|
|
pIniPrinter->pSecurityDescriptor,
|
|
&pPartialSecurityDescriptor,
|
|
&SecurityDescriptorLength))
|
|
{
|
|
ErrorOccurred = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (pPartialSecurityDescriptor)
|
|
{
|
|
pSecurityDescriptor = pPartialSecurityDescriptor;
|
|
}
|
|
else
|
|
{
|
|
ErrorOccurred = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSecurityDescriptor = pIniPrinter->pSecurityDescriptor;
|
|
|
|
SecurityDescriptorLength = GetSecurityDescriptorLength(pSecurityDescriptor);
|
|
}
|
|
|
|
if (!ErrorOccurred)
|
|
{
|
|
pEnd -= SecurityDescriptorLength;
|
|
pEnd = (PBYTE) ALIGN_PTR_DOWN(pEnd);
|
|
|
|
switch( Level )
|
|
{
|
|
case 2:
|
|
ppSecurityDescriptorCopy =
|
|
&((LPPRINTER_INFO_2)pPrinterInfo)->pSecurityDescriptor;
|
|
break;
|
|
|
|
case 3:
|
|
ppSecurityDescriptorCopy =
|
|
&((LPPRINTER_INFO_3)pPrinterInfo)->pSecurityDescriptor;
|
|
break;
|
|
|
|
default:
|
|
|
|
ErrorOccurred = TRUE;
|
|
|
|
/* This should never happen */
|
|
DBGMSG( DBG_ERROR, ("Invalid level %d in CopyIniPrinterSecurityDescriptor\n", Level ));
|
|
|
|
break;
|
|
}
|
|
|
|
if (!ErrorOccurred) {
|
|
|
|
// Copy the descriptor into the buffer that will be returned:
|
|
|
|
*ppSecurityDescriptorCopy = (PSECURITY_DESCRIPTOR)pEnd;
|
|
|
|
//
|
|
// This is OK because GetPrinterSize has validated that the buffer
|
|
// is big enough.
|
|
//
|
|
memcpy(*ppSecurityDescriptorCopy, pSecurityDescriptor, SecurityDescriptorLength);
|
|
}
|
|
}
|
|
|
|
if (pPartialSecurityDescriptor)
|
|
{
|
|
FreeSplMem(pPartialSecurityDescriptor);
|
|
}
|
|
|
|
if (ErrorOccurred)
|
|
{
|
|
pEnd = NULL;
|
|
}
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
|
|
/* CopyIniPrinterToPrinter
|
|
*
|
|
* Copies the spooler's internal printer data to the caller's buffer,
|
|
* depending on the level of information requested.
|
|
*
|
|
* Parameters
|
|
*
|
|
* pIniPrinter - A pointer to the spooler's internal data structure
|
|
* for the printer concerned.
|
|
*
|
|
* Level - Level of information requested (1, 2 or 3). Any level
|
|
* other than those supported will cause the routine to return
|
|
* immediately.
|
|
*
|
|
* pPrinterInfo - Pointer to the buffer to receive the PRINTER_INFO_*
|
|
* structure.
|
|
*
|
|
* pEnd - Current position in the buffer to receive the data.
|
|
* This will be decremented to point to the next free bit of the
|
|
* buffer and will be returned.
|
|
*
|
|
* pSecondPrinter - If the printer has a port which is being controlled
|
|
* by a monitor, this parameter points to information retrieved
|
|
* about a network printer. This allows us, e.g., to return
|
|
* the number of jobs on the printer that the output of the
|
|
* printer is currently being directed to.
|
|
*
|
|
* Remote - Indicates whether the caller is remote. If so we have to
|
|
* include the machine name in the printer name returned.
|
|
*
|
|
* CopySecurityDescriptor - Indicates whether the security descriptor
|
|
* should be copied. The security descriptor should not be copied
|
|
* on EnumPrinters calls, because this API requires
|
|
* SERVER_ACCESS_ENUMERATE access, and we'd have to do an access
|
|
* check on every printer enumerated to determine how much of the
|
|
* security descriptor could be copied. This would be costly,
|
|
* and the caller would probably not need the information anyway.
|
|
*
|
|
* GrantedAccess - An access mask used to determine how much of the
|
|
* security descriptor the caller has access to.
|
|
*
|
|
*
|
|
* Returns
|
|
*
|
|
* A pointer to the point in the buffer reached after the requested
|
|
* data has been copied.
|
|
*
|
|
* If there was an error, the return value is NULL.
|
|
*
|
|
*
|
|
* Assumes
|
|
*
|
|
* The largest PRINTER_INFO_* structure is PRINTER_INFO_2.
|
|
*
|
|
*/
|
|
LPBYTE
|
|
CopyIniPrinterToPrinter(
|
|
PINIPRINTER pIniPrinter,
|
|
DWORD Level,
|
|
LPBYTE pPrinterInfo,
|
|
LPBYTE pEnd,
|
|
PPRINTER_INFO_2 pSecondPrinter2,
|
|
LPWSTR lpRemote, // contains this machine name, or NULL
|
|
BOOL CopySecurityDescriptor,
|
|
ACCESS_MASK GrantedAccess,
|
|
PDEVMODE pDevMode
|
|
)
|
|
{
|
|
LPWSTR SourceStrings[sizeof(PRINTER_INFO_2)/sizeof(LPWSTR)];
|
|
LPWSTR *pSourceStrings=SourceStrings;
|
|
DWORD Attributes;
|
|
|
|
//
|
|
// Max string: "\\Computer\Printer,Driver,Location"
|
|
//
|
|
|
|
DWORD dwRet;
|
|
PWSTR pszString = NULL;
|
|
WCHAR string[MAX_PRINTER_BROWSE_NAME];
|
|
DWORD dwLength;
|
|
WCHAR printerString[ MAX_UNC_PRINTER_NAME ];
|
|
LPWSTR pszPorts;
|
|
|
|
PPRINTER_INFO_3 pPrinter3 = (PPRINTER_INFO_3)pPrinterInfo;
|
|
PPRINTER_INFO_2 pPrinter2 = (PPRINTER_INFO_2)pPrinterInfo;
|
|
PPRINTER_INFO_1 pPrinter1 = (PPRINTER_INFO_1)pPrinterInfo;
|
|
PPRINTER_INFO_4 pPrinter4 = (PPRINTER_INFO_4)pPrinterInfo;
|
|
PPRINTER_INFO_5 pPrinter5 = (PPRINTER_INFO_5)pPrinterInfo;
|
|
PPRINTER_INFO_6 pPrinter6 = (PPRINTER_INFO_6)pPrinterInfo;
|
|
PPRINTER_INFO_7 pPrinter7 = (PPRINTER_INFO_7)pPrinterInfo;
|
|
PPRINTER_INFO_STRESS pPrinter0 = (PPRINTER_INFO_STRESS)pPrinterInfo;
|
|
PSECURITY_DESCRIPTOR pPartialSecurityDescriptor = NULL;
|
|
DWORD *pOffsets;
|
|
SYSTEM_INFO si;
|
|
DWORD cbNeeded;
|
|
|
|
switch (Level) {
|
|
|
|
case STRESSINFOLEVEL:
|
|
|
|
pOffsets = PrinterInfoStressStrings;
|
|
break;
|
|
|
|
case 4:
|
|
|
|
pOffsets = PrinterInfo4Strings;
|
|
break;
|
|
|
|
case 1:
|
|
|
|
pOffsets = PrinterInfo1Strings;
|
|
break;
|
|
|
|
case 2:
|
|
pOffsets = PrinterInfo2Strings;
|
|
break;
|
|
|
|
case 3:
|
|
pOffsets = PrinterInfo3Strings;
|
|
break;
|
|
|
|
case 5:
|
|
pOffsets = PrinterInfo5Strings;
|
|
break;
|
|
|
|
case 6:
|
|
pOffsets = PrinterInfo6Strings;
|
|
break;
|
|
|
|
case 7:
|
|
pOffsets = PrinterInfo7Strings;
|
|
break;
|
|
|
|
default:
|
|
return pEnd;
|
|
}
|
|
|
|
//
|
|
// If it's a cluster printer, it always appears remote.
|
|
//
|
|
Attributes = pIniPrinter->Attributes;
|
|
|
|
Attributes |= ( pIniPrinter->pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER ) ?
|
|
PRINTER_ATTRIBUTE_NETWORK :
|
|
PRINTER_ATTRIBUTE_LOCAL;
|
|
|
|
switch (Level) {
|
|
|
|
case STRESSINFOLEVEL:
|
|
|
|
if (lpRemote) {
|
|
|
|
if (!BoolFromHResult(StringCchPrintf(string, COUNTOF(string), L"%ws\\%ws", lpRemote, pIniPrinter->pName))) {
|
|
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
*pSourceStrings++=string;
|
|
*pSourceStrings++=lpRemote;
|
|
|
|
} else {
|
|
*pSourceStrings++=pIniPrinter->pName;
|
|
*pSourceStrings++=NULL;
|
|
}
|
|
|
|
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter0, pOffsets, pEnd);
|
|
|
|
pPrinter0->cJobs = pIniPrinter->cJobs;
|
|
pPrinter0->cTotalJobs = pIniPrinter->cTotalJobs;
|
|
pPrinter0->cTotalBytes = pIniPrinter->cTotalBytes.LowPart;
|
|
pPrinter0->dwHighPartTotalBytes = pIniPrinter->cTotalBytes.HighPart;
|
|
pPrinter0->stUpTime = pIniPrinter->stUpTime;
|
|
pPrinter0->MaxcRef = pIniPrinter->MaxcRef;
|
|
pPrinter0->cTotalPagesPrinted = pIniPrinter->cTotalPagesPrinted;
|
|
pPrinter0->dwGetVersion = GetVersion();
|
|
#if DBG
|
|
pPrinter0->fFreeBuild = FALSE;
|
|
#else
|
|
pPrinter0->fFreeBuild = TRUE;
|
|
#endif
|
|
GetSystemInfo(&si);
|
|
pPrinter0->dwProcessorType = si.dwProcessorType;
|
|
pPrinter0->dwNumberOfProcessors = si.dwNumberOfProcessors;
|
|
pPrinter0->cSpooling = pIniPrinter->cSpooling;
|
|
pPrinter0->cMaxSpooling = pIniPrinter->cMaxSpooling;
|
|
pPrinter0->cRef = pIniPrinter->cRef;
|
|
pPrinter0->cErrorOutOfPaper = pIniPrinter->cErrorOutOfPaper;
|
|
pPrinter0->cErrorNotReady = pIniPrinter->cErrorNotReady;
|
|
pPrinter0->cJobError = pIniPrinter->cJobError;
|
|
pPrinter0->cChangeID = pIniPrinter->cChangeID;
|
|
pPrinter0->dwLastError = pIniPrinter->dwLastError;
|
|
|
|
pPrinter0->Status = MapPrinterStatus(MAP_READABLE,
|
|
pIniPrinter->Status) |
|
|
pIniPrinter->PortStatus;
|
|
|
|
pPrinter0->cEnumerateNetworkPrinters = pIniPrinter->pIniSpooler->cEnumerateNetworkPrinters;
|
|
pPrinter0->cAddNetPrinters = pIniPrinter->pIniSpooler->cAddNetPrinters;
|
|
|
|
pPrinter0->wProcessorArchitecture = si.wProcessorArchitecture;
|
|
pPrinter0->wProcessorLevel = si.wProcessorLevel;
|
|
pPrinter0->cRefIC = pIniPrinter->cRefIC;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if (lpRemote) {
|
|
|
|
if (!BoolFromHResult(StringCchPrintf(string, COUNTOF(string), L"%ws\\%ws", lpRemote, pIniPrinter->pName))) {
|
|
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
*pSourceStrings++=string;
|
|
*pSourceStrings++= lpRemote;
|
|
|
|
} else {
|
|
*pSourceStrings++=pIniPrinter->pName;
|
|
*pSourceStrings++=NULL;
|
|
}
|
|
|
|
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter4, pOffsets, pEnd);
|
|
|
|
//
|
|
// Add additional info later
|
|
//
|
|
pPrinter4->Attributes = Attributes;
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (lpRemote) {
|
|
|
|
dwRet = StrCatAlloc(&pszString,
|
|
lpRemote,
|
|
L"\\",
|
|
pIniPrinter->pName,
|
|
L",",
|
|
pIniPrinter->pIniDriver->pName,
|
|
L",",
|
|
pIniPrinter->pLocation ?
|
|
pIniPrinter->pLocation :
|
|
szNull,
|
|
NULL);
|
|
|
|
if (dwRet != ERROR_SUCCESS) {
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
if (!BoolFromHResult(StringCchPrintf(printerString, COUNTOF(printerString), L"%ws\\%ws", lpRemote, pIniPrinter->pName))) {
|
|
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
dwRet = StrCatAlloc(&pszString,
|
|
pIniPrinter->pName,
|
|
L",",
|
|
pIniPrinter->pIniDriver->pName,
|
|
L",",
|
|
pIniPrinter->pLocation ?
|
|
pIniPrinter->pLocation :
|
|
szNull,
|
|
NULL);
|
|
if (dwRet != ERROR_SUCCESS) {
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
if (!BoolFromHResult(StringCchCopy(printerString, COUNTOF(printerString), pIniPrinter->pName))) {
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pSourceStrings++=pszString;
|
|
*pSourceStrings++=printerString;
|
|
*pSourceStrings++=pIniPrinter->pComment;
|
|
|
|
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter1, pOffsets, pEnd);
|
|
|
|
FreeSplStr(pszString);
|
|
|
|
pPrinter1->Flags = PRINTER_ENUM_ICON8;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (lpRemote) {
|
|
*pSourceStrings++= lpRemote;
|
|
|
|
if (!BoolFromHResult(StringCchPrintf(string, COUNTOF(string), L"%ws\\%ws", lpRemote, pIniPrinter->pName))){
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
*pSourceStrings++=string;
|
|
|
|
} else {
|
|
*pSourceStrings++=NULL;
|
|
*pSourceStrings++=pIniPrinter->pName;
|
|
}
|
|
|
|
*pSourceStrings++=pIniPrinter->pShareName;
|
|
|
|
cbNeeded = 0;
|
|
GetPrinterPorts(pIniPrinter, 0, &cbNeeded);
|
|
|
|
if (pszPorts = AllocSplMem(cbNeeded)) {
|
|
|
|
GetPrinterPorts(pIniPrinter, pszPorts, &cbNeeded);
|
|
|
|
*pSourceStrings++=pszPorts;
|
|
*pSourceStrings++=pIniPrinter->pIniDriver->pName;
|
|
*pSourceStrings++=pIniPrinter->pComment;
|
|
*pSourceStrings++=pIniPrinter->pLocation;
|
|
*pSourceStrings++=pIniPrinter->pSepFile;
|
|
*pSourceStrings++=pIniPrinter->pIniPrintProc->pName;
|
|
*pSourceStrings++=pIniPrinter->pDatatype;
|
|
*pSourceStrings++=pIniPrinter->pParameters;
|
|
|
|
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter2, pOffsets, pEnd);
|
|
|
|
FreeSplMem(pszPorts);
|
|
}
|
|
else {
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
|
|
if (pDevMode) {
|
|
|
|
pEnd -= pDevMode->dmSize + pDevMode->dmDriverExtra;
|
|
|
|
pEnd = (PBYTE)ALIGN_PTR_DOWN(pEnd);
|
|
|
|
pPrinter2->pDevMode=(LPDEVMODE)pEnd;
|
|
|
|
//
|
|
// This is OK because GetPrinterSize has validated the buffer is big enough.
|
|
//
|
|
memcpy(pPrinter2->pDevMode, pDevMode, pDevMode->dmSize + pDevMode->dmDriverExtra);
|
|
|
|
//
|
|
// In the remote case, append the name of the server
|
|
// in the devmode.dmDeviceName. This allows dmDeviceName
|
|
// to always match win.ini's [devices] section.
|
|
//
|
|
FixDevModeDeviceName(lpRemote ? string : pIniPrinter->pName,
|
|
pPrinter2->pDevMode,
|
|
pIniPrinter->cbDevMode);
|
|
} else {
|
|
|
|
pPrinter2->pDevMode=NULL;
|
|
}
|
|
|
|
|
|
|
|
pPrinter2->Attributes = Attributes;
|
|
pPrinter2->Priority = pIniPrinter->Priority;
|
|
pPrinter2->DefaultPriority = pIniPrinter->DefaultPriority;
|
|
pPrinter2->StartTime = pIniPrinter->StartTime;
|
|
pPrinter2->UntilTime = pIniPrinter->UntilTime;
|
|
|
|
if (pSecondPrinter2) {
|
|
|
|
pPrinter2->cJobs = pSecondPrinter2->cJobs;
|
|
pPrinter2->Status = pSecondPrinter2->Status;
|
|
|
|
if( pIniPrinter->Status & PRINTER_PENDING_DELETION ){
|
|
pPrinter2->Status |= PRINTER_STATUS_PENDING_DELETION;
|
|
}
|
|
|
|
} else {
|
|
|
|
pPrinter2->cJobs=pIniPrinter->cJobs;
|
|
|
|
pPrinter2->Status = MapPrinterStatus(MAP_READABLE,
|
|
pIniPrinter->Status) |
|
|
pIniPrinter->PortStatus;
|
|
}
|
|
|
|
pPrinter2->AveragePPM=pIniPrinter->AveragePPM;
|
|
|
|
if( CopySecurityDescriptor ) {
|
|
|
|
pEnd = CopyIniPrinterSecurityDescriptor(pIniPrinter,
|
|
Level,
|
|
pPrinterInfo,
|
|
pEnd,
|
|
GrantedAccess);
|
|
} else {
|
|
|
|
pPrinter2->pSecurityDescriptor = NULL;
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
pEnd = CopyIniPrinterSecurityDescriptor(pIniPrinter,
|
|
Level,
|
|
pPrinterInfo,
|
|
pEnd,
|
|
GrantedAccess);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
if (lpRemote) {
|
|
|
|
if (!BoolFromHResult(StringCchPrintf(string, COUNTOF(string), L"%ws\\%ws", lpRemote, pIniPrinter->pName))) {
|
|
pEnd = NULL;
|
|
break;
|
|
}
|
|
|
|
*pSourceStrings++=string;
|
|
} else {
|
|
*pSourceStrings++=pIniPrinter->pName;
|
|
}
|
|
|
|
cbNeeded = 0;
|
|
GetPrinterPorts(pIniPrinter, 0, &cbNeeded);
|
|
|
|
if (pszPorts = AllocSplMem(cbNeeded)) {
|
|
|
|
GetPrinterPorts(pIniPrinter, pszPorts, &cbNeeded);
|
|
|
|
*pSourceStrings++ = pszPorts;
|
|
|
|
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter5, pOffsets, pEnd);
|
|
|
|
pPrinter5->Attributes = Attributes;
|
|
pPrinter5->DeviceNotSelectedTimeout = pIniPrinter->dnsTimeout;
|
|
pPrinter5->TransmissionRetryTimeout = pIniPrinter->txTimeout;
|
|
|
|
FreeSplMem(pszPorts);
|
|
}
|
|
else
|
|
pEnd = NULL;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
if (pSecondPrinter2) {
|
|
pPrinter6->dwStatus = pSecondPrinter2->Status;
|
|
|
|
if( pIniPrinter->Status & PRINTER_PENDING_DELETION ){
|
|
pPrinter6->dwStatus |= PRINTER_STATUS_PENDING_DELETION;
|
|
}
|
|
} else {
|
|
pPrinter6->dwStatus = MapPrinterStatus(MAP_READABLE,
|
|
pIniPrinter->Status) |
|
|
pIniPrinter->PortStatus;
|
|
}
|
|
break;
|
|
|
|
case 7:
|
|
|
|
*pSourceStrings++ = pIniPrinter->pszObjectGUID;
|
|
|
|
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter7, pOffsets, pEnd);
|
|
|
|
if ( pIniPrinter->pIniSpooler->SpoolerFlags & SPL_TYPE_CACHE) {
|
|
|
|
//
|
|
// For connections, we rely directly on dwAction. The caching code
|
|
// is the only one that updates dwAction in RefreshPrinterInfo7.
|
|
//
|
|
pPrinter7->dwAction = pIniPrinter->dwAction;
|
|
|
|
} else {
|
|
|
|
if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
|
|
pPrinter7->dwAction = DSPRINT_PUBLISH;
|
|
if (!pIniPrinter->pszObjectGUID ||
|
|
pIniPrinter->DsKeyUpdate ||
|
|
pIniPrinter->DsKeyUpdateForeground) {
|
|
pPrinter7->dwAction |= DSPRINT_PENDING;
|
|
}
|
|
} else {
|
|
pPrinter7->dwAction = DSPRINT_UNPUBLISH;
|
|
if (pIniPrinter->pszObjectGUID ||
|
|
(pIniPrinter->DsKeyUpdate & DS_KEY_UNPUBLISH) ||
|
|
(pIniPrinter->DsKeyUpdateForeground & DS_KEY_UNPUBLISH)) {
|
|
pPrinter7->dwAction |= DSPRINT_PENDING;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return pEnd;
|
|
}
|
|
|
|
return pEnd;
|
|
}
|
|
|
|
BOOL
|
|
SplGetPrinter(
|
|
HANDLE hPrinter,
|
|
DWORD Level,
|
|
LPBYTE pPrinter,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded
|
|
)
|
|
{
|
|
PSPOOL pSpool = (PSPOOL)hPrinter;
|
|
BOOL AccessIsGranted = FALSE; // Must intialize
|
|
PPRINTER_INFO_2 pSecondPrinter=NULL;
|
|
LPBYTE pEnd;
|
|
LPWSTR lpRemote;
|
|
BOOL bReturn = FALSE;
|
|
PDEVMODE pDevMode = NULL;
|
|
PINIPRINTER pIniPrinter;
|
|
BOOL bNt3xClient;
|
|
PWSTR pszCN = NULL, pszDN = NULL;
|
|
DWORD dwRet;
|
|
|
|
|
|
EnterSplSem();
|
|
|
|
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
pIniPrinter = pSpool->pIniPrinter;
|
|
bNt3xClient = (pSpool->TypeofHandle & PRINTER_HANDLE_3XCLIENT);
|
|
|
|
//
|
|
// If Nt3x client we will converted devmode. If driver can't convert we will not return devmode
|
|
//
|
|
if ( bNt3xClient && Level == 2 && pIniPrinter->pDevMode ) {
|
|
|
|
//
|
|
// Call driver to get a Nt3x DevMode (if fails no devmode is given)
|
|
//
|
|
if (wcsstr(pSpool->pName, gszDrvConvert))
|
|
pDevMode = ConvertDevModeToSpecifiedVersion(pIniPrinter,
|
|
pIniPrinter->pDevMode,
|
|
NULL,
|
|
pSpool->pName,
|
|
NT3X_VERSION);
|
|
else
|
|
pDevMode = ConvertDevModeToSpecifiedVersion(pIniPrinter,
|
|
pIniPrinter->pDevMode,
|
|
NULL,
|
|
NULL,
|
|
NT3X_VERSION);
|
|
}
|
|
|
|
SplInSem();
|
|
|
|
if (( pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_DATA ) ||
|
|
( pSpool->pIniSpooler != pLocalIniSpooler )) {
|
|
|
|
lpRemote = pSpool->pFullMachineName;
|
|
|
|
} else {
|
|
|
|
lpRemote = NULL;
|
|
|
|
}
|
|
|
|
|
|
switch (Level) {
|
|
|
|
case STRESSINFOLEVEL:
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
|
|
if ( !AccessGranted(SPOOLER_OBJECT_PRINTER,
|
|
PRINTER_ACCESS_USE,
|
|
pSpool) ) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
goto Cleanup;
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (!AreAnyAccessesGranted(pSpool->GrantedAccess,
|
|
READ_CONTROL | ACCESS_SYSTEM_SECURITY)) {
|
|
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
goto Cleanup;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
if (pSpool->pIniPort && !(pSpool->pIniPort->Status & PP_MONITOR)) {
|
|
|
|
HANDLE hPort = pSpool->hPort;
|
|
|
|
if (hPort == INVALID_PORT_HANDLE) {
|
|
|
|
DBGMSG(DBG_WARNING, ("GetPrinter called with bad port handle. Setting error %d\n",
|
|
pSpool->OpenPortError));
|
|
|
|
//
|
|
// If this value is 0, then when we return GetLastError,
|
|
// the client will think we succeeded.
|
|
//
|
|
SPLASSERT(pSpool->OpenPortError);
|
|
|
|
goto PartialSuccess;
|
|
}
|
|
|
|
|
|
LeaveSplSem();
|
|
|
|
if ((Level == 2 || Level == 6)) {
|
|
|
|
if (!RetrieveMasqPrinterInfo(pSpool, &pSecondPrinter)) {
|
|
goto CleanupFromOutsideSplSem;
|
|
}
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
/* Re-validate the handle, since it might possibly have been closed
|
|
* while we were outside the semaphore:
|
|
*/
|
|
if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
PartialSuccess:
|
|
|
|
*pcbNeeded = GetPrinterSize(pIniPrinter, Level, 0, lpRemote,
|
|
bNt3xClient ? pDevMode : pIniPrinter->pDevMode);
|
|
|
|
|
|
if (*pcbNeeded > cbBuf) {
|
|
|
|
DBGMSG(DBG_TRACE, ("SplGetPrinter Failure with ERROR_INSUFFICIENT_BUFFER cbBuf is %d and pcbNeeded is %d\n", cbBuf, *pcbNeeded));
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
goto Cleanup;
|
|
}
|
|
|
|
pEnd = CopyIniPrinterToPrinter(pIniPrinter, Level, pPrinter,
|
|
pPrinter+cbBuf, pSecondPrinter,
|
|
lpRemote,
|
|
TRUE, pSpool->GrantedAccess,
|
|
bNt3xClient ? pDevMode : pIniPrinter->pDevMode);
|
|
|
|
if ( pEnd != NULL)
|
|
bReturn = TRUE;
|
|
|
|
Cleanup:
|
|
|
|
LeaveSplSem();
|
|
|
|
CleanupFromOutsideSplSem:
|
|
|
|
SplOutSem();
|
|
FreeSplMem(pSecondPrinter);
|
|
|
|
FreeSplMem(pDevMode);
|
|
|
|
if ( bReturn == FALSE ) {
|
|
|
|
SPLASSERT(GetLastError() != ERROR_SUCCESS);
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
EnumerateNetworkPrinters(
|
|
LPBYTE pPrinter,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
PININETPRINT pIniNetPrint;
|
|
DWORD cb;
|
|
LPBYTE pEnd;
|
|
BOOL bReturnValue = FALSE;
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// All network printers reside in pLocalIniSpooler to avoid
|
|
// duplicates.
|
|
//
|
|
RemoveOldNetPrinters( NULL, pLocalIniSpooler );
|
|
|
|
//
|
|
// If the Server has not been up long enough, then fail
|
|
// so the client will ask another Server for the Browse List.
|
|
//
|
|
|
|
if ( bNetInfoReady == FALSE ) {
|
|
|
|
SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
goto Done;
|
|
}
|
|
|
|
cb = 0;
|
|
|
|
pIniNetPrint = pIniSpooler->pIniNetPrint;
|
|
|
|
while (pIniNetPrint) {
|
|
|
|
cb += GetIniNetPrintSize( pIniNetPrint );
|
|
pIniNetPrint = pIniNetPrint->pNext;
|
|
}
|
|
|
|
*pcbNeeded = cb;
|
|
|
|
if (cb > cbBuf) {
|
|
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
goto Done;
|
|
}
|
|
|
|
pIniNetPrint = pIniSpooler->pIniNetPrint;
|
|
pEnd = pPrinter + cbBuf;
|
|
|
|
while ( pIniNetPrint ) {
|
|
|
|
pEnd = CopyIniNetPrintToPrinter( pIniNetPrint, pPrinter, pEnd );
|
|
(*pcReturned)++;
|
|
pPrinter += sizeof(PRINTER_INFO_1);
|
|
pIniNetPrint = pIniNetPrint->pNext;
|
|
|
|
}
|
|
|
|
if ( *pcReturned == 0 ) {
|
|
|
|
bNetInfoReady = FALSE;
|
|
FirstAddNetPrinterTickCount = 0;
|
|
SetLastError( ERROR_CAN_NOT_COMPLETE );
|
|
|
|
DBGMSG( DBG_TRACE, ("EnumerateNetworkPrinters returning ERROR_CAN_NOT_COMPELTE becase there is no browse list\n"));
|
|
|
|
} else {
|
|
|
|
pIniSpooler->cEnumerateNetworkPrinters++; // Stats only
|
|
bReturnValue = TRUE;
|
|
|
|
DBGMSG( DBG_TRACE, (" EnumerateNetworkPrnters called %d times returning %d printers\n", pIniSpooler->cEnumerateNetworkPrinters, *pcReturned ));
|
|
}
|
|
|
|
Done:
|
|
LeaveSplSem();
|
|
SplOutSem();
|
|
return bReturnValue;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
UpdateSpoolersRef
|
|
|
|
Routine Description:
|
|
|
|
Does and AddRef or a DecRef on all pIniSpooler matching a certain criteria
|
|
|
|
Arguments:
|
|
|
|
SpoolerType - Type of pIniSpoolers which to addref/decref
|
|
(Ex. SPL_TYPE_CLUSTER | SPL_TYPE_LOCAL)
|
|
bAddRef - TRUE means AddRef, FALSE means DecRef
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
VOID
|
|
UpdateSpoolersRef(
|
|
IN DWORD SpoolerType,
|
|
IN BOOL bAddRef
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// AddRef or DecRef all local and cluster spoolers
|
|
//
|
|
for (pIniSpooler = pLocalIniSpooler; pIniSpooler; pIniSpooler = pIniSpooler->pIniNextSpooler)
|
|
{
|
|
if (pIniSpooler->SpoolerFlags & SpoolerType)
|
|
{
|
|
if (bAddRef)
|
|
{
|
|
INCSPOOLERREF(pIniSpooler);
|
|
}
|
|
else
|
|
{
|
|
DECSPOOLERREF(pIniSpooler);
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveSplSem();
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name
|
|
|
|
SplEnumAllClusterPrinters
|
|
|
|
Routine Description:
|
|
|
|
Enumerates all printers in the local spooler and all cluster
|
|
spoolers hosted by localspl at the time of the call.
|
|
|
|
Arguments:
|
|
|
|
InputFlags - combination of ENUM_PRINTER_xxx
|
|
pszInputName - name of the print provider
|
|
Level - level of the call
|
|
pPrinter - buffer to hold the PRINTER_INFO_xxx structures
|
|
cbInputBufSize - size of pPrinter buffer
|
|
pcbNeeded - bytes required to hold all the printer info structures
|
|
pcReturned - number of structures returned by this function
|
|
|
|
Return Value:
|
|
|
|
TRUE - cal succeeded
|
|
FALSE - an error occurred, the function sets the last error
|
|
|
|
--*/
|
|
BOOL
|
|
SplEnumAllClusterPrinters(
|
|
DWORD InputFlags,
|
|
LPWSTR pszInputName,
|
|
DWORD Level,
|
|
LPBYTE pPrinter,
|
|
DWORD cbInputBufSize,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned
|
|
)
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
|
|
DWORD cbBuf = cbInputBufSize;
|
|
DWORD cTotalReturned = 0;
|
|
DWORD cbTotalNeeded = 0;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD cbStruct;
|
|
|
|
switch (Level)
|
|
{
|
|
case STRESSINFOLEVEL:
|
|
cbStruct = sizeof(PRINTER_INFO_STRESS);
|
|
break;
|
|
|
|
case 1:
|
|
cbStruct = sizeof(PRINTER_INFO_1);
|
|
break;
|
|
|
|
case 2:
|
|
cbStruct = sizeof(PRINTER_INFO_2);
|
|
break;
|
|
|
|
case 4:
|
|
cbStruct = sizeof(PRINTER_INFO_4);
|
|
break;
|
|
|
|
case 5:
|
|
cbStruct = sizeof(PRINTER_INFO_5);
|
|
break;
|
|
|
|
default:
|
|
dwError = ERROR_INVALID_LEVEL;
|
|
}
|
|
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// AddRef all ini spoolers
|
|
//
|
|
UpdateSpoolersRef(SPL_TYPE_LOCAL | SPL_TYPE_CLUSTER, TRUE);
|
|
|
|
//
|
|
// Enumerate all the printers
|
|
//
|
|
for (pIniSpooler = pLocalIniSpooler; pIniSpooler; pIniSpooler = pIniSpooler->pIniNextSpooler)
|
|
{
|
|
if (pIniSpooler->SpoolerFlags & (SPL_TYPE_LOCAL | SPL_TYPE_CLUSTER))
|
|
{
|
|
DWORD cReturned;
|
|
DWORD cbNeeded;
|
|
DWORD Flags = InputFlags;
|
|
LPWSTR pszName = pszInputName;
|
|
|
|
//
|
|
// For clusters force the printer name to be fully qualified
|
|
//
|
|
if (pIniSpooler->SpoolerFlags & SPL_TYPE_CLUSTER)
|
|
{
|
|
Flags |= PRINTER_ENUM_NAME;
|
|
pszName = pIniSpooler->pMachineName;
|
|
}
|
|
|
|
if (SplEnumPrinters(Flags,
|
|
pszName,
|
|
Level,
|
|
pPrinter,
|
|
cbBuf,
|
|
&cbNeeded,
|
|
&cReturned,
|
|
pIniSpooler))
|
|
{
|
|
cTotalReturned += cReturned;
|
|
cbBuf -= cbNeeded;
|
|
pPrinter += cReturned * cbStruct;
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if (dwError == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
cbBuf = 0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We cannot continue on an error different than insufficient buffer
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
cbTotalNeeded += cbNeeded;
|
|
}
|
|
}
|
|
|
|
//
|
|
// DecRef all ini spoolers
|
|
//
|
|
UpdateSpoolersRef(SPL_TYPE_LOCAL | SPL_TYPE_CLUSTER, FALSE);
|
|
|
|
//
|
|
// Update out variables
|
|
//
|
|
if (dwError == ERROR_SUCCESS)
|
|
{
|
|
*pcbNeeded = cbTotalNeeded;
|
|
*pcReturned = cTotalReturned;
|
|
}
|
|
else if (dwError == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
*pcbNeeded = cbTotalNeeded;
|
|
}
|
|
else
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
}
|
|
|
|
return dwError == ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
|
|
EnumPrinters can be called with the following combinations:
|
|
|
|
Flags Name Meaning
|
|
|
|
PRINTER_ENUM_LOCAL NULL Enumerate all Printers on this machine
|
|
|
|
PRINTER_ENUM_NAME MachineName Enumerate all Printers on this machine
|
|
|
|
PRINTER_ENUM_NAME | MachineName Enumerate all shared Printers on this
|
|
PRINTER_ENUM_SHARED MachineName machine
|
|
|
|
PRINTER_ENUM_NETWORK MachineName Enumerate all added remote printers
|
|
|
|
PRINTER_ENUM_REMOTE ? Return error - let win32spl handle it
|
|
|
|
PRINTER_ENUM_NAME NULL Give back Print Providor name
|
|
|
|
PRINTER_ENUM_NAME "Windows NT Local Print Providor"
|
|
same as PRINTER_ENUM_LOCAL
|
|
|
|
It is not an error if no known flag is specified.
|
|
In this case we just return TRUE without any data
|
|
(This is so that other print providers may define
|
|
their own flags.)
|
|
|
|
*/
|
|
|
|
BOOL
|
|
LocalEnumPrinters(
|
|
DWORD Flags,
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pPrinter,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned
|
|
)
|
|
{
|
|
BOOL bReturn = ROUTER_UNKNOWN;
|
|
|
|
if (Flags & PRINTER_ENUM_CLUSTER)
|
|
{
|
|
bReturn = SplEnumAllClusterPrinters(Flags,
|
|
pName,
|
|
Level,
|
|
pPrinter,
|
|
cbBuf,
|
|
pcbNeeded,
|
|
pcReturned);
|
|
}
|
|
else
|
|
{
|
|
PINISPOOLER pIniSpooler;
|
|
|
|
//
|
|
// Mask cluster flag
|
|
//
|
|
Flags &= ~PRINTER_ENUM_CLUSTER;
|
|
|
|
pIniSpooler = FindSpoolerByNameIncRef( pName, NULL );
|
|
|
|
if (pIniSpooler)
|
|
{
|
|
bReturn = SplEnumPrinters(Flags,
|
|
pName,
|
|
Level,
|
|
pPrinter,
|
|
cbBuf,
|
|
pcbNeeded,
|
|
pcReturned,
|
|
pIniSpooler);
|
|
|
|
FindSpoolerByNameDecRef(pIniSpooler);
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumThisPrinter(
|
|
DWORD Flags,
|
|
PINIPRINTER pIniPrinter,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
|
|
|
|
//
|
|
// If they only want shared Printers
|
|
//
|
|
if ( (Flags & PRINTER_ENUM_SHARED) &&
|
|
!(pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED) )
|
|
return FALSE;
|
|
|
|
//
|
|
// Only allow them to see printers which are being deleted if they have jobs
|
|
// This allows remote admin to work well.
|
|
if ( (pIniPrinter->Status & PRINTER_PENDING_DELETION) &&
|
|
pIniPrinter->cJobs == 0 )
|
|
return FALSE;
|
|
|
|
//
|
|
// Don't count printers which are partially created
|
|
//
|
|
if ( pIniPrinter->Status & PRINTER_PENDING_CREATION )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SplEnumPrinters(
|
|
DWORD Flags,
|
|
LPWSTR Name,
|
|
DWORD Level,
|
|
LPBYTE pPrinter,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned,
|
|
PINISPOOLER pIniSpooler
|
|
)
|
|
{
|
|
PINIPRINTER pIniPrinter;
|
|
PPRINTER_INFO_1 pPrinter1=(PPRINTER_INFO_1)pPrinter;
|
|
DWORD cb;
|
|
LPBYTE pEnd;
|
|
LPWSTR lpRemote;
|
|
|
|
|
|
*pcbNeeded = 0;
|
|
*pcReturned = 0;
|
|
|
|
if ( !ValidateObjectAccess( SPOOLER_OBJECT_SERVER,
|
|
SERVER_ACCESS_ENUMERATE,
|
|
NULL, NULL, pIniSpooler )) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if ( Flags & PRINTER_ENUM_NAME ) {
|
|
if ( Name && *Name ) {
|
|
if (lstrcmpi(Name, szPrintProvidorName) && !MyName( Name, pIniSpooler)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// If it's PRINTER_ENUM_NAME of our name,
|
|
// do the same as PRINTER_ENUM_LOCAL:
|
|
|
|
Flags |= PRINTER_ENUM_LOCAL;
|
|
|
|
// Also if it is for us then ignore the REMOTE flag.
|
|
// Otherwise the call will get passed to Win32Spl which
|
|
// will end up calling us back forever.
|
|
|
|
Flags &= ~PRINTER_ENUM_REMOTE;
|
|
}
|
|
}
|
|
|
|
if ( Flags & PRINTER_ENUM_REMOTE ) {
|
|
SetLastError( ERROR_INVALID_NAME );
|
|
return FALSE;
|
|
}
|
|
|
|
lpRemote = NULL;
|
|
|
|
if ( Name && *Name ) {
|
|
|
|
if ( MyName( Name, pIniSpooler ) ) {
|
|
lpRemote = Name;
|
|
}
|
|
}
|
|
|
|
if ((Level == 1) && (Flags & PRINTER_ENUM_NETWORK)) {
|
|
|
|
SplOutSem();
|
|
return EnumerateNetworkPrinters( pPrinter, cbBuf, pcbNeeded, pcReturned, pIniSpooler );
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
if ((Level == 1 ) && (Flags & PRINTER_ENUM_NAME) && !Name) {
|
|
|
|
LPWSTR SourceStrings[sizeof(PRINTER_INFO_1)/sizeof(LPWSTR)];
|
|
LPWSTR *pSourceStrings=SourceStrings;
|
|
|
|
cb = wcslen(szPrintProvidorName)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
wcslen(szPrintProvidorDescription)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
wcslen(szPrintProvidorComment)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
sizeof(PRINTER_INFO_1);
|
|
|
|
*pcbNeeded=cb;
|
|
|
|
if (cb > cbBuf) {
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
*pcReturned = 1;
|
|
|
|
pPrinter1->Flags = PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON1;
|
|
|
|
*pSourceStrings++=szPrintProvidorDescription;
|
|
*pSourceStrings++=szPrintProvidorName;
|
|
*pSourceStrings++=szPrintProvidorComment;
|
|
|
|
PackStrings(SourceStrings, pPrinter, PrinterInfo1Strings,
|
|
pPrinter+cbBuf);
|
|
|
|
LeaveSplSem();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
cb=0;
|
|
|
|
if (Flags & (PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME)) {
|
|
|
|
//
|
|
// For remote user's who are not admins enumerate shared printers only
|
|
//
|
|
if ( S_FALSE == CheckLocalCall() &&
|
|
!(Flags & PRINTER_ENUM_SHARED) &&
|
|
!ValidateObjectAccess(SPOOLER_OBJECT_SERVER,
|
|
SERVER_ACCESS_ADMINISTER,
|
|
NULL,
|
|
NULL,
|
|
pIniSpooler) )
|
|
Flags |= PRINTER_ENUM_SHARED;
|
|
|
|
//
|
|
// Calculate the size required
|
|
//
|
|
|
|
for ( pIniPrinter = pIniSpooler->pIniPrinter;
|
|
pIniPrinter != NULL;
|
|
pIniPrinter = pIniPrinter->pNext ) {
|
|
|
|
|
|
if ( EnumThisPrinter(Flags, pIniPrinter, pIniSpooler) && ShowThisPrinter(pIniPrinter, NULL))
|
|
cb += GetPrinterSize(pIniPrinter, Level, Flags,
|
|
lpRemote, pIniPrinter->pDevMode);
|
|
}
|
|
|
|
}
|
|
*pcbNeeded=cb;
|
|
|
|
if (cb > cbBuf) {
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
if (Flags & (PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME)) {
|
|
|
|
for ( pIniPrinter = pIniSpooler->pIniPrinter, pEnd = pPrinter + cbBuf;
|
|
pIniPrinter != NULL;
|
|
pIniPrinter = pIniPrinter->pNext ) {
|
|
|
|
|
|
if ( !EnumThisPrinter(Flags, pIniPrinter, pIniSpooler) )
|
|
continue;
|
|
|
|
//
|
|
// Do not list printers without access
|
|
if( !ShowThisPrinter(pIniPrinter, NULL) ) {
|
|
continue;
|
|
}
|
|
|
|
pEnd = CopyIniPrinterToPrinter( pIniPrinter, Level, pPrinter,
|
|
pEnd, NULL, lpRemote, FALSE, 0,
|
|
pIniPrinter->pDevMode );
|
|
|
|
if (!pEnd) {
|
|
LeaveSplSem();
|
|
return FALSE;
|
|
}
|
|
|
|
(*pcReturned)++;
|
|
|
|
switch (Level) {
|
|
|
|
case STRESSINFOLEVEL:
|
|
pPrinter+=sizeof(PRINTER_INFO_STRESS);
|
|
break;
|
|
|
|
case 1:
|
|
pPrinter+=sizeof(PRINTER_INFO_1);
|
|
break;
|
|
|
|
case 2:
|
|
pPrinter+=sizeof(PRINTER_INFO_2);
|
|
break;
|
|
|
|
case 4:
|
|
pPrinter+=sizeof(PRINTER_INFO_4);
|
|
break;
|
|
|
|
case 5:
|
|
pPrinter+=sizeof(PRINTER_INFO_5);
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* ShowThisPrinter
|
|
*
|
|
* Returns whether this printer should be visible to the current
|
|
* user.
|
|
*
|
|
* We do not show printers that the caller does not
|
|
* have access to. This is for two reasons:
|
|
*
|
|
* 1: A multi-user system with 200 users each with their
|
|
* own client printers would cause a large confusing
|
|
* list of printers from within applications such as word.
|
|
* "Client" printers are owned by the user of that station,
|
|
* and by default only allow print access for that user.
|
|
* This provides a simple way of filtering printers to show
|
|
* to the user for a selection.
|
|
*
|
|
* 2: Programs such as Windows write get confused when
|
|
* they see a printer they can not open. This is a bad
|
|
* program, since normal NT can have a printer denied to a user, but we must
|
|
* make it work anyway.
|
|
*
|
|
*
|
|
* Must work out security modes!
|
|
*
|
|
* Parameters
|
|
*
|
|
* pIniPrinter - A pointer to the spooler's internal data structure
|
|
* for the printer concerned.
|
|
* hToken - An optional pointer to the token for the user we are
|
|
wanting to make the test for.
|
|
*/
|
|
BOOL
|
|
ShowThisPrinter(
|
|
IN PINIPRINTER pIniPrinter,
|
|
IN HANDLE hToken OPTIONAL
|
|
)
|
|
{
|
|
LPWSTR pObjectName;
|
|
BOOL bRet = TRUE;
|
|
BOOL AccessStatus = TRUE;
|
|
HANDLE hClientToken = NULL;
|
|
ACCESS_MASK MappedDesiredAccess;
|
|
DWORD GrantedAccess = 0;
|
|
PBOOL pGenerateOnClose;
|
|
BYTE PrivilegeSetBuffer[256];
|
|
DWORD PrivilegeSetBufferLength = 256;
|
|
PPRIVILEGE_SET pPrivilegeSet;
|
|
DWORD dwRetCode;
|
|
|
|
PTOKEN_PRIVILEGES pPreviousTokenPrivileges;
|
|
DWORD PreviousTokenPrivilegesLength;
|
|
|
|
//
|
|
// External references to global variables in security.c
|
|
//
|
|
extern GENERIC_MAPPING GenericMapping[];
|
|
extern WCHAR *szSpooler;
|
|
|
|
|
|
//
|
|
// If Hydra is not enabled, keep NT behavior unchanged
|
|
//
|
|
if( !(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer)) )
|
|
{
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we weren't passed in a token handle, then use the current
|
|
// impersonation token or the process token.
|
|
//
|
|
if (!hToken)
|
|
{
|
|
bRet = GetTokenHandle(&hClientToken);
|
|
}
|
|
else
|
|
{
|
|
hClientToken = hToken;
|
|
}
|
|
|
|
//
|
|
// Administrators see all printers. This allows an
|
|
// Admin to take ownership of a printer whose ACL is
|
|
// messed up.
|
|
//
|
|
if (bRet)
|
|
{
|
|
bRet = ValidateObjectAccessWithToken(hClientToken, SPOOLER_OBJECT_SERVER, SERVER_ACCESS_ADMINISTER, NULL, NULL, pIniPrinter->pIniSpooler);
|
|
|
|
if (!bRet)
|
|
{
|
|
MapGenericToSpecificAccess(SPOOLER_OBJECT_PRINTER, PRINTER_ACCESS_USE, &MappedDesiredAccess);
|
|
|
|
pPrivilegeSet = (PPRIVILEGE_SET)PrivilegeSetBuffer;
|
|
|
|
//
|
|
// Call AccessCheck followed by ObjectOpenAuditAlarm rather than
|
|
// AccessCheckAndAuditAlarm, because we may need to enable
|
|
// SeSecurityPrivilege in order to check for ACCESS_SYSTEM_SECURITY
|
|
// privilege. We must ensure that the security access-checking
|
|
// API has the actual token whose security privilege we have enabled.
|
|
// AccessCheckAndAuditAlarm is no good for this, because it opens
|
|
// the client's token again, which may not have the privilege enabled.
|
|
//
|
|
// We do not audit and set off alarms because the caller
|
|
// did not really try and open the printer.
|
|
// We the server tried to check access for display purposes, not
|
|
// for handle create.
|
|
//
|
|
bRet = AccessCheck(pIniPrinter->pSecurityDescriptor,
|
|
hClientToken,
|
|
MappedDesiredAccess,
|
|
&GenericMapping[SPOOLER_OBJECT_PRINTER],
|
|
pPrivilegeSet,
|
|
&PrivilegeSetBufferLength,
|
|
&GrantedAccess,
|
|
&AccessStatus);
|
|
|
|
//
|
|
// If the access check failed, but it was because we didn't have an
|
|
// impersonation token, then we can view the printer.
|
|
//
|
|
if (!bRet)
|
|
{
|
|
if (GetLastError() == ERROR_NO_IMPERSONATION_TOKEN)
|
|
{
|
|
bRet = TRUE;
|
|
DBGMSG( DBG_ERROR, ("ShowThisPrinter: No impersonation token. Printer will be enumerated\n"));
|
|
}
|
|
else
|
|
{
|
|
DBGMSG( DBG_TRACE, ("ShowThisPrinter: Printer %ws Not accessable by caller Access Check failuer %d\n",pIniPrinter->pName,GetLastError()));
|
|
}
|
|
}
|
|
else if (!AccessStatus)
|
|
{
|
|
DBGMSG( DBG_TRACE, ("ShowThisPrinter: Printer %ws Not accessable by caller AccessStatus failure %d\n",pIniPrinter->pName,GetLastError()));
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have a client token and one was not passed in, then close the token
|
|
// handle.
|
|
//
|
|
if (hClientToken && !hToken)
|
|
{
|
|
CloseHandle(hClientToken);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
RetrieveMasqPrinterInfo
|
|
|
|
Description:
|
|
|
|
This retrieves Masq information for the printer, it is either cached state
|
|
or a direct call into the provider depending on Reg Settings.
|
|
|
|
Arguments:
|
|
|
|
pSpool - The spool handle that we use for synchronisation.
|
|
ppPrinterInfo - The returned printer info.
|
|
|
|
Returns:
|
|
|
|
A Boolean, if FALSE, last error is set.
|
|
|
|
--*/
|
|
BOOL
|
|
RetrieveMasqPrinterInfo(
|
|
IN PSPOOL pSpool,
|
|
OUT PRINTER_INFO_2 **ppPrinterInfo
|
|
)
|
|
{
|
|
BOOL bRet = TRUE;
|
|
PINIPRINTER pIniPrinter = NULL;
|
|
|
|
pIniPrinter = pSpool->pIniPrinter;
|
|
|
|
SplOutSem();
|
|
|
|
if (!(pSpool->pIniSpooler->dwSpoolerSettings & SPOOLER_CACHEMASQPRINTERS))
|
|
{
|
|
//
|
|
// Just synchronously return the data from the partial print provider.
|
|
//
|
|
bRet = BoolFromStatus(GetPrinterInfoFromRouter(pSpool->hPort, ppPrinterInfo));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Kick off the thread that goes and reads the masq status.
|
|
//
|
|
BOOL bCreateThread = FALSE;
|
|
PRINTER_INFO_2 *pPrinterInfo2 = NULL;
|
|
|
|
EnterSplSem();
|
|
|
|
if (!pIniPrinter->MasqCache.bThreadRunning)
|
|
{
|
|
bCreateThread = TRUE;
|
|
pIniPrinter->MasqCache.bThreadRunning = TRUE;
|
|
|
|
INCPRINTERREF(pIniPrinter);
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
SplOutSem();
|
|
|
|
if (bCreateThread)
|
|
{
|
|
HANDLE hThread = NULL;
|
|
MasqUpdateThreadData *pThreadData = NULL;
|
|
DWORD dwThreadId = 0;
|
|
|
|
pThreadData = AllocSplMem(sizeof(*pThreadData));
|
|
|
|
bRet = pThreadData != NULL;
|
|
|
|
if (bRet)
|
|
{
|
|
bRet = GetSid(&pThreadData->hUserToken);
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
pThreadData->pIniPrinter = pIniPrinter;
|
|
|
|
hThread = CreateThread(NULL, 0, AsyncPopulateMasqPrinterCache, (VOID *)pThreadData, 0, &dwThreadId);
|
|
|
|
bRet = hThread != NULL;
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
pThreadData = NULL;
|
|
}
|
|
|
|
//
|
|
// If we couldn't create the thread, then drop the ref count on the
|
|
// pIniPrinter again and set thread running to false.
|
|
//
|
|
if (!bRet)
|
|
{
|
|
EnterSplSem();
|
|
|
|
pIniPrinter->MasqCache.bThreadRunning = FALSE;
|
|
DECPRINTERREF(pIniPrinter);
|
|
|
|
LeaveSplSem();
|
|
}
|
|
|
|
if (hThread)
|
|
{
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
if (pThreadData)
|
|
{
|
|
if (pThreadData->hUserToken)
|
|
{
|
|
CloseHandle(pThreadData->hUserToken);
|
|
}
|
|
|
|
FreeSplMem(pThreadData);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to see the cached error return for the printer.
|
|
//
|
|
if (bRet)
|
|
{
|
|
EnterSplSem();
|
|
|
|
if (pIniPrinter->MasqCache.dwError != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(pIniPrinter->MasqCache.dwError);
|
|
|
|
bRet = FALSE;
|
|
}
|
|
|
|
//
|
|
// Caller is only interested in the Status and cJobs members, all strings
|
|
// are set to NULL by the allocation.
|
|
//
|
|
if (bRet)
|
|
{
|
|
pPrinterInfo2 = AllocSplMem(sizeof(PRINTER_INFO_2));
|
|
|
|
bRet = pPrinterInfo2 != NULL;
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
pPrinterInfo2->Status = pIniPrinter->MasqCache.Status;
|
|
pPrinterInfo2->cJobs = pIniPrinter->MasqCache.cJobs;
|
|
|
|
*ppPrinterInfo = pPrinterInfo2;
|
|
pPrinterInfo2 = NULL;
|
|
}
|
|
|
|
LeaveSplSem();
|
|
}
|
|
|
|
FreeSplMem(pPrinterInfo2);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
GetPrinterInfoFromRouter
|
|
|
|
Description:
|
|
|
|
This does a GetPrinter call against the router, it returns the error code
|
|
as a status.
|
|
|
|
Arguments:
|
|
|
|
hMasqPrinter - Handle to the masq printer.
|
|
ppPrinterInfo - Returned printer info.
|
|
|
|
Returns:
|
|
|
|
Status Code.
|
|
|
|
--*/
|
|
DWORD
|
|
GetPrinterInfoFromRouter(
|
|
IN HANDLE hMasqPrinter,
|
|
OUT PRINTER_INFO_2 **ppPrinterInfo
|
|
)
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
DWORD cb = 4096;
|
|
PRINTER_INFO_2 *pPrinterInfo2 = NULL;
|
|
DWORD cbNeeded;
|
|
|
|
SplOutSem();
|
|
|
|
pPrinterInfo2 = AllocSplMem(cb);
|
|
|
|
Status = pPrinterInfo2 != NULL ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
|
|
|
|
if (ERROR_SUCCESS == Status)
|
|
{
|
|
Status = GetPrinter(hMasqPrinter, 2, (BYTE *)pPrinterInfo2, cb, &cbNeeded) ? ERROR_SUCCESS : GetLastError();
|
|
}
|
|
|
|
if (ERROR_INSUFFICIENT_BUFFER == Status)
|
|
{
|
|
FreeSplMem(pPrinterInfo2);
|
|
pPrinterInfo2 = NULL;
|
|
|
|
cb = cbNeeded;
|
|
|
|
pPrinterInfo2 = AllocSplMem(cb);
|
|
|
|
Status = pPrinterInfo2 != NULL ? ERROR_SUCCESS : ERROR_OUTOFMEMORY;
|
|
|
|
if (ERROR_SUCCESS == Status)
|
|
{
|
|
Status = GetPrinter(hMasqPrinter, 2, (BYTE *)pPrinterInfo2, cb, &cbNeeded) ? ERROR_SUCCESS : GetLastError();
|
|
}
|
|
}
|
|
|
|
if (ERROR_SUCCESS == Status)
|
|
{
|
|
*ppPrinterInfo = pPrinterInfo2;
|
|
pPrinterInfo2 = NULL;
|
|
}
|
|
|
|
FreeSplMem(pPrinterInfo2);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
AsyncPopulateMasqPrinterCache
|
|
|
|
Description:
|
|
|
|
This populates the Masq printer cache for a given pIniPrinter.
|
|
|
|
Arguments:
|
|
|
|
pvThreadData - Pointer to a MasqUpdateThreadData structure
|
|
|
|
Returns:
|
|
|
|
A DWORD status, ignored.
|
|
|
|
--*/
|
|
DWORD
|
|
AsyncPopulateMasqPrinterCache(
|
|
IN VOID *pvThreadData
|
|
)
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
PINIPRINTER pIniPrinter = NULL;
|
|
PINIPORT pIniPort = NULL;
|
|
PWSTR pszPrinterName = NULL;
|
|
HANDLE hMasqPrinter = NULL;
|
|
PRINTER_INFO_2 *pPrinterInfo2 = NULL;
|
|
MasqUpdateThreadData *pThreadData = NULL;
|
|
|
|
SplOutSem();
|
|
|
|
pThreadData = (MasqUpdateThreadData *)pvThreadData;
|
|
|
|
pIniPrinter = pThreadData->pIniPrinter;
|
|
|
|
Status = SetCurrentSid(pThreadData->hUserToken) ? ERROR_SUCCESS : GetLastError();
|
|
|
|
EnterSplSem();
|
|
|
|
//
|
|
// Find the port associated with the printer.
|
|
//
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
pIniPort = FindIniPortFromIniPrinter(pIniPrinter);
|
|
|
|
Status = pIniPort && !(pIniPort->Status & PP_MONITOR) ? ERROR_SUCCESS : ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
INCPORTREF(pIniPort);
|
|
|
|
LeaveSplSem();
|
|
|
|
SplOutSem();
|
|
|
|
//
|
|
// This relies on the fact that a masq port cannot be renamed.
|
|
//
|
|
if (OpenPrinterPortW(pIniPort->pName, &hMasqPrinter, NULL))
|
|
{
|
|
//
|
|
// This will propogate any errors into the masq cache, which we
|
|
// don't have to do twice, so we ignore the return code.
|
|
//
|
|
Status = GetPrinterInfoFromRouter(hMasqPrinter, &pPrinterInfo2);
|
|
|
|
ClosePrinter(hMasqPrinter);
|
|
}
|
|
else
|
|
{
|
|
Status = GetLastError();
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = ERROR_UNEXP_NET_ERR;
|
|
}
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
if (ERROR_SUCCESS == Status)
|
|
{
|
|
pIniPrinter->MasqCache.cJobs = pPrinterInfo2->cJobs;
|
|
pIniPrinter->MasqCache.Status = pPrinterInfo2->Status;
|
|
}
|
|
else
|
|
{
|
|
pIniPrinter->MasqCache.cJobs = 0;
|
|
pIniPrinter->MasqCache.Status = 0;
|
|
}
|
|
|
|
//
|
|
// We always want to reset the status.
|
|
//
|
|
pIniPrinter->MasqCache.dwError = Status;
|
|
|
|
|
|
DECPORTREF(pIniPort);
|
|
}
|
|
|
|
SplInSem();
|
|
|
|
pIniPrinter->MasqCache.bThreadRunning = FALSE;
|
|
DECPRINTERREF(pIniPrinter);
|
|
|
|
DeletePrinterCheck(pIniPrinter);
|
|
|
|
LeaveSplSem();
|
|
|
|
if (pThreadData)
|
|
{
|
|
CloseHandle(pThreadData->hUserToken);
|
|
}
|
|
|
|
FreeSplMem(pThreadData);
|
|
|
|
FreeSplMem(pPrinterInfo2);
|
|
|
|
return Status;
|
|
}
|
|
|