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