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