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
);
49 static void *libcups_handle
;
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
68 static cups_dest_t
* (*pcupsGetNamedDest
)(http_t
*, const char *, const char *);
69 static const char * (*pcupsLastErrorString
)(void);
71 #endif /* SONAME_LIBCUPS */
75 BOOL (*write_doc
)(struct _doc_t
*, const BYTE
*buf
, unsigned int size
);
76 BOOL (*end_doc
)(struct _doc_t
*);
101 BOOL restore_ps_header
;
103 cups_option_t
*options
;
105 char buf
[257]; /* DSC max of 256 + '\0' */
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
)
124 wret
= waitpid(doc
->pipe
.pid
, &status
, 0);
125 } while (wret
< 0 && errno
== EINTR
);
128 ERR("waitpid() failed!\n");
131 if (!WIFEXITED(status
) || WEXITSTATUS(status
))
133 ERR("child process failed! %d\n", status
);
140 static BOOL
pipe_start_doc(doc_t
*doc
, const WCHAR
*cmd
)
146 doc
->write_doc
= pipe_write_doc
;
147 doc
->end_doc
= pipe_end_doc
;
150 cmdA
= malloc(len
* 3 + 1);
151 ntdll_wcstoumbs(cmd
, len
+ 1, cmdA
, len
* 3 + 1, FALSE
);
153 TRACE("printing with: %s\n", cmdA
);
157 ERR("pipe() failed!\n");
162 if ((doc
->pipe
.pid
= fork()) == 0)
168 /* reset signals that we previously set to SIG_IGN */
169 signal(SIGPIPE
, SIG_DFL
);
171 execl("/bin/sh", "/bin/sh", "-c", cmdA
, NULL
);
176 if (doc
->pipe
.pid
== -1)
178 ERR("fork() failed!\n");
183 doc
->pipe
.fd
= fds
[1];
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
);
198 static BOOL
unixname_start_doc(doc_t
*doc
, const WCHAR
*output
)
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);
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
);
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
);
233 #ifdef SONAME_LIBCUPS
234 static int get_cups_default_options(const char *printer
, int num_options
, cups_option_t
**options
)
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
);
255 static BOOL
cups_gets(doc_t
*doc
, const BYTE
**buf
, unsigned int *size
)
259 while(doc
->cups
.buf_len
< sizeof(doc
->cups
.buf
) && *size
)
262 doc
->cups
.buf
[doc
->cups
.buf_len
++] = b
;
272 static BOOL
cups_write(const char *buf
, unsigned int size
)
277 if (pcupsWriteRequestData(CUPS_HTTP_DEFAULT
, buf
, size
) != HTTP_STATUS_CONTINUE
)
279 if (pcupsLastErrorString
)
280 WARN("cupsWriteRequestData failed: %s\n", debugstr_a(pcupsLastErrorString()));
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
))
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;
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
)
319 if (!cups_gets(doc
, &buf
, &size
))
321 if (doc
->cups
.buf_len
!= sizeof(doc
->cups
.buf
))
323 doc
->cups
.state
= doc_create_job
;
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;
335 doc
->cups
.state
= doc_create_job
;
341 if (doc
->cups
.state
== doc_create_job
)
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
);
362 if (pcupsLastErrorString
)
363 WARN("cupsCreateJob failed: %s\n", debugstr_a(pcupsLastErrorString()));
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()));
375 doc
->cups
.state
= doc_initialized
;
378 if (doc
->cups
.restore_ps_header
)
380 if (!cups_write(ps_adobe
, sizeof(ps_adobe
) - 1))
382 doc
->cups
.restore_ps_header
= FALSE
;
385 if (doc
->cups
.buf_len
)
387 if (!cups_write(doc
->cups
.buf
, doc
->cups
.buf_len
))
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
);
414 static BOOL
cups_start_doc(doc_t
*doc
, const WCHAR
*printer_name
, const WCHAR
*document_title
)
416 #ifdef SONAME_LIBCUPS
417 if (pcupsWriteRequestData
)
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
);
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
;
447 p##x = dlsym(libcups_handle, #x); \
450 ERR("failed to load symbol %s\n", #x); \
451 libcups_handle = NULL; \
452 return STATUS_ENTRYPOINT_NOT_FOUND; \
456 #define DO_FUNC(x) p##x = dlsym(libcups_handle, #x)
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
));
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
);
484 *params
->doc
= (size_t)doc
;
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
;
504 ret
= doc
->end_doc(doc
);
509 const unixlib_entry_t __wine_unix_call_funcs
[] =
521 static NTSTATUS
wow64_start_doc(void *args
)
527 PTR32 document_title
;
529 } const *params32
= args
;
531 struct start_doc_params params
=
534 ULongToPtr(params32
->port
),
535 ULongToPtr(params32
->document_title
),
536 ULongToPtr(params32
->doc
),
539 return start_doc(¶ms
);
542 static NTSTATUS
wow64_write_doc(void *args
)
549 } const *params32
= args
;
551 struct write_doc_params params
=
554 ULongToPtr(params32
->buf
),
558 return write_doc(¶ms
);
561 const unixlib_entry_t __wine_unix_call_wow64_funcs
[] =