winepulse: Lock sessions in AudioClient's GetService.
[wine.git] / dlls / localspl / cups.c
bloba7f2273aba19a2805e0c36cc5746e53d23add980
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 #ifdef SONAME_LIBCUPS
49 static void *libcups_handle;
51 #define CUPS_FUNCS \
52 DO_FUNC(cupsAddOption); \
53 DO_FUNC(cupsCreateJob); \
54 DO_FUNC(cupsFinishDocument); \
55 DO_FUNC(cupsFreeDests); \
56 DO_FUNC(cupsFreeOptions); \
57 DO_FUNC(cupsGetOption); \
58 DO_FUNC(cupsParseOptions); \
59 DO_FUNC(cupsStartDocument); \
60 DO_FUNC(cupsWriteRequestData)
61 #define CUPS_OPT_FUNCS \
62 DO_FUNC(cupsGetNamedDest); \
63 DO_FUNC(cupsLastErrorString)
65 #define DO_FUNC(f) static typeof(f) *p##f
66 CUPS_FUNCS;
67 #undef DO_FUNC
68 static cups_dest_t * (*pcupsGetNamedDest)(http_t *, const char *, const char *);
69 static const char * (*pcupsLastErrorString)(void);
71 #endif /* SONAME_LIBCUPS */
73 typedef struct _doc_t
75 BOOL (*write_doc)(struct _doc_t *, const BYTE *buf, unsigned int size);
76 BOOL (*end_doc)(struct _doc_t *);
78 union
80 struct
82 pid_t pid;
83 int fd;
84 } pipe;
85 struct
87 int fd;
88 } unixname;
89 #ifdef SONAME_LIBCUPS
90 struct
92 char *queue;
93 char *doc_title;
94 enum
96 doc_parse_header = 0,
97 doc_parse_options,
98 doc_create_job,
99 doc_initialized,
100 } state;
101 BOOL restore_ps_header;
102 int num_options;
103 cups_option_t *options;
104 int buf_len;
105 char buf[257]; /* DSC max of 256 + '\0' */
106 } cups;
107 #endif
109 } doc_t;
111 static BOOL pipe_write_doc(doc_t *doc, const BYTE *buf, unsigned int size)
113 return write(doc->pipe.fd, buf, size) == size;
116 static BOOL pipe_end_doc(doc_t *doc)
118 pid_t wret;
119 int status;
121 close(doc->pipe.fd);
123 do {
124 wret = waitpid(doc->pipe.pid, &status, 0);
125 } while (wret < 0 && errno == EINTR);
126 if (wret < 0)
128 ERR("waitpid() failed!\n");
129 return FALSE;
131 if (!WIFEXITED(status) || WEXITSTATUS(status))
133 ERR("child process failed! %d\n", status);
134 return FALSE;
137 return TRUE;
140 static BOOL pipe_start_doc(doc_t *doc, const WCHAR *cmd)
142 char *cmdA;
143 int fds[2];
144 DWORD len;
146 doc->write_doc = pipe_write_doc;
147 doc->end_doc = pipe_end_doc;
149 len = wcslen(cmd);
150 cmdA = malloc(len * 3 + 1);
151 ntdll_wcstoumbs(cmd, len + 1, cmdA, len * 3 + 1, FALSE);
153 TRACE("printing with: %s\n", cmdA);
155 if (pipe(fds))
157 ERR("pipe() failed!\n");
158 free(cmdA);
159 return FALSE;
162 if ((doc->pipe.pid = fork()) == 0)
164 close(0);
165 dup2(fds[0], 0);
166 close(fds[1]);
168 /* reset signals that we previously set to SIG_IGN */
169 signal(SIGPIPE, SIG_DFL);
171 execl("/bin/sh", "/bin/sh", "-c", cmdA, NULL);
172 _exit(1);
174 close(fds[0]);
175 free(cmdA);
176 if (doc->pipe.pid == -1)
178 ERR("fork() failed!\n");
179 close(fds[1]);
180 return FALSE;
183 doc->pipe.fd = fds[1];
184 return TRUE;
187 static BOOL unixname_write_doc(doc_t *doc, const BYTE *buf, unsigned int size)
189 return write(doc->unixname.fd, buf, size) == size;
192 static BOOL unixname_end_doc(doc_t *doc)
194 close(doc->unixname.fd);
195 return TRUE;
198 static BOOL unixname_start_doc(doc_t *doc, const WCHAR *output)
200 char *outputA;
201 DWORD len;
203 doc->write_doc = unixname_write_doc;
204 doc->end_doc = unixname_end_doc;
206 len = wcslen(output);
207 outputA = malloc(len * 3 + 1);
208 ntdll_wcstoumbs(output, len + 1, outputA, len * 3 + 1, FALSE);
210 doc->unixname.fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
211 free(outputA);
213 return doc->unixname.fd != -1;
216 static BOOL lpr_start_doc(doc_t *doc, const WCHAR *printer_name)
218 static const WCHAR lpr[] = { 'l','p','r',' ','-','P','\'' };
219 static const WCHAR quote[] = { '\'',0 };
220 int printer_len = wcslen(printer_name);
221 WCHAR *cmd;
222 BOOL ret;
224 cmd = malloc(printer_len * sizeof(WCHAR) + sizeof(lpr) + sizeof(quote));
225 memcpy(cmd, lpr, sizeof(lpr));
226 memcpy(cmd + ARRAY_SIZE(lpr), printer_name, printer_len * sizeof(WCHAR));
227 memcpy(cmd + ARRAY_SIZE(lpr) + printer_len, quote, sizeof(quote));
228 ret = pipe_start_doc(doc, cmd);
229 free(cmd);
230 return ret;
233 #ifdef SONAME_LIBCUPS
234 static int get_cups_default_options(const char *printer, int num_options, cups_option_t **options)
236 cups_dest_t *dest;
237 int i;
239 if (!pcupsGetNamedDest) return num_options;
241 dest = pcupsGetNamedDest(NULL, printer, NULL);
242 if (!dest) return num_options;
244 for (i = 0; i < dest->num_options; i++)
246 if (!pcupsGetOption(dest->options[i].name, num_options, *options))
247 num_options = pcupsAddOption(dest->options[i].name,
248 dest->options[i].value, num_options, options);
251 pcupsFreeDests(1, dest);
252 return num_options;
255 static BOOL cups_gets(doc_t *doc, const BYTE **buf, unsigned int *size)
257 BYTE b;
259 while(doc->cups.buf_len < sizeof(doc->cups.buf) && *size)
261 b = (*buf)[0];
262 doc->cups.buf[doc->cups.buf_len++] = b;
263 (*buf)++;
264 (*size)--;
266 if (b == '\n')
267 return TRUE;
269 return FALSE;
272 static BOOL cups_write(const char *buf, unsigned int size)
274 if (!size)
275 return TRUE;
277 if (pcupsWriteRequestData(CUPS_HTTP_DEFAULT, buf, size) != HTTP_STATUS_CONTINUE)
279 if (pcupsLastErrorString)
280 WARN("cupsWriteRequestData failed: %s\n", debugstr_a(pcupsLastErrorString()));
281 return FALSE;
283 return TRUE;
286 static BOOL cups_write_doc(doc_t *doc, const BYTE *buf, unsigned int size)
288 const char ps_adobe[] = "%!PS-Adobe-3.0\n";
289 const char cups_job[] = "%cupsJobTicket:";
291 if (doc->cups.state == doc_parse_header)
293 if (!cups_gets(doc, &buf, &size))
295 if (doc->cups.buf_len != sizeof(doc->cups.buf))
296 return TRUE;
297 doc->cups.state = doc_create_job;
299 else if (!strncmp(doc->cups.buf, ps_adobe, sizeof(ps_adobe) - 1))
301 doc->cups.restore_ps_header = TRUE;
302 doc->cups.state = doc_parse_options;
303 doc->cups.buf_len = 0;
305 else
307 doc->cups.state = doc_create_job;
311 /* Explicitly set CUPS options based on any %cupsJobTicket lines.
312 * The CUPS scheduler only looks for these in Print-File requests, and since
313 * we use Create-Job / Send-Document, the ticket lines don't get parsed.
315 if (doc->cups.state == doc_parse_options)
317 while (1)
319 if (!cups_gets(doc, &buf, &size))
321 if (doc->cups.buf_len != sizeof(doc->cups.buf))
322 return TRUE;
323 doc->cups.state = doc_create_job;
324 break;
326 else if (!strncmp(doc->cups.buf, cups_job, sizeof(cups_job) - 1))
328 doc->cups.buf[doc->cups.buf_len - 1] = 0;
329 doc->cups.num_options = pcupsParseOptions(doc->cups.buf + sizeof(cups_job) - 1,
330 doc->cups.num_options, &doc->cups.options);
331 doc->cups.buf_len = 0;
333 else
335 doc->cups.state = doc_create_job;
336 break;
341 if (doc->cups.state == doc_create_job)
343 const char *format;
344 int i, job_id;
346 doc->cups.num_options = get_cups_default_options(doc->cups.queue,
347 doc->cups.num_options, &doc->cups.options);
349 TRACE("printing via cups with options:\n");
350 for (i = 0; i < doc->cups.num_options; i++)
351 TRACE("\t%d: %s = %s\n", i, doc->cups.options[i].name, doc->cups.options[i].value);
353 if (pcupsGetOption("raw", doc->cups.num_options, doc->cups.options))
354 format = CUPS_FORMAT_RAW;
355 else if (!(format = pcupsGetOption("document-format", doc->cups.num_options, doc->cups.options)))
356 format = CUPS_FORMAT_AUTO;
358 job_id = pcupsCreateJob(CUPS_HTTP_DEFAULT, doc->cups.queue,
359 doc->cups.doc_title, doc->cups.num_options, doc->cups.options);
360 if (!job_id)
362 if (pcupsLastErrorString)
363 WARN("cupsCreateJob failed: %s\n", debugstr_a(pcupsLastErrorString()));
364 return FALSE;
367 if (pcupsStartDocument(CUPS_HTTP_DEFAULT, doc->cups.queue, job_id,
368 doc->cups.doc_title, format, TRUE) != HTTP_STATUS_CONTINUE)
370 if (pcupsLastErrorString)
371 WARN("cupsStartDocument failed: %s\n", debugstr_a(pcupsLastErrorString()));
372 return FALSE;
375 doc->cups.state = doc_initialized;
378 if (doc->cups.restore_ps_header)
380 if (!cups_write(ps_adobe, sizeof(ps_adobe) - 1))
381 return FALSE;
382 doc->cups.restore_ps_header = FALSE;
385 if (doc->cups.buf_len)
387 if (!cups_write(doc->cups.buf, doc->cups.buf_len))
388 return FALSE;
389 doc->cups.buf_len = 0;
392 return cups_write((const char *)buf, size);
395 static BOOL cups_end_doc(doc_t *doc)
397 if (doc->cups.buf_len)
399 if (doc->cups.state != doc_initialized)
400 doc->cups.state = doc_create_job;
401 cups_write_doc(doc, NULL, 0);
404 if (doc->cups.state == doc_initialized)
405 pcupsFinishDocument(CUPS_HTTP_DEFAULT, doc->cups.queue);
407 free(doc->cups.queue);
408 free(doc->cups.doc_title);
409 pcupsFreeOptions(doc->cups.num_options, doc->cups.options);
410 return TRUE;
412 #endif
414 static BOOL cups_start_doc(doc_t *doc, const WCHAR *printer_name, const WCHAR *document_title)
416 #ifdef SONAME_LIBCUPS
417 if (pcupsWriteRequestData)
419 int len;
421 doc->write_doc = cups_write_doc;
422 doc->end_doc = cups_end_doc;
424 len = wcslen(printer_name);
425 doc->cups.queue = malloc(len * 3 + 1);
426 ntdll_wcstoumbs(printer_name, len + 1, doc->cups.queue, len * 3 + 1, FALSE);
428 len = wcslen(document_title);
429 doc->cups.doc_title = malloc(len * 3 + 1);
430 ntdll_wcstoumbs(document_title, len + 1, doc->cups.doc_title, len + 3 + 1, FALSE);
432 return TRUE;
434 #endif
436 return lpr_start_doc(doc, printer_name);
439 static NTSTATUS process_attach(void *args)
441 #ifdef SONAME_LIBCUPS
442 libcups_handle = dlopen(SONAME_LIBCUPS, RTLD_NOW);
443 TRACE("%p: %s loaded\n", libcups_handle, SONAME_LIBCUPS);
444 if (!libcups_handle) return STATUS_DLL_NOT_FOUND;
446 #define DO_FUNC(x) \
447 p##x = dlsym(libcups_handle, #x); \
448 if (!p##x) \
450 ERR("failed to load symbol %s\n", #x); \
451 libcups_handle = NULL; \
452 return STATUS_ENTRYPOINT_NOT_FOUND; \
454 CUPS_FUNCS;
455 #undef DO_FUNC
456 #define DO_FUNC(x) p##x = dlsym(libcups_handle, #x)
457 CUPS_OPT_FUNCS;
458 #undef DO_FUNC
459 return STATUS_SUCCESS;
460 #else /* SONAME_LIBCUPS */
461 return STATUS_NOT_SUPPORTED;
462 #endif /* SONAME_LIBCUPS */
465 static NTSTATUS start_doc(void *args)
467 const struct start_doc_params *params = args;
468 doc_t *doc = calloc(1, sizeof(*doc));
469 BOOL ret = FALSE;
471 if (!doc) return STATUS_NO_MEMORY;
473 if (params->type == PORT_IS_PIPE)
474 ret = pipe_start_doc(doc, params->port + 1 /* strlen("|") */);
475 else if (params->type == PORT_IS_UNIXNAME)
476 ret = unixname_start_doc(doc, params->port);
477 else if (params->type == PORT_IS_LPR)
478 ret = lpr_start_doc(doc, params->port + 4 /* strlen("lpr:") */);
479 else if (params->type == PORT_IS_CUPS)
480 ret = cups_start_doc(doc, params->port + 5 /*strlen("cups:") */,
481 params->document_title);
483 if (ret)
484 *params->doc = (size_t)doc;
485 else
486 free(doc);
487 return ret;
490 static NTSTATUS write_doc(void *args)
492 const struct write_doc_params *params = args;
493 doc_t *doc = (doc_t *)(size_t)params->doc;
495 return doc->write_doc(doc, params->buf, params->size);
498 static NTSTATUS end_doc(void *args)
500 const struct end_doc_params *params = args;
501 doc_t *doc = (doc_t *)(size_t)params->doc;
502 NTSTATUS ret;
504 ret = doc->end_doc(doc);
505 free(doc);
506 return ret;
509 const unixlib_entry_t __wine_unix_call_funcs[] =
511 process_attach,
512 start_doc,
513 write_doc,
514 end_doc,
517 #ifdef _WIN64
519 typedef ULONG PTR32;
521 static NTSTATUS wow64_start_doc(void *args)
523 struct
525 unsigned int type;
526 PTR32 port;
527 PTR32 document_title;
528 PTR32 doc;
529 } const *params32 = args;
531 struct start_doc_params params =
533 params32->type,
534 ULongToPtr(params32->port),
535 ULongToPtr(params32->document_title),
536 ULongToPtr(params32->doc),
539 return start_doc(&params);
542 static NTSTATUS wow64_write_doc(void *args)
544 struct
546 INT64 doc;
547 PTR32 buf;
548 unsigned int size;
549 } const *params32 = args;
551 struct write_doc_params params =
553 params32->doc,
554 ULongToPtr(params32->buf),
555 params32->size,
558 return write_doc(&params);
561 const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
563 process_attach,
564 wow64_start_doc,
565 wow64_write_doc,
566 end_doc,
569 #endif /* _WIN64 */