FreeRDP
Loading...
Searching...
No Matches
printer_main.c
1
24#include <freerdp/config.h>
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include <winpr/crt.h>
31#include <winpr/assert.h>
32#include <winpr/string.h>
33#include <winpr/synch.h>
34#include <winpr/thread.h>
35#include <winpr/stream.h>
36#include <winpr/interlocked.h>
37#include <winpr/path.h>
38
39#include <freerdp/channels/rdpdr.h>
40#include <freerdp/crypto/crypto.h>
41#include <freerdp/freerdp.h>
42
43#include "../printer.h"
44
45#include <freerdp/client/printer.h>
46
47#include <freerdp/channels/log.h>
48
49#define TAG CHANNELS_TAG("printer.client")
50
51typedef struct
52{
53 DEVICE device;
54
55 rdpPrinter* printer;
56
57 WINPR_PSLIST_HEADER pIrpList;
58
59 HANDLE event;
60 HANDLE stopEvent;
61
62 HANDLE thread;
63 rdpContext* rdpcontext;
64 char port[64];
65 BOOL async;
66} PRINTER_DEVICE;
67
68typedef enum
69{
70 PRN_CONF_PORT = 0,
71 PRN_CONF_PNP = 1,
72 PRN_CONF_DRIVER = 2,
73 PRN_CONF_DATA = 3
74} prn_conf_t;
75
76static const char* filemap[] = { "PortDosName", "PnPName", "DriverName",
77 "CachedPrinterConfigData" };
78
79static char* get_printer_config_path(const rdpSettings* settings, const WCHAR* name, size_t length)
80{
81 const char* path = freerdp_settings_get_string(settings, FreeRDP_ConfigPath);
82 char* dir = GetCombinedPath(path, "printers");
83 char* bname = crypto_base64_encode((const BYTE*)name, length);
84 char* config = GetCombinedPath(dir, bname);
85
86 if (config && !winpr_PathFileExists(config))
87 {
88 if (!winpr_PathMakePath(config, NULL))
89 {
90 free(config);
91 config = NULL;
92 }
93 }
94
95 free(dir);
96 free(bname);
97 return config;
98}
99
100static BOOL printer_write_setting(const char* path, prn_conf_t type, const void* data,
101 size_t length)
102{
103 DWORD written = 0;
104 BOOL rc = FALSE;
105 HANDLE file = NULL;
106 char* base64 = NULL;
107 const char* name = filemap[type];
108 char* abs = GetCombinedPath(path, name);
109
110 if (!abs || (length > INT32_MAX))
111 {
112 free(abs);
113 return FALSE;
114 }
115
116 file = CreateFileA(abs, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
117 free(abs);
118
119 if (file == INVALID_HANDLE_VALUE)
120 return FALSE;
121
122 if (length > 0)
123 {
124 base64 = crypto_base64_encode(data, length);
125
126 if (!base64)
127 goto fail;
128
129 /* base64 char represents 6bit -> 4*(n/3) is the length which is
130 * always smaller than 2*n */
131 const size_t b64len = strnlen(base64, 2 * length);
132 rc = WriteFile(file, base64, (UINT32)b64len, &written, NULL);
133
134 if (b64len != written)
135 rc = FALSE;
136 }
137 else
138 rc = TRUE;
139
140fail:
141 (void)CloseHandle(file);
142 free(base64);
143 return rc;
144}
145
146static BOOL printer_config_valid(const char* path)
147{
148 if (!path)
149 return FALSE;
150
151 if (!winpr_PathFileExists(path))
152 return FALSE;
153
154 return TRUE;
155}
156
157static BOOL printer_read_setting(const char* path, prn_conf_t type, void** data, UINT32* length)
158{
159 DWORD lowSize = 0;
160 DWORD highSize = 0;
161 DWORD read = 0;
162 BOOL rc = FALSE;
163 char* fdata = NULL;
164 const char* name = filemap[type];
165
166 switch (type)
167 {
168 case PRN_CONF_DATA:
169 break;
170 default:
171 WLog_DBG(TAG, "Printer option %s ignored", name);
172 return FALSE;
173 }
174
175 char* abs = GetCombinedPath(path, name);
176 if (!abs)
177 return FALSE;
178
179 HANDLE file =
180 CreateFileA(abs, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
181 free(abs);
182
183 if (file == INVALID_HANDLE_VALUE)
184 return FALSE;
185
186 lowSize = GetFileSize(file, &highSize);
187
188 if ((lowSize == INVALID_FILE_SIZE) || (highSize != 0))
189 goto fail;
190
191 if (lowSize != 0)
192 {
193 fdata = malloc(lowSize);
194
195 if (!fdata)
196 goto fail;
197
198 rc = ReadFile(file, fdata, lowSize, &read, NULL);
199
200 if (lowSize != read)
201 rc = FALSE;
202 }
203
204fail:
205 (void)CloseHandle(file);
206
207 if (rc && (lowSize <= INT_MAX))
208 {
209 size_t blen = 0;
210 crypto_base64_decode(fdata, lowSize, (BYTE**)data, &blen);
211
212 if (*data && (blen > 0))
213 *length = (UINT32)blen;
214 else
215 {
216 rc = FALSE;
217 *length = 0;
218 }
219 }
220 else
221 {
222 *length = 0;
223 *data = NULL;
224 }
225
226 free(fdata);
227 return rc;
228}
229
230static BOOL printer_save_to_config(const rdpSettings* settings, const char* PortDosName,
231 size_t PortDosNameLen, const WCHAR* PnPName, size_t PnPNameLen,
232 const WCHAR* DriverName, size_t DriverNameLen,
233 const WCHAR* PrinterName, size_t PrintNameLen,
234 const BYTE* CachedPrinterConfigData, size_t CacheFieldsLen)
235{
236 BOOL rc = FALSE;
237 char* path = get_printer_config_path(settings, PrinterName, PrintNameLen);
238
239 if (!path)
240 goto fail;
241
242 if (!printer_write_setting(path, PRN_CONF_PORT, PortDosName, PortDosNameLen))
243 goto fail;
244
245 if (!printer_write_setting(path, PRN_CONF_PNP, PnPName, PnPNameLen))
246 goto fail;
247
248 if (!printer_write_setting(path, PRN_CONF_DRIVER, DriverName, DriverNameLen))
249 goto fail;
250
251 if (!printer_write_setting(path, PRN_CONF_DATA, CachedPrinterConfigData, CacheFieldsLen))
252 goto fail;
253
254fail:
255 free(path);
256 return rc;
257}
258
259static BOOL printer_update_to_config(const rdpSettings* settings, const WCHAR* name, size_t length,
260 const BYTE* data, size_t datalen)
261{
262 BOOL rc = FALSE;
263 char* path = get_printer_config_path(settings, name, length);
264 rc = printer_write_setting(path, PRN_CONF_DATA, data, datalen);
265 free(path);
266 return rc;
267}
268
269static BOOL printer_remove_config(const rdpSettings* settings, const WCHAR* name, size_t length)
270{
271 BOOL rc = FALSE;
272 char* path = get_printer_config_path(settings, name, length);
273
274 if (!printer_config_valid(path))
275 goto fail;
276
277 rc = winpr_RemoveDirectory(path);
278fail:
279 free(path);
280 return rc;
281}
282
283static BOOL printer_move_config(const rdpSettings* settings, const WCHAR* oldName, size_t oldLength,
284 const WCHAR* newName, size_t newLength)
285{
286 BOOL rc = FALSE;
287 char* oldPath = get_printer_config_path(settings, oldName, oldLength);
288 char* newPath = get_printer_config_path(settings, newName, newLength);
289
290 if (printer_config_valid(oldPath))
291 rc = winpr_MoveFile(oldPath, newPath);
292
293 free(oldPath);
294 free(newPath);
295 return rc;
296}
297
298static BOOL printer_load_from_config(const rdpSettings* settings, rdpPrinter* printer,
299 PRINTER_DEVICE* printer_dev)
300{
301 BOOL res = FALSE;
302 WCHAR* wname = NULL;
303 size_t wlen = 0;
304 char* path = NULL;
305 UINT32 flags = 0;
306 void* DriverName = NULL;
307 UINT32 DriverNameLen = 0;
308 void* PnPName = NULL;
309 UINT32 PnPNameLen = 0;
310 void* CachedPrinterConfigData = NULL;
311 UINT32 CachedFieldsLen = 0;
312 UINT32 PrinterNameLen = 0;
313
314 if (!settings || !printer || !printer->name)
315 return FALSE;
316
317 wname = ConvertUtf8ToWCharAlloc(printer->name, &wlen);
318
319 if (!wname)
320 goto fail;
321
322 wlen++;
323 path = get_printer_config_path(settings, wname, wlen * sizeof(WCHAR));
324 {
325 const size_t plen = wlen * sizeof(WCHAR);
326 if (plen > UINT32_MAX)
327 goto fail;
328 PrinterNameLen = (UINT32)plen;
329 }
330
331 if (!path)
332 goto fail;
333
334 if (printer->is_default)
335 flags |= RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER;
336
337 if (!printer_read_setting(path, PRN_CONF_PNP, &PnPName, &PnPNameLen))
338 {
339 }
340
341 if (!printer_read_setting(path, PRN_CONF_DRIVER, &DriverName, &DriverNameLen))
342 {
343 size_t len = 0;
344 DriverName = ConvertUtf8ToWCharAlloc(printer->driver, &len);
345 if (!DriverName)
346 goto fail;
347 const size_t dlen = (len + 1) * sizeof(WCHAR);
348 if (dlen > UINT32_MAX)
349 goto fail;
350 DriverNameLen = (UINT32)dlen;
351 }
352
353 if (!printer_read_setting(path, PRN_CONF_DATA, &CachedPrinterConfigData, &CachedFieldsLen))
354 {
355 }
356
357 Stream_SetPosition(printer_dev->device.data, 0);
358
359 if (!Stream_EnsureRemainingCapacity(printer_dev->device.data, 24))
360 goto fail;
361
362 Stream_Write_UINT32(printer_dev->device.data, flags);
363 Stream_Write_UINT32(printer_dev->device.data, 0); /* CodePage, reserved */
364 Stream_Write_UINT32(printer_dev->device.data, PnPNameLen); /* PnPNameLen */
365 Stream_Write_UINT32(printer_dev->device.data, DriverNameLen);
366 Stream_Write_UINT32(printer_dev->device.data, PrinterNameLen);
367 Stream_Write_UINT32(printer_dev->device.data, CachedFieldsLen);
368
369 if (!Stream_EnsureRemainingCapacity(printer_dev->device.data, PnPNameLen))
370 goto fail;
371
372 if (PnPNameLen > 0)
373 Stream_Write(printer_dev->device.data, PnPName, PnPNameLen);
374
375 if (!Stream_EnsureRemainingCapacity(printer_dev->device.data, DriverNameLen))
376 goto fail;
377
378 Stream_Write(printer_dev->device.data, DriverName, DriverNameLen);
379
380 if (!Stream_EnsureRemainingCapacity(printer_dev->device.data, PrinterNameLen))
381 goto fail;
382
383 union
384 {
385 char c[2];
386 WCHAR w;
387 } backslash;
388 backslash.c[0] = '\\';
389 backslash.c[1] = '\0';
390
391 for (WCHAR* wptr = wname; (wptr = _wcschr(wptr, backslash.w));)
392 *wptr = L'_';
393 Stream_Write(printer_dev->device.data, wname, PrinterNameLen);
394
395 if (!Stream_EnsureRemainingCapacity(printer_dev->device.data, CachedFieldsLen))
396 goto fail;
397
398 Stream_Write(printer_dev->device.data, CachedPrinterConfigData, CachedFieldsLen);
399 res = TRUE;
400fail:
401 free(path);
402 free(wname);
403 free(PnPName);
404 free(DriverName);
405 free(CachedPrinterConfigData);
406 return res;
407}
408
409static BOOL printer_save_default_config(const rdpSettings* settings, rdpPrinter* printer)
410{
411 BOOL res = FALSE;
412 WCHAR* wname = NULL;
413 WCHAR* driver = NULL;
414 size_t wlen = 0;
415 size_t dlen = 0;
416 char* path = NULL;
417
418 if (!settings || !printer || !printer->name || !printer->driver)
419 return FALSE;
420
421 wname = ConvertUtf8ToWCharAlloc(printer->name, NULL);
422
423 if (!wname)
424 goto fail;
425
426 driver = ConvertUtf8ToWCharAlloc(printer->driver, NULL);
427
428 if (!driver)
429 goto fail;
430
431 wlen = _wcslen(wname) + 1;
432 dlen = _wcslen(driver) + 1;
433 path = get_printer_config_path(settings, wname, wlen * sizeof(WCHAR));
434
435 if (!path)
436 goto fail;
437
438 if (dlen > 1)
439 {
440 if (!printer_write_setting(path, PRN_CONF_DRIVER, driver, dlen * sizeof(WCHAR)))
441 goto fail;
442 }
443
444 res = TRUE;
445fail:
446 free(path);
447 free(wname);
448 free(driver);
449 return res;
450}
451
457static UINT printer_process_irp_create(PRINTER_DEVICE* printer_dev, IRP* irp)
458{
459 rdpPrintJob* printjob = NULL;
460
461 WINPR_ASSERT(printer_dev);
462 WINPR_ASSERT(irp);
463
464 if (printer_dev->printer)
465 {
466 WINPR_ASSERT(printer_dev->printer->CreatePrintJob);
467 printjob =
468 printer_dev->printer->CreatePrintJob(printer_dev->printer, irp->devman->id_sequence++);
469 }
470
471 if (printjob)
472 {
473 Stream_Write_UINT32(irp->output, printjob->id); /* FileId */
474 }
475 else
476 {
477 Stream_Write_UINT32(irp->output, 0); /* FileId */
478 irp->IoStatus = STATUS_PRINT_QUEUE_FULL;
479 }
480
481 WINPR_ASSERT(irp->Complete);
482 return irp->Complete(irp);
483}
484
490static UINT printer_process_irp_close(PRINTER_DEVICE* printer_dev, IRP* irp)
491{
492 rdpPrintJob* printjob = NULL;
493
494 WINPR_ASSERT(printer_dev);
495 WINPR_ASSERT(irp);
496
497 if (printer_dev->printer)
498 {
499 WINPR_ASSERT(printer_dev->printer->FindPrintJob);
500 printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
501 }
502
503 if (!printjob)
504 {
505 irp->IoStatus = STATUS_UNSUCCESSFUL;
506 }
507 else
508 {
509 printjob->Close(printjob);
510 }
511
512 Stream_Zero(irp->output, 4); /* Padding(4) */
513 WINPR_ASSERT(irp->Complete);
514 return irp->Complete(irp);
515}
516
522static UINT printer_process_irp_write(PRINTER_DEVICE* printer_dev, IRP* irp)
523{
524 UINT32 Length = 0;
525 UINT64 Offset = 0;
526 rdpPrintJob* printjob = NULL;
527 UINT error = CHANNEL_RC_OK;
528
529 WINPR_ASSERT(printer_dev);
530 WINPR_ASSERT(irp);
531
532 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 32))
533 return ERROR_INVALID_DATA;
534 Stream_Read_UINT32(irp->input, Length);
535 Stream_Read_UINT64(irp->input, Offset);
536 (void)Offset; /* [MS-RDPEPC] 2.2.2.9 Server Printer Write Request (DR_PRN_WRITE_REQ)
537 * reserved for future use, ignore */
538 Stream_Seek(irp->input, 20); /* Padding */
539 const void* ptr = Stream_ConstPointer(irp->input);
540 if (!Stream_SafeSeek(irp->input, Length))
541 return ERROR_INVALID_DATA;
542 if (printer_dev->printer)
543 {
544 WINPR_ASSERT(printer_dev->printer->FindPrintJob);
545 printjob = printer_dev->printer->FindPrintJob(printer_dev->printer, irp->FileId);
546 }
547
548 if (!printjob)
549 {
550 irp->IoStatus = STATUS_UNSUCCESSFUL;
551 Length = 0;
552 }
553 else
554 {
555 error = printjob->Write(printjob, ptr, Length);
556 }
557
558 if (error)
559 {
560 WLog_ERR(TAG, "printjob->Write failed with error %" PRIu32 "!", error);
561 return error;
562 }
563
564 Stream_Write_UINT32(irp->output, Length);
565 Stream_Write_UINT8(irp->output, 0); /* Padding */
566
567 WINPR_ASSERT(irp->Complete);
568 return irp->Complete(irp);
569}
570
576static UINT printer_process_irp_device_control(WINPR_ATTR_UNUSED PRINTER_DEVICE* printer_dev,
577 IRP* irp)
578{
579 WINPR_ASSERT(printer_dev);
580 WINPR_ASSERT(irp);
581
582 Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
583
584 WINPR_ASSERT(irp->Complete);
585 return irp->Complete(irp);
586}
587
593static UINT printer_process_irp(PRINTER_DEVICE* printer_dev, IRP* irp)
594{
595 UINT error = 0;
596
597 WINPR_ASSERT(printer_dev);
598 WINPR_ASSERT(irp);
599
600 switch (irp->MajorFunction)
601 {
602 case IRP_MJ_CREATE:
603 if ((error = printer_process_irp_create(printer_dev, irp)))
604 {
605 WLog_ERR(TAG, "printer_process_irp_create failed with error %" PRIu32 "!", error);
606 return error;
607 }
608
609 break;
610
611 case IRP_MJ_CLOSE:
612 if ((error = printer_process_irp_close(printer_dev, irp)))
613 {
614 WLog_ERR(TAG, "printer_process_irp_close failed with error %" PRIu32 "!", error);
615 return error;
616 }
617
618 break;
619
620 case IRP_MJ_WRITE:
621 if ((error = printer_process_irp_write(printer_dev, irp)))
622 {
623 WLog_ERR(TAG, "printer_process_irp_write failed with error %" PRIu32 "!", error);
624 return error;
625 }
626
627 break;
628
629 case IRP_MJ_DEVICE_CONTROL:
630 if ((error = printer_process_irp_device_control(printer_dev, irp)))
631 {
632 WLog_ERR(TAG, "printer_process_irp_device_control failed with error %" PRIu32 "!",
633 error);
634 return error;
635 }
636
637 break;
638
639 default:
640 irp->IoStatus = STATUS_NOT_SUPPORTED;
641 WINPR_ASSERT(irp->Complete);
642 return irp->Complete(irp);
643 }
644
645 return CHANNEL_RC_OK;
646}
647
648static DWORD WINAPI printer_thread_func(LPVOID arg)
649{
650 IRP* irp = NULL;
651 PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)arg;
652 UINT error = CHANNEL_RC_OK;
653
654 WINPR_ASSERT(printer_dev);
655
656 while (1)
657 {
658 HANDLE obj[] = { printer_dev->event, printer_dev->stopEvent };
659 DWORD rc = WaitForMultipleObjects(ARRAYSIZE(obj), obj, FALSE, INFINITE);
660
661 if (rc == WAIT_FAILED)
662 {
663 error = GetLastError();
664 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "!", error);
665 break;
666 }
667
668 if (rc == WAIT_OBJECT_0 + 1)
669 break;
670 else if (rc != WAIT_OBJECT_0)
671 continue;
672
673 (void)ResetEvent(printer_dev->event);
674 irp = (IRP*)InterlockedPopEntrySList(printer_dev->pIrpList);
675
676 if (irp == NULL)
677 {
678 WLog_ERR(TAG, "InterlockedPopEntrySList failed!");
679 error = ERROR_INTERNAL_ERROR;
680 break;
681 }
682
683 if ((error = printer_process_irp(printer_dev, irp)))
684 {
685 WLog_ERR(TAG, "printer_process_irp failed with error %" PRIu32 "!", error);
686 break;
687 }
688 }
689
690 if (error && printer_dev->rdpcontext)
691 setChannelError(printer_dev->rdpcontext, error, "printer_thread_func reported an error");
692
693 ExitThread(error);
694 return error;
695}
696
702static UINT printer_irp_request(DEVICE* device, IRP* irp)
703{
704 PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
705
706 WINPR_ASSERT(printer_dev);
707 WINPR_ASSERT(irp);
708
709 if (printer_dev->async)
710 {
711 InterlockedPushEntrySList(printer_dev->pIrpList, &(irp->ItemEntry));
712 (void)SetEvent(printer_dev->event);
713 }
714 else
715 {
716 UINT error = printer_process_irp(printer_dev, irp);
717 if (error)
718 {
719 WLog_ERR(TAG, "printer_process_irp failed with error %" PRIu32 "!", error);
720 return error;
721 }
722 }
723
724 return CHANNEL_RC_OK;
725}
726
727static UINT printer_custom_component(DEVICE* device, UINT16 component, UINT16 packetId, wStream* s)
728{
729 UINT32 eventID = 0;
730 PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
731
732 WINPR_ASSERT(printer_dev);
733 WINPR_ASSERT(printer_dev->rdpcontext);
734
735 const rdpSettings* settings = printer_dev->rdpcontext->settings;
736 WINPR_ASSERT(settings);
737
738 if (component != RDPDR_CTYP_PRN)
739 return ERROR_INVALID_DATA;
740
741 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
742 return ERROR_INVALID_DATA;
743
744 Stream_Read_UINT32(s, eventID);
745
746 switch (packetId)
747 {
748 case PAKID_PRN_CACHE_DATA:
749 switch (eventID)
750 {
751 case RDPDR_ADD_PRINTER_EVENT:
752 {
753 char PortDosName[8];
754 UINT32 PnPNameLen = 0;
755 UINT32 DriverNameLen = 0;
756 UINT32 PrintNameLen = 0;
757 UINT32 CacheFieldsLen = 0;
758 const WCHAR* PnPName = NULL;
759 const WCHAR* DriverName = NULL;
760 const WCHAR* PrinterName = NULL;
761 const BYTE* CachedPrinterConfigData = NULL;
762
763 if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
764 return ERROR_INVALID_DATA;
765
766 Stream_Read(s, PortDosName, sizeof(PortDosName));
767 Stream_Read_UINT32(s, PnPNameLen);
768 Stream_Read_UINT32(s, DriverNameLen);
769 Stream_Read_UINT32(s, PrintNameLen);
770 Stream_Read_UINT32(s, CacheFieldsLen);
771
772 if (!Stream_CheckAndLogRequiredLength(TAG, s, PnPNameLen))
773 return ERROR_INVALID_DATA;
774
775 PnPName = Stream_ConstPointer(s);
776 Stream_Seek(s, PnPNameLen);
777
778 if (!Stream_CheckAndLogRequiredLength(TAG, s, DriverNameLen))
779 return ERROR_INVALID_DATA;
780
781 DriverName = Stream_ConstPointer(s);
782 Stream_Seek(s, DriverNameLen);
783
784 if (!Stream_CheckAndLogRequiredLength(TAG, s, PrintNameLen))
785 return ERROR_INVALID_DATA;
786
787 PrinterName = Stream_ConstPointer(s);
788 Stream_Seek(s, PrintNameLen);
789
790 if (!Stream_CheckAndLogRequiredLength(TAG, s, CacheFieldsLen))
791 return ERROR_INVALID_DATA;
792
793 CachedPrinterConfigData = Stream_ConstPointer(s);
794 Stream_Seek(s, CacheFieldsLen);
795
796 if (!printer_save_to_config(settings, PortDosName, sizeof(PortDosName), PnPName,
797 PnPNameLen, DriverName, DriverNameLen, PrinterName,
798 PrintNameLen, CachedPrinterConfigData,
799 CacheFieldsLen))
800 return ERROR_INTERNAL_ERROR;
801 }
802 break;
803
804 case RDPDR_UPDATE_PRINTER_EVENT:
805 {
806 UINT32 PrinterNameLen = 0;
807 UINT32 ConfigDataLen = 0;
808 const WCHAR* PrinterName = NULL;
809 const BYTE* ConfigData = NULL;
810
811 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
812 return ERROR_INVALID_DATA;
813
814 Stream_Read_UINT32(s, PrinterNameLen);
815 Stream_Read_UINT32(s, ConfigDataLen);
816
817 if (!Stream_CheckAndLogRequiredLength(TAG, s, PrinterNameLen))
818 return ERROR_INVALID_DATA;
819
820 PrinterName = Stream_ConstPointer(s);
821 Stream_Seek(s, PrinterNameLen);
822
823 if (!Stream_CheckAndLogRequiredLength(TAG, s, ConfigDataLen))
824 return ERROR_INVALID_DATA;
825
826 ConfigData = Stream_ConstPointer(s);
827 Stream_Seek(s, ConfigDataLen);
828
829 if (!printer_update_to_config(settings, PrinterName, PrinterNameLen, ConfigData,
830 ConfigDataLen))
831 return ERROR_INTERNAL_ERROR;
832 }
833 break;
834
835 case RDPDR_DELETE_PRINTER_EVENT:
836 {
837 UINT32 PrinterNameLen = 0;
838 const WCHAR* PrinterName = NULL;
839
840 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
841 return ERROR_INVALID_DATA;
842
843 Stream_Read_UINT32(s, PrinterNameLen);
844
845 if (!Stream_CheckAndLogRequiredLength(TAG, s, PrinterNameLen))
846 return ERROR_INVALID_DATA;
847
848 PrinterName = Stream_ConstPointer(s);
849 Stream_Seek(s, PrinterNameLen);
850 printer_remove_config(settings, PrinterName, PrinterNameLen);
851 }
852 break;
853
854 case RDPDR_RENAME_PRINTER_EVENT:
855 {
856 UINT32 OldPrinterNameLen = 0;
857 UINT32 NewPrinterNameLen = 0;
858 const WCHAR* OldPrinterName = NULL;
859 const WCHAR* NewPrinterName = NULL;
860
861 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
862 return ERROR_INVALID_DATA;
863
864 Stream_Read_UINT32(s, OldPrinterNameLen);
865 Stream_Read_UINT32(s, NewPrinterNameLen);
866
867 if (!Stream_CheckAndLogRequiredLength(TAG, s, OldPrinterNameLen))
868 return ERROR_INVALID_DATA;
869
870 OldPrinterName = Stream_ConstPointer(s);
871 Stream_Seek(s, OldPrinterNameLen);
872
873 if (!Stream_CheckAndLogRequiredLength(TAG, s, NewPrinterNameLen))
874 return ERROR_INVALID_DATA;
875
876 NewPrinterName = Stream_ConstPointer(s);
877 Stream_Seek(s, NewPrinterNameLen);
878
879 if (!printer_move_config(settings, OldPrinterName, OldPrinterNameLen,
880 NewPrinterName, NewPrinterNameLen))
881 return ERROR_INTERNAL_ERROR;
882 }
883 break;
884
885 default:
886 WLog_ERR(TAG, "Unknown cache data eventID: 0x%08" PRIX32 "", eventID);
887 return ERROR_INVALID_DATA;
888 }
889
890 break;
891
892 case PAKID_PRN_USING_XPS:
893 {
894 UINT32 flags = 0;
895
896 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
897 return ERROR_INVALID_DATA;
898
899 Stream_Read_UINT32(s, flags);
900 WLog_ERR(TAG,
901 "Ignoring unhandled message PAKID_PRN_USING_XPS [printerID=%08" PRIx32
902 ", flags=%08" PRIx32 "]",
903 eventID, flags);
904 }
905 break;
906
907 default:
908 WLog_ERR(TAG, "Unknown printing component packetID: 0x%04" PRIX16 "", packetId);
909 return ERROR_INVALID_DATA;
910 }
911
912 return CHANNEL_RC_OK;
913}
914
920static UINT printer_free(DEVICE* device)
921{
922 IRP* irp = NULL;
923 PRINTER_DEVICE* printer_dev = (PRINTER_DEVICE*)device;
924 UINT error = 0;
925
926 WINPR_ASSERT(printer_dev);
927
928 if (printer_dev->async)
929 {
930 (void)SetEvent(printer_dev->stopEvent);
931
932 if (WaitForSingleObject(printer_dev->thread, INFINITE) == WAIT_FAILED)
933 {
934 error = GetLastError();
935 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
936
937 /* The analyzer is confused by this premature return value.
938 * Since this case can not be handled gracefully silence the
939 * analyzer here. */
940#ifndef __clang_analyzer__
941 return error;
942#endif
943 }
944
945 while ((irp = (IRP*)InterlockedPopEntrySList(printer_dev->pIrpList)) != NULL)
946 {
947 WINPR_ASSERT(irp->Discard);
948 irp->Discard(irp);
949 }
950
951 (void)CloseHandle(printer_dev->thread);
952 (void)CloseHandle(printer_dev->stopEvent);
953 (void)CloseHandle(printer_dev->event);
954 winpr_aligned_free(printer_dev->pIrpList);
955 }
956
957 if (printer_dev->printer)
958 {
959 WINPR_ASSERT(printer_dev->printer->ReleaseRef);
960 printer_dev->printer->ReleaseRef(printer_dev->printer);
961 }
962
963 Stream_Free(printer_dev->device.data, TRUE);
964 free(printer_dev);
965 return CHANNEL_RC_OK;
966}
967
973static UINT printer_register(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, rdpPrinter* printer)
974{
975 PRINTER_DEVICE* printer_dev = NULL;
976 UINT error = ERROR_INTERNAL_ERROR;
977
978 WINPR_ASSERT(pEntryPoints);
979 WINPR_ASSERT(printer);
980
981 printer_dev = (PRINTER_DEVICE*)calloc(1, sizeof(PRINTER_DEVICE));
982
983 if (!printer_dev)
984 {
985 WLog_ERR(TAG, "calloc failed!");
986 return CHANNEL_RC_NO_MEMORY;
987 }
988
989 printer_dev->device.data = Stream_New(NULL, 1024);
990
991 if (!printer_dev->device.data)
992 goto error_out;
993
994 (void)sprintf_s(printer_dev->port, sizeof(printer_dev->port), "PRN%" PRIuz, printer->id);
995 printer_dev->device.type = RDPDR_DTYP_PRINT;
996 printer_dev->device.name = printer_dev->port;
997 printer_dev->device.IRPRequest = printer_irp_request;
998 printer_dev->device.CustomComponentRequest = printer_custom_component;
999 printer_dev->device.Free = printer_free;
1000 printer_dev->rdpcontext = pEntryPoints->rdpcontext;
1001 printer_dev->printer = printer;
1002
1003 if (!freerdp_settings_get_bool(pEntryPoints->rdpcontext->settings,
1004 FreeRDP_SynchronousStaticChannels))
1005 printer_dev->async = TRUE;
1006
1007 if (!printer_load_from_config(pEntryPoints->rdpcontext->settings, printer, printer_dev))
1008 goto error_out;
1009
1010 if (printer_dev->async)
1011 {
1012 printer_dev->pIrpList = (WINPR_PSLIST_HEADER)winpr_aligned_malloc(
1013 sizeof(WINPR_SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
1014
1015 if (!printer_dev->pIrpList)
1016 {
1017 WLog_ERR(TAG, "_aligned_malloc failed!");
1018 error = CHANNEL_RC_NO_MEMORY;
1019 goto error_out;
1020 }
1021
1022 InitializeSListHead(printer_dev->pIrpList);
1023
1024 printer_dev->event = CreateEvent(NULL, TRUE, FALSE, NULL);
1025 if (!printer_dev->event)
1026 {
1027 WLog_ERR(TAG, "CreateEvent failed!");
1028 error = ERROR_INTERNAL_ERROR;
1029 goto error_out;
1030 }
1031
1032 printer_dev->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1033 if (!printer_dev->stopEvent)
1034 {
1035 WLog_ERR(TAG, "CreateEvent failed!");
1036 error = ERROR_INTERNAL_ERROR;
1037 goto error_out;
1038 }
1039 }
1040
1041 error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &printer_dev->device);
1042 if (error)
1043 {
1044 WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
1045 goto error_out;
1046 }
1047
1048 if (printer_dev->async)
1049 {
1050 printer_dev->thread =
1051 CreateThread(NULL, 0, printer_thread_func, (void*)printer_dev, 0, NULL);
1052 if (!printer_dev->thread)
1053 {
1054 WLog_ERR(TAG, "CreateThread failed!");
1055 error = ERROR_INTERNAL_ERROR;
1056 goto error_out;
1057 }
1058 }
1059
1060 WINPR_ASSERT(printer->AddRef);
1061 printer->AddRef(printer);
1062 return CHANNEL_RC_OK;
1063error_out:
1064 printer_free(&printer_dev->device);
1065 return error;
1066}
1067
1068static rdpPrinterDriver* printer_load_backend(const char* backend)
1069{
1070 typedef UINT(VCAPITYPE * backend_load_t)(rdpPrinterDriver**);
1071 PVIRTUALCHANNELENTRY entry = freerdp_load_channel_addin_entry("printer", backend, NULL, 0);
1072 backend_load_t func = WINPR_FUNC_PTR_CAST(entry, backend_load_t);
1073 if (!func)
1074 return NULL;
1075
1076 rdpPrinterDriver* printer = NULL;
1077 const UINT rc = func(&printer);
1078 if (rc != CHANNEL_RC_OK)
1079 return NULL;
1080
1081 return printer;
1082}
1083
1089FREERDP_ENTRY_POINT(
1090 UINT VCAPITYPE printer_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
1091{
1092 char* name = NULL;
1093 char* driver_name = NULL;
1094 BOOL default_backend = TRUE;
1095 RDPDR_PRINTER* device = NULL;
1096 rdpPrinterDriver* driver = NULL;
1097 UINT error = CHANNEL_RC_OK;
1098
1099 if (!pEntryPoints || !pEntryPoints->device)
1100 return ERROR_INVALID_PARAMETER;
1101
1102 device = (RDPDR_PRINTER*)pEntryPoints->device;
1103 name = device->device.Name;
1104 driver_name = _strdup(device->DriverName);
1105
1106 /* Secondary argument is one of the following:
1107 *
1108 * <driver_name> ... name of a printer driver
1109 * <driver_name>:<backend_name> ... name of a printer driver and local printer backend to use
1110 */
1111 if (driver_name)
1112 {
1113 char* sep = strstr(driver_name, ":");
1114 if (sep)
1115 {
1116 const char* backend = sep + 1;
1117 *sep = '\0';
1118 driver = printer_load_backend(backend);
1119 default_backend = FALSE;
1120 }
1121 }
1122
1123 if (!driver && default_backend)
1124 {
1125 const char* backend =
1126#if defined(WITH_CUPS)
1127 "cups"
1128#elif defined(_WIN32)
1129 "win"
1130#else
1131 ""
1132#endif
1133 ;
1134
1135 driver = printer_load_backend(backend);
1136 }
1137
1138 if (!driver)
1139 {
1140 WLog_ERR(TAG, "Could not get a printer driver!");
1141 error = CHANNEL_RC_INITIALIZATION_ERROR;
1142 goto fail;
1143 }
1144
1145 if (name && name[0])
1146 {
1147 WINPR_ASSERT(driver->GetPrinter);
1148 rdpPrinter* printer = driver->GetPrinter(driver, name, driver_name, device->IsDefault);
1149
1150 if (!printer)
1151 {
1152 WLog_ERR(TAG, "Could not get printer %s!", name);
1153 error = CHANNEL_RC_INITIALIZATION_ERROR;
1154 goto fail;
1155 }
1156
1157 WINPR_ASSERT(printer->ReleaseRef);
1158 if (!printer_save_default_config(pEntryPoints->rdpcontext->settings, printer))
1159 {
1160 error = CHANNEL_RC_INITIALIZATION_ERROR;
1161 printer->ReleaseRef(printer);
1162 goto fail;
1163 }
1164
1165 error = printer_register(pEntryPoints, printer);
1166 printer->ReleaseRef(printer);
1167 if (error)
1168 {
1169 WLog_ERR(TAG, "printer_register failed with error %" PRIu32 "!", error);
1170 goto fail;
1171 }
1172 }
1173 else
1174 {
1175 WINPR_ASSERT(driver->EnumPrinters);
1176 rdpPrinter** printers = driver->EnumPrinters(driver);
1177 if (printers)
1178 {
1179 for (rdpPrinter** current = printers; *current; ++current)
1180 {
1181 error = printer_register(pEntryPoints, *current);
1182 if (error)
1183 {
1184 WLog_ERR(TAG, "printer_register failed with error %" PRIu32 "!", error);
1185 break;
1186 }
1187 }
1188 }
1189 else
1190 {
1191 WLog_ERR(TAG, "Failed to enumerate printers!");
1192 error = CHANNEL_RC_INITIALIZATION_ERROR;
1193 }
1194
1195 WINPR_ASSERT(driver->ReleaseEnumPrinters);
1196 driver->ReleaseEnumPrinters(printers);
1197 }
1198
1199fail:
1200 free(driver_name);
1201 if (driver)
1202 {
1203 WINPR_ASSERT(driver->ReleaseRef);
1204 driver->ReleaseRef(driver);
1205 }
1206
1207 return error;
1208}
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.