4 * Copyright 2021 Huw Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include <sys/types.h>
37 #ifdef HAVE_CUPS_CUPS_H
38 #include <cups/cups.h>
40 #ifdef HAVE_CUPS_PPD_H
44 #ifdef HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H
45 #define GetCurrentProcess GetCurrentProcess_Mac
46 #define GetCurrentThread GetCurrentThread_Mac
47 #define LoadResource LoadResource_Mac
48 #define AnimatePalette AnimatePalette_Mac
49 #define EqualRgn EqualRgn_Mac
50 #define FillRgn FillRgn_Mac
51 #define FrameRgn FrameRgn_Mac
52 #define GetPixel GetPixel_Mac
53 #define InvertRgn InvertRgn_Mac
54 #define LineTo LineTo_Mac
55 #define OffsetRgn OffsetRgn_Mac
56 #define PaintRgn PaintRgn_Mac
57 #define Polygon Polygon_Mac
58 #define ResizePalette ResizePalette_Mac
59 #define SetRectRgn SetRectRgn_Mac
60 #define EqualRect EqualRect_Mac
61 #define FillRect FillRect_Mac
62 #define FrameRect FrameRect_Mac
63 #define GetCursor GetCursor_Mac
64 #define InvertRect InvertRect_Mac
65 #define OffsetRect OffsetRect_Mac
66 #define PtInRect PtInRect_Mac
67 #define SetCursor SetCursor_Mac
68 #define SetRect SetRect_Mac
69 #define ShowCursor ShowCursor_Mac
70 #define UnionRect UnionRect_Mac
71 #include <ApplicationServices/ApplicationServices.h>
72 #undef GetCurrentProcess
73 #undef GetCurrentThread
100 #include "ntstatus.h"
101 #define WIN32_NO_STATUS
104 #include "winternl.h"
106 #include "winerror.h"
109 #include "winspool.h"
110 #include "ddk/winsplp.h"
111 #include "wine/debug.h"
112 #include "wine/unicode.h"
113 #include "wine/unixlib.h"
117 WINE_DEFAULT_DEBUG_CHANNEL(winspool
);
119 static const WCHAR CUPS_Port
[] = { 'C','U','P','S',':',0 };
120 static const WCHAR LPR_Port
[] = { 'L','P','R',':',0 };
122 #ifdef SONAME_LIBCUPS
124 static void *libcups_handle
;
127 DO_FUNC(cupsAddOption); \
128 DO_FUNC(cupsFreeDests); \
129 DO_FUNC(cupsFreeOptions); \
130 DO_FUNC(cupsGetDests); \
131 DO_FUNC(cupsGetOption); \
132 DO_FUNC(cupsParseOptions); \
133 DO_FUNC(cupsPrintFile)
134 #define CUPS_OPT_FUNCS \
135 DO_FUNC(cupsGetNamedDest); \
136 DO_FUNC(cupsGetPPD); \
137 DO_FUNC(cupsGetPPD3); \
138 DO_FUNC(cupsLastErrorString)
140 #define DO_FUNC(f) static typeof(f) *p##f
143 static cups_dest_t
* (*pcupsGetNamedDest
)(http_t
*, const char *, const char *);
144 static const char * (*pcupsGetPPD
)(const char *);
145 static http_status_t (*pcupsGetPPD3
)(http_t
*, const char *, time_t *, char *, size_t);
146 static const char * (*pcupsLastErrorString
)(void);
148 #endif /* SONAME_LIBCUPS */
150 static NTSTATUS
process_attach( void *args
)
152 #ifdef SONAME_LIBCUPS
153 libcups_handle
= dlopen( SONAME_LIBCUPS
, RTLD_NOW
);
154 TRACE( "%p: %s loaded\n", libcups_handle
, SONAME_LIBCUPS
);
155 if (!libcups_handle
) return STATUS_DLL_NOT_FOUND
;
158 p##x = dlsym( libcups_handle, #x ); \
161 ERR( "failed to load symbol %s\n", #x ); \
162 libcups_handle = NULL; \
163 return STATUS_ENTRYPOINT_NOT_FOUND; \
167 #define DO_FUNC(x) p##x = dlsym( libcups_handle, #x )
170 return STATUS_SUCCESS
;
171 #else /* SONAME_LIBCUPS */
172 return STATUS_NOT_SUPPORTED
;
173 #endif /* SONAME_LIBCUPS */
176 static BOOL
copy_file( const char *src
, const char *dst
)
178 int fds
[2] = { -1, -1 }, num
;
182 fds
[0] = open( src
, O_RDONLY
);
183 fds
[1] = open( dst
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0666 );
184 if (fds
[0] == -1 || fds
[1] == -1) goto fail
;
186 while ((num
= read( fds
[0], buf
, sizeof(buf
) )) != 0)
188 if (num
== -1) goto fail
;
189 if (write( fds
[1], buf
, num
) != num
) goto fail
;
194 if (fds
[1] != -1) close( fds
[1] );
195 if (fds
[0] != -1) close( fds
[0] );
199 static char *get_unix_file_name( LPCWSTR path
)
201 UNICODE_STRING nt_name
;
202 OBJECT_ATTRIBUTES attr
;
207 nt_name
.Buffer
= (WCHAR
*)path
;
208 nt_name
.MaximumLength
= nt_name
.Length
= lstrlenW( path
) * sizeof(WCHAR
);
209 InitializeObjectAttributes( &attr
, &nt_name
, 0, 0, NULL
);
212 if (!(buffer
= malloc( size
))) return NULL
;
213 status
= wine_nt_to_unix_file_name( &attr
, buffer
, &size
, FILE_OPEN_IF
);
214 if (status
!= STATUS_BUFFER_TOO_SMALL
) break;
217 if (status
&& status
!= STATUS_NO_SUCH_FILE
)
226 #ifdef SONAME_LIBCUPS
227 static WCHAR
*cups_get_optionW( const char *opt_name
, int num_options
, cups_option_t
*options
)
233 value
= pcupsGetOption( opt_name
, num_options
, options
);
234 if (!value
) return NULL
;
236 len
= strlen( value
) + 1;
237 ret
= malloc( len
* sizeof(WCHAR
) );
238 if (ret
) ntdll_umbstowcs( value
, len
, ret
, len
);
243 static cups_ptype_t
cups_get_printer_type( const cups_dest_t
*dest
)
249 value
= pcupsGetOption( "printer-type", dest
->num_options
, dest
->options
);
250 if (!value
) return 0;
251 ret
= (cups_ptype_t
)strtoul( value
, &end
, 10 );
256 static BOOL
cups_is_scanner( cups_dest_t
*dest
)
258 return cups_get_printer_type( dest
) & 0x2000000 /* CUPS_PRINTER_SCANNER */;
261 static http_status_t
cupsGetPPD3_wrapper( http_t
*http
, const char *name
, time_t *modtime
,
262 char *buffer
, size_t bufsize
)
266 if (pcupsGetPPD3
) return pcupsGetPPD3( http
, name
, modtime
, buffer
, bufsize
);
267 if (!pcupsGetPPD
) return HTTP_NOT_FOUND
;
269 TRACE( "No cupsGetPPD3 implementation, so calling cupsGetPPD\n" );
272 ppd
= pcupsGetPPD( name
);
274 TRACE( "cupsGetPPD returns %s\n", debugstr_a(ppd
) );
276 if (!ppd
) return HTTP_NOT_FOUND
;
278 if (rename( ppd
, buffer
) == -1)
280 BOOL res
= copy_file( ppd
, buffer
);
282 if (!res
) return HTTP_NOT_FOUND
;
287 /*****************************************************************************
288 * get_cups_jobs_ticket_options
290 * Explicitly set CUPS options based on any %cupsJobTicket lines.
291 * The CUPS scheduler only looks for these in Print-File requests, and since
292 * cupsPrintFile uses Create-Job / Send-Document, the ticket lines don't get
295 static int get_cups_job_ticket_options( const char *file
, int num_options
, cups_option_t
**options
)
297 FILE *fp
= fopen( file
, "r" );
298 char buf
[257]; /* DSC max of 256 + '\0' */
299 const char *ps_adobe
= "%!PS-Adobe-";
300 const char *cups_job
= "%cupsJobTicket:";
302 if (!fp
) return num_options
;
303 if (!fgets( buf
, sizeof(buf
), fp
)) goto end
;
304 if (strncmp( buf
, ps_adobe
, strlen( ps_adobe
) )) goto end
;
305 while (fgets( buf
, sizeof(buf
), fp
))
307 if (strncmp( buf
, cups_job
, strlen( cups_job
) )) break;
308 num_options
= pcupsParseOptions( buf
+ strlen( cups_job
), num_options
, options
);
316 static int get_cups_default_options( const char *printer
, int num_options
, cups_option_t
**options
)
321 if (!pcupsGetNamedDest
) return num_options
;
323 dest
= pcupsGetNamedDest( NULL
, printer
, NULL
);
324 if (!dest
) return num_options
;
326 for (i
= 0; i
< dest
->num_options
; i
++)
328 if (!pcupsGetOption( dest
->options
[i
].name
, num_options
, *options
))
329 num_options
= pcupsAddOption( dest
->options
[i
].name
, dest
->options
[i
].value
,
330 num_options
, options
);
333 pcupsFreeDests( 1, dest
);
336 #endif /* SONAME_LIBCUPS */
338 static NTSTATUS
enum_printers( void *args
)
340 const struct enum_printers_params
*params
= args
;
341 #ifdef SONAME_LIBCUPS
342 unsigned int num
, i
, name_len
, comment_len
, location_len
, needed
;
343 WCHAR
*comment
, *location
, *ptr
;
344 struct printer_info
*info
;
348 if (!pcupsGetDests
) return STATUS_NOT_SUPPORTED
;
350 num
= pcupsGetDests( &dests
);
352 for (i
= 0; i
< num
; i
++)
354 if (cups_is_scanner( dests
+ i
))
356 TRACE( "Printer %d: %s - skipping scanner\n", i
, debugstr_a( dests
[i
].name
) );
359 TRACE( "Printer %d: %s\n", i
, debugstr_a( dests
[i
].name
) );
363 needed
= sizeof( *info
) * *params
->num
;
364 info
= params
->printers
;
365 ptr
= (WCHAR
*)(info
+ *params
->num
);
367 for (i
= 0; i
< num
; i
++)
369 if (cups_is_scanner( dests
+ i
)) continue;
371 comment
= cups_get_optionW( "printer-info", dests
[i
].num_options
, dests
[i
].options
);
372 location
= cups_get_optionW( "printer-location", dests
[i
].num_options
, dests
[i
].options
);
374 name_len
= strlen( dests
[i
].name
) + 1;
375 comment_len
= comment
? strlenW( comment
) + 1 : 0;
376 location_len
= location
? strlenW( location
) + 1 : 0;
377 needed
+= (name_len
+ comment_len
+ location_len
) * sizeof(WCHAR
);
379 if (needed
<= *params
->size
)
382 ntdll_umbstowcs( dests
[i
].name
, name_len
, info
->name
, name_len
);
383 info
->comment
= comment
? ptr
+ name_len
: NULL
;
384 memcpy( info
->comment
, comment
, comment_len
* sizeof(WCHAR
) );
385 info
->location
= location
? ptr
+ name_len
+ comment_len
: NULL
;
386 memcpy( info
->location
, location
, location_len
* sizeof(WCHAR
) );
387 info
->is_default
= dests
[i
].is_default
;
389 ptr
+= name_len
+ comment_len
+ location_len
;
394 pcupsFreeDests( num
, dests
);
396 if (needed
> *params
->size
)
398 *params
->size
= needed
;
399 return STATUS_BUFFER_OVERFLOW
;
401 return STATUS_SUCCESS
;
404 return STATUS_NOT_SUPPORTED
;
405 #endif /* SONAME_LIBCUPS */
408 static NTSTATUS
get_ppd( void *args
)
410 const struct get_ppd_params
*params
= args
;
411 char *unix_ppd
= get_unix_file_name( params
->ppd
);
412 NTSTATUS status
= STATUS_SUCCESS
;
414 TRACE( "(%s, %s)\n", debugstr_w( params
->printer
), debugstr_w( params
->ppd
) );
416 if (!unix_ppd
) return STATUS_NO_SUCH_FILE
;
418 if (!params
->printer
) /* unlink */
424 #ifdef SONAME_LIBCUPS
425 http_status_t http_status
;
430 len
= strlenW( params
->printer
);
431 printer_name
= malloc( len
* 3 + 1 );
432 ntdll_wcstoumbs( params
->printer
, len
+ 1, printer_name
, len
* 3 + 1, FALSE
);
434 http_status
= cupsGetPPD3_wrapper( 0, printer_name
, &modtime
, unix_ppd
, strlen( unix_ppd
) + 1 );
435 if (http_status
!= HTTP_OK
)
438 status
= STATUS_DEVICE_UNREACHABLE
;
440 free( printer_name
);
442 status
= STATUS_NOT_SUPPORTED
;
449 static NTSTATUS
get_default_page_size( void *args
)
451 #ifdef HAVE_APPLICATIONSERVICES_APPLICATIONSERVICES_H
452 const struct get_default_page_size_params
*params
= args
;
453 NTSTATUS status
= STATUS_UNSUCCESSFUL
;
454 PMPrintSession session
= NULL
;
455 PMPageFormat format
= NULL
;
456 CFStringRef paper_name
;
461 if (PMCreateSession( &session
)) goto end
;
462 if (PMCreatePageFormat( &format
)) goto end
;
463 if (PMSessionDefaultPageFormat( session
, format
)) goto end
;
464 if (PMGetPageFormatPaper( format
, &paper
)) goto end
;
465 if (PMPaperGetPPDPaperName( paper
, &paper_name
)) goto end
;
468 range
.length
= CFStringGetLength( paper_name
);
469 size
= (range
.length
+ 1) * sizeof(WCHAR
);
471 if (*params
->name_size
>= size
)
473 CFStringGetCharacters( paper_name
, range
, (UniChar
*)params
->name
);
474 params
->name
[range
.length
] = 0;
475 status
= STATUS_SUCCESS
;
478 status
= STATUS_BUFFER_OVERFLOW
;
479 *params
->name_size
= size
;
482 if (format
) PMRelease( format
);
483 if (session
) PMRelease( session
);
486 return STATUS_NOT_IMPLEMENTED
;
490 /*****************************************************************************
493 static BOOL
schedule_pipe( const WCHAR
*cmd
, const WCHAR
*filename
)
495 char *unixname
, *cmdA
;
497 int fds
[2] = { -1, -1 }, file_fd
= -1, no_read
;
503 if (!(unixname
= get_unix_file_name( filename
))) return FALSE
;
505 len
= strlenW( cmd
);
506 cmdA
= malloc( len
* 3 + 1);
507 ntdll_wcstoumbs( cmd
, len
+ 1, cmdA
, len
* 3 + 1, FALSE
);
509 TRACE( "printing with: %s\n", cmdA
);
511 if ((file_fd
= open( unixname
, O_RDONLY
)) == -1) goto end
;
515 ERR( "pipe() failed!\n" );
519 if ((pid
= fork()) == 0)
525 /* reset signals that we previously set to SIG_IGN */
526 signal( SIGPIPE
, SIG_DFL
);
528 execl( "/bin/sh", "/bin/sh", "-c", cmdA
, NULL
);
533 ERR( "fork() failed!\n" );
539 while ((no_read
= read( file_fd
, buf
, sizeof(buf
) )) > 0)
540 write( fds
[1], buf
, no_read
);
547 wret
= waitpid( pid
, &status
, 0 );
548 } while (wret
< 0 && errno
== EINTR
);
551 ERR( "waitpid() failed!\n" );
554 if (!WIFEXITED(status
) || WEXITSTATUS(status
))
556 ERR( "child process failed! %d\n", status
);
563 if (file_fd
!= -1) close( file_fd
);
564 if (fds
[0] != -1) close( fds
[0] );
565 if (fds
[1] != -1) close( fds
[1] );
572 /*****************************************************************************
575 static BOOL
schedule_unixfile( const WCHAR
*output
, const WCHAR
*filename
)
577 char *unixname
, *outputA
;
581 if (!(unixname
= get_unix_file_name( filename
))) return FALSE
;
583 len
= strlenW( output
);
584 outputA
= malloc( len
* 3 + 1);
585 ntdll_wcstoumbs( output
, len
+ 1, outputA
, len
* 3 + 1, FALSE
);
587 ret
= copy_file( unixname
, outputA
);
594 /*****************************************************************************
597 static BOOL
schedule_lpr( const WCHAR
*printer_name
, const WCHAR
*filename
)
599 static const WCHAR lpr
[] = { 'l','p','r',' ','-','P','\'' };
600 static const WCHAR quote
[] = { '\'',0 };
601 int printer_len
= strlenW( printer_name
);
605 cmd
= malloc( printer_len
* sizeof(WCHAR
) + sizeof(lpr
) + sizeof(quote
) );
606 memcpy( cmd
, lpr
, sizeof(lpr
) );
607 memcpy( cmd
+ ARRAY_SIZE(lpr
), printer_name
, printer_len
* sizeof(WCHAR
) );
608 memcpy( cmd
+ ARRAY_SIZE(lpr
) + printer_len
, quote
, sizeof(quote
) );
610 ret
= schedule_pipe( cmd
, filename
);
616 /*****************************************************************************
619 static BOOL
schedule_cups( const WCHAR
*printer_name
, const WCHAR
*filename
, const WCHAR
*document_title
)
621 #ifdef SONAME_LIBCUPS
624 char *unixname
, *queue
, *unix_doc_title
;
625 cups_option_t
*options
= NULL
;
626 int num_options
= 0, i
;
630 if (!(unixname
= get_unix_file_name( filename
))) return FALSE
;
631 len
= strlenW( printer_name
);
632 queue
= malloc( len
* 3 + 1);
633 ntdll_wcstoumbs( printer_name
, len
+ 1, queue
, len
* 3 + 1, FALSE
);
635 len
= strlenW( document_title
);
636 unix_doc_title
= malloc( len
* 3 + 1 );
637 ntdll_wcstoumbs( document_title
, len
+ 1, unix_doc_title
, len
+ 3 + 1, FALSE
);
639 num_options
= get_cups_job_ticket_options( unixname
, num_options
, &options
);
640 num_options
= get_cups_default_options( queue
, num_options
, &options
);
642 TRACE( "printing via cups with options:\n" );
643 for (i
= 0; i
< num_options
; i
++)
644 TRACE( "\t%d: %s = %s\n", i
, options
[i
].name
, options
[i
].value
);
646 ret
= pcupsPrintFile( queue
, unixname
, unix_doc_title
, num_options
, options
);
647 if (ret
== 0 && pcupsLastErrorString
)
648 WARN( "cupsPrintFile failed with error %s\n", debugstr_a( pcupsLastErrorString() ) );
650 pcupsFreeOptions( num_options
, options
);
652 free( unix_doc_title
);
660 return schedule_lpr( printer_name
, filename
);
664 static NTSTATUS
schedule_job( void *args
)
666 const struct schedule_job_params
*params
= args
;
668 if (params
->wine_port
[0] == '|')
669 return schedule_pipe( params
->wine_port
+ 1, params
->filename
);
671 if (params
->wine_port
[0])
672 return schedule_unixfile( params
->wine_port
, params
->filename
);
674 if (!strncmpW( params
->port
, LPR_Port
, ARRAY_SIZE(LPR_Port
) - 1 ))
675 return schedule_lpr( params
->port
+ ARRAY_SIZE(LPR_Port
) - 1, params
->filename
);
677 if (!strncmpW( params
->port
, CUPS_Port
, ARRAY_SIZE(CUPS_Port
) - 1 ))
678 return schedule_cups( params
->port
+ ARRAY_SIZE(CUPS_Port
) - 1, params
->filename
, params
->document_title
);
683 const unixlib_entry_t __wine_unix_call_funcs
[] =
687 get_default_page_size
,
696 struct printer_info32
704 static NTSTATUS
wow64_enum_printers( void *args
)
711 } const *params32
= args
;
715 struct enum_printers_params params
=
717 ULongToPtr( params32
->printers
),
718 ULongToPtr( params32
->size
),
719 ULongToPtr( params32
->num
)
722 if (!(status
= enum_printers( ¶ms
)))
724 /* convert structures in place */
725 struct printer_info
*info
= ULongToPtr( params32
->printers
);
726 struct printer_info32
*info32
= (struct printer_info32
*)info
;
727 unsigned int num
= *(unsigned int *)ULongToPtr( params32
->num
);
729 for (i
= 0; i
< num
; i
++)
731 info32
[i
].name
= PtrToUlong(info
[i
].name
);
732 info32
[i
].comment
= PtrToUlong(info
[i
].comment
);
733 info32
[i
].location
= PtrToUlong(info
[i
].location
);
734 info32
[i
].is_default
= info
[i
].is_default
;
740 static NTSTATUS
wow64_get_default_page_size( void *args
)
746 } const *params32
= args
;
748 struct get_default_page_size_params params
=
750 ULongToPtr( params32
->name
),
751 ULongToPtr( params32
->name_size
)
754 return get_default_page_size( ¶ms
);
757 static NTSTATUS
wow64_get_ppd( void *args
)
763 } const *params32
= args
;
765 struct get_ppd_params params
=
767 ULongToPtr( params32
->printer
),
768 ULongToPtr( params32
->ppd
)
771 return get_ppd( ¶ms
);
774 static NTSTATUS
wow64_schedule_job( void *args
)
780 PTR32 document_title
;
782 } const *params32
= args
;
784 struct schedule_job_params params
=
786 ULongToPtr( params32
->filename
),
787 ULongToPtr( params32
->port
),
788 ULongToPtr( params32
->document_title
),
789 ULongToPtr( params32
->wine_port
)
792 return schedule_job( ¶ms
);
795 const unixlib_entry_t __wine_unix_call_wow64_funcs
[] =
799 wow64_get_default_page_size
,