981 lines
25 KiB
C
981 lines
25 KiB
C
/*++
|
|
|
|
Copyright (c) 1990-1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
local.c
|
|
|
|
Abstract:
|
|
|
|
This module provides all the public exported APIs relating to Printer
|
|
and Job management for the Local Print Providor
|
|
|
|
Author:
|
|
|
|
Dave Snipp (DaveSn) 15-Mar-1991
|
|
|
|
Revision History:
|
|
|
|
16-Jun-1992 JohnRo net print vs. UNICODE.
|
|
July 1994 MattFe Caching
|
|
|
|
--*/
|
|
#include <winerror.h>
|
|
#include <windows.h>
|
|
#include <winspool.h>
|
|
#include <lm.h>
|
|
#include <w32types.h>
|
|
#include <local.h>
|
|
#include <splcom.h> // DBGMSG
|
|
|
|
#include <wininet.h>
|
|
#include <offsets.h>
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
char szPMRaw[]="PM_Q_RAW";
|
|
|
|
WCHAR *szAdmin = L"ADMIN$";
|
|
|
|
extern HANDLE hNetApi;
|
|
extern NET_API_STATUS (*pfnNetServerGetInfo)();
|
|
extern NET_API_STATUS (*pfnNetApiBufferFree)();
|
|
|
|
HMODULE hSpoolssDll = NULL;
|
|
FARPROC pfnSpoolssEnumPorts = NULL;
|
|
|
|
DWORD
|
|
GetPortSize(
|
|
PWINIPORT pIniPort,
|
|
DWORD Level
|
|
)
|
|
{
|
|
DWORD cb;
|
|
WCHAR szMonitor[MAX_PATH+1], szPort[MAX_PATH+1];
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
|
|
cb=sizeof(PORT_INFO_1) +
|
|
wcslen(pIniPort->pName)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
break;
|
|
|
|
case 2:
|
|
LoadString(hInst, IDS_MONITOR_NAME, szMonitor, sizeof(szMonitor)/sizeof(szMonitor[0])-1);
|
|
LoadString(hInst, IDS_PORT_NAME, szPort, sizeof(szPort)/sizeof(szPort[0])-1);
|
|
cb = wcslen(pIniPort->pName) + 1 +
|
|
wcslen(szMonitor) + 1 +
|
|
wcslen(szPort) + 1;
|
|
cb *= sizeof(WCHAR);
|
|
cb += sizeof(PORT_INFO_2);
|
|
break;
|
|
|
|
default:
|
|
cb = 0;
|
|
break;
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
|
|
LPBYTE
|
|
CopyIniPortToPort(
|
|
PWINIPORT pIniPort,
|
|
DWORD Level,
|
|
LPBYTE pPortInfo,
|
|
LPBYTE pEnd
|
|
)
|
|
{
|
|
LPWSTR *SourceStrings, *pSourceStrings;
|
|
DWORD *pOffsets;
|
|
WCHAR szMonitor[MAX_PATH+1], szPort[MAX_PATH+1];
|
|
DWORD Count;
|
|
LPPORT_INFO_2 pPort2 = (LPPORT_INFO_2) pPortInfo;
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
pOffsets = PortInfo1Strings;
|
|
break;
|
|
|
|
case 2:
|
|
pOffsets = PortInfo2Strings;
|
|
break;
|
|
|
|
default:
|
|
DBGMSG(DBG_ERROR, ("CopyIniPortToPort: invalid level %d", Level));
|
|
return pEnd;
|
|
}
|
|
|
|
for ( Count = 0 ; pOffsets[Count] != -1 ; ++Count ) {
|
|
}
|
|
|
|
SourceStrings = pSourceStrings = AllocSplMem(Count * sizeof(LPWSTR));
|
|
|
|
if ( !SourceStrings ) {
|
|
DBGMSG(DBG_WARNING,
|
|
("CopyIniPortToPort: Failed to alloc port source strings.\n"));
|
|
return NULL;
|
|
}
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
|
|
*pSourceStrings++=pIniPort->pName;
|
|
break;
|
|
|
|
case 2:
|
|
*pSourceStrings++=pIniPort->pName;
|
|
LoadString(hInst, IDS_MONITOR_NAME, szMonitor, sizeof(szMonitor)/sizeof(szMonitor[0])-1);
|
|
LoadString(hInst, IDS_PORT_NAME, szPort, sizeof(szPort)/sizeof(szPort[0])-1);
|
|
*pSourceStrings++ = szMonitor;
|
|
*pSourceStrings++ = szPort;
|
|
pPort2->fPortType = PORT_TYPE_WRITE;
|
|
pPort2->Reserved = 0;
|
|
break;
|
|
|
|
default:
|
|
return pEnd;
|
|
DBGMSG(DBG_ERROR,
|
|
("CopyIniPortToPort: invalid level %d", Level));
|
|
}
|
|
|
|
pEnd = PackStrings(SourceStrings, pPortInfo, pOffsets, pEnd);
|
|
FreeSplMem(SourceStrings);
|
|
return pEnd;
|
|
}
|
|
|
|
|
|
/* PortExists
|
|
*
|
|
* Calls EnumPorts to check whether the port name already exists.
|
|
* This asks every monitor, rather than just this one.
|
|
* The function will return TRUE if the specified port is in the list.
|
|
* If an error occurs, the return is FALSE and the variable pointed
|
|
* to by pError contains the return from GetLastError().
|
|
* The caller must therefore always check that *pError == NO_ERROR.
|
|
*/
|
|
BOOL
|
|
PortExists(
|
|
LPWSTR pName,
|
|
LPWSTR pPortName,
|
|
PDWORD pError
|
|
)
|
|
{
|
|
DWORD cbNeeded;
|
|
DWORD cReturned;
|
|
DWORD cbPorts;
|
|
LPPORT_INFO_1 pPorts;
|
|
DWORD i;
|
|
BOOL Found = TRUE;
|
|
|
|
*pError = NO_ERROR;
|
|
|
|
if (!hSpoolssDll) {
|
|
|
|
hSpoolssDll = LoadLibrary(L"SPOOLSS.DLL");
|
|
|
|
if (hSpoolssDll) {
|
|
pfnSpoolssEnumPorts = GetProcAddress(hSpoolssDll,
|
|
"EnumPortsW");
|
|
if (!pfnSpoolssEnumPorts) {
|
|
|
|
*pError = GetLastError();
|
|
FreeLibrary(hSpoolssDll);
|
|
hSpoolssDll = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
*pError = GetLastError();
|
|
}
|
|
}
|
|
|
|
if (!pfnSpoolssEnumPorts)
|
|
return FALSE;
|
|
|
|
|
|
if (!(*pfnSpoolssEnumPorts)(pName, 1, NULL, 0, &cbNeeded, &cReturned))
|
|
{
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
cbPorts = cbNeeded;
|
|
|
|
pPorts = AllocSplMem(cbPorts);
|
|
|
|
if (pPorts)
|
|
{
|
|
if ((*pfnSpoolssEnumPorts)(pName, 1, (LPBYTE)pPorts, cbPorts,
|
|
&cbNeeded, &cReturned))
|
|
{
|
|
Found = FALSE;
|
|
|
|
for (i = 0; i < cReturned; i++)
|
|
{
|
|
if (!lstrcmpi(pPorts[i].pName, pPortName))
|
|
Found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeSplMem(pPorts);
|
|
}
|
|
}
|
|
|
|
else
|
|
Found = FALSE;
|
|
|
|
|
|
return Found;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
LMOpenPrinter(
|
|
LPWSTR pPrinterName,
|
|
LPHANDLE phPrinter,
|
|
LPPRINTER_DEFAULTS pDefault
|
|
)
|
|
{
|
|
PWINIPORT pIniPort;
|
|
PWSPOOL pSpool;
|
|
DWORD cb;
|
|
PUSE_INFO_0 pUseInfo;
|
|
LPWSTR pShare;
|
|
WCHAR PrinterName[MAX_UNC_PRINTER_NAME];
|
|
DWORD cbNeeded;
|
|
DWORD rc;
|
|
BYTE Buffer[4];
|
|
DWORD Error = NO_ERROR;
|
|
DWORD dwEntry = 0xffffffff;
|
|
PSERVER_INFO_101 pserver_info_101 = NULL;
|
|
|
|
|
|
/* If we already have an INI port entry by this name, don't worry
|
|
* about hitting the network. This ensures that we don't try to
|
|
* make a network call when we're not impersonating - like on
|
|
* bootup.
|
|
*/
|
|
if (!(pIniPort = FindPort(pPrinterName, pIniFirstPort))) {
|
|
|
|
if (!NetUseGetInfo(NULL, pPrinterName, 0, (LPBYTE *)&pUseInfo))
|
|
pPrinterName = AllocSplStr(pUseInfo->ui0_remote);
|
|
|
|
NetApiBufferFree( (LPVOID) pUseInfo );
|
|
}
|
|
|
|
if ( !pPrinterName ||
|
|
*pPrinterName != L'\\' ||
|
|
*(pPrinterName+1) != L'\\' ||
|
|
wcslen(pPrinterName) + 1 > MAX_UNC_PRINTER_NAME ) {
|
|
|
|
SetLastError(ERROR_INVALID_NAME);
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy(PrinterName, pPrinterName);
|
|
pShare=wcschr(PrinterName+2, L'\\');
|
|
|
|
if ( !pShare ) {
|
|
|
|
SetLastError(ERROR_INVALID_NAME);
|
|
return FALSE;
|
|
}
|
|
|
|
*pShare++=0;
|
|
|
|
|
|
if (!pIniPort) {
|
|
|
|
/* Verify that this guy actually exists.
|
|
* Call it with a zero-length buffer,
|
|
* and see if the error is that the buffer isn't big enough.
|
|
* If we make Buffer = NULL, it fails with
|
|
* ERROR_INVALID_PARAMETER, which is pretty abysmal,
|
|
* so we must pass a Buffer address.
|
|
* (Actually, it seems to accept any non-NULL value,
|
|
* regardless of whether it's a valid address.)
|
|
*/
|
|
|
|
EnterSplSem();
|
|
dwEntry = FindEntryinLMCache(PrinterName, pShare);
|
|
LeaveSplSem();
|
|
|
|
if (dwEntry == -1) {
|
|
|
|
DBGMSG(DBG_TRACE, ("We haven't cached this entry so we have to hit the net\n"));
|
|
|
|
rc = RxPrintQGetInfo(PrinterName, /* e.g. \\msprint07 */
|
|
pShare, /* e.g. l07corpa */
|
|
0, /* Level 0 */
|
|
Buffer, /* Dummy - won't get filled in */
|
|
0, /* Length of buffer */
|
|
&cbNeeded); /* How much we need - we'll ignore */
|
|
|
|
DBGMSG(DBG_INFO, ("LMOpenPrinter!RxPrintQGetInfo returned %d\n", rc));
|
|
|
|
if (rc == ERROR_ACCESS_DENIED) {
|
|
|
|
/* The print share exists; we just don't have access to it.
|
|
*/
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!((rc == ERROR_MORE_DATA)
|
|
||(rc == NERR_BufTooSmall)
|
|
||(rc == ERROR_INSUFFICIENT_BUFFER))) {
|
|
SetLastError(ERROR_INVALID_NAME);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Be sure that we are connecting to a downlevel server.
|
|
// If the server is Windows NT Machine, then fail the call:
|
|
// downlevel NT connections won't work properly because
|
|
// "\\Server\Printer Name" will get past RxPrintQGetInfo, but
|
|
// we can't do CreateFile on it (we need to user the share
|
|
// name). Downlevel connects also lose admin functionality.
|
|
//
|
|
if (pfnNetServerGetInfo) {
|
|
|
|
rc = pfnNetServerGetInfo(PrinterName, 101, &pserver_info_101);
|
|
|
|
//
|
|
// Advanced Server for Unix (ASU) by AT&T reports that
|
|
// they are TYPE_NT even though they don't support the
|
|
// rpc interface. They also set TYPE_XENIX_SERVER.
|
|
// Since the need lm connections, allow them when
|
|
// TYPE_XENIX_SERVER is specified.
|
|
//
|
|
// Also make this change for SERVER_VMS and SERVER_OSF.
|
|
// These changes are for AT&T also.
|
|
//
|
|
// Note: this will also allow accidental downlevel
|
|
// connections to any servers that set TYPE_XENIX_SERVER,
|
|
// TYPE_SERVER_VMS, or TYPE_SERVER_OSF.
|
|
//
|
|
|
|
if (!rc &&
|
|
(pserver_info_101->sv101_type & SV_TYPE_NT) &&
|
|
!(pserver_info_101->sv101_type &
|
|
( SV_TYPE_XENIX_SERVER |
|
|
SV_TYPE_SERVER_VMS |
|
|
SV_TYPE_SERVER_OSF))) {
|
|
|
|
DBGMSG(DBG_WARNING, ("NetServerGetInfo indicates %ws is a WinNT\n", PrinterName));
|
|
pfnNetApiBufferFree((LPVOID)pserver_info_101);
|
|
|
|
SetLastError(ERROR_INVALID_NAME);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now free the buffer
|
|
//
|
|
if (pserver_info_101) {
|
|
pfnNetApiBufferFree((LPVOID)pserver_info_101);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add entry to the cache
|
|
//
|
|
EnterSplSem();
|
|
AddEntrytoLMCache(PrinterName, pShare);
|
|
LeaveSplSem();
|
|
}
|
|
}
|
|
|
|
/* Make sure we can write to the print share.
|
|
* This will fail if there's an invalid password.
|
|
*/
|
|
/* hTest = CreateFile(pPrinterName, GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hTest == INVALID_HANDLE_VALUE) {
|
|
|
|
DBGMSG(DBG_WARNING, ("Can't write to %ws: Error %d\n",
|
|
pPrinterName, GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
CloseHandle(hTest); */
|
|
|
|
/* Make sure there's a port of this name so that
|
|
* EnumPorts will return it:
|
|
*/
|
|
if (!PortExists(NULL, pPrinterName, &Error) && (Error == NO_ERROR)) {
|
|
if (CreatePortEntry(pPrinterName, &pIniFirstPort)) {
|
|
CreateRegistryEntry(pPrinterName);
|
|
}
|
|
}
|
|
|
|
if (Error != NO_ERROR)
|
|
return FALSE;
|
|
|
|
cb = sizeof(WSPOOL);
|
|
|
|
EnterSplSem();
|
|
pSpool = AllocWSpool();
|
|
LeaveSplSem();
|
|
|
|
if ( pSpool != NULL ) {
|
|
|
|
pSpool->pServer = AllocSplStr(PrinterName);
|
|
pSpool->pShare = AllocSplStr(pShare);
|
|
pSpool->Status = 0;
|
|
pSpool->Type = LM_HANDLE;
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_TRACE,("Error: LMOpenPrinter to return ERROR_NOT_ENOUGH_MEMORY\n"));
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
*phPrinter = (HANDLE)pSpool;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
LMSetPrinter(
|
|
HANDLE hPrinter,
|
|
DWORD Level,
|
|
LPBYTE pPrinter,
|
|
DWORD Command
|
|
)
|
|
{
|
|
PWSPOOL pSpool = (PWSPOOL)hPrinter;
|
|
|
|
API_RET_TYPE uReturnCode;
|
|
DWORD dwParmError;
|
|
USE_INFO_1 UseInfo1;
|
|
PUSE_INFO_1 pUseInfo1 = &UseInfo1;
|
|
WCHAR szRemoteShare[MAX_PATH];
|
|
DWORD dwRet;
|
|
|
|
|
|
if (!pSpool ||
|
|
pSpool->signature != WSJ_SIGNATURE) {
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
if( (dwRet = StrNCatBuff(szRemoteShare, COUNTOF(szRemoteShare), pSpool->pServer, L"\\", szAdmin, NULL )) != ERROR_SUCCESS ) {
|
|
|
|
SetLastError(dwRet);
|
|
return FALSE;
|
|
}
|
|
|
|
pUseInfo1->ui1_local = NULL;
|
|
pUseInfo1->ui1_remote = szRemoteShare;
|
|
pUseInfo1->ui1_password = NULL;
|
|
pUseInfo1->ui1_asg_type = 0;
|
|
dwParmError = 0;
|
|
|
|
switch (Command) {
|
|
|
|
case 0:
|
|
break;
|
|
|
|
case PRINTER_CONTROL_PURGE:
|
|
uReturnCode = RxPrintQPurge(pSpool->pServer, pSpool->pShare);
|
|
if (uReturnCode) {
|
|
|
|
uReturnCode = NetUseAdd(NULL, 1,
|
|
(LPBYTE)pUseInfo1,
|
|
&dwParmError);
|
|
if (uReturnCode == ERROR_ACCESS_DENIED) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return(FALSE);
|
|
|
|
} else {
|
|
|
|
uReturnCode = RxPrintQPurge(pSpool->pServer, pSpool->pShare);
|
|
if (uReturnCode == ERROR_ACCESS_DENIED) {
|
|
NetUseDel(NULL,
|
|
pUseInfo1->ui1_remote, USE_FORCE);
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return(FALSE);
|
|
}
|
|
NetUseDel(NULL,
|
|
pUseInfo1->ui1_remote, USE_FORCE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PRINTER_CONTROL_RESUME:
|
|
uReturnCode = RxPrintQContinue(pSpool->pServer, pSpool->pShare);
|
|
if (uReturnCode) {
|
|
|
|
uReturnCode = NetUseAdd(NULL, 1,
|
|
(LPBYTE)pUseInfo1,
|
|
&dwParmError);
|
|
if (uReturnCode == ERROR_ACCESS_DENIED) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return(FALSE);
|
|
|
|
} else {
|
|
uReturnCode = RxPrintQContinue(pSpool->pServer, pSpool->pShare);
|
|
if (uReturnCode == ERROR_ACCESS_DENIED) {
|
|
NetUseDel(NULL,
|
|
pUseInfo1->ui1_remote, USE_FORCE);
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return(FALSE);
|
|
}
|
|
NetUseDel(NULL,
|
|
pUseInfo1->ui1_remote, USE_FORCE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PRINTER_CONTROL_PAUSE:
|
|
uReturnCode = RxPrintQPause(pSpool->pServer, pSpool->pShare);
|
|
if (uReturnCode) {
|
|
|
|
uReturnCode = NetUseAdd(NULL, 1,
|
|
(LPBYTE)pUseInfo1,
|
|
&dwParmError);
|
|
if (uReturnCode == ERROR_ACCESS_DENIED) {
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return(FALSE);
|
|
|
|
} else {
|
|
uReturnCode = RxPrintQPause(pSpool->pServer, pSpool->pShare);
|
|
if (uReturnCode) {
|
|
NetUseDel(NULL,
|
|
pUseInfo1->ui1_remote, USE_FORCE);
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return(FALSE);
|
|
}
|
|
NetUseDel(NULL,
|
|
pUseInfo1->ui1_remote, USE_FORCE);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// SetPrinter successful - so pulse here if event set,
|
|
// or reply to spooler.
|
|
//
|
|
LMSetSpoolChange(pSpool);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define Nullstrlen(psz) ((psz) ? wcslen(psz)*sizeof(WCHAR)+sizeof(WCHAR) : 0)
|
|
|
|
DWORD
|
|
GetPrqInfo3Size(
|
|
PWSPOOL pSpool,
|
|
PRQINFO3 *pPrqInfo3,
|
|
DWORD Level
|
|
)
|
|
{
|
|
DWORD cb;
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
|
|
//
|
|
// 3 extra chars
|
|
// (2 NULL terminators, 1 for '\' (server/share separator)
|
|
//
|
|
cb=sizeof(PRINTER_INFO_1) +
|
|
wcslen(pSpool->pServer)*sizeof(WCHAR)*2 +
|
|
wcslen(pSpool->pShare)*sizeof(WCHAR) +
|
|
sizeof(WCHAR)*3 +
|
|
Nullstrlen(pPrqInfo3->pszComment);
|
|
break;
|
|
|
|
case 2:
|
|
|
|
cb = sizeof(PRINTER_INFO_2) +
|
|
wcslen(pSpool->pServer)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
wcslen(pSpool->pShare)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
wcslen(pSpool->pServer)*sizeof(WCHAR) +
|
|
sizeof(WCHAR) +
|
|
wcslen(pSpool->pShare)*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
Nullstrlen(pPrqInfo3->pszPrinters) +
|
|
Nullstrlen(pPrqInfo3->pszDriverName) +
|
|
Nullstrlen(pPrqInfo3->pszComment) +
|
|
Nullstrlen(pPrqInfo3->pszSepFile) +
|
|
Nullstrlen(pPrqInfo3->pszPrProc) +
|
|
wcslen(L"RAW")*sizeof(WCHAR) + sizeof(WCHAR) +
|
|
Nullstrlen(pPrqInfo3->pszParms);
|
|
break;
|
|
|
|
default:
|
|
cb = 0;
|
|
break;
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
// This can be radically tidied up
|
|
// We should probably use the stack for the
|
|
// array of string pointers rather than dynamically allocating it !
|
|
|
|
LPBYTE
|
|
CopyPrqInfo3ToPrinter(
|
|
PWSPOOL pSpool,
|
|
PRQINFO3 *pPrqInfo3,
|
|
DWORD Level,
|
|
LPBYTE pPrinterInfo,
|
|
LPBYTE pEnd
|
|
)
|
|
{
|
|
LPWSTR *pSourceStrings, *SourceStrings;
|
|
PPRINTER_INFO_2 pPrinter2 = (PPRINTER_INFO_2)pPrinterInfo;
|
|
PPRINTER_INFO_1 pPrinter1 = (PPRINTER_INFO_1)pPrinterInfo;
|
|
DWORD i;
|
|
DWORD *pOffsets;
|
|
DWORD RetVal = ERROR_SUCCESS;
|
|
WCHAR szFileName[MAX_PATH];
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
pOffsets = PrinterInfo1Strings;
|
|
break;
|
|
|
|
case 2:
|
|
pOffsets = PrinterInfo2Strings;
|
|
break;
|
|
|
|
default:
|
|
return pEnd;
|
|
}
|
|
|
|
for (i=0; pOffsets[i] != -1; i++) {
|
|
}
|
|
|
|
SourceStrings = pSourceStrings = AllocSplMem(i * sizeof(LPWSTR));
|
|
|
|
if (!SourceStrings)
|
|
return NULL;
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
*pSourceStrings++=pSpool->pServer;
|
|
|
|
RetVal = StrNCatBuff( szFileName,
|
|
COUNTOF(szFileName),
|
|
pSpool->pServer,
|
|
L"\\",
|
|
pSpool->pShare,
|
|
NULL );
|
|
if ( RetVal != ERROR_SUCCESS )
|
|
{
|
|
break;
|
|
}
|
|
|
|
*pSourceStrings++=szFileName;
|
|
*pSourceStrings++=pPrqInfo3->pszComment;
|
|
|
|
pEnd = PackStrings(SourceStrings, pPrinterInfo, pOffsets, pEnd);
|
|
|
|
pPrinter1->Flags = PRINTER_ENUM_REMOTE | PRINTER_ENUM_NAME;
|
|
break;
|
|
|
|
case 2:
|
|
|
|
RetVal = StrNCatBuff( szFileName,
|
|
COUNTOF(szFileName),
|
|
pSpool->pServer,
|
|
L"\\",
|
|
pSpool->pShare,
|
|
NULL );
|
|
if ( RetVal != ERROR_SUCCESS )
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
*pSourceStrings++=pSpool->pServer;
|
|
|
|
*pSourceStrings++=szFileName;
|
|
|
|
*pSourceStrings++=pSpool->pShare;
|
|
*pSourceStrings++=pPrqInfo3->pszPrinters;
|
|
*pSourceStrings++=pPrqInfo3->pszDriverName ? pPrqInfo3->pszDriverName : L"";
|
|
*pSourceStrings++=pPrqInfo3->pszComment;
|
|
*pSourceStrings++=NULL;
|
|
*pSourceStrings++=pPrqInfo3->pszSepFile;
|
|
*pSourceStrings++=pPrqInfo3->pszPrProc;
|
|
*pSourceStrings++=L"RAW";
|
|
*pSourceStrings++=pPrqInfo3->pszParms;
|
|
|
|
pEnd = PackStrings(SourceStrings, (LPBYTE)pPrinter2, pOffsets, pEnd);
|
|
|
|
pPrinter2->pDevMode=0;
|
|
pPrinter2->Attributes=PRINTER_ATTRIBUTE_QUEUED;
|
|
pPrinter2->Priority=pPrqInfo3->uPriority;
|
|
pPrinter2->DefaultPriority=pPrqInfo3->uPriority;
|
|
pPrinter2->StartTime=pPrqInfo3->uStartTime;
|
|
pPrinter2->UntilTime=pPrqInfo3->uUntilTime;
|
|
|
|
pPrinter2->Status=0;
|
|
|
|
if (pPrqInfo3->fsStatus & PRQ3_PAUSED)
|
|
pPrinter2->Status|=PRINTER_STATUS_PAUSED;
|
|
|
|
if (pPrqInfo3->fsStatus & PRQ3_PENDING)
|
|
pPrinter2->Status|=PRINTER_STATUS_PENDING_DELETION;
|
|
|
|
pPrinter2->cJobs=pPrqInfo3->cJobs;
|
|
pPrinter2->AveragePPM=0;
|
|
break;
|
|
|
|
default:
|
|
return pEnd;
|
|
}
|
|
|
|
FreeSplMem(SourceStrings);
|
|
|
|
if ( RetVal == ERROR_SUCCESS )
|
|
{
|
|
return pEnd;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
LMGetPrinter(
|
|
HANDLE hPrinter,
|
|
DWORD Level,
|
|
LPBYTE pPrinter,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded
|
|
)
|
|
{
|
|
PWSPOOL pSpool = (PWSPOOL)hPrinter;
|
|
PRQINFO3 *pPrqInfo3;
|
|
PRQINFO3 PrqInfo3;
|
|
PRQINFO *pPrqInfo=NULL;
|
|
DWORD cb = 0x400;
|
|
DWORD rc;
|
|
DWORD cbNeeded;
|
|
LPBYTE pInfo = NULL;
|
|
BOOL bWFW = FALSE;
|
|
|
|
|
|
if (Level >= 7) {
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!pSpool ||
|
|
pSpool->signature != WSJ_SIGNATURE) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
pPrqInfo3 = AllocSplMem(cb);
|
|
|
|
if ( !pPrqInfo3 )
|
|
goto Cleanup;
|
|
|
|
if ( rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare, 3,
|
|
(PBYTE)pPrqInfo3, cb, &cbNeeded)) {
|
|
|
|
if (rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall) {
|
|
|
|
pPrqInfo3=ReallocSplMem(pPrqInfo3, 0, cbNeeded);
|
|
|
|
if ( !pPrqInfo3 )
|
|
goto Cleanup;
|
|
|
|
cb=cbNeeded;
|
|
|
|
if (rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare,
|
|
3, (PBYTE)pPrqInfo3, cb, &cbNeeded)) {
|
|
|
|
SetLastError(rc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else if (rc == ERROR_INVALID_LEVEL) {
|
|
|
|
// Must be WFW
|
|
|
|
if (rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare, 1,
|
|
(PBYTE)pPrqInfo3, cb, &cbNeeded)) {
|
|
|
|
if (rc == ERROR_MORE_DATA || rc == NERR_BufTooSmall) {
|
|
|
|
pPrqInfo3 = ReallocSplMem(pPrqInfo3, 0, cbNeeded);
|
|
|
|
if ( !pPrqInfo3 )
|
|
goto Cleanup;
|
|
|
|
cb=cbNeeded;
|
|
|
|
if (rc = RxPrintQGetInfo(pSpool->pServer, pSpool->pShare, 1,
|
|
(PBYTE)pPrqInfo3, cb, &cbNeeded)) {
|
|
|
|
SetLastError(rc);
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
SetLastError(rc);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pPrqInfo = (PRQINFO *)pPrqInfo3;
|
|
|
|
PrqInfo3.pszName = pPrqInfo->szName;
|
|
PrqInfo3.uPriority = pPrqInfo->uPriority;
|
|
PrqInfo3.uStartTime = pPrqInfo->uStartTime;
|
|
PrqInfo3.uUntilTime = pPrqInfo->uUntilTime;
|
|
PrqInfo3.pad1 = 0;
|
|
PrqInfo3.pszSepFile = pPrqInfo->pszSepFile;
|
|
PrqInfo3.pszPrProc = pPrqInfo->pszPrProc;
|
|
PrqInfo3.pszParms = pPrqInfo->pszDestinations;
|
|
PrqInfo3.pszComment = pPrqInfo->pszComment;
|
|
PrqInfo3.fsStatus = pPrqInfo->fsStatus;
|
|
PrqInfo3.cJobs = pPrqInfo->cJobs;
|
|
PrqInfo3.pszPrinters = pPrqInfo->pszDestinations;
|
|
PrqInfo3.pszDriverName = L"";
|
|
PrqInfo3.pDriverData = NULL;
|
|
|
|
bWFW = TRUE;
|
|
|
|
} else {
|
|
|
|
SetLastError(rc);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
cbNeeded=GetPrqInfo3Size(pSpool,
|
|
bWFW ? &PrqInfo3 : pPrqInfo3,
|
|
Level);
|
|
|
|
*pcbNeeded=cbNeeded;
|
|
|
|
if (cbNeeded > cbBuf) {
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
goto Cleanup;
|
|
}
|
|
|
|
pInfo = CopyPrqInfo3ToPrinter(pSpool,
|
|
bWFW ? &PrqInfo3 : pPrqInfo3,
|
|
Level,
|
|
pPrinter,
|
|
(LPBYTE)pPrinter+cbBuf);
|
|
|
|
Cleanup:
|
|
if (pPrqInfo3)
|
|
FreeSplMem(pPrqInfo3);
|
|
|
|
return pInfo != NULL;
|
|
}
|
|
|
|
BOOL
|
|
LMEnumPorts(
|
|
LPWSTR pName,
|
|
DWORD Level,
|
|
LPBYTE pPorts,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbNeeded,
|
|
LPDWORD pcReturned
|
|
)
|
|
{
|
|
BOOL rc=TRUE;
|
|
DWORD cb;
|
|
PWINIPORT pIniPort;
|
|
LPBYTE pEnd;
|
|
|
|
|
|
switch (Level) {
|
|
|
|
case 1:
|
|
break;
|
|
|
|
case 2:
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
return FALSE;
|
|
}
|
|
|
|
EnterSplSem();
|
|
|
|
cb=0;
|
|
|
|
pIniPort = pIniFirstPort;
|
|
|
|
while (pIniPort) {
|
|
cb+=GetPortSize(pIniPort, Level);
|
|
pIniPort=pIniPort->pNext;
|
|
}
|
|
|
|
*pcbNeeded=cb;
|
|
|
|
|
|
if (cb <= cbBuf) {
|
|
|
|
pEnd=pPorts+cbBuf;
|
|
*pcReturned=0;
|
|
|
|
pIniPort = pIniFirstPort;
|
|
while (pIniPort) {
|
|
pEnd = CopyIniPortToPort(pIniPort, Level, pPorts, pEnd);
|
|
switch (Level) {
|
|
case 1:
|
|
pPorts+=sizeof(PORT_INFO_1);
|
|
break;
|
|
|
|
case 2:
|
|
pPorts+=sizeof(PORT_INFO_2);
|
|
break;
|
|
}
|
|
pIniPort=pIniPort->pNext;
|
|
(*pcReturned)++;
|
|
}
|
|
|
|
} else {
|
|
|
|
*pcReturned = 0;
|
|
|
|
rc = FALSE;
|
|
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
return rc;
|
|
}
|