/* * Printing interface for PuTTY. */ #include "putty.h" #include 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. Discard the return value since it * will almost certainly be a failure due to lack of space. */ p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512, &needed, &nprinters); 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); }