mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-02-03 21:52:24 +00:00
138df73e81
A user reports that if the Print Spooler service is disabled via services.msc, then PuTTY can report 'Out of memory!' when you try to open the Terminal config pane, which is the one containing the combo box enumerating the available printers. Apparently this is because the call to EnumPrinters failed with the error code other than the expected ERROR_INSUFFICIENT_BUFFER, and in the process, left garbage in the pcbNeeded output parameter. That wouldn't be too surprising if it had simply _not written_ to that parameter and therefore it was never initialised at all in the calling function printer_add_enum. But in fact, printer_add_enum *does* precautionarily initialise needed=0 before the initial call to EnumPrinters. So EnumPrinters must have actively written one of its *own* uninitialised variables into it! Anyway, the obvious fix is to distinguish ERROR_INSUFFICIENT_BUFFER from any other kind of EnumPrinters failure (in fact turning off Print Spooler seems to lead to RPC_S_SERVER_UNAVAILABLE), and not attempt to proceed in the case of other failures.
229 lines
6.1 KiB
C
229 lines
6.1 KiB
C
/*
|
|
* Printing interface for PuTTY.
|
|
*/
|
|
|
|
#include "putty.h"
|
|
#include <winspool.h>
|
|
|
|
struct printer_enum_tag {
|
|
int nprinters;
|
|
DWORD enum_level;
|
|
union {
|
|
LPPRINTER_INFO_4 i4;
|
|
LPPRINTER_INFO_5 i5;
|
|
} info;
|
|
};
|
|
|
|
struct printer_job_tag {
|
|
HANDLE hprinter;
|
|
};
|
|
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, EnumPrinters,
|
|
(DWORD, LPTSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, OpenPrinter,
|
|
(LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, ClosePrinter, (HANDLE));
|
|
DECL_WINDOWS_FUNCTION(static, DWORD, StartDocPrinter, (HANDLE, DWORD, LPBYTE));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, EndDocPrinter, (HANDLE));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, StartPagePrinter, (HANDLE));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, EndPagePrinter, (HANDLE));
|
|
DECL_WINDOWS_FUNCTION(static, BOOL, WritePrinter,
|
|
(HANDLE, LPVOID, DWORD, LPDWORD));
|
|
|
|
static void init_winfuncs(void)
|
|
{
|
|
static bool initialised = false;
|
|
if (initialised)
|
|
return;
|
|
{
|
|
HMODULE winspool_module = load_system32_dll("winspool.drv");
|
|
/* Some MSDN documentation claims that some of the below functions
|
|
* should be loaded from spoolss.dll, but this doesn't seem to
|
|
* be reliable in practice.
|
|
* Nevertheless, we load spoolss.dll ourselves using our safe
|
|
* loading method, against the possibility that winspool.drv
|
|
* later loads it unsafely. */
|
|
(void) load_system32_dll("spoolss.dll");
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, EnumPrinters);
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, OpenPrinter);
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, ClosePrinter);
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, StartDocPrinter);
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, EndDocPrinter);
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, StartPagePrinter);
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, EndPagePrinter);
|
|
GET_WINDOWS_FUNCTION_PP(winspool_module, WritePrinter);
|
|
}
|
|
initialised = true;
|
|
}
|
|
|
|
static bool printer_add_enum(int param, DWORD level, char **buffer,
|
|
int offset, int *nprinters_ptr)
|
|
{
|
|
DWORD needed = 0, nprinters = 0;
|
|
|
|
init_winfuncs();
|
|
|
|
*buffer = sresize(*buffer, offset+512, char);
|
|
|
|
/*
|
|
* Exploratory call to EnumPrinters to determine how much space
|
|
* we'll need for the output.
|
|
*
|
|
* If we get ERROR_INSUFFICIENT_BUFFER, that's fine, we're
|
|
* prepared to deal with it. Any other error, we return failure.
|
|
*/
|
|
if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512,
|
|
&needed, &nprinters) == 0 &&
|
|
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
return false;
|
|
|
|
if (needed < 512)
|
|
needed = 512;
|
|
|
|
*buffer = sresize(*buffer, offset+needed, char);
|
|
|
|
if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset),
|
|
needed, &needed, &nprinters) == 0)
|
|
return false;
|
|
|
|
*nprinters_ptr += nprinters;
|
|
|
|
return true;
|
|
}
|
|
|
|
printer_enum *printer_start_enum(int *nprinters_ptr)
|
|
{
|
|
printer_enum *ret = snew(printer_enum);
|
|
char *buffer = NULL;
|
|
|
|
*nprinters_ptr = 0; /* default return value */
|
|
buffer = snewn(512, char);
|
|
|
|
/*
|
|
* Determine what enumeration level to use.
|
|
* When enumerating printers, we need to use PRINTER_INFO_4 on
|
|
* NT-class systems to avoid Windows looking too hard for them and
|
|
* slowing things down; and we need to avoid PRINTER_INFO_5 as
|
|
* we've seen network printers not show up.
|
|
* On 9x-class systems, PRINTER_INFO_4 isn't available and
|
|
* PRINTER_INFO_5 is recommended.
|
|
* Bletch.
|
|
*/
|
|
if (osPlatformId != VER_PLATFORM_WIN32_NT) {
|
|
ret->enum_level = 5;
|
|
} else {
|
|
ret->enum_level = 4;
|
|
}
|
|
|
|
if (!printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
|
|
ret->enum_level, &buffer, 0, nprinters_ptr))
|
|
goto error;
|
|
|
|
switch (ret->enum_level) {
|
|
case 4:
|
|
ret->info.i4 = (LPPRINTER_INFO_4)buffer;
|
|
break;
|
|
case 5:
|
|
ret->info.i5 = (LPPRINTER_INFO_5)buffer;
|
|
break;
|
|
}
|
|
ret->nprinters = *nprinters_ptr;
|
|
|
|
return ret;
|
|
|
|
error:
|
|
sfree(buffer);
|
|
sfree(ret);
|
|
*nprinters_ptr = 0;
|
|
return NULL;
|
|
}
|
|
|
|
char *printer_get_name(printer_enum *pe, int i)
|
|
{
|
|
if (!pe)
|
|
return NULL;
|
|
if (i < 0 || i >= pe->nprinters)
|
|
return NULL;
|
|
switch (pe->enum_level) {
|
|
case 4:
|
|
return pe->info.i4[i].pPrinterName;
|
|
case 5:
|
|
return pe->info.i5[i].pPrinterName;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void printer_finish_enum(printer_enum *pe)
|
|
{
|
|
if (!pe)
|
|
return;
|
|
switch (pe->enum_level) {
|
|
case 4:
|
|
sfree(pe->info.i4);
|
|
break;
|
|
case 5:
|
|
sfree(pe->info.i5);
|
|
break;
|
|
}
|
|
sfree(pe);
|
|
}
|
|
|
|
printer_job *printer_start_job(char *printer)
|
|
{
|
|
printer_job *ret = snew(printer_job);
|
|
DOC_INFO_1 docinfo;
|
|
bool jobstarted = false, pagestarted = false;
|
|
|
|
init_winfuncs();
|
|
|
|
ret->hprinter = NULL;
|
|
if (!p_OpenPrinter(printer, &ret->hprinter, NULL))
|
|
goto error;
|
|
|
|
docinfo.pDocName = "PuTTY remote printer output";
|
|
docinfo.pOutputFile = NULL;
|
|
docinfo.pDatatype = "RAW";
|
|
|
|
if (!p_StartDocPrinter(ret->hprinter, 1, (LPBYTE)&docinfo))
|
|
goto error;
|
|
jobstarted = true;
|
|
|
|
if (!p_StartPagePrinter(ret->hprinter))
|
|
goto error;
|
|
pagestarted = true;
|
|
|
|
return ret;
|
|
|
|
error:
|
|
if (pagestarted)
|
|
p_EndPagePrinter(ret->hprinter);
|
|
if (jobstarted)
|
|
p_EndDocPrinter(ret->hprinter);
|
|
if (ret->hprinter)
|
|
p_ClosePrinter(ret->hprinter);
|
|
sfree(ret);
|
|
return NULL;
|
|
}
|
|
|
|
void printer_job_data(printer_job *pj, const void *data, size_t len)
|
|
{
|
|
DWORD written;
|
|
|
|
if (!pj)
|
|
return;
|
|
|
|
p_WritePrinter(pj->hprinter, (void *)data, len, &written);
|
|
}
|
|
|
|
void printer_finish_job(printer_job *pj)
|
|
{
|
|
if (!pj)
|
|
return;
|
|
|
|
p_EndPagePrinter(pj->hprinter);
|
|
p_EndDocPrinter(pj->hprinter);
|
|
p_ClosePrinter(pj->hprinter);
|
|
sfree(pj);
|
|
}
|