mfplat/sample: Optimize copying to 2d buffer.
[wine.git] / dlls / winspool.drv / cups.c
blob6ad270709cc9271eadac499d44d3e48c91c89f82
1 /*
2 * CUPS functions
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
20 #if 0
21 #pragma makedep unix
22 #endif
24 #include "config.h"
26 #include <stdarg.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <dlfcn.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <sys/wait.h>
37 #ifdef HAVE_CUPS_CUPS_H
38 #include <cups/cups.h>
39 #endif
40 #ifdef HAVE_CUPS_PPD_H
41 #include <cups/ppd.h>
42 #endif
44 #ifdef __APPLE__
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
74 #undef LoadResource
75 #undef AnimatePalette
76 #undef EqualRgn
77 #undef FillRgn
78 #undef FrameRgn
79 #undef GetPixel
80 #undef InvertRgn
81 #undef LineTo
82 #undef OffsetRgn
83 #undef PaintRgn
84 #undef Polygon
85 #undef ResizePalette
86 #undef SetRectRgn
87 #undef EqualRect
88 #undef FillRect
89 #undef FrameRect
90 #undef GetCursor
91 #undef InvertRect
92 #undef OffsetRect
93 #undef PtInRect
94 #undef SetCursor
95 #undef SetRect
96 #undef ShowCursor
97 #undef UnionRect
98 #endif
100 #include "ntstatus.h"
101 #define WIN32_NO_STATUS
102 #include "windef.h"
103 #include "winbase.h"
104 #include "winternl.h"
105 #include "winuser.h"
106 #include "winerror.h"
107 #include "winreg.h"
108 #include "wingdi.h"
109 #include "winspool.h"
110 #include "ddk/winsplp.h"
111 #include "wine/debug.h"
112 #include "wine/unixlib.h"
114 #include "wspool.h"
116 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
118 #ifdef SONAME_LIBCUPS
120 static void *libcups_handle;
122 #define CUPS_FUNCS \
123 DO_FUNC(cupsFreeDests); \
124 DO_FUNC(cupsGetDests); \
125 DO_FUNC(cupsGetOption);
126 #define CUPS_OPT_FUNCS \
127 DO_FUNC(cupsGetPPD); \
128 DO_FUNC(cupsGetPPD3);
130 #define DO_FUNC(f) static typeof(f) *p##f
131 CUPS_FUNCS;
132 #undef DO_FUNC
133 static const char * (*pcupsGetPPD)(const char *);
134 static http_status_t (*pcupsGetPPD3)(http_t *, const char *, time_t *, char *, size_t);
136 #endif /* SONAME_LIBCUPS */
138 static NTSTATUS process_attach( void *args )
140 #ifdef SONAME_LIBCUPS
141 libcups_handle = dlopen( SONAME_LIBCUPS, RTLD_NOW );
142 TRACE( "%p: %s loaded\n", libcups_handle, SONAME_LIBCUPS );
143 if (!libcups_handle) return STATUS_DLL_NOT_FOUND;
145 #define DO_FUNC(x) \
146 p##x = dlsym( libcups_handle, #x ); \
147 if (!p##x) \
149 ERR( "failed to load symbol %s\n", #x ); \
150 libcups_handle = NULL; \
151 return STATUS_ENTRYPOINT_NOT_FOUND; \
153 CUPS_FUNCS;
154 #undef DO_FUNC
155 #define DO_FUNC(x) p##x = dlsym( libcups_handle, #x )
156 CUPS_OPT_FUNCS;
157 #undef DO_FUNC
158 return STATUS_SUCCESS;
159 #else /* SONAME_LIBCUPS */
160 return STATUS_NOT_SUPPORTED;
161 #endif /* SONAME_LIBCUPS */
164 static char *get_unix_file_name( LPCWSTR path )
166 UNICODE_STRING nt_name;
167 OBJECT_ATTRIBUTES attr;
168 NTSTATUS status;
169 ULONG size = 256;
170 char *buffer;
172 nt_name.Buffer = (WCHAR *)path;
173 nt_name.MaximumLength = nt_name.Length = wcslen( path ) * sizeof(WCHAR);
174 InitializeObjectAttributes( &attr, &nt_name, 0, 0, NULL );
175 for (;;)
177 if (!(buffer = malloc( size ))) return NULL;
178 status = wine_nt_to_unix_file_name( &attr, buffer, &size, FILE_OPEN_IF );
179 if (status != STATUS_BUFFER_TOO_SMALL) break;
180 free( buffer );
182 if (status && status != STATUS_NO_SUCH_FILE)
184 free( buffer );
185 return NULL;
187 return buffer;
191 #ifdef SONAME_LIBCUPS
192 static WCHAR *cups_get_optionW( const char *opt_name, int num_options, cups_option_t *options )
194 const char *value;
195 WCHAR *ret;
196 int len;
198 value = pcupsGetOption( opt_name, num_options, options );
199 if (!value) return NULL;
201 len = strlen( value ) + 1;
202 ret = malloc( len * sizeof(WCHAR) );
203 if (ret) ntdll_umbstowcs( value, len, ret, len );
205 return ret;
208 static cups_ptype_t cups_get_printer_type( const cups_dest_t *dest )
210 const char *value;
211 cups_ptype_t ret;
212 char *end;
214 value = pcupsGetOption( "printer-type", dest->num_options, dest->options );
215 if (!value) return 0;
216 ret = (cups_ptype_t)strtoul( value, &end, 10 );
217 if (*end) ret = 0;
218 return ret;
221 static BOOL cups_is_scanner( cups_dest_t *dest )
223 return cups_get_printer_type( dest ) & 0x2000000 /* CUPS_PRINTER_SCANNER */;
226 static BOOL copy_file( const char *src, const char *dst )
228 int fds[2] = { -1, -1 }, num;
229 char buf[1024];
230 BOOL ret = FALSE;
232 fds[0] = open( src, O_RDONLY );
233 fds[1] = open( dst, O_CREAT | O_TRUNC | O_WRONLY, 0666 );
234 if (fds[0] == -1 || fds[1] == -1) goto fail;
236 while ((num = read( fds[0], buf, sizeof(buf) )) != 0)
238 if (num == -1) goto fail;
239 if (write( fds[1], buf, num ) != num) goto fail;
241 ret = TRUE;
243 fail:
244 if (fds[1] != -1) close( fds[1] );
245 if (fds[0] != -1) close( fds[0] );
246 return ret;
249 static http_status_t cupsGetPPD3_wrapper( http_t *http, const char *name, time_t *modtime,
250 char *buffer, size_t bufsize )
252 const char *ppd;
254 if (pcupsGetPPD3) return pcupsGetPPD3( http, name, modtime, buffer, bufsize );
255 if (!pcupsGetPPD) return HTTP_NOT_FOUND;
257 TRACE( "No cupsGetPPD3 implementation, so calling cupsGetPPD\n" );
259 *modtime = 0;
260 ppd = pcupsGetPPD( name );
262 TRACE( "cupsGetPPD returns %s\n", debugstr_a(ppd) );
264 if (!ppd) return HTTP_NOT_FOUND;
266 if (rename( ppd, buffer ) == -1)
268 BOOL res = copy_file( ppd, buffer );
269 unlink( ppd );
270 if (!res) return HTTP_NOT_FOUND;
272 return HTTP_OK;
274 #endif /* SONAME_LIBCUPS */
276 static NTSTATUS enum_printers( void *args )
278 const struct enum_printers_params *params = args;
279 #ifdef SONAME_LIBCUPS
280 unsigned int num, i, name_len, comment_len, location_len, needed;
281 WCHAR *comment, *location, *ptr;
282 struct printer_info *info;
283 cups_dest_t *dests;
285 *params->num = 0;
286 if (!pcupsGetDests) return STATUS_NOT_SUPPORTED;
288 num = pcupsGetDests( &dests );
290 for (i = 0; i < num; i++)
292 if (cups_is_scanner( dests + i ))
294 TRACE( "Printer %d: %s - skipping scanner\n", i, debugstr_a( dests[i].name ) );
295 continue;
297 TRACE( "Printer %d: %s\n", i, debugstr_a( dests[i].name ) );
298 (*params->num)++;
301 needed = sizeof( *info ) * *params->num;
302 info = params->printers;
303 ptr = (WCHAR *)(info + *params->num);
305 for (i = 0; i < num; i++)
307 if (cups_is_scanner( dests + i )) continue;
309 comment = cups_get_optionW( "printer-info", dests[i].num_options, dests[i].options );
310 location = cups_get_optionW( "printer-location", dests[i].num_options, dests[i].options );
312 name_len = strlen( dests[i].name ) + 1;
313 comment_len = comment ? wcslen( comment ) + 1 : 0;
314 location_len = location ? wcslen( location ) + 1 : 0;
315 needed += (name_len + comment_len + location_len) * sizeof(WCHAR);
317 if (needed <= *params->size)
319 info->name = ptr;
320 ntdll_umbstowcs( dests[i].name, name_len, info->name, name_len );
321 info->comment = comment ? ptr + name_len : NULL;
322 memcpy( info->comment, comment, comment_len * sizeof(WCHAR) );
323 info->location = location ? ptr + name_len + comment_len : NULL;
324 memcpy( info->location, location, location_len * sizeof(WCHAR) );
325 info->is_default = dests[i].is_default;
326 info++;
327 ptr += name_len + comment_len + location_len;
329 free( comment );
330 free( location );
332 pcupsFreeDests( num, dests );
334 if (needed > *params->size)
336 *params->size = needed;
337 return STATUS_BUFFER_OVERFLOW;
339 return STATUS_SUCCESS;
340 #else
341 *params->num = 0;
342 return STATUS_NOT_SUPPORTED;
343 #endif /* SONAME_LIBCUPS */
346 static NTSTATUS get_ppd( void *args )
348 const struct get_ppd_params *params = args;
349 char *unix_ppd = get_unix_file_name( params->ppd );
350 NTSTATUS status = STATUS_SUCCESS;
352 TRACE( "(%s, %s)\n", debugstr_w( params->printer ), debugstr_w( params->ppd ) );
354 if (!unix_ppd) return STATUS_NO_SUCH_FILE;
356 if (!params->printer) /* unlink */
358 unlink( unix_ppd );
360 else
362 #ifdef SONAME_LIBCUPS
363 http_status_t http_status;
364 time_t modtime = 0;
365 char *printer_name;
366 int len;
368 len = wcslen( params->printer );
369 printer_name = malloc( len * 3 + 1 );
370 ntdll_wcstoumbs( params->printer, len + 1, printer_name, len * 3 + 1, FALSE );
372 http_status = cupsGetPPD3_wrapper( 0, printer_name, &modtime, unix_ppd, strlen( unix_ppd ) + 1 );
373 if (http_status != HTTP_OK)
375 unlink( unix_ppd );
376 status = STATUS_DEVICE_UNREACHABLE;
378 free( printer_name );
379 #else
380 status = STATUS_NOT_SUPPORTED;
381 #endif
383 free( unix_ppd );
384 return status;
387 static NTSTATUS get_default_page_size( void *args )
389 #ifdef __APPLE__
390 const struct get_default_page_size_params *params = args;
391 NTSTATUS status = STATUS_UNSUCCESSFUL;
392 PMPrintSession session = NULL;
393 PMPageFormat format = NULL;
394 CFStringRef paper_name;
395 PMPaper paper;
396 CFRange range;
397 int size;
399 if (PMCreateSession( &session )) goto end;
400 if (PMCreatePageFormat( &format )) goto end;
401 if (PMSessionDefaultPageFormat( session, format )) goto end;
402 if (PMGetPageFormatPaper( format, &paper )) goto end;
403 if (PMPaperGetPPDPaperName( paper, &paper_name )) goto end;
405 range.location = 0;
406 range.length = CFStringGetLength( paper_name );
407 size = (range.length + 1) * sizeof(WCHAR);
409 if (*params->name_size >= size)
411 CFStringGetCharacters( paper_name, range, (UniChar*)params->name );
412 params->name[range.length] = 0;
413 status = STATUS_SUCCESS;
415 else
416 status = STATUS_BUFFER_OVERFLOW;
417 *params->name_size = size;
419 end:
420 if (format) PMRelease( format );
421 if (session) PMRelease( session );
422 return status;
423 #else
424 return STATUS_NOT_IMPLEMENTED;
425 #endif
428 const unixlib_entry_t __wine_unix_call_funcs[] =
430 process_attach,
431 enum_printers,
432 get_default_page_size,
433 get_ppd,
436 C_ASSERT( ARRAYSIZE(__wine_unix_call_funcs) == unix_funcs_count );
438 #ifdef _WIN64
440 typedef ULONG PTR32;
442 struct printer_info32
444 PTR32 name;
445 PTR32 comment;
446 PTR32 location;
447 BOOL is_default;
450 static NTSTATUS wow64_enum_printers( void *args )
452 struct
454 PTR32 printers;
455 PTR32 size;
456 PTR32 num;
457 } const *params32 = args;
459 NTSTATUS status;
460 unsigned int i;
461 struct enum_printers_params params =
463 ULongToPtr( params32->printers ),
464 ULongToPtr( params32->size ),
465 ULongToPtr( params32->num )
468 if (!(status = enum_printers( &params )))
470 /* convert structures in place */
471 struct printer_info *info = ULongToPtr( params32->printers );
472 struct printer_info32 *info32 = (struct printer_info32 *)info;
473 unsigned int num = *(unsigned int *)ULongToPtr( params32->num );
475 for (i = 0; i < num; i++)
477 info32[i].name = PtrToUlong(info[i].name);
478 info32[i].comment = PtrToUlong(info[i].comment);
479 info32[i].location = PtrToUlong(info[i].location);
480 info32[i].is_default = info[i].is_default;
483 return status;
486 static NTSTATUS wow64_get_default_page_size( void *args )
488 struct
490 PTR32 name;
491 PTR32 name_size;
492 } const *params32 = args;
494 struct get_default_page_size_params params =
496 ULongToPtr( params32->name ),
497 ULongToPtr( params32->name_size )
500 return get_default_page_size( &params );
503 static NTSTATUS wow64_get_ppd( void *args )
505 struct
507 PTR32 printer;
508 PTR32 ppd;
509 } const *params32 = args;
511 struct get_ppd_params params =
513 ULongToPtr( params32->printer ),
514 ULongToPtr( params32->ppd )
517 return get_ppd( &params );
520 const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
522 process_attach,
523 wow64_enum_printers,
524 wow64_get_default_page_size,
525 wow64_get_ppd,
528 C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count );
530 #endif /* _WIN64 */