d3d9: Get rid of IDirect3DVertexBuffer9Impl.
[wine] / dlls / winspool.drv / info.c
1 /*
2  * WINSPOOL functions
3  *
4  * Copyright 1996 John Harvey
5  * Copyright 1998 Andreas Mohr
6  * Copyright 1999 Klaas van Gend
7  * Copyright 1999, 2000 Huw D M Davies
8  * Copyright 2001 Marcus Meissner
9  * Copyright 2005-2010 Detlef Riekenberg
10  * Copyright 2010 Vitaly Perov
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26
27 #include "config.h"
28 #include "wine/port.h"
29
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <stddef.h>
36 #include <errno.h>
37 #ifdef HAVE_SYS_WAIT_H
38 #include <sys/wait.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 #include <signal.h>
44 #ifdef HAVE_CUPS_CUPS_H
45 # include <cups/cups.h>
46 #endif
47
48 #define NONAMELESSUNION
49 #define NONAMELESSSTRUCT
50 #include "wine/library.h"
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winuser.h"
54 #include "winerror.h"
55 #include "winreg.h"
56 #include "wingdi.h"
57 #include "winspool.h"
58 #include "winternl.h"
59 #include "wine/windef16.h"
60 #include "wine/unicode.h"
61 #include "wine/debug.h"
62 #include "wine/list.h"
63 #include "winnls.h"
64
65 #include "ddk/winsplp.h"
66 #include "wspool.h"
67
68 WINE_DEFAULT_DEBUG_CHANNEL(winspool);
69
70 /* ############################### */
71
72 static CRITICAL_SECTION printer_handles_cs;
73 static CRITICAL_SECTION_DEBUG printer_handles_cs_debug = 
74 {
75     0, 0, &printer_handles_cs,
76     { &printer_handles_cs_debug.ProcessLocksList, &printer_handles_cs_debug.ProcessLocksList },
77       0, 0, { (DWORD_PTR)(__FILE__ ": printer_handles_cs") }
78 };
79 static CRITICAL_SECTION printer_handles_cs = { &printer_handles_cs_debug, -1, 0, 0, 0, 0 };
80
81 /* ############################### */
82
83 typedef struct {
84     DWORD job_id;
85     HANDLE hf;
86 } started_doc_t;
87
88 typedef struct {
89     struct list jobs;
90     LONG ref;
91 } jobqueue_t;
92
93 typedef struct {
94     LPWSTR name;
95     LPWSTR printername;
96     HANDLE backend_printer;
97     jobqueue_t *queue;
98     started_doc_t *doc;
99     DEVMODEW *devmode;
100 } opened_printer_t;
101
102 typedef struct {
103     struct list entry;
104     DWORD job_id;
105     WCHAR *filename;
106     WCHAR *portname;
107     WCHAR *document_title;
108     WCHAR *printer_name;
109     LPDEVMODEW devmode;
110 } job_t;
111
112
113 typedef struct {
114     LPCWSTR  envname;
115     LPCWSTR  subdir;
116     DWORD    driverversion;
117     LPCWSTR  versionregpath;
118     LPCWSTR  versionsubdir;
119 } printenv_t;
120
121 /* ############################### */
122
123 static opened_printer_t **printer_handles;
124 static UINT nb_printer_handles;
125 static LONG next_job_id = 1;
126
127 static DWORD (WINAPI *GDI_CallDeviceCapabilities16)( LPCSTR lpszDevice, LPCSTR lpszPort,
128                                                      WORD fwCapability, LPSTR lpszOutput,
129                                                      LPDEVMODEA lpdm );
130 static INT (WINAPI *GDI_CallExtDeviceMode16)( HWND hwnd, LPDEVMODEA lpdmOutput,
131                                               LPSTR lpszDevice, LPSTR lpszPort,
132                                               LPDEVMODEA lpdmInput, LPSTR lpszProfile,
133                                               DWORD fwMode );
134
135 static const WCHAR DriversW[] = { 'S','y','s','t','e','m','\\',
136                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
137                                   'c','o','n','t','r','o','l','\\',
138                                   'P','r','i','n','t','\\',
139                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
140                                   '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
141
142 static const WCHAR PrintersW[] = {'S','y','s','t','e','m','\\',
143                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
144                                   'C','o','n','t','r','o','l','\\',
145                                   'P','r','i','n','t','\\',
146                                   'P','r','i','n','t','e','r','s',0};
147
148 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
149
150 static const WCHAR user_default_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
151                                               'M','i','c','r','o','s','o','f','t','\\',
152                                               'W','i','n','d','o','w','s',' ','N','T','\\',
153                                               'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
154                                               'W','i','n','d','o','w','s',0};
155
156 static const WCHAR user_printers_reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
157                                                'M','i','c','r','o','s','o','f','t','\\',
158                                                'W','i','n','d','o','w','s',' ','N','T','\\',
159                                                'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
160                                                'D','e','v','i','c','e','s',0};
161
162 static const WCHAR WinNT_CV_PortsW[] = {'S','o','f','t','w','a','r','e','\\',
163                                         'M','i','c','r','o','s','o','f','t','\\',
164                                         'W','i','n','d','o','w','s',' ','N','T','\\',
165                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
166                                         'P','o','r','t','s',0};
167
168 static const WCHAR WinNT_CV_PrinterPortsW[] = { 'S','o','f','t','w','a','r','e','\\',
169                                                 'M','i','c','r','o','s','o','f','t','\\',
170                                                 'W','i','n','d','o','w','s',' ','N','T','\\',
171                                                 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
172                                                 'P','r','i','n','t','e','r','P','o','r','t','s',0};
173
174 static const WCHAR DefaultEnvironmentW[] = {'W','i','n','e',0};
175 static       WCHAR envname_win40W[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
176 static const WCHAR envname_x64W[] =   {'W','i','n','d','o','w','s',' ','x','6','4',0};
177 static       WCHAR envname_x86W[] =   {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
178 static const WCHAR subdir_win40W[] = {'w','i','n','4','0',0};
179 static const WCHAR subdir_x64W[] =   {'x','6','4',0};
180 static const WCHAR subdir_x86W[] =   {'w','3','2','x','8','6',0};
181 static const WCHAR Version0_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
182 static const WCHAR Version0_SubdirW[] = {'\\','0',0};
183 static const WCHAR Version3_RegPathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
184 static const WCHAR Version3_SubdirW[] = {'\\','3',0};
185
186 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
187
188 static const WCHAR AttributesW[] = {'A','t','t','r','i','b','u','t','e','s',0};
189 static const WCHAR backslashW[] = {'\\',0};
190 static const WCHAR Configuration_FileW[] = {'C','o','n','f','i','g','u','r','a','t',
191                                       'i','o','n',' ','F','i','l','e',0};
192 static const WCHAR DatatypeW[] = {'D','a','t','a','t','y','p','e',0};
193 static const WCHAR Data_FileW[] = {'D','a','t','a',' ','F','i','l','e',0};
194 static const WCHAR Default_DevModeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
195 static const WCHAR Default_PriorityW[] = {'D','e','f','a','u','l','t',' ','P','r','i','o','r','i','t','y',0};
196 static const WCHAR Dependent_FilesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
197 static const WCHAR DescriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
198 static const WCHAR dnsTimeoutW[] = {'d','n','s','T','i','m','e','o','u','t',0};
199 static const WCHAR DriverW[] = {'D','r','i','v','e','r',0};
200 static const WCHAR HardwareIDW[] = {'H','a','r','d','w','a','r','e','I','D',0};
201 static const WCHAR Help_FileW[] = {'H','e','l','p',' ','F','i','l','e',0};
202 static const WCHAR LocationW[] = {'L','o','c','a','t','i','o','n',0};
203 static const WCHAR ManufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
204 static const WCHAR MonitorW[] = {'M','o','n','i','t','o','r',0};
205 static const WCHAR NameW[] = {'N','a','m','e',0};
206 static const WCHAR ObjectGUIDW[] = {'O','b','j','e','c','t','G','U','I','D',0};
207 static const WCHAR OEM_UrlW[] = {'O','E','M',' ','U','r','l',0};
208 static const WCHAR ParametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
209 static const WCHAR PortW[] = {'P','o','r','t',0};
210 static const WCHAR bs_Ports_bsW[] = {'\\','P','o','r','t','s','\\',0};
211 static const WCHAR Previous_NamesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
212 static const WCHAR Print_ProcessorW[] = {'P','r','i','n','t',' ','P','r','o','c','e','s','s','o','r',0};
213 static const WCHAR Printer_DriverW[] = {'P','r','i','n','t','e','r',' ','D','r','i','v','e','r',0};
214 static const WCHAR PrinterDriverDataW[] = {'P','r','i','n','t','e','r','D','r','i','v','e','r','D','a','t','a',0};
215 static const WCHAR PrinterPortsW[] = {'P','r','i','n','t','e','r','P','o','r','t','s',0};
216 static const WCHAR PriorityW[] = {'P','r','i','o','r','i','t','y',0};
217 static const WCHAR ProviderW[] = {'P','r','o','v','i','d','e','r',0};
218 static const WCHAR Separator_FileW[] = {'S','e','p','a','r','a','t','o','r',' ','F','i','l','e',0};
219 static const WCHAR Share_NameW[] = {'S','h','a','r','e',' ','N','a','m','e',0};
220 static const WCHAR StartTimeW[] = {'S','t','a','r','t','T','i','m','e',0};
221 static const WCHAR StatusW[] = {'S','t','a','t','u','s',0};
222 static const WCHAR txTimeoutW[] = {'t','x','T','i','m','e','o','u','t',0};
223 static const WCHAR UntilTimeW[] = {'U','n','t','i','l','T','i','m','e',0};
224 static const WCHAR VersionW[] = {'V','e','r','s','i','o','n',0};
225 static       WCHAR WinPrintW[] = {'W','i','n','P','r','i','n','t',0};
226 static const WCHAR deviceW[]  = {'d','e','v','i','c','e',0};
227 static const WCHAR devicesW[] = {'d','e','v','i','c','e','s',0};
228 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
229 static       WCHAR generic_ppdW[] = {'g','e','n','e','r','i','c','.','p','p','d',0};
230 static       WCHAR rawW[] = {'R','A','W',0};
231 static       WCHAR driver_9x[] = {'w','i','n','e','p','s','1','6','.','d','r','v',0};
232 static       WCHAR driver_nt[] = {'w','i','n','e','p','s','.','d','r','v',0};
233 static const WCHAR timeout_15_45[] = {',','1','5',',','4','5',0};
234 static const WCHAR commaW[] = {',',0};
235 static       WCHAR emptyStringW[] = {0};
236
237 static const WCHAR May_Delete_Value[] = {'W','i','n','e','M','a','y','D','e','l','e','t','e','M','e',0};
238
239 static const WCHAR CUPS_Port[] = {'C','U','P','S',':',0};
240 static const WCHAR FILE_Port[] = {'F','I','L','E',':',0};
241 static const WCHAR LPR_Port[] = {'L','P','R',':',0};
242
243 static const WCHAR default_doc_title[] = {'L','o','c','a','l',' ','D','o','w','n','l','e','v','e','l',' ',
244                                           'D','o','c','u','m','e','n','t',0};
245
246 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
247                                      sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
248                                      sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
249                                   0, sizeof(DRIVER_INFO_8W)};
250
251
252 static const DWORD pi_sizeof[] = {0, sizeof(PRINTER_INFO_1W), sizeof(PRINTER_INFO_2W),
253                                      sizeof(PRINTER_INFO_3),  sizeof(PRINTER_INFO_4W),
254                                      sizeof(PRINTER_INFO_5W), sizeof(PRINTER_INFO_6),
255                                      sizeof(PRINTER_INFO_7W), sizeof(PRINTER_INFO_8W),
256                                      sizeof(PRINTER_INFO_9W)};
257
258 static const printenv_t env_x64 = {envname_x64W, subdir_x64W, 3, Version3_RegPathW, Version3_SubdirW};
259 static const printenv_t env_x86 = {envname_x86W, subdir_x86W, 3, Version3_RegPathW, Version3_SubdirW};
260 static const printenv_t env_win40 = {envname_win40W, subdir_win40W, 0, Version0_RegPathW, Version0_SubdirW};
261
262 static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_win40};
263
264 /******************************************************************
265  *  validate the user-supplied printing-environment [internal]
266  *
267  * PARAMS
268  *  env  [I] PTR to Environment-String or NULL
269  *
270  * RETURNS
271  *  Failure:  NULL
272  *  Success:  PTR to printenv_t
273  *
274  * NOTES
275  *  An empty string is handled the same way as NULL.
276  *  SetLastEror(ERROR_INVALID_ENVIRONMENT) is called on Failure
277  *  
278  */
279
280 static const  printenv_t * validate_envW(LPCWSTR env)
281 {
282     const printenv_t *result = NULL;
283     unsigned int i;
284
285     TRACE("testing %s\n", debugstr_w(env));
286     if (env && env[0])
287     {
288         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
289         {
290             if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
291             {
292                 result = all_printenv[i];
293                 break;
294             }
295         }
296
297         if (result == NULL) {
298             FIXME("unsupported Environment: %s\n", debugstr_w(env));
299             SetLastError(ERROR_INVALID_ENVIRONMENT);
300         }
301         /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
302     }
303     else
304     {
305         result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
306     }
307     TRACE("using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
308
309     return result;
310 }
311
312
313 /* RtlCreateUnicodeStringFromAsciiz will return an empty string in the buffer
314    if passed a NULL string. This returns NULLs to the result. 
315 */
316 static inline PWSTR asciitounicode( UNICODE_STRING * usBufferPtr, LPCSTR src )
317 {
318     if ( (src) )
319     {
320         RtlCreateUnicodeStringFromAsciiz(usBufferPtr, src);
321         return usBufferPtr->Buffer;
322     }
323     usBufferPtr->Buffer = NULL; /* so that RtlFreeUnicodeString won't barf */
324     return NULL;
325 }
326             
327 static LPWSTR strdupW(LPCWSTR p)
328 {
329     LPWSTR ret;
330     DWORD len;
331
332     if(!p) return NULL;
333     len = (strlenW(p) + 1) * sizeof(WCHAR);
334     ret = HeapAlloc(GetProcessHeap(), 0, len);
335     memcpy(ret, p, len);
336     return ret;
337 }
338
339 static LPSTR strdupWtoA( LPCWSTR str )
340 {
341     LPSTR ret;
342     INT len;
343
344     if (!str) return NULL;
345     len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
346     ret = HeapAlloc( GetProcessHeap(), 0, len );
347     if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
348     return ret;
349 }
350
351 static DEVMODEW *dup_devmode( const DEVMODEW *dm )
352 {
353     DEVMODEW *ret;
354
355     if (!dm) return NULL;
356     ret = HeapAlloc( GetProcessHeap(), 0, dm->dmSize + dm->dmDriverExtra );
357     if (ret) memcpy( ret, dm, dm->dmSize + dm->dmDriverExtra );
358     return ret;
359 }
360
361 /***********************************************************
362  * DEVMODEdupWtoA
363  * Creates an ansi copy of supplied devmode
364  */
365 static DEVMODEA *DEVMODEdupWtoA( const DEVMODEW *dmW )
366 {
367     LPDEVMODEA dmA;
368     DWORD size;
369
370     if (!dmW) return NULL;
371     size = dmW->dmSize - CCHDEVICENAME -
372                         ((dmW->dmSize > FIELD_OFFSET( DEVMODEW, dmFormName )) ? CCHFORMNAME : 0);
373
374     dmA = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size + dmW->dmDriverExtra );
375     if (!dmA) return NULL;
376
377     WideCharToMultiByte( CP_ACP, 0, dmW->dmDeviceName, -1,
378                          (LPSTR)dmA->dmDeviceName, CCHDEVICENAME, NULL, NULL );
379
380     if (FIELD_OFFSET( DEVMODEW, dmFormName ) >= dmW->dmSize)
381     {
382         memcpy( &dmA->dmSpecVersion, &dmW->dmSpecVersion,
383                 dmW->dmSize - FIELD_OFFSET( DEVMODEW, dmSpecVersion ) );
384     }
385     else
386     {
387         memcpy( &dmA->dmSpecVersion, &dmW->dmSpecVersion,
388                 FIELD_OFFSET( DEVMODEW, dmFormName ) - FIELD_OFFSET( DEVMODEW, dmSpecVersion ) );
389         WideCharToMultiByte( CP_ACP, 0, dmW->dmFormName, -1,
390                              (LPSTR)dmA->dmFormName, CCHFORMNAME, NULL, NULL );
391
392         memcpy( &dmA->dmLogPixels, &dmW->dmLogPixels, dmW->dmSize - FIELD_OFFSET( DEVMODEW, dmLogPixels ) );
393     }
394
395     dmA->dmSize = size;
396     memcpy( (char *)dmA + dmA->dmSize, (const char *)dmW + dmW->dmSize, dmW->dmDriverExtra );
397     return dmA;
398 }
399
400
401 /******************************************************************
402  * verify, that the filename is a local file
403  *
404  */
405 static inline BOOL is_local_file(LPWSTR name)
406 {
407     return (name[0] && (name[1] == ':') && (name[2] == '\\'));
408 }
409
410 /* ################################ */
411
412 static int multi_sz_lenA(const char *str)
413 {
414     const char *ptr = str;
415     if(!str) return 0;
416     do
417     {
418         ptr += lstrlenA(ptr) + 1;
419     } while(*ptr);
420
421     return ptr - str + 1;
422 }
423
424 static void
425 WINSPOOL_SetDefaultPrinter(const char *devname, const char *name, BOOL force) {
426     char qbuf[200];
427
428     /* If forcing, or no profile string entry for device yet, set the entry
429      *
430      * The always change entry if not WINEPS yet is discussable.
431      */
432     if (force                                                           ||
433         !GetProfileStringA("windows","device","*",qbuf,sizeof(qbuf))    ||
434         !strcmp(qbuf,"*")                                               ||
435         !strstr(qbuf,"WINEPS.DRV")
436     ) {
437         char *buf = HeapAlloc(GetProcessHeap(),0,strlen(name)+strlen(devname)+strlen(",WINEPS.DRV,LPR:")+1);
438         HKEY hkey;
439
440         sprintf(buf,"%s,WINEPS.DRV,LPR:%s",devname,name);
441         WriteProfileStringA("windows","device",buf);
442         if(RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hkey) == ERROR_SUCCESS) {
443             RegSetValueExA(hkey, "Device", 0, REG_SZ, (LPBYTE)buf, strlen(buf) + 1);
444             RegCloseKey(hkey);
445         }
446         HeapFree(GetProcessHeap(),0,buf);
447     }
448 }
449
450 static BOOL add_printer_driver(WCHAR *name)
451 {
452     DRIVER_INFO_3W di3;
453
454     ZeroMemory(&di3, sizeof(DRIVER_INFO_3W));
455     di3.cVersion         = 3;
456     di3.pName            = name;
457     di3.pEnvironment     = envname_x86W;
458     di3.pDriverPath      = driver_nt;
459     di3.pDataFile        = generic_ppdW;
460     di3.pConfigFile      = driver_nt;
461     di3.pDefaultDataType = rawW;
462
463     if (AddPrinterDriverW(NULL, 3, (LPBYTE)&di3) ||
464         (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
465     {
466         di3.cVersion     = 0;
467         di3.pEnvironment = envname_win40W;
468         di3.pDriverPath  = driver_9x;
469         di3.pConfigFile  = driver_9x;
470         if (AddPrinterDriverW(NULL, 3, (LPBYTE)&di3) ||
471             (GetLastError() ==  ERROR_PRINTER_DRIVER_ALREADY_INSTALLED ))
472         {
473             return TRUE;
474         }
475     }
476     ERR("failed with %u for %s (%s)\n", GetLastError(), debugstr_w(di3.pDriverPath), debugstr_w(di3.pEnvironment));
477     return FALSE;
478 }
479
480 #ifdef SONAME_LIBCUPS
481
482 static void *cupshandle;
483
484 #define CUPS_FUNCS \
485     DO_FUNC(cupsFreeDests); \
486     DO_FUNC(cupsFreeOptions); \
487     DO_FUNC(cupsGetDests); \
488     DO_FUNC(cupsGetPPD); \
489     DO_FUNC(cupsParseOptions); \
490     DO_FUNC(cupsPrintFile);
491
492 #define DO_FUNC(f) static typeof(f) *p##f
493 CUPS_FUNCS;
494 #undef DO_FUNC
495
496 static BOOL CUPS_LoadPrinters(void)
497 {
498     int                   i, nrofdests;
499     BOOL                  hadprinter = FALSE, haddefault = FALSE;
500     cups_dest_t          *dests;
501     PRINTER_INFO_2W       pi2;
502     WCHAR   *port;
503     HKEY hkeyPrinter, hkeyPrinters;
504     char    loaderror[256];
505     WCHAR   nameW[MAX_PATH];
506     HANDLE  added_printer;
507
508     cupshandle = wine_dlopen(SONAME_LIBCUPS, RTLD_NOW, loaderror, sizeof(loaderror));
509     if (!cupshandle) {
510         TRACE("%s\n", loaderror);
511         return FALSE;
512     }
513     TRACE("%p: %s loaded\n", cupshandle, SONAME_LIBCUPS);
514
515 #define DO_FUNC(x) p##x = wine_dlsym( cupshandle, #x, NULL, 0 ); if (!p##x) return FALSE;
516     CUPS_FUNCS;
517 #undef DO_FUNC
518
519     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
520        ERROR_SUCCESS) {
521         ERR("Can't create Printers key\n");
522         return FALSE;
523     }
524
525     nrofdests = pcupsGetDests(&dests);
526     TRACE("Found %d CUPS %s:\n", nrofdests, (nrofdests == 1) ? "printer" : "printers");
527     for (i=0;i<nrofdests;i++) {
528         MultiByteToWideChar(CP_UNIXCP, 0, dests[i].name, -1, nameW, sizeof(nameW) / sizeof(WCHAR));
529
530         port = HeapAlloc(GetProcessHeap(), 0, sizeof(CUPS_Port) + lstrlenW(nameW) * sizeof(WCHAR));
531         lstrcpyW(port, CUPS_Port);
532         lstrcatW(port, nameW);
533
534         TRACE("Printer %d: %s\n", i, debugstr_w(nameW));
535         if(RegOpenKeyW(hkeyPrinters, nameW, &hkeyPrinter) == ERROR_SUCCESS) {
536             /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
537                and continue */
538             TRACE("Printer already exists\n");
539             /* overwrite old LPR:* port */
540             RegSetValueExW(hkeyPrinter, PortW, 0, REG_SZ, (LPBYTE)port, (lstrlenW(port) + 1) * sizeof(WCHAR));
541             RegDeleteValueW(hkeyPrinter, May_Delete_Value);
542             RegCloseKey(hkeyPrinter);
543         } else {
544             static WCHAR comment_cups[]  = {'W','I','N','E','P','S',' ','P','r','i','n','t','e','r',
545                                             ' ','u','s','i','n','g',' ','C','U','P','S',0};
546
547             add_printer_driver(nameW);
548
549             memset(&pi2, 0, sizeof(PRINTER_INFO_2W));
550             pi2.pPrinterName    = nameW;
551             pi2.pDatatype       = rawW;
552             pi2.pPrintProcessor = WinPrintW;
553             pi2.pDriverName     = nameW;
554             pi2.pComment        = comment_cups;
555             pi2.pLocation       = emptyStringW;
556             pi2.pPortName       = port;
557             pi2.pParameters     = emptyStringW;
558             pi2.pShareName      = emptyStringW;
559             pi2.pSepFile        = emptyStringW;
560
561             added_printer = AddPrinterW( NULL, 2, (LPBYTE)&pi2 );
562             if (added_printer) ClosePrinter( added_printer );
563             else if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
564                 ERR( "printer '%s' not added by AddPrinter (error %d)\n", debugstr_w(nameW), GetLastError() );
565         }
566         HeapFree(GetProcessHeap(),0,port);
567
568         hadprinter = TRUE;
569         if (dests[i].is_default) {
570             SetDefaultPrinterW(nameW);
571             haddefault = TRUE;
572         }
573     }
574     if (hadprinter && !haddefault) {
575         MultiByteToWideChar(CP_UNIXCP, 0, dests[0].name, -1, nameW, sizeof(nameW) / sizeof(WCHAR));
576         SetDefaultPrinterW(nameW);
577     }
578     pcupsFreeDests(nrofdests, dests);
579     RegCloseKey(hkeyPrinters);
580     return TRUE;
581 }
582 #endif
583
584 static BOOL PRINTCAP_ParseEntry( const char *pent, BOOL isfirst )
585 {
586     PRINTER_INFO_2A     pinfo2a;
587     const char  *r;
588     size_t              name_len;
589     char                *e,*s,*name,*prettyname,*devname;
590     BOOL                ret = FALSE, set_default = FALSE;
591     char *port = NULL, *env_default;
592     HKEY hkeyPrinter, hkeyPrinters;
593     WCHAR devnameW[MAX_PATH];
594     HANDLE added_printer;
595
596     while (isspace(*pent)) pent++;
597     r = strchr(pent,':');
598     if (r)
599         name_len = r - pent;
600     else
601         name_len = strlen(pent);
602     name = HeapAlloc(GetProcessHeap(), 0, name_len + 1);
603     memcpy(name, pent, name_len);
604     name[name_len] = '\0';
605     if (r)
606         pent = r;
607     else
608         pent = "";
609
610     TRACE("name=%s entry=%s\n",name, pent);
611
612     if(ispunct(*name)) { /* a tc entry, not a real printer */
613         TRACE("skipping tc entry\n");
614         goto end;
615     }
616
617     if(strstr(pent,":server")) { /* server only version so skip */
618         TRACE("skipping server entry\n");
619         goto end;
620     }
621
622     /* Determine whether this is a postscript printer. */
623
624     ret = TRUE;
625     env_default = getenv("PRINTER");
626     prettyname = name;
627     /* Get longest name, usually the one at the right for later display. */
628     while((s=strchr(prettyname,'|'))) {
629         *s = '\0';
630         e = s;
631         while(isspace(*--e)) *e = '\0';
632         TRACE("\t%s\n", debugstr_a(prettyname));
633         if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
634         for(prettyname = s+1; isspace(*prettyname); prettyname++)
635             ;
636     }
637     e = prettyname + strlen(prettyname);
638     while(isspace(*--e)) *e = '\0';
639     TRACE("\t%s\n", debugstr_a(prettyname));
640     if(env_default && !strcasecmp(prettyname, env_default)) set_default = TRUE;
641
642     /* prettyname must fit into the dmDeviceName member of DEVMODE struct,
643      * if it is too long, we use it as comment below. */
644     devname = prettyname;
645     if (strlen(devname)>=CCHDEVICENAME-1)
646          devname = name;
647     if (strlen(devname)>=CCHDEVICENAME-1) {
648         ret = FALSE;
649         goto end;
650     }
651
652     port = HeapAlloc(GetProcessHeap(),0,strlen("LPR:")+strlen(name)+1);
653     sprintf(port,"LPR:%s",name);
654
655     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
656        ERROR_SUCCESS) {
657         ERR("Can't create Printers key\n");
658         ret = FALSE;
659         goto end;
660     }
661
662     MultiByteToWideChar(CP_ACP, 0, devname, -1, devnameW, sizeof(devnameW) / sizeof(WCHAR));
663
664     if(RegOpenKeyA(hkeyPrinters, devname, &hkeyPrinter) == ERROR_SUCCESS) {
665         /* Printer already in registry, delete the tag added in WINSPOOL_LoadSystemPrinters
666            and continue */
667         TRACE("Printer already exists\n");
668         RegDeleteValueW(hkeyPrinter, May_Delete_Value);
669         RegCloseKey(hkeyPrinter);
670     } else {
671         static CHAR data_type[]   = "RAW",
672                     print_proc[]  = "WinPrint",
673                     comment[]     = "WINEPS Printer using LPR",
674                     params[]      = "<parameters?>",
675                     share_name[]  = "<share name?>",
676                     sep_file[]    = "<sep file?>";
677
678         add_printer_driver(devnameW);
679
680         memset(&pinfo2a,0,sizeof(pinfo2a));
681         pinfo2a.pPrinterName    = devname;
682         pinfo2a.pDatatype       = data_type;
683         pinfo2a.pPrintProcessor = print_proc;
684         pinfo2a.pDriverName     = devname;
685         pinfo2a.pComment        = comment;
686         pinfo2a.pLocation       = prettyname;
687         pinfo2a.pPortName       = port;
688         pinfo2a.pParameters     = params;
689         pinfo2a.pShareName      = share_name;
690         pinfo2a.pSepFile        = sep_file;
691
692         added_printer = AddPrinterA( NULL, 2, (LPBYTE)&pinfo2a );
693         if (added_printer) ClosePrinter( added_printer );
694         else if (GetLastError() != ERROR_PRINTER_ALREADY_EXISTS)
695             ERR( "printer '%s' not added by AddPrinter (error %d)\n", debugstr_a(name), GetLastError() );
696     }
697     RegCloseKey(hkeyPrinters);
698
699     if (isfirst || set_default)
700         WINSPOOL_SetDefaultPrinter(devname,name,TRUE);
701
702  end:
703     HeapFree(GetProcessHeap(), 0, port);
704     HeapFree(GetProcessHeap(), 0, name);
705     return ret;
706 }
707
708 static BOOL
709 PRINTCAP_LoadPrinters(void) {
710     BOOL                hadprinter = FALSE;
711     char                buf[200];
712     FILE                *f;
713     char *pent = NULL;
714     BOOL had_bash = FALSE;
715
716     f = fopen("/etc/printcap","r");
717     if (!f)
718         return FALSE;
719
720     while(fgets(buf,sizeof(buf),f)) {
721         char *start, *end;
722
723         end=strchr(buf,'\n');
724         if (end) *end='\0';
725     
726         start = buf;
727         while(isspace(*start)) start++;
728         if(*start == '#' || *start == '\0')
729             continue;
730
731         if(pent && !had_bash && *start != ':' && *start != '|') { /* start of new entry, parse the previous one */
732             hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
733             HeapFree(GetProcessHeap(),0,pent);
734             pent = NULL;
735         }
736
737         if (end && *--end == '\\') {
738             *end = '\0';
739             had_bash = TRUE;
740         } else
741             had_bash = FALSE;
742
743         if (pent) {
744             pent=HeapReAlloc(GetProcessHeap(),0,pent,strlen(pent)+strlen(start)+1);
745             strcat(pent,start);
746         } else {
747             pent=HeapAlloc(GetProcessHeap(),0,strlen(start)+1);
748             strcpy(pent,start);
749         }
750
751     }
752     if(pent) {
753         hadprinter |= PRINTCAP_ParseEntry(pent,!hadprinter);
754         HeapFree(GetProcessHeap(),0,pent);
755     }
756     fclose(f);
757     return hadprinter;
758 }
759
760 static inline DWORD set_reg_DWORD(HKEY hkey, const WCHAR *keyname, const DWORD value)
761 {
762     return RegSetValueExW(hkey, keyname, 0, REG_DWORD, (const BYTE*)&value, sizeof(value));
763 }
764
765 static inline DWORD set_reg_szW(HKEY hkey, const WCHAR *keyname, const WCHAR *value)
766 {
767     if (value)
768         return RegSetValueExW(hkey, keyname, 0, REG_SZ, (const BYTE*)value,
769                               (lstrlenW(value) + 1) * sizeof(WCHAR));
770     else
771         return ERROR_FILE_NOT_FOUND;
772 }
773
774 static inline DWORD set_reg_devmode( HKEY key, const WCHAR *name, const DEVMODEW *dm )
775 {
776     DEVMODEA *dmA = DEVMODEdupWtoA( dm );
777     DWORD ret = ERROR_FILE_NOT_FOUND;
778
779     /* FIXME: Write DEVMODEA not DEVMODEW into reg.  This is what win9x does
780        and we support these drivers.  NT writes DEVMODEW so somehow
781        we'll need to distinguish between these when we support NT
782        drivers */
783
784     if (dmA)
785     {
786         ret = RegSetValueExW( key, name, 0, REG_BINARY,
787                               (LPBYTE)dmA, dmA->dmSize + dmA->dmDriverExtra );
788         HeapFree( GetProcessHeap(), 0, dmA );
789     }
790
791     return ret;
792 }
793
794 /******************************************************************
795  * get_servername_from_name  (internal)
796  *
797  * for an external server, a copy of the serverpart from the full name is returned
798  *
799  */
800 static LPWSTR get_servername_from_name(LPCWSTR name)
801 {
802     LPWSTR  server;
803     LPWSTR  ptr;
804     WCHAR   buffer[MAX_PATH];
805     DWORD   len;
806
807     if (name == NULL) return NULL;
808     if ((name[0] != '\\') || (name[1] != '\\')) return NULL;
809
810     server = strdupW(&name[2]);     /* skip over both backslash */
811     if (server == NULL) return NULL;
812
813     /* strip '\' and the printername */
814     ptr = strchrW(server, '\\');
815     if (ptr) ptr[0] = '\0';
816
817     TRACE("found %s\n", debugstr_w(server));
818
819     len = sizeof(buffer)/sizeof(buffer[0]);
820     if (GetComputerNameW(buffer, &len)) {
821         if (lstrcmpW(buffer, server) == 0) {
822             /* The requested Servername is our computername */
823             HeapFree(GetProcessHeap(), 0, server);
824             return NULL;
825         }
826     }
827     return server;
828 }
829
830 /******************************************************************
831  * get_basename_from_name  (internal)
832  *
833  * skip over the serverpart from the full name
834  *
835  */
836 static LPCWSTR get_basename_from_name(LPCWSTR name)
837 {
838     if (name == NULL)  return NULL;
839     if ((name[0] == '\\') && (name[1] == '\\')) {
840         /* skip over the servername and search for the following '\'  */
841         name = strchrW(&name[2], '\\');
842         if ((name) && (name[1])) {
843             /* found a separator ('\') followed by a name:
844                skip over the separator and return the rest */
845             name++;
846         }
847         else
848         {
849             /* no basename present (we found only a servername) */
850             return NULL;
851         }
852     }
853     return name;
854 }
855
856 static void free_printer_entry( opened_printer_t *printer )
857 {
858     /* the queue is shared, so don't free that here */
859     HeapFree( GetProcessHeap(), 0, printer->printername );
860     HeapFree( GetProcessHeap(), 0, printer->name );
861     HeapFree( GetProcessHeap(), 0, printer->devmode );
862     HeapFree( GetProcessHeap(), 0, printer );
863 }
864
865 /******************************************************************
866  *  get_opened_printer_entry
867  *  Get the first place empty in the opened printer table
868  *
869  * ToDo:
870  *  - pDefault is ignored
871  */
872 static HANDLE get_opened_printer_entry(LPWSTR name, LPPRINTER_DEFAULTSW pDefault)
873 {
874     UINT_PTR handle = nb_printer_handles, i;
875     jobqueue_t *queue = NULL;
876     opened_printer_t *printer = NULL;
877     LPWSTR  servername;
878     LPCWSTR printername;
879
880     if ((backend == NULL)  && !load_backend()) return NULL;
881
882     servername = get_servername_from_name(name);
883     if (servername) {
884         FIXME("server %s not supported\n", debugstr_w(servername));
885         HeapFree(GetProcessHeap(), 0, servername);
886         SetLastError(ERROR_INVALID_PRINTER_NAME);
887         return NULL;
888     }
889
890     printername = get_basename_from_name(name);
891     if (name != printername) TRACE("converted %s to %s\n", debugstr_w(name), debugstr_w(printername));
892
893     /* an empty printername is invalid */
894     if (printername && (!printername[0])) {
895         SetLastError(ERROR_INVALID_PARAMETER);
896         return NULL;
897     }
898
899     EnterCriticalSection(&printer_handles_cs);
900
901     for (i = 0; i < nb_printer_handles; i++)
902     {
903         if (!printer_handles[i])
904         {
905             if(handle == nb_printer_handles)
906                 handle = i;
907         }
908         else
909         {
910             if(!queue && (name) && !lstrcmpW(name, printer_handles[i]->name))
911                 queue = printer_handles[i]->queue;
912         }
913     }
914
915     if (handle >= nb_printer_handles)
916     {
917         opened_printer_t **new_array;
918         if (printer_handles)
919             new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, printer_handles,
920                                      (nb_printer_handles + 16) * sizeof(*new_array) );
921         else
922             new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
923                                    (nb_printer_handles + 16) * sizeof(*new_array) );
924
925         if (!new_array)
926         {
927             handle = 0;
928             goto end;
929         }
930         printer_handles = new_array;
931         nb_printer_handles += 16;
932     }
933
934     if (!(printer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*printer))))
935     {
936         handle = 0;
937         goto end;
938     }
939
940     /* get a printer handle from the backend */
941     if (! backend->fpOpenPrinter(name, &printer->backend_printer, pDefault)) {
942         handle = 0;
943         goto end;
944     }
945
946     /* clone the base name. This is NULL for the printserver */
947     printer->printername = strdupW(printername);
948
949     /* clone the full name */
950     printer->name = strdupW(name);
951     if (name && (!printer->name)) {
952         handle = 0;
953         goto end;
954     }
955
956     if (pDefault && pDefault->pDevMode)
957         printer->devmode = dup_devmode( pDefault->pDevMode );
958
959     if(queue)
960         printer->queue = queue;
961     else
962     {
963         printer->queue = HeapAlloc(GetProcessHeap(), 0, sizeof(*queue));
964         if (!printer->queue) {
965             handle = 0;
966             goto end;
967         }
968         list_init(&printer->queue->jobs);
969         printer->queue->ref = 0;
970     }
971     InterlockedIncrement(&printer->queue->ref);
972
973     printer_handles[handle] = printer;
974     handle++;
975 end:
976     LeaveCriticalSection(&printer_handles_cs);
977     if (!handle && printer) {
978         if (!queue) HeapFree(GetProcessHeap(), 0, printer->queue);
979         free_printer_entry( printer );
980     }
981
982     return (HANDLE)handle;
983 }
984
985 /******************************************************************
986  *  get_opened_printer
987  *  Get the pointer to the opened printer referred by the handle
988  */
989 static opened_printer_t *get_opened_printer(HANDLE hprn)
990 {
991     UINT_PTR idx = (UINT_PTR)hprn;
992     opened_printer_t *ret = NULL;
993
994     EnterCriticalSection(&printer_handles_cs);
995
996     if ((idx > 0) && (idx <= nb_printer_handles)) {
997         ret = printer_handles[idx - 1];
998     }
999     LeaveCriticalSection(&printer_handles_cs);
1000     return ret;
1001 }
1002
1003 /******************************************************************
1004  *  get_opened_printer_name
1005  *  Get the pointer to the opened printer name referred by the handle
1006  */
1007 static LPCWSTR get_opened_printer_name(HANDLE hprn)
1008 {
1009     opened_printer_t *printer = get_opened_printer(hprn);
1010     if(!printer) return NULL;
1011     return printer->name;
1012 }
1013
1014 static DWORD open_printer_reg_key( const WCHAR *name, HKEY *key )
1015 {
1016     HKEY printers;
1017     DWORD err;
1018
1019     *key = NULL;
1020     err = RegCreateKeyW( HKEY_LOCAL_MACHINE, PrintersW, &printers );
1021     if (err) return err;
1022
1023     err = RegOpenKeyW( printers, name, key );
1024     if (err) err = ERROR_INVALID_PRINTER_NAME;
1025     RegCloseKey( printers );
1026     return err;
1027 }
1028
1029 /******************************************************************
1030  *  WINSPOOL_GetOpenedPrinterRegKey
1031  *
1032  */
1033 static DWORD WINSPOOL_GetOpenedPrinterRegKey(HANDLE hPrinter, HKEY *phkey)
1034 {
1035     LPCWSTR name = get_opened_printer_name(hPrinter);
1036
1037     if(!name) return ERROR_INVALID_HANDLE;
1038     return open_printer_reg_key( name, phkey );
1039 }
1040
1041 static void old_printer_check( BOOL delete_phase )
1042 {
1043     PRINTER_INFO_5W* pi;
1044     DWORD needed, type, num, delete, i, size;
1045     const DWORD one = 1;
1046     HKEY key;
1047     HANDLE hprn;
1048
1049     EnumPrintersW( PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &needed, &num );
1050     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return;
1051
1052     pi = HeapAlloc( GetProcessHeap(), 0, needed );
1053     EnumPrintersW( PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)pi, needed, &needed, &num );
1054     for (i = 0; i < num; i++)
1055     {
1056         if (strncmpW( pi[i].pPortName, CUPS_Port, strlenW(CUPS_Port) ) &&
1057             strncmpW( pi[i].pPortName, LPR_Port, strlenW(LPR_Port) ))
1058             continue;
1059
1060         if (open_printer_reg_key( pi[i].pPrinterName, &key )) continue;
1061
1062         if (!delete_phase)
1063         {
1064             RegSetValueExW( key, May_Delete_Value, 0, REG_DWORD, (LPBYTE)&one, sizeof(one) );
1065             RegCloseKey( key );
1066         }
1067         else
1068         {
1069             delete = 0;
1070             size = sizeof( delete );
1071             RegQueryValueExW( key, May_Delete_Value, NULL, &type, (LPBYTE)&delete, &size );
1072             RegCloseKey( key );
1073             if (delete)
1074             {
1075                 TRACE( "Deleting old printer %s\n", debugstr_w(pi[i].pPrinterName) );
1076                 if (OpenPrinterW( pi[i].pPrinterName, &hprn, NULL ))
1077                 {
1078                     DeletePrinter( hprn );
1079                     ClosePrinter( hprn );
1080                 }
1081                 DeletePrinterDriverExW( NULL, NULL, pi[i].pPrinterName, 0, 0 );
1082             }
1083         }
1084     }
1085     HeapFree(GetProcessHeap(), 0, pi);
1086 }
1087
1088 static const WCHAR winspool_mutex_name[] = {'_','_','W','I','N','E','_','W','I','N','S','P','O','O','L','_',
1089                                             'M','U','T','E','X','_','_','\0'};
1090
1091 void WINSPOOL_LoadSystemPrinters(void)
1092 {
1093     HKEY                hkey, hkeyPrinters;
1094     DWORD               needed, num, i;
1095     WCHAR               PrinterName[256];
1096     BOOL                done = FALSE;
1097     HANDLE              mutex;
1098
1099     /* FIXME: The init code should be moved to spoolsv.exe */
1100     mutex = CreateMutexW( NULL, TRUE, winspool_mutex_name );
1101     if (!mutex)
1102     {
1103         ERR( "Failed to create mutex\n" );
1104         return;
1105     }
1106     if (GetLastError() == ERROR_ALREADY_EXISTS)
1107     {
1108         WaitForSingleObject( mutex, INFINITE );
1109         ReleaseMutex( mutex );
1110         TRACE( "Init already done\n" );
1111         return;
1112     }
1113
1114     /* This ensures that all printer entries have a valid Name value.  If causes
1115        problems later if they don't.  If one is found to be missed we create one
1116        and set it equal to the name of the key */
1117     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
1118         if(RegQueryInfoKeyW(hkeyPrinters, NULL, NULL, NULL, &num, NULL, NULL,
1119                             NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
1120             for(i = 0; i < num; i++) {
1121                 if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) == ERROR_SUCCESS) {
1122                     if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkey) == ERROR_SUCCESS) {
1123                         if(RegQueryValueExW(hkey, NameW, 0, 0, 0, &needed) == ERROR_FILE_NOT_FOUND) {
1124                             set_reg_szW(hkey, NameW, PrinterName);
1125                         }
1126                         RegCloseKey(hkey);
1127                     }
1128                 }
1129             }
1130         }
1131         RegCloseKey(hkeyPrinters);
1132     }
1133
1134     old_printer_check( FALSE );
1135
1136 #ifdef SONAME_LIBCUPS
1137     done = CUPS_LoadPrinters();
1138 #endif
1139
1140     if(!done) /* If we have any CUPS based printers, skip looking for printcap printers */
1141         PRINTCAP_LoadPrinters();
1142
1143     old_printer_check( TRUE );
1144
1145     ReleaseMutex( mutex );
1146     return;
1147 }
1148
1149 /******************************************************************
1150  *                  get_job
1151  *
1152  *  Get the pointer to the specified job.
1153  *  Should hold the printer_handles_cs before calling.
1154  */
1155 static job_t *get_job(HANDLE hprn, DWORD JobId)
1156 {
1157     opened_printer_t *printer = get_opened_printer(hprn);
1158     job_t *job;
1159
1160     if(!printer) return NULL;
1161     LIST_FOR_EACH_ENTRY(job, &printer->queue->jobs, job_t, entry)
1162     {
1163         if(job->job_id == JobId)
1164             return job;
1165     }
1166     return NULL;
1167 }
1168
1169 /***********************************************************
1170  *      DEVMODEcpyAtoW
1171  */
1172 static LPDEVMODEW DEVMODEcpyAtoW(DEVMODEW *dmW, const DEVMODEA *dmA)
1173 {
1174     BOOL Formname;
1175     ptrdiff_t off_formname = (const char *)dmA->dmFormName - (const char *)dmA;
1176     DWORD size;
1177
1178     Formname = (dmA->dmSize > off_formname);
1179     size = dmA->dmSize + CCHDEVICENAME + (Formname ? CCHFORMNAME : 0);
1180     MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmDeviceName, -1,
1181                         dmW->dmDeviceName, CCHDEVICENAME);
1182     if(!Formname) {
1183       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1184              dmA->dmSize - CCHDEVICENAME);
1185     } else {
1186       memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion,
1187              off_formname - CCHDEVICENAME);
1188       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)dmA->dmFormName, -1,
1189                           dmW->dmFormName, CCHFORMNAME);
1190       memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA->dmSize -
1191              (off_formname + CCHFORMNAME));
1192     }
1193     dmW->dmSize = size;
1194     memcpy((char *)dmW + dmW->dmSize, (const char *)dmA + dmA->dmSize,
1195            dmA->dmDriverExtra);
1196     return dmW;
1197 }
1198
1199 /******************************************************************
1200  * convert_printerinfo_W_to_A [internal]
1201  *
1202  */
1203 static void convert_printerinfo_W_to_A(LPBYTE out, LPBYTE pPrintersW,
1204                                        DWORD level, DWORD outlen, DWORD numentries)
1205 {
1206     DWORD id = 0;
1207     LPSTR ptr;
1208     INT len;
1209
1210     TRACE("(%p, %p, %d, %u, %u)\n", out, pPrintersW, level, outlen, numentries);
1211
1212     len = pi_sizeof[level] * numentries;
1213     ptr = (LPSTR) out + len;
1214     outlen -= len;
1215
1216     /* copy the numbers of all PRINTER_INFO_* first */
1217     memcpy(out, pPrintersW, len);
1218
1219     while (id < numentries) {
1220         switch (level) {
1221             case 1:
1222                 {
1223                     PRINTER_INFO_1W * piW = (PRINTER_INFO_1W *) pPrintersW;
1224                     PRINTER_INFO_1A * piA = (PRINTER_INFO_1A *) out;
1225
1226                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pName));
1227                     if (piW->pDescription) {
1228                         piA->pDescription = ptr;
1229                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDescription, -1,
1230                                                   ptr, outlen, NULL, NULL);
1231                         ptr += len;
1232                         outlen -= len;
1233                     }
1234                     if (piW->pName) {
1235                         piA->pName = ptr;
1236                         len = WideCharToMultiByte(CP_ACP, 0, piW->pName, -1,
1237                                                   ptr, outlen, NULL, NULL);
1238                         ptr += len;
1239                         outlen -= len;
1240                     }
1241                     if (piW->pComment) {
1242                         piA->pComment = ptr;
1243                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1244                                                   ptr, outlen, NULL, NULL);
1245                         ptr += len;
1246                         outlen -= len;
1247                     }
1248                     break;
1249                 }
1250
1251             case 2:
1252                 {
1253                     PRINTER_INFO_2W * piW = (PRINTER_INFO_2W *) pPrintersW;
1254                     PRINTER_INFO_2A * piA = (PRINTER_INFO_2A *) out;
1255                     LPDEVMODEA dmA;
1256
1257                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1258                     if (piW->pServerName) {
1259                         piA->pServerName = ptr;
1260                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1261                                                   ptr, outlen, NULL, NULL);
1262                         ptr += len;
1263                         outlen -= len;
1264                     }
1265                     if (piW->pPrinterName) {
1266                         piA->pPrinterName = ptr;
1267                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1268                                                   ptr, outlen, NULL, NULL);
1269                         ptr += len;
1270                         outlen -= len;
1271                     }
1272                     if (piW->pShareName) {
1273                         piA->pShareName = ptr;
1274                         len = WideCharToMultiByte(CP_ACP, 0, piW->pShareName, -1,
1275                                                   ptr, outlen, NULL, NULL);
1276                         ptr += len;
1277                         outlen -= len;
1278                     }
1279                     if (piW->pPortName) {
1280                         piA->pPortName = ptr;
1281                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1282                                                   ptr, outlen, NULL, NULL);
1283                         ptr += len;
1284                         outlen -= len;
1285                     }
1286                     if (piW->pDriverName) {
1287                         piA->pDriverName = ptr;
1288                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDriverName, -1,
1289                                                   ptr, outlen, NULL, NULL);
1290                         ptr += len;
1291                         outlen -= len;
1292                     }
1293                     if (piW->pComment) {
1294                         piA->pComment = ptr;
1295                         len = WideCharToMultiByte(CP_ACP, 0, piW->pComment, -1,
1296                                                   ptr, outlen, NULL, NULL);
1297                         ptr += len;
1298                         outlen -= len;
1299                     }
1300                     if (piW->pLocation) {
1301                         piA->pLocation = ptr;
1302                         len = WideCharToMultiByte(CP_ACP, 0, piW->pLocation, -1,
1303                                                   ptr, outlen, NULL, NULL);
1304                         ptr += len;
1305                         outlen -= len;
1306                     }
1307
1308                     dmA = DEVMODEdupWtoA(piW->pDevMode);
1309                     if (dmA) {
1310                         /* align DEVMODEA to a DWORD boundary */
1311                         len = (4 - ( (DWORD_PTR) ptr & 3)) & 3;
1312                         ptr += len;
1313                         outlen -= len;
1314
1315                         piA->pDevMode = (LPDEVMODEA) ptr;
1316                         len = dmA->dmSize + dmA->dmDriverExtra;
1317                         memcpy(ptr, dmA, len);
1318                         HeapFree(GetProcessHeap(), 0, dmA);
1319
1320                         ptr += len;
1321                         outlen -= len;
1322                     }
1323
1324                     if (piW->pSepFile) {
1325                         piA->pSepFile = ptr;
1326                         len = WideCharToMultiByte(CP_ACP, 0, piW->pSepFile, -1,
1327                                                   ptr, outlen, NULL, NULL);
1328                         ptr += len;
1329                         outlen -= len;
1330                     }
1331                     if (piW->pPrintProcessor) {
1332                         piA->pPrintProcessor = ptr;
1333                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrintProcessor, -1,
1334                                                   ptr, outlen, NULL, NULL);
1335                         ptr += len;
1336                         outlen -= len;
1337                     }
1338                     if (piW->pDatatype) {
1339                         piA->pDatatype = ptr;
1340                         len = WideCharToMultiByte(CP_ACP, 0, piW->pDatatype, -1,
1341                                                   ptr, outlen, NULL, NULL);
1342                         ptr += len;
1343                         outlen -= len;
1344                     }
1345                     if (piW->pParameters) {
1346                         piA->pParameters = ptr;
1347                         len = WideCharToMultiByte(CP_ACP, 0, piW->pParameters, -1,
1348                                                   ptr, outlen, NULL, NULL);
1349                         ptr += len;
1350                         outlen -= len;
1351                     }
1352                     if (piW->pSecurityDescriptor) {
1353                         piA->pSecurityDescriptor = NULL;
1354                         FIXME("pSecurityDescriptor ignored: %s\n", debugstr_w(piW->pPrinterName));
1355                     }
1356                     break;
1357                 }
1358
1359             case 4:
1360                 {
1361                     PRINTER_INFO_4W * piW = (PRINTER_INFO_4W *) pPrintersW;
1362                     PRINTER_INFO_4A * piA = (PRINTER_INFO_4A *) out;
1363
1364                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1365
1366                     if (piW->pPrinterName) {
1367                         piA->pPrinterName = ptr;
1368                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1369                                                   ptr, outlen, NULL, NULL);
1370                         ptr += len;
1371                         outlen -= len;
1372                     }
1373                     if (piW->pServerName) {
1374                         piA->pServerName = ptr;
1375                         len = WideCharToMultiByte(CP_ACP, 0, piW->pServerName, -1,
1376                                                   ptr, outlen, NULL, NULL);
1377                         ptr += len;
1378                         outlen -= len;
1379                     }
1380                     break;
1381                 }
1382
1383             case 5:
1384                 {
1385                     PRINTER_INFO_5W * piW = (PRINTER_INFO_5W *) pPrintersW;
1386                     PRINTER_INFO_5A * piA = (PRINTER_INFO_5A *) out;
1387
1388                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(piW->pPrinterName));
1389
1390                     if (piW->pPrinterName) {
1391                         piA->pPrinterName = ptr;
1392                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPrinterName, -1,
1393                                                   ptr, outlen, NULL, NULL);
1394                         ptr += len;
1395                         outlen -= len;
1396                     }
1397                     if (piW->pPortName) {
1398                         piA->pPortName = ptr;
1399                         len = WideCharToMultiByte(CP_ACP, 0, piW->pPortName, -1,
1400                                                   ptr, outlen, NULL, NULL);
1401                         ptr += len;
1402                         outlen -= len;
1403                     }
1404                     break;
1405                 }
1406
1407             case 6:  /* 6A and 6W are the same structure */
1408                 break;
1409
1410             case 7:
1411                 {
1412                     PRINTER_INFO_7W * piW = (PRINTER_INFO_7W *) pPrintersW;
1413                     PRINTER_INFO_7A * piA = (PRINTER_INFO_7A *) out;
1414
1415                     TRACE("(%u) #%u\n", level, id);
1416                     if (piW->pszObjectGUID) {
1417                         piA->pszObjectGUID = ptr;
1418                         len = WideCharToMultiByte(CP_ACP, 0, piW->pszObjectGUID, -1,
1419                                                   ptr, outlen, NULL, NULL);
1420                         ptr += len;
1421                         outlen -= len;
1422                     }
1423                     break;
1424                 }
1425
1426             case 8:
1427             case 9:
1428                 {
1429                     PRINTER_INFO_9W * piW = (PRINTER_INFO_9W *) pPrintersW;
1430                     PRINTER_INFO_9A * piA = (PRINTER_INFO_9A *) out;
1431                     LPDEVMODEA dmA;
1432
1433                     TRACE("(%u) #%u\n", level, id);
1434                     dmA = DEVMODEdupWtoA(piW->pDevMode);
1435                     if (dmA) {
1436                         /* align DEVMODEA to a DWORD boundary */
1437                         len = (4 - ( (DWORD_PTR) ptr & 3)) & 3;
1438                         ptr += len;
1439                         outlen -= len;
1440
1441                         piA->pDevMode = (LPDEVMODEA) ptr;
1442                         len = dmA->dmSize + dmA->dmDriverExtra;
1443                         memcpy(ptr, dmA, len);
1444                         HeapFree(GetProcessHeap(), 0, dmA);
1445
1446                         ptr += len;
1447                         outlen -= len;
1448                     }
1449
1450                     break;
1451                 }
1452
1453             default:
1454                 FIXME("for level %u\n", level);
1455         }
1456         pPrintersW += pi_sizeof[level];
1457         out += pi_sizeof[level];
1458         id++;
1459     }
1460 }
1461
1462 /******************************************************************
1463  * convert_driverinfo_W_to_A [internal]
1464  *
1465  */
1466 static void convert_driverinfo_W_to_A(LPBYTE out, LPBYTE pDriversW,
1467                                        DWORD level, DWORD outlen, DWORD numentries)
1468 {
1469     DWORD id = 0;
1470     LPSTR ptr;
1471     INT len;
1472
1473     TRACE("(%p, %p, %d, %u, %u)\n", out, pDriversW, level, outlen, numentries);
1474
1475     len = di_sizeof[level] * numentries;
1476     ptr = (LPSTR) out + len;
1477     outlen -= len;
1478
1479     /* copy the numbers of all PRINTER_INFO_* first */
1480     memcpy(out, pDriversW, len);
1481
1482 #define COPY_STRING(fld) \
1483                     { if (diW->fld){ \
1484                         diA->fld = ptr; \
1485                         len = WideCharToMultiByte(CP_ACP, 0, diW->fld, -1, ptr, outlen, NULL, NULL);\
1486                         ptr += len; outlen -= len;\
1487                     }}
1488 #define COPY_MULTIZ_STRING(fld) \
1489                     { LPWSTR p = diW->fld; if (p){ \
1490                         diA->fld = ptr; \
1491                         do {\
1492                         len = WideCharToMultiByte(CP_ACP, 0, p, -1, ptr, outlen, NULL, NULL);\
1493                         ptr += len; outlen -= len; p += len;\
1494                         }\
1495                         while(len > 1 && outlen > 0); \
1496                     }}
1497
1498     while (id < numentries)
1499     {
1500         switch (level)
1501         {
1502             case 1:
1503                 {
1504                     DRIVER_INFO_1W * diW = (DRIVER_INFO_1W *) pDriversW;
1505                     DRIVER_INFO_1A * diA = (DRIVER_INFO_1A *) out;
1506
1507                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(diW->pName));
1508
1509                     COPY_STRING(pName);
1510                     break;
1511                 }
1512             case 2:
1513                 {
1514                     DRIVER_INFO_2W * diW = (DRIVER_INFO_2W *) pDriversW;
1515                     DRIVER_INFO_2A * diA = (DRIVER_INFO_2A *) out;
1516
1517                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(diW->pName));
1518
1519                     COPY_STRING(pName);
1520                     COPY_STRING(pEnvironment);
1521                     COPY_STRING(pDriverPath);
1522                     COPY_STRING(pDataFile);
1523                     COPY_STRING(pConfigFile);
1524                     break;
1525                 }
1526             case 3:
1527                 {
1528                     DRIVER_INFO_3W * diW = (DRIVER_INFO_3W *) pDriversW;
1529                     DRIVER_INFO_3A * diA = (DRIVER_INFO_3A *) out;
1530
1531                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(diW->pName));
1532
1533                     COPY_STRING(pName);
1534                     COPY_STRING(pEnvironment);
1535                     COPY_STRING(pDriverPath);
1536                     COPY_STRING(pDataFile);
1537                     COPY_STRING(pConfigFile);
1538                     COPY_STRING(pHelpFile);
1539                     COPY_MULTIZ_STRING(pDependentFiles);
1540                     COPY_STRING(pMonitorName);
1541                     COPY_STRING(pDefaultDataType);
1542                     break;
1543                 }
1544             case 4:
1545                 {
1546                     DRIVER_INFO_4W * diW = (DRIVER_INFO_4W *) pDriversW;
1547                     DRIVER_INFO_4A * diA = (DRIVER_INFO_4A *) out;
1548
1549                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(diW->pName));
1550
1551                     COPY_STRING(pName);
1552                     COPY_STRING(pEnvironment);
1553                     COPY_STRING(pDriverPath);
1554                     COPY_STRING(pDataFile);
1555                     COPY_STRING(pConfigFile);
1556                     COPY_STRING(pHelpFile);
1557                     COPY_MULTIZ_STRING(pDependentFiles);
1558                     COPY_STRING(pMonitorName);
1559                     COPY_STRING(pDefaultDataType);
1560                     COPY_MULTIZ_STRING(pszzPreviousNames);
1561                     break;
1562                 }
1563             case 5:
1564                 {
1565                     DRIVER_INFO_5W * diW = (DRIVER_INFO_5W *) pDriversW;
1566                     DRIVER_INFO_5A * diA = (DRIVER_INFO_5A *) out;
1567
1568                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(diW->pName));
1569
1570                     COPY_STRING(pName);
1571                     COPY_STRING(pEnvironment);
1572                     COPY_STRING(pDriverPath);
1573                     COPY_STRING(pDataFile);
1574                     COPY_STRING(pConfigFile);
1575                     break;
1576                 }
1577             case 6:
1578                 {
1579                     DRIVER_INFO_6W * diW = (DRIVER_INFO_6W *) pDriversW;
1580                     DRIVER_INFO_6A * diA = (DRIVER_INFO_6A *) out;
1581
1582                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(diW->pName));
1583
1584                     COPY_STRING(pName);
1585                     COPY_STRING(pEnvironment);
1586                     COPY_STRING(pDriverPath);
1587                     COPY_STRING(pDataFile);
1588                     COPY_STRING(pConfigFile);
1589                     COPY_STRING(pHelpFile);
1590                     COPY_MULTIZ_STRING(pDependentFiles);
1591                     COPY_STRING(pMonitorName);
1592                     COPY_STRING(pDefaultDataType);
1593                     COPY_MULTIZ_STRING(pszzPreviousNames);
1594                     COPY_STRING(pszMfgName);
1595                     COPY_STRING(pszOEMUrl);
1596                     COPY_STRING(pszHardwareID);
1597                     COPY_STRING(pszProvider);
1598                     break;
1599                 }
1600             case 8:
1601                 {
1602                     DRIVER_INFO_8W * diW = (DRIVER_INFO_8W *) pDriversW;
1603                     DRIVER_INFO_8A * diA = (DRIVER_INFO_8A *) out;
1604
1605                     TRACE("(%u) #%u: %s\n", level, id, debugstr_w(diW->pName));
1606
1607                     COPY_STRING(pName);
1608                     COPY_STRING(pEnvironment);
1609                     COPY_STRING(pDriverPath);
1610                     COPY_STRING(pDataFile);
1611                     COPY_STRING(pConfigFile);
1612                     COPY_STRING(pHelpFile);
1613                     COPY_MULTIZ_STRING(pDependentFiles);
1614                     COPY_STRING(pMonitorName);
1615                     COPY_STRING(pDefaultDataType);
1616                     COPY_MULTIZ_STRING(pszzPreviousNames);
1617                     COPY_STRING(pszMfgName);
1618                     COPY_STRING(pszOEMUrl);
1619                     COPY_STRING(pszHardwareID);
1620                     COPY_STRING(pszProvider);
1621                     COPY_STRING(pszPrintProcessor);
1622                     COPY_STRING(pszVendorSetup);
1623                     COPY_MULTIZ_STRING(pszzColorProfiles);
1624                     COPY_STRING(pszInfPath);
1625                     COPY_MULTIZ_STRING(pszzCoreDriverDependencies);
1626                     break;
1627                 }
1628
1629
1630             default:
1631                 FIXME("for level %u\n", level);
1632         }
1633
1634         pDriversW += di_sizeof[level];
1635         out += di_sizeof[level];
1636         id++;
1637
1638     }
1639 #undef COPY_STRING
1640 #undef COPY_MULTIZ_STRING
1641 }
1642
1643
1644 /***********************************************************
1645  *             printer_info_AtoW
1646  */
1647 static void *printer_info_AtoW( const void *data, DWORD level )
1648 {
1649     void *ret;
1650     UNICODE_STRING usBuffer;
1651
1652     if (!data) return NULL;
1653
1654     if (level < 1 || level > 9) return NULL;
1655
1656     ret = HeapAlloc( GetProcessHeap(), 0, pi_sizeof[level] );
1657     if (!ret) return NULL;
1658
1659     memcpy( ret, data, pi_sizeof[level] ); /* copy everything first */
1660
1661     switch (level)
1662     {
1663     case 2:
1664     {
1665         const PRINTER_INFO_2A *piA = (const PRINTER_INFO_2A *)data;
1666         PRINTER_INFO_2W *piW = (PRINTER_INFO_2W *)ret;
1667
1668         piW->pServerName = asciitounicode( &usBuffer, piA->pServerName );
1669         piW->pPrinterName = asciitounicode( &usBuffer, piA->pPrinterName );
1670         piW->pShareName = asciitounicode( &usBuffer, piA->pShareName );
1671         piW->pPortName = asciitounicode( &usBuffer, piA->pPortName );
1672         piW->pDriverName = asciitounicode( &usBuffer, piA->pDriverName );
1673         piW->pComment = asciitounicode( &usBuffer, piA->pComment );
1674         piW->pLocation = asciitounicode( &usBuffer, piA->pLocation );
1675         piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW( piA->pDevMode ) : NULL;
1676         piW->pSepFile = asciitounicode( &usBuffer, piA->pSepFile );
1677         piW->pPrintProcessor = asciitounicode( &usBuffer, piA->pPrintProcessor );
1678         piW->pDatatype = asciitounicode( &usBuffer, piA->pDatatype );
1679         piW->pParameters = asciitounicode( &usBuffer, piA->pParameters );
1680         break;
1681     }
1682
1683     case 8:
1684     case 9:
1685     {
1686         const PRINTER_INFO_9A *piA = (const PRINTER_INFO_9A *)data;
1687         PRINTER_INFO_9W *piW = (PRINTER_INFO_9W *)ret;
1688
1689         piW->pDevMode = piA->pDevMode ? GdiConvertToDevmodeW( piA->pDevMode ) : NULL;
1690         break;
1691     }
1692
1693     default:
1694         FIXME( "Unhandled level %d\n", level );
1695         HeapFree( GetProcessHeap(), 0, ret );
1696         return NULL;
1697     }
1698
1699     return ret;
1700 }
1701
1702 /***********************************************************
1703  *       free_printer_info
1704  */
1705 static void free_printer_info( void *data, DWORD level )
1706 {
1707     if (!data) return;
1708
1709     switch (level)
1710     {
1711     case 2:
1712     {
1713         PRINTER_INFO_2W *piW = (PRINTER_INFO_2W *)data;
1714
1715         HeapFree( GetProcessHeap(), 0, piW->pServerName );
1716         HeapFree( GetProcessHeap(), 0, piW->pPrinterName );
1717         HeapFree( GetProcessHeap(), 0, piW->pShareName );
1718         HeapFree( GetProcessHeap(), 0, piW->pPortName );
1719         HeapFree( GetProcessHeap(), 0, piW->pDriverName );
1720         HeapFree( GetProcessHeap(), 0, piW->pComment );
1721         HeapFree( GetProcessHeap(), 0, piW->pLocation );
1722         HeapFree( GetProcessHeap(), 0, piW->pDevMode );
1723         HeapFree( GetProcessHeap(), 0, piW->pSepFile );
1724         HeapFree( GetProcessHeap(), 0, piW->pPrintProcessor );
1725         HeapFree( GetProcessHeap(), 0, piW->pDatatype );
1726         HeapFree( GetProcessHeap(), 0, piW->pParameters );
1727         break;
1728     }
1729
1730     case 8:
1731     case 9:
1732     {
1733         PRINTER_INFO_9W *piW = (PRINTER_INFO_9W *)data;
1734
1735         HeapFree( GetProcessHeap(), 0, piW->pDevMode );
1736         break;
1737     }
1738
1739     default:
1740         FIXME( "Unhandled level %d\n", level );
1741     }
1742
1743     HeapFree( GetProcessHeap(), 0, data );
1744     return;
1745 }
1746
1747 /******************************************************************
1748  *              DeviceCapabilities     [WINSPOOL.@]
1749  *              DeviceCapabilitiesA    [WINSPOOL.@]
1750  *
1751  */
1752 INT WINAPI DeviceCapabilitiesA(LPCSTR pDevice,LPCSTR pPort, WORD cap,
1753                                LPSTR pOutput, LPDEVMODEA lpdm)
1754 {
1755     INT ret;
1756
1757     if (!GDI_CallDeviceCapabilities16)
1758     {
1759         GDI_CallDeviceCapabilities16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1760                                                               (LPCSTR)104 );
1761         if (!GDI_CallDeviceCapabilities16) return -1;
1762     }
1763     ret = GDI_CallDeviceCapabilities16(pDevice, pPort, cap, pOutput, lpdm);
1764
1765     /* If DC_PAPERSIZE map POINT16s to POINTs */
1766     if(ret != -1 && cap == DC_PAPERSIZE && pOutput) {
1767         POINT16 *tmp = HeapAlloc( GetProcessHeap(), 0, ret * sizeof(POINT16) );
1768         POINT *pt = (POINT *)pOutput;
1769         INT i;
1770         memcpy(tmp, pOutput, ret * sizeof(POINT16));
1771         for(i = 0; i < ret; i++, pt++)
1772         {
1773             pt->x = tmp[i].x;
1774             pt->y = tmp[i].y;
1775         }
1776         HeapFree( GetProcessHeap(), 0, tmp );
1777     }
1778     return ret;
1779 }
1780
1781
1782 /*****************************************************************************
1783  *          DeviceCapabilitiesW        [WINSPOOL.@]
1784  *
1785  * Call DeviceCapabilitiesA since we later call 16bit stuff anyway
1786  *
1787  */
1788 INT WINAPI DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort,
1789                                WORD fwCapability, LPWSTR pOutput,
1790                                const DEVMODEW *pDevMode)
1791 {
1792     LPDEVMODEA dmA = DEVMODEdupWtoA(pDevMode);
1793     LPSTR pDeviceA = strdupWtoA(pDevice);
1794     LPSTR pPortA = strdupWtoA(pPort);
1795     INT ret;
1796
1797     if(pOutput && (fwCapability == DC_BINNAMES ||
1798                    fwCapability == DC_FILEDEPENDENCIES ||
1799                    fwCapability == DC_PAPERNAMES)) {
1800       /* These need A -> W translation */
1801         INT size = 0, i;
1802         LPSTR pOutputA;
1803         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, NULL,
1804                                   dmA);
1805         if(ret == -1)
1806             return ret;
1807         switch(fwCapability) {
1808         case DC_BINNAMES:
1809             size = 24;
1810             break;
1811         case DC_PAPERNAMES:
1812         case DC_FILEDEPENDENCIES:
1813             size = 64;
1814             break;
1815         }
1816         pOutputA = HeapAlloc(GetProcessHeap(), 0, size * ret);
1817         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability, pOutputA,
1818                                   dmA);
1819         for(i = 0; i < ret; i++)
1820             MultiByteToWideChar(CP_ACP, 0, pOutputA + (i * size), -1,
1821                                 pOutput + (i * size), size);
1822         HeapFree(GetProcessHeap(), 0, pOutputA);
1823     } else {
1824         ret = DeviceCapabilitiesA(pDeviceA, pPortA, fwCapability,
1825                                   (LPSTR)pOutput, dmA);
1826     }
1827     HeapFree(GetProcessHeap(),0,pPortA);
1828     HeapFree(GetProcessHeap(),0,pDeviceA);
1829     HeapFree(GetProcessHeap(),0,dmA);
1830     return ret;
1831 }
1832
1833 /******************************************************************
1834  *              DocumentPropertiesA   [WINSPOOL.@]
1835  *
1836  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1837  */
1838 LONG WINAPI DocumentPropertiesA(HWND hWnd,HANDLE hPrinter,
1839                                 LPSTR pDeviceName, LPDEVMODEA pDevModeOutput,
1840                                 LPDEVMODEA pDevModeInput,DWORD fMode )
1841 {
1842     LPSTR lpName = pDeviceName;
1843     static CHAR port[] = "LPT1:";
1844     LONG ret;
1845
1846     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1847         hWnd,hPrinter,pDeviceName,pDevModeOutput,pDevModeInput,fMode
1848     );
1849
1850     if(!pDeviceName) {
1851         LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
1852         if(!lpNameW) {
1853                 ERR("no name from hPrinter?\n");
1854                 SetLastError(ERROR_INVALID_HANDLE);
1855                 return -1;
1856         }
1857         lpName = strdupWtoA(lpNameW);
1858     }
1859
1860     if (!GDI_CallExtDeviceMode16)
1861     {
1862         GDI_CallExtDeviceMode16 = (void*)GetProcAddress( GetModuleHandleA("gdi32"),
1863                                                          (LPCSTR)102 );
1864         if (!GDI_CallExtDeviceMode16) {
1865                 ERR("No CallExtDeviceMode16?\n");
1866                 return -1;
1867         }
1868     }
1869     ret = GDI_CallExtDeviceMode16(hWnd, pDevModeOutput, lpName, port,
1870                                   pDevModeInput, NULL, fMode);
1871
1872     if(!pDeviceName)
1873         HeapFree(GetProcessHeap(),0,lpName);
1874     return ret;
1875 }
1876
1877
1878 /*****************************************************************************
1879  *          DocumentPropertiesW (WINSPOOL.@)
1880  *
1881  * FIXME: implement DocumentPropertiesA via DocumentPropertiesW, not vice versa
1882  */
1883 LONG WINAPI DocumentPropertiesW(HWND hWnd, HANDLE hPrinter,
1884                                 LPWSTR pDeviceName,
1885                                 LPDEVMODEW pDevModeOutput,
1886                                 LPDEVMODEW pDevModeInput, DWORD fMode)
1887 {
1888
1889     LPSTR pDeviceNameA = strdupWtoA(pDeviceName);
1890     LPDEVMODEA pDevModeInputA = DEVMODEdupWtoA(pDevModeInput);
1891     LPDEVMODEA pDevModeOutputA = NULL;
1892     LONG ret;
1893
1894     TRACE("(%p,%p,%s,%p,%p,%d)\n",
1895           hWnd,hPrinter,debugstr_w(pDeviceName),pDevModeOutput,pDevModeInput,
1896           fMode);
1897     if(pDevModeOutput) {
1898         ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, NULL, NULL, 0);
1899         if(ret < 0) return ret;
1900         pDevModeOutputA = HeapAlloc(GetProcessHeap(), 0, ret);
1901     }
1902     ret = DocumentPropertiesA(hWnd, hPrinter, pDeviceNameA, pDevModeOutputA,
1903                               pDevModeInputA, fMode);
1904     if(pDevModeOutput) {
1905         DEVMODEcpyAtoW(pDevModeOutput, pDevModeOutputA);
1906         HeapFree(GetProcessHeap(),0,pDevModeOutputA);
1907     }
1908     if(fMode == 0 && ret > 0)
1909         ret += (CCHDEVICENAME + CCHFORMNAME);
1910     HeapFree(GetProcessHeap(),0,pDevModeInputA);
1911     HeapFree(GetProcessHeap(),0,pDeviceNameA);
1912     return ret;
1913 }
1914
1915 /*****************************************************************************
1916  *          IsValidDevmodeA            [WINSPOOL.@]
1917  *
1918  * Validate a DEVMODE structure and fix errors if possible.
1919  *
1920  */
1921 BOOL WINAPI IsValidDevmodeA(PDEVMODEA *pDevMode, SIZE_T size)
1922 {
1923     FIXME("(%p,%ld): stub\n", pDevMode, size);
1924
1925     if(!pDevMode)
1926         return FALSE;
1927
1928     return TRUE;
1929 }
1930
1931 /*****************************************************************************
1932  *          IsValidDevmodeW            [WINSPOOL.@]
1933  *
1934  * Validate a DEVMODE structure and fix errors if possible.
1935  *
1936  */
1937 BOOL WINAPI IsValidDevmodeW(PDEVMODEW *pDevMode, SIZE_T size)
1938 {
1939     FIXME("(%p,%ld): stub\n", pDevMode, size);
1940
1941     if(!pDevMode)
1942         return FALSE;
1943
1944     return TRUE;
1945 }
1946
1947 /******************************************************************
1948  *              OpenPrinterA        [WINSPOOL.@]
1949  *
1950  * See OpenPrinterW.
1951  *
1952  */
1953 BOOL WINAPI OpenPrinterA(LPSTR lpPrinterName,HANDLE *phPrinter,
1954                          LPPRINTER_DEFAULTSA pDefault)
1955 {
1956     UNICODE_STRING lpPrinterNameW;
1957     UNICODE_STRING usBuffer;
1958     PRINTER_DEFAULTSW DefaultW, *pDefaultW = NULL;
1959     PWSTR pwstrPrinterNameW;
1960     BOOL ret;
1961
1962     pwstrPrinterNameW = asciitounicode(&lpPrinterNameW,lpPrinterName);
1963
1964     if(pDefault) {
1965         DefaultW.pDatatype = asciitounicode(&usBuffer,pDefault->pDatatype);
1966         DefaultW.pDevMode = pDefault->pDevMode ? GdiConvertToDevmodeW(pDefault->pDevMode) : NULL;
1967         DefaultW.DesiredAccess = pDefault->DesiredAccess;
1968         pDefaultW = &DefaultW;
1969     }
1970     ret = OpenPrinterW(pwstrPrinterNameW, phPrinter, pDefaultW);
1971     if(pDefault) {
1972         RtlFreeUnicodeString(&usBuffer);
1973         HeapFree(GetProcessHeap(), 0, DefaultW.pDevMode);
1974     }
1975     RtlFreeUnicodeString(&lpPrinterNameW);
1976     return ret;
1977 }
1978
1979 /******************************************************************
1980  *              OpenPrinterW        [WINSPOOL.@]
1981  *
1982  * Open a Printer / Printserver or a Printer-Object
1983  *
1984  * PARAMS
1985  *  lpPrinterName [I] Name of Printserver, Printer, or Printer-Object
1986  *  phPrinter     [O] The resulting Handle is stored here
1987  *  pDefault      [I] PTR to Default Printer Settings or NULL
1988  *
1989  * RETURNS
1990  *  Success: TRUE
1991  *  Failure: FALSE
1992  *
1993  * NOTES
1994  *  lpPrinterName is one of:
1995  *|  Printserver (NT only): "Servername" or NULL for the local Printserver
1996  *|  Printer: "PrinterName"
1997  *|  Printer-Object: "PrinterName,Job xxx"
1998  *|  XcvMonitor: "Servername,XcvMonitor MonitorName"
1999  *|  XcvPort: "Servername,XcvPort PortName"
2000  *
2001  * BUGS
2002  *|  Printer-Object not supported
2003  *|  pDefaults is ignored
2004  *
2005  */
2006 BOOL WINAPI OpenPrinterW(LPWSTR lpPrinterName,HANDLE *phPrinter, LPPRINTER_DEFAULTSW pDefault)
2007 {
2008
2009     TRACE("(%s, %p, %p)\n", debugstr_w(lpPrinterName), phPrinter, pDefault);
2010
2011     if(!phPrinter) {
2012         /* NT: FALSE with ERROR_INVALID_PARAMETER, 9x: TRUE */
2013         SetLastError(ERROR_INVALID_PARAMETER);
2014         return FALSE;
2015     }
2016
2017     /* Get the unique handle of the printer or Printserver */
2018     *phPrinter = get_opened_printer_entry(lpPrinterName, pDefault);
2019     TRACE("returning %d with %u and %p\n", *phPrinter != NULL, GetLastError(), *phPrinter);
2020     return (*phPrinter != 0);
2021 }
2022
2023 /******************************************************************
2024  *              AddMonitorA        [WINSPOOL.@]
2025  *
2026  * See AddMonitorW.
2027  *
2028  */
2029 BOOL WINAPI AddMonitorA(LPSTR pName, DWORD Level, LPBYTE pMonitors)
2030 {
2031     LPWSTR  nameW = NULL;
2032     INT     len;
2033     BOOL    res;
2034     LPMONITOR_INFO_2A mi2a;
2035     MONITOR_INFO_2W mi2w;
2036
2037     mi2a = (LPMONITOR_INFO_2A) pMonitors;
2038     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_a(pName), Level, pMonitors,
2039           debugstr_a(mi2a ? mi2a->pName : NULL),
2040           debugstr_a(mi2a ? mi2a->pEnvironment : NULL),
2041           debugstr_a(mi2a ? mi2a->pDLLName : NULL));
2042
2043     if  (Level != 2) {
2044         SetLastError(ERROR_INVALID_LEVEL);
2045         return FALSE;
2046     }
2047
2048     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2049     if (mi2a == NULL) {
2050         return FALSE;
2051     }
2052
2053     if (pName) {
2054         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2055         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2056         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2057     }
2058
2059     memset(&mi2w, 0, sizeof(MONITOR_INFO_2W));
2060     if (mi2a->pName) {
2061         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, NULL, 0);
2062         mi2w.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2063         MultiByteToWideChar(CP_ACP, 0, mi2a->pName, -1, mi2w.pName, len);
2064     }
2065     if (mi2a->pEnvironment) {
2066         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, NULL, 0);
2067         mi2w.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2068         MultiByteToWideChar(CP_ACP, 0, mi2a->pEnvironment, -1, mi2w.pEnvironment, len);
2069     }
2070     if (mi2a->pDLLName) {
2071         len = MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, NULL, 0);
2072         mi2w.pDLLName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2073         MultiByteToWideChar(CP_ACP, 0, mi2a->pDLLName, -1, mi2w.pDLLName, len);
2074     }
2075
2076     res = AddMonitorW(nameW, Level, (LPBYTE) &mi2w);
2077
2078     HeapFree(GetProcessHeap(), 0, mi2w.pName); 
2079     HeapFree(GetProcessHeap(), 0, mi2w.pEnvironment); 
2080     HeapFree(GetProcessHeap(), 0, mi2w.pDLLName); 
2081
2082     HeapFree(GetProcessHeap(), 0, nameW); 
2083     return (res);
2084 }
2085
2086 /******************************************************************************
2087  *              AddMonitorW        [WINSPOOL.@]
2088  *
2089  * Install a Printmonitor
2090  *
2091  * PARAMS
2092  *  pName       [I] Servername or NULL (local Computer)
2093  *  Level       [I] Structure-Level (Must be 2)
2094  *  pMonitors   [I] PTR to MONITOR_INFO_2
2095  *
2096  * RETURNS
2097  *  Success: TRUE
2098  *  Failure: FALSE
2099  *
2100  * NOTES
2101  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
2102  *
2103  */
2104 BOOL WINAPI AddMonitorW(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
2105 {
2106     LPMONITOR_INFO_2W mi2w;
2107
2108     mi2w = (LPMONITOR_INFO_2W) pMonitors;
2109     TRACE("(%s, %d, %p) :  %s %s %s\n", debugstr_w(pName), Level, pMonitors,
2110           debugstr_w(mi2w ? mi2w->pName : NULL),
2111           debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
2112           debugstr_w(mi2w ? mi2w->pDLLName : NULL));
2113
2114     if ((backend == NULL)  && !load_backend()) return FALSE;
2115
2116     if (Level != 2) {
2117         SetLastError(ERROR_INVALID_LEVEL);
2118         return FALSE;
2119     }
2120
2121     /* XP: unchanged, win9x: ERROR_INVALID_ENVIRONMENT */
2122     if (mi2w == NULL) {
2123         return FALSE;
2124     }
2125
2126     return backend->fpAddMonitor(pName, Level, pMonitors);
2127 }
2128
2129 /******************************************************************
2130  *              DeletePrinterDriverA        [WINSPOOL.@]
2131  *
2132  */
2133 BOOL WINAPI DeletePrinterDriverA (LPSTR pName, LPSTR pEnvironment, LPSTR pDriverName)
2134 {
2135     return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
2136 }
2137
2138 /******************************************************************
2139  *              DeletePrinterDriverW        [WINSPOOL.@]
2140  *
2141  */
2142 BOOL WINAPI DeletePrinterDriverW (LPWSTR pName, LPWSTR pEnvironment, LPWSTR pDriverName)
2143 {
2144     return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
2145 }
2146
2147 /******************************************************************
2148  *              DeleteMonitorA        [WINSPOOL.@]
2149  *
2150  * See DeleteMonitorW.
2151  *
2152  */
2153 BOOL WINAPI DeleteMonitorA (LPSTR pName, LPSTR pEnvironment, LPSTR pMonitorName)
2154 {
2155     LPWSTR  nameW = NULL;
2156     LPWSTR  EnvironmentW = NULL;
2157     LPWSTR  MonitorNameW = NULL;
2158     BOOL    res;
2159     INT     len;
2160
2161     if (pName) {
2162         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2163         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2164         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2165     }
2166
2167     if (pEnvironment) {
2168         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
2169         EnvironmentW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2170         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, EnvironmentW, len);
2171     }
2172     if (pMonitorName) {
2173         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
2174         MonitorNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2175         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, MonitorNameW, len);
2176     }
2177
2178     res = DeleteMonitorW(nameW, EnvironmentW, MonitorNameW);
2179
2180     HeapFree(GetProcessHeap(), 0, MonitorNameW); 
2181     HeapFree(GetProcessHeap(), 0, EnvironmentW);
2182     HeapFree(GetProcessHeap(), 0, nameW); 
2183     return (res);
2184 }
2185
2186 /******************************************************************
2187  *              DeleteMonitorW        [WINSPOOL.@]
2188  *
2189  * Delete a specific Printmonitor from a Printing-Environment
2190  *
2191  * PARAMS
2192  *  pName        [I] Servername or NULL (local Computer)
2193  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
2194  *  pMonitorName [I] Name of the Monitor, that should be deleted
2195  *
2196  * RETURNS
2197  *  Success: TRUE
2198  *  Failure: FALSE
2199  *
2200  * NOTES
2201  *  pEnvironment is ignored in Windows for the local Computer.
2202  *
2203  */
2204 BOOL WINAPI DeleteMonitorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
2205 {
2206
2207     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
2208            debugstr_w(pMonitorName));
2209
2210     if ((backend == NULL)  && !load_backend()) return FALSE;
2211
2212     return backend->fpDeleteMonitor(pName, pEnvironment, pMonitorName);
2213 }
2214
2215
2216 /******************************************************************
2217  *              DeletePortA        [WINSPOOL.@]
2218  *
2219  * See DeletePortW.
2220  *
2221  */
2222 BOOL WINAPI DeletePortA (LPSTR pName, HWND hWnd, LPSTR pPortName)
2223 {
2224     LPWSTR  nameW = NULL;
2225     LPWSTR  portW = NULL;
2226     INT     len;
2227     DWORD   res;
2228
2229     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
2230
2231     /* convert servername to unicode */
2232     if (pName) {
2233         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
2234         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2235         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
2236     }
2237
2238     /* convert portname to unicode */
2239     if (pPortName) {
2240         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
2241         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2242         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
2243     }
2244
2245     res = DeletePortW(nameW, hWnd, portW);
2246     HeapFree(GetProcessHeap(), 0, nameW);
2247     HeapFree(GetProcessHeap(), 0, portW);
2248     return res;
2249 }
2250
2251 /******************************************************************
2252  *              DeletePortW        [WINSPOOL.@]
2253  *
2254  * Delete a specific Port
2255  *
2256  * PARAMS
2257  *  pName     [I] Servername or NULL (local Computer)
2258  *  hWnd      [I] Handle to parent Window for the Dialog-Box
2259  *  pPortName [I] Name of the Port, that should be deleted
2260  *
2261  * RETURNS
2262  *  Success: TRUE
2263  *  Failure: FALSE
2264  *
2265  */
2266 BOOL WINAPI DeletePortW (LPWSTR pName, HWND hWnd, LPWSTR pPortName)
2267 {
2268     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
2269
2270     if ((backend == NULL)  && !load_backend()) return FALSE;
2271
2272     if (!pPortName) {
2273         SetLastError(RPC_X_NULL_REF_POINTER);
2274         return FALSE;
2275     }
2276
2277     return backend->fpDeletePort(pName, hWnd, pPortName);
2278 }
2279
2280 /******************************************************************************
2281  *    WritePrinter  [WINSPOOL.@]
2282  */
2283 BOOL WINAPI WritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
2284 {
2285     opened_printer_t *printer;
2286     BOOL ret = FALSE;
2287
2288     TRACE("(%p, %p, %d, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
2289
2290     EnterCriticalSection(&printer_handles_cs);
2291     printer = get_opened_printer(hPrinter);
2292     if(!printer)
2293     {
2294         SetLastError(ERROR_INVALID_HANDLE);
2295         goto end;
2296     }
2297
2298     if(!printer->doc)
2299     {
2300         SetLastError(ERROR_SPL_NO_STARTDOC);
2301         goto end;
2302     }
2303
2304     ret = WriteFile(printer->doc->hf, pBuf, cbBuf, pcWritten, NULL);
2305 end:
2306     LeaveCriticalSection(&printer_handles_cs);
2307     return ret;
2308 }
2309
2310 /*****************************************************************************
2311  *          AddFormA  [WINSPOOL.@]
2312  */
2313 BOOL WINAPI AddFormA(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2314 {
2315     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2316     return 1;
2317 }
2318
2319 /*****************************************************************************
2320  *          AddFormW  [WINSPOOL.@]
2321  */
2322 BOOL WINAPI AddFormW(HANDLE hPrinter, DWORD Level, LPBYTE pForm)
2323 {
2324     FIXME("(%p,%d,%p): stub\n", hPrinter, Level, pForm);
2325     return 1;
2326 }
2327
2328 /*****************************************************************************
2329  *          AddJobA  [WINSPOOL.@]
2330  */
2331 BOOL WINAPI AddJobA(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2332 {
2333     BOOL ret;
2334     BYTE buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
2335     DWORD needed;
2336
2337     if(Level != 1) {
2338         SetLastError(ERROR_INVALID_LEVEL);
2339         return FALSE;
2340     }
2341
2342     ret = AddJobW(hPrinter, Level, buf, sizeof(buf), &needed);
2343
2344     if(ret) {
2345         ADDJOB_INFO_1W *addjobW = (ADDJOB_INFO_1W*)buf;
2346         DWORD len = WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, NULL, 0, NULL, NULL);
2347         *pcbNeeded = len + sizeof(ADDJOB_INFO_1A);
2348         if(*pcbNeeded > cbBuf) {
2349             SetLastError(ERROR_INSUFFICIENT_BUFFER);
2350             ret = FALSE;
2351         } else {
2352             ADDJOB_INFO_1A *addjobA = (ADDJOB_INFO_1A*)pData;
2353             addjobA->JobId = addjobW->JobId;
2354             addjobA->Path = (char *)(addjobA + 1);
2355             WideCharToMultiByte(CP_ACP, 0, addjobW->Path, -1, addjobA->Path, len, NULL, NULL);
2356         }
2357     }
2358     return ret;
2359 }
2360
2361 /*****************************************************************************
2362  *          AddJobW  [WINSPOOL.@]
2363  */
2364 BOOL WINAPI AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
2365 {
2366     opened_printer_t *printer;
2367     job_t *job;
2368     BOOL ret = FALSE;
2369     static const WCHAR spool_path[] = {'s','p','o','o','l','\\','P','R','I','N','T','E','R','S','\\',0};
2370     static const WCHAR fmtW[] = {'%','s','%','0','5','d','.','S','P','L',0};
2371     WCHAR path[MAX_PATH], filename[MAX_PATH];
2372     DWORD len;
2373     ADDJOB_INFO_1W *addjob;
2374
2375     TRACE("(%p,%d,%p,%d,%p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
2376     
2377     EnterCriticalSection(&printer_handles_cs);
2378
2379     printer = get_opened_printer(hPrinter);
2380
2381     if(!printer) {
2382         SetLastError(ERROR_INVALID_HANDLE);
2383         goto end;
2384     }
2385
2386     if(Level != 1) {
2387         SetLastError(ERROR_INVALID_LEVEL);
2388         goto end;
2389     }
2390
2391     job = HeapAlloc(GetProcessHeap(), 0, sizeof(*job));
2392     if(!job)
2393         goto end;
2394
2395     job->job_id = InterlockedIncrement(&next_job_id);
2396
2397     len = GetSystemDirectoryW(path, sizeof(path) / sizeof(WCHAR));
2398     if(path[len - 1] != '\\')
2399         path[len++] = '\\';
2400     memcpy(path + len, spool_path, sizeof(spool_path));    
2401     sprintfW(filename, fmtW, path, job->job_id);
2402
2403     len = strlenW(filename);
2404     job->filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2405     memcpy(job->filename, filename, (len + 1) * sizeof(WCHAR));
2406     job->portname = NULL;
2407     job->document_title = strdupW(default_doc_title);
2408     job->printer_name = strdupW(printer->name);
2409     job->devmode = dup_devmode( printer->devmode );
2410     list_add_tail(&printer->queue->jobs, &job->entry);
2411
2412     *pcbNeeded = (len + 1) * sizeof(WCHAR) + sizeof(*addjob);
2413     if(*pcbNeeded <= cbBuf) {
2414         addjob = (ADDJOB_INFO_1W*)pData;
2415         addjob->JobId = job->job_id;
2416         addjob->Path = (WCHAR *)(addjob + 1);
2417         memcpy(addjob->Path, filename, (len + 1) * sizeof(WCHAR));
2418         ret = TRUE;
2419     } else 
2420         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2421
2422 end:
2423     LeaveCriticalSection(&printer_handles_cs);
2424     return ret;
2425 }
2426
2427 /*****************************************************************************
2428  *          GetPrintProcessorDirectoryA  [WINSPOOL.@]
2429  *
2430  * Return the PATH for the Print-Processors
2431  *
2432  * See GetPrintProcessorDirectoryW.
2433  *
2434  *
2435  */
2436 BOOL WINAPI GetPrintProcessorDirectoryA(LPSTR server, LPSTR env,
2437                                         DWORD level,  LPBYTE Info,
2438                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2439 {
2440     LPWSTR  serverW = NULL;
2441     LPWSTR  envW = NULL;
2442     BOOL    ret;
2443     INT     len;
2444
2445     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(server), 
2446           debugstr_a(env), level, Info, cbBuf, pcbNeeded);
2447  
2448
2449     if (server) {
2450         len = MultiByteToWideChar(CP_ACP, 0, server, -1, NULL, 0);
2451         serverW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2452         MultiByteToWideChar(CP_ACP, 0, server, -1, serverW, len);
2453     }
2454
2455     if (env) {
2456         len = MultiByteToWideChar(CP_ACP, 0, env, -1, NULL, 0);
2457         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2458         MultiByteToWideChar(CP_ACP, 0, env, -1, envW, len);
2459     }
2460
2461     /* NT requires the buffersize from GetPrintProcessorDirectoryW also
2462        for GetPrintProcessorDirectoryA and WC2MB is done in-place.
2463      */
2464     ret = GetPrintProcessorDirectoryW(serverW, envW, level, Info, 
2465                                       cbBuf, pcbNeeded);
2466
2467     if (ret) ret = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)Info, -1, (LPSTR)Info,
2468                                        cbBuf, NULL, NULL) > 0;
2469
2470
2471     TRACE(" required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
2472     HeapFree(GetProcessHeap(), 0, envW);
2473     HeapFree(GetProcessHeap(), 0, serverW);
2474     return ret;
2475 }
2476
2477 /*****************************************************************************
2478  *          GetPrintProcessorDirectoryW  [WINSPOOL.@]
2479  *
2480  * Return the PATH for the Print-Processors
2481  *
2482  * PARAMS
2483  *   server     [I] Servername (NT only) or NULL (local Computer)
2484  *   env        [I] Printing-Environment (see below) or NULL (Default)
2485  *   level      [I] Structure-Level (must be 1)
2486  *   Info       [O] PTR to Buffer that receives the Result
2487  *   cbBuf      [I] Size of Buffer at "Info"
2488  *   pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / 
2489  *                  required for the Buffer at "Info"
2490  *
2491  * RETURNS
2492  *   Success: TRUE  and in pcbNeeded the Bytes used in Info
2493  *   Failure: FALSE and in pcbNeeded the Bytes required for Info,
2494  *   if cbBuf is too small
2495  * 
2496  *   Native Values returned in Info on Success:
2497  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\PRTPROCS\\w32x86" 
2498  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\PRTPROCS\\win40" 
2499  *|  win9x(Windows 4.0):  "%winsysdir%" 
2500  *
2501  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
2502  *
2503  * BUGS
2504  *  Only NULL or "" is supported for server
2505  *
2506  */
2507 BOOL WINAPI GetPrintProcessorDirectoryW(LPWSTR server, LPWSTR env,
2508                                         DWORD level,  LPBYTE Info,
2509                                         DWORD cbBuf,  LPDWORD pcbNeeded)
2510 {
2511
2512     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(server), debugstr_w(env), level,
2513                                         Info, cbBuf, pcbNeeded);
2514
2515     if ((backend == NULL)  && !load_backend()) return FALSE;
2516
2517     if (level != 1) {
2518         /* (Level != 1) is ignored in win9x */
2519         SetLastError(ERROR_INVALID_LEVEL);
2520         return FALSE;
2521     }
2522
2523     if (pcbNeeded == NULL) {
2524         /* (pcbNeeded == NULL) is ignored in win9x */
2525         SetLastError(RPC_X_NULL_REF_POINTER);
2526         return FALSE;
2527     }
2528
2529     return backend->fpGetPrintProcessorDirectory(server, env, level, Info, cbBuf, pcbNeeded);
2530 }
2531
2532 /*****************************************************************************
2533  *          WINSPOOL_OpenDriverReg [internal]
2534  *
2535  * opens the registry for the printer drivers depending on the given input
2536  * variable pEnvironment
2537  *
2538  * RETURNS:
2539  *    the opened hkey on success
2540  *    NULL on error
2541  */
2542 static HKEY WINSPOOL_OpenDriverReg( LPCVOID pEnvironment)
2543 {   
2544     HKEY  retval = NULL;
2545     LPWSTR buffer;
2546     const printenv_t * env;
2547
2548     TRACE("(%s)\n", debugstr_w(pEnvironment));
2549
2550     env = validate_envW(pEnvironment);
2551     if (!env) return NULL;
2552
2553     buffer = HeapAlloc( GetProcessHeap(), 0,
2554                 (strlenW(DriversW) + strlenW(env->envname) + 
2555                  strlenW(env->versionregpath) + 1) * sizeof(WCHAR));
2556     if(buffer) {
2557         wsprintfW(buffer, DriversW, env->envname, env->versionregpath);
2558         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
2559         HeapFree(GetProcessHeap(), 0, buffer);
2560     }
2561     return retval;
2562 }
2563
2564 /*****************************************************************************
2565  * set_devices_and_printerports [internal]
2566  *
2567  * set the [Devices] and [PrinterPorts] entries for a printer.
2568  *
2569  */
2570 static void set_devices_and_printerports(PRINTER_INFO_2W *pi)
2571 {
2572     DWORD portlen = lstrlenW(pi->pPortName) * sizeof(WCHAR);
2573     WCHAR *devline;
2574     HKEY  hkey;
2575
2576     TRACE("(%p) %s\n", pi, debugstr_w(pi->pPrinterName));
2577
2578     /* FIXME: the driver must change to "winspool" */
2579     devline = HeapAlloc(GetProcessHeap(), 0, sizeof(driver_nt) + portlen + sizeof(timeout_15_45));
2580     if (devline) {
2581         lstrcpyW(devline, driver_nt);
2582         lstrcatW(devline, commaW);
2583         lstrcatW(devline, pi->pPortName);
2584
2585         TRACE("using %s\n", debugstr_w(devline));
2586         WriteProfileStringW(devicesW, pi->pPrinterName, devline);
2587         if (!RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey)) {
2588             RegSetValueExW(hkey, pi->pPrinterName, 0, REG_SZ, (LPBYTE)devline,
2589                             (lstrlenW(devline) + 1) * sizeof(WCHAR));
2590             RegCloseKey(hkey);
2591         }
2592
2593         lstrcatW(devline, timeout_15_45);
2594         WriteProfileStringW(PrinterPortsW, pi->pPrinterName, devline);
2595         if (!RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey)) {
2596             RegSetValueExW(hkey, pi->pPrinterName, 0, REG_SZ, (LPBYTE)devline,
2597                             (lstrlenW(devline) + 1) * sizeof(WCHAR));
2598             RegCloseKey(hkey);
2599         }
2600         HeapFree(GetProcessHeap(), 0, devline);
2601     }
2602 }
2603
2604 /*****************************************************************************
2605  *          AddPrinterW  [WINSPOOL.@]
2606  */
2607 HANDLE WINAPI AddPrinterW(LPWSTR pName, DWORD Level, LPBYTE pPrinter)
2608 {
2609     PRINTER_INFO_2W *pi = (PRINTER_INFO_2W *) pPrinter;
2610     LPDEVMODEW dm;
2611     HANDLE retval;
2612     HKEY hkeyPrinter, hkeyPrinters, hkeyDriver, hkeyDrivers;
2613     LONG size;
2614
2615     TRACE("(%s,%d,%p)\n", debugstr_w(pName), Level, pPrinter);
2616
2617     if(pName != NULL) {
2618         ERR("pName = %s - unsupported\n", debugstr_w(pName));
2619         SetLastError(ERROR_INVALID_PARAMETER);
2620         return 0;
2621     }
2622     if(Level != 2) {
2623         ERR("Level = %d, unsupported!\n", Level);
2624         SetLastError(ERROR_INVALID_LEVEL);
2625         return 0;
2626     }
2627     if(!pPrinter) {
2628         SetLastError(ERROR_INVALID_PARAMETER);
2629         return 0;
2630     }
2631     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
2632        ERROR_SUCCESS) {
2633         ERR("Can't create Printers key\n");
2634         return 0;
2635     }
2636     if(!RegOpenKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter)) {
2637         if (!RegQueryValueW(hkeyPrinter, AttributesW, NULL, NULL)) {
2638             SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
2639             RegCloseKey(hkeyPrinter);
2640             RegCloseKey(hkeyPrinters);
2641             return 0;
2642         }
2643         RegCloseKey(hkeyPrinter);
2644     }
2645     hkeyDrivers = WINSPOOL_OpenDriverReg(NULL);
2646     if(!hkeyDrivers) {
2647         ERR("Can't create Drivers key\n");
2648         RegCloseKey(hkeyPrinters);
2649         return 0;
2650     }
2651     if(RegOpenKeyW(hkeyDrivers, pi->pDriverName, &hkeyDriver) !=
2652        ERROR_SUCCESS) {
2653         WARN("Can't find driver %s\n", debugstr_w(pi->pDriverName));
2654         RegCloseKey(hkeyPrinters);
2655         RegCloseKey(hkeyDrivers);
2656         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER);
2657         return 0;
2658     }
2659     RegCloseKey(hkeyDriver);
2660     RegCloseKey(hkeyDrivers);
2661
2662     if(lstrcmpiW(pi->pPrintProcessor, WinPrintW)) {  /* FIXME */
2663         FIXME("Can't find processor %s\n", debugstr_w(pi->pPrintProcessor));
2664         SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
2665         RegCloseKey(hkeyPrinters);
2666         return 0;
2667     }
2668
2669     if(RegCreateKeyW(hkeyPrinters, pi->pPrinterName, &hkeyPrinter) !=
2670        ERROR_SUCCESS) {
2671         FIXME("Can't create printer %s\n", debugstr_w(pi->pPrinterName));
2672         SetLastError(ERROR_INVALID_PRINTER_NAME);
2673         RegCloseKey(hkeyPrinters);
2674         return 0;
2675     }
2676
2677     set_devices_and_printerports(pi);
2678     RegSetValueExW(hkeyPrinter, AttributesW, 0, REG_DWORD,
2679                    (LPBYTE)&pi->Attributes, sizeof(DWORD));
2680     set_reg_szW(hkeyPrinter, DatatypeW, pi->pDatatype);
2681
2682     /* See if we can load the driver.  We may need the devmode structure anyway
2683      *
2684      * FIXME:
2685      * Note that DocumentPropertiesW will briefly try to open the printer we
2686      * just create to find a DEVMODE struct (it will use the WINEPS default
2687      * one in case it is not there, so we are ok).
2688      */
2689     size = DocumentPropertiesW(0, 0, pi->pPrinterName, NULL, NULL, 0);
2690
2691     if(size < 0) {
2692         FIXME("DocumentPropertiesW on printer %s fails\n", debugstr_w(pi->pPrinterName));
2693         size = sizeof(DEVMODEW);
2694     }
2695     if(pi->pDevMode)
2696         dm = pi->pDevMode;
2697     else
2698     {
2699         dm = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
2700         dm->dmSize = size;
2701         if (DocumentPropertiesW(0, 0, pi->pPrinterName, dm, NULL, DM_OUT_BUFFER) < 0)
2702         {
2703             WARN("DocumentPropertiesW on printer %s failed!\n", debugstr_w(pi->pPrinterName));
2704             HeapFree( GetProcessHeap(), 0, dm );
2705             dm = NULL;
2706         }
2707         else
2708         {
2709             /* set devmode to printer name */
2710             lstrcpynW( dm->dmDeviceName, pi->pPrinterName, CCHDEVICENAME );
2711         }
2712     }
2713
2714     set_reg_devmode( hkeyPrinter, Default_DevModeW, dm );
2715     if (!pi->pDevMode) HeapFree( GetProcessHeap(), 0, dm );
2716
2717     set_reg_szW(hkeyPrinter, DescriptionW, pi->pComment);
2718     set_reg_szW(hkeyPrinter, LocationW, pi->pLocation);
2719     set_reg_szW(hkeyPrinter, NameW, pi->pPrinterName);
2720     set_reg_szW(hkeyPrinter, ParametersW, pi->pParameters);
2721
2722     set_reg_szW(hkeyPrinter, PortW, pi->pPortName);
2723     set_reg_szW(hkeyPrinter, Print_ProcessorW, pi->pPrintProcessor);
2724     set_reg_szW(hkeyPrinter, Printer_DriverW, pi->pDriverName);
2725     RegSetValueExW(hkeyPrinter, PriorityW, 0, REG_DWORD,
2726                    (LPBYTE)&pi->Priority, sizeof(DWORD));
2727     set_reg_szW(hkeyPrinter, Separator_FileW, pi->pSepFile);
2728     set_reg_szW(hkeyPrinter, Share_NameW, pi->pShareName);
2729     RegSetValueExW(hkeyPrinter, StartTimeW, 0, REG_DWORD,
2730                    (LPBYTE)&pi->StartTime, sizeof(DWORD));
2731     RegSetValueExW(hkeyPrinter, StatusW, 0, REG_DWORD,
2732                    (LPBYTE)&pi->Status, sizeof(DWORD));
2733     RegSetValueExW(hkeyPrinter, UntilTimeW, 0, REG_DWORD,
2734                    (LPBYTE)&pi->UntilTime, sizeof(DWORD));
2735
2736     RegCloseKey(hkeyPrinter);
2737     RegCloseKey(hkeyPrinters);
2738     if(!OpenPrinterW(pi->pPrinterName, &retval, NULL)) {
2739         ERR("OpenPrinter failing\n");
2740         return 0;
2741     }
2742     return retval;
2743 }
2744
2745 /*****************************************************************************
2746  *          AddPrinterA  [WINSPOOL.@]
2747  */
2748 HANDLE WINAPI AddPrinterA(LPSTR pName, DWORD Level, LPBYTE pPrinter)
2749 {
2750     UNICODE_STRING pNameW;
2751     PWSTR pwstrNameW;
2752     PRINTER_INFO_2W *piW;
2753     PRINTER_INFO_2A *piA = (PRINTER_INFO_2A*)pPrinter;
2754     HANDLE ret;
2755
2756     TRACE("(%s, %d, %p)\n", debugstr_a(pName), Level, pPrinter);
2757     if(Level != 2) {
2758         ERR("Level = %d, unsupported!\n", Level);
2759         SetLastError(ERROR_INVALID_LEVEL);
2760         return 0;
2761     }
2762     pwstrNameW = asciitounicode(&pNameW,pName);
2763     piW = printer_info_AtoW( piA, Level );
2764
2765     ret = AddPrinterW(pwstrNameW, Level, (LPBYTE)piW);
2766
2767     free_printer_info( piW, Level );
2768     RtlFreeUnicodeString(&pNameW);
2769     return ret;
2770 }
2771
2772
2773 /*****************************************************************************
2774  *          ClosePrinter  [WINSPOOL.@]
2775  */
2776 BOOL WINAPI ClosePrinter(HANDLE hPrinter)
2777 {
2778     UINT_PTR i = (UINT_PTR)hPrinter;
2779     opened_printer_t *printer = NULL;
2780     BOOL ret = FALSE;
2781
2782     TRACE("(%p)\n", hPrinter);
2783
2784     EnterCriticalSection(&printer_handles_cs);
2785
2786     if ((i > 0) && (i <= nb_printer_handles))
2787         printer = printer_handles[i - 1];
2788
2789
2790     if(printer)
2791     {
2792         struct list *cursor, *cursor2;
2793
2794         TRACE("closing %s (doc: %p)\n", debugstr_w(printer->name), printer->doc);
2795
2796         if (printer->backend_printer) {
2797             backend->fpClosePrinter(printer->backend_printer);
2798         }
2799
2800         if(printer->doc)
2801             EndDocPrinter(hPrinter);
2802
2803         if(InterlockedDecrement(&printer->queue->ref) == 0)
2804         {
2805             LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
2806             {
2807                 job_t *job = LIST_ENTRY(cursor, job_t, entry);
2808                 ScheduleJob(hPrinter, job->job_id);
2809             }
2810             HeapFree(GetProcessHeap(), 0, printer->queue);
2811         }
2812
2813         free_printer_entry( printer );
2814         printer_handles[i - 1] = NULL;
2815         ret = TRUE;
2816     }
2817     LeaveCriticalSection(&printer_handles_cs);
2818     return ret;
2819 }
2820
2821 /*****************************************************************************
2822  *          DeleteFormA  [WINSPOOL.@]
2823  */
2824 BOOL WINAPI DeleteFormA(HANDLE hPrinter, LPSTR pFormName)
2825 {
2826     FIXME("(%p,%s): stub\n", hPrinter, pFormName);
2827     return 1;
2828 }
2829
2830 /*****************************************************************************
2831  *          DeleteFormW  [WINSPOOL.@]
2832  */
2833 BOOL WINAPI DeleteFormW(HANDLE hPrinter, LPWSTR pFormName)
2834 {
2835     FIXME("(%p,%s): stub\n", hPrinter, debugstr_w(pFormName));
2836     return 1;
2837 }
2838
2839 /*****************************************************************************
2840  *          DeletePrinter  [WINSPOOL.@]
2841  */
2842 BOOL WINAPI DeletePrinter(HANDLE hPrinter)
2843 {
2844     LPCWSTR lpNameW = get_opened_printer_name(hPrinter);
2845     HKEY hkeyPrinters, hkey;
2846     WCHAR def[MAX_PATH];
2847     DWORD size = sizeof( def ) / sizeof( def[0] );
2848
2849     if(!lpNameW) {
2850         SetLastError(ERROR_INVALID_HANDLE);
2851         return FALSE;
2852     }
2853     if(RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) == ERROR_SUCCESS) {
2854         RegDeleteTreeW(hkeyPrinters, lpNameW);
2855         RegCloseKey(hkeyPrinters);
2856     }
2857     WriteProfileStringW(devicesW, lpNameW, NULL);
2858     WriteProfileStringW(PrinterPortsW, lpNameW, NULL);
2859
2860     if(RegCreateKeyW(HKEY_CURRENT_USER, user_printers_reg_key, &hkey) == ERROR_SUCCESS) {
2861         RegDeleteValueW(hkey, lpNameW);
2862         RegCloseKey(hkey);
2863     }
2864
2865     if(RegCreateKeyW(HKEY_CURRENT_USER, WinNT_CV_PrinterPortsW, &hkey) == ERROR_SUCCESS) {
2866         RegDeleteValueW(hkey, lpNameW);
2867         RegCloseKey(hkey);
2868     }
2869
2870     if (GetDefaultPrinterW( def, &size ) && !strcmpW( def, lpNameW ))
2871     {
2872         WriteProfileStringW( windowsW, deviceW, NULL );
2873         if (!RegCreateKeyW( HKEY_CURRENT_USER, user_default_reg_key, &hkey ))
2874         {
2875             RegDeleteValueW( hkey, deviceW );
2876             RegCloseKey( hkey );
2877         }
2878         SetDefaultPrinterW( NULL );
2879     }
2880
2881     return TRUE;
2882 }
2883
2884 /*****************************************************************************
2885  *          SetPrinterA  [WINSPOOL.@]
2886  */
2887 BOOL WINAPI SetPrinterA( HANDLE printer, DWORD level, LPBYTE data, DWORD command )
2888 {
2889     BYTE *dataW = data;
2890     BOOL ret;
2891
2892     if (level != 0)
2893     {
2894         dataW = printer_info_AtoW( data, level );
2895         if (!dataW) return FALSE;
2896     }
2897
2898     ret = SetPrinterW( printer, level, dataW, command );
2899
2900     if (dataW != data) free_printer_info( dataW, level );
2901
2902     return ret;
2903 }
2904
2905 static void set_printer_2( HKEY key, const PRINTER_INFO_2W *pi )
2906 {
2907     set_reg_szW( key, NameW, pi->pPrinterName );
2908     set_reg_szW( key, Share_NameW, pi->pShareName );
2909     set_reg_szW( key, PortW, pi->pPortName );
2910     set_reg_szW( key, Printer_DriverW, pi->pDriverName );
2911     set_reg_szW( key, DescriptionW, pi->pComment );
2912     set_reg_szW( key, LocationW, pi->pLocation );
2913
2914     if (pi->pDevMode)
2915         set_reg_devmode( key, Default_DevModeW, pi->pDevMode );
2916
2917     set_reg_szW( key, Separator_FileW, pi->pSepFile );
2918     set_reg_szW( key, Print_ProcessorW, pi->pPrintProcessor );
2919     set_reg_szW( key, DatatypeW, pi->pDatatype );
2920     set_reg_szW( key, ParametersW, pi->pParameters );
2921
2922     set_reg_DWORD( key, AttributesW, pi->Attributes );
2923     set_reg_DWORD( key, PriorityW, pi->Priority );
2924     set_reg_DWORD( key, Default_PriorityW, pi->DefaultPriority );
2925     set_reg_DWORD( key, StartTimeW, pi->StartTime );
2926     set_reg_DWORD( key, UntilTimeW, pi->UntilTime );
2927 }
2928
2929 static BOOL set_printer_9( HKEY key, const PRINTER_INFO_9W *pi )
2930 {
2931     if (!pi->pDevMode) return FALSE;
2932
2933     set_reg_devmode( key, Default_DevModeW, pi->pDevMode );
2934     return TRUE;
2935 }
2936
2937 /******************************************************************************
2938  *    SetPrinterW  [WINSPOOL.@]
2939  */
2940 BOOL WINAPI SetPrinterW( HANDLE printer, DWORD level, LPBYTE data, DWORD command )
2941 {
2942     HKEY key;
2943     BOOL ret = FALSE;
2944
2945     TRACE( "(%p, %d, %p, %d)\n", printer, level, data, command );
2946
2947     if (command != 0) FIXME( "Ignoring command %d\n", command );
2948
2949     if (WINSPOOL_GetOpenedPrinterRegKey( printer, &key ))
2950         return FALSE;
2951
2952     switch (level)
2953     {
2954     case 2:
2955     {
2956         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)data;
2957         set_printer_2( key, pi2 );
2958         ret = TRUE;
2959         break;
2960     }
2961
2962     case 9:
2963     {
2964         PRINTER_INFO_9W *pi = (PRINTER_INFO_9W *)data;
2965         ret = set_printer_9( key, pi );
2966         break;
2967     }
2968
2969     default:
2970         FIXME( "Unimplemented level %d\n", level );
2971         SetLastError( ERROR_INVALID_LEVEL );
2972     }
2973
2974     RegCloseKey( key );
2975     return ret;
2976 }
2977
2978 /*****************************************************************************
2979  *          SetJobA  [WINSPOOL.@]
2980  */
2981 BOOL WINAPI SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level,
2982                     LPBYTE pJob, DWORD Command)
2983 {
2984     BOOL ret;
2985     LPBYTE JobW;
2986     UNICODE_STRING usBuffer;
2987
2988     TRACE("(%p, %d, %d, %p, %d)\n",hPrinter, JobId, Level, pJob, Command);
2989
2990     /* JobId, pPrinterName, pMachineName, pDriverName, Size, Submitted, Time and TotalPages
2991        are all ignored by SetJob, so we don't bother copying them */
2992     switch(Level)
2993     {
2994     case 0:
2995         JobW = NULL;
2996         break;
2997     case 1:
2998       {
2999         JOB_INFO_1W *info1W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info1W));
3000         JOB_INFO_1A *info1A = (JOB_INFO_1A*)pJob;
3001
3002         JobW = (LPBYTE)info1W;
3003         info1W->pUserName = asciitounicode(&usBuffer, info1A->pUserName);
3004         info1W->pDocument = asciitounicode(&usBuffer, info1A->pDocument);
3005         info1W->pDatatype = asciitounicode(&usBuffer, info1A->pDatatype);
3006         info1W->pStatus = asciitounicode(&usBuffer, info1A->pStatus);
3007         info1W->Status = info1A->Status;
3008         info1W->Priority = info1A->Priority;
3009         info1W->Position = info1A->Position;
3010         info1W->PagesPrinted = info1A->PagesPrinted;
3011         break;
3012       }
3013     case 2:
3014       {
3015         JOB_INFO_2W *info2W = HeapAlloc(GetProcessHeap(), 0, sizeof(*info2W));
3016         JOB_INFO_2A *info2A = (JOB_INFO_2A*)pJob;
3017
3018         JobW = (LPBYTE)info2W;
3019         info2W->pUserName = asciitounicode(&usBuffer, info2A->pUserName);
3020         info2W->pDocument = asciitounicode(&usBuffer, info2A->pDocument);
3021         info2W->pNotifyName = asciitounicode(&usBuffer, info2A->pNotifyName);
3022         info2W->pDatatype = asciitounicode(&usBuffer, info2A->pDatatype);
3023         info2W->pPrintProcessor = asciitounicode(&usBuffer, info2A->pPrintProcessor);
3024         info2W->pParameters = asciitounicode(&usBuffer, info2A->pParameters);
3025         info2W->pDevMode = info2A->pDevMode ? GdiConvertToDevmodeW(info2A->pDevMode) : NULL;
3026         info2W->pStatus = asciitounicode(&usBuffer, info2A->pStatus);
3027         info2W->pSecurityDescriptor = info2A->pSecurityDescriptor;
3028         info2W->Status = info2A->Status;
3029         info2W->Priority = info2A->Priority;
3030         info2W->Position = info2A->Position;
3031         info2W->StartTime = info2A->StartTime;
3032         info2W->UntilTime = info2A->UntilTime;
3033         info2W->PagesPrinted = info2A->PagesPrinted;
3034         break;
3035       }
3036     case 3:
3037         JobW = HeapAlloc(GetProcessHeap(), 0, sizeof(JOB_INFO_3));
3038         memcpy(JobW, pJob, sizeof(JOB_INFO_3));
3039         break;
3040     default:
3041         SetLastError(ERROR_INVALID_LEVEL);
3042         return FALSE;
3043     }
3044
3045     ret = SetJobW(hPrinter, JobId, Level, JobW, Command);
3046
3047     switch(Level)
3048     {
3049     case 1:
3050       {
3051         JOB_INFO_1W *info1W = (JOB_INFO_1W*)JobW;
3052         HeapFree(GetProcessHeap(), 0, info1W->pUserName);
3053         HeapFree(GetProcessHeap(), 0, info1W->pDocument); 
3054         HeapFree(GetProcessHeap(), 0, info1W->pDatatype);
3055         HeapFree(GetProcessHeap(), 0, info1W->pStatus);
3056         break;
3057       }
3058     case 2:
3059       {
3060         JOB_INFO_2W *info2W = (JOB_INFO_2W*)JobW;
3061         HeapFree(GetProcessHeap(), 0, info2W->pUserName);
3062         HeapFree(GetProcessHeap(), 0, info2W->pDocument); 
3063         HeapFree(GetProcessHeap(), 0, info2W->pNotifyName);
3064         HeapFree(GetProcessHeap(), 0, info2W->pDatatype);
3065         HeapFree(GetProcessHeap(), 0, info2W->pPrintProcessor);
3066         HeapFree(GetProcessHeap(), 0, info2W->pParameters);
3067         HeapFree(GetProcessHeap(), 0, info2W->pDevMode);
3068         HeapFree(GetProcessHeap(), 0, info2W->pStatus);
3069         break;
3070       }
3071     }
3072     HeapFree(GetProcessHeap(), 0, JobW);
3073
3074     return ret;
3075 }
3076
3077 /*****************************************************************************
3078  *          SetJobW  [WINSPOOL.@]
3079  */
3080 BOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level,
3081                     LPBYTE pJob, DWORD Command)
3082 {
3083     BOOL ret = FALSE;
3084     job_t *job;
3085
3086     TRACE("(%p, %d, %d, %p, %d)\n", hPrinter, JobId, Level, pJob, Command);
3087     FIXME("Ignoring everything other than document title\n");
3088
3089     EnterCriticalSection(&printer_handles_cs);
3090     job = get_job(hPrinter, JobId);
3091     if(!job)
3092         goto end;
3093
3094     switch(Level)
3095     {
3096     case 0:
3097         break;
3098     case 1:
3099       {
3100         JOB_INFO_1W *info1 = (JOB_INFO_1W*)pJob;
3101         HeapFree(GetProcessHeap(), 0, job->document_title);
3102         job->document_title = strdupW(info1->pDocument);
3103         break;
3104       }
3105     case 2:
3106       {
3107         JOB_INFO_2W *info2 = (JOB_INFO_2W*)pJob;
3108         HeapFree(GetProcessHeap(), 0, job->document_title);
3109         job->document_title = strdupW(info2->pDocument);
3110         HeapFree(GetProcessHeap(), 0, job->devmode);
3111         job->devmode = dup_devmode( info2->pDevMode );
3112         break;
3113       }
3114     case 3:
3115         break;
3116     default:
3117         SetLastError(ERROR_INVALID_LEVEL);
3118         goto end;
3119     }
3120     ret = TRUE;
3121 end:
3122     LeaveCriticalSection(&printer_handles_cs);
3123     return ret;
3124 }
3125
3126 /*****************************************************************************
3127  *          EndDocPrinter  [WINSPOOL.@]
3128  */
3129 BOOL WINAPI EndDocPrinter(HANDLE hPrinter)
3130 {
3131     opened_printer_t *printer;
3132     BOOL ret = FALSE;
3133     TRACE("(%p)\n", hPrinter);
3134
3135     EnterCriticalSection(&printer_handles_cs);
3136
3137     printer = get_opened_printer(hPrinter);
3138     if(!printer)
3139     {
3140         SetLastError(ERROR_INVALID_HANDLE);
3141         goto end;
3142     }
3143
3144     if(!printer->doc)    
3145     {
3146         SetLastError(ERROR_SPL_NO_STARTDOC);
3147         goto end;
3148     }
3149
3150     CloseHandle(printer->doc->hf);
3151     ScheduleJob(hPrinter, printer->doc->job_id);
3152     HeapFree(GetProcessHeap(), 0, printer->doc);
3153     printer->doc = NULL;
3154     ret = TRUE;
3155 end:
3156     LeaveCriticalSection(&printer_handles_cs);
3157     return ret;
3158 }
3159
3160 /*****************************************************************************
3161  *          EndPagePrinter  [WINSPOOL.@]
3162  */
3163 BOOL WINAPI EndPagePrinter(HANDLE hPrinter)
3164 {
3165     FIXME("(%p): stub\n", hPrinter);
3166     return TRUE;
3167 }
3168
3169 /*****************************************************************************
3170  *          StartDocPrinterA  [WINSPOOL.@]
3171  */
3172 DWORD WINAPI StartDocPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3173 {
3174     UNICODE_STRING usBuffer;
3175     DOC_INFO_2W doc2W;
3176     DOC_INFO_2A *doc2 = (DOC_INFO_2A*)pDocInfo;
3177     DWORD ret;
3178
3179     /* DOC_INFO_1, 2 and 3 all have the strings in the same place with either two (DOC_INFO_2)
3180        or one (DOC_INFO_3) extra DWORDs */
3181
3182     switch(Level) {
3183     case 2:
3184         doc2W.JobId = doc2->JobId;
3185         /* fall through */
3186     case 3:
3187         doc2W.dwMode = doc2->dwMode;
3188         /* fall through */
3189     case 1:
3190         doc2W.pDocName = asciitounicode(&usBuffer, doc2->pDocName);
3191         doc2W.pOutputFile = asciitounicode(&usBuffer, doc2->pOutputFile);
3192         doc2W.pDatatype = asciitounicode(&usBuffer, doc2->pDatatype);
3193         break;
3194
3195     default:
3196         SetLastError(ERROR_INVALID_LEVEL);
3197         return FALSE;
3198     }
3199
3200     ret = StartDocPrinterW(hPrinter, Level, (LPBYTE)&doc2W);
3201
3202     HeapFree(GetProcessHeap(), 0, doc2W.pDatatype);
3203     HeapFree(GetProcessHeap(), 0, doc2W.pOutputFile);
3204     HeapFree(GetProcessHeap(), 0, doc2W.pDocName);
3205
3206     return ret;
3207 }
3208
3209 /*****************************************************************************
3210  *          StartDocPrinterW  [WINSPOOL.@]
3211  */
3212 DWORD WINAPI StartDocPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
3213 {
3214     DOC_INFO_2W *doc = (DOC_INFO_2W *)pDocInfo;
3215     opened_printer_t *printer;
3216     BYTE addjob_buf[MAX_PATH * sizeof(WCHAR) + sizeof(ADDJOB_INFO_1W)];
3217     ADDJOB_INFO_1W *addjob = (ADDJOB_INFO_1W*) addjob_buf;
3218     JOB_INFO_1W job_info;
3219     DWORD needed, ret = 0;
3220     HANDLE hf;
3221     WCHAR *filename;
3222     job_t *job;
3223
3224     TRACE("(hPrinter = %p, Level = %d, pDocInfo = %p {pDocName = %s, pOutputFile = %s, pDatatype = %s}):\n",
3225           hPrinter, Level, doc, debugstr_w(doc->pDocName), debugstr_w(doc->pOutputFile),
3226           debugstr_w(doc->pDatatype));
3227
3228     if(Level < 1 || Level > 3)
3229     {
3230         SetLastError(ERROR_INVALID_LEVEL);
3231         return 0;
3232     }
3233
3234     EnterCriticalSection(&printer_handles_cs);
3235     printer = get_opened_printer(hPrinter);
3236     if(!printer)
3237     {
3238         SetLastError(ERROR_INVALID_HANDLE);
3239         goto end;
3240     }
3241
3242     if(printer->doc)
3243     {
3244         SetLastError(ERROR_INVALID_PRINTER_STATE);
3245         goto end;
3246     }
3247
3248     /* Even if we're printing to a file we still add a print job, we'll
3249        just ignore the spool file name */
3250
3251     if(!AddJobW(hPrinter, 1, addjob_buf, sizeof(addjob_buf), &needed))
3252     {
3253         ERR("AddJob failed gle %u\n", GetLastError());
3254         goto end;
3255     }
3256
3257     /* use pOutputFile only, when it is a real filename */
3258     if ((doc->pOutputFile) && is_local_file(doc->pOutputFile))
3259         filename = doc->pOutputFile;
3260     else
3261         filename = addjob->Path;
3262
3263     hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3264     if(hf == INVALID_HANDLE_VALUE)
3265         goto end;
3266
3267     memset(&job_info, 0, sizeof(job_info));
3268     job_info.pDocument = doc->pDocName;
3269     SetJobW(hPrinter, addjob->JobId, 1, (LPBYTE)&job_info, 0);
3270
3271     printer->doc = HeapAlloc(GetProcessHeap(), 0, sizeof(*printer->doc));
3272     printer->doc->hf = hf;
3273     ret = printer->doc->job_id = addjob->JobId;
3274     job = get_job(hPrinter, ret);
3275     job->portname = strdupW(doc->pOutputFile);
3276
3277 end:
3278     LeaveCriticalSection(&printer_handles_cs);
3279
3280     return ret;
3281 }
3282
3283 /*****************************************************************************
3284  *          StartPagePrinter  [WINSPOOL.@]
3285  */
3286 BOOL WINAPI StartPagePrinter(HANDLE hPrinter)
3287 {
3288     FIXME("(%p): stub\n", hPrinter);
3289     return TRUE;
3290 }
3291
3292 /*****************************************************************************
3293  *          GetFormA  [WINSPOOL.@]
3294  */
3295 BOOL WINAPI GetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3296                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3297 {
3298     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,pFormName,
3299          Level,pForm,cbBuf,pcbNeeded);
3300     return FALSE;
3301 }
3302
3303 /*****************************************************************************
3304  *          GetFormW  [WINSPOOL.@]
3305  */
3306 BOOL WINAPI GetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3307                  LPBYTE pForm, DWORD cbBuf, LPDWORD pcbNeeded)
3308 {
3309     FIXME("(%p,%s,%d,%p,%d,%p): stub\n",hPrinter,
3310           debugstr_w(pFormName),Level,pForm,cbBuf,pcbNeeded);
3311     return FALSE;
3312 }
3313
3314 /*****************************************************************************
3315  *          SetFormA  [WINSPOOL.@]
3316  */
3317 BOOL WINAPI SetFormA(HANDLE hPrinter, LPSTR pFormName, DWORD Level,
3318                         LPBYTE pForm)
3319 {
3320     FIXME("(%p,%s,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3321     return FALSE;
3322 }
3323
3324 /*****************************************************************************
3325  *          SetFormW  [WINSPOOL.@]
3326  */
3327 BOOL WINAPI SetFormW(HANDLE hPrinter, LPWSTR pFormName, DWORD Level,
3328                         LPBYTE pForm)
3329 {
3330     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pFormName,Level,pForm);
3331     return FALSE;
3332 }
3333
3334 /*****************************************************************************
3335  *          ReadPrinter  [WINSPOOL.@]
3336  */
3337 BOOL WINAPI ReadPrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf,
3338                            LPDWORD pNoBytesRead)
3339 {
3340     FIXME("(%p,%p,%d,%p): stub\n",hPrinter,pBuf,cbBuf,pNoBytesRead);
3341     return FALSE;
3342 }
3343
3344 /*****************************************************************************
3345  *          ResetPrinterA  [WINSPOOL.@]
3346  */
3347 BOOL WINAPI ResetPrinterA(HANDLE hPrinter, LPPRINTER_DEFAULTSA pDefault)
3348 {
3349     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3350     return FALSE;
3351 }
3352
3353 /*****************************************************************************
3354  *          ResetPrinterW  [WINSPOOL.@]
3355  */
3356 BOOL WINAPI ResetPrinterW(HANDLE hPrinter, LPPRINTER_DEFAULTSW pDefault)
3357 {
3358     FIXME("(%p, %p): stub\n", hPrinter, pDefault);
3359     return FALSE;
3360 }
3361
3362 /*****************************************************************************
3363  *    get_dword_from_reg
3364  *
3365  * Return DWORD associated with name from hkey.
3366  */
3367 static DWORD get_dword_from_reg( HKEY hkey, const WCHAR *name )
3368 {
3369     DWORD sz = sizeof(DWORD), type, value = 0;
3370     LONG ret;
3371
3372     ret = RegQueryValueExW( hkey, name, 0, &type, (LPBYTE)&value, &sz );
3373
3374     if(ret != ERROR_SUCCESS) {
3375         WARN("Got ret = %d on name %s\n", ret, debugstr_w(name));
3376         return 0;
3377     }
3378     if(type != REG_DWORD) {
3379         ERR("Got type %d\n", type);
3380         return 0;
3381     }
3382     return value;
3383 }
3384
3385 /*****************************************************************************
3386  * get_filename_from_reg [internal]
3387  *
3388  * Get ValueName from hkey storing result in out
3389  * when the Value in the registry has only a filename, use driverdir as prefix
3390  * outlen is space left in out
3391  * String is stored either as unicode or ascii
3392  *
3393  */
3394
3395 static BOOL get_filename_from_reg(HKEY hkey, LPCWSTR driverdir, DWORD dirlen, LPCWSTR ValueName,
3396                                   LPBYTE out, DWORD outlen, LPDWORD needed)
3397 {
3398     WCHAR   filename[MAX_PATH];
3399     DWORD   size;
3400     DWORD   type;
3401     LONG    ret;
3402     LPWSTR  buffer = filename;
3403     LPWSTR  ptr;
3404
3405     *needed = 0;
3406     size = sizeof(filename);
3407     buffer[0] = '\0';
3408     ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3409     if (ret == ERROR_MORE_DATA) {
3410         TRACE("need dynamic buffer: %u\n", size);
3411         buffer = HeapAlloc(GetProcessHeap(), 0, size);
3412         if (!buffer) {
3413             /* No Memory is bad */
3414             return FALSE;
3415         }
3416         buffer[0] = '\0';
3417         ret = RegQueryValueExW(hkey, ValueName, NULL, &type, (LPBYTE) buffer, &size);
3418     }
3419
3420     if ((ret != ERROR_SUCCESS) || (!buffer[0])) {
3421         if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3422         return FALSE;
3423     }
3424
3425     ptr = buffer;
3426     while (ptr) {
3427         /* do we have a full path ? */
3428         ret = (((buffer[0] == '\\') && (buffer[1] == '\\')) ||
3429                 (buffer[0] && (buffer[1] == ':') && (buffer[2] == '\\')) );
3430
3431         if (!ret) {
3432             /* we must build the full Path */
3433             *needed += dirlen;
3434             if ((out) && (outlen > dirlen)) {
3435                 lstrcpyW((LPWSTR)out, driverdir);
3436                 out += dirlen;
3437                 outlen -= dirlen;
3438             }
3439             else
3440                 out = NULL;
3441         }
3442
3443         /* write the filename */
3444         size = (lstrlenW(ptr) + 1) * sizeof(WCHAR);
3445         if ((out) && (outlen >= size)) {
3446             lstrcpyW((LPWSTR)out, ptr);
3447             out += size;
3448             outlen -= size;
3449         }
3450         else
3451             out = NULL;
3452         *needed += size;
3453         ptr +=  lstrlenW(ptr)+1;
3454         if ((type != REG_MULTI_SZ) || (!ptr[0]))  ptr = NULL;
3455     }
3456
3457     if (buffer != filename) HeapFree(GetProcessHeap(), 0, buffer);
3458
3459     /* write the multisz-termination */
3460     if (type == REG_MULTI_SZ) {
3461         size = sizeof(WCHAR);
3462
3463         *needed += size;
3464         if (out && (outlen >= size)) {
3465             memset (out, 0, size);
3466         }
3467     }
3468     return TRUE;
3469 }
3470
3471 /*****************************************************************************
3472  *    WINSPOOL_GetStringFromReg
3473  *
3474  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3475  * String is stored as unicode.
3476  */
3477 static BOOL WINSPOOL_GetStringFromReg(HKEY hkey, LPCWSTR ValueName, LPBYTE ptr,
3478                                       DWORD buflen, DWORD *needed)
3479 {
3480     DWORD sz = buflen, type;
3481     LONG ret;
3482
3483     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3484     if(ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) {
3485         WARN("Got ret = %d\n", ret);
3486         *needed = 0;
3487         return FALSE;
3488     }
3489     /* add space for terminating '\0' */
3490     sz += sizeof(WCHAR);
3491     *needed = sz;
3492
3493     if (ptr)
3494         TRACE("%s: %s\n", debugstr_w(ValueName), debugstr_w((LPCWSTR)ptr));
3495
3496     return TRUE;
3497 }
3498
3499 /*****************************************************************************
3500  *    WINSPOOL_GetDefaultDevMode
3501  *
3502  * Get a default DevMode values for wineps.
3503  * FIXME - use ppd.
3504  */
3505
3506 static void WINSPOOL_GetDefaultDevMode(
3507         LPBYTE ptr,
3508         DWORD buflen, DWORD *needed)
3509 {
3510     DEVMODEW    dm;
3511     static const WCHAR szWwps[] = {'w', 'i', 'n', 'e', 'p', 's', '.', 'd', 'r', 'v', 0 };
3512
3513         /* fill default DEVMODE - should be read from ppd... */
3514         ZeroMemory( &dm, sizeof(dm) );
3515         memcpy(dm.dmDeviceName,szWwps,sizeof szWwps);
3516         dm.dmSpecVersion = DM_SPECVERSION;
3517         dm.dmDriverVersion = 1;
3518         dm.dmSize = sizeof(DEVMODEW);
3519         dm.dmDriverExtra = 0;
3520         dm.dmFields =
3521                 DM_ORIENTATION | DM_PAPERSIZE |
3522                 DM_PAPERLENGTH | DM_PAPERWIDTH |
3523                 DM_SCALE |
3524                 DM_COPIES |
3525                 DM_DEFAULTSOURCE | DM_PRINTQUALITY |
3526                 DM_YRESOLUTION | DM_TTOPTION;
3527
3528         dm.u1.s1.dmOrientation = DMORIENT_PORTRAIT;
3529         dm.u1.s1.dmPaperSize = DMPAPER_A4;
3530         dm.u1.s1.dmPaperLength = 2970;
3531         dm.u1.s1.dmPaperWidth = 2100;
3532
3533         dm.u1.s1.dmScale = 100;
3534         dm.u1.s1.dmCopies = 1;
3535         dm.u1.s1.dmDefaultSource = DMBIN_AUTO;
3536         dm.u1.s1.dmPrintQuality = DMRES_MEDIUM;
3537         /* dm.dmColor */
3538         /* dm.dmDuplex */
3539         dm.dmYResolution = 300; /* 300dpi */
3540         dm.dmTTOption = DMTT_BITMAP;
3541         /* dm.dmCollate */
3542         /* dm.dmFormName */
3543         /* dm.dmLogPixels */
3544         /* dm.dmBitsPerPel */
3545         /* dm.dmPelsWidth */
3546         /* dm.dmPelsHeight */
3547         /* dm.u2.dmDisplayFlags */
3548         /* dm.dmDisplayFrequency */
3549         /* dm.dmICMMethod */
3550         /* dm.dmICMIntent */
3551         /* dm.dmMediaType */
3552         /* dm.dmDitherType */
3553         /* dm.dmReserved1 */
3554         /* dm.dmReserved2 */
3555         /* dm.dmPanningWidth */
3556         /* dm.dmPanningHeight */
3557
3558     if(buflen >= sizeof(DEVMODEW))
3559         memcpy(ptr, &dm, sizeof(DEVMODEW));
3560     *needed = sizeof(DEVMODEW);
3561 }
3562
3563 /*****************************************************************************
3564  *    WINSPOOL_GetDevModeFromReg
3565  *
3566  * Get ValueName from hkey storing result in ptr.  buflen is space left in ptr
3567  * DevMode is stored either as unicode or ascii.
3568  */
3569 static BOOL WINSPOOL_GetDevModeFromReg(HKEY hkey, LPCWSTR ValueName,
3570                                        LPBYTE ptr,
3571                                        DWORD buflen, DWORD *needed)
3572 {
3573     DWORD sz = buflen, type;
3574     LONG ret;
3575
3576     if (ptr && buflen>=sizeof(DEVMODEA)) memset(ptr, 0, sizeof(DEVMODEA));
3577     ret = RegQueryValueExW(hkey, ValueName, 0, &type, ptr, &sz);
3578     if ((ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA)) sz = 0;
3579     if (sz < sizeof(DEVMODEA))
3580     {
3581         TRACE("corrupted registry for %s ( size %d)\n",debugstr_w(ValueName),sz);
3582         return FALSE;
3583     }
3584     /* ensures that dmSize is not erratically bogus if registry is invalid */
3585     if (ptr && ((DEVMODEA*)ptr)->dmSize < sizeof(DEVMODEA))
3586         ((DEVMODEA*)ptr)->dmSize = sizeof(DEVMODEA);
3587     sz += (CCHDEVICENAME + CCHFORMNAME);
3588     if (ptr && (buflen >= sz)) {
3589         DEVMODEW *dmW = GdiConvertToDevmodeW((DEVMODEA*)ptr);
3590         memcpy(ptr, dmW, sz);
3591         HeapFree(GetProcessHeap(),0,dmW);
3592     }
3593     *needed = sz;
3594     return TRUE;
3595 }
3596
3597 /*********************************************************************
3598  *    WINSPOOL_GetPrinter_1
3599  *
3600  * Fills out a PRINTER_INFO_1W struct storing the strings in buf.
3601  */
3602 static BOOL WINSPOOL_GetPrinter_1(HKEY hkeyPrinter, PRINTER_INFO_1W *pi1,
3603                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
3604 {
3605     DWORD size, left = cbBuf;
3606     BOOL space = (cbBuf > 0);
3607     LPBYTE ptr = buf;
3608
3609     *pcbNeeded = 0;
3610
3611     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
3612         if(space && size <= left) {
3613             pi1->pName = (LPWSTR)ptr;
3614             ptr += size;
3615             left -= size;
3616         } else
3617             space = FALSE;
3618         *pcbNeeded += size;
3619     }
3620
3621     /* FIXME: pDescription should be something like "Name,Driver_Name,". */
3622     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
3623         if(space && size <= left) {
3624             pi1->pDescription = (LPWSTR)ptr;
3625             ptr += size;
3626             left -= size;
3627         } else
3628             space = FALSE;
3629         *pcbNeeded += size;
3630     }
3631
3632     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size)) {
3633         if(space && size <= left) {
3634             pi1->pComment = (LPWSTR)ptr;
3635             ptr += size;
3636             left -= size;
3637         } else
3638             space = FALSE;
3639         *pcbNeeded += size;
3640     }
3641
3642     if(pi1) pi1->Flags = PRINTER_ENUM_ICON8; /* We're a printer */
3643
3644     if(!space && pi1) /* zero out pi1 if we can't completely fill buf */
3645         memset(pi1, 0, sizeof(*pi1));
3646
3647     return space;
3648 }
3649 /*********************************************************************
3650  *    WINSPOOL_GetPrinter_2
3651  *
3652  * Fills out a PRINTER_INFO_2W struct storing the strings in buf.
3653  */
3654 static BOOL WINSPOOL_GetPrinter_2(HKEY hkeyPrinter, PRINTER_INFO_2W *pi2,
3655                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
3656 {
3657     DWORD size, left = cbBuf;
3658     BOOL space = (cbBuf > 0);
3659     LPBYTE ptr = buf;
3660
3661     *pcbNeeded = 0;
3662
3663     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
3664         if(space && size <= left) {
3665             pi2->pPrinterName = (LPWSTR)ptr;
3666             ptr += size;
3667             left -= size;
3668         } else
3669             space = FALSE;
3670         *pcbNeeded += size;
3671     }
3672     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Share_NameW, ptr, left, &size)) {
3673         if(space && size <= left) {
3674             pi2->pShareName = (LPWSTR)ptr;
3675             ptr += size;
3676             left -= size;
3677         } else
3678             space = FALSE;
3679         *pcbNeeded += size;
3680     }
3681     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size)) {
3682         if(space && size <= left) {
3683             pi2->pPortName = (LPWSTR)ptr;
3684             ptr += size;
3685             left -= size;
3686         } else
3687             space = FALSE;
3688         *pcbNeeded += size;
3689     }
3690     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Printer_DriverW, ptr, left, &size)) {
3691         if(space && size <= left) {
3692             pi2->pDriverName = (LPWSTR)ptr;
3693             ptr += size;
3694             left -= size;
3695         } else
3696             space = FALSE;
3697         *pcbNeeded += size;
3698     }
3699     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DescriptionW, ptr, left, &size)) {
3700         if(space && size <= left) {
3701             pi2->pComment = (LPWSTR)ptr;
3702             ptr += size;
3703             left -= size;
3704         } else
3705             space = FALSE;
3706         *pcbNeeded += size;
3707     }
3708     if(WINSPOOL_GetStringFromReg(hkeyPrinter, LocationW, ptr, left, &size)) {
3709         if(space && size <= left) {
3710             pi2->pLocation = (LPWSTR)ptr;
3711             ptr += size;
3712             left -= size;
3713         } else
3714             space = FALSE;
3715         *pcbNeeded += size;
3716     }
3717     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, ptr, left, &size)) {
3718         if(space && size <= left) {
3719             pi2->pDevMode = (LPDEVMODEW)ptr;
3720             ptr += size;
3721             left -= size;
3722         } else
3723             space = FALSE;
3724         *pcbNeeded += size;
3725     }
3726     else
3727     {
3728         WINSPOOL_GetDefaultDevMode(ptr, left, &size);
3729         if(space && size <= left) {
3730             pi2->pDevMode = (LPDEVMODEW)ptr;
3731             ptr += size;
3732             left -= size;
3733         } else
3734             space = FALSE;
3735         *pcbNeeded += size;
3736     }
3737     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Separator_FileW, ptr, left, &size)) {
3738         if(space && size <= left) {
3739             pi2->pSepFile = (LPWSTR)ptr;
3740             ptr += size;
3741             left -= size;
3742         } else
3743             space = FALSE;
3744         *pcbNeeded += size;
3745     }
3746     if(WINSPOOL_GetStringFromReg(hkeyPrinter, Print_ProcessorW, ptr, left, &size)) {
3747         if(space && size <= left) {
3748             pi2->pPrintProcessor = (LPWSTR)ptr;
3749             ptr += size;
3750             left -= size;
3751         } else
3752             space = FALSE;
3753         *pcbNeeded += size;
3754     }
3755     if(WINSPOOL_GetStringFromReg(hkeyPrinter, DatatypeW, ptr, left, &size)) {
3756         if(space && size <= left) {
3757             pi2->pDatatype = (LPWSTR)ptr;
3758             ptr += size;
3759             left -= size;
3760         } else
3761             space = FALSE;
3762         *pcbNeeded += size;
3763     }
3764     if(WINSPOOL_GetStringFromReg(hkeyPrinter, ParametersW, ptr, left, &size)) {
3765         if(space && size <= left) {
3766             pi2->pParameters = (LPWSTR)ptr;
3767             ptr += size;
3768             left -= size;
3769         } else
3770             space = FALSE;
3771         *pcbNeeded += size;
3772     }
3773     if(pi2) {
3774         pi2->Attributes = get_dword_from_reg( hkeyPrinter, AttributesW );
3775         pi2->Priority = get_dword_from_reg( hkeyPrinter, PriorityW );
3776         pi2->DefaultPriority = get_dword_from_reg( hkeyPrinter, Default_PriorityW );
3777         pi2->StartTime = get_dword_from_reg( hkeyPrinter, StartTimeW );
3778         pi2->UntilTime = get_dword_from_reg( hkeyPrinter, UntilTimeW );
3779     }
3780
3781     if(!space && pi2) /* zero out pi2 if we can't completely fill buf */
3782         memset(pi2, 0, sizeof(*pi2));
3783
3784     return space;
3785 }
3786
3787 /*********************************************************************
3788  *    WINSPOOL_GetPrinter_4
3789  *
3790  * Fills out a PRINTER_INFO_4 struct storing the strings in buf.
3791  */
3792 static BOOL WINSPOOL_GetPrinter_4(HKEY hkeyPrinter, PRINTER_INFO_4W *pi4,
3793                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
3794 {
3795     DWORD size, left = cbBuf;
3796     BOOL space = (cbBuf > 0);
3797     LPBYTE ptr = buf;
3798
3799     *pcbNeeded = 0;
3800
3801     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
3802         if(space && size <= left) {
3803             pi4->pPrinterName = (LPWSTR)ptr;
3804             ptr += size;
3805             left -= size;
3806         } else
3807             space = FALSE;
3808         *pcbNeeded += size;
3809     }
3810     if(pi4) {
3811         pi4->Attributes = get_dword_from_reg( hkeyPrinter, AttributesW );
3812     }
3813
3814     if(!space && pi4) /* zero out pi4 if we can't completely fill buf */
3815         memset(pi4, 0, sizeof(*pi4));
3816
3817     return space;
3818 }
3819
3820 /*********************************************************************
3821  *    WINSPOOL_GetPrinter_5
3822  *
3823  * Fills out a PRINTER_INFO_5 struct storing the strings in buf.
3824  */
3825 static BOOL WINSPOOL_GetPrinter_5(HKEY hkeyPrinter, PRINTER_INFO_5W *pi5,
3826                                   LPBYTE buf, DWORD cbBuf, LPDWORD pcbNeeded)
3827 {
3828     DWORD size, left = cbBuf;
3829     BOOL space = (cbBuf > 0);
3830     LPBYTE ptr = buf;
3831
3832     *pcbNeeded = 0;
3833
3834     if(WINSPOOL_GetStringFromReg(hkeyPrinter, NameW, ptr, left, &size)) {
3835         if(space && size <= left) {
3836             pi5->pPrinterName = (LPWSTR)ptr;
3837             ptr += size;
3838             left -= size;
3839         } else
3840             space = FALSE;
3841         *pcbNeeded += size;
3842     }
3843     if(WINSPOOL_GetStringFromReg(hkeyPrinter, PortW, ptr, left, &size)) {
3844         if(space && size <= left) {
3845             pi5->pPortName = (LPWSTR)ptr;
3846             ptr += size;
3847             left -= size;
3848         } else
3849             space = FALSE;
3850         *pcbNeeded += size;
3851     }
3852     if(pi5) {
3853         pi5->Attributes = get_dword_from_reg( hkeyPrinter, AttributesW );
3854         pi5->DeviceNotSelectedTimeout = get_dword_from_reg( hkeyPrinter, dnsTimeoutW );
3855         pi5->TransmissionRetryTimeout = get_dword_from_reg( hkeyPrinter, txTimeoutW );
3856     }
3857
3858     if(!space && pi5) /* zero out pi5 if we can't completely fill buf */
3859         memset(pi5, 0, sizeof(*pi5));
3860
3861     return space;
3862 }
3863
3864 /*********************************************************************
3865  *    WINSPOOL_GetPrinter_7
3866  *
3867  * Fills out a PRINTER_INFO_7 struct storing the strings in buf.
3868  */
3869 static BOOL WINSPOOL_GetPrinter_7(HKEY hkeyPrinter, PRINTER_INFO_7W *pi7, LPBYTE buf,
3870                                   DWORD cbBuf, LPDWORD pcbNeeded)
3871 {
3872     DWORD size, left = cbBuf;
3873     BOOL space = (cbBuf > 0);
3874     LPBYTE ptr = buf;
3875
3876     *pcbNeeded = 0;
3877
3878     if (! WINSPOOL_GetStringFromReg(hkeyPrinter, ObjectGUIDW, ptr, left, &size))
3879     {
3880         ptr = NULL;
3881         size = sizeof(pi7->pszObjectGUID);
3882     }
3883     if (space && size <= left) {
3884         pi7->pszObjectGUID = (LPWSTR)ptr;
3885         ptr += size;
3886         left -= size;
3887     } else
3888         space = FALSE;
3889     *pcbNeeded += size;
3890     if (pi7) {
3891         /* We do not have a Directory Service */
3892         pi7->dwAction = DSPRINT_UNPUBLISH;
3893     }
3894
3895     if (!space && pi7) /* zero out pi7 if we can't completely fill buf */
3896         memset(pi7, 0, sizeof(*pi7));
3897
3898     return space;
3899 }
3900
3901 /*********************************************************************
3902  *    WINSPOOL_GetPrinter_9
3903  *
3904  * Fills out a PRINTER_INFO_9AW struct storing the strings in buf.
3905  */
3906 static BOOL WINSPOOL_GetPrinter_9(HKEY hkeyPrinter, PRINTER_INFO_9W *pi9, LPBYTE buf,
3907                                   DWORD cbBuf, LPDWORD pcbNeeded)
3908 {
3909     DWORD size;
3910     BOOL space = (cbBuf > 0);
3911
3912     *pcbNeeded = 0;
3913
3914     if(WINSPOOL_GetDevModeFromReg(hkeyPrinter, Default_DevModeW, buf, cbBuf, &size)) {
3915         if(space && size <= cbBuf) {
3916             pi9->pDevMode = (LPDEVMODEW)buf;
3917         } else
3918             space = FALSE;
3919         *pcbNeeded += size;
3920     }
3921     else
3922     {
3923         WINSPOOL_GetDefaultDevMode(buf, cbBuf, &size);
3924         if(space && size <= cbBuf) {
3925             pi9->pDevMode = (LPDEVMODEW)buf;
3926         } else
3927             space = FALSE;
3928         *pcbNeeded += size;
3929     }
3930
3931     if(!space && pi9) /* zero out pi9 if we can't completely fill buf */
3932         memset(pi9, 0, sizeof(*pi9));
3933
3934     return space;
3935 }
3936
3937 /*****************************************************************************
3938  *          GetPrinterW  [WINSPOOL.@]
3939  */
3940 BOOL WINAPI GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
3941                         DWORD cbBuf, LPDWORD pcbNeeded)
3942 {
3943     DWORD size, needed = 0, err;
3944     LPBYTE ptr = NULL;
3945     HKEY hkeyPrinter;
3946     BOOL ret;
3947
3948     TRACE("(%p,%d,%p,%d,%p)\n",hPrinter,Level,pPrinter,cbBuf, pcbNeeded);
3949
3950     err = WINSPOOL_GetOpenedPrinterRegKey( hPrinter, &hkeyPrinter );
3951     if (err)
3952     {
3953         SetLastError( err );
3954         return FALSE;
3955     }
3956
3957     switch(Level) {
3958     case 2:
3959       {
3960         PRINTER_INFO_2W *pi2 = (PRINTER_INFO_2W *)pPrinter;
3961
3962         size = sizeof(PRINTER_INFO_2W);
3963         if(size <= cbBuf) {
3964             ptr = pPrinter + size;
3965             cbBuf -= size;
3966             memset(pPrinter, 0, size);
3967         } else {
3968             pi2 = NULL;
3969             cbBuf = 0;
3970         }
3971         ret = WINSPOOL_GetPrinter_2(hkeyPrinter, pi2, ptr, cbBuf, &needed);
3972         needed += size;
3973         break;
3974       }
3975
3976     case 4:
3977       {
3978         PRINTER_INFO_4W *pi4 = (PRINTER_INFO_4W *)pPrinter;
3979
3980         size = sizeof(PRINTER_INFO_4W);
3981         if(size <= cbBuf) {
3982             ptr = pPrinter + size;
3983             cbBuf -= size;
3984             memset(pPrinter, 0, size);
3985         } else {
3986             pi4 = NULL;
3987             cbBuf = 0;
3988         }
3989         ret = WINSPOOL_GetPrinter_4(hkeyPrinter, pi4, ptr, cbBuf, &needed);
3990         needed += size;
3991         break;
3992       }
3993
3994
3995     case 5:
3996       {
3997         PRINTER_INFO_5W *pi5 = (PRINTER_INFO_5W *)pPrinter;
3998
3999         size = sizeof(PRINTER_INFO_5W);
4000         if(size <= cbBuf) {
4001             ptr = pPrinter + size;
4002             cbBuf -= size;
4003             memset(pPrinter, 0, size);
4004         } else {
4005             pi5 = NULL;
4006             cbBuf = 0;
4007         }
4008
4009         ret = WINSPOOL_GetPrinter_5(hkeyPrinter, pi5, ptr, cbBuf, &needed);
4010         needed += size;
4011         break;
4012       }
4013
4014
4015     case 6:
4016       {
4017         PRINTER_INFO_6 *pi6 = (PRINTER_INFO_6 *) pPrinter;
4018
4019         size = sizeof(PRINTER_INFO_6);
4020         if (size <= cbBuf) {
4021             /* FIXME: We do not update the status yet */
4022             pi6->dwStatus = get_dword_from_reg( hkeyPrinter, StatusW );
4023             ret = TRUE;
4024         } else {
4025             ret = FALSE;
4026         }
4027
4028         needed += size;
4029         break;
4030       }
4031
4032     case 7:
4033       {
4034         PRINTER_INFO_7W *pi7 = (PRINTER_INFO_7W *) pPrinter;
4035
4036         size = sizeof(PRINTER_INFO_7W);
4037         if (size <= cbBuf) {
4038             ptr = pPrinter + size;
4039             cbBuf -= size;
4040             memset(pPrinter, 0, size);
4041         } else {
4042             pi7 = NULL;
4043             cbBuf = 0;
4044         }
4045
4046         ret = WINSPOOL_GetPrinter_7(hkeyPrinter, pi7, ptr, cbBuf, &needed);
4047         needed += size;
4048         break;
4049       }
4050
4051
4052     case 8:
4053         /* 8 is the global default printer info and 9 already gets it instead of the per-user one */
4054         /* still, PRINTER_INFO_8W is the same as PRINTER_INFO_9W */
4055         /* fall through */
4056     case 9:
4057       {
4058         PRINTER_INFO_9W *pi9 = (PRINTER_INFO_9W *)pPrinter;
4059
4060         size = sizeof(PRINTER_INFO_9W);
4061         if(size <= cbBuf) {
4062             ptr = pPrinter + size;
4063             cbBuf -= size;
4064             memset(pPrinter, 0, size);
4065         } else {
4066             pi9 = NULL;
4067             cbBuf = 0;
4068         }
4069
4070         ret = WINSPOOL_GetPrinter_9(hkeyPrinter, pi9, ptr, cbBuf, &needed);
4071         needed += size;
4072         break;
4073       }
4074
4075
4076     default:
4077         FIXME("Unimplemented level %d\n", Level);
4078         SetLastError(ERROR_INVALID_LEVEL);
4079         RegCloseKey(hkeyPrinter);
4080         return FALSE;
4081     }
4082
4083     RegCloseKey(hkeyPrinter);
4084
4085     TRACE("returning %d needed = %d\n", ret, needed);
4086     if(pcbNeeded) *pcbNeeded = needed;
4087     if(!ret)
4088         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4089     return ret;
4090 }
4091
4092 /*****************************************************************************
4093  *          GetPrinterA  [WINSPOOL.@]
4094  */
4095 BOOL WINAPI GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter,
4096                     DWORD cbBuf, LPDWORD pcbNeeded)
4097 {
4098     BOOL ret;
4099     LPBYTE buf = NULL;
4100
4101     if (cbBuf)
4102         buf = HeapAlloc(GetProcessHeap(), 0, cbBuf);
4103
4104     ret = GetPrinterW(hPrinter, Level, buf, cbBuf, pcbNeeded);
4105     if (ret)
4106         convert_printerinfo_W_to_A(pPrinter, buf, Level, cbBuf, 1);
4107     HeapFree(GetProcessHeap(), 0, buf);
4108
4109     return ret;
4110 }
4111
4112 /*****************************************************************************
4113  *          WINSPOOL_EnumPrintersW
4114  *
4115  *    Implementation of EnumPrintersW
4116  */
4117 static BOOL WINSPOOL_EnumPrintersW(DWORD dwType, LPWSTR lpszName,
4118                                   DWORD dwLevel, LPBYTE lpbPrinters,
4119                                   DWORD cbBuf, LPDWORD lpdwNeeded,
4120                                   LPDWORD lpdwReturned)
4121
4122 {
4123     HKEY hkeyPrinters, hkeyPrinter;
4124     WCHAR PrinterName[255];
4125     DWORD needed = 0, number = 0;
4126     DWORD used, i, left;
4127     PBYTE pi, buf;
4128
4129     if(lpbPrinters)
4130         memset(lpbPrinters, 0, cbBuf);
4131     if(lpdwReturned)
4132         *lpdwReturned = 0;
4133     if(lpdwNeeded)
4134         *lpdwNeeded = 0;
4135
4136     /* PRINTER_ENUM_DEFAULT is only supported under win9x, we behave like NT */
4137     if(dwType == PRINTER_ENUM_DEFAULT)
4138         return TRUE;
4139
4140     if (dwType & PRINTER_ENUM_CONNECTIONS) {
4141         TRACE("ignoring PRINTER_ENUM_CONNECTIONS\n");
4142         dwType &= ~PRINTER_ENUM_CONNECTIONS; /* we don't handle that */
4143         if (!dwType) {
4144             FIXME("We don't handle PRINTER_ENUM_CONNECTIONS\n");
4145             return TRUE;
4146         }
4147
4148     }
4149
4150     if (!((dwType & PRINTER_ENUM_LOCAL) || (dwType & PRINTER_ENUM_NAME))) {
4151         FIXME("dwType = %08x\n", dwType);
4152         SetLastError(ERROR_INVALID_FLAGS);
4153         return FALSE;
4154     }
4155
4156     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters) !=
4157        ERROR_SUCCESS) {
4158         ERR("Can't create Printers key\n");
4159         return FALSE;
4160     }
4161
4162     if(RegQueryInfoKeyA(hkeyPrinters, NULL, NULL, NULL, &number, NULL, NULL,
4163                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
4164         RegCloseKey(hkeyPrinters);
4165         ERR("Can't query Printers key\n");
4166         return FALSE;
4167     }
4168     TRACE("Found %d printers\n", number);
4169
4170     switch(dwLevel) {
4171     case 1:
4172         used = number * sizeof(PRINTER_INFO_1W);
4173         break;
4174     case 2:
4175         used = number * sizeof(PRINTER_INFO_2W);
4176         break;
4177     case 4:
4178         used = number * sizeof(PRINTER_INFO_4W);
4179         break;
4180     case 5:
4181         used = number * sizeof(PRINTER_INFO_5W);
4182         break;
4183
4184     default:
4185         SetLastError(ERROR_INVALID_LEVEL);
4186         RegCloseKey(hkeyPrinters);
4187         return FALSE;
4188     }
4189     pi = (used <= cbBuf) ? lpbPrinters : NULL;
4190
4191     for(i = 0; i < number; i++) {
4192         if(RegEnumKeyW(hkeyPrinters, i, PrinterName, sizeof(PrinterName)/sizeof(PrinterName[0])) !=
4193            ERROR_SUCCESS) {
4194             ERR("Can't enum key number %d\n", i);
4195             RegCloseKey(hkeyPrinters);
4196             return FALSE;
4197         }
4198         TRACE("Printer %d is %s\n", i, debugstr_w(PrinterName));
4199         if(RegOpenKeyW(hkeyPrinters, PrinterName, &hkeyPrinter) !=
4200            ERROR_SUCCESS) {
4201             ERR("Can't open key %s\n", debugstr_w(PrinterName));
4202             RegCloseKey(hkeyPrinters);
4203             return FALSE;
4204         }
4205
4206         if(cbBuf > used) {
4207             buf = lpbPrinters + used;
4208             left = cbBuf - used;
4209         } else {
4210             buf = NULL;
4211             left = 0;
4212         }
4213
4214         switch(dwLevel) {
4215         case 1:
4216             WINSPOOL_GetPrinter_1(hkeyPrinter, (PRINTER_INFO_1W *)pi, buf,
4217                                   left, &needed);
4218             used += needed;
4219             if(pi) pi += sizeof(PRINTER_INFO_1W);
4220             break;
4221         case 2:
4222             WINSPOOL_GetPrinter_2(hkeyPrinter, (PRINTER_INFO_2W *)pi, buf,
4223                                   left, &needed);
4224             used += needed;
4225             if(pi) pi += sizeof(PRINTER_INFO_2W);
4226             break;
4227         case 4:
4228             WINSPOOL_GetPrinter_4(hkeyPrinter, (PRINTER_INFO_4W *)pi, buf,
4229                                   left, &needed);
4230             used += needed;
4231             if(pi) pi += sizeof(PRINTER_INFO_4W);
4232             break;
4233         case 5:
4234             WINSPOOL_GetPrinter_5(hkeyPrinter, (PRINTER_INFO_5W *)pi, buf,
4235                                   left, &needed);
4236             used += needed;
4237             if(pi) pi += sizeof(PRINTER_INFO_5W);
4238             break;
4239         default:
4240             ERR("Shouldn't be here!\n");
4241             RegCloseKey(hkeyPrinter);
4242             RegCloseKey(hkeyPrinters);
4243             return FALSE;
4244         }
4245         RegCloseKey(hkeyPrinter);
4246     }
4247     RegCloseKey(hkeyPrinters);
4248
4249     if(lpdwNeeded)
4250         *lpdwNeeded = used;
4251
4252     if(used > cbBuf) {
4253         if(lpbPrinters)
4254             memset(lpbPrinters, 0, cbBuf);
4255         SetLastError(ERROR_INSUFFICIENT_BUFFER);
4256         return FALSE;
4257     }
4258     if(lpdwReturned)
4259         *lpdwReturned = number;
4260     SetLastError(ERROR_SUCCESS);
4261     return TRUE;
4262 }
4263
4264
4265 /******************************************************************
4266  *              EnumPrintersW        [WINSPOOL.@]
4267  *
4268  *    Enumerates the available printers, print servers and print
4269  *    providers, depending on the specified flags, name and level.
4270  *
4271  * RETURNS:
4272  *
4273  *    If level is set to 1:
4274  *      Returns an array of PRINTER_INFO_1 data structures in the
4275  *      lpbPrinters buffer.
4276  *
4277  *    If level is set to 2:
4278  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4279  *      Returns an array of PRINTER_INFO_2 data structures in the
4280  *      lpbPrinters buffer. Note that according to MSDN also an
4281  *      OpenPrinter should be performed on every remote printer.
4282  *
4283  *    If level is set to 4 (officially WinNT only):
4284  *              Possible flags: PRINTER_ENUM_CONNECTIONS, PRINTER_ENUM_LOCAL.
4285  *      Fast: Only the registry is queried to retrieve printer names,
4286  *      no connection to the driver is made.
4287  *      Returns an array of PRINTER_INFO_4 data structures in the
4288  *      lpbPrinters buffer.
4289  *
4290  *    If level is set to 5 (officially WinNT4/Win9x only):
4291  *      Fast: Only the registry is queried to retrieve printer names,
4292  *      no connection to the driver is made.
4293  *      Returns an array of PRINTER_INFO_5 data structures in the
4294  *      lpbPrinters buffer.
4295  *
4296  *    If level set to 3 or 6+:
4297  *          returns zero (failure!)
4298  *
4299  *    Returns nonzero (TRUE) on success, or zero on failure, use GetLastError
4300  *    for information.
4301  *
4302  * BUGS:
4303  *    - Only PRINTER_ENUM_LOCAL and PRINTER_ENUM_NAME are implemented.
4304  *    - Only levels 2, 4 and 5 are implemented at the moment.
4305  *    - 16-bit printer drivers are not enumerated.
4306  *    - Returned amount of bytes used/needed does not match the real Windoze
4307  *      implementation (as in this implementation, all strings are part
4308  *      of the buffer, whereas Win32 keeps them somewhere else)
4309  *    - At level 2, EnumPrinters should also call OpenPrinter for remote printers.
4310  *
4311  * NOTE:
4312  *    - In a regular Wine installation, no registry settings for printers
4313  *      exist, which makes this function return an empty list.
4314  */
4315 BOOL  WINAPI EnumPrintersW(
4316                 DWORD dwType,        /* [in] Types of print objects to enumerate */
4317                 LPWSTR lpszName,     /* [in] name of objects to enumerate */
4318                 DWORD dwLevel,       /* [in] type of printer info structure */
4319                 LPBYTE lpbPrinters,  /* [out] buffer which receives info */
4320                 DWORD cbBuf,         /* [in] max size of buffer in bytes */
4321                 LPDWORD lpdwNeeded,  /* [out] pointer to var: # bytes used/needed */
4322                 LPDWORD lpdwReturned /* [out] number of entries returned */
4323                 )
4324 {
4325     return WINSPOOL_EnumPrintersW(dwType, lpszName, dwLevel, lpbPrinters, cbBuf,
4326                                  lpdwNeeded, lpdwReturned);
4327 }
4328
4329 /******************************************************************
4330  * EnumPrintersA    [WINSPOOL.@]
4331  *
4332  * See EnumPrintersW
4333  *
4334  */
4335 BOOL WINAPI EnumPrintersA(DWORD flags, LPSTR pName, DWORD level, LPBYTE pPrinters,
4336                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
4337 {
4338     BOOL ret;
4339     UNICODE_STRING pNameU;
4340     LPWSTR pNameW;
4341     LPBYTE pPrintersW;
4342
4343     TRACE("(0x%x, %s, %u, %p, %d, %p, %p)\n", flags, debugstr_a(pName), level,
4344                                               pPrinters, cbBuf, pcbNeeded, pcReturned);
4345
4346     pNameW = asciitounicode(&pNameU, pName);
4347
4348     /* Request a buffer with a size, that is big enough for EnumPrintersW.
4349        MS Office need this */
4350     pPrintersW = (pPrinters && cbBuf) ? HeapAlloc(GetProcessHeap(), 0, cbBuf) : NULL;
4351
4352     ret = EnumPrintersW(flags, pNameW, level, pPrintersW, cbBuf, pcbNeeded, pcReturned);
4353
4354     RtlFreeUnicodeString(&pNameU);
4355     if (ret) {
4356         convert_printerinfo_W_to_A(pPrinters, pPrintersW, level, *pcbNeeded, *pcReturned);
4357     }
4358     HeapFree(GetProcessHeap(), 0, pPrintersW);
4359     return ret;
4360 }
4361
4362 /*****************************************************************************
4363  *          WINSPOOL_GetDriverInfoFromReg [internal]
4364  *
4365  *    Enters the information from the registry into the DRIVER_INFO struct
4366  *
4367  * RETURNS
4368  *    zero if the printer driver does not exist in the registry
4369  *    (only if Level > 1) otherwise nonzero
4370  */
4371 static BOOL WINSPOOL_GetDriverInfoFromReg(
4372                             HKEY    hkeyDrivers,
4373                             LPWSTR  DriverName,
4374                             const printenv_t * env,
4375                             DWORD   Level,
4376                             LPBYTE  ptr,            /* DRIVER_INFO */
4377                             LPBYTE  pDriverStrings, /* strings buffer */
4378                             DWORD   cbBuf,          /* size of string buffer */
4379                             LPDWORD pcbNeeded)      /* space needed for str. */
4380 {
4381     DWORD  size, tmp;
4382     HKEY   hkeyDriver;
4383     WCHAR  driverdir[MAX_PATH];
4384     DWORD  dirlen;
4385     LPBYTE strPtr = pDriverStrings;
4386     LPDRIVER_INFO_8W di = (LPDRIVER_INFO_8W) ptr;
4387
4388     TRACE("(%p, %s, %p, %d, %p, %p, %d)\n", hkeyDrivers,
4389           debugstr_w(DriverName), env,
4390           Level, di, pDriverStrings, cbBuf);
4391
4392     if (di) ZeroMemory(di, di_sizeof[Level]);
4393
4394     *pcbNeeded = (lstrlenW(DriverName) + 1) * sizeof(WCHAR);
4395     if (*pcbNeeded <= cbBuf)
4396        strcpyW((LPWSTR)strPtr, DriverName);
4397
4398     /* pName for level 1 has a different offset! */
4399     if (Level == 1) {
4400        if (di) ((LPDRIVER_INFO_1W) di)->pName = (LPWSTR) strPtr;
4401        return TRUE;
4402     }
4403
4404     /* .cVersion and .pName for level > 1 */
4405     if (di) {
4406         di->cVersion = env->driverversion;
4407         di->pName = (LPWSTR) strPtr;
4408         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4409     }
4410
4411     /* Reserve Space for the largest subdir and a Backslash*/
4412     size = sizeof(driverdir) - sizeof(Version3_SubdirW) - sizeof(WCHAR);
4413     if (!GetPrinterDriverDirectoryW(NULL, (LPWSTR) env->envname, 1, (LPBYTE) driverdir, size, &size)) {
4414         /* Should never Fail */
4415         return FALSE;
4416     }
4417     lstrcatW(driverdir, env->versionsubdir);
4418     lstrcatW(driverdir, backslashW);
4419
4420     /* dirlen must not include the terminating zero */
4421     dirlen = lstrlenW(driverdir) * sizeof(WCHAR);
4422
4423     if (!DriverName[0] || RegOpenKeyW(hkeyDrivers, DriverName, &hkeyDriver) != ERROR_SUCCESS) {
4424         ERR("Can't find driver %s in registry\n", debugstr_w(DriverName));
4425         SetLastError(ERROR_UNKNOWN_PRINTER_DRIVER); /* ? */
4426         return FALSE;
4427     }
4428
4429     /* pEnvironment */
4430     size = (lstrlenW(env->envname) + 1) * sizeof(WCHAR);
4431
4432     *pcbNeeded += size;
4433     if (*pcbNeeded <= cbBuf) {
4434         lstrcpyW((LPWSTR)strPtr, env->envname);
4435         if (di) di->pEnvironment = (LPWSTR)strPtr;
4436         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4437     }
4438
4439     /* .pDriverPath is the Graphics rendering engine.
4440         The full Path is required to avoid a crash in some apps */
4441     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, 0, &size)) {
4442         *pcbNeeded += size;
4443         if (*pcbNeeded <= cbBuf)
4444             get_filename_from_reg(hkeyDriver, driverdir, dirlen, DriverW, strPtr, size, &tmp);
4445
4446         if (di) di->pDriverPath = (LPWSTR)strPtr;
4447         strPtr = (pDriverStrings) ? (pDriverStrings + (*pcbNeeded)) : NULL;
4448     }
4449
4450     /* .pDataFile: For postscript-drivers, this is the ppd-file */
4451     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, 0, &size)) {
4452         *pcbNeeded += size;
4453         if (*pcbNeeded <= cbBuf)
4454             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Data_FileW, strPtr, size, &size);
4455
4456         if (di) di->pDataFile = (LPWSTR)strPtr;
4457         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4458     }
4459
4460     /* .pConfigFile is the Driver user Interface */
4461     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, 0, &size)) {
4462         *pcbNeeded += size;
4463         if (*pcbNeeded <= cbBuf)
4464             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Configuration_FileW, strPtr, size, &size);
4465
4466         if (di) di->pConfigFile = (LPWSTR)strPtr;
4467         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4468     }
4469
4470     if (Level == 2 ) {
4471         RegCloseKey(hkeyDriver);
4472         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4473         return TRUE;
4474     }
4475
4476     if (Level == 5 ) {
4477         RegCloseKey(hkeyDriver);
4478         FIXME("level 5: incomplete\n");
4479         return TRUE;
4480     }
4481
4482     /* .pHelpFile */
4483     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, 0, &size)) {
4484         *pcbNeeded += size;
4485         if (*pcbNeeded <= cbBuf)
4486             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Help_FileW, strPtr, size, &size);
4487
4488         if (di) di->pHelpFile = (LPWSTR)strPtr;
4489         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4490     }
4491
4492     /* .pDependentFiles */
4493     if (get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, 0, &size)) {
4494         *pcbNeeded += size;
4495         if (*pcbNeeded <= cbBuf)
4496             get_filename_from_reg(hkeyDriver, driverdir, dirlen, Dependent_FilesW, strPtr, size, &size);
4497
4498         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4499         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4500     }
4501     else if (GetVersion() & 0x80000000) {
4502         /* PowerPoint XP expects that pDependentFiles is always valid on win9x */
4503         size = 2 * sizeof(WCHAR);
4504         *pcbNeeded += size;
4505         if ((*pcbNeeded <= cbBuf) && strPtr) ZeroMemory(strPtr, size);
4506
4507         if (di) di->pDependentFiles = (LPWSTR)strPtr;
4508         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4509     }
4510
4511     /* .pMonitorName is the optional Language Monitor */
4512     if (WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, 0, &size)) {
4513         *pcbNeeded += size;
4514         if (*pcbNeeded <= cbBuf)
4515             WINSPOOL_GetStringFromReg(hkeyDriver, MonitorW, strPtr, size, &size);
4516
4517         if (di) di->pMonitorName = (LPWSTR)strPtr;
4518         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4519     }
4520
4521     /* .pDefaultDataType */
4522     if (WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, 0, &size)) {
4523         *pcbNeeded += size;
4524         if(*pcbNeeded <= cbBuf)
4525             WINSPOOL_GetStringFromReg(hkeyDriver, DatatypeW, strPtr, size, &size);
4526
4527         if (di) di->pDefaultDataType = (LPWSTR)strPtr;
4528         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4529     }
4530
4531     if (Level == 3 ) {
4532         RegCloseKey(hkeyDriver);
4533         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4534         return TRUE;
4535     }
4536
4537     /* .pszzPreviousNames */
4538     if (WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, 0, &size)) {
4539         *pcbNeeded += size;
4540         if(*pcbNeeded <= cbBuf)
4541             WINSPOOL_GetStringFromReg(hkeyDriver, Previous_NamesW, strPtr, size, &size);
4542
4543         if (di) di->pszzPreviousNames = (LPWSTR)strPtr;
4544         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4545     }
4546
4547     if (Level == 4 ) {
4548         RegCloseKey(hkeyDriver);
4549         TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4550         return TRUE;
4551     }
4552
4553     /* support is missing, but not important enough for a FIXME */
4554     TRACE("%s: DriverDate + DriverVersion not supported\n", debugstr_w(DriverName));
4555
4556     /* .pszMfgName */
4557     if (WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, 0, &size)) {
4558         *pcbNeeded += size;
4559         if(*pcbNeeded <= cbBuf)
4560             WINSPOOL_GetStringFromReg(hkeyDriver, ManufacturerW, strPtr, size, &size);
4561
4562         if (di) di->pszMfgName = (LPWSTR)strPtr;
4563         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4564     }
4565
4566     /* .pszOEMUrl */
4567     if (WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, 0, &size)) {
4568         *pcbNeeded += size;
4569         if(*pcbNeeded <= cbBuf)
4570             WINSPOOL_GetStringFromReg(hkeyDriver, OEM_UrlW, strPtr, size, &size);
4571
4572         if (di) di->pszOEMUrl = (LPWSTR)strPtr;
4573         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4574     }
4575
4576     /* .pszHardwareID */
4577     if (WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, 0, &size)) {
4578         *pcbNeeded += size;
4579         if(*pcbNeeded <= cbBuf)
4580             WINSPOOL_GetStringFromReg(hkeyDriver, HardwareIDW, strPtr, size, &size);
4581
4582         if (di) di->pszHardwareID = (LPWSTR)strPtr;
4583         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4584     }
4585
4586     /* .pszProvider */
4587     if (WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, 0, &size)) {
4588         *pcbNeeded += size;
4589         if(*pcbNeeded <= cbBuf)
4590             WINSPOOL_GetStringFromReg(hkeyDriver, ProviderW, strPtr, size, &size);
4591
4592         if (di) di->pszProvider = (LPWSTR)strPtr;
4593         strPtr = (pDriverStrings) ? pDriverStrings + (*pcbNeeded) : NULL;
4594     }
4595
4596     if (Level == 6 ) {
4597         RegCloseKey(hkeyDriver);
4598         return TRUE;
4599     }
4600
4601     /* support is missing, but not important enough for a FIXME */
4602     TRACE("level 8: incomplete\n");
4603
4604     TRACE("buffer space %d required %d\n", cbBuf, *pcbNeeded);
4605     RegCloseKey(hkeyDriver);
4606     return TRUE;
4607 }
4608
4609 /*****************************************************************************
4610  *          GetPrinterDriverW  [WINSPOOL.@]
4611  */
4612 BOOL WINAPI GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment,
4613                                   DWORD Level, LPBYTE pDriverInfo,
4614                                   DWORD cbBuf, LPDWORD pcbNeeded)
4615 {
4616     LPCWSTR name;
4617     WCHAR DriverName[100];
4618     DWORD ret, type, size, needed = 0;
4619     LPBYTE ptr = NULL;
4620     HKEY hkeyPrinter, hkeyDrivers;
4621     const printenv_t * env;
4622
4623     TRACE("(%p,%s,%d,%p,%d,%p)\n",hPrinter,debugstr_w(pEnvironment),
4624           Level,pDriverInfo,cbBuf, pcbNeeded);
4625
4626     if (cbBuf > 0)
4627         ZeroMemory(pDriverInfo, cbBuf);
4628
4629     if (!(name = get_opened_printer_name(hPrinter))) {
4630         SetLastError(ERROR_INVALID_HANDLE);
4631         return FALSE;
4632     }
4633
4634     if (Level < 1 || Level == 7 || Level > 8) {
4635         SetLastError(ERROR_INVALID_LEVEL);
4636         return FALSE;
4637     }
4638
4639     env = validate_envW(pEnvironment);
4640     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
4641
4642     ret = open_printer_reg_key( name, &hkeyPrinter );
4643     if (ret)
4644     {
4645         ERR( "Can't find opened printer %s in registry\n", debugstr_w(name) );
4646         SetLastError( ret );
4647         return FALSE;
4648     }
4649
4650     size = sizeof(DriverName);
4651     DriverName[0] = 0;
4652     ret = RegQueryValueExW(hkeyPrinter, Printer_DriverW, 0, &type,
4653                            (LPBYTE)DriverName, &size);
4654     RegCloseKey(hkeyPrinter);
4655     if(ret != ERROR_SUCCESS) {
4656         ERR("Can't get DriverName for printer %s\n", debugstr_w(name));
4657         return FALSE;
4658     }
4659
4660     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment);
4661     if(!hkeyDrivers) {
4662         ERR("Can't create Drivers key\n");
4663         return FALSE;
4664     }
4665
4666     size = di_sizeof[Level];
4667     if ((size <= cbBuf) && pDriverInfo)
4668         ptr = pDriverInfo + size;
4669
4670     if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverName,
4671                          env, Level, pDriverInfo, ptr,
4672                          (cbBuf < size) ? 0 : cbBuf - size,
4673                          &needed)) {
4674             RegCloseKey(hkeyDrivers);
4675             return FALSE;
4676     }
4677
4678     RegCloseKey(hkeyDrivers);
4679
4680     if(pcbNeeded) *pcbNeeded = size + needed;
4681     TRACE("buffer space %d required %d\n", cbBuf, size + needed);
4682     if(cbBuf >= size + needed) return TRUE;
4683     SetLastError(ERROR_INSUFFICIENT_BUFFER);
4684     return FALSE;
4685 }
4686
4687 /*****************************************************************************
4688  *          GetPrinterDriverA  [WINSPOOL.@]
4689  */
4690 BOOL WINAPI GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment,
4691                               DWORD Level, LPBYTE pDriverInfo,
4692                               DWORD cbBuf, LPDWORD pcbNeeded)
4693 {
4694     BOOL ret;
4695     UNICODE_STRING pEnvW;
4696     PWSTR pwstrEnvW;
4697     LPBYTE buf = NULL;
4698
4699     if (cbBuf)
4700     {
4701         ZeroMemory(pDriverInfo, cbBuf);
4702         buf = HeapAlloc(GetProcessHeap(), 0, cbBuf);
4703     }
4704
4705     pwstrEnvW = asciitounicode(&pEnvW, pEnvironment);
4706     ret = GetPrinterDriverW(hPrinter, pwstrEnvW, Level, buf,
4707                                     cbBuf, pcbNeeded);
4708     if (ret)
4709         convert_driverinfo_W_to_A(pDriverInfo, buf, Level, cbBuf, 1);
4710
4711     HeapFree(GetProcessHeap(), 0, buf);
4712
4713     RtlFreeUnicodeString(&pEnvW);
4714     return ret;
4715 }
4716
4717 /*****************************************************************************
4718  *       GetPrinterDriverDirectoryW  [WINSPOOL.@]
4719  *
4720  * Return the PATH for the Printer-Drivers (UNICODE)
4721  *
4722  * PARAMS
4723  *   pName            [I] Servername (NT only) or NULL (local Computer)
4724  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
4725  *   Level            [I] Structure-Level (must be 1)
4726  *   pDriverDirectory [O] PTR to Buffer that receives the Result
4727  *   cbBuf            [I] Size of Buffer at pDriverDirectory
4728  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used / 
4729  *                        required for pDriverDirectory
4730  *
4731  * RETURNS
4732  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
4733  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
4734  *   if cbBuf is too small
4735  * 
4736  *   Native Values returned in pDriverDirectory on Success:
4737  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86" 
4738  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40" 
4739  *|  win9x(Windows 4.0):  "%winsysdir%" 
4740  *
4741  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
4742  *
4743  * FIXME
4744  *-  Only NULL or "" is supported for pName
4745  *
4746  */
4747 BOOL WINAPI GetPrinterDriverDirectoryW(LPWSTR pName, LPWSTR pEnvironment,
4748                                        DWORD Level, LPBYTE pDriverDirectory,
4749                                        DWORD cbBuf, LPDWORD pcbNeeded)
4750 {
4751     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName), 
4752           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4753
4754     if ((backend == NULL)  && !load_backend()) return FALSE;
4755
4756     if (Level != 1) {
4757         /* (Level != 1) is ignored in win9x */
4758         SetLastError(ERROR_INVALID_LEVEL);
4759         return FALSE;
4760     }
4761     if (pcbNeeded == NULL) {
4762         /* (pcbNeeded == NULL) is ignored in win9x */
4763         SetLastError(RPC_X_NULL_REF_POINTER);
4764         return FALSE;
4765     }
4766
4767     return backend->fpGetPrinterDriverDirectory(pName, pEnvironment, Level,
4768                                                 pDriverDirectory, cbBuf, pcbNeeded);
4769
4770 }
4771
4772
4773 /*****************************************************************************
4774  *       GetPrinterDriverDirectoryA  [WINSPOOL.@]
4775  *
4776  * Return the PATH for the Printer-Drivers (ANSI)
4777  *
4778  * See GetPrinterDriverDirectoryW.
4779  *
4780  * NOTES
4781  * On NT, pDriverDirectory need the same Size as the Unicode-Version
4782  *
4783  */
4784 BOOL WINAPI GetPrinterDriverDirectoryA(LPSTR pName, LPSTR pEnvironment,
4785                                        DWORD Level, LPBYTE pDriverDirectory,
4786                                        DWORD cbBuf, LPDWORD pcbNeeded)
4787 {
4788     UNICODE_STRING nameW, environmentW;
4789     BOOL ret;
4790     DWORD pcbNeededW;
4791     INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
4792     WCHAR *driverDirectoryW = NULL;
4793
4794     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName), 
4795           debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
4796  
4797     if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
4798
4799     if(pName) RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
4800     else nameW.Buffer = NULL;
4801     if(pEnvironment) RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
4802     else environmentW.Buffer = NULL;
4803
4804     ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level,
4805                                       (LPBYTE)driverDirectoryW, len, &pcbNeededW );
4806     if (ret) {
4807         DWORD needed;
4808         needed =  WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, 
4809                                    (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
4810         if(pcbNeeded)
4811             *pcbNeeded = needed;
4812         ret = (needed <= cbBuf) ? TRUE : FALSE;
4813     } else 
4814         if(pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
4815
4816     TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
4817
4818     HeapFree( GetProcessHeap(), 0, driverDirectoryW );
4819     RtlFreeUnicodeString(&environmentW);
4820     RtlFreeUnicodeString(&nameW);
4821
4822     return ret;
4823 }
4824
4825 /*****************************************************************************
4826  *          AddPrinterDriverA  [WINSPOOL.@]
4827  *
4828  * See AddPrinterDriverW.
4829  *
4830  */
4831 BOOL WINAPI AddPrinterDriverA(LPSTR pName, DWORD level, LPBYTE pDriverInfo)
4832 {
4833     TRACE("(%s, %d, %p)\n", debugstr_a(pName), level, pDriverInfo);
4834     return AddPrinterDriverExA(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
4835 }
4836
4837 /******************************************************************************
4838  *  AddPrinterDriverW (WINSPOOL.@)
4839  *
4840  * Install a Printer Driver
4841  *
4842  * PARAMS
4843  *  pName           [I] Servername or NULL (local Computer)
4844  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
4845  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
4846  *
4847  * RESULTS
4848  *  Success: TRUE
4849  *  Failure: FALSE
4850  *
4851  */
4852 BOOL WINAPI AddPrinterDriverW(LPWSTR pName, DWORD level, LPBYTE pDriverInfo)
4853 {
4854     TRACE("(%s, %d, %p)\n", debugstr_w(pName), level, pDriverInfo);
4855     return AddPrinterDriverExW(pName, level, pDriverInfo, APD_COPY_NEW_FILES);
4856 }
4857
4858 /*****************************************************************************
4859  *          AddPrintProcessorA  [WINSPOOL.@]
4860  */
4861 BOOL WINAPI AddPrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPathName,
4862                                LPSTR pPrintProcessorName)
4863 {
4864     FIXME("(%s,%s,%s,%s): stub\n", debugstr_a(pName), debugstr_a(pEnvironment),
4865           debugstr_a(pPathName), debugstr_a(pPrintProcessorName));
4866     return FALSE;
4867 }
4868
4869 /*****************************************************************************
4870  *          AddPrintProcessorW  [WINSPOOL.@]
4871  */
4872 BOOL WINAPI AddPrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPathName,
4873                                LPWSTR pPrintProcessorName)
4874 {
4875     FIXME("(%s,%s,%s,%s): stub\n", debugstr_w(pName), debugstr_w(pEnvironment),
4876           debugstr_w(pPathName), debugstr_w(pPrintProcessorName));
4877     return TRUE;
4878 }
4879
4880 /*****************************************************************************
4881  *          AddPrintProvidorA  [WINSPOOL.@]
4882  */
4883 BOOL WINAPI AddPrintProvidorA(LPSTR pName, DWORD Level, LPBYTE pProviderInfo)
4884 {
4885     FIXME("(%s,0x%08x,%p): stub\n", debugstr_a(pName), Level, pProviderInfo);
4886     return FALSE;
4887 }
4888
4889 /*****************************************************************************
4890  *          AddPrintProvidorW  [WINSPOOL.@]
4891  */
4892 BOOL WINAPI AddPrintProvidorW(LPWSTR pName, DWORD Level, LPBYTE pProviderInfo)
4893 {
4894     FIXME("(%s,0x%08x,%p): stub\n", debugstr_w(pName), Level, pProviderInfo);
4895     return FALSE;
4896 }
4897
4898 /*****************************************************************************
4899  *          AdvancedDocumentPropertiesA  [WINSPOOL.@]
4900  */
4901 LONG WINAPI AdvancedDocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName,
4902                                         PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput)
4903 {
4904     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_a(pDeviceName),
4905           pDevModeOutput, pDevModeInput);
4906     return 0;
4907 }
4908
4909 /*****************************************************************************
4910  *          AdvancedDocumentPropertiesW  [WINSPOOL.@]
4911  */
4912 LONG WINAPI AdvancedDocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName,
4913                                         PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput)
4914 {
4915     FIXME("(%p,%p,%s,%p,%p): stub\n", hWnd, hPrinter, debugstr_w(pDeviceName),
4916           pDevModeOutput, pDevModeInput);
4917     return 0;
4918 }
4919
4920 /*****************************************************************************
4921  *          PrinterProperties  [WINSPOOL.@]
4922  *
4923  *     Displays a dialog to set the properties of the printer.
4924  *
4925  * RETURNS
4926  *     nonzero on success or zero on failure
4927  *
4928  * BUGS
4929  *         implemented as stub only
4930  */
4931 BOOL WINAPI PrinterProperties(HWND hWnd,      /* [in] handle to parent window */
4932                               HANDLE hPrinter /* [in] handle to printer object */
4933 ){
4934     FIXME("(%p,%p): stub\n", hWnd, hPrinter);
4935     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
4936     return FALSE;
4937 }
4938
4939 /*****************************************************************************
4940  *          EnumJobsA [WINSPOOL.@]
4941  *
4942  */
4943 BOOL WINAPI EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4944                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4945                       LPDWORD pcReturned)
4946 {
4947     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4948         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4949     );
4950     if(pcbNeeded) *pcbNeeded = 0;
4951     if(pcReturned) *pcReturned = 0;
4952     return FALSE;
4953 }
4954
4955
4956 /*****************************************************************************
4957  *          EnumJobsW [WINSPOOL.@]
4958  *
4959  */
4960 BOOL WINAPI EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs,
4961                       DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded,
4962                       LPDWORD pcReturned)
4963 {
4964     FIXME("(%p,first=%d,no=%d,level=%d,job=%p,cb=%d,%p,%p), stub!\n",
4965         hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned
4966     );
4967     if(pcbNeeded) *pcbNeeded = 0;
4968     if(pcReturned) *pcReturned = 0;
4969     return FALSE;
4970 }
4971
4972 /*****************************************************************************
4973  *          WINSPOOL_EnumPrinterDrivers [internal]
4974  *
4975  *    Delivers information about all printer drivers installed on the
4976  *    localhost or a given server
4977  *
4978  * RETURNS
4979  *    nonzero on success or zero on failure. If the buffer for the returned
4980  *    information is too small the function will return an error
4981  *
4982  * BUGS
4983  *    - only implemented for localhost, foreign hosts will return an error
4984  */
4985 static BOOL WINSPOOL_EnumPrinterDrivers(LPWSTR pName, LPCWSTR pEnvironment,
4986                                         DWORD Level, LPBYTE pDriverInfo,
4987                                         DWORD driver_index,
4988                                         DWORD cbBuf, LPDWORD pcbNeeded,
4989                                         LPDWORD pcFound, DWORD data_offset)
4990
4991 {   HKEY  hkeyDrivers;
4992     DWORD i, size = 0;
4993     const printenv_t * env;
4994
4995     TRACE("%s,%s,%d,%p,%d,%d,%d\n",
4996           debugstr_w(pName), debugstr_w(pEnvironment),
4997           Level, pDriverInfo, driver_index, cbBuf, data_offset);
4998
4999     env = validate_envW(pEnvironment);
5000     if (!env) return FALSE;     /* SetLastError() is in validate_envW */
5001
5002     *pcFound = 0;
5003
5004     hkeyDrivers = WINSPOOL_OpenDriverReg(pEnvironment);
5005     if(!hkeyDrivers) {
5006         ERR("Can't open Drivers key\n");
5007         return FALSE;
5008     }
5009
5010     if(RegQueryInfoKeyA(hkeyDrivers, NULL, NULL, NULL, pcFound, NULL, NULL,
5011                         NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
5012         RegCloseKey(hkeyDrivers);
5013         ERR("Can't query Drivers key\n");
5014         return FALSE;
5015     }
5016     TRACE("Found %d Drivers\n", *pcFound);
5017
5018     /* get size of single struct
5019      * unicode and ascii structure have the same size
5020      */
5021     size = di_sizeof[Level];
5022
5023     if (data_offset == 0)
5024         data_offset = size * (*pcFound);
5025     *pcbNeeded = data_offset;
5026
5027     for( i = 0; i < *pcFound; i++) {
5028         WCHAR DriverNameW[255];
5029         PBYTE table_ptr = NULL;
5030         PBYTE data_ptr = NULL;
5031         DWORD needed = 0;
5032
5033         if(RegEnumKeyW(hkeyDrivers, i, DriverNameW, sizeof(DriverNameW)/sizeof(DriverNameW[0]))
5034                        != ERROR_SUCCESS) {
5035             ERR("Can't enum key number %d\n", i);
5036             RegCloseKey(hkeyDrivers);
5037             return FALSE;
5038         }
5039
5040         if (pDriverInfo && ((driver_index + i + 1) * size) <= cbBuf)
5041             table_ptr = pDriverInfo + (driver_index + i) * size;
5042         if (pDriverInfo && *pcbNeeded <= cbBuf)
5043             data_ptr = pDriverInfo + *pcbNeeded;
5044
5045         if(!WINSPOOL_GetDriverInfoFromReg(hkeyDrivers, DriverNameW,
5046                          env, Level, table_ptr, data_ptr,
5047                          (cbBuf < *pcbNeeded) ? 0 : cbBuf - *pcbNeeded,
5048                          &needed)) {
5049             RegCloseKey(hkeyDrivers);
5050             return FALSE;
5051         }
5052
5053         *pcbNeeded += needed;
5054     }
5055
5056     RegCloseKey(hkeyDrivers);
5057
5058     if(cbBuf < *pcbNeeded){
5059         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5060         return FALSE;
5061     }
5062
5063     return TRUE;
5064 }
5065
5066 /*****************************************************************************
5067  *          EnumPrinterDriversW  [WINSPOOL.@]
5068  *
5069  *    see function EnumPrinterDrivers for RETURNS, BUGS
5070  */
5071 BOOL WINAPI EnumPrinterDriversW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
5072                                 LPBYTE pDriverInfo, DWORD cbBuf,
5073                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5074 {
5075     static const WCHAR allW[] = {'a','l','l',0};
5076     BOOL ret;
5077     DWORD found;
5078
5079     if ((pcbNeeded == NULL) || (pcReturned == NULL))
5080     {
5081         SetLastError(RPC_X_NULL_REF_POINTER);
5082         return FALSE;
5083     }
5084
5085     /* check for local drivers */
5086     if((pName) && (pName[0])) {
5087         FIXME("remote drivers (%s) not supported!\n", debugstr_w(pName));
5088         SetLastError(ERROR_ACCESS_DENIED);
5089         return FALSE;
5090     }
5091
5092     /* check input parameter */
5093     if ((Level < 1) || (Level == 7) || (Level > 8)) {
5094         SetLastError(ERROR_INVALID_LEVEL);
5095         return FALSE;
5096     }
5097
5098     if(pDriverInfo && cbBuf > 0)
5099         memset( pDriverInfo, 0, cbBuf);
5100
5101     /* Exception:  pull all printers */
5102     if (pEnvironment && !strcmpW(pEnvironment, allW))
5103     {
5104         DWORD i, needed, bufsize = cbBuf;
5105         DWORD total_needed = 0;
5106         DWORD total_found = 0;
5107         DWORD data_offset;
5108
5109         /* Precompute the overall total; we need this to know
5110            where pointers end and data begins (i.e. data_offset) */
5111         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
5112         {
5113             needed = found = 0;
5114             ret = WINSPOOL_EnumPrinterDrivers(pName, all_printenv[i]->envname, Level,
5115                                               NULL, 0, 0, &needed, &found, 0);
5116             if (!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
5117             total_needed += needed;
5118             total_found += found;
5119         }
5120
5121         data_offset = di_sizeof[Level] * total_found;
5122
5123         *pcReturned = 0;
5124         *pcbNeeded = 0;
5125         total_found = 0;
5126         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
5127         {
5128             needed = found = 0;
5129             ret = WINSPOOL_EnumPrinterDrivers(pName, all_printenv[i]->envname, Level,
5130                                               pDriverInfo, total_found, bufsize, &needed, &found, data_offset);
5131             if (!ret && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
5132             else if (ret)
5133                 *pcReturned += found;
5134             *pcbNeeded = needed;
5135             data_offset = needed;
5136             total_found += found;
5137         }
5138         return ret;
5139     }
5140
5141     /* Normal behavior */
5142     ret = WINSPOOL_EnumPrinterDrivers(pName, pEnvironment, Level, pDriverInfo,
5143                                        0, cbBuf, pcbNeeded, &found, 0);
5144     if (ret)
5145         *pcReturned = found;
5146
5147     return ret;
5148 }
5149
5150 /*****************************************************************************
5151  *          EnumPrinterDriversA  [WINSPOOL.@]
5152  *
5153  *    see function EnumPrinterDrivers for RETURNS, BUGS
5154  */
5155 BOOL WINAPI EnumPrinterDriversA(LPSTR pName, LPSTR pEnvironment, DWORD Level,
5156                                 LPBYTE pDriverInfo, DWORD cbBuf,
5157                                 LPDWORD pcbNeeded, LPDWORD pcReturned)
5158 {
5159     BOOL ret;
5160     UNICODE_STRING pNameW, pEnvironmentW;
5161     PWSTR pwstrNameW, pwstrEnvironmentW;
5162     LPBYTE buf = NULL;
5163
5164     if (cbBuf)
5165         buf = HeapAlloc(GetProcessHeap(), 0, cbBuf);
5166
5167     pwstrNameW = asciitounicode(&pNameW, pName);
5168     pwstrEnvironmentW = asciitounicode(&pEnvironmentW, pEnvironment);
5169
5170     ret = EnumPrinterDriversW(pwstrNameW, pwstrEnvironmentW, Level,
5171                                 buf, cbBuf, pcbNeeded, pcReturned);
5172     if (ret)
5173         convert_driverinfo_W_to_A(pDriverInfo, buf, Level, cbBuf, *pcReturned);
5174
5175     HeapFree(GetProcessHeap(), 0, buf);
5176
5177     RtlFreeUnicodeString(&pNameW);
5178     RtlFreeUnicodeString(&pEnvironmentW);
5179
5180     return ret;
5181 }
5182
5183 /******************************************************************************
5184  *              EnumPortsA   (WINSPOOL.@)
5185  *
5186  * See EnumPortsW.
5187  *
5188  */
5189 BOOL WINAPI EnumPortsA( LPSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
5190                         LPDWORD pcbNeeded, LPDWORD pcReturned)
5191 {
5192     BOOL    res;
5193     LPBYTE  bufferW = NULL;
5194     LPWSTR  nameW = NULL;
5195     DWORD   needed = 0;
5196     DWORD   numentries = 0;
5197     INT     len;
5198
5199     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pPorts,
5200           cbBuf, pcbNeeded, pcReturned);
5201
5202     /* convert servername to unicode */
5203     if (pName) {
5204         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
5205         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5206         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
5207     }
5208     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the Ports */
5209     needed = cbBuf * sizeof(WCHAR);    
5210     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
5211     res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5212
5213     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
5214         if (pcbNeeded) needed = *pcbNeeded;
5215         /* HeapReAlloc return NULL, when bufferW was NULL */
5216         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
5217                               HeapAlloc(GetProcessHeap(), 0, needed);
5218
5219         /* Try again with the large Buffer */
5220         res = EnumPortsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
5221     }
5222     needed = pcbNeeded ? *pcbNeeded : 0;
5223     numentries = pcReturned ? *pcReturned : 0;
5224
5225     /*
5226        W2k require the buffersize from EnumPortsW also for EnumPortsA.
5227        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
5228      */
5229     if (res) {
5230         /* EnumPortsW collected all Data. Parse them to calculate ANSI-Size */
5231         DWORD   entrysize = 0;
5232         DWORD   index;
5233         LPSTR   ptr;
5234         LPPORT_INFO_2W pi2w;
5235         LPPORT_INFO_2A pi2a;
5236
5237         needed = 0;
5238         entrysize = (Level == 1) ? sizeof(PORT_INFO_1A) : sizeof(PORT_INFO_2A);
5239
5240         /* First pass: calculate the size for all Entries */
5241         pi2w = (LPPORT_INFO_2W) bufferW;
5242         pi2a = (LPPORT_INFO_2A) pPorts;
5243         index = 0;
5244         while (index < numentries) {
5245             index++;
5246             needed += entrysize;    /* PORT_INFO_?A */
5247             TRACE("%p: parsing #%d (%s)\n", pi2w, index, debugstr_w(pi2w->pPortName));
5248
5249             needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5250                                             NULL, 0, NULL, NULL);
5251             if (Level > 1) {
5252                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5253                                                 NULL, 0, NULL, NULL);
5254                 needed += WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5255                                                 NULL, 0, NULL, NULL);
5256             }
5257             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5258             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5259             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5260         }
5261
5262         /* check for errors and quit on failure */
5263         if (cbBuf < needed) {
5264             SetLastError(ERROR_INSUFFICIENT_BUFFER);
5265             res = FALSE;
5266             goto cleanup;
5267         }
5268         len = entrysize * numentries;       /* room for all PORT_INFO_?A */
5269         ptr = (LPSTR) &pPorts[len];         /* room for strings */
5270         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
5271         pi2w = (LPPORT_INFO_2W) bufferW;
5272         pi2a = (LPPORT_INFO_2A) pPorts;
5273         index = 0;
5274         /* Second Pass: Fill the User Buffer (if we have one) */
5275         while ((index < numentries) && pPorts) {
5276             index++;
5277             TRACE("%p: writing PORT_INFO_%dA #%d\n", pi2a, Level, index);
5278             pi2a->pPortName = ptr;
5279             len = WideCharToMultiByte(CP_ACP, 0, pi2w->pPortName, -1,
5280                                             ptr, cbBuf , NULL, NULL);
5281             ptr += len;
5282             cbBuf -= len;
5283             if (Level > 1) {
5284                 pi2a->pMonitorName = ptr;
5285                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pMonitorName, -1,
5286                                             ptr, cbBuf, NULL, NULL);
5287                 ptr += len;
5288                 cbBuf -= len;
5289
5290                 pi2a->pDescription = ptr;
5291                 len = WideCharToMultiByte(CP_ACP, 0, pi2w->pDescription, -1,
5292                                             ptr, cbBuf, NULL, NULL);
5293                 ptr += len;
5294                 cbBuf -= len;
5295
5296                 pi2a->fPortType = pi2w->fPortType;
5297                 pi2a->Reserved = 0;              /* documented: "must be zero" */
5298                 
5299             }
5300             /* use LPBYTE with entrysize to avoid double code (PORT_INFO_1 + PORT_INFO_2) */
5301             pi2w = (LPPORT_INFO_2W) (((LPBYTE)pi2w) + entrysize);
5302             pi2a = (LPPORT_INFO_2A) (((LPBYTE)pi2a) + entrysize);
5303         }
5304     }
5305
5306 cleanup:
5307     if (pcbNeeded)  *pcbNeeded = needed;
5308     if (pcReturned) *pcReturned = (res) ? numentries : 0;
5309
5310     HeapFree(GetProcessHeap(), 0, nameW);
5311     HeapFree(GetProcessHeap(), 0, bufferW);
5312
5313     TRACE("returning %d with %d (%d byte for %d of %d entries)\n", 
5314             (res), GetLastError(), needed, (res)? numentries : 0, numentries);
5315
5316     return (res);
5317
5318 }
5319
5320 /******************************************************************************
5321  *      EnumPortsW   (WINSPOOL.@)
5322  *
5323  * Enumerate available Ports
5324  *
5325  * PARAMS
5326  *  pName      [I] Servername or NULL (local Computer)
5327  *  Level      [I] Structure-Level (1 or 2)
5328  *  pPorts     [O] PTR to Buffer that receives the Result
5329  *  cbBuf      [I] Size of Buffer at pPorts
5330  *  pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
5331  *  pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
5332  *
5333  * RETURNS
5334  *  Success: TRUE
5335  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
5336  *
5337  */
5338 BOOL WINAPI EnumPortsW(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
5339 {
5340
5341     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
5342           cbBuf, pcbNeeded, pcReturned);
5343
5344     if ((backend == NULL)  && !load_backend()) return FALSE;
5345
5346     /* Level is not checked in win9x */
5347     if (!Level || (Level > 2)) {
5348         WARN("level (%d) is ignored in win9x\n", Level);
5349         SetLastError(ERROR_INVALID_LEVEL);
5350         return FALSE;
5351     }
5352     if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
5353         SetLastError(RPC_X_NULL_REF_POINTER);
5354         return FALSE;
5355     }
5356
5357     return backend->fpEnumPorts(pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
5358 }
5359
5360 /******************************************************************************
5361  *              GetDefaultPrinterW   (WINSPOOL.@)
5362  *
5363  * FIXME
5364  *      This function must read the value from data 'device' of key
5365  *      HCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows
5366  */
5367 BOOL WINAPI GetDefaultPrinterW(LPWSTR name, LPDWORD namesize)
5368 {
5369     BOOL  retval = TRUE;
5370     DWORD insize, len;
5371     WCHAR *buffer, *ptr;
5372
5373     if (!namesize)
5374     {
5375         SetLastError(ERROR_INVALID_PARAMETER);
5376         return FALSE;
5377     }
5378
5379     /* make the buffer big enough for the stuff from the profile/registry,
5380      * the content must fit into the local buffer to compute the correct
5381      * size even if the extern buffer is too small or not given.
5382      * (20 for ,driver,port) */
5383     insize = *namesize;
5384     len = max(100, (insize + 20));
5385     buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR));
5386
5387     if (!GetProfileStringW(windowsW, deviceW, emptyStringW, buffer, len))
5388     {
5389         SetLastError (ERROR_FILE_NOT_FOUND);
5390         retval = FALSE;
5391         goto end;
5392     }
5393     TRACE("%s\n", debugstr_w(buffer));
5394
5395     if ((ptr = strchrW(buffer, ',')) == NULL)
5396     {
5397         SetLastError(ERROR_INVALID_NAME);
5398         retval = FALSE;
5399         goto end;
5400     }
5401
5402     *ptr = 0;
5403     *namesize = strlenW(buffer) + 1;
5404     if(!name || (*namesize > insize))
5405     {
5406         SetLastError(ERROR_INSUFFICIENT_BUFFER);
5407         retval = FALSE;
5408         goto end;
5409     }
5410     strcpyW(name, buffer);
5411
5412 end:
5413     HeapFree( GetProcessHeap(), 0, buffer);
5414     return retval;
5415 }
5416
5417
5418 /******************************************************************************
5419  *              GetDefaultPrinterA   (WINSPOOL.@)
5420  */
5421 BOOL WINAPI GetDefaultPrinterA(LPSTR name, LPDWORD namesize)
5422 {
5423     BOOL  retval = TRUE;
5424     DWORD insize = 0;
5425     WCHAR *bufferW = NULL;
5426
5427     if (!namesize)
5428     {
5429         SetLastError(ERROR_INVALID_PARAMETER);
5430         return FALSE;
5431     }
5432
5433     if(name && *namesize) {
5434         insize = *namesize;
5435         bufferW = HeapAlloc( GetProcessHeap(), 0, insize * sizeof(WCHAR));
5436     }
5437
5438     if(!GetDefaultPrinterW( bufferW, namesize)) {
5439         retval = FALSE;
5440         goto end;
5441     }
5442
5443     *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, name, insize,
5444                                     NULL, NULL);
5445     if (!*namesize)
5446     {
5447         *namesize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
5448         retval = FALSE;
5449     }
5450     TRACE("0x%08x/0x%08x:%s\n", *namesize, insize, debugstr_w(bufferW));
5451
5452 end:
5453     HeapFree( GetProcessHeap(), 0, bufferW);
5454     return retval;
5455 }
5456
5457
5458 /******************************************************************************
5459  *              SetDefaultPrinterW   (WINSPOOL.204)
5460  *
5461  * Set the Name of the Default Printer
5462  *
5463  * PARAMS
5464  *  pszPrinter [I] Name of the Printer or NULL
5465  *
5466  * RETURNS
5467  *  Success:    True
5468  *  Failure:    FALSE
5469  *
5470  * NOTES
5471  *  When the Parameter is NULL or points to an Empty String and
5472  *  a Default Printer was already present, then this Function changes nothing.
5473  *  Without a Default Printer and NULL (or an Empty String) as Parameter,
5474  *  the First enumerated local Printer is used.
5475  *
5476  */
5477 BOOL WINAPI SetDefaultPrinterW(LPCWSTR pszPrinter)
5478 {
5479     WCHAR   default_printer[MAX_PATH];
5480     LPWSTR  buffer = NULL;
5481     HKEY    hreg;
5482     DWORD   size;
5483     DWORD   namelen;
5484     LONG    lres;
5485
5486     TRACE("(%s)\n", debugstr_w(pszPrinter));
5487     if ((pszPrinter == NULL) || (pszPrinter[0] == '\0')) {
5488
5489         default_printer[0] = '\0';
5490         size = sizeof(default_printer)/sizeof(WCHAR);
5491
5492         /* if we have a default Printer, do nothing. */
5493         if (GetDefaultPrinterW(default_printer, &size))
5494             return TRUE;
5495
5496         pszPrinter = NULL;
5497         /* we have no default Printer: search local Printers and use the first */
5498         if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE, PrintersW, 0, KEY_READ, &hreg)) {
5499
5500             default_printer[0] = '\0';
5501             size = sizeof(default_printer)/sizeof(WCHAR);
5502             if (!RegEnumKeyExW(hreg, 0, default_printer, &size, NULL, NULL, NULL, NULL)) {
5503
5504                 pszPrinter = default_printer;
5505                 TRACE("using %s\n", debugstr_w(pszPrinter));
5506             }
5507             RegCloseKey(hreg);
5508         }
5509
5510         if (pszPrinter == NULL) {
5511             TRACE("no local printer found\n");
5512             SetLastError(ERROR_FILE_NOT_FOUND);
5513             return FALSE;
5514         }
5515     }
5516
5517     /* "pszPrinter" is never empty or NULL here. */
5518     namelen = lstrlenW(pszPrinter);
5519     size = namelen + (MAX_PATH * 2) + 3; /* printer,driver,port and a 0 */
5520     buffer = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
5521     if (!buffer ||
5522         (RegOpenKeyExW(HKEY_CURRENT_USER, user_printers_reg_key, 0, KEY_READ, &hreg) != ERROR_SUCCESS)) {
5523         HeapFree(GetProcessHeap(), 0, buffer);
5524         SetLastError(ERROR_FILE_NOT_FOUND);
5525         return FALSE;
5526     }
5527
5528     /* read the devices entry for the printer (driver,port) to build the string for the
5529        default device entry (printer,driver,port) */
5530     memcpy(buffer, pszPrinter, namelen * sizeof(WCHAR));
5531     buffer[namelen] = ',';
5532     namelen++; /* move index to the start of the driver */
5533
5534     size = ((MAX_PATH * 2) + 2) * sizeof(WCHAR); /* driver,port and a 0 */
5535     lres = RegQueryValueExW(hreg, pszPrinter, NULL, NULL, (LPBYTE) (&buffer[namelen]), &size);
5536     if (!lres) {
5537         TRACE("set device to %s\n", debugstr_w(buffer));
5538
5539         if (!WriteProfileStringW(windowsW, deviceW, buffer)) {
5540             TRACE("failed to set the device entry: %d\n", GetLastError());
5541             lres = ERROR_INVALID_PRINTER_NAME;
5542         }
5543
5544         /* remove the next section, when INIFileMapping is implemented */
5545         {
5546             HKEY hdev;
5547             if (!RegCreateKeyW(HKEY_CURRENT_USER, user_default_reg_key, &hdev)) {
5548                 RegSetValueExW(hdev, deviceW, 0, REG_SZ, (LPBYTE)buffer, (lstrlenW(buffer) + 1) * sizeof(WCHAR));
5549                 RegCloseKey(hdev);
5550             }
5551         }
5552     }
5553     else
5554     {
5555         if (lres != ERROR_FILE_NOT_FOUND)
5556             FIXME("RegQueryValueExW failed with %d for %s\n", lres, debugstr_w(pszPrinter));
5557
5558         SetLastError(ERROR_INVALID_PRINTER_NAME);
5559     }
5560
5561     RegCloseKey(hreg);
5562     HeapFree(GetProcessHeap(), 0, buffer);
5563     return (lres == ERROR_SUCCESS);
5564 }
5565
5566 /******************************************************************************
5567  *              SetDefaultPrinterA   (WINSPOOL.202)
5568  *
5569  * See SetDefaultPrinterW.
5570  *
5571  */
5572 BOOL WINAPI SetDefaultPrinterA(LPCSTR pszPrinter)
5573 {
5574     LPWSTR  bufferW = NULL;
5575     BOOL    res;
5576
5577     TRACE("(%s)\n", debugstr_a(pszPrinter));
5578     if(pszPrinter) {
5579         INT len = MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, NULL, 0);
5580         bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
5581         if (bufferW) MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, bufferW, len);
5582     }
5583     res = SetDefaultPrinterW(bufferW);
5584     HeapFree(GetProcessHeap(), 0, bufferW);
5585     return res;
5586 }
5587
5588 /******************************************************************************
5589  *              SetPrinterDataExA   (WINSPOOL.@)
5590  */
5591 DWORD WINAPI SetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5592                                LPCSTR pValueName, DWORD Type,
5593                                LPBYTE pData, DWORD cbData)
5594 {
5595     HKEY hkeyPrinter, hkeySubkey;
5596     DWORD ret;
5597
5598     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_a(pKeyName),
5599           debugstr_a(pValueName), Type, pData, cbData);
5600
5601     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5602        != ERROR_SUCCESS)
5603         return ret;
5604
5605     if((ret = RegCreateKeyA(hkeyPrinter, pKeyName, &hkeySubkey))
5606        != ERROR_SUCCESS) {
5607         ERR("Can't create subkey %s\n", debugstr_a(pKeyName));
5608         RegCloseKey(hkeyPrinter);
5609         return ret;
5610     }
5611     ret = RegSetValueExA(hkeySubkey, pValueName, 0, Type, pData, cbData);
5612     RegCloseKey(hkeySubkey);
5613     RegCloseKey(hkeyPrinter);
5614     return ret;
5615 }
5616
5617 /******************************************************************************
5618  *              SetPrinterDataExW   (WINSPOOL.@)
5619  */
5620 DWORD WINAPI SetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5621                                LPCWSTR pValueName, DWORD Type,
5622                                LPBYTE pData, DWORD cbData)
5623 {
5624     HKEY hkeyPrinter, hkeySubkey;
5625     DWORD ret;
5626
5627     TRACE("(%p, %s, %s %08x, %p, %08x)\n", hPrinter, debugstr_w(pKeyName),
5628           debugstr_w(pValueName), Type, pData, cbData);
5629
5630     if((ret = WINSPOOL_GetOpenedPrinterRegKey(hPrinter, &hkeyPrinter))
5631        != ERROR_SUCCESS)
5632         return ret;
5633
5634     if((ret = RegCreateKeyW(hkeyPrinter, pKeyName, &hkeySubkey))
5635        != ERROR_SUCCESS) {
5636         ERR("Can't create subkey %s\n", debugstr_w(pKeyName));
5637         RegCloseKey(hkeyPrinter);
5638         return ret;
5639     }
5640     ret = RegSetValueExW(hkeySubkey, pValueName, 0, Type, pData, cbData);
5641     RegCloseKey(hkeySubkey);
5642     RegCloseKey(hkeyPrinter);
5643     return ret;
5644 }
5645
5646 /******************************************************************************
5647  *              SetPrinterDataA   (WINSPOOL.@)
5648  */
5649 DWORD WINAPI SetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, DWORD Type,
5650                                LPBYTE pData, DWORD cbData)
5651 {
5652     return SetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, Type,
5653                              pData, cbData);
5654 }
5655
5656 /******************************************************************************
5657  *              SetPrinterDataW   (WINSPOOL.@)
5658  */
5659 DWORD WINAPI SetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, DWORD Type,
5660                              LPBYTE pData, DWORD cbData)
5661 {
5662     return SetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, Type,
5663                              pData, cbData);
5664 }
5665
5666 /******************************************************************************
5667  *              GetPrinterDataExA   (WINSPOOL.@)
5668  */
5669 DWORD WINAPI GetPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
5670                                LPCSTR pValueName, LPDWORD pType,
5671                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5672 {
5673     opened_printer_t *printer;
5674     HKEY hkeyPrinters, hkeyPrinter = 0, hkeySubkey = 0;
5675     DWORD ret;
5676
5677     TRACE("(%p, %s, %s, %p, %p, %u, %p)\n", hPrinter, debugstr_a(pKeyName),
5678             debugstr_a(pValueName), pType, pData, nSize, pcbNeeded);
5679
5680     printer = get_opened_printer(hPrinter);
5681     if(!printer) return ERROR_INVALID_HANDLE;
5682
5683     ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters);
5684     if (ret) return ret;
5685
5686     TRACE("printer->name: %s\n", debugstr_w(printer->name));
5687
5688     if (printer->name) {
5689
5690         ret = RegOpenKeyW(hkeyPrinters, printer->name, &hkeyPrinter);
5691         if (ret) {
5692             RegCloseKey(hkeyPrinters);
5693             return ret;
5694         }
5695         if((ret = RegOpenKeyA(hkeyPrinter, pKeyName, &hkeySubkey)) != ERROR_SUCCESS) {
5696             WARN("Can't open subkey %s: %d\n", debugstr_a(pKeyName), ret);
5697             RegCloseKey(hkeyPrinter);
5698             RegCloseKey(hkeyPrinters);
5699             return ret;
5700         }
5701     }
5702     *pcbNeeded = nSize;
5703     ret = RegQueryValueExA(printer->name ? hkeySubkey : hkeyPrinters, pValueName,
5704                           0, pType, pData, pcbNeeded);
5705
5706     if (!ret && !pData) ret = ERROR_MORE_DATA;
5707
5708     RegCloseKey(hkeySubkey);
5709     RegCloseKey(hkeyPrinter);
5710     RegCloseKey(hkeyPrinters);
5711
5712     TRACE("--> %d\n", ret);
5713     return ret;
5714 }
5715
5716 /******************************************************************************
5717  *              GetPrinterDataExW   (WINSPOOL.@)
5718  */
5719 DWORD WINAPI GetPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5720                                LPCWSTR pValueName, LPDWORD pType,
5721                                LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5722 {
5723     opened_printer_t *printer;
5724     HKEY hkeyPrinters, hkeyPrinter = 0, hkeySubkey = 0;
5725     DWORD ret;
5726
5727     TRACE("(%p, %s, %s, %p, %p, %u, %p)\n", hPrinter, debugstr_w(pKeyName),
5728             debugstr_w(pValueName), pType, pData, nSize, pcbNeeded);
5729
5730     printer = get_opened_printer(hPrinter);
5731     if(!printer) return ERROR_INVALID_HANDLE;
5732
5733     ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, PrintersW, &hkeyPrinters);
5734     if (ret) return ret;
5735
5736     TRACE("printer->name: %s\n", debugstr_w(printer->name));
5737
5738     if (printer->name) {
5739
5740         ret = RegOpenKeyW(hkeyPrinters, printer->name, &hkeyPrinter);
5741         if (ret) {
5742             RegCloseKey(hkeyPrinters);
5743             return ret;
5744         }
5745         if((ret = RegOpenKeyW(hkeyPrinter, pKeyName, &hkeySubkey)) != ERROR_SUCCESS) {
5746             WARN("Can't open subkey %s: %d\n", debugstr_w(pKeyName), ret);
5747             RegCloseKey(hkeyPrinter);
5748             RegCloseKey(hkeyPrinters);
5749             return ret;
5750         }
5751     }
5752     *pcbNeeded = nSize;
5753     ret = RegQueryValueExW(printer->name ? hkeySubkey : hkeyPrinters, pValueName,
5754                           0, pType, pData, pcbNeeded);
5755
5756     if (!ret && !pData) ret = ERROR_MORE_DATA;
5757
5758     RegCloseKey(hkeySubkey);
5759     RegCloseKey(hkeyPrinter);
5760     RegCloseKey(hkeyPrinters);
5761
5762     TRACE("--> %d\n", ret);
5763     return ret;
5764 }
5765
5766 /******************************************************************************
5767  *              GetPrinterDataA   (WINSPOOL.@)
5768  */
5769 DWORD WINAPI GetPrinterDataA(HANDLE hPrinter, LPSTR pValueName, LPDWORD pType,
5770                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5771 {
5772     return GetPrinterDataExA(hPrinter, "PrinterDriverData", pValueName, pType,
5773                              pData, nSize, pcbNeeded);
5774 }
5775
5776 /******************************************************************************
5777  *              GetPrinterDataW   (WINSPOOL.@)
5778  */
5779 DWORD WINAPI GetPrinterDataW(HANDLE hPrinter, LPWSTR pValueName, LPDWORD pType,
5780                              LPBYTE pData, DWORD nSize, LPDWORD pcbNeeded)
5781 {
5782     return GetPrinterDataExW(hPrinter, PrinterDriverDataW, pValueName, pType,
5783                              pData, nSize, pcbNeeded);
5784 }
5785
5786 /*******************************************************************************
5787  *              EnumPrinterDataExW      [WINSPOOL.@]
5788  */
5789 DWORD WINAPI EnumPrinterDataExW(HANDLE hPrinter, LPCWSTR pKeyName,
5790                                 LPBYTE pEnumValues, DWORD cbEnumValues,
5791                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
5792 {
5793     HKEY                    hkPrinter, hkSubKey;
5794     DWORD                   r, ret, dwIndex, cValues, cbMaxValueNameLen,
5795                             cbValueNameLen, cbMaxValueLen, cbValueLen,
5796                             cbBufSize, dwType;
5797     LPWSTR                  lpValueName;
5798     HANDLE                  hHeap;
5799     PBYTE                   lpValue;
5800     PPRINTER_ENUM_VALUESW   ppev;
5801
5802     TRACE ("%p %s\n", hPrinter, debugstr_w (pKeyName));
5803
5804     if (pKeyName == NULL || *pKeyName == 0)
5805         return ERROR_INVALID_PARAMETER;
5806
5807     ret = WINSPOOL_GetOpenedPrinterRegKey (hPrinter, &hkPrinter);
5808     if (ret != ERROR_SUCCESS)
5809     {
5810         TRACE ("WINSPOOL_GetOpenedPrinterRegKey (%p) returned %i\n",
5811                 hPrinter, ret);
5812         return ret;
5813     }
5814
5815     ret = RegOpenKeyExW (hkPrinter, pKeyName, 0, KEY_READ, &hkSubKey);
5816     if (ret != ERROR_SUCCESS)
5817     {
5818         r = RegCloseKey (hkPrinter);
5819         if (r != ERROR_SUCCESS)
5820             WARN ("RegCloseKey returned %i\n", r);
5821         TRACE ("RegOpenKeyExW (%p, %s) returned %i\n", hPrinter,
5822                 debugstr_w (pKeyName), ret);
5823         return ret;
5824     }
5825
5826     ret = RegCloseKey (hkPrinter);
5827     if (ret != ERROR_SUCCESS)
5828     {
5829         ERR ("RegCloseKey returned %i\n", ret);
5830         r = RegCloseKey (hkSubKey);
5831         if (r != ERROR_SUCCESS)
5832             WARN ("RegCloseKey returned %i\n", r);
5833         return ret;
5834     }
5835
5836     ret = RegQueryInfoKeyW (hkSubKey, NULL, NULL, NULL, NULL, NULL, NULL,
5837             &cValues, &cbMaxValueNameLen, &cbMaxValueLen, NULL, NULL);
5838     if (ret != ERROR_SUCCESS)
5839     {
5840         r = RegCloseKey (hkSubKey);
5841         if (r != ERROR_SUCCESS)
5842             WARN ("RegCloseKey returned %i\n", r);
5843         TRACE ("RegQueryInfoKeyW (%p) returned %i\n", hkSubKey, ret);
5844         return ret;
5845     }
5846
5847     TRACE ("RegQueryInfoKeyW returned cValues = %i, cbMaxValueNameLen = %i, "
5848             "cbMaxValueLen = %i\n", cValues, cbMaxValueNameLen, cbMaxValueLen);
5849
5850     if (cValues == 0)                   /* empty key */
5851     {
5852         r = RegCloseKey (hkSubKey);
5853         if (r != ERROR_SUCCESS)
5854             WARN ("RegCloseKey returned %i\n", r);
5855         *pcbEnumValues = *pnEnumValues = 0;
5856         return ERROR_SUCCESS;
5857     }
5858
5859     ++cbMaxValueNameLen;                        /* allow for trailing '\0' */
5860
5861     hHeap = GetProcessHeap ();
5862     if (hHeap == NULL)
5863     {
5864         ERR ("GetProcessHeap failed\n");
5865         r = RegCloseKey (hkSubKey);
5866         if (r != ERROR_SUCCESS)
5867             WARN ("RegCloseKey returned %i\n", r);
5868         return ERROR_OUTOFMEMORY;
5869     }
5870
5871     lpValueName = HeapAlloc (hHeap, 0, cbMaxValueNameLen * sizeof (WCHAR));
5872     if (lpValueName == NULL)
5873     {
5874         ERR ("Failed to allocate %i WCHARs from process heap\n", cbMaxValueNameLen);
5875         r = RegCloseKey (hkSubKey);
5876         if (r != ERROR_SUCCESS)
5877             WARN ("RegCloseKey returned %i\n", r);
5878         return ERROR_OUTOFMEMORY;
5879     }
5880
5881     lpValue = HeapAlloc (hHeap, 0, cbMaxValueLen);
5882     if (lpValue == NULL)
5883     {
5884         ERR ("Failed to allocate %i bytes from process heap\n", cbMaxValueLen);
5885         if (HeapFree (hHeap, 0, lpValueName) == 0)
5886             WARN ("HeapFree failed with code %i\n", GetLastError ());
5887         r = RegCloseKey (hkSubKey);
5888         if (r != ERROR_SUCCESS)
5889             WARN ("RegCloseKey returned %i\n", r);
5890         return ERROR_OUTOFMEMORY;
5891     }
5892
5893     TRACE ("pass 1: calculating buffer required for all names and values\n");
5894
5895     cbBufSize = cValues * sizeof (PRINTER_ENUM_VALUESW);
5896
5897     TRACE ("%i bytes required for %i headers\n", cbBufSize, cValues);
5898
5899     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5900     {
5901         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5902         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5903                 NULL, NULL, lpValue, &cbValueLen);
5904         if (ret != ERROR_SUCCESS)
5905         {
5906             if (HeapFree (hHeap, 0, lpValue) == 0)
5907                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5908             if (HeapFree (hHeap, 0, lpValueName) == 0)
5909                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5910             r = RegCloseKey (hkSubKey);
5911             if (r != ERROR_SUCCESS)
5912                 WARN ("RegCloseKey returned %i\n", r);
5913             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5914             return ret;
5915         }
5916
5917         TRACE ("%s [%i]: name needs %i WCHARs, data needs %i bytes\n",
5918                 debugstr_w (lpValueName), dwIndex,
5919                 cbValueNameLen + 1, cbValueLen);
5920
5921         cbBufSize += (cbValueNameLen + 1) * sizeof (WCHAR);
5922         cbBufSize += cbValueLen;
5923     }
5924
5925     TRACE ("%i bytes required for all %i values\n", cbBufSize, cValues);
5926
5927     *pcbEnumValues = cbBufSize;
5928     *pnEnumValues = cValues;
5929
5930     if (cbEnumValues < cbBufSize)       /* buffer too small */
5931     {
5932         if (HeapFree (hHeap, 0, lpValue) == 0)
5933             WARN ("HeapFree failed with code %i\n", GetLastError ());
5934         if (HeapFree (hHeap, 0, lpValueName) == 0)
5935             WARN ("HeapFree failed with code %i\n", GetLastError ());
5936         r = RegCloseKey (hkSubKey);
5937         if (r != ERROR_SUCCESS)
5938             WARN ("RegCloseKey returned %i\n", r);
5939         TRACE ("%i byte buffer is not large enough\n", cbEnumValues);
5940         return ERROR_MORE_DATA;
5941     }
5942
5943     TRACE ("pass 2: copying all names and values to buffer\n");
5944
5945     ppev = (PPRINTER_ENUM_VALUESW) pEnumValues;         /* array of structs */
5946     pEnumValues += cValues * sizeof (PRINTER_ENUM_VALUESW);
5947
5948     for (dwIndex = 0; dwIndex < cValues; ++dwIndex)
5949     {
5950         cbValueNameLen = cbMaxValueNameLen; cbValueLen = cbMaxValueLen;
5951         ret = RegEnumValueW (hkSubKey, dwIndex, lpValueName, &cbValueNameLen,
5952                 NULL, &dwType, lpValue, &cbValueLen);
5953         if (ret != ERROR_SUCCESS)
5954         {
5955             if (HeapFree (hHeap, 0, lpValue) == 0)
5956                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5957             if (HeapFree (hHeap, 0, lpValueName) == 0)
5958                 WARN ("HeapFree failed with code %i\n", GetLastError ());
5959             r = RegCloseKey (hkSubKey);
5960             if (r != ERROR_SUCCESS)
5961                 WARN ("RegCloseKey returned %i\n", r);
5962             TRACE ("RegEnumValueW (%i) returned %i\n", dwIndex, ret);
5963             return ret;
5964         }
5965
5966         cbValueNameLen = (cbValueNameLen + 1) * sizeof (WCHAR);
5967         memcpy (pEnumValues, lpValueName, cbValueNameLen);
5968         ppev[dwIndex].pValueName = (LPWSTR) pEnumValues;
5969         pEnumValues += cbValueNameLen;
5970
5971         /* return # of *bytes* (including trailing \0), not # of chars */
5972         ppev[dwIndex].cbValueName = cbValueNameLen;
5973
5974         ppev[dwIndex].dwType = dwType;
5975
5976         memcpy (pEnumValues, lpValue, cbValueLen);
5977         ppev[dwIndex].pData = pEnumValues;
5978         pEnumValues += cbValueLen;
5979
5980         ppev[dwIndex].cbData = cbValueLen;
5981
5982         TRACE ("%s [%i]: copied name (%i bytes) and data (%i bytes)\n",
5983                 debugstr_w (lpValueName), dwIndex, cbValueNameLen, cbValueLen);
5984     }
5985
5986     if (HeapFree (hHeap, 0, lpValue) == 0)
5987     {
5988         ret = GetLastError ();
5989         ERR ("HeapFree failed with code %i\n", ret);
5990         if (HeapFree (hHeap, 0, lpValueName) == 0)
5991             WARN ("HeapFree failed with code %i\n", GetLastError ());
5992         r = RegCloseKey (hkSubKey);
5993         if (r != ERROR_SUCCESS)
5994             WARN ("RegCloseKey returned %i\n", r);
5995         return ret;
5996     }
5997
5998     if (HeapFree (hHeap, 0, lpValueName) == 0)
5999     {
6000         ret = GetLastError ();
6001         ERR ("HeapFree failed with code %i\n", ret);
6002         r = RegCloseKey (hkSubKey);
6003         if (r != ERROR_SUCCESS)
6004             WARN ("RegCloseKey returned %i\n", r);
6005         return ret;
6006     }
6007
6008     ret = RegCloseKey (hkSubKey);
6009     if (ret != ERROR_SUCCESS)
6010     {
6011         ERR ("RegCloseKey returned %i\n", ret);
6012         return ret;
6013     }
6014
6015     return ERROR_SUCCESS;
6016 }
6017
6018 /*******************************************************************************
6019  *              EnumPrinterDataExA      [WINSPOOL.@]
6020  *
6021  * This functions returns value names and REG_SZ, REG_EXPAND_SZ, and
6022  * REG_MULTI_SZ values as ASCII strings in Unicode-sized buffers.  This is
6023  * what Windows 2000 SP1 does.
6024  *
6025  */
6026 DWORD WINAPI EnumPrinterDataExA(HANDLE hPrinter, LPCSTR pKeyName,
6027                                 LPBYTE pEnumValues, DWORD cbEnumValues,
6028                                 LPDWORD pcbEnumValues, LPDWORD pnEnumValues)
6029 {
6030     INT     len;
6031     LPWSTR  pKeyNameW;
6032     DWORD   ret, dwIndex, dwBufSize;
6033     HANDLE  hHeap;
6034     LPSTR   pBuffer;
6035
6036     TRACE ("%p %s\n", hPrinter, pKeyName);
6037
6038     if (pKeyName == NULL || *pKeyName == 0)
6039         return ERROR_INVALID_PARAMETER;
6040
6041     len = MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, NULL, 0);
6042     if (len == 0)
6043     {
6044         ret = GetLastError ();
6045         ERR ("MultiByteToWideChar failed with code %i\n", ret);
6046         return ret;
6047     }
6048
6049     hHeap = GetProcessHeap ();
6050     if (hHeap == NULL)
6051     {
6052         ERR ("GetProcessHeap failed\n");
6053         return ERROR_OUTOFMEMORY;
6054     }
6055
6056     pKeyNameW = HeapAlloc (hHeap, 0, len * sizeof (WCHAR));
6057     if (pKeyNameW == NULL)
6058     {
6059         ERR ("Failed to allocate %i bytes from process heap\n",
6060              (LONG)(len * sizeof (WCHAR)));
6061         return ERROR_OUTOFMEMORY;
6062     }
6063
6064     if (MultiByteToWideChar (CP_ACP, 0, pKeyName, -1, pKeyNameW, len) == 0)
6065     {
6066         ret = GetLastError ();
6067         ERR ("MultiByteToWideChar failed with code %i\n", ret);
6068         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6069             WARN ("HeapFree failed with code %i\n", GetLastError ());
6070         return ret;
6071     }
6072
6073     ret = EnumPrinterDataExW (hPrinter, pKeyNameW, pEnumValues, cbEnumValues,
6074             pcbEnumValues, pnEnumValues);
6075     if (ret != ERROR_SUCCESS)
6076     {
6077         if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6078             WARN ("HeapFree failed with code %i\n", GetLastError ());
6079         TRACE ("EnumPrinterDataExW returned %i\n", ret);
6080         return ret;
6081     }
6082
6083     if (HeapFree (hHeap, 0, pKeyNameW) == 0)
6084     {
6085         ret = GetLastError ();
6086         ERR ("HeapFree failed with code %i\n", ret);
6087         return ret;
6088     }
6089
6090     if (*pnEnumValues == 0)     /* empty key */
6091         return ERROR_SUCCESS;
6092
6093     dwBufSize = 0;
6094     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6095     {
6096         PPRINTER_ENUM_VALUESW ppev =
6097                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6098
6099         if (dwBufSize < ppev->cbValueName)
6100             dwBufSize = ppev->cbValueName;
6101
6102         if (dwBufSize < ppev->cbData && (ppev->dwType == REG_SZ ||
6103                 ppev->dwType == REG_EXPAND_SZ || ppev->dwType == REG_MULTI_SZ))
6104             dwBufSize = ppev->cbData;
6105     }
6106
6107     TRACE ("Largest Unicode name or value is %i bytes\n", dwBufSize);
6108
6109     pBuffer = HeapAlloc (hHeap, 0, dwBufSize);
6110     if (pBuffer == NULL)
6111     {
6112         ERR ("Failed to allocate %i bytes from process heap\n", dwBufSize);
6113         return ERROR_OUTOFMEMORY;
6114     }
6115
6116     for (dwIndex = 0; dwIndex < *pnEnumValues; ++dwIndex)
6117     {
6118         PPRINTER_ENUM_VALUESW ppev =
6119                 &((PPRINTER_ENUM_VALUESW) pEnumValues)[dwIndex];
6120
6121         len = WideCharToMultiByte (CP_ACP, 0, ppev->pValueName,
6122                 ppev->cbValueName / sizeof (WCHAR), pBuffer, dwBufSize, NULL,
6123                 NULL);
6124         if (len == 0)
6125         {
6126             ret = GetLastError ();
6127             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6128             if (HeapFree (hHeap, 0, pBuffer) == 0)
6129                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6130             return ret;
6131         }
6132
6133         memcpy (ppev->pValueName, pBuffer, len);
6134
6135         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6136
6137         if (ppev->dwType != REG_SZ && ppev->dwType != REG_EXPAND_SZ &&
6138                 ppev->dwType != REG_MULTI_SZ)
6139             continue;
6140
6141         len = WideCharToMultiByte (CP_ACP, 0, (LPWSTR) ppev->pData,
6142                 ppev->cbData / sizeof (WCHAR), pBuffer, dwBufSize, NULL, NULL);
6143         if (len == 0)
6144         {
6145             ret = GetLastError ();
6146             ERR ("WideCharToMultiByte failed with code %i\n", ret);
6147             if (HeapFree (hHeap, 0, pBuffer) == 0)
6148                 WARN ("HeapFree failed with code %i\n", GetLastError ());
6149             return ret;
6150         }
6151
6152         memcpy (ppev->pData, pBuffer, len);
6153
6154         TRACE ("Converted '%s' from Unicode to ASCII\n", pBuffer);
6155         TRACE ("  (only first string of REG_MULTI_SZ printed)\n");
6156     }
6157
6158     if (HeapFree (hHeap, 0, pBuffer) == 0)
6159     {
6160         ret = GetLastError ();
6161         ERR ("HeapFree failed with code %i\n", ret);
6162         return ret;
6163     }
6164
6165     return ERROR_SUCCESS;
6166 }
6167
6168 /******************************************************************************
6169  *      AbortPrinter (WINSPOOL.@)
6170  */
6171 BOOL WINAPI AbortPrinter( HANDLE hPrinter )
6172 {
6173     FIXME("(%p), stub!\n", hPrinter);
6174     return TRUE;
6175 }
6176
6177 /******************************************************************************
6178  *              AddPortA (WINSPOOL.@)
6179  *
6180  * See AddPortW.
6181  *
6182  */
6183 BOOL WINAPI AddPortA(LPSTR pName, HWND hWnd, LPSTR pMonitorName)
6184 {
6185     LPWSTR  nameW = NULL;
6186     LPWSTR  monitorW = NULL;
6187     DWORD   len;
6188     BOOL    res;
6189
6190     TRACE("(%s, %p, %s)\n",debugstr_a(pName), hWnd, debugstr_a(pMonitorName));
6191
6192     if (pName) {
6193         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6194         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6195         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6196     }
6197
6198     if (pMonitorName) {
6199         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6200         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6201         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6202     }
6203     res = AddPortW(nameW, hWnd, monitorW);
6204     HeapFree(GetProcessHeap(), 0, nameW);
6205     HeapFree(GetProcessHeap(), 0, monitorW);
6206     return res;
6207 }
6208
6209 /******************************************************************************
6210  *      AddPortW (WINSPOOL.@)
6211  *
6212  * Add a Port for a specific Monitor
6213  *
6214  * PARAMS
6215  *  pName        [I] Servername or NULL (local Computer)
6216  *  hWnd         [I] Handle to parent Window for the Dialog-Box
6217  *  pMonitorName [I] Name of the Monitor that manage the Port
6218  *
6219  * RETURNS
6220  *  Success: TRUE
6221  *  Failure: FALSE
6222  *
6223  */
6224 BOOL WINAPI AddPortW(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName)
6225 {
6226     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pMonitorName));
6227
6228     if ((backend == NULL)  && !load_backend()) return FALSE;
6229
6230     if (!pMonitorName) {
6231         SetLastError(RPC_X_NULL_REF_POINTER);
6232         return FALSE;
6233     }
6234
6235     return backend->fpAddPort(pName, hWnd, pMonitorName);
6236 }
6237
6238 /******************************************************************************
6239  *             AddPortExA (WINSPOOL.@)
6240  *
6241  * See AddPortExW.
6242  *
6243  */
6244 BOOL WINAPI AddPortExA(LPSTR pName, DWORD level, LPBYTE pBuffer, LPSTR pMonitorName)
6245 {
6246     PORT_INFO_2W   pi2W;
6247     PORT_INFO_2A * pi2A;
6248     LPWSTR  nameW = NULL;
6249     LPWSTR  monitorW = NULL;
6250     DWORD   len;
6251     BOOL    res;
6252
6253     pi2A = (PORT_INFO_2A *) pBuffer;
6254
6255     TRACE("(%s, %d, %p, %s): %s\n", debugstr_a(pName), level, pBuffer,
6256             debugstr_a(pMonitorName), debugstr_a(pi2A ? pi2A->pPortName : NULL));
6257
6258     if ((level < 1) || (level > 2)) {
6259         SetLastError(ERROR_INVALID_LEVEL);
6260         return FALSE;
6261     }
6262
6263     if (!pi2A) {
6264         SetLastError(ERROR_INVALID_PARAMETER);
6265         return FALSE;
6266     }
6267
6268     if (pName) {
6269         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6270         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6271         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6272     }
6273
6274     if (pMonitorName) {
6275         len = MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, NULL, 0);
6276         monitorW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6277         MultiByteToWideChar(CP_ACP, 0, pMonitorName, -1, monitorW, len);
6278     }
6279
6280     ZeroMemory(&pi2W, sizeof(PORT_INFO_2W));
6281
6282     if (pi2A->pPortName) {
6283         len = MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, NULL, 0);
6284         pi2W.pPortName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6285         MultiByteToWideChar(CP_ACP, 0, pi2A->pPortName, -1, pi2W.pPortName, len);
6286     }
6287
6288     if (level > 1) {
6289         if (pi2A->pMonitorName) {
6290             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, NULL, 0);
6291             pi2W.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6292             MultiByteToWideChar(CP_ACP, 0, pi2A->pMonitorName, -1, pi2W.pMonitorName, len);
6293         }
6294
6295         if (pi2A->pDescription) {
6296             len = MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, NULL, 0);
6297             pi2W.pDescription = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6298             MultiByteToWideChar(CP_ACP, 0, pi2A->pDescription, -1, pi2W.pDescription, len);
6299         }
6300         pi2W.fPortType = pi2A->fPortType;
6301         pi2W.Reserved = pi2A->Reserved;
6302     }
6303
6304     res = AddPortExW(nameW, level, (LPBYTE) &pi2W, monitorW);
6305
6306     HeapFree(GetProcessHeap(), 0, nameW);
6307     HeapFree(GetProcessHeap(), 0, monitorW);
6308     HeapFree(GetProcessHeap(), 0, pi2W.pPortName);
6309     HeapFree(GetProcessHeap(), 0, pi2W.pMonitorName);
6310     HeapFree(GetProcessHeap(), 0, pi2W.pDescription);
6311     return res;
6312
6313 }
6314
6315 /******************************************************************************
6316  *             AddPortExW (WINSPOOL.@)
6317  *
6318  * Add a Port for a specific Monitor, without presenting a user interface
6319  *
6320  * PARAMS
6321  *  pName         [I] Servername or NULL (local Computer)
6322  *  level         [I] Structure-Level (1 or 2) for pBuffer
6323  *  pBuffer       [I] PTR to: PORT_INFO_1 or PORT_INFO_2
6324  *  pMonitorName  [I] Name of the Monitor that manage the Port
6325  *
6326  * RETURNS
6327  *  Success: TRUE
6328  *  Failure: FALSE
6329  *
6330  */
6331 BOOL WINAPI AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
6332 {
6333     PORT_INFO_2W * pi2;
6334
6335     pi2 = (PORT_INFO_2W *) pBuffer;
6336
6337     TRACE("(%s, %d, %p, %s): %s %s %s\n", debugstr_w(pName), level, pBuffer,
6338             debugstr_w(pMonitorName), debugstr_w(pi2 ? pi2->pPortName : NULL),
6339             debugstr_w(((level > 1) && pi2) ? pi2->pMonitorName : NULL),
6340             debugstr_w(((level > 1) && pi2) ? pi2->pDescription : NULL));
6341
6342     if ((backend == NULL)  && !load_backend()) return FALSE;
6343
6344     if ((!pi2) || (!pMonitorName) || (!pMonitorName[0])) {
6345         SetLastError(ERROR_INVALID_PARAMETER);
6346         return FALSE;
6347     }
6348
6349     return backend->fpAddPortEx(pName, level, pBuffer, pMonitorName);
6350 }
6351
6352 /******************************************************************************
6353  *      AddPrinterConnectionA (WINSPOOL.@)
6354  */
6355 BOOL WINAPI AddPrinterConnectionA( LPSTR pName )
6356 {
6357     FIXME("%s\n", debugstr_a(pName));
6358     return FALSE;
6359 }
6360
6361 /******************************************************************************
6362  *      AddPrinterConnectionW (WINSPOOL.@)
6363  */
6364 BOOL WINAPI AddPrinterConnectionW( LPWSTR pName )
6365 {
6366     FIXME("%s\n", debugstr_w(pName));
6367     return FALSE;
6368 }
6369
6370 /******************************************************************************
6371  *  AddPrinterDriverExW (WINSPOOL.@)
6372  *
6373  * Install a Printer Driver with the Option to upgrade / downgrade the Files
6374  *
6375  * PARAMS
6376  *  pName           [I] Servername or NULL (local Computer)
6377  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
6378  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
6379  *  dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
6380  *
6381  * RESULTS
6382  *  Success: TRUE
6383  *  Failure: FALSE
6384  *
6385  */
6386 BOOL WINAPI AddPrinterDriverExW( LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6387 {
6388     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
6389
6390     if ((backend == NULL)  && !load_backend()) return FALSE;
6391
6392     if (level < 2 || level == 5 || level == 7 || level > 8) {
6393         SetLastError(ERROR_INVALID_LEVEL);
6394         return FALSE;
6395     }
6396
6397     if (!pDriverInfo) {
6398         SetLastError(ERROR_INVALID_PARAMETER);
6399         return FALSE;
6400     }
6401
6402     return backend->fpAddPrinterDriverEx(pName, level, pDriverInfo, dwFileCopyFlags);
6403 }
6404
6405 /******************************************************************************
6406  *  AddPrinterDriverExA (WINSPOOL.@)
6407  *
6408  * See AddPrinterDriverExW.
6409  *
6410  */
6411 BOOL WINAPI AddPrinterDriverExA(LPSTR pName, DWORD Level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
6412 {
6413     DRIVER_INFO_8A  *diA;
6414     DRIVER_INFO_8W   diW;
6415     LPWSTR  nameW = NULL;
6416     DWORD   lenA;
6417     DWORD   len;
6418     DWORD   res = FALSE;
6419
6420     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_a(pName), Level, pDriverInfo, dwFileCopyFlags);
6421
6422     diA = (DRIVER_INFO_8A  *) pDriverInfo;
6423     ZeroMemory(&diW, sizeof(diW));
6424
6425     if (Level < 2 || Level == 5 || Level == 7 || Level > 8) {
6426         SetLastError(ERROR_INVALID_LEVEL);
6427         return FALSE;
6428     }
6429
6430     if (diA == NULL) {
6431         SetLastError(ERROR_INVALID_PARAMETER);
6432         return FALSE;
6433     }
6434
6435     /* convert servername to unicode */
6436     if (pName) {
6437         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6438         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6439         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6440     }
6441
6442     /* common fields */
6443     diW.cVersion = diA->cVersion;
6444
6445     if (diA->pName) {
6446         len = MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, NULL, 0);
6447         diW.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6448         MultiByteToWideChar(CP_ACP, 0, diA->pName, -1, diW.pName, len);
6449     }
6450
6451     if (diA->pEnvironment) {
6452         len = MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, NULL, 0);
6453         diW.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6454         MultiByteToWideChar(CP_ACP, 0, diA->pEnvironment, -1, diW.pEnvironment, len);
6455     }
6456
6457     if (diA->pDriverPath) {
6458         len = MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, NULL, 0);
6459         diW.pDriverPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6460         MultiByteToWideChar(CP_ACP, 0, diA->pDriverPath, -1, diW.pDriverPath, len);
6461     }
6462
6463     if (diA->pDataFile) {
6464         len = MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, NULL, 0);
6465         diW.pDataFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6466         MultiByteToWideChar(CP_ACP, 0, diA->pDataFile, -1, diW.pDataFile, len);
6467     }
6468
6469     if (diA->pConfigFile) {
6470         len = MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, NULL, 0);
6471         diW.pConfigFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6472         MultiByteToWideChar(CP_ACP, 0, diA->pConfigFile, -1, diW.pConfigFile, len);
6473     }
6474
6475     if ((Level > 2) && diA->pDependentFiles) {
6476         lenA = multi_sz_lenA(diA->pDependentFiles);
6477         len = MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, NULL, 0);
6478         diW.pDependentFiles = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6479         MultiByteToWideChar(CP_ACP, 0, diA->pDependentFiles, lenA, diW.pDependentFiles, len);
6480     }
6481
6482     if ((Level > 2) && diA->pMonitorName) {
6483         len = MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, NULL, 0);
6484         diW.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6485         MultiByteToWideChar(CP_ACP, 0, diA->pMonitorName, -1, diW.pMonitorName, len);
6486     }
6487
6488     if ((Level > 3) && diA->pDefaultDataType) {
6489         len = MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, NULL, 0);
6490         diW.pDefaultDataType = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6491         MultiByteToWideChar(CP_ACP, 0, diA->pDefaultDataType, -1, diW.pDefaultDataType, len);
6492     }
6493
6494     if ((Level > 3) && diA->pszzPreviousNames) {
6495         lenA = multi_sz_lenA(diA->pszzPreviousNames);
6496         len = MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, NULL, 0);
6497         diW.pszzPreviousNames = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6498         MultiByteToWideChar(CP_ACP, 0, diA->pszzPreviousNames, lenA, diW.pszzPreviousNames, len);
6499     }
6500
6501     if ((Level > 5) && diA->pszMfgName) {
6502         len = MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, NULL, 0);
6503         diW.pszMfgName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6504         MultiByteToWideChar(CP_ACP, 0, diA->pszMfgName, -1, diW.pszMfgName, len);
6505     }
6506
6507     if ((Level > 5) && diA->pszOEMUrl) {
6508         len = MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, NULL, 0);
6509         diW.pszOEMUrl = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6510         MultiByteToWideChar(CP_ACP, 0, diA->pszOEMUrl, -1, diW.pszOEMUrl, len);
6511     }
6512
6513     if ((Level > 5) && diA->pszHardwareID) {
6514         len = MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, NULL, 0);
6515         diW.pszHardwareID = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6516         MultiByteToWideChar(CP_ACP, 0, diA->pszHardwareID, -1, diW.pszHardwareID, len);
6517     }
6518
6519     if ((Level > 5) && diA->pszProvider) {
6520         len = MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, NULL, 0);
6521         diW.pszProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6522         MultiByteToWideChar(CP_ACP, 0, diA->pszProvider, -1, diW.pszProvider, len);
6523     }
6524
6525     if (Level > 7) {
6526         FIXME("level %u is incomplete\n", Level);
6527     }
6528
6529     res = AddPrinterDriverExW(nameW, Level, (LPBYTE) &diW, dwFileCopyFlags);
6530     TRACE("got %u with %u\n", res, GetLastError());
6531     HeapFree(GetProcessHeap(), 0, nameW);
6532     HeapFree(GetProcessHeap(), 0, diW.pName);
6533     HeapFree(GetProcessHeap(), 0, diW.pEnvironment);
6534     HeapFree(GetProcessHeap(), 0, diW.pDriverPath);
6535     HeapFree(GetProcessHeap(), 0, diW.pDataFile);
6536     HeapFree(GetProcessHeap(), 0, diW.pConfigFile);
6537     HeapFree(GetProcessHeap(), 0, diW.pDependentFiles);
6538     HeapFree(GetProcessHeap(), 0, diW.pMonitorName);
6539     HeapFree(GetProcessHeap(), 0, diW.pDefaultDataType);
6540     HeapFree(GetProcessHeap(), 0, diW.pszzPreviousNames);
6541     HeapFree(GetProcessHeap(), 0, diW.pszMfgName);
6542     HeapFree(GetProcessHeap(), 0, diW.pszOEMUrl);
6543     HeapFree(GetProcessHeap(), 0, diW.pszHardwareID);
6544     HeapFree(GetProcessHeap(), 0, diW.pszProvider);
6545
6546     TRACE("=> %u with %u\n", res, GetLastError());
6547     return res;
6548 }
6549
6550 /******************************************************************************
6551  *      ConfigurePortA (WINSPOOL.@)
6552  *
6553  * See ConfigurePortW.
6554  *
6555  */
6556 BOOL WINAPI ConfigurePortA(LPSTR pName, HWND hWnd, LPSTR pPortName)
6557 {
6558     LPWSTR  nameW = NULL;
6559     LPWSTR  portW = NULL;
6560     INT     len;
6561     DWORD   res;
6562
6563     TRACE("(%s, %p, %s)\n", debugstr_a(pName), hWnd, debugstr_a(pPortName));
6564
6565     /* convert servername to unicode */
6566     if (pName) {
6567         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6568         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6569         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6570     }
6571
6572     /* convert portname to unicode */
6573     if (pPortName) {
6574         len = MultiByteToWideChar(CP_ACP, 0, pPortName, -1, NULL, 0);
6575         portW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6576         MultiByteToWideChar(CP_ACP, 0, pPortName, -1, portW, len);
6577     }
6578
6579     res = ConfigurePortW(nameW, hWnd, portW);
6580     HeapFree(GetProcessHeap(), 0, nameW);
6581     HeapFree(GetProcessHeap(), 0, portW);
6582     return res;
6583 }
6584
6585 /******************************************************************************
6586  *      ConfigurePortW (WINSPOOL.@)
6587  *
6588  * Display the Configuration-Dialog for a specific Port
6589  *
6590  * PARAMS
6591  *  pName     [I] Servername or NULL (local Computer)
6592  *  hWnd      [I] Handle to parent Window for the Dialog-Box
6593  *  pPortName [I] Name of the Port, that should be configured
6594  *
6595  * RETURNS
6596  *  Success: TRUE
6597  *  Failure: FALSE
6598  *
6599  */
6600 BOOL WINAPI ConfigurePortW(LPWSTR pName, HWND hWnd, LPWSTR pPortName)
6601 {
6602
6603     TRACE("(%s, %p, %s)\n", debugstr_w(pName), hWnd, debugstr_w(pPortName));
6604
6605     if ((backend == NULL)  && !load_backend()) return FALSE;
6606
6607     if (!pPortName) {
6608         SetLastError(RPC_X_NULL_REF_POINTER);
6609         return FALSE;
6610     }
6611
6612     return backend->fpConfigurePort(pName, hWnd, pPortName);
6613 }
6614
6615 /******************************************************************************
6616  *      ConnectToPrinterDlg (WINSPOOL.@)
6617  */
6618 HANDLE WINAPI ConnectToPrinterDlg( HWND hWnd, DWORD Flags )
6619 {
6620     FIXME("%p %x\n", hWnd, Flags);
6621     return NULL;
6622 }
6623
6624 /******************************************************************************
6625  *      DeletePrinterConnectionA (WINSPOOL.@)
6626  */
6627 BOOL WINAPI DeletePrinterConnectionA( LPSTR pName )
6628 {
6629     FIXME("%s\n", debugstr_a(pName));
6630     return TRUE;
6631 }
6632
6633 /******************************************************************************
6634  *      DeletePrinterConnectionW (WINSPOOL.@)
6635  */
6636 BOOL WINAPI DeletePrinterConnectionW( LPWSTR pName )
6637 {
6638     FIXME("%s\n", debugstr_w(pName));
6639     return TRUE;
6640 }
6641
6642 /******************************************************************************
6643  *              DeletePrinterDriverExW (WINSPOOL.@)
6644  */
6645 BOOL WINAPI DeletePrinterDriverExW( LPWSTR pName, LPWSTR pEnvironment,
6646     LPWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6647 {
6648     HKEY hkey_drivers;
6649     BOOL ret = FALSE;
6650
6651     TRACE("%s %s %s %x %x\n", debugstr_w(pName), debugstr_w(pEnvironment),
6652           debugstr_w(pDriverName), dwDeleteFlag, dwVersionFlag);
6653
6654     if(pName && pName[0])
6655     {
6656         FIXME("pName = %s - unsupported\n", debugstr_w(pName));
6657         SetLastError(ERROR_INVALID_PARAMETER);
6658         return FALSE;
6659     }
6660
6661     if(dwDeleteFlag)
6662     {
6663         FIXME("dwDeleteFlag = %x - unsupported\n", dwDeleteFlag);
6664         SetLastError(ERROR_INVALID_PARAMETER);
6665         return FALSE;
6666     }
6667
6668     hkey_drivers = WINSPOOL_OpenDriverReg(pEnvironment);
6669
6670     if(!hkey_drivers)
6671     {
6672         ERR("Can't open drivers key\n");
6673         return FALSE;
6674     }
6675
6676     if(RegDeleteTreeW(hkey_drivers, pDriverName) == ERROR_SUCCESS)
6677         ret = TRUE;
6678
6679     RegCloseKey(hkey_drivers);
6680
6681     return ret;
6682 }
6683
6684 /******************************************************************************
6685  *              DeletePrinterDriverExA (WINSPOOL.@)
6686  */
6687 BOOL WINAPI DeletePrinterDriverExA( LPSTR pName, LPSTR pEnvironment,
6688     LPSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
6689 {
6690     UNICODE_STRING NameW, EnvW, DriverW;
6691     BOOL ret;
6692
6693     asciitounicode(&NameW, pName);
6694     asciitounicode(&EnvW, pEnvironment);
6695     asciitounicode(&DriverW, pDriverName);
6696
6697     ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
6698
6699     RtlFreeUnicodeString(&DriverW);
6700     RtlFreeUnicodeString(&EnvW);
6701     RtlFreeUnicodeString(&NameW);
6702
6703     return ret;
6704 }
6705
6706 /******************************************************************************
6707  *              DeletePrinterDataExW (WINSPOOL.@)
6708  */
6709 DWORD WINAPI DeletePrinterDataExW( HANDLE hPrinter, LPCWSTR pKeyName,
6710                                   LPCWSTR pValueName)
6711 {
6712     FIXME("%p %s %s\n", hPrinter, 
6713           debugstr_w(pKeyName), debugstr_w(pValueName));
6714     return ERROR_INVALID_PARAMETER;
6715 }
6716
6717 /******************************************************************************
6718  *              DeletePrinterDataExA (WINSPOOL.@)
6719  */
6720 DWORD WINAPI DeletePrinterDataExA( HANDLE hPrinter, LPCSTR pKeyName,
6721                                   LPCSTR pValueName)
6722 {
6723     FIXME("%p %s %s\n", hPrinter, 
6724           debugstr_a(pKeyName), debugstr_a(pValueName));
6725     return ERROR_INVALID_PARAMETER;
6726 }
6727
6728 /******************************************************************************
6729  *      DeletePrintProcessorA (WINSPOOL.@)
6730  */
6731 BOOL WINAPI DeletePrintProcessorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProcessorName)
6732 {
6733     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6734           debugstr_a(pPrintProcessorName));
6735     return TRUE;
6736 }
6737
6738 /******************************************************************************
6739  *      DeletePrintProcessorW (WINSPOOL.@)
6740  */
6741 BOOL WINAPI DeletePrintProcessorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProcessorName)
6742 {
6743     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6744           debugstr_w(pPrintProcessorName));
6745     return TRUE;
6746 }
6747
6748 /******************************************************************************
6749  *      DeletePrintProvidorA (WINSPOOL.@)
6750  */
6751 BOOL WINAPI DeletePrintProvidorA(LPSTR pName, LPSTR pEnvironment, LPSTR pPrintProviderName)
6752 {
6753     FIXME("%s %s %s\n", debugstr_a(pName), debugstr_a(pEnvironment),
6754           debugstr_a(pPrintProviderName));
6755     return TRUE;
6756 }
6757
6758 /******************************************************************************
6759  *      DeletePrintProvidorW (WINSPOOL.@)
6760  */
6761 BOOL WINAPI DeletePrintProvidorW(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pPrintProviderName)
6762 {
6763     FIXME("%s %s %s\n", debugstr_w(pName), debugstr_w(pEnvironment),
6764           debugstr_w(pPrintProviderName));
6765     return TRUE;
6766 }
6767
6768 /******************************************************************************
6769  *      EnumFormsA (WINSPOOL.@)
6770  */
6771 BOOL WINAPI EnumFormsA( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6772     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6773 {
6774     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6775     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6776     return FALSE;
6777 }
6778
6779 /******************************************************************************
6780  *      EnumFormsW (WINSPOOL.@)
6781  */
6782 BOOL WINAPI EnumFormsW( HANDLE hPrinter, DWORD Level, LPBYTE pForm,
6783     DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned )
6784 {
6785     FIXME("%p %x %p %x %p %p\n", hPrinter, Level, pForm, cbBuf, pcbNeeded, pcReturned);
6786     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
6787     return FALSE;
6788 }
6789
6790 /*****************************************************************************
6791  *          EnumMonitorsA [WINSPOOL.@]
6792  *
6793  * See EnumMonitorsW.
6794  *
6795  */
6796 BOOL WINAPI EnumMonitorsA(LPSTR pName, DWORD Level, LPBYTE pMonitors,
6797                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6798 {
6799     BOOL    res;
6800     LPBYTE  bufferW = NULL;
6801     LPWSTR  nameW = NULL;
6802     DWORD   needed = 0;
6803     DWORD   numentries = 0;
6804     INT     len;
6805
6806     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), Level, pMonitors,
6807           cbBuf, pcbNeeded, pcReturned);
6808
6809     /* convert servername to unicode */
6810     if (pName) {
6811         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
6812         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
6813         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
6814     }
6815     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
6816     needed = cbBuf * sizeof(WCHAR);    
6817     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
6818     res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6819
6820     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
6821         if (pcbNeeded) needed = *pcbNeeded;
6822         /* HeapReAlloc return NULL, when bufferW was NULL */
6823         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
6824                               HeapAlloc(GetProcessHeap(), 0, needed);
6825
6826         /* Try again with the large Buffer */
6827         res = EnumMonitorsW(nameW, Level, bufferW, needed, pcbNeeded, pcReturned);
6828     }
6829     numentries = pcReturned ? *pcReturned : 0;
6830     needed = 0;
6831     /*
6832        W2k require the buffersize from EnumMonitorsW also for EnumMonitorsA.
6833        We use the smaller Ansi-Size to avoid conflicts with fixed Buffers of old Apps.
6834      */
6835     if (res) {
6836         /* EnumMonitorsW collected all Data. Parse them to calculate ANSI-Size */
6837         DWORD   entrysize = 0;
6838         DWORD   index;
6839         LPSTR   ptr;
6840         LPMONITOR_INFO_2W mi2w;
6841         LPMONITOR_INFO_2A mi2a;
6842
6843         /* MONITOR_INFO_*W and MONITOR_INFO_*A have the same size */
6844         entrysize = (Level == 1) ? sizeof(MONITOR_INFO_1A) : sizeof(MONITOR_INFO_2A);
6845
6846         /* First pass: calculate the size for all Entries */
6847         mi2w = (LPMONITOR_INFO_2W) bufferW;
6848         mi2a = (LPMONITOR_INFO_2A) pMonitors;
6849         index = 0;
6850         while (index < numentries) {
6851             index++;
6852             needed += entrysize;    /* MONITOR_INFO_?A */
6853             TRACE("%p: parsing #%d (%s)\n", mi2w, index, debugstr_w(mi2w->pName));
6854
6855             needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6856                                             NULL, 0, NULL, NULL);
6857             if (Level > 1) {
6858                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6859                                                 NULL, 0, NULL, NULL);
6860                 needed += WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6861                                                 NULL, 0, NULL, NULL);
6862             }
6863             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6864             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6865             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6866         }
6867
6868         /* check for errors and quit on failure */
6869         if (cbBuf < needed) {
6870             SetLastError(ERROR_INSUFFICIENT_BUFFER);
6871             res = FALSE;
6872             goto emA_cleanup;
6873         }
6874         len = entrysize * numentries;       /* room for all MONITOR_INFO_?A */
6875         ptr = (LPSTR) &pMonitors[len];      /* room for strings */
6876         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
6877         mi2w = (LPMONITOR_INFO_2W) bufferW;
6878         mi2a = (LPMONITOR_INFO_2A) pMonitors;
6879         index = 0;
6880         /* Second Pass: Fill the User Buffer (if we have one) */
6881         while ((index < numentries) && pMonitors) {
6882             index++;
6883             TRACE("%p: writing MONITOR_INFO_%dA #%d\n", mi2a, Level, index);
6884             mi2a->pName = ptr;
6885             len = WideCharToMultiByte(CP_ACP, 0, mi2w->pName, -1,
6886                                             ptr, cbBuf , NULL, NULL);
6887             ptr += len;
6888             cbBuf -= len;
6889             if (Level > 1) {
6890                 mi2a->pEnvironment = ptr;
6891                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pEnvironment, -1,
6892                                             ptr, cbBuf, NULL, NULL);
6893                 ptr += len;
6894                 cbBuf -= len;
6895
6896                 mi2a->pDLLName = ptr;
6897                 len = WideCharToMultiByte(CP_ACP, 0, mi2w->pDLLName, -1,
6898                                             ptr, cbBuf, NULL, NULL);
6899                 ptr += len;
6900                 cbBuf -= len;
6901             }
6902             /* use LPBYTE with entrysize to avoid double code (MONITOR_INFO_1 + MONITOR_INFO_2) */
6903             mi2w = (LPMONITOR_INFO_2W) (((LPBYTE)mi2w) + entrysize);
6904             mi2a = (LPMONITOR_INFO_2A) (((LPBYTE)mi2a) + entrysize);
6905         }
6906     }
6907 emA_cleanup:
6908     if (pcbNeeded)  *pcbNeeded = needed;
6909     if (pcReturned) *pcReturned = (res) ? numentries : 0;
6910
6911     HeapFree(GetProcessHeap(), 0, nameW);
6912     HeapFree(GetProcessHeap(), 0, bufferW);
6913
6914     TRACE("returning %d with %d (%d byte for %d entries)\n", 
6915             (res), GetLastError(), needed, numentries);
6916
6917     return (res);
6918
6919 }
6920
6921 /*****************************************************************************
6922  *          EnumMonitorsW [WINSPOOL.@]
6923  *
6924  * Enumerate available Port-Monitors
6925  *
6926  * PARAMS
6927  *  pName       [I] Servername or NULL (local Computer)
6928  *  Level       [I] Structure-Level (1:Win9x+NT or 2:NT only)
6929  *  pMonitors   [O] PTR to Buffer that receives the Result
6930  *  cbBuf       [I] Size of Buffer at pMonitors
6931  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
6932  *  pcReturned  [O] PTR to DWORD that receives the number of Monitors in pMonitors
6933  *
6934  * RETURNS
6935  *  Success: TRUE
6936  *  Failure: FALSE and in pcbNeeded the Bytes required for buffer, if cbBuf is too small
6937  *
6938  */
6939 BOOL WINAPI EnumMonitorsW(LPWSTR pName, DWORD Level, LPBYTE pMonitors,
6940                           DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
6941 {
6942
6943     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
6944           cbBuf, pcbNeeded, pcReturned);
6945
6946     if ((backend == NULL)  && !load_backend()) return FALSE;
6947
6948     if (!pcbNeeded || !pcReturned || (!pMonitors && (cbBuf > 0))) {
6949         SetLastError(RPC_X_NULL_REF_POINTER);
6950         return FALSE;
6951     }
6952
6953     return backend->fpEnumMonitors(pName, Level, pMonitors, cbBuf, pcbNeeded, pcReturned);
6954 }
6955
6956 /******************************************************************************
6957  * SpoolerInit (WINSPOOL.@)
6958  *
6959  * Initialize the Spooler
6960  *
6961  * RETURNS
6962  *  Success: TRUE
6963  *  Failure: FALSE
6964  *
6965  * NOTES
6966  *  The function fails on windows, when the spooler service is not running
6967  *
6968  */
6969 BOOL WINAPI SpoolerInit(void)
6970 {
6971
6972     if ((backend == NULL)  && !load_backend()) return FALSE;
6973     return TRUE;
6974 }
6975
6976 /******************************************************************************
6977  *              XcvDataW (WINSPOOL.@)
6978  *
6979  * Execute commands in the Printmonitor DLL
6980  *
6981  * PARAMS
6982  *  hXcv            [i] Handle from OpenPrinter (with XcvMonitor or XcvPort)
6983  *  pszDataName     [i] Name of the command to execute
6984  *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
6985  *  cbInputData     [i] Size in Bytes of Buffer at pInputData
6986  *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
6987  *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
6988  *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
6989  *  pdwStatus       [o] PTR to receive the win32 error code from the Printmonitor DLL
6990  *
6991  * RETURNS
6992  *  Success: TRUE
6993  *  Failure: FALSE
6994  *
6995  * NOTES
6996  *  Returning "TRUE" does mean, that the Printmonitor DLL was called successful.
6997  *  The execution of the command can still fail (check pdwStatus for ERROR_SUCCESS).
6998  *
6999  *  Minimal List of commands, that a Printmonitor DLL should support:
7000  *
7001  *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
7002  *| "AddPort"   : Add a Port
7003  *| "DeletePort": Delete a Port
7004  *
7005  *  Many Printmonitors support additional commands. Examples for localspl.dll:
7006  *  "GetDefaultCommConfig", "SetDefaultCommConfig",
7007  *  "GetTransmissionRetryTimeout", "ConfigureLPTPortCommandOK"
7008  *
7009  */
7010 BOOL WINAPI XcvDataW( HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData,
7011     DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData,
7012     PDWORD pcbOutputNeeded, PDWORD pdwStatus)
7013 {
7014     opened_printer_t *printer;
7015
7016     TRACE("(%p, %s, %p, %d, %p, %d, %p, %p)\n", hXcv, debugstr_w(pszDataName),
7017           pInputData, cbInputData, pOutputData,
7018           cbOutputData, pcbOutputNeeded, pdwStatus);
7019
7020     if ((backend == NULL)  && !load_backend()) return FALSE;
7021
7022     printer = get_opened_printer(hXcv);
7023     if (!printer || (!printer->backend_printer)) {
7024         SetLastError(ERROR_INVALID_HANDLE);
7025         return FALSE;
7026     }
7027
7028     if (!pcbOutputNeeded) {
7029         SetLastError(ERROR_INVALID_PARAMETER);
7030         return FALSE;
7031     }
7032
7033     if (!pszDataName || !pdwStatus || (!pOutputData && (cbOutputData > 0))) {
7034         SetLastError(RPC_X_NULL_REF_POINTER);
7035         return FALSE;
7036     }
7037
7038     *pcbOutputNeeded = 0;
7039
7040     return backend->fpXcvData(printer->backend_printer, pszDataName, pInputData,
7041                     cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
7042
7043 }
7044
7045 /*****************************************************************************
7046  *          EnumPrinterDataA [WINSPOOL.@]
7047  *
7048  */
7049 DWORD WINAPI EnumPrinterDataA( HANDLE hPrinter, DWORD dwIndex, LPSTR pValueName,
7050     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7051     DWORD cbData, LPDWORD pcbData )
7052 {
7053     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7054           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7055     return ERROR_NO_MORE_ITEMS;
7056 }
7057
7058 /*****************************************************************************
7059  *          EnumPrinterDataW [WINSPOOL.@]
7060  *
7061  */
7062 DWORD WINAPI EnumPrinterDataW( HANDLE hPrinter, DWORD dwIndex, LPWSTR pValueName,
7063     DWORD cbValueName, LPDWORD pcbValueName, LPDWORD pType, LPBYTE pData,
7064     DWORD cbData, LPDWORD pcbData )
7065 {
7066     FIXME("%p %x %p %x %p %p %p %x %p\n", hPrinter, dwIndex, pValueName,
7067           cbValueName, pcbValueName, pType, pData, cbData, pcbData);
7068     return ERROR_NO_MORE_ITEMS;
7069 }
7070
7071 /*****************************************************************************
7072  *          EnumPrintProcessorDatatypesA [WINSPOOL.@]
7073  *
7074  */
7075 BOOL WINAPI EnumPrintProcessorDatatypesA(LPSTR pName, LPSTR pPrintProcessorName,
7076                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7077                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7078 {
7079     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_a(pName),
7080           debugstr_a(pPrintProcessorName), Level, pDatatypes, cbBuf,
7081           pcbNeeded, pcReturned);
7082     return FALSE;
7083 }
7084
7085 /*****************************************************************************
7086  *          EnumPrintProcessorDatatypesW [WINSPOOL.@]
7087  *
7088  */
7089 BOOL WINAPI EnumPrintProcessorDatatypesW(LPWSTR pName, LPWSTR pPrintProcessorName,
7090                                          DWORD Level, LPBYTE pDatatypes, DWORD cbBuf,
7091                                          LPDWORD pcbNeeded, LPDWORD pcReturned)
7092 {
7093     FIXME("Stub: %s %s %d %p %d %p %p\n", debugstr_w(pName),
7094           debugstr_w(pPrintProcessorName), Level, pDatatypes, cbBuf,
7095           pcbNeeded, pcReturned);
7096     return FALSE;
7097 }
7098
7099 /*****************************************************************************
7100  *          EnumPrintProcessorsA [WINSPOOL.@]
7101  *
7102  * See EnumPrintProcessorsW.
7103  *
7104  */
7105 BOOL WINAPI EnumPrintProcessorsA(LPSTR pName, LPSTR pEnvironment, DWORD Level, 
7106                             LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7107 {
7108     BOOL    res;
7109     LPBYTE  bufferW = NULL;
7110     LPWSTR  nameW = NULL;
7111     LPWSTR  envW = NULL;
7112     DWORD   needed = 0;
7113     DWORD   numentries = 0;
7114     INT     len;
7115
7116     TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_a(pName), debugstr_a(pEnvironment),
7117                 Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
7118
7119     /* convert names to unicode */
7120     if (pName) {
7121         len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
7122         nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7123         MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
7124     }
7125     if (pEnvironment) {
7126         len = MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, NULL, 0);
7127         envW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
7128         MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, envW, len);
7129     }
7130
7131     /* alloc (userbuffersize*sizeof(WCHAR) and try to enum the monitors */
7132     needed = cbBuf * sizeof(WCHAR);
7133     if (needed) bufferW = HeapAlloc(GetProcessHeap(), 0, needed);
7134     res = EnumPrintProcessorsW(nameW, envW, Level, bufferW, needed, pcbNeeded, pcReturned);
7135
7136     if(!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
7137         if (pcbNeeded) needed = *pcbNeeded;
7138         /* HeapReAlloc return NULL, when bufferW was NULL */
7139         bufferW = (bufferW) ? HeapReAlloc(GetProcessHeap(), 0, bufferW, needed) :
7140                               HeapAlloc(GetProcessHeap(), 0, needed);
7141
7142         /* Try again with the large Buffer */
7143         res = EnumPrintProcessorsW(nameW, envW, Level, bufferW, needed, pcbNeeded, pcReturned);
7144     }
7145     numentries = pcReturned ? *pcReturned : 0;
7146     needed = 0;
7147
7148     if (res) {
7149         /* EnumPrintProcessorsW collected all Data. Parse them to calculate ANSI-Size */
7150         DWORD   index;
7151         LPSTR   ptr;
7152         PPRINTPROCESSOR_INFO_1W ppiw;
7153         PPRINTPROCESSOR_INFO_1A ppia;
7154
7155         /* First pass: calculate the size for all Entries */
7156         ppiw = (PPRINTPROCESSOR_INFO_1W) bufferW;
7157         ppia = (PPRINTPROCESSOR_INFO_1A) pPPInfo;
7158         index = 0;
7159         while (index < numentries) {
7160             index++;
7161             needed += sizeof(PRINTPROCESSOR_INFO_1A);
7162             TRACE("%p: parsing #%d (%s)\n", ppiw, index, debugstr_w(ppiw->pName));
7163
7164             needed += WideCharToMultiByte(CP_ACP, 0, ppiw->pName, -1,
7165                                             NULL, 0, NULL, NULL);
7166
7167             ppiw = (PPRINTPROCESSOR_INFO_1W) (((LPBYTE)ppiw) + sizeof(PRINTPROCESSOR_INFO_1W));
7168             ppia = (PPRINTPROCESSOR_INFO_1A) (((LPBYTE)ppia) + sizeof(PRINTPROCESSOR_INFO_1A));
7169         }
7170
7171         /* check for errors and quit on failure */
7172         if (cbBuf < needed) {
7173             SetLastError(ERROR_INSUFFICIENT_BUFFER);
7174             res = FALSE;
7175             goto epp_cleanup;
7176         }
7177
7178         len = numentries * sizeof(PRINTPROCESSOR_INFO_1A); /* room for structs */
7179         ptr = (LPSTR) &pPPInfo[len];        /* start of strings */
7180         cbBuf -= len ;                      /* free Bytes in the user-Buffer */
7181         ppiw = (PPRINTPROCESSOR_INFO_1W) bufferW;
7182         ppia = (PPRINTPROCESSOR_INFO_1A) pPPInfo;
7183         index = 0;
7184         /* Second Pass: Fill the User Buffer (if we have one) */
7185         while ((index < numentries) && pPPInfo) {
7186             index++;
7187             TRACE("%p: writing PRINTPROCESSOR_INFO_1A #%d\n", ppia, index);
7188             ppia->pName = ptr;
7189             len = WideCharToMultiByte(CP_ACP, 0, ppiw->pName, -1,
7190                                             ptr, cbBuf , NULL, NULL);
7191             ptr += len;
7192             cbBuf -= len;
7193
7194             ppiw = (PPRINTPROCESSOR_INFO_1W) (((LPBYTE)ppiw) + sizeof(PRINTPROCESSOR_INFO_1W));
7195             ppia = (PPRINTPROCESSOR_INFO_1A) (((LPBYTE)ppia) + sizeof(PRINTPROCESSOR_INFO_1A));
7196
7197         }
7198     }
7199 epp_cleanup:
7200     if (pcbNeeded)  *pcbNeeded = needed;
7201     if (pcReturned) *pcReturned = (res) ? numentries : 0;
7202
7203     HeapFree(GetProcessHeap(), 0, nameW);
7204     HeapFree(GetProcessHeap(), 0, envW);
7205     HeapFree(GetProcessHeap(), 0, bufferW);
7206
7207     TRACE("returning %d with %d (%d byte for %d entries)\n",
7208             (res), GetLastError(), needed, numentries);
7209
7210     return (res);
7211 }
7212
7213 /*****************************************************************************
7214  *          EnumPrintProcessorsW [WINSPOOL.@]
7215  *
7216  * Enumerate available Print Processors
7217  *
7218  * PARAMS
7219  *  pName        [I] Servername or NULL (local Computer)
7220  *  pEnvironment [I] Printing-Environment or NULL (Default)
7221  *  Level        [I] Structure-Level (Only 1 is allowed)
7222  *  pPPInfo      [O] PTR to Buffer that receives the Result
7223  *  cbBuf        [I] Size of Buffer at pPPInfo
7224  *  pcbNeeded    [O] PTR to DWORD that receives the size in Bytes used / required for pPPInfo
7225  *  pcReturned   [O] PTR to DWORD that receives the number of Print Processors in pPPInfo
7226  *
7227  * RETURNS
7228  *  Success: TRUE
7229  *  Failure: FALSE and in pcbNeeded the Bytes required for pPPInfo, if cbBuf is too small
7230  *
7231  */
7232 BOOL WINAPI EnumPrintProcessorsW(LPWSTR pName, LPWSTR pEnvironment, DWORD Level,
7233                             LPBYTE pPPInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
7234 {
7235
7236     TRACE("(%s, %s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), debugstr_w(pEnvironment),
7237                                 Level, pPPInfo, cbBuf, pcbNeeded, pcReturned);
7238
7239     if ((backend == NULL)  && !load_backend()) return FALSE;
7240
7241     if (!pcbNeeded || !pcReturned) {
7242         SetLastError(RPC_X_NULL_REF_POINTER);
7243         return FALSE;
7244     }
7245
7246     if (!pPPInfo && (cbBuf > 0)) {
7247         SetLastError(ERROR_INVALID_USER_BUFFER);
7248         return FALSE;
7249     }
7250
7251     return backend->fpEnumPrintProcessors(pName, pEnvironment, Level, pPPInfo,
7252                                           cbBuf, pcbNeeded, pcReturned);
7253 }
7254
7255 /*****************************************************************************
7256  *          ExtDeviceMode [WINSPOOL.@]
7257  *
7258  */
7259 LONG WINAPI ExtDeviceMode( HWND hWnd, HANDLE hInst, LPDEVMODEA pDevModeOutput,
7260     LPSTR pDeviceName, LPSTR pPort, LPDEVMODEA pDevModeInput, LPSTR pProfile,
7261     DWORD fMode)
7262 {
7263     FIXME("Stub: %p %p %p %s %s %p %s %x\n", hWnd, hInst, pDevModeOutput,
7264           debugstr_a(pDeviceName), debugstr_a(pPort), pDevModeInput,
7265           debugstr_a(pProfile), fMode);
7266     return -1;
7267 }
7268
7269 /*****************************************************************************
7270  *          FindClosePrinterChangeNotification [WINSPOOL.@]
7271  *
7272  */
7273 BOOL WINAPI FindClosePrinterChangeNotification( HANDLE hChange )
7274 {
7275     FIXME("Stub: %p\n", hChange);
7276     return TRUE;
7277 }
7278
7279 /*****************************************************************************
7280  *          FindFirstPrinterChangeNotification [WINSPOOL.@]
7281  *
7282  */
7283 HANDLE WINAPI FindFirstPrinterChangeNotification( HANDLE hPrinter,
7284     DWORD fdwFlags, DWORD fdwOptions, LPVOID pPrinterNotifyOptions )
7285 {
7286     FIXME("Stub: %p %x %x %p\n",
7287           hPrinter, fdwFlags, fdwOptions, pPrinterNotifyOptions);
7288     return INVALID_HANDLE_VALUE;
7289 }
7290
7291 /*****************************************************************************
7292  *          FindNextPrinterChangeNotification [WINSPOOL.@]
7293  *
7294  */
7295 BOOL WINAPI FindNextPrinterChangeNotification( HANDLE hChange, PDWORD pdwChange,
7296     LPVOID pPrinterNotifyOptions, LPVOID *ppPrinterNotifyInfo )
7297 {
7298     FIXME("Stub: %p %p %p %p\n",
7299           hChange, pdwChange, pPrinterNotifyOptions, ppPrinterNotifyInfo);
7300     return FALSE;
7301 }
7302
7303 /*****************************************************************************
7304  *          FreePrinterNotifyInfo [WINSPOOL.@]
7305  *
7306  */
7307 BOOL WINAPI FreePrinterNotifyInfo( PPRINTER_NOTIFY_INFO pPrinterNotifyInfo )
7308 {
7309     FIXME("Stub: %p\n", pPrinterNotifyInfo);
7310     return TRUE;
7311 }
7312
7313 /*****************************************************************************
7314  *          string_to_buf
7315  *
7316  * Copies a unicode string into a buffer.  The buffer will either contain unicode or
7317  * ansi depending on the unicode parameter.
7318  */
7319 static BOOL string_to_buf(LPCWSTR str, LPBYTE ptr, DWORD cb, DWORD *size, BOOL unicode)
7320 {
7321     if(!str)
7322     {
7323         *size = 0;
7324         return TRUE;
7325     }
7326
7327     if(unicode)
7328     {
7329         *size = (strlenW(str) + 1) * sizeof(WCHAR);
7330         if(*size <= cb)
7331         {
7332             memcpy(ptr, str, *size);
7333             return TRUE;
7334         }
7335         return FALSE;
7336     }
7337     else
7338     {
7339         *size = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
7340         if(*size <= cb)
7341         {
7342             WideCharToMultiByte(CP_ACP, 0, str, -1, (LPSTR)ptr, *size, NULL, NULL);
7343             return TRUE;
7344         }
7345         return FALSE;
7346     }
7347 }
7348
7349 /*****************************************************************************
7350  *          get_job_info_1
7351  */
7352 static BOOL get_job_info_1(job_t *job, JOB_INFO_1W *ji1, LPBYTE buf, DWORD cbBuf,
7353                            LPDWORD pcbNeeded, BOOL unicode)
7354 {
7355     DWORD size, left = cbBuf;
7356     BOOL space = (cbBuf > 0);
7357     LPBYTE ptr = buf;
7358
7359     *pcbNeeded = 0;
7360
7361     if(space)
7362     {
7363         ji1->JobId = job->job_id;
7364     }
7365
7366     string_to_buf(job->document_title, ptr, left, &size, unicode);
7367     if(space && size <= left)
7368     {
7369         ji1->pDocument = (LPWSTR)ptr;
7370         ptr += size;
7371         left -= size;
7372     }
7373     else
7374         space = FALSE;
7375     *pcbNeeded += size;
7376
7377     if (job->printer_name)
7378     {
7379         string_to_buf(job->printer_name, ptr, left, &size, unicode);
7380         if(space && size <= left)
7381         {
7382             ji1->pPrinterName = (LPWSTR)ptr;
7383             ptr += size;
7384             left -= size;
7385         }
7386         else
7387             space = FALSE;
7388         *pcbNeeded += size;
7389     }
7390
7391     return space;
7392 }
7393
7394 /*****************************************************************************
7395  *          get_job_info_2
7396  */
7397 static BOOL get_job_info_2(job_t *job, JOB_INFO_2W *ji2, LPBYTE buf, DWORD cbBuf,
7398                            LPDWORD pcbNeeded, BOOL unicode)
7399 {
7400     DWORD size, left = cbBuf;
7401     DWORD shift;
7402     BOOL space = (cbBuf > 0);
7403     LPBYTE ptr = buf;
7404     LPDEVMODEA  dmA = NULL;
7405     LPDEVMODEW  devmode;
7406
7407     *pcbNeeded = 0;
7408
7409     if(space)
7410     {
7411         ji2->JobId = job->job_id;
7412     }
7413
7414     string_to_buf(job->document_title, ptr, left, &size, unicode);
7415     if(space && size <= left)
7416     {
7417         ji2->pDocument = (LPWSTR)ptr;
7418         ptr += size;
7419         left -= size;
7420     }
7421     else
7422         space = FALSE;
7423     *pcbNeeded += size;
7424
7425     if (job->printer_name)
7426     {
7427         string_to_buf(job->printer_name, ptr, left, &size, unicode);
7428         if(space && size <= left)
7429         {
7430             ji2->pPrinterName = (LPWSTR)ptr;
7431             ptr += size;
7432             left -= size;
7433         }
7434         else
7435             space = FALSE;
7436         *pcbNeeded += size;
7437     }
7438
7439     if (job->devmode)
7440     {
7441         if (!unicode)
7442         {
7443             dmA = DEVMODEdupWtoA(job->devmode);
7444             devmode = (LPDEVMODEW) dmA;
7445             if (dmA) size = dmA->dmSize + dmA->dmDriverExtra;
7446         }
7447         else
7448         {
7449             devmode = job->devmode;
7450             size = devmode->dmSize + devmode->dmDriverExtra;
7451         }
7452
7453         if (!devmode)
7454              FIXME("Can't convert DEVMODE W to A\n");
7455         else
7456         {
7457             /* align DEVMODE to a DWORD boundary */
7458             shift = (4 - (*pcbNeeded & 3)) & 3;
7459             size += shift;
7460
7461             if (size <= left)
7462             {
7463                 ptr += shift;
7464                 memcpy(ptr, devmode, size-shift);
7465                 ji2->pDevMode = (LPDEVMODEW)ptr;
7466                 if (!unicode) HeapFree(GetProcessHeap(), 0, dmA);
7467                 ptr += size-shift;
7468                 left -= size;
7469             }
7470             else
7471                 space = FALSE;
7472             *pcbNeeded +=size;
7473         }
7474     }
7475
7476     return space;
7477 }
7478
7479 /*****************************************************************************
7480  *          get_job_info
7481  */
7482 static BOOL get_job_info(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7483                          DWORD cbBuf, LPDWORD pcbNeeded, BOOL unicode)
7484 {
7485     BOOL ret = FALSE;
7486     DWORD needed = 0, size;
7487     job_t *job;
7488     LPBYTE ptr = pJob;
7489
7490     TRACE("%p %d %d %p %d %p\n", hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
7491
7492     EnterCriticalSection(&printer_handles_cs);
7493     job = get_job(hPrinter, JobId);
7494     if(!job)
7495         goto end;
7496
7497     switch(Level)
7498     {
7499     case 1:
7500         size = sizeof(JOB_INFO_1W);
7501         if(cbBuf >= size)
7502         {
7503             cbBuf -= size;
7504             ptr += size;
7505             memset(pJob, 0, size);
7506         }
7507         else
7508             cbBuf = 0;
7509         ret = get_job_info_1(job, (JOB_INFO_1W *)pJob, ptr, cbBuf, &needed, unicode);
7510         needed += size;
7511         break;
7512
7513     case 2:
7514         size = sizeof(JOB_INFO_2W);
7515         if(cbBuf >= size)
7516         {
7517             cbBuf -= size;
7518             ptr += size;
7519             memset(pJob, 0, size);
7520         }
7521         else
7522             cbBuf = 0;
7523         ret = get_job_info_2(job, (JOB_INFO_2W *)pJob, ptr, cbBuf, &needed, unicode);
7524         needed += size;
7525         break;
7526
7527     case 3:
7528         size = sizeof(JOB_INFO_3);
7529         if(cbBuf >= size)
7530         {
7531             cbBuf -= size;
7532             memset(pJob, 0, size);
7533             ret = TRUE;
7534         }
7535         else
7536             cbBuf = 0;
7537         needed = size;
7538         break;
7539
7540     default:
7541         SetLastError(ERROR_INVALID_LEVEL);
7542         goto end;
7543     }
7544     if(pcbNeeded)
7545         *pcbNeeded = needed;
7546 end:
7547     LeaveCriticalSection(&printer_handles_cs);
7548     return ret;
7549 }
7550
7551 /*****************************************************************************
7552  *          GetJobA [WINSPOOL.@]
7553  *
7554  */
7555 BOOL WINAPI GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7556                     DWORD cbBuf, LPDWORD pcbNeeded)
7557 {
7558     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, FALSE);
7559 }
7560
7561 /*****************************************************************************
7562  *          GetJobW [WINSPOOL.@]
7563  *
7564  */
7565 BOOL WINAPI GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob,
7566                     DWORD cbBuf, LPDWORD pcbNeeded)
7567 {
7568     return get_job_info(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded, TRUE);
7569 }
7570
7571 /*****************************************************************************
7572  *          schedule_pipe
7573  */
7574 static BOOL schedule_pipe(LPCWSTR cmd, LPCWSTR filename)
7575 {
7576 #ifdef HAVE_FORK
7577     char *unixname, *cmdA;
7578     DWORD len;
7579     int fds[2] = {-1, -1}, file_fd = -1, no_read;
7580     BOOL ret = FALSE;
7581     char buf[1024];
7582     pid_t pid, wret;
7583     int status;
7584
7585     if(!(unixname = wine_get_unix_file_name(filename)))
7586         return FALSE;
7587
7588     len = WideCharToMultiByte(CP_UNIXCP, 0, cmd, -1, NULL, 0, NULL, NULL);
7589     cmdA = HeapAlloc(GetProcessHeap(), 0, len);
7590     WideCharToMultiByte(CP_UNIXCP, 0, cmd, -1, cmdA, len, NULL, NULL);
7591
7592     TRACE("printing with: %s\n", cmdA);
7593
7594     if((file_fd = open(unixname, O_RDONLY)) == -1)
7595         goto end;
7596
7597     if (pipe(fds))
7598     {
7599         ERR("pipe() failed!\n");
7600         goto end;
7601     }
7602
7603     if ((pid = fork()) == 0)
7604     {
7605         close(0);
7606         dup2(fds[0], 0);
7607         close(fds[1]);
7608
7609         /* reset signals that we previously set to SIG_IGN */
7610         signal(SIGPIPE, SIG_DFL);
7611
7612         execl("/bin/sh", "/bin/sh", "-c", cmdA, NULL);
7613         _exit(1);
7614     }
7615     else if (pid == -1)
7616     {
7617         ERR("fork() failed!\n");
7618         goto end;
7619     }
7620
7621     while((no_read = read(file_fd, buf, sizeof(buf))) > 0)
7622         write(fds[1], buf, no_read);
7623
7624     close(fds[1]);
7625     fds[1] = -1;
7626
7627     /* reap child */
7628     do {
7629         wret = waitpid(pid, &status, 0);
7630     } while (wret < 0 && errno == EINTR);
7631     if (wret < 0)
7632     {
7633         ERR("waitpid() failed!\n");
7634         goto end;
7635     }
7636     if (!WIFEXITED(status) || WEXITSTATUS(status))
7637     {
7638         ERR("child process failed! %d\n", status);
7639         goto end;
7640     }
7641
7642     ret = TRUE;
7643
7644 end:
7645     if(file_fd != -1) close(file_fd);
7646     if(fds[0] != -1) close(fds[0]);
7647     if(fds[1] != -1) close(fds[1]);
7648
7649     HeapFree(GetProcessHeap(), 0, cmdA);
7650     HeapFree(GetProcessHeap(), 0, unixname);
7651     return ret;
7652 #else
7653     return FALSE;
7654 #endif
7655 }
7656
7657 /*****************************************************************************
7658  *          schedule_lpr
7659  */
7660 static BOOL schedule_lpr(LPCWSTR printer_name, LPCWSTR filename)
7661 {
7662     WCHAR *cmd;
7663     const WCHAR fmtW[] = {'l','p','r',' ','-','P','\'','%','s','\'',0};
7664     BOOL r;
7665
7666     cmd = HeapAlloc(GetProcessHeap(), 0, strlenW(printer_name) * sizeof(WCHAR) + sizeof(fmtW));
7667     sprintfW(cmd, fmtW, printer_name);
7668
7669     r = schedule_pipe(cmd, filename);
7670
7671     HeapFree(GetProcessHeap(), 0, cmd);
7672     return r;
7673 }
7674
7675 #ifdef SONAME_LIBCUPS
7676 /*****************************************************************************
7677  *          get_cups_jobs_ticket_options
7678  *
7679  * Explicitly set CUPS options based on any %cupsJobTicket lines.
7680  * The CUPS scheduler only looks for these in Print-File requests, and since
7681  * cupsPrintFile uses Create-Job / Send-Document, the ticket lines don't get
7682  * parsed.
7683  */
7684 static int get_cups_job_ticket_options( const char *file, int num_options, cups_option_t **options )
7685 {
7686     FILE *fp = fopen( file, "r" );
7687     char buf[257]; /* DSC max of 256 + '\0' */
7688     const char *ps_adobe = "%!PS-Adobe-";
7689     const char *cups_job = "%cupsJobTicket:";
7690
7691     if (!fp) return num_options;
7692     if (!fgets( buf, sizeof(buf), fp )) goto end;
7693     if (strncmp( buf, ps_adobe, strlen( ps_adobe ) )) goto end;
7694     while (fgets( buf, sizeof(buf), fp ))
7695     {
7696         if (strncmp( buf, cups_job, strlen( cups_job ) )) break;
7697         num_options = pcupsParseOptions( buf + strlen( cups_job ), num_options, options );
7698     }
7699
7700 end:
7701     fclose( fp );
7702     return num_options;
7703 }
7704 #endif
7705
7706 /*****************************************************************************
7707  *          schedule_cups
7708  */
7709 static BOOL schedule_cups(LPCWSTR printer_name, LPCWSTR filename, LPCWSTR document_title)
7710 {
7711 #ifdef SONAME_LIBCUPS
7712     if(pcupsPrintFile)
7713     {
7714         char *unixname, *queue, *unix_doc_title;
7715         DWORD len;
7716         BOOL ret;
7717         int num_options = 0, i;
7718         cups_option_t *options = NULL;
7719
7720         if(!(unixname = wine_get_unix_file_name(filename)))
7721             return FALSE;
7722
7723         len = WideCharToMultiByte(CP_UNIXCP, 0, printer_name, -1, NULL, 0, NULL, NULL);
7724         queue = HeapAlloc(GetProcessHeap(), 0, len);
7725         WideCharToMultiByte(CP_UNIXCP, 0, printer_name, -1, queue, len, NULL, NULL);
7726
7727         len = WideCharToMultiByte(CP_UNIXCP, 0, document_title, -1, NULL, 0, NULL, NULL);
7728         unix_doc_title = HeapAlloc(GetProcessHeap(), 0, len);
7729         WideCharToMultiByte(CP_UNIXCP, 0, document_title, -1, unix_doc_title, len, NULL, NULL);
7730
7731         num_options = get_cups_job_ticket_options( unixname, num_options, &options );
7732
7733         TRACE( "printing via cups with options:\n" );
7734         for (i = 0; i < num_options; i++)
7735             TRACE( "\t%d: %s = %s\n", i, options[i].name, options[i].value );
7736
7737         ret = pcupsPrintFile( queue, unixname, unix_doc_title, num_options, options );
7738
7739         pcupsFreeOptions( num_options, options );
7740
7741         HeapFree(GetProcessHeap(), 0, unix_doc_title);
7742         HeapFree(GetProcessHeap(), 0, queue);
7743         HeapFree(GetProcessHeap(), 0, unixname);
7744         return ret;
7745     }
7746     else
7747 #endif
7748     {
7749         return schedule_lpr(printer_name, filename);
7750     }
7751 }
7752
7753 static INT_PTR CALLBACK file_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
7754 {
7755     LPWSTR filename;
7756
7757     switch(msg)
7758     {
7759     case WM_INITDIALOG:
7760         SetWindowLongPtrW(hwnd, DWLP_USER, lparam);
7761         return TRUE;
7762
7763     case WM_COMMAND:
7764         if(HIWORD(wparam) == BN_CLICKED)
7765         {
7766             if(LOWORD(wparam) == IDOK)
7767             {
7768                 HANDLE hf;
7769                 DWORD len = SendDlgItemMessageW(hwnd, EDITBOX, WM_GETTEXTLENGTH, 0, 0);
7770                 LPWSTR *output;
7771
7772                 filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
7773                 GetDlgItemTextW(hwnd, EDITBOX, filename, len + 1);
7774
7775                 if(GetFileAttributesW(filename) != INVALID_FILE_ATTRIBUTES)
7776                 {
7777                     WCHAR caption[200], message[200];
7778                     int mb_ret;
7779
7780                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7781                     LoadStringW(WINSPOOL_hInstance, IDS_FILE_EXISTS, message, sizeof(message) / sizeof(WCHAR));
7782                     mb_ret = MessageBoxW(hwnd, message, caption, MB_OKCANCEL | MB_ICONEXCLAMATION);
7783                     if(mb_ret == IDCANCEL)
7784                     {
7785                         HeapFree(GetProcessHeap(), 0, filename);
7786                         return TRUE;
7787                     }
7788                 }
7789                 hf = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
7790                 if(hf == INVALID_HANDLE_VALUE)
7791                 {
7792                     WCHAR caption[200], message[200];
7793
7794                     LoadStringW(WINSPOOL_hInstance, IDS_CAPTION, caption, sizeof(caption) / sizeof(WCHAR));
7795                     LoadStringW(WINSPOOL_hInstance, IDS_CANNOT_OPEN, message, sizeof(message) / sizeof(WCHAR));
7796                     MessageBoxW(hwnd, message, caption, MB_OK | MB_ICONEXCLAMATION);
7797                     HeapFree(GetProcessHeap(), 0, filename);
7798                     return TRUE;
7799                 }
7800                 CloseHandle(hf);
7801                 DeleteFileW(filename);
7802                 output = (LPWSTR *)GetWindowLongPtrW(hwnd, DWLP_USER);
7803                 *output = filename;
7804                 EndDialog(hwnd, IDOK);
7805                 return TRUE;
7806             }
7807             if(LOWORD(wparam) == IDCANCEL)
7808             {
7809                 EndDialog(hwnd, IDCANCEL);
7810                 return TRUE;
7811             }
7812         }
7813         return FALSE;
7814     }
7815     return FALSE;
7816 }
7817
7818 /*****************************************************************************
7819  *          get_filename
7820  */
7821 static BOOL get_filename(LPWSTR *filename)
7822 {
7823     return DialogBoxParamW(WINSPOOL_hInstance, MAKEINTRESOURCEW(FILENAME_DIALOG), GetForegroundWindow(),
7824                            file_dlg_proc, (LPARAM)filename) == IDOK;
7825 }
7826
7827 /*****************************************************************************
7828  *          schedule_file
7829  */
7830 static BOOL schedule_file(LPCWSTR filename)
7831 {
7832     LPWSTR output = NULL;
7833
7834     if(get_filename(&output))
7835     {
7836         BOOL r;
7837         TRACE("copy to %s\n", debugstr_w(output));
7838         r = CopyFileW(filename, output, FALSE);
7839         HeapFree(GetProcessHeap(), 0, output);
7840         return r;
7841     }
7842     return FALSE;
7843 }
7844
7845 /*****************************************************************************
7846  *          schedule_unixfile
7847  */
7848 static BOOL schedule_unixfile(LPCWSTR output, LPCWSTR filename)
7849 {
7850     int in_fd, out_fd, no_read;
7851     char buf[1024];
7852     BOOL ret = FALSE;
7853     char *unixname, *outputA;
7854     DWORD len;
7855
7856     if(!(unixname = wine_get_unix_file_name(filename)))
7857         return FALSE;
7858
7859     len = WideCharToMultiByte(CP_UNIXCP, 0, output, -1, NULL, 0, NULL, NULL);
7860     outputA = HeapAlloc(GetProcessHeap(), 0, len);
7861     WideCharToMultiByte(CP_UNIXCP, 0, output, -1, outputA, len, NULL, NULL);
7862     
7863     out_fd = open(outputA, O_CREAT | O_TRUNC | O_WRONLY, 0666);
7864     in_fd = open(unixname, O_RDONLY);
7865     if(out_fd == -1 || in_fd == -1)
7866         goto end;
7867
7868     while((no_read = read(in_fd, buf, sizeof(buf))) > 0)
7869         write(out_fd, buf, no_read);
7870
7871     ret = TRUE;
7872 end:
7873     if(in_fd != -1) close(in_fd);
7874     if(out_fd != -1) close(out_fd);
7875     HeapFree(GetProcessHeap(), 0, outputA);
7876     HeapFree(GetProcessHeap(), 0, unixname);
7877     return ret;
7878 }
7879
7880 /*****************************************************************************
7881  *          ScheduleJob [WINSPOOL.@]
7882  *
7883  */
7884 BOOL WINAPI ScheduleJob( HANDLE hPrinter, DWORD dwJobID )
7885 {
7886     opened_printer_t *printer;
7887     BOOL ret = FALSE;
7888     struct list *cursor, *cursor2;
7889
7890     TRACE("(%p, %x)\n", hPrinter, dwJobID);
7891     EnterCriticalSection(&printer_handles_cs);
7892     printer = get_opened_printer(hPrinter);
7893     if(!printer)
7894         goto end;
7895
7896     LIST_FOR_EACH_SAFE(cursor, cursor2, &printer->queue->jobs)
7897     {
7898         job_t *job = LIST_ENTRY(cursor, job_t, entry);
7899         HANDLE hf;
7900
7901         if(job->job_id != dwJobID) continue;
7902
7903         hf = CreateFileW(job->filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
7904         if(hf != INVALID_HANDLE_VALUE)
7905         {
7906             PRINTER_INFO_5W *pi5 = NULL;
7907             LPWSTR portname = job->portname;
7908             DWORD needed;
7909             HKEY hkey;
7910             WCHAR output[1024];
7911             static const WCHAR spooler_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
7912                                                 'P','r','i','n','t','i','n','g','\\','S','p','o','o','l','e','r',0};
7913
7914             if (!portname)
7915             {
7916                 GetPrinterW(hPrinter, 5, NULL, 0, &needed);
7917                 pi5 = HeapAlloc(GetProcessHeap(), 0, needed);
7918                 GetPrinterW(hPrinter, 5, (LPBYTE)pi5, needed, &needed);
7919                 portname = pi5->pPortName;
7920             }
7921             TRACE("need to schedule job %d filename %s to port %s\n", job->job_id, debugstr_w(job->filename),
7922                   debugstr_w(portname));
7923             
7924             output[0] = 0;
7925
7926             /* @@ Wine registry key: HKCU\Software\Wine\Printing\Spooler */
7927             if(RegOpenKeyW(HKEY_CURRENT_USER, spooler_key, &hkey) == ERROR_SUCCESS)
7928             {
7929                 DWORD type, count = sizeof(output);
7930                 RegQueryValueExW(hkey, portname, NULL, &type, (LPBYTE)output, &count);
7931                 RegCloseKey(hkey);
7932             }
7933             if(output[0] == '|')
7934             {
7935                 ret = schedule_pipe(output + 1, job->filename);
7936             }
7937             else if(output[0])
7938             {
7939                 ret = schedule_unixfile(output, job->filename);
7940             }
7941             else if(!strncmpW(portname, LPR_Port, strlenW(LPR_Port)))
7942             {
7943                 ret = schedule_lpr(portname + strlenW(LPR_Port), job->filename);
7944             }
7945             else if(!strncmpW(portname, CUPS_Port, strlenW(CUPS_Port)))
7946             {
7947                 ret = schedule_cups(portname + strlenW(CUPS_Port), job->filename, job->document_title);
7948             }
7949             else if(!strncmpW(portname, FILE_Port, strlenW(FILE_Port)))
7950             {
7951                 ret = schedule_file(job->filename);
7952             }
7953             else
7954             {
7955                 FIXME("can't schedule to port %s\n", debugstr_w(portname));
7956             }
7957             HeapFree(GetProcessHeap(), 0, pi5);
7958             CloseHandle(hf);
7959             DeleteFileW(job->filename);
7960         }
7961         list_remove(cursor);
7962         HeapFree(GetProcessHeap(), 0, job->document_title);
7963         HeapFree(GetProcessHeap(), 0, job->printer_name);
7964         HeapFree(GetProcessHeap(), 0, job->portname);
7965         HeapFree(GetProcessHeap(), 0, job->filename);
7966         HeapFree(GetProcessHeap(), 0, job->devmode);
7967         HeapFree(GetProcessHeap(), 0, job);
7968         break;
7969     }
7970 end:
7971     LeaveCriticalSection(&printer_handles_cs);
7972     return ret;
7973 }
7974
7975 /*****************************************************************************
7976  *          StartDocDlgA [WINSPOOL.@]
7977  */
7978 LPSTR WINAPI StartDocDlgA( HANDLE hPrinter, DOCINFOA *doc )
7979 {
7980     UNICODE_STRING usBuffer;
7981     DOCINFOW docW;
7982     LPWSTR retW;
7983     LPWSTR docnameW = NULL, outputW = NULL, datatypeW = NULL;
7984     LPSTR ret = NULL;
7985
7986     docW.cbSize = sizeof(docW);
7987     if (doc->lpszDocName)
7988     {
7989         docnameW = asciitounicode(&usBuffer, doc->lpszDocName);
7990         if (!(docW.lpszDocName = docnameW)) return NULL;
7991     }
7992     if (doc->lpszOutput)
7993     {
7994         outputW = asciitounicode(&usBuffer, doc->lpszOutput);
7995         if (!(docW.lpszOutput = outputW)) return NULL;
7996     }
7997     if (doc->lpszDatatype)
7998     {
7999         datatypeW = asciitounicode(&usBuffer, doc->lpszDatatype);
8000         if (!(docW.lpszDatatype = datatypeW)) return NULL;
8001     }
8002     docW.fwType = doc->fwType;
8003
8004     retW = StartDocDlgW(hPrinter, &docW);
8005
8006     if(retW)
8007     {
8008         DWORD len = WideCharToMultiByte(CP_ACP, 0, retW, -1, NULL, 0, NULL, NULL);
8009         ret = HeapAlloc(GetProcessHeap(), 0, len);
8010         WideCharToMultiByte(CP_ACP, 0, retW, -1, ret, len, NULL, NULL);
8011         HeapFree(GetProcessHeap(), 0, retW);
8012     }
8013
8014     HeapFree(GetProcessHeap(), 0, datatypeW);
8015     HeapFree(GetProcessHeap(), 0, outputW);
8016     HeapFree(GetProcessHeap(), 0, docnameW);
8017
8018     return ret;
8019 }
8020
8021 /*****************************************************************************
8022  *          StartDocDlgW [WINSPOOL.@]
8023  *
8024  * Undocumented: Apparently used by gdi32:StartDocW() to popup the file dialog
8025  * when lpszOutput is "FILE:" or if lpszOutput is NULL and the default printer
8026  * port is "FILE:". Also returns the full path if passed a relative path.
8027  *
8028  * The caller should free the returned string from the process heap.
8029  */
8030 LPWSTR WINAPI StartDocDlgW( HANDLE hPrinter, DOCINFOW *doc )
8031 {
8032     LPWSTR ret = NULL;
8033     DWORD len, attr;
8034
8035     if(doc->lpszOutput == NULL) /* Check whether default port is FILE: */
8036     {
8037         PRINTER_INFO_5W *pi5;
8038         GetPrinterW(hPrinter, 5, NULL, 0, &len);
8039         if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
8040             return NULL;
8041         pi5 = HeapAlloc(GetProcessHeap(), 0, len);
8042         GetPrinterW(hPrinter, 5, (LPBYTE)pi5, len, &len);
8043         if(!pi5->pPortName || strcmpW(pi5->pPortName, FILE_Port))
8044         {
8045             HeapFree(GetProcessHeap(), 0, pi5);
8046             return NULL;
8047         }
8048         HeapFree(GetProcessHeap(), 0, pi5);
8049     }
8050
8051     if(doc->lpszOutput == NULL || !strcmpW(doc->lpszOutput, FILE_Port))
8052     {
8053         LPWSTR name;
8054
8055         if (get_filename(&name))
8056         {
8057             if(!(len = GetFullPathNameW(name, 0, NULL, NULL)))
8058             {
8059                 HeapFree(GetProcessHeap(), 0, name);
8060                 return NULL;
8061             }
8062             ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
8063             GetFullPathNameW(name, len, ret, NULL);
8064             HeapFree(GetProcessHeap(), 0, name);
8065         }
8066         return ret;
8067     }
8068
8069     if(!(len = GetFullPathNameW(doc->lpszOutput, 0, NULL, NULL)))
8070         return NULL;
8071
8072     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
8073     GetFullPathNameW(doc->lpszOutput, len, ret, NULL);
8074         
8075     attr = GetFileAttributesW(ret);
8076     if(attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
8077     {
8078         HeapFree(GetProcessHeap(), 0, ret);
8079         ret = NULL;
8080     }
8081     return ret;
8082 }