wined3d: Downgrade an ERR to a WARN.
[wine] / dlls / localspl / provider.c
1 /*
2  * Implementation of the Local Printprovider
3  *
4  * Copyright 2006-2009 Detlef Riekenberg
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winreg.h"
30 #include "winspool.h"
31 #include "winuser.h"
32 #include "ddk/winddiui.h"
33 #include "ddk/winsplp.h"
34
35 #include "wine/list.h"
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "localspl_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
41
42 /* ############################### */
43
44 static CRITICAL_SECTION monitor_handles_cs;
45 static CRITICAL_SECTION_DEBUG monitor_handles_cs_debug =
46 {
47     0, 0, &monitor_handles_cs,
48     { &monitor_handles_cs_debug.ProcessLocksList, &monitor_handles_cs_debug.ProcessLocksList },
49       0, 0, { (DWORD_PTR)(__FILE__ ": monitor_handles_cs") }
50 };
51 static CRITICAL_SECTION monitor_handles_cs = { &monitor_handles_cs_debug, -1, 0, 0, 0, 0 };
52
53 /* ############################### */
54
55 typedef struct {
56     WCHAR   src[MAX_PATH+MAX_PATH];
57     WCHAR   dst[MAX_PATH+MAX_PATH];
58     DWORD   srclen;
59     DWORD   dstlen;
60     DWORD   copyflags;
61     BOOL    lazy;
62 } apd_data_t;
63
64 typedef struct {
65     struct list     entry;
66     LPWSTR          name;
67     LPWSTR          dllname;
68     PMONITORUI      monitorUI;
69     LPMONITOR       monitor;
70     HMODULE         hdll;
71     DWORD           refcount;
72     DWORD           dwMonitorSize;
73 } monitor_t;
74
75 typedef struct {
76     LPCWSTR  envname;
77     LPCWSTR  subdir;
78     DWORD    driverversion;
79     LPCWSTR  versionregpath;
80     LPCWSTR  versionsubdir;
81 } printenv_t;
82
83 /* ############################### */
84
85 static struct list monitor_handles = LIST_INIT( monitor_handles );
86 static monitor_t * pm_localport;
87
88 static const PRINTPROVIDOR * pprovider = NULL;
89
90 static const WCHAR backslashW[] = {'\\',0};
91 static const WCHAR configuration_fileW[] = {'C','o','n','f','i','g','u','r','a','t','i','o','n',' ','F','i','l','e',0};
92 static const WCHAR datatypeW[] = {'D','a','t','a','t','y','p','e',0};
93 static const WCHAR data_fileW[] = {'D','a','t','a',' ','F','i','l','e',0};
94 static const WCHAR default_devmodeW[] = {'D','e','f','a','u','l','t',' ','D','e','v','M','o','d','e',0};
95 static const WCHAR dependent_filesW[] = {'D','e','p','e','n','d','e','n','t',' ','F','i','l','e','s',0};
96 static const WCHAR descriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
97 static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
98 static const WCHAR fmt_driversW[] = { 'S','y','s','t','e','m','\\',
99                                   'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
100                                   'c','o','n','t','r','o','l','\\',
101                                   'P','r','i','n','t','\\',
102                                   'E','n','v','i','r','o','n','m','e','n','t','s','\\',
103                                   '%','s','\\','D','r','i','v','e','r','s','%','s',0 };
104 static const WCHAR hardwareidW[] = {'H','a','r','d','w','a','r','e','I','D',0};
105 static const WCHAR help_fileW[] = {'H','e','l','p',' ','F','i','l','e',0};
106 static const WCHAR localportW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
107 static const WCHAR locationW[] = {'L','o','c','a','t','i','o','n',0};
108 static const WCHAR manufacturerW[] = {'M','a','n','u','f','a','c','t','u','r','e','r',0};
109 static const WCHAR monitorW[] = {'M','o','n','i','t','o','r',0};
110 static const WCHAR monitorsW[] = {'S','y','s','t','e','m','\\',
111                                 'C','u', 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
112                                 'C','o','n','t','r','o','l','\\',
113                                 'P','r','i','n','t','\\',
114                                 'M','o','n','i','t','o','r','s','\\',0};
115 static const WCHAR monitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
116 static const WCHAR nameW[] = {'N','a','m','e',0};
117 static const WCHAR oem_urlW[] = {'O','E','M',' ','U','r','l',0};
118 static const WCHAR parametersW[] = {'P','a','r','a','m','e','t','e','r','s',0};
119 static const WCHAR portW[] = {'P','o','r','t',0};
120 static const WCHAR previous_namesW[] = {'P','r','e','v','i','o','u','s',' ','N','a','m','e','s',0};
121 static const WCHAR spooldriversW[] = {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\',0};
122 static const WCHAR versionW[] = {'V','e','r','s','i','o','n',0};
123
124 static const WCHAR win40_envnameW[] = {'W','i','n','d','o','w','s',' ','4','.','0',0};
125 static const WCHAR win40_subdirW[] = {'w','i','n','4','0',0};
126 static const WCHAR version0_regpathW[] = {'\\','V','e','r','s','i','o','n','-','0',0};
127 static const WCHAR version0_subdirW[] = {'\\','0',0};
128
129 static const WCHAR x64_envnameW[] = {'W','i','n','d','o','w','s',' ','x','6','4',0};
130 static const WCHAR x64_subdirW[] = {'x','6','4',0};
131 static const WCHAR x86_envnameW[] = {'W','i','n','d','o','w','s',' ','N','T',' ','x','8','6',0};
132 static const WCHAR x86_subdirW[] = {'w','3','2','x','8','6',0};
133 static const WCHAR version3_regpathW[] = {'\\','V','e','r','s','i','o','n','-','3',0};
134 static const WCHAR version3_subdirW[] = {'\\','3',0};
135
136
137 static const printenv_t env_x86 =   {x86_envnameW, x86_subdirW, 3,
138                                      version3_regpathW, version3_subdirW};
139
140 static const printenv_t env_x64 =   {x64_envnameW, x64_subdirW, 3,
141                                      version3_regpathW, version3_subdirW};
142
143 static const printenv_t env_win40 = {win40_envnameW, win40_subdirW, 0,
144                                      version0_regpathW, version0_subdirW};
145
146 static const printenv_t * const all_printenv[] = {&env_x86, &env_x64, &env_win40};
147
148
149 static const DWORD di_sizeof[] = {0, sizeof(DRIVER_INFO_1W), sizeof(DRIVER_INFO_2W),
150                                      sizeof(DRIVER_INFO_3W), sizeof(DRIVER_INFO_4W),
151                                      sizeof(DRIVER_INFO_5W), sizeof(DRIVER_INFO_6W),
152                                   0, sizeof(DRIVER_INFO_8W)};
153
154
155 /******************************************************************
156  * strdupW [internal]
157  *
158  * create a copy of a unicode-string
159  *
160  */
161 static LPWSTR strdupW(LPCWSTR p)
162 {
163     LPWSTR ret;
164     DWORD len;
165
166     if(!p) return NULL;
167     len = (lstrlenW(p) + 1) * sizeof(WCHAR);
168     ret = heap_alloc(len);
169     memcpy(ret, p, len);
170     return ret;
171 }
172
173 /******************************************************************
174  *  apd_copyfile [internal]
175  *
176  * Copy a file from the driverdirectory to the versioned directory
177  *
178  * RETURNS
179  *  Success: TRUE
180  *  Failure: FALSE
181  *
182  */
183 static BOOL apd_copyfile(LPWSTR filename, apd_data_t *apd)
184 {
185     LPWSTR  ptr;
186     LPWSTR  srcname;
187     DWORD   res;
188
189     apd->src[apd->srclen] = '\0';
190     apd->dst[apd->dstlen] = '\0';
191
192     if (!filename || !filename[0]) {
193         /* nothing to copy */
194         return TRUE;
195     }
196
197     ptr = strrchrW(filename, '\\');
198     if (ptr) {
199         ptr++;
200     }
201     else
202     {
203         ptr = filename;
204     }
205
206     if (apd->copyflags & APD_COPY_FROM_DIRECTORY) {
207         /* we have an absolute Path */
208         srcname = filename;
209     }
210     else
211     {
212         srcname = apd->src;
213         lstrcatW(srcname, ptr);
214     }
215     lstrcatW(apd->dst, ptr);
216
217     TRACE("%s => %s\n", debugstr_w(filename), debugstr_w(apd->dst));
218
219     /* FIXME: handle APD_COPY_NEW_FILES */
220     res = CopyFileW(srcname, apd->dst, FALSE);
221     TRACE("got %u with %u\n", res, GetLastError());
222
223     return (apd->lazy) ? TRUE : res;
224 }
225
226 /******************************************************************
227  * copy_servername_from_name  (internal)
228  *
229  * for an external server, the serverpart from the name is copied.
230  *
231  * RETURNS
232  *  the length (in WCHAR) of the serverpart (0 for the local computer)
233  *  (-length), when the name is to long
234  *
235  */
236 static LONG copy_servername_from_name(LPCWSTR name, LPWSTR target)
237 {
238     LPCWSTR server;
239     LPWSTR  ptr;
240     WCHAR   buffer[MAX_COMPUTERNAME_LENGTH +1];
241     DWORD   len;
242     DWORD   serverlen;
243
244     if (target) *target = '\0';
245
246     if (name == NULL) return 0;
247     if ((name[0] != '\\') || (name[1] != '\\')) return 0;
248
249     server = &name[2];
250     /* skip over both backslash, find separator '\' */
251     ptr = strchrW(server, '\\');
252     serverlen = (ptr) ? ptr - server : lstrlenW(server);
253
254     /* servername is empty */
255     if (serverlen == 0) return 0;
256
257     TRACE("found %s\n", debugstr_wn(server, serverlen));
258
259     if (serverlen > MAX_COMPUTERNAME_LENGTH) return -serverlen;
260
261     if (target) {
262         memcpy(target, server, serverlen * sizeof(WCHAR));
263         target[serverlen] = '\0';
264     }
265
266     len = sizeof(buffer) / sizeof(buffer[0]);
267     if (GetComputerNameW(buffer, &len)) {
268         if ((serverlen == len) && (strncmpiW(server, buffer, len) == 0)) {
269             /* The requested Servername is our computername */
270             return 0;
271         }
272     }
273     return serverlen;
274 }
275
276 /******************************************************************
277  * monitor_unload [internal]
278  *
279  * release a printmonitor and unload it from memory, when needed
280  *
281  */
282 static void monitor_unload(monitor_t * pm)
283 {
284     if (pm == NULL) return;
285     TRACE("%p (refcount: %d) %s\n", pm, pm->refcount, debugstr_w(pm->name));
286
287     EnterCriticalSection(&monitor_handles_cs);
288
289     if (pm->refcount) pm->refcount--;
290
291     if (pm->refcount == 0) {
292         list_remove(&pm->entry);
293         FreeLibrary(pm->hdll);
294         heap_free(pm->name);
295         heap_free(pm->dllname);
296         heap_free(pm);
297     }
298     LeaveCriticalSection(&monitor_handles_cs);
299 }
300
301 /******************************************************************
302  * monitor_unloadall [internal]
303  *
304  * release all printmonitors and unload them from memory, when needed
305  *
306  */
307
308 static void monitor_unloadall(void)
309 {
310     monitor_t * pm;
311     monitor_t * next;
312
313     EnterCriticalSection(&monitor_handles_cs);
314     /* iterate through the list, with safety against removal */
315     LIST_FOR_EACH_ENTRY_SAFE(pm, next, &monitor_handles, monitor_t, entry)
316     {
317         monitor_unload(pm);
318     }
319     LeaveCriticalSection(&monitor_handles_cs);
320 }
321
322 /******************************************************************
323  * monitor_load [internal]
324  *
325  * load a printmonitor, get the dllname from the registry, when needed
326  * initialize the monitor and dump found function-pointers
327  *
328  * On failure, SetLastError() is called and NULL is returned
329  */
330
331 static monitor_t * monitor_load(LPCWSTR name, LPWSTR dllname)
332 {
333     LPMONITOR2  (WINAPI *pInitializePrintMonitor2) (PMONITORINIT, LPHANDLE);
334     PMONITORUI  (WINAPI *pInitializePrintMonitorUI)(VOID);
335     LPMONITOREX (WINAPI *pInitializePrintMonitor)  (LPWSTR);
336     DWORD (WINAPI *pInitializeMonitorEx)(LPWSTR, LPMONITOR);
337     DWORD (WINAPI *pInitializeMonitor)  (LPWSTR);
338
339     monitor_t * pm = NULL;
340     monitor_t * cursor;
341     LPWSTR  regroot = NULL;
342     LPWSTR  driver = dllname;
343
344     TRACE("(%s, %s)\n", debugstr_w(name), debugstr_w(dllname));
345     /* Is the Monitor already loaded? */
346     EnterCriticalSection(&monitor_handles_cs);
347
348     if (name) {
349         LIST_FOR_EACH_ENTRY(cursor, &monitor_handles, monitor_t, entry)
350         {
351             if (cursor->name && (lstrcmpW(name, cursor->name) == 0)) {
352                 pm = cursor;
353                 break;
354             }
355         }
356     }
357
358     if (pm == NULL) {
359         pm = heap_alloc_zero(sizeof(monitor_t));
360         if (pm == NULL) goto cleanup;
361         list_add_tail(&monitor_handles, &pm->entry);
362     }
363     pm->refcount++;
364
365     if (pm->name == NULL) {
366         /* Load the monitor */
367         LPMONITOREX pmonitorEx;
368         DWORD   len;
369
370         if (name) {
371             len = lstrlenW(monitorsW) + lstrlenW(name) + 2;
372             regroot = heap_alloc(len * sizeof(WCHAR));
373         }
374
375         if (regroot) {
376             lstrcpyW(regroot, monitorsW);
377             lstrcatW(regroot, name);
378             /* Get the Driver from the Registry */
379             if (driver == NULL) {
380                 HKEY    hroot;
381                 DWORD   namesize;
382                 if (RegOpenKeyW(HKEY_LOCAL_MACHINE, regroot, &hroot) == ERROR_SUCCESS) {
383                     if (RegQueryValueExW(hroot, driverW, NULL, NULL, NULL,
384                                         &namesize) == ERROR_SUCCESS) {
385                         driver = heap_alloc(namesize);
386                         RegQueryValueExW(hroot, driverW, NULL, NULL, (LPBYTE) driver, &namesize) ;
387                     }
388                     RegCloseKey(hroot);
389                 }
390             }
391         }
392
393         pm->name = strdupW(name);
394         pm->dllname = strdupW(driver);
395
396         if ((name && (!regroot || !pm->name)) || !pm->dllname) {
397             monitor_unload(pm);
398             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
399             pm = NULL;
400             goto cleanup;
401         }
402
403         pm->hdll = LoadLibraryW(driver);
404         TRACE("%p: LoadLibrary(%s) => %d\n", pm->hdll, debugstr_w(driver), GetLastError());
405
406         if (pm->hdll == NULL) {
407             monitor_unload(pm);
408             SetLastError(ERROR_MOD_NOT_FOUND);
409             pm = NULL;
410             goto cleanup;
411         }
412
413         pInitializePrintMonitor2  = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor2");
414         pInitializePrintMonitorUI = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitorUI");
415         pInitializePrintMonitor   = (void *)GetProcAddress(pm->hdll, "InitializePrintMonitor");
416         pInitializeMonitorEx = (void *)GetProcAddress(pm->hdll, "InitializeMonitorEx");
417         pInitializeMonitor   = (void *)GetProcAddress(pm->hdll, "InitializeMonitor");
418
419
420         TRACE("%p: %s,pInitializePrintMonitor2\n", pInitializePrintMonitor2, debugstr_w(driver));
421         TRACE("%p: %s,pInitializePrintMonitorUI\n", pInitializePrintMonitorUI, debugstr_w(driver));
422         TRACE("%p: %s,pInitializePrintMonitor\n", pInitializePrintMonitor, debugstr_w(driver));
423         TRACE("%p: %s,pInitializeMonitorEx\n", pInitializeMonitorEx, debugstr_w(driver));
424         TRACE("%p: %s,pInitializeMonitor\n", pInitializeMonitor, debugstr_w(driver));
425
426         if (pInitializePrintMonitorUI  != NULL) {
427             pm->monitorUI = pInitializePrintMonitorUI();
428             TRACE("%p: MONITORUI from %s,InitializePrintMonitorUI()\n", pm->monitorUI, debugstr_w(driver));
429             if (pm->monitorUI) {
430                 TRACE("0x%08x: dwMonitorSize (%d)\n",
431                         pm->monitorUI->dwMonitorUISize, pm->monitorUI->dwMonitorUISize);
432
433             }
434         }
435
436         if (pInitializePrintMonitor && regroot) {
437             pmonitorEx = pInitializePrintMonitor(regroot);
438             TRACE("%p: LPMONITOREX from %s,InitializePrintMonitor(%s)\n",
439                     pmonitorEx, debugstr_w(driver), debugstr_w(regroot));
440
441             if (pmonitorEx) {
442                 pm->dwMonitorSize = pmonitorEx->dwMonitorSize;
443                 pm->monitor = &(pmonitorEx->Monitor);
444             }
445         }
446
447         if (pm->monitor) {
448             TRACE("0x%08x: dwMonitorSize (%d)\n", pm->dwMonitorSize, pm->dwMonitorSize);
449
450         }
451
452         if (!pm->monitor && regroot) {
453             if (pInitializePrintMonitor2 != NULL) {
454                 FIXME("%s,InitializePrintMonitor2 not implemented\n", debugstr_w(driver));
455             }
456             if (pInitializeMonitorEx != NULL) {
457                 FIXME("%s,InitializeMonitorEx not implemented\n", debugstr_w(driver));
458             }
459             if (pInitializeMonitor != NULL) {
460                 FIXME("%s,InitializeMonitor not implemented\n", debugstr_w(driver));
461             }
462         }
463         if (!pm->monitor && !pm->monitorUI) {
464             monitor_unload(pm);
465             SetLastError(ERROR_PROC_NOT_FOUND);
466             pm = NULL;
467         }
468     }
469 cleanup:
470     if ((pm_localport ==  NULL) && (pm != NULL) && (lstrcmpW(pm->name, localportW) == 0)) {
471         pm->refcount++;
472         pm_localport = pm;
473     }
474     LeaveCriticalSection(&monitor_handles_cs);
475     if (driver != dllname) heap_free(driver);
476     heap_free(regroot);
477     TRACE("=> %p\n", pm);
478     return pm;
479 }
480
481 /******************************************************************
482  * monitor_loadall [internal]
483  *
484  * Load all registered monitors
485  *
486  */
487 static DWORD monitor_loadall(void)
488 {
489     monitor_t * pm;
490     DWORD   registered = 0;
491     DWORD   loaded = 0;
492     HKEY    hmonitors;
493     WCHAR   buffer[MAX_PATH];
494     DWORD   id = 0;
495
496     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hmonitors) == ERROR_SUCCESS) {
497         RegQueryInfoKeyW(hmonitors, NULL, NULL, NULL, &registered, NULL, NULL,
498                         NULL, NULL, NULL, NULL, NULL);
499
500         TRACE("%d monitors registered\n", registered);
501
502         while (id < registered) {
503             buffer[0] = '\0';
504             RegEnumKeyW(hmonitors, id, buffer, MAX_PATH);
505             pm = monitor_load(buffer, NULL);
506             if (pm) loaded++;
507             id++;
508         }
509         RegCloseKey(hmonitors);
510     }
511     TRACE("%d monitors loaded\n", loaded);
512     return loaded;
513 }
514
515 /******************************************************************
516  * Return the number of bytes for an multi_sz string.
517  * The result includes all \0s
518  * (specifically the extra \0, that is needed as multi_sz terminator).
519  */
520 static int multi_sz_lenW(const WCHAR *str)
521 {
522     const WCHAR *ptr = str;
523     if (!str) return 0;
524     do
525     {
526         ptr += lstrlenW(ptr) + 1;
527     } while (*ptr);
528
529     return (ptr - str + 1) * sizeof(WCHAR);
530 }
531
532 /******************************************************************
533  * validate_envW [internal]
534  *
535  * validate the user-supplied printing-environment
536  *
537  * PARAMS
538  *  env  [I] PTR to Environment-String or NULL
539  *
540  * RETURNS
541  *  Success:  PTR to printenv_t
542  *  Failure:  NULL and ERROR_INVALID_ENVIRONMENT
543  *
544  * NOTES
545  *  An empty string is handled the same way as NULL.
546  *
547  */
548
549 static const  printenv_t * validate_envW(LPCWSTR env)
550 {
551     const printenv_t *result = NULL;
552     unsigned int i;
553
554     TRACE("(%s)\n", debugstr_w(env));
555     if (env && env[0])
556     {
557         for (i = 0; i < sizeof(all_printenv)/sizeof(all_printenv[0]); i++)
558         {
559             if (lstrcmpiW(env, all_printenv[i]->envname) == 0)
560             {
561                 result = all_printenv[i];
562                 break;
563             }
564         }
565         if (result == NULL) {
566             FIXME("unsupported Environment: %s\n", debugstr_w(env));
567             SetLastError(ERROR_INVALID_ENVIRONMENT);
568         }
569         /* on win9x, only "Windows 4.0" is allowed, but we ignore this */
570     }
571     else
572     {
573         result = (GetVersion() & 0x80000000) ? &env_win40 : &env_x86;
574     }
575
576     TRACE("=> using %p: %s\n", result, debugstr_w(result ? result->envname : NULL));
577     return result;
578 }
579
580 /*****************************************************************************
581  * enumerate the local monitors (INTERNAL)
582  *
583  * returns the needed size (in bytes) for pMonitors
584  * and  *lpreturned is set to number of entries returned in pMonitors
585  *
586  * Language-Monitors are also installed in the same Registry-Location but
587  * they are filtered in Windows (not returned by EnumMonitors).
588  * We do no filtering to simplify our Code.
589  *
590  */
591 static DWORD get_local_monitors(DWORD level, LPBYTE pMonitors, DWORD cbBuf, LPDWORD lpreturned)
592 {
593     HKEY    hroot = NULL;
594     HKEY    hentry = NULL;
595     LPWSTR  ptr;
596     LPMONITOR_INFO_2W mi;
597     WCHAR   buffer[MAX_PATH];
598     WCHAR   dllname[MAX_PATH];
599     DWORD   dllsize;
600     DWORD   len;
601     DWORD   index = 0;
602     DWORD   needed = 0;
603     DWORD   numentries;
604     DWORD   entrysize;
605
606     entrysize = (level == 1) ? sizeof(MONITOR_INFO_1W) : sizeof(MONITOR_INFO_2W);
607
608     numentries = *lpreturned;       /* this is 0, when we scan the registry */
609     len = entrysize * numentries;
610     ptr = (LPWSTR) &pMonitors[len];
611
612     numentries = 0;
613     len = sizeof(buffer)/sizeof(buffer[0]);
614     buffer[0] = '\0';
615
616     /* Windows creates the "Monitors"-Key on reboot / start "spooler" */
617     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) == ERROR_SUCCESS) {
618         /* Scan all Monitor-Registry-Keys */
619         while (RegEnumKeyExW(hroot, index, buffer, &len, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
620             TRACE("Monitor_%d: %s\n", numentries, debugstr_w(buffer));
621             dllsize = sizeof(dllname);
622             dllname[0] = '\0';
623
624             /* The Monitor must have a Driver-DLL */
625             if (RegOpenKeyExW(hroot, buffer, 0, KEY_READ, &hentry) == ERROR_SUCCESS) {
626                 if (RegQueryValueExW(hentry, driverW, NULL, NULL, (LPBYTE) dllname, &dllsize) == ERROR_SUCCESS) {
627                     /* We found a valid DLL for this Monitor. */
628                     TRACE("using Driver: %s\n", debugstr_w(dllname));
629                 }
630                 RegCloseKey(hentry);
631             }
632
633             /* Windows returns only Port-Monitors here, but to simplify our code,
634                we do no filtering for Language-Monitors */
635             if (dllname[0]) {
636                 numentries++;
637                 needed += entrysize;
638                 needed += (len+1) * sizeof(WCHAR);  /* len is lstrlenW(monitorname) */
639                 if (level > 1) {
640                     /* we install and return only monitors for "Windows NT x86" */
641                     needed += (lstrlenW(x86_envnameW) +1) * sizeof(WCHAR);
642                     needed += dllsize;
643                 }
644
645                 /* required size is calculated. Now fill the user-buffer */
646                 if (pMonitors && (cbBuf >= needed)){
647                     mi = (LPMONITOR_INFO_2W) pMonitors;
648                     pMonitors += entrysize;
649
650                     TRACE("%p: writing MONITOR_INFO_%dW #%d\n", mi, level, numentries);
651                     mi->pName = ptr;
652                     lstrcpyW(ptr, buffer);      /* Name of the Monitor */
653                     ptr += (len+1);               /* len is lstrlenW(monitorname) */
654                     if (level > 1) {
655                         mi->pEnvironment = ptr;
656                         lstrcpyW(ptr, x86_envnameW); /* fixed to "Windows NT x86" */
657                         ptr += (lstrlenW(x86_envnameW)+1);
658
659                         mi->pDLLName = ptr;
660                         lstrcpyW(ptr, dllname);         /* Name of the Driver-DLL */
661                         ptr += (dllsize / sizeof(WCHAR));
662                     }
663                 }
664             }
665             index++;
666             len = sizeof(buffer)/sizeof(buffer[0]);
667             buffer[0] = '\0';
668         }
669         RegCloseKey(hroot);
670     }
671     *lpreturned = numentries;
672     TRACE("need %d byte for %d entries\n", needed, numentries);
673     return needed;
674 }
675
676 /******************************************************************
677  * enumerate the local Ports from all loaded monitors (internal)
678  *
679  * returns the needed size (in bytes) for pPorts
680  * and  *lpreturned is set to number of entries returned in pPorts
681  *
682  */
683 static DWORD get_ports_from_all_monitors(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
684 {
685     monitor_t * pm;
686     LPWSTR      ptr;
687     LPPORT_INFO_2W cache;
688     LPPORT_INFO_2W out;
689     LPBYTE  pi_buffer = NULL;
690     DWORD   pi_allocated = 0;
691     DWORD   pi_needed;
692     DWORD   pi_index;
693     DWORD   pi_returned;
694     DWORD   res;
695     DWORD   outindex = 0;
696     DWORD   needed;
697     DWORD   numentries;
698     DWORD   entrysize;
699
700
701     TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
702     entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
703
704     numentries = *lpreturned;       /* this is 0, when we scan the registry */
705     needed = entrysize * numentries;
706     ptr = (LPWSTR) &pPorts[needed];
707
708     numentries = 0;
709     needed = 0;
710
711     LIST_FOR_EACH_ENTRY(pm, &monitor_handles, monitor_t, entry)
712     {
713         if ((pm->monitor) && (pm->monitor->pfnEnumPorts)) {
714             pi_needed = 0;
715             pi_returned = 0;
716             res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
717             if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
718                 /* Do not use heap_realloc (we do not need the old data in the buffer) */
719                 heap_free(pi_buffer);
720                 pi_buffer = heap_alloc(pi_needed);
721                 pi_allocated = (pi_buffer) ? pi_needed : 0;
722                 res = pm->monitor->pfnEnumPorts(NULL, level, pi_buffer, pi_allocated, &pi_needed, &pi_returned);
723             }
724             TRACE("(%s) got %d with %d (need %d byte for %d entries)\n",
725                   debugstr_w(pm->name), res, GetLastError(), pi_needed, pi_returned);
726
727             numentries += pi_returned;
728             needed += pi_needed;
729
730             /* fill the output-buffer (pPorts), if we have one */
731             if (pPorts && (cbBuf >= needed ) && pi_buffer) {
732                 pi_index = 0;
733                 while (pi_returned > pi_index) {
734                     cache = (LPPORT_INFO_2W) &pi_buffer[pi_index * entrysize];
735                     out = (LPPORT_INFO_2W) &pPorts[outindex * entrysize];
736                     out->pPortName = ptr;
737                     lstrcpyW(ptr, cache->pPortName);
738                     ptr += (lstrlenW(ptr)+1);
739                     if (level > 1) {
740                         out->pMonitorName = ptr;
741                         lstrcpyW(ptr,  cache->pMonitorName);
742                         ptr += (lstrlenW(ptr)+1);
743
744                         out->pDescription = ptr;
745                         lstrcpyW(ptr,  cache->pDescription);
746                         ptr += (lstrlenW(ptr)+1);
747                         out->fPortType = cache->fPortType;
748                         out->Reserved = cache->Reserved;
749                     }
750                     pi_index++;
751                     outindex++;
752                 }
753             }
754         }
755     }
756     /* the temporary portinfo-buffer is no longer needed */
757     heap_free(pi_buffer);
758
759     *lpreturned = numentries;
760     TRACE("need %d byte for %d entries\n", needed, numentries);
761     return needed;
762 }
763
764
765 /*****************************************************************************
766  * open_driver_reg [internal]
767  *
768  * opens the registry for the printer drivers depending on the given input
769  * variable pEnvironment
770  *
771  * RETURNS:
772  *    Success: the opened hkey
773  *    Failure: NULL
774  */
775 static HKEY open_driver_reg(LPCWSTR pEnvironment)
776 {
777     HKEY  retval = NULL;
778     LPWSTR buffer;
779     const printenv_t * env;
780
781     TRACE("(%s)\n", debugstr_w(pEnvironment));
782
783     env = validate_envW(pEnvironment);
784     if (!env) return NULL;
785
786     buffer = HeapAlloc(GetProcessHeap(), 0, sizeof(fmt_driversW) +
787                 (lstrlenW(env->envname) + lstrlenW(env->versionregpath)) * sizeof(WCHAR));
788
789     if (buffer) {
790         wsprintfW(buffer, fmt_driversW, env->envname, env->versionregpath);
791         RegCreateKeyW(HKEY_LOCAL_MACHINE, buffer, &retval);
792         HeapFree(GetProcessHeap(), 0, buffer);
793     }
794     return retval;
795 }
796
797 /*****************************************************************************
798  * fpGetPrinterDriverDirectory [exported through PRINTPROVIDOR]
799  *
800  * Return the PATH for the Printer-Drivers
801  *
802  * PARAMS
803  *   pName            [I] Servername (NT only) or NULL (local Computer)
804  *   pEnvironment     [I] Printing-Environment (see below) or NULL (Default)
805  *   Level            [I] Structure-Level (must be 1)
806  *   pDriverDirectory [O] PTR to Buffer that receives the Result
807  *   cbBuf            [I] Size of Buffer at pDriverDirectory
808  *   pcbNeeded        [O] PTR to DWORD that receives the size in Bytes used /
809  *                        required for pDriverDirectory
810  *
811  * RETURNS
812  *   Success: TRUE  and in pcbNeeded the Bytes used in pDriverDirectory
813  *   Failure: FALSE and in pcbNeeded the Bytes required for pDriverDirectory,
814  *            if cbBuf is too small
815  *
816  *   Native Values returned in pDriverDirectory on Success:
817  *|  NT(Windows NT x86):  "%winsysdir%\\spool\\DRIVERS\\w32x86"
818  *|  NT(Windows 4.0):     "%winsysdir%\\spool\\DRIVERS\\win40"
819  *|  win9x(Windows 4.0):  "%winsysdir%"
820  *
821  *   "%winsysdir%" is the Value from GetSystemDirectoryW()
822  *
823  */
824 static BOOL WINAPI fpGetPrinterDriverDirectory(LPWSTR pName, LPWSTR pEnvironment,
825             DWORD Level, LPBYTE pDriverDirectory, DWORD cbBuf, LPDWORD pcbNeeded)
826 {
827     DWORD needed;
828     const printenv_t * env;
829
830     TRACE("(%s, %s, %d, %p, %d, %p)\n", debugstr_w(pName),
831           debugstr_w(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
832
833     if (pName != NULL && pName[0]) {
834         FIXME("server %s not supported\n", debugstr_w(pName));
835         SetLastError(ERROR_INVALID_PARAMETER);
836         return FALSE;
837     }
838
839     env = validate_envW(pEnvironment);
840     if (!env) return FALSE;  /* pEnvironment invalid or unsupported */
841
842
843     /* GetSystemDirectoryW returns number of WCHAR including the '\0' */
844     needed = GetSystemDirectoryW(NULL, 0);
845     /* add the Size for the Subdirectories */
846     needed += lstrlenW(spooldriversW);
847     needed += lstrlenW(env->subdir);
848     needed *= sizeof(WCHAR);  /* return-value is size in Bytes */
849
850     *pcbNeeded = needed;
851
852     if (needed > cbBuf) {
853         SetLastError(ERROR_INSUFFICIENT_BUFFER);
854         return FALSE;
855     }
856
857     if (pDriverDirectory == NULL) {
858         /* ERROR_INVALID_USER_BUFFER is NT, ERROR_INVALID_PARAMETER is win9x */
859         SetLastError(ERROR_INVALID_USER_BUFFER);
860         return FALSE;
861     }
862
863     GetSystemDirectoryW((LPWSTR) pDriverDirectory, cbBuf/sizeof(WCHAR));
864     /* add the Subdirectories */
865     lstrcatW((LPWSTR) pDriverDirectory, spooldriversW);
866     lstrcatW((LPWSTR) pDriverDirectory, env->subdir);
867
868     TRACE("=> %s\n", debugstr_w((LPWSTR) pDriverDirectory));
869     return TRUE;
870 }
871
872 /******************************************************************
873  * driver_load [internal]
874  *
875  * load a driver user interface dll
876  *
877  * On failure, NULL is returned
878  *
879  */
880
881 static HMODULE driver_load(const printenv_t * env, LPWSTR dllname)
882 {
883     WCHAR fullname[MAX_PATH];
884     HMODULE hui;
885     DWORD len;
886
887     TRACE("(%p, %s)\n", env, debugstr_w(dllname));
888
889     /* build the driverdir */
890     len = sizeof(fullname) -
891           (lstrlenW(env->versionsubdir) + 1 + lstrlenW(dllname) + 1) * sizeof(WCHAR);
892
893     if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
894                                      (LPBYTE) fullname, len, &len)) {
895         /* Should never Fail */
896         SetLastError(ERROR_BUFFER_OVERFLOW);
897         return NULL;
898     }
899
900     lstrcatW(fullname, env->versionsubdir);
901     lstrcatW(fullname, backslashW);
902     lstrcatW(fullname, dllname);
903
904     hui = LoadLibraryW(fullname);
905     TRACE("%p: LoadLibrary(%s) %d\n", hui, debugstr_w(fullname), GetLastError());
906
907     return hui;
908 }
909
910 /******************************************************************************
911  *  myAddPrinterDriverEx [internal]
912  *
913  * Install a Printer Driver with the Option to upgrade / downgrade the Files
914  * and a special mode with lazy error checking.
915  *
916  */
917 static BOOL myAddPrinterDriverEx(DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags, BOOL lazy)
918 {
919     static const WCHAR emptyW[1];
920     const printenv_t *env;
921     apd_data_t apd;
922     DRIVER_INFO_8W di;
923     BOOL    (WINAPI *pDrvDriverEvent)(DWORD, DWORD, LPBYTE, LPARAM);
924     HMODULE hui;
925     LPWSTR  ptr;
926     HKEY    hroot;
927     HKEY    hdrv;
928     DWORD   disposition;
929     DWORD   len;
930     LONG    lres;
931     BOOL    res;
932
933     /* we need to set all entries in the Registry, independent from the Level of
934        DRIVER_INFO, that the caller supplied */
935
936     ZeroMemory(&di, sizeof(di));
937     if (pDriverInfo && (level < (sizeof(di_sizeof) / sizeof(di_sizeof[0])))) {
938         memcpy(&di, pDriverInfo, di_sizeof[level]);
939     }
940
941     /* dump the most used infos */
942     TRACE("%p: .cVersion    : 0x%x/%d\n", pDriverInfo, di.cVersion, di.cVersion);
943     TRACE("%p: .pName       : %s\n", di.pName, debugstr_w(di.pName));
944     TRACE("%p: .pEnvironment: %s\n", di.pEnvironment, debugstr_w(di.pEnvironment));
945     TRACE("%p: .pDriverPath : %s\n", di.pDriverPath, debugstr_w(di.pDriverPath));
946     TRACE("%p: .pDataFile   : %s\n", di.pDataFile, debugstr_w(di.pDataFile));
947     TRACE("%p: .pConfigFile : %s\n", di.pConfigFile, debugstr_w(di.pConfigFile));
948     TRACE("%p: .pHelpFile   : %s\n", di.pHelpFile, debugstr_w(di.pHelpFile));
949     /* dump only the first of the additional Files */
950     TRACE("%p: .pDependentFiles: %s\n", di.pDependentFiles, debugstr_w(di.pDependentFiles));
951
952
953     /* check environment */
954     env = validate_envW(di.pEnvironment);
955     if (env == NULL) return FALSE;        /* ERROR_INVALID_ENVIRONMENT */
956
957     /* fill the copy-data / get the driverdir */
958     len = sizeof(apd.src) - sizeof(version3_subdirW) - sizeof(WCHAR);
959     if (!fpGetPrinterDriverDirectory(NULL, (LPWSTR) env->envname, 1,
960                                     (LPBYTE) apd.src, len, &len)) {
961         /* Should never Fail */
962         return FALSE;
963     }
964     memcpy(apd.dst, apd.src, len);
965     lstrcatW(apd.src, backslashW);
966     apd.srclen = lstrlenW(apd.src);
967     lstrcatW(apd.dst, env->versionsubdir);
968     lstrcatW(apd.dst, backslashW);
969     apd.dstlen = lstrlenW(apd.dst);
970     apd.copyflags = dwFileCopyFlags;
971     apd.lazy = lazy;
972     CreateDirectoryW(apd.src, NULL);
973     CreateDirectoryW(apd.dst, NULL);
974
975     hroot = open_driver_reg(env->envname);
976     if (!hroot) {
977         ERR("Can't create Drivers key\n");
978         return FALSE;
979     }
980
981     /* Fill the Registry for the Driver */
982     if ((lres = RegCreateKeyExW(hroot, di.pName, 0, NULL, REG_OPTION_NON_VOLATILE,
983                                 KEY_WRITE | KEY_QUERY_VALUE, NULL,
984                                 &hdrv, &disposition)) != ERROR_SUCCESS) {
985
986         ERR("can't create driver %s: %u\n", debugstr_w(di.pName), lres);
987         RegCloseKey(hroot);
988         SetLastError(lres);
989         return FALSE;
990     }
991     RegCloseKey(hroot);
992
993     if (disposition == REG_OPENED_EXISTING_KEY) {
994         TRACE("driver %s already installed\n", debugstr_w(di.pName));
995         RegCloseKey(hdrv);
996         SetLastError(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED);
997         return FALSE;
998     }
999
1000     /* Verified with the Adobe PS Driver, that w2k does not use di.Version */
1001     RegSetValueExW(hdrv, versionW, 0, REG_DWORD, (LPBYTE) &env->driverversion,
1002                    sizeof(DWORD));
1003
1004     RegSetValueExW(hdrv, driverW, 0, REG_SZ, (LPBYTE) di.pDriverPath,
1005                    (lstrlenW(di.pDriverPath)+1)* sizeof(WCHAR));
1006     apd_copyfile(di.pDriverPath, &apd);
1007
1008     RegSetValueExW(hdrv, data_fileW, 0, REG_SZ, (LPBYTE) di.pDataFile,
1009                    (lstrlenW(di.pDataFile)+1)* sizeof(WCHAR));
1010     apd_copyfile(di.pDataFile, &apd);
1011
1012     RegSetValueExW(hdrv, configuration_fileW, 0, REG_SZ, (LPBYTE) di.pConfigFile,
1013                    (lstrlenW(di.pConfigFile)+1)* sizeof(WCHAR));
1014     apd_copyfile(di.pConfigFile, &apd);
1015
1016     /* settings for level 3 */
1017     if (di.pHelpFile)
1018         RegSetValueExW(hdrv, help_fileW, 0, REG_SZ, (LPBYTE) di.pHelpFile,
1019                        (lstrlenW(di.pHelpFile)+1)* sizeof(WCHAR));
1020     else
1021         RegSetValueExW(hdrv, help_fileW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1022     apd_copyfile(di.pHelpFile, &apd);
1023
1024
1025     ptr = di.pDependentFiles;
1026     if (ptr)
1027         RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE) di.pDependentFiles,
1028                        multi_sz_lenW(di.pDependentFiles));
1029     else
1030         RegSetValueExW(hdrv, dependent_filesW, 0, REG_MULTI_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1031     while ((ptr != NULL) && (ptr[0])) {
1032         if (apd_copyfile(ptr, &apd)) {
1033             ptr += lstrlenW(ptr) + 1;
1034         }
1035         else
1036         {
1037             WARN("Failed to copy %s\n", debugstr_w(ptr));
1038             ptr = NULL;
1039         }
1040     }
1041     /* The language-Monitor was already copied by the caller to "%SystemRoot%\system32" */
1042     if (di.pMonitorName)
1043         RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE) di.pMonitorName,
1044                        (lstrlenW(di.pMonitorName)+1)* sizeof(WCHAR));
1045     else
1046         RegSetValueExW(hdrv, monitorW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1047
1048     if (di.pDefaultDataType)
1049         RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE) di.pDefaultDataType,
1050                        (lstrlenW(di.pDefaultDataType)+1)* sizeof(WCHAR));
1051     else
1052         RegSetValueExW(hdrv, datatypeW, 0, REG_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1053
1054     /* settings for level 4 */
1055     if (di.pszzPreviousNames)
1056         RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE) di.pszzPreviousNames,
1057                        multi_sz_lenW(di.pszzPreviousNames));
1058     else
1059         RegSetValueExW(hdrv, previous_namesW, 0, REG_MULTI_SZ, (LPBYTE)emptyW, sizeof(emptyW));
1060
1061     if (level > 5) TRACE("level %u for Driver %s is incomplete\n", level, debugstr_w(di.pName));
1062
1063     RegCloseKey(hdrv);
1064     hui = driver_load(env, di.pConfigFile);
1065     pDrvDriverEvent = (void *)GetProcAddress(hui, "DrvDriverEvent");
1066     if (hui && pDrvDriverEvent) {
1067
1068         /* Support for DrvDriverEvent is optional */
1069         TRACE("DRIVER_EVENT_INITIALIZE for %s (%s)\n", debugstr_w(di.pName), debugstr_w(di.pConfigFile));
1070         /* MSDN: level for DRIVER_INFO is 1 to 3 */
1071         res = pDrvDriverEvent(DRIVER_EVENT_INITIALIZE, 3, (LPBYTE) &di, 0);
1072         TRACE("got %d from DRIVER_EVENT_INITIALIZE\n", res);
1073     }
1074     FreeLibrary(hui);
1075
1076     TRACE("=> TRUE with %u\n", GetLastError());
1077     return TRUE;
1078
1079 }
1080
1081 /******************************************************************************
1082  * fpAddMonitor [exported through PRINTPROVIDOR]
1083  *
1084  * Install a Printmonitor
1085  *
1086  * PARAMS
1087  *  pName       [I] Servername or NULL (local Computer)
1088  *  Level       [I] Structure-Level (Must be 2)
1089  *  pMonitors   [I] PTR to MONITOR_INFO_2
1090  *
1091  * RETURNS
1092  *  Success: TRUE
1093  *  Failure: FALSE
1094  *
1095  * NOTES
1096  *  All Files for the Monitor must already be copied to %winsysdir% ("%SystemRoot%\system32")
1097  *
1098  */
1099 static BOOL WINAPI fpAddMonitor(LPWSTR pName, DWORD Level, LPBYTE pMonitors)
1100 {
1101     monitor_t * pm = NULL;
1102     LPMONITOR_INFO_2W mi2w;
1103     HKEY    hroot = NULL;
1104     HKEY    hentry = NULL;
1105     DWORD   disposition;
1106     BOOL    res = FALSE;
1107
1108     mi2w = (LPMONITOR_INFO_2W) pMonitors;
1109     TRACE("(%s, %d, %p): %s %s %s\n", debugstr_w(pName), Level, pMonitors,
1110             debugstr_w(mi2w ? mi2w->pName : NULL),
1111             debugstr_w(mi2w ? mi2w->pEnvironment : NULL),
1112             debugstr_w(mi2w ? mi2w->pDLLName : NULL));
1113
1114     if (copy_servername_from_name(pName, NULL)) {
1115         FIXME("server %s not supported\n", debugstr_w(pName));
1116         SetLastError(ERROR_ACCESS_DENIED);
1117         return FALSE;
1118     }
1119
1120     if (!mi2w->pName || (! mi2w->pName[0])) {
1121         WARN("pName not valid : %s\n", debugstr_w(mi2w->pName));
1122         SetLastError(ERROR_INVALID_PARAMETER);
1123         return FALSE;
1124     }
1125     if (!mi2w->pEnvironment || lstrcmpW(mi2w->pEnvironment, x86_envnameW)) {
1126         WARN("Environment %s requested (we support only %s)\n",
1127                 debugstr_w(mi2w->pEnvironment), debugstr_w(x86_envnameW));
1128         SetLastError(ERROR_INVALID_ENVIRONMENT);
1129         return FALSE;
1130     }
1131
1132     if (!mi2w->pDLLName || (! mi2w->pDLLName[0])) {
1133         WARN("pDLLName not valid : %s\n", debugstr_w(mi2w->pDLLName));
1134         SetLastError(ERROR_INVALID_PARAMETER);
1135         return FALSE;
1136     }
1137
1138     /* Load and initialize the monitor. SetLastError() is called on failure */
1139     if ((pm = monitor_load(mi2w->pName, mi2w->pDLLName)) == NULL) {
1140         return FALSE;
1141     }
1142     monitor_unload(pm);
1143
1144     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1145         ERR("unable to create key %s\n", debugstr_w(monitorsW));
1146         return FALSE;
1147     }
1148
1149     if (RegCreateKeyExW(hroot, mi2w->pName, 0, NULL, REG_OPTION_NON_VOLATILE,
1150                         KEY_WRITE | KEY_QUERY_VALUE, NULL, &hentry,
1151                         &disposition) == ERROR_SUCCESS) {
1152
1153         /* Some installers set options for the port before calling AddMonitor.
1154            We query the "Driver" entry to verify that the monitor is installed,
1155            before we return an error.
1156            When a user installs two print monitors at the same time with the
1157            same name, a race condition is possible but silently ignored. */
1158
1159         DWORD   namesize = 0;
1160
1161         if ((disposition == REG_OPENED_EXISTING_KEY) &&
1162             (RegQueryValueExW(hentry, driverW, NULL, NULL, NULL,
1163                               &namesize) == ERROR_SUCCESS)) {
1164             TRACE("monitor %s already exists\n", debugstr_w(mi2w->pName));
1165             /* 9x use ERROR_ALREADY_EXISTS */
1166             SetLastError(ERROR_PRINT_MONITOR_ALREADY_INSTALLED);
1167         }
1168         else
1169         {
1170             INT len;
1171             len = (lstrlenW(mi2w->pDLLName) +1) * sizeof(WCHAR);
1172             res = (RegSetValueExW(hentry, driverW, 0, REG_SZ,
1173                     (LPBYTE) mi2w->pDLLName, len) == ERROR_SUCCESS);
1174         }
1175         RegCloseKey(hentry);
1176     }
1177
1178     RegCloseKey(hroot);
1179     return (res);
1180 }
1181
1182 /******************************************************************************
1183  * fpAddPrinterDriverEx [exported through PRINTPROVIDOR]
1184  *
1185  * Install a Printer Driver with the Option to upgrade / downgrade the Files
1186  *
1187  * PARAMS
1188  *  pName           [I] Servername or NULL (local Computer)
1189  *  level           [I] Level for the supplied DRIVER_INFO_*W struct
1190  *  pDriverInfo     [I] PTR to DRIVER_INFO_*W struct with the Driver Parameter
1191  *  dwFileCopyFlags [I] How to Copy / Upgrade / Downgrade the needed Files
1192  *
1193  * RESULTS
1194  *  Success: TRUE
1195  *  Failure: FALSE
1196  *
1197  */
1198 static BOOL WINAPI fpAddPrinterDriverEx(LPWSTR pName, DWORD level, LPBYTE pDriverInfo, DWORD dwFileCopyFlags)
1199 {
1200     LONG lres;
1201
1202     TRACE("(%s, %d, %p, 0x%x)\n", debugstr_w(pName), level, pDriverInfo, dwFileCopyFlags);
1203     lres = copy_servername_from_name(pName, NULL);
1204     if (lres) {
1205         FIXME("server %s not supported\n", debugstr_w(pName));
1206         SetLastError(ERROR_ACCESS_DENIED);
1207         return FALSE;
1208     }
1209
1210     if ((dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY) != APD_COPY_ALL_FILES) {
1211         TRACE("Flags 0x%x ignored (using APD_COPY_ALL_FILES)\n", dwFileCopyFlags & ~APD_COPY_FROM_DIRECTORY);
1212     }
1213
1214     return myAddPrinterDriverEx(level, pDriverInfo, dwFileCopyFlags, TRUE);
1215 }
1216 /******************************************************************
1217  * fpDeleteMonitor [exported through PRINTPROVIDOR]
1218  *
1219  * Delete a specific Printmonitor from a Printing-Environment
1220  *
1221  * PARAMS
1222  *  pName        [I] Servername or NULL (local Computer)
1223  *  pEnvironment [I] Printing-Environment of the Monitor or NULL (Default)
1224  *  pMonitorName [I] Name of the Monitor, that should be deleted
1225  *
1226  * RETURNS
1227  *  Success: TRUE
1228  *  Failure: FALSE
1229  *
1230  * NOTES
1231  *  pEnvironment is ignored in Windows for the local Computer.
1232  *
1233  */
1234
1235 static BOOL WINAPI fpDeleteMonitor(LPWSTR pName, LPWSTR pEnvironment, LPWSTR pMonitorName)
1236 {
1237     HKEY    hroot = NULL;
1238     LONG    lres;
1239
1240     TRACE("(%s, %s, %s)\n",debugstr_w(pName),debugstr_w(pEnvironment),
1241            debugstr_w(pMonitorName));
1242
1243     lres = copy_servername_from_name(pName, NULL);
1244     if (lres) {
1245         FIXME("server %s not supported\n", debugstr_w(pName));
1246         SetLastError(ERROR_INVALID_NAME);
1247         return FALSE;
1248     }
1249
1250     /*  pEnvironment is ignored in Windows for the local Computer */
1251     if (!pMonitorName || !pMonitorName[0]) {
1252         TRACE("pMonitorName %s is invalid\n", debugstr_w(pMonitorName));
1253         SetLastError(ERROR_INVALID_PARAMETER);
1254         return FALSE;
1255     }
1256
1257     if(RegCreateKeyW(HKEY_LOCAL_MACHINE, monitorsW, &hroot) != ERROR_SUCCESS) {
1258         ERR("unable to create key %s\n", debugstr_w(monitorsW));
1259         return FALSE;
1260     }
1261
1262     if(RegDeleteTreeW(hroot, pMonitorName) == ERROR_SUCCESS) {
1263         TRACE("%s deleted\n", debugstr_w(pMonitorName));
1264         RegCloseKey(hroot);
1265         return TRUE;
1266     }
1267
1268     TRACE("%s does not exist\n", debugstr_w(pMonitorName));
1269     RegCloseKey(hroot);
1270
1271     /* NT: ERROR_UNKNOWN_PRINT_MONITOR (3000), 9x: ERROR_INVALID_PARAMETER (87) */
1272     SetLastError(ERROR_UNKNOWN_PRINT_MONITOR);
1273     return FALSE;
1274 }
1275
1276 /*****************************************************************************
1277  * fpEnumMonitors [exported through PRINTPROVIDOR]
1278  *
1279  * Enumerate available Port-Monitors
1280  *
1281  * PARAMS
1282  *  pName      [I] Servername or NULL (local Computer)
1283  *  Level      [I] Structure-Level (1:Win9x+NT or 2:NT only)
1284  *  pMonitors  [O] PTR to Buffer that receives the Result
1285  *  cbBuf      [I] Size of Buffer at pMonitors
1286  *  pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / required for pMonitors
1287  *  pcReturned [O] PTR to DWORD that receives the number of Monitors in pMonitors
1288  *
1289  * RETURNS
1290  *  Success: TRUE
1291  *  Failure: FALSE and in pcbNeeded the Bytes required for pMonitors, if cbBuf is too small
1292  *
1293  * NOTES
1294  *  Windows reads the Registry once and cache the Results.
1295  *
1296  */
1297 static BOOL WINAPI fpEnumMonitors(LPWSTR pName, DWORD Level, LPBYTE pMonitors, DWORD cbBuf,
1298                                   LPDWORD pcbNeeded, LPDWORD pcReturned)
1299 {
1300     DWORD   numentries = 0;
1301     DWORD   needed = 0;
1302     LONG    lres;
1303     BOOL    res = FALSE;
1304
1305     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pMonitors,
1306           cbBuf, pcbNeeded, pcReturned);
1307
1308     lres = copy_servername_from_name(pName, NULL);
1309     if (lres) {
1310         FIXME("server %s not supported\n", debugstr_w(pName));
1311         SetLastError(ERROR_INVALID_NAME);
1312         goto em_cleanup;
1313     }
1314
1315     if (!Level || (Level > 2)) {
1316         WARN("level (%d) is ignored in win9x\n", Level);
1317         SetLastError(ERROR_INVALID_LEVEL);
1318         return FALSE;
1319     }
1320
1321     /* Scan all Monitor-Keys */
1322     numentries = 0;
1323     needed = get_local_monitors(Level, NULL, 0, &numentries);
1324
1325     /* we calculated the needed buffersize. now do more error-checks */
1326     if (cbBuf < needed) {
1327         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1328         goto em_cleanup;
1329     }
1330
1331     /* fill the Buffer with the Monitor-Keys */
1332     needed = get_local_monitors(Level, pMonitors, cbBuf, &numentries);
1333     res = TRUE;
1334
1335 em_cleanup:
1336     if (pcbNeeded)  *pcbNeeded = needed;
1337     if (pcReturned) *pcReturned = numentries;
1338
1339     TRACE("returning %d with %d (%d byte for %d entries)\n",
1340             res, GetLastError(), needed, numentries);
1341
1342     return (res);
1343 }
1344
1345 /******************************************************************************
1346  * fpEnumPorts [exported through PRINTPROVIDOR]
1347  *
1348  * Enumerate available Ports
1349  *
1350  * PARAMS
1351  *  pName      [I] Servername or NULL (local Computer)
1352  *  Level      [I] Structure-Level (1 or 2)
1353  *  pPorts     [O] PTR to Buffer that receives the Result
1354  *  cbBuf      [I] Size of Buffer at pPorts
1355  *  pcbNeeded  [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
1356  *  pcReturned [O] PTR to DWORD that receives the number of Ports in pPorts
1357  *
1358  * RETURNS
1359  *  Success: TRUE
1360  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
1361  *
1362  */
1363 static BOOL WINAPI fpEnumPorts(LPWSTR pName, DWORD Level, LPBYTE pPorts, DWORD cbBuf,
1364                                LPDWORD pcbNeeded, LPDWORD pcReturned)
1365 {
1366     DWORD   needed = 0;
1367     DWORD   numentries = 0;
1368     LONG    lres;
1369     BOOL    res = FALSE;
1370
1371     TRACE("(%s, %d, %p, %d, %p, %p)\n", debugstr_w(pName), Level, pPorts,
1372           cbBuf, pcbNeeded, pcReturned);
1373
1374     lres = copy_servername_from_name(pName, NULL);
1375     if (lres) {
1376         FIXME("server %s not supported\n", debugstr_w(pName));
1377         SetLastError(ERROR_INVALID_NAME);
1378         goto emP_cleanup;
1379     }
1380
1381     if (!Level || (Level > 2)) {
1382         SetLastError(ERROR_INVALID_LEVEL);
1383         goto emP_cleanup;
1384     }
1385
1386     if (!pcbNeeded || (!pPorts && (cbBuf > 0))) {
1387         SetLastError(RPC_X_NULL_REF_POINTER);
1388         goto emP_cleanup;
1389     }
1390
1391     EnterCriticalSection(&monitor_handles_cs);
1392     monitor_loadall();
1393
1394     /* Scan all local Ports */
1395     numentries = 0;
1396     needed = get_ports_from_all_monitors(Level, NULL, 0, &numentries);
1397
1398     /* we calculated the needed buffersize. now do the error-checks */
1399     if (cbBuf < needed) {
1400         monitor_unloadall();
1401         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1402         goto emP_cleanup_cs;
1403     }
1404     else if (!pPorts || !pcReturned) {
1405         monitor_unloadall();
1406         SetLastError(RPC_X_NULL_REF_POINTER);
1407         goto emP_cleanup_cs;
1408     }
1409
1410     /* Fill the Buffer */
1411     needed = get_ports_from_all_monitors(Level, pPorts, cbBuf, &numentries);
1412     res = TRUE;
1413     monitor_unloadall();
1414
1415 emP_cleanup_cs:
1416     LeaveCriticalSection(&monitor_handles_cs);
1417
1418 emP_cleanup:
1419     if (pcbNeeded)  *pcbNeeded = needed;
1420     if (pcReturned) *pcReturned = (res) ? numentries : 0;
1421
1422     TRACE("returning %d with %d (%d byte for %d of %d entries)\n",
1423           (res), GetLastError(), needed, (res) ? numentries : 0, numentries);
1424
1425     return (res);
1426 }
1427
1428 /*****************************************************
1429  *  setup_provider [internal]
1430  */
1431 void setup_provider(void)
1432 {
1433     static const PRINTPROVIDOR backend = {
1434         NULL,   /* fpOpenPrinter */
1435         NULL,   /* fpSetJob */
1436         NULL,   /* fpGetJob */
1437         NULL,   /* fpEnumJobs */
1438         NULL,   /* fpAddPrinter */
1439         NULL,   /* fpDeletePrinter */
1440         NULL,   /* fpSetPrinter */
1441         NULL,   /* fpGetPrinter */
1442         NULL,   /* fpEnumPrinters */
1443         NULL,   /* fpAddPrinterDriver */
1444         NULL,   /* fpEnumPrinterDrivers */
1445         NULL,   /* fpGetPrinterDriver */
1446         fpGetPrinterDriverDirectory,
1447         NULL,   /* fpDeletePrinterDriver */
1448         NULL,   /* fpAddPrintProcessor */
1449         NULL,   /* fpEnumPrintProcessors */
1450         NULL,   /* fpGetPrintProcessorDirectory */
1451         NULL,   /* fpDeletePrintProcessor */
1452         NULL,   /* fpEnumPrintProcessorDatatypes */
1453         NULL,   /* fpStartDocPrinter */
1454         NULL,   /* fpStartPagePrinter */
1455         NULL,   /* fpWritePrinter */
1456         NULL,   /* fpEndPagePrinter */
1457         NULL,   /* fpAbortPrinter */
1458         NULL,   /* fpReadPrinter */
1459         NULL,   /* fpEndDocPrinter */
1460         NULL,   /* fpAddJob */
1461         NULL,   /* fpScheduleJob */
1462         NULL,   /* fpGetPrinterData */
1463         NULL,   /* fpSetPrinterData */
1464         NULL,   /* fpWaitForPrinterChange */
1465         NULL,   /* fpClosePrinter */
1466         NULL,   /* fpAddForm */
1467         NULL,   /* fpDeleteForm */
1468         NULL,   /* fpGetForm */
1469         NULL,   /* fpSetForm */
1470         NULL,   /* fpEnumForms */
1471         fpEnumMonitors,
1472         fpEnumPorts,
1473         NULL,   /* fpAddPort */
1474         NULL,   /* fpConfigurePort */
1475         NULL,   /* fpDeletePort */
1476         NULL,   /* fpCreatePrinterIC */
1477         NULL,   /* fpPlayGdiScriptOnPrinterIC */
1478         NULL,   /* fpDeletePrinterIC */
1479         NULL,   /* fpAddPrinterConnection */
1480         NULL,   /* fpDeletePrinterConnection */
1481         NULL,   /* fpPrinterMessageBox */
1482         fpAddMonitor,
1483         fpDeleteMonitor,
1484         NULL,   /* fpResetPrinter */
1485         NULL,   /* fpGetPrinterDriverEx */
1486         NULL,   /* fpFindFirstPrinterChangeNotification */
1487         NULL,   /* fpFindClosePrinterChangeNotification */
1488         NULL,   /* fpAddPortEx */
1489         NULL,   /* fpShutDown */
1490         NULL,   /* fpRefreshPrinterChangeNotification */
1491         NULL,   /* fpOpenPrinterEx */
1492         NULL,   /* fpAddPrinterEx */
1493         NULL,   /* fpSetPort */
1494         NULL,   /* fpEnumPrinterData */
1495         NULL,   /* fpDeletePrinterData */
1496         NULL,   /* fpClusterSplOpen */
1497         NULL,   /* fpClusterSplClose */
1498         NULL,   /* fpClusterSplIsAlive */
1499         NULL,   /* fpSetPrinterDataEx */
1500         NULL,   /* fpGetPrinterDataEx */
1501         NULL,   /* fpEnumPrinterDataEx */
1502         NULL,   /* fpEnumPrinterKey */
1503         NULL,   /* fpDeletePrinterDataEx */
1504         NULL,   /* fpDeletePrinterKey */
1505         NULL,   /* fpSeekPrinter */
1506         NULL,   /* fpDeletePrinterDriverEx */
1507         NULL,   /* fpAddPerMachineConnection */
1508         NULL,   /* fpDeletePerMachineConnection */
1509         NULL,   /* fpEnumPerMachineConnections */
1510         NULL,   /* fpXcvData */
1511         fpAddPrinterDriverEx,
1512         NULL,   /* fpSplReadPrinter */
1513         NULL,   /* fpDriverUnloadComplete */
1514         NULL,   /* fpGetSpoolFileInfo */
1515         NULL,   /* fpCommitSpoolData */
1516         NULL,   /* fpCloseSpoolFileHandle */
1517         NULL,   /* fpFlushPrinter */
1518         NULL,   /* fpSendRecvBidiData */
1519         NULL    /* fpAddDriverCatalog */
1520     };
1521     pprovider = &backend;
1522
1523 }
1524
1525 /*****************************************************
1526  * InitializePrintProvidor     (localspl.@)
1527  *
1528  * Initialize the Printprovider
1529  *
1530  * PARAMS
1531  *  pPrintProvidor    [I] Buffer to fill with a struct PRINTPROVIDOR
1532  *  cbPrintProvidor   [I] Size of Buffer in Bytes
1533  *  pFullRegistryPath [I] Registry-Path for the Printprovidor
1534  *
1535  * RETURNS
1536  *  Success: TRUE and pPrintProvidor filled
1537  *  Failure: FALSE
1538  *
1539  * NOTES
1540  *  The RegistryPath should be:
1541  *  "System\CurrentControlSet\Control\Print\Providers\<providername>",
1542  *  but this Parameter is ignored in "localspl.dll".
1543  *
1544  */
1545
1546 BOOL WINAPI InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,
1547                                     DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
1548 {
1549
1550     TRACE("(%p, %u, %s)\n", pPrintProvidor, cbPrintProvidor, debugstr_w(pFullRegistryPath));
1551     memcpy(pPrintProvidor, pprovider,
1552           (cbPrintProvidor < sizeof(PRINTPROVIDOR)) ? cbPrintProvidor : sizeof(PRINTPROVIDOR));
1553
1554     return TRUE;
1555 }