cfgmgr32: Add stub for CM_Get_Device_Interface_PropertyW.
[wine.git] / dlls / localspl / cups.c
blobb5c26cc2626a1c3a1535cd99519f333dbb4f5d00
1 /*
2 * CUPS functions
4 * Copyright 2021 Huw Davies
5 * Copyright 2022 Piotr Caban
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <dlfcn.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <sys/wait.h>
35 #ifdef HAVE_CUPS_CUPS_H
36 #include <cups/cups.h>
37 #endif
39 #include "ntstatus.h"
40 #define WIN32_NO_STATUS
41 #include "wine/debug.h"
43 #include "localspl_private.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
47 /* cups.h before version 1.7.0 doesn't have HTTP_STATUS_CONTINUE */
48 #define HTTP_STATUS_CONTINUE 100
50 #ifdef SONAME_LIBCUPS
52 static void *libcups_handle;
54 #define CUPS_FUNCS \
55 DO_FUNC(cupsAddOption); \
56 DO_FUNC(cupsCreateJob); \
57 DO_FUNC(cupsFinishDocument); \
58 DO_FUNC(cupsFreeDests); \
59 DO_FUNC(cupsFreeOptions); \
60 DO_FUNC(cupsGetOption); \
61 DO_FUNC(cupsParseOptions); \
62 DO_FUNC(cupsStartDocument); \
63 DO_FUNC(cupsWriteRequestData)
64 #define CUPS_OPT_FUNCS \
65 DO_FUNC(cupsGetNamedDest); \
66 DO_FUNC(cupsLastErrorString)
68 #define DO_FUNC(f) static typeof(f) *p##f
69 CUPS_FUNCS;
70 #undef DO_FUNC
71 static cups_dest_t * (*pcupsGetNamedDest)(http_t *, const char *, const char *);
72 static const char * (*pcupsLastErrorString)(void);
74 #endif /* SONAME_LIBCUPS */
76 typedef struct _doc_t
78 BOOL (*write_doc)(struct _doc_t *, const BYTE *buf, unsigned int size);
79 BOOL (*end_doc)(struct _doc_t *);
81 union
83 struct
85 pid_t pid;
86 int fd;
87 } pipe;
88 struct
90 int fd;
91 } unixname;
92 #ifdef SONAME_LIBCUPS
93 struct
95 char *queue;
96 char *doc_title;
97 enum
99 doc_parse_header = 0,
100 doc_parse_options,
101 doc_create_job,
102 doc_initialized,
103 } state;
104 BOOL restore_ps_header;
105 int num_options;
106 cups_option_t *options;
107 int buf_len;
108 char buf[257]; /* DSC max of 256 + '\0' */
109 } cups;
110 #endif
112 } doc_t;
114 static BOOL pipe_write_doc(doc_t *doc, const BYTE *buf, unsigned int size)
116 return write(doc->pipe.fd, buf, size) == size;
119 static BOOL pipe_end_doc(doc_t *doc)
121 pid_t wret;
122 int status;
124 close(doc->pipe.fd);
126 do {
127 wret = waitpid(doc->pipe.pid, &status, 0);
128 } while (wret < 0 && errno == EINTR);
129 if (wret < 0)
131 ERR("waitpid() failed!\n");
132 return FALSE;
134 if (!WIFEXITED(status) || WEXITSTATUS(status))
136 ERR("child process failed! %d\n", status);
137 return FALSE;
140 return TRUE;
143 static BOOL pipe_start_doc(doc_t *doc, const WCHAR *cmd)
145 char *cmdA;
146 int fds[2];
147 DWORD len;
149 doc->write_doc = pipe_write_doc;
150 doc->end_doc = pipe_end_doc;
152 len = wcslen(cmd);
153 cmdA = malloc(len * 3 + 1);
154 ntdll_wcstoumbs(cmd, len + 1, cmdA, len * 3 + 1, FALSE);
156 TRACE("printing with: %s\n", cmdA);
158 if (pipe(fds))
160 ERR("pipe() failed!\n");
161 free(cmdA);
162 return FALSE;
165 if ((doc->pipe.pid = fork()) == 0)
167 close(0);
168 dup2(fds[0], 0);
169 close(fds[1]);
171 /* reset signals that we previously set to SIG_IGN */
172 signal(SIGPIPE, SIG_DFL);
174 execl("/bin/sh", "/bin/sh", "-c", cmdA, NULL);
175 _exit(1);
177 close(fds[0]);
178 free(cmdA);
179 if (doc->pipe.pid == -1)
181 ERR("fork() failed!\n");
182 close(fds[1]);
183 return FALSE;
186 doc->pipe.fd = fds[1];
187 return TRUE;
190 static BOOL unixname_write_doc(doc_t *doc, const BYTE *buf, unsigned int size)
192 return write(doc->unixname.fd, buf, size) == size;
195 static BOOL unixname_end_doc(doc_t *doc)
197 close(doc->unixname.fd);
198 return TRUE;
201 static BOOL unixname_start_doc(doc_t *doc, const WCHAR *output)
203 char *outputA;
204 DWORD len;
206 doc->write_doc = unixname_write_doc;
207 doc->end_doc = unixname_end_doc;
209 len = wcslen(output);
210 outputA = malloc(len * 3 + 1);
211 ntdll_wcstoumbs(output, len + 1, outputA, len * 3 + 1, FALSE);
213 doc->unixname.fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
214 free(outputA);
216 return doc->unixname.fd != -1;
219 static BOOL lpr_start_doc(doc_t *doc, const WCHAR *printer_name)
221 static const WCHAR lpr[] = { 'l','p','r',' ','-','P','\'' };
222 static const WCHAR quote[] = { '\'',0 };
223 int printer_len = wcslen(printer_name);
224 WCHAR *cmd;
225 BOOL ret;
227 cmd = malloc(printer_len * sizeof(WCHAR) + sizeof(lpr) + sizeof(quote));
228 memcpy(cmd, lpr, sizeof(lpr));
229 memcpy(cmd + ARRAY_SIZE(lpr), printer_name, printer_len * sizeof(WCHAR));
230 memcpy(cmd + ARRAY_SIZE(lpr) + printer_len, quote, sizeof(quote));
231 ret = pipe_start_doc(doc, cmd);
232 free(cmd);
233 return ret;
236 #ifdef SONAME_LIBCUPS
237 static int get_cups_default_options(const char *printer, int num_options, cups_option_t **options)
239 cups_dest_t *dest;
240 int i;
242 if (!pcupsGetNamedDest) return num_options;
244 dest = pcupsGetNamedDest(NULL, printer, NULL);
245 if (!dest) return num_options;
247 for (i = 0; i < dest->num_options; i++)
249 if (!pcupsGetOption(dest->options[i].name, num_options, *options))
250 num_options = pcupsAddOption(dest->options[i].name,
251 dest->options[i].value, num_options, options);
254 pcupsFreeDests(1, dest);
255 return num_options;
258 static BOOL cups_gets(doc_t *doc, const BYTE **buf, unsigned int *size)
260 BYTE b;
262 while(doc->cups.buf_len < sizeof(doc->cups.buf) && *size)
264 b = (*buf)[0];
265 doc->cups.buf[doc->cups.buf_len++] = b;
266 (*buf)++;
267 (*size)--;
269 if (b == '\n')
270 return TRUE;
272 return FALSE;
275 static BOOL cups_write(const char *buf, unsigned int size)
277 if (!size)
278 return TRUE;
280 if (pcupsWriteRequestData(CUPS_HTTP_DEFAULT, buf, size) != HTTP_STATUS_CONTINUE)
282 if (pcupsLastErrorString)
283 WARN("cupsWriteRequestData failed: %s\n", debugstr_a(pcupsLastErrorString()));
284 return FALSE;
286 return TRUE;
289 static BOOL cups_write_doc(doc_t *doc, const BYTE *buf, unsigned int size)
291 const char ps_adobe[] = "%!PS-Adobe-3.0\n";
292 const char cups_job[] = "%cupsJobTicket:";
294 if (doc->cups.state == doc_parse_header)
296 if (!cups_gets(doc, &buf, &size))
298 if (doc->cups.buf_len != sizeof(doc->cups.buf))
299 return TRUE;
300 doc->cups.state = doc_create_job;
302 else if (!strncmp(doc->cups.buf, ps_adobe, sizeof(ps_adobe) - 1))
304 doc->cups.restore_ps_header = TRUE;
305 doc->cups.state = doc_parse_options;
306 doc->cups.buf_len = 0;
308 else
310 doc->cups.state = doc_create_job;
314 /* Explicitly set CUPS options based on any %cupsJobTicket lines.
315 * The CUPS scheduler only looks for these in Print-File requests, and since
316 * we use Create-Job / Send-Document, the ticket lines don't get parsed.
318 if (doc->cups.state == doc_parse_options)
320 while (1)
322 if (!cups_gets(doc, &buf, &size))
324 if (doc->cups.buf_len != sizeof(doc->cups.buf))
325 return TRUE;
326 doc->cups.state = doc_create_job;
327 break;
329 else if (!strncmp(doc->cups.buf, cups_job, sizeof(cups_job) - 1))
331 doc->cups.buf[doc->cups.buf_len - 1] = 0;
332 doc->cups.num_options = pcupsParseOptions(doc->cups.buf + sizeof(cups_job) - 1,
333 doc->cups.num_options, &doc->cups.options);
334 doc->cups.buf_len = 0;
336 else
338 doc->cups.state = doc_create_job;
339 break;
344 if (doc->cups.state == doc_create_job)
346 const char *format;
347 int i, job_id;
349 doc->cups.num_options = get_cups_default_options(doc->cups.queue,
350 doc->cups.num_options, &doc->cups.options);
352 TRACE("printing via cups with options:\n");
353 for (i = 0; i < doc->cups.num_options; i++)
354 TRACE("\t%d: %s = %s\n", i, doc->cups.options[i].name, doc->cups.options[i].value);
356 if (pcupsGetOption("raw", doc->cups.num_options, doc->cups.options))
357 format = CUPS_FORMAT_RAW;
358 else if (!(format = pcupsGetOption("document-format", doc->cups.num_options, doc->cups.options)))
359 format = CUPS_FORMAT_AUTO;
361 job_id = pcupsCreateJob(CUPS_HTTP_DEFAULT, doc->cups.queue,
362 doc->cups.doc_title, doc->cups.num_options, doc->cups.options);
363 if (!job_id)
365 if (pcupsLastErrorString)
366 WARN("cupsCreateJob failed: %s\n", debugstr_a(pcupsLastErrorString()));
367 return FALSE;
370 if (pcupsStartDocument(CUPS_HTTP_DEFAULT, doc->cups.queue, job_id,
371 NULL, format, TRUE) != HTTP_STATUS_CONTINUE)
373 if (pcupsLastErrorString)
374 WARN("cupsStartDocument failed: %s\n", debugstr_a(pcupsLastErrorString()));
375 return FALSE;
378 doc->cups.state = doc_initialized;
381 if (doc->cups.restore_ps_header)
383 if (!cups_write(ps_adobe, sizeof(ps_adobe) - 1))
384 return FALSE;
385 doc->cups.restore_ps_header = FALSE;
388 if (doc->cups.buf_len)
390 if (!cups_write(doc->cups.buf, doc->cups.buf_len))
391 return FALSE;
392 doc->cups.buf_len = 0;
395 return cups_write((const char *)buf, size);
398 static BOOL cups_end_doc(doc_t *doc)
400 if (doc->cups.buf_len)
402 if (doc->cups.state != doc_initialized)
403 doc->cups.state = doc_create_job;
404 cups_write_doc(doc, NULL, 0);
407 if (doc->cups.state == doc_initialized)
408 pcupsFinishDocument(CUPS_HTTP_DEFAULT, doc->cups.queue);
410 free(doc->cups.queue);
411 free(doc->cups.doc_title);
412 pcupsFreeOptions(doc->cups.num_options, doc->cups.options);
413 return TRUE;
415 #endif
417 static BOOL cups_start_doc(doc_t *doc, const WCHAR *printer_name, const WCHAR *document_title)
419 #ifdef SONAME_LIBCUPS
420 if (pcupsWriteRequestData)
422 int len;
424 doc->write_doc = cups_write_doc;
425 doc->end_doc = cups_end_doc;
427 len = wcslen(printer_name);
428 doc->cups.queue = malloc(len * 3 + 1);
429 ntdll_wcstoumbs(printer_name, len + 1, doc->cups.queue, len * 3 + 1, FALSE);
431 len = wcslen(document_title);
432 doc->cups.doc_title = malloc(len * 3 + 1);
433 ntdll_wcstoumbs(document_title, len + 1, doc->cups.doc_title, len + 3 + 1, FALSE);
435 return TRUE;
437 #endif
439 return lpr_start_doc(doc, printer_name);
442 static NTSTATUS process_attach(void *args)
444 #ifdef SONAME_LIBCUPS
445 libcups_handle = dlopen(SONAME_LIBCUPS, RTLD_NOW);
446 TRACE("%p: %s loaded\n", libcups_handle, SONAME_LIBCUPS);
447 if (!libcups_handle) return STATUS_DLL_NOT_FOUND;
449 #define DO_FUNC(x) \
450 p##x = dlsym(libcups_handle, #x); \
451 if (!p##x) \
453 ERR("failed to load symbol %s\n", #x); \
454 libcups_handle = NULL; \
455 return STATUS_ENTRYPOINT_NOT_FOUND; \
457 CUPS_FUNCS;
458 #undef DO_FUNC
459 #define DO_FUNC(x) p##x = dlsym(libcups_handle, #x)
460 CUPS_OPT_FUNCS;
461 #undef DO_FUNC
462 return STATUS_SUCCESS;
463 #else /* SONAME_LIBCUPS */
464 return STATUS_NOT_SUPPORTED;
465 #endif /* SONAME_LIBCUPS */
468 static NTSTATUS start_doc(void *args)
470 const struct start_doc_params *params = args;
471 doc_t *doc = calloc(1, sizeof(*doc));
472 BOOL ret = FALSE;
474 if (!doc) return STATUS_NO_MEMORY;
476 if (params->type == PORT_IS_PIPE)
477 ret = pipe_start_doc(doc, params->port + 1 /* strlen("|") */);
478 else if (params->type == PORT_IS_UNIXNAME)
479 ret = unixname_start_doc(doc, params->port);
480 else if (params->type == PORT_IS_LPR)
481 ret = lpr_start_doc(doc, params->port + 4 /* strlen("lpr:") */);
482 else if (params->type == PORT_IS_CUPS)
483 ret = cups_start_doc(doc, params->port + 5 /*strlen("cups:") */,
484 params->document_title);
486 if (ret)
487 *params->doc = (size_t)doc;
488 else
489 free(doc);
490 return ret;
493 static NTSTATUS write_doc(void *args)
495 const struct write_doc_params *params = args;
496 doc_t *doc = (doc_t *)(size_t)params->doc;
498 return doc->write_doc(doc, params->buf, params->size);
501 static NTSTATUS end_doc(void *args)
503 const struct end_doc_params *params = args;
504 doc_t *doc = (doc_t *)(size_t)params->doc;
505 NTSTATUS ret;
507 ret = doc->end_doc(doc);
508 free(doc);
509 return ret;
512 const unixlib_entry_t __wine_unix_call_funcs[] =
514 process_attach,
515 start_doc,
516 write_doc,
517 end_doc,
520 #ifdef _WIN64
522 typedef ULONG PTR32;
524 static NTSTATUS wow64_start_doc(void *args)
526 struct
528 unsigned int type;
529 PTR32 port;
530 PTR32 document_title;
531 PTR32 doc;
532 } const *params32 = args;
534 struct start_doc_params params =
536 params32->type,
537 ULongToPtr(params32->port),
538 ULongToPtr(params32->document_title),
539 ULongToPtr(params32->doc),
542 return start_doc(&params);
545 static NTSTATUS wow64_write_doc(void *args)
547 struct
549 INT64 doc;
550 PTR32 buf;
551 unsigned int size;
552 } const *params32 = args;
554 struct write_doc_params params =
556 params32->doc,
557 ULongToPtr(params32->buf),
558 params32->size,
561 return write_doc(&params);
564 const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
566 process_attach,
567 wow64_start_doc,
568 wow64_write_doc,
569 end_doc,
572 #endif /* _WIN64 */