1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * Print Virtual Channel - WIN driver
4 *
5 * Copyright 2012 Gerald Richter
6 * Copyright 2015 Thincast Technologies GmbH
7 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
8 * Copyright 2016 Armin Novak <armin.novak@gmail.com>
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <winpr/crt.h>
28 #include <winpr/string.h>
29 #include <winpr/windows.h>
30
31 #include <time.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <winspool.h>
36
37 #include <freerdp/client/printer.h>
38
39 #define PRINTER_TAG CHANNELS_TAG("printer.client")
40 #ifdef WITH_DEBUG_WINPR
41 #define DEBUG_WINPR(...) WLog_DBG(PRINTER_TAG, __VA_ARGS__)
42 #else
43 #define DEBUG_WINPR(...) \
44 do \
45 { \
46 } while (0)
47 #endif
48
49 typedef struct rdp_win_printer_driver rdpWinPrinterDriver;
50 typedef struct rdp_win_printer rdpWinPrinter;
51 typedef struct rdp_win_print_job rdpWinPrintJob;
52
53 struct rdp_win_printer_driver
54 {
55 rdpPrinterDriver driver;
56
57 size_t id_sequence;
58 size_t references;
59 };
60
61 struct rdp_win_printer
62 {
63 rdpPrinter printer;
64 HANDLE hPrinter;
65 rdpWinPrintJob* printjob;
66 };
67
68 struct rdp_win_print_job
69 {
70 rdpPrintJob printjob;
71 DOC_INFO_1 di;
72 DWORD handle;
73
74 void* printjob_object;
75 int printjob_id;
76 };
77
printer_win_get_printjob_name(size_t id)78 static WCHAR* printer_win_get_printjob_name(size_t id)
79 {
80 time_t tt;
81 struct tm tres;
82 struct tm* t;
83 WCHAR* str;
84 size_t len = 1024;
85 int rc;
86
87 tt = time(NULL);
88 t = localtime_s(&tt, &tres);
89
90 str = calloc(len, sizeof(WCHAR));
91 if (!str)
92 return NULL;
93
94 rc = swprintf_s(str, len, L"FreeRDP Print %04d-%02d-%02d% 02d-%02d-%02d - Job %lu\0",
95 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec,
96 id);
97
98 return str;
99 }
100
101 /**
102 * Function description
103 *
104 * @return 0 on success, otherwise a Win32 error code
105 */
printer_win_write_printjob(rdpPrintJob * printjob,const BYTE * data,size_t size)106 static UINT printer_win_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
107 {
108 rdpWinPrinter* printer;
109 LPCVOID pBuf = data;
110 DWORD cbBuf = size;
111 DWORD pcWritten;
112
113 if (!printjob || !data)
114 return ERROR_BAD_ARGUMENTS;
115
116 printer = (rdpWinPrinter*)printjob->printer;
117 if (!printer)
118 return ERROR_BAD_ARGUMENTS;
119
120 if (!WritePrinter(printer->hPrinter, pBuf, cbBuf, &pcWritten))
121 return ERROR_INTERNAL_ERROR;
122 return CHANNEL_RC_OK;
123 }
124
printer_win_close_printjob(rdpPrintJob * printjob)125 static void printer_win_close_printjob(rdpPrintJob* printjob)
126 {
127 rdpWinPrintJob* win_printjob = (rdpWinPrintJob*)printjob;
128 rdpWinPrinter* win_printer;
129
130 if (!printjob)
131 return;
132
133 win_printer = (rdpWinPrinter*)printjob->printer;
134 if (!win_printer)
135 return;
136
137 if (!EndPagePrinter(win_printer->hPrinter))
138 {
139 }
140
141 if (!ClosePrinter(win_printer->hPrinter))
142 {
143 }
144
145 win_printer->printjob = NULL;
146
147 free(win_printjob->di.pDocName);
148 free(win_printjob);
149 }
150
printer_win_create_printjob(rdpPrinter * printer,UINT32 id)151 static rdpPrintJob* printer_win_create_printjob(rdpPrinter* printer, UINT32 id)
152 {
153 rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
154 rdpWinPrintJob* win_printjob;
155
156 if (win_printer->printjob != NULL)
157 return NULL;
158
159 win_printjob = (rdpWinPrintJob*)calloc(1, sizeof(rdpWinPrintJob));
160 if (!win_printjob)
161 return NULL;
162
163 win_printjob->printjob.id = id;
164 win_printjob->printjob.printer = printer;
165 win_printjob->di.pDocName = printer_win_get_printjob_name(id);
166 win_printjob->di.pDatatype = NULL;
167 win_printjob->di.pOutputFile = NULL;
168
169 win_printjob->handle = StartDocPrinter(win_printer->hPrinter, 1, (LPBYTE) & (win_printjob->di));
170
171 if (!win_printjob->handle)
172 {
173 free(win_printjob->di.pDocName);
174 free(win_printjob);
175 return NULL;
176 }
177
178 if (!StartPagePrinter(win_printer->hPrinter))
179 {
180 free(win_printjob->di.pDocName);
181 free(win_printjob);
182 return NULL;
183 }
184
185 win_printjob->printjob.Write = printer_win_write_printjob;
186 win_printjob->printjob.Close = printer_win_close_printjob;
187
188 win_printer->printjob = win_printjob;
189
190 return &win_printjob->printjob;
191 }
192
printer_win_find_printjob(rdpPrinter * printer,UINT32 id)193 static rdpPrintJob* printer_win_find_printjob(rdpPrinter* printer, UINT32 id)
194 {
195 rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
196
197 if (!win_printer->printjob)
198 return NULL;
199
200 if (win_printer->printjob->printjob.id != id)
201 return NULL;
202
203 return (rdpPrintJob*)win_printer->printjob;
204 }
205
printer_win_free_printer(rdpPrinter * printer)206 static void printer_win_free_printer(rdpPrinter* printer)
207 {
208 rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
209
210 if (win_printer->printjob)
211 win_printer->printjob->printjob.Close((rdpPrintJob*)win_printer->printjob);
212
213 if (printer->backend)
214 printer->backend->ReleaseRef(printer->backend);
215
216 free(printer->name);
217 free(printer->driver);
218 free(printer);
219 }
220
printer_win_add_ref_printer(rdpPrinter * printer)221 static void printer_win_add_ref_printer(rdpPrinter* printer)
222 {
223 if (printer)
224 printer->references++;
225 }
226
printer_win_release_ref_printer(rdpPrinter * printer)227 static void printer_win_release_ref_printer(rdpPrinter* printer)
228 {
229 if (!printer)
230 return;
231 if (printer->references <= 1)
232 printer_win_free_printer(printer);
233 else
234 printer->references--;
235 }
236
printer_win_new_printer(rdpWinPrinterDriver * win_driver,const WCHAR * name,const WCHAR * drivername,BOOL is_default)237 static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver, const WCHAR* name,
238 const WCHAR* drivername, BOOL is_default)
239 {
240 rdpWinPrinter* win_printer;
241 DWORD needed = 0;
242 int status;
243 PRINTER_INFO_2* prninfo = NULL;
244
245 win_printer = (rdpWinPrinter*)calloc(1, sizeof(rdpWinPrinter));
246 if (!win_printer)
247 return NULL;
248
249 win_printer->printer.backend = &win_driver->driver;
250 win_printer->printer.id = win_driver->id_sequence++;
251 if (ConvertFromUnicode(CP_UTF8, 0, name, -1, &win_printer->printer.name, 0, NULL, NULL) < 1)
252 {
253 free(win_printer);
254 return NULL;
255 }
256
257 if (!win_printer->printer.name)
258 {
259 free(win_printer);
260 return NULL;
261 }
262 win_printer->printer.is_default = is_default;
263
264 win_printer->printer.CreatePrintJob = printer_win_create_printjob;
265 win_printer->printer.FindPrintJob = printer_win_find_printjob;
266 win_printer->printer.AddRef = printer_win_add_ref_printer;
267 win_printer->printer.ReleaseRef = printer_win_release_ref_printer;
268
269 if (!OpenPrinter(name, &(win_printer->hPrinter), NULL))
270 {
271 free(win_printer->printer.name);
272 free(win_printer);
273 return NULL;
274 }
275
276 /* How many memory should be allocated for printer data */
277 GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, 0, &needed);
278 if (needed == 0)
279 {
280 free(win_printer->printer.name);
281 free(win_printer);
282 return NULL;
283 }
284
285 prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
286 if (!prninfo)
287 {
288 free(win_printer->printer.name);
289 free(win_printer);
290 return NULL;
291 }
292
293 if (!GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, needed, &needed))
294 {
295 GlobalFree(prninfo);
296 free(win_printer->printer.name);
297 free(win_printer);
298 return NULL;
299 }
300
301 if (drivername)
302 status = ConvertFromUnicode(CP_UTF8, 0, drivername, -1, &win_printer->printer.driver, 0,
303 NULL, NULL);
304 else
305 status = ConvertFromUnicode(CP_UTF8, 0, prninfo->pDriverName, -1,
306 &win_printer->printer.driver, 0, NULL, NULL);
307 if (!win_printer->printer.driver || (status <= 0))
308 {
309 GlobalFree(prninfo);
310 free(win_printer->printer.name);
311 free(win_printer);
312 return NULL;
313 }
314
315 win_printer->printer.AddRef(&win_printer->printer);
316 win_printer->printer.backend->AddRef(win_printer->printer.backend);
317 return &win_printer->printer;
318 }
319
printer_win_release_enum_printers(rdpPrinter ** printers)320 static void printer_win_release_enum_printers(rdpPrinter** printers)
321 {
322 rdpPrinter** cur = printers;
323
324 while ((cur != NULL) && ((*cur) != NULL))
325 {
326 if ((*cur)->ReleaseRef)
327 (*cur)->ReleaseRef(*cur);
328 cur++;
329 }
330 free(printers);
331 }
332
printer_win_enum_printers(rdpPrinterDriver * driver)333 static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
334 {
335 rdpPrinter** printers;
336 int num_printers;
337 int i;
338 PRINTER_INFO_2* prninfo = NULL;
339 DWORD needed, returned;
340
341 /* find required size for the buffer */
342 EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, NULL, 0, &needed,
343 &returned);
344
345 /* allocate array of PRINTER_INFO structures */
346 prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
347 if (!prninfo)
348 return NULL;
349
350 /* call again */
351 if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 2, (LPBYTE)prninfo,
352 needed, &needed, &returned))
353 {
354 }
355
356 printers = (rdpPrinter**)calloc((returned + 1), sizeof(rdpPrinter*));
357 if (!printers)
358 {
359 GlobalFree(prninfo);
360 return NULL;
361 }
362
363 num_printers = 0;
364
365 for (i = 0; i < (int)returned; i++)
366 {
367 rdpPrinter* current = printers[num_printers];
368 current = printer_win_new_printer((rdpWinPrinterDriver*)driver, prninfo[i].pPrinterName,
369 prninfo[i].pDriverName, 0);
370 if (!current)
371 {
372 printer_win_release_enum_printers(printers);
373 printers = NULL;
374 break;
375 }
376 printers[num_printers++] = current;
377 }
378
379 GlobalFree(prninfo);
380 return printers;
381 }
382
printer_win_get_printer(rdpPrinterDriver * driver,const char * name,const char * driverName)383 static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver, const char* name,
384 const char* driverName)
385 {
386 WCHAR* driverNameW = NULL;
387 WCHAR* nameW = NULL;
388 rdpWinPrinterDriver* win_driver = (rdpWinPrinterDriver*)driver;
389 rdpPrinter* myPrinter = NULL;
390
391 if (name)
392 {
393 ConvertToUnicode(CP_UTF8, 0, name, -1, &nameW, 0);
394 if (!driverNameW)
395 return NULL;
396 }
397 if (driverName)
398 {
399 ConvertToUnicode(CP_UTF8, 0, driverName, -1, &driverNameW, 0);
400 if (!driverNameW)
401 return NULL;
402 }
403
404 myPrinter = printer_win_new_printer(win_driver, nameW, driverNameW,
405 win_driver->id_sequence == 1 ? TRUE : FALSE);
406 free(driverNameW);
407 free(nameW);
408
409 return myPrinter;
410 }
411
printer_win_add_ref_driver(rdpPrinterDriver * driver)412 static void printer_win_add_ref_driver(rdpPrinterDriver* driver)
413 {
414 rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
415 if (win)
416 win->references++;
417 }
418
419 /* Singleton */
420 static rdpWinPrinterDriver* win_driver = NULL;
421
printer_win_release_ref_driver(rdpPrinterDriver * driver)422 static void printer_win_release_ref_driver(rdpPrinterDriver* driver)
423 {
424 rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
425 if (win->references <= 1)
426 {
427 free(win);
428 win_driver = NULL;
429 }
430 else
431 win->references--;
432 }
433
434 #ifdef BUILTIN_CHANNELS
win_freerdp_printer_client_subsystem_entry(void)435 rdpPrinterDriver* win_freerdp_printer_client_subsystem_entry(void)
436 #else
437 FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void)
438 #endif
439 {
440 if (!win_driver)
441 {
442 win_driver = (rdpWinPrinterDriver*)calloc(1, sizeof(rdpWinPrinterDriver));
443
444 if (!win_driver)
445 return NULL;
446
447 win_driver->driver.EnumPrinters = printer_win_enum_printers;
448 win_driver->driver.ReleaseEnumPrinters = printer_win_release_enum_printers;
449 win_driver->driver.GetPrinter = printer_win_get_printer;
450
451 win_driver->driver.AddRef = printer_win_add_ref_driver;
452 win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
453
454 win_driver->id_sequence = 1;
455 win_driver->driver.AddRef(&win_driver->driver);
456 }
457
458 return &win_driver->driver;
459 }
460