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.id = win_driver->id_sequence++;
268 win_printer->printer.name = ConvertWCharToUtf8Alloc(name, nullptr);
269 if (!win_printer->printer.name)
270 goto fail;
271
272 if (!win_printer->printer.name)
273 goto fail;
274 win_printer->printer.is_default = is_default;
275
276 win_printer->printer.CreatePrintJob = printer_win_create_printjob;
277 win_printer->printer.FindPrintJob = printer_win_find_printjob;
278 win_printer->printer.AddRef = printer_win_add_ref_printer;
279 win_printer->printer.ReleaseRef = printer_win_release_ref_printer;
280
281 if (!OpenPrinter(WINPR_CAST_CONST_PTR_AWAY(name, WCHAR*), &(win_printer->hPrinter), nullptr))
282 goto fail;
283
284 /* How many memory should be allocated for printer data */
285 GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, 0, &needed);
286 if (needed == 0)
287 goto fail;
288
289 prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
290 if (!prninfo)
291 goto fail;
292
293 if (!GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, needed, &needed))
294 {
295 GlobalFree(prninfo);
296 goto fail;
297 }
298
299 if (drivername)
300 win_printer->printer.driver = ConvertWCharToUtf8Alloc(drivername, nullptr);
301 else
302 win_printer->printer.driver = ConvertWCharToUtf8Alloc(prninfo->pDriverName, nullptr);
303 GlobalFree(prninfo);
304 if (!win_printer->printer.driver)
305 goto fail;
306
307 win_printer->printer.AddRef(&win_printer->printer);
308 win_printer->printer.backend->AddRef(win_printer->printer.backend);
309 return &win_printer->printer;
310
311fail:
312 printer_win_free_printer(&win_printer->printer);
313 return nullptr;
314}
315
316static void printer_win_release_enum_printers(rdpPrinter** printers)
317{
318 rdpPrinter** cur = printers;
319
320 while ((cur != nullptr) && ((*cur) != nullptr))
321 {
322 if ((*cur)->ReleaseRef)
323 (*cur)->ReleaseRef(*cur);
324 cur++;
325 }
326 free(printers);
327}
328
329static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
330{
331 rdpPrinter** printers;
332 int num_printers;
333 PRINTER_INFO_2* prninfo = nullptr;
334 DWORD needed, returned;
335 BOOL haveDefault = FALSE;
336 LPWSTR defaultPrinter = nullptr;
337
338 GetDefaultPrinter(nullptr, &needed);
339 if (needed)
340 {
341 defaultPrinter = (LPWSTR)calloc(needed, sizeof(WCHAR));
342
343 if (!defaultPrinter)
344 return nullptr;
345
346 if (!GetDefaultPrinter(defaultPrinter, &needed))
347 defaultPrinter[0] = '\0';
348 }
349
350 /* find required size for the buffer */
351 EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 2, nullptr, 0, &needed,
352 &returned);
353
354 /* allocate array of PRINTER_INFO structures */
355 prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
356 if (!prninfo)
357 {
358 free(defaultPrinter);
359 return nullptr;
360 }
361
362 /* call again */
363 if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 2, (LPBYTE)prninfo,
364 needed, &needed, &returned))
365 {
366 }
367
368 printers = (rdpPrinter**)calloc((returned + 1), sizeof(rdpPrinter*));
369 if (!printers)
370 {
371 GlobalFree(prninfo);
372 free(defaultPrinter);
373 return nullptr;
374 }
375
376 num_printers = 0;
377
378 for (int i = 0; i < (int)returned; i++)
379 {
380 rdpPrinter* current = printers[num_printers];
381 current = printer_win_new_printer((rdpWinPrinterDriver*)driver, prninfo[i].pPrinterName,
382 prninfo[i].pDriverName,
383 _wcscmp(prninfo[i].pPrinterName, defaultPrinter) == 0);
384 if (!current)
385 {
386 printer_win_release_enum_printers(printers);
387 printers = nullptr;
388 break;
389 }
390 if (current->is_default)
391 haveDefault = TRUE;
392 printers[num_printers++] = current;
393 }
394
395 if (!haveDefault && (returned > 0))
396 printers[0]->is_default = TRUE;
397
398 GlobalFree(prninfo);
399 free(defaultPrinter);
400 return printers;
401}
402
403static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver, const char* name,
404 const char* driverName, BOOL isDefault)
405{
406 WCHAR* driverNameW = nullptr;
407 WCHAR* nameW = nullptr;
408 rdpWinPrinterDriver* win_driver = (rdpWinPrinterDriver*)driver;
409 rdpPrinter* myPrinter = nullptr;
410
411 if (name)
412 {
413 nameW = ConvertUtf8ToWCharAlloc(name, nullptr);
414 if (!nameW)
415 return nullptr;
416 }
417 if (driverName)
418 {
419 driverNameW = ConvertUtf8ToWCharAlloc(driverName, nullptr);
420 if (!driverNameW)
421 return nullptr;
422 }
423
424 myPrinter = printer_win_new_printer(win_driver, nameW, driverNameW, isDefault);
425 free(driverNameW);
426 free(nameW);
427
428 return myPrinter;
429}
430
431static void printer_win_add_ref_driver(rdpPrinterDriver* driver)
432{
433 rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
434 if (win)
435 win->references++;
436}
437
438/* Singleton */
439static rdpWinPrinterDriver* win_driver = nullptr;
440
441static void printer_win_release_ref_driver(rdpPrinterDriver* driver)
442{
443 rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
444 if (win->references <= 1)
445 {
446 free(win);
447 win_driver = nullptr;
448 }
449 else
450 win->references--;
451}
452
453FREERDP_ENTRY_POINT(UINT VCAPITYPE win_freerdp_printer_client_subsystem_entry(void* arg))
454{
455 rdpPrinterDriver** ppPrinter = (rdpPrinterDriver**)arg;
456 if (!ppPrinter)
457 return ERROR_INVALID_PARAMETER;
458
459 if (!win_driver)
460 {
461 win_driver = (rdpWinPrinterDriver*)calloc(1, sizeof(rdpWinPrinterDriver));
462
463 if (!win_driver)
464 return ERROR_OUTOFMEMORY;
465
466 win_driver->driver.EnumPrinters = printer_win_enum_printers;
467 win_driver->driver.ReleaseEnumPrinters = printer_win_release_enum_printers;
468 win_driver->driver.GetPrinter = printer_win_get_printer;
469
470 win_driver->driver.AddRef = printer_win_add_ref_driver;
471 win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
472
473 win_driver->id_sequence = 1;
474 }
475
476 win_driver->driver.AddRef(&win_driver->driver);
477
478 *ppPrinter = &win_driver->driver;
479 return CHANNEL_RC_OK;
480}