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