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