FreeRDP
Loading...
Searching...
No Matches
printer_win.c
1
23#include <freerdp/config.h>
24
25#include <winpr/crt.h>
26#include <winpr/wtsapi.h>
27#include <winpr/string.h>
28#include <winpr/windows.h>
29
30#include <time.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <winspool.h>
35
36#include <freerdp/client/printer.h>
37#include <freerdp/utils/helpers.h>
38
39#define WIDEN_INT(x) L##x
40#define WIDEN(x) WIDEN_INT(x)
41#define PRINTER_TAG CHANNELS_TAG("printer.client")
42#ifdef WITH_DEBUG_WINPR
43#define DEBUG_WINPR(...) WLog_DBG(PRINTER_TAG, __VA_ARGS__)
44#else
45#define DEBUG_WINPR(...) \
46 do \
47 { \
48 } while (0)
49#endif
50
51typedef struct
52{
53 rdpPrinterDriver driver;
54
55 size_t id_sequence;
56 size_t references;
57} rdpWinPrinterDriver;
58
59typedef struct
60{
61 rdpPrintJob printjob;
62 DOC_INFO_1 di;
63 DWORD handle;
64
65 void* printjob_object;
66 int printjob_id;
67} rdpWinPrintJob;
68
69typedef struct
70{
71 rdpPrinter printer;
72 HANDLE hPrinter;
73 rdpWinPrintJob* printjob;
74} rdpWinPrinter;
75
76WINPR_ATTR_MALLOC(free, 1)
77static WCHAR* printer_win_get_printjob_name(size_t id)
78{
79 struct tm tres = WINPR_C_ARRAY_INIT;
80 WCHAR* str = nullptr;
81 size_t len = 0;
82
83 const time_t tt = time(nullptr);
84 const errno_t err = localtime_s(&tres, &tt);
85
86 do
87 {
88 if (len > 0)
89 {
90 str = calloc(len + 1, sizeof(WCHAR));
91 if (!str)
92 return nullptr;
93 }
94
95 const int rc = swprintf_s(
96 str, len,
97 WIDEN("%s Print %04d-%02d-%02d %02d-%02d-%02d - Job %") WIDEN(PRIuz) WIDEN("\0"),
98 freerdp_getApplicationDetailsStringW(), tres.tm_year + 1900, tres.tm_mon + 1,
99 tres.tm_mday, tres.tm_hour, tres.tm_min, tres.tm_sec, id);
100 if (rc <= 0)
101 {
102 free(str);
103 return nullptr;
104 }
105 len = WINPR_ASSERTING_INT_CAST(size_t, rc) + 1ull;
106 } while (!str);
107
108 return str;
109}
110
116static UINT printer_win_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
117{
118 LPCVOID pBuf = data;
119 DWORD pcWritten = 0;
120
121 if (size > UINT32_MAX)
122 return ERROR_BAD_ARGUMENTS;
123
124 if (!printjob || !data)
125 return ERROR_BAD_ARGUMENTS;
126
127 rdpWinPrinter* printer = (rdpWinPrinter*)printjob->printer;
128 if (!printer)
129 return ERROR_BAD_ARGUMENTS;
130
131 DWORD cbBuf = WINPR_ASSERTING_INT_CAST(uint32_t, size);
132 if (!WritePrinter(printer->hPrinter, WINPR_CAST_CONST_PTR_AWAY(pBuf, void*), cbBuf, &pcWritten))
133 return ERROR_INTERNAL_ERROR;
134 return CHANNEL_RC_OK;
135}
136
137static void printer_win_close_printjob(rdpPrintJob* printjob)
138{
139 rdpWinPrintJob* win_printjob = (rdpWinPrintJob*)printjob;
140 rdpWinPrinter* win_printer;
141
142 if (!printjob)
143 return;
144
145 win_printer = (rdpWinPrinter*)printjob->printer;
146 if (!win_printer)
147 return;
148
149 if (!EndPagePrinter(win_printer->hPrinter))
150 {
151 }
152
153 if (!EndDocPrinter(win_printer->hPrinter))
154 {
155 }
156
157 win_printer->printjob = nullptr;
158
159 free(win_printjob->di.pDocName);
160 free(win_printjob);
161}
162
163static rdpPrintJob* printer_win_create_printjob(rdpPrinter* printer, UINT32 id)
164{
165 rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
166 rdpWinPrintJob* win_printjob;
167
168 if (win_printer->printjob != nullptr)
169 return nullptr;
170
171 win_printjob = (rdpWinPrintJob*)calloc(1, sizeof(rdpWinPrintJob));
172 if (!win_printjob)
173 return nullptr;
174
175 win_printjob->printjob.id = id;
176 win_printjob->printjob.printer = printer;
177 win_printjob->di.pDocName = printer_win_get_printjob_name(id);
178 win_printjob->di.pDatatype = nullptr;
179 win_printjob->di.pOutputFile = nullptr;
180
181 win_printjob->handle = StartDocPrinter(win_printer->hPrinter, 1, (LPBYTE) & (win_printjob->di));
182
183 if (!win_printjob->handle)
184 {
185 free(win_printjob->di.pDocName);
186 free(win_printjob);
187 return nullptr;
188 }
189
190 if (!StartPagePrinter(win_printer->hPrinter))
191 {
192 free(win_printjob->di.pDocName);
193 free(win_printjob);
194 return nullptr;
195 }
196
197 win_printjob->printjob.Write = printer_win_write_printjob;
198 win_printjob->printjob.Close = printer_win_close_printjob;
199
200 win_printer->printjob = win_printjob;
201
202 return &win_printjob->printjob;
203}
204
205static rdpPrintJob* printer_win_find_printjob(rdpPrinter* printer, UINT32 id)
206{
207 rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
208
209 if (!win_printer->printjob)
210 return nullptr;
211
212 if (win_printer->printjob->printjob.id != id)
213 return nullptr;
214
215 return (rdpPrintJob*)win_printer->printjob;
216}
217
218static void printer_win_free_printer(rdpPrinter* printer)
219{
220 rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
221
222 if (win_printer->printjob)
223 win_printer->printjob->printjob.Close((rdpPrintJob*)win_printer->printjob);
224
225 if (win_printer->hPrinter)
226 ClosePrinter(win_printer->hPrinter);
227
228 if (printer->backend)
229 printer->backend->ReleaseRef(printer->backend);
230
231 free(printer->name);
232 free(printer->driver);
233 free(printer);
234}
235
236static void printer_win_add_ref_printer(rdpPrinter* printer)
237{
238 if (printer)
239 printer->references++;
240}
241
242static void printer_win_release_ref_printer(rdpPrinter* printer)
243{
244 if (!printer)
245 return;
246 if (printer->references <= 1)
247 printer_win_free_printer(printer);
248 else
249 printer->references--;
250}
251
252static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver, const WCHAR* name,
253 const WCHAR* drivername, BOOL is_default)
254{
255 rdpWinPrinter* win_printer;
256 DWORD needed = 0;
257 PRINTER_INFO_2* prninfo = nullptr;
258
259 if (!name)
260 return nullptr;
261
262 win_printer = (rdpWinPrinter*)calloc(1, sizeof(rdpWinPrinter));
263 if (!win_printer)
264 return nullptr;
265
266 win_printer->printer.backend = &win_driver->driver;
267 win_printer->printer.backend->AddRef(win_printer->printer.backend);
268 win_printer->printer.id = win_driver->id_sequence++;
269 win_printer->printer.name = ConvertWCharToUtf8Alloc(name, nullptr);
270 if (!win_printer->printer.name)
271 goto fail;
272 win_printer->printer.is_default = is_default;
273
274 win_printer->printer.CreatePrintJob = printer_win_create_printjob;
275 win_printer->printer.FindPrintJob = printer_win_find_printjob;
276 win_printer->printer.AddRef = printer_win_add_ref_printer;
277 win_printer->printer.ReleaseRef = printer_win_release_ref_printer;
278
279 if (!OpenPrinter(WINPR_CAST_CONST_PTR_AWAY(name, WCHAR*), &(win_printer->hPrinter), nullptr))
280 goto fail;
281
282 /* How many memory should be allocated for printer data */
283 GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, 0, &needed);
284 if (needed == 0)
285 goto fail;
286
287 prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
288 if (!prninfo)
289 goto fail;
290
291 if (!GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, needed, &needed))
292 {
293 GlobalFree(prninfo);
294 goto fail;
295 }
296
297 if (drivername)
298 win_printer->printer.driver = ConvertWCharToUtf8Alloc(drivername, nullptr);
299 else
300 win_printer->printer.driver = ConvertWCharToUtf8Alloc(prninfo->pDriverName, nullptr);
301 GlobalFree(prninfo);
302 if (!win_printer->printer.driver)
303 goto fail;
304
305 win_printer->printer.AddRef(&win_printer->printer);
306 return &win_printer->printer;
307
308fail:
309 printer_win_free_printer(&win_printer->printer);
310 return nullptr;
311}
312
313static void printer_win_release_enum_printers(rdpPrinter** printers)
314{
315 rdpPrinter** cur = printers;
316
317 while ((cur != nullptr) && ((*cur) != nullptr))
318 {
319 if ((*cur)->ReleaseRef)
320 (*cur)->ReleaseRef(*cur);
321 cur++;
322 }
323 free(printers);
324}
325
326static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
327{
328 rdpPrinter** printers;
329 int num_printers;
330 PRINTER_INFO_2* prninfo = nullptr;
331 DWORD needed, returned;
332 BOOL haveDefault = FALSE;
333 LPWSTR defaultPrinter = nullptr;
334
335 GetDefaultPrinter(nullptr, &needed);
336 if (needed)
337 {
338 defaultPrinter = (LPWSTR)calloc(needed, sizeof(WCHAR));
339
340 if (!defaultPrinter)
341 return nullptr;
342
343 if (!GetDefaultPrinter(defaultPrinter, &needed))
344 defaultPrinter[0] = '\0';
345 }
346
347 /* find required size for the buffer */
348 EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 2, nullptr, 0, &needed,
349 &returned);
350
351 /* allocate array of PRINTER_INFO structures */
352 prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
353 if (!prninfo)
354 {
355 free(defaultPrinter);
356 return nullptr;
357 }
358
359 /* call again */
360 if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 2, (LPBYTE)prninfo,
361 needed, &needed, &returned))
362 {
363 }
364
365 printers = (rdpPrinter**)calloc((returned + 1), sizeof(rdpPrinter*));
366 if (!printers)
367 {
368 GlobalFree(prninfo);
369 free(defaultPrinter);
370 return nullptr;
371 }
372
373 num_printers = 0;
374
375 for (int i = 0; i < (int)returned; i++)
376 {
377 rdpPrinter* current = printers[num_printers];
378 current = printer_win_new_printer((rdpWinPrinterDriver*)driver, prninfo[i].pPrinterName,
379 prninfo[i].pDriverName,
380 _wcscmp(prninfo[i].pPrinterName, defaultPrinter) == 0);
381 if (!current)
382 {
383 printer_win_release_enum_printers(printers);
384 printers = nullptr;
385 break;
386 }
387 if (current->is_default)
388 haveDefault = TRUE;
389 printers[num_printers++] = current;
390 }
391
392 if (printers && !haveDefault && (returned > 0))
393 printers[0]->is_default = TRUE;
394
395 GlobalFree(prninfo);
396 free(defaultPrinter);
397 return printers;
398}
399
400static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver, const char* name,
401 const char* driverName, BOOL isDefault)
402{
403 WCHAR* driverNameW = nullptr;
404 WCHAR* nameW = nullptr;
405 rdpWinPrinterDriver* win_driver = (rdpWinPrinterDriver*)driver;
406 rdpPrinter* myPrinter = nullptr;
407
408 if (name)
409 {
410 nameW = ConvertUtf8ToWCharAlloc(name, nullptr);
411 if (!nameW)
412 return nullptr;
413 }
414 if (driverName)
415 {
416 driverNameW = ConvertUtf8ToWCharAlloc(driverName, nullptr);
417 if (!driverNameW)
418 return nullptr;
419 }
420
421 myPrinter = printer_win_new_printer(win_driver, nameW, driverNameW, isDefault);
422 free(driverNameW);
423 free(nameW);
424
425 return myPrinter;
426}
427
428static void printer_win_add_ref_driver(rdpPrinterDriver* driver)
429{
430 rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
431 if (win)
432 win->references++;
433}
434
435/* Singleton */
436static rdpWinPrinterDriver* win_driver = nullptr;
437
438static void printer_win_release_ref_driver(rdpPrinterDriver* driver)
439{
440 rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
441 if (win->references <= 1)
442 {
443 free(win);
444 win_driver = nullptr;
445 }
446 else
447 win->references--;
448}
449
450FREERDP_ENTRY_POINT(UINT VCAPITYPE win_freerdp_printer_client_subsystem_entry(void* arg))
451{
452 rdpPrinterDriver** ppPrinter = (rdpPrinterDriver**)arg;
453 if (!ppPrinter)
454 return ERROR_INVALID_PARAMETER;
455
456 if (!win_driver)
457 {
458 win_driver = (rdpWinPrinterDriver*)calloc(1, sizeof(rdpWinPrinterDriver));
459
460 if (!win_driver)
461 return ERROR_OUTOFMEMORY;
462
463 win_driver->driver.EnumPrinters = printer_win_enum_printers;
464 win_driver->driver.ReleaseEnumPrinters = printer_win_release_enum_printers;
465 win_driver->driver.GetPrinter = printer_win_get_printer;
466
467 win_driver->driver.AddRef = printer_win_add_ref_driver;
468 win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
469
470 win_driver->id_sequence = 1;
471 }
472
473 win_driver->driver.AddRef(&win_driver->driver);
474
475 *ppPrinter = &win_driver->driver;
476 return CHANNEL_RC_OK;
477}