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
35 #ifdef HAVE_CUPS_CUPS_H
36 #include <cups/cups.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
52 static void *libcups_handle
;
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
71 static cups_dest_t
* (*pcupsGetNamedDest
)(http_t
*, const char *, const char *);
72 static const char * (*pcupsLastErrorString
)(void);
74 #endif /* SONAME_LIBCUPS */
78 BOOL (*write_doc
)(struct _doc_t
*, const BYTE
*buf
, unsigned int size
);
79 BOOL (*end_doc
)(struct _doc_t
*);
104 BOOL restore_ps_header
;
106 cups_option_t
*options
;
108 char buf
[257]; /* DSC max of 256 + '\0' */
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
)
127 wret
= waitpid(doc
->pipe
.pid
, &status
, 0);
128 } while (wret
< 0 && errno
== EINTR
);
131 ERR("waitpid() failed!\n");
134 if (!WIFEXITED(status
) || WEXITSTATUS(status
))
136 ERR("child process failed! %d\n", status
);
143 static BOOL
pipe_start_doc(doc_t
*doc
, const WCHAR
*cmd
)
149 doc
->write_doc
= pipe_write_doc
;
150 doc
->end_doc
= pipe_end_doc
;
153 cmdA
= malloc(len
* 3 + 1);
154 ntdll_wcstoumbs(cmd
, len
+ 1, cmdA
, len
* 3 + 1, FALSE
);
156 TRACE("printing with: %s\n", cmdA
);
160 ERR("pipe() failed!\n");
165 if ((doc
->pipe
.pid
= fork()) == 0)
171 /* reset signals that we previously set to SIG_IGN */
172 signal(SIGPIPE
, SIG_DFL
);
174 execl("/bin/sh", "/bin/sh", "-c", cmdA
, NULL
);
179 if (doc
->pipe
.pid
== -1)
181 ERR("fork() failed!\n");
186 doc
->pipe
.fd
= fds
[1];
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
);
201 static BOOL
unixname_start_doc(doc_t
*doc
, const WCHAR
*output
)
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);
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
);
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
);
236 #ifdef SONAME_LIBCUPS
237 static int get_cups_default_options(const char *printer
, int num_options
, cups_option_t
**options
)
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
);
258 static BOOL
cups_gets(doc_t
*doc
, const BYTE
**buf
, unsigned int *size
)
262 while(doc
->cups
.buf_len
< sizeof(doc
->cups
.buf
) && *size
)
265 doc
->cups
.buf
[doc
->cups
.buf_len
++] = b
;
275 static BOOL
cups_write(const char *buf
, unsigned int size
)
280 if (pcupsWriteRequestData(CUPS_HTTP_DEFAULT
, buf
, size
) != HTTP_STATUS_CONTINUE
)
282 if (pcupsLastErrorString
)
283 WARN("cupsWriteRequestData failed: %s\n", debugstr_a(pcupsLastErrorString()));
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
))
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;
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
)
322 if (!cups_gets(doc
, &buf
, &size
))
324 if (doc
->cups
.buf_len
!= sizeof(doc
->cups
.buf
))
326 doc
->cups
.state
= doc_create_job
;
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;
338 doc
->cups
.state
= doc_create_job
;
344 if (doc
->cups
.state
== doc_create_job
)
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
);
365 if (pcupsLastErrorString
)
366 WARN("cupsCreateJob failed: %s\n", debugstr_a(pcupsLastErrorString()));
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()));
378 doc
->cups
.state
= doc_initialized
;
381 if (doc
->cups
.restore_ps_header
)
383 if (!cups_write(ps_adobe
, sizeof(ps_adobe
) - 1))
385 doc
->cups
.restore_ps_header
= FALSE
;
388 if (doc
->cups
.buf_len
)
390 if (!cups_write(doc
->cups
.buf
, doc
->cups
.buf_len
))
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
);
417 static BOOL
cups_start_doc(doc_t
*doc
, const WCHAR
*printer_name
, const WCHAR
*document_title
)
419 #ifdef SONAME_LIBCUPS
420 if (pcupsWriteRequestData
)
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
);
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
;
450 p##x = dlsym(libcups_handle, #x); \
453 ERR("failed to load symbol %s\n", #x); \
454 libcups_handle = NULL; \
455 return STATUS_ENTRYPOINT_NOT_FOUND; \
459 #define DO_FUNC(x) p##x = dlsym(libcups_handle, #x)
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
));
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
);
487 *params
->doc
= (size_t)doc
;
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
;
507 ret
= doc
->end_doc(doc
);
512 const unixlib_entry_t __wine_unix_call_funcs
[] =
524 static NTSTATUS
wow64_start_doc(void *args
)
530 PTR32 document_title
;
532 } const *params32
= args
;
534 struct start_doc_params params
=
537 ULongToPtr(params32
->port
),
538 ULongToPtr(params32
->document_title
),
539 ULongToPtr(params32
->doc
),
542 return start_doc(¶ms
);
545 static NTSTATUS
wow64_write_doc(void *args
)
552 } const *params32
= args
;
554 struct write_doc_params params
=
557 ULongToPtr(params32
->buf
),
561 return write_doc(¶ms
);
564 const unixlib_entry_t __wine_unix_call_wow64_funcs
[] =