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