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