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