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