msi: Close the original record before copying new data into it.
[wine] / dlls / localspl / localmon.c
1 /*
2  * Implementation of the Local Printmonitor
3  *
4  * Copyright 2006 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 "winuser.h"
30 #include "winreg.h"
31
32 #include "winspool.h"
33 #include "ddk/winsplp.h"
34 #include "localspl_private.h"
35
36 #include "wine/debug.h"
37 #include "wine/list.h"
38 #include "wine/unicode.h"
39
40
41 WINE_DEFAULT_DEBUG_CHANNEL(localspl);
42
43 /*****************************************************/
44
45 static CRITICAL_SECTION port_handles_cs;
46 static CRITICAL_SECTION_DEBUG port_handles_cs_debug =
47 {
48     0, 0, &port_handles_cs,
49     { &port_handles_cs_debug.ProcessLocksList, &port_handles_cs_debug.ProcessLocksList },
50       0, 0, { (DWORD_PTR)(__FILE__ ": port_handles_cs") }
51 };
52 static CRITICAL_SECTION port_handles_cs = { &port_handles_cs_debug, -1, 0, 0, 0, 0 };
53
54
55 static CRITICAL_SECTION xcv_handles_cs;
56 static CRITICAL_SECTION_DEBUG xcv_handles_cs_debug =
57 {
58     0, 0, &xcv_handles_cs,
59     { &xcv_handles_cs_debug.ProcessLocksList, &xcv_handles_cs_debug.ProcessLocksList },
60       0, 0, { (DWORD_PTR)(__FILE__ ": xcv_handles_cs") }
61 };
62 static CRITICAL_SECTION xcv_handles_cs = { &xcv_handles_cs_debug, -1, 0, 0, 0, 0 };
63
64 /* ############################### */
65
66 typedef struct {
67     struct list entry;
68     DWORD   type;
69     WCHAR   nameW[1];
70 } port_t;
71
72 typedef struct {
73     struct list entry;
74     ACCESS_MASK GrantedAccess;
75     WCHAR       nameW[1];
76 } xcv_t;
77
78 static struct list port_handles = LIST_INIT( port_handles );
79 static struct list xcv_handles = LIST_INIT( xcv_handles );
80
81 /* ############################### */
82
83 static const WCHAR cmd_AddPortW[] = {'A','d','d','P','o','r','t',0};
84 static const WCHAR cmd_DeletePortW[] = {'D','e','l','e','t','e','P','o','r','t',0};
85 static const WCHAR cmd_ConfigureLPTPortCommandOKW[] = {'C','o','n','f','i','g','u','r','e',
86                                     'L','P','T','P','o','r','t',
87                                     'C','o','m','m','a','n','d','O','K',0};
88
89 static const WCHAR cmd_GetDefaultCommConfigW[] = {'G','e','t',
90                                     'D','e','f','a','u','l','t',
91                                     'C','o','m','m','C','o','n','f','i','g',0};
92
93 static const WCHAR cmd_GetTransmissionRetryTimeoutW[] = {'G','e','t',
94                                     'T','r','a','n','s','m','i','s','s','i','o','n',
95                                     'R','e','t','r','y','T','i','m','e','o','u','t',0};
96
97 static const WCHAR cmd_MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
98 static const WCHAR cmd_PortIsValidW[] = {'P','o','r','t','I','s','V','a','l','i','d',0};
99 static const WCHAR cmd_SetDefaultCommConfigW[] = {'S','e','t',
100                                     'D','e','f','a','u','l','t',
101                                     'C','o','m','m','C','o','n','f','i','g',0};
102
103 static const WCHAR dllnameuiW[] = {'l','o','c','a','l','u','i','.','d','l','l',0};
104 static const WCHAR emptyW[] = {0};
105 static const WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
106
107 static const WCHAR portname_LPT[]  = {'L','P','T',0};
108 static const WCHAR portname_COM[]  = {'C','O','M',0};
109 static const WCHAR portname_FILE[] = {'F','I','L','E',':',0};
110 static const WCHAR portname_CUPS[] = {'C','U','P','S',':',0};
111 static const WCHAR portname_LPR[]  = {'L','P','R',':',0};
112
113 static const WCHAR TransmissionRetryTimeoutW[] = {'T','r','a','n','s','m','i','s','s','i','o','n',
114                                     'R','e','t','r','y','T','i','m','e','o','u','t',0};
115
116 static const WCHAR WinNT_CV_PortsW[] = {'S','o','f','t','w','a','r','e','\\',
117                                         'M','i','c','r','o','s','o','f','t','\\',
118                                         'W','i','n','d','o','w','s',' ','N','T','\\',
119                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
120                                         'P','o','r','t','s',0};
121
122 static const WCHAR WinNT_CV_WindowsW[] = {'S','o','f','t','w','a','r','e','\\',
123                                         'M','i','c','r','o','s','o','f','t','\\',
124                                         'W','i','n','d','o','w','s',' ','N','T','\\',
125                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
126                                         'W','i','n','d','o','w','s',0};
127
128
129 /******************************************************************
130  * does_port_exist (internal)
131  *
132  * returns TRUE, when the Port already exists
133  *
134  */
135 static BOOL does_port_exist(LPCWSTR myname)
136 {
137
138     LPPORT_INFO_1W  pi;
139     DWORD   needed = 0;
140     DWORD   returned;
141     DWORD   id;
142
143     TRACE("(%s)\n", debugstr_w(myname));
144
145     id = EnumPortsW(NULL, 1, NULL, 0, &needed, &returned);
146     pi = heap_alloc(needed);
147     returned = 0;
148     if (pi)
149         id = EnumPortsW(NULL, 1, (LPBYTE) pi, needed, &needed, &returned);
150
151     if (id && returned > 0) {
152         /* we got a number of valid names. */
153         for (id = 0; id < returned; id++)
154         {
155             if (lstrcmpiW(myname, pi[id].pName) == 0) {
156                 TRACE("(%u) found %s\n", id, debugstr_w(pi[id].pName));
157                 heap_free(pi);
158                 return TRUE;
159             }
160         }
161     }
162
163     heap_free(pi);
164     return FALSE;
165 }
166
167 /******************************************************************
168  * enumerate the local Ports from the Registry (internal)  
169  *
170  * See localmon_EnumPortsW.
171  *
172  * NOTES
173  *  returns the needed size (in bytes) for pPorts
174  *  and *lpreturned is set to number of entries returned in pPorts
175  *
176  */
177
178 static DWORD get_ports_from_reg(DWORD level, LPBYTE pPorts, DWORD cbBuf, LPDWORD lpreturned)
179 {
180     HKEY    hroot = 0;
181     LPWSTR  ptr;
182     LPPORT_INFO_2W out;
183     WCHAR   portname[MAX_PATH];
184     WCHAR   res_PortW[IDS_LOCALPORT_MAXLEN];
185     WCHAR   res_MonitorW[IDS_LOCALMONITOR_MAXLEN];
186     INT     reslen_PortW;
187     INT     reslen_MonitorW;
188     DWORD   len;
189     DWORD   res;
190     DWORD   needed = 0;
191     DWORD   numentries;
192     DWORD   entrysize;
193     DWORD   id = 0;
194
195     TRACE("(%d, %p, %d, %p)\n", level, pPorts, cbBuf, lpreturned);
196
197     entrysize = (level == 1) ? sizeof(PORT_INFO_1W) : sizeof(PORT_INFO_2W);
198
199     numentries = *lpreturned;           /* this is 0, when we scan the registry */
200     needed = entrysize * numentries;
201     ptr = (LPWSTR) &pPorts[needed];
202
203     if (needed > cbBuf) pPorts = NULL;  /* No buffer for the structs */
204
205     numentries = 0;
206     needed = 0;
207
208     /* we do not check more parameters as done in windows */
209     if ((level < 1) || (level > 2)) {
210         goto getports_cleanup;
211     }
212
213     /* "+1" for '\0' */
214     reslen_MonitorW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALMONITOR, res_MonitorW, IDS_LOCALMONITOR_MAXLEN) + 1;  
215     reslen_PortW = LoadStringW(LOCALSPL_hInstance, IDS_LOCALPORT, res_PortW, IDS_LOCALPORT_MAXLEN) + 1;  
216
217     res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
218     if (res == ERROR_SUCCESS) {
219
220         /* Scan all Port-Names */
221         while (res == ERROR_SUCCESS) {
222             len = MAX_PATH;
223             portname[0] = '\0';
224             res = RegEnumValueW(hroot, id, portname, &len, NULL, NULL, NULL, NULL);
225
226             if ((res == ERROR_SUCCESS) && (portname[0])) {
227                 numentries++;
228                 /* calculate the required size */
229                 needed += entrysize;
230                 needed += (len + 1) * sizeof(WCHAR);
231                 if (level > 1) {
232                     needed += (reslen_MonitorW + reslen_PortW) * sizeof(WCHAR);
233                 }
234
235                 /* Now fill the user-buffer, if available */
236                 if (pPorts && (cbBuf >= needed)){
237                     out = (LPPORT_INFO_2W) pPorts;
238                     pPorts += entrysize;
239                     TRACE("%p: writing PORT_INFO_%dW #%d (%s)\n", out, level, numentries, debugstr_w(portname));
240                     out->pPortName = ptr;
241                     lstrcpyW(ptr, portname);            /* Name of the Port */
242                     ptr += (len + 1);
243                     if (level > 1) {
244                         out->pMonitorName = ptr;
245                         lstrcpyW(ptr, res_MonitorW);    /* Name of the Monitor */
246                         ptr += reslen_MonitorW;
247
248                         out->pDescription = ptr;
249                         lstrcpyW(ptr, res_PortW);       /* Port Description */
250                         ptr += reslen_PortW;
251
252                         out->fPortType = PORT_TYPE_WRITE;
253                         out->Reserved = 0;
254                     }
255                 }
256                 id++;
257             }
258         }
259         RegCloseKey(hroot);
260     }
261     else
262     {
263         ERR("failed with %d for %s\n", res, debugstr_w(WinNT_CV_PortsW));
264         SetLastError(res);
265     }
266
267 getports_cleanup:
268     *lpreturned = numentries;
269     TRACE("need %d byte for %d entries (%d)\n", needed, numentries, GetLastError());
270     return needed;
271 }
272
273 /*****************************************************
274  * get_type_from_name (internal)
275  * 
276  */
277
278 static DWORD get_type_from_name(LPCWSTR name)
279 {
280     HANDLE  hfile;
281
282     if (!strncmpW(name, portname_LPT, sizeof(portname_LPT) / sizeof(WCHAR) -1))
283         return PORT_IS_LPT;
284
285     if (!strncmpW(name, portname_COM, sizeof(portname_COM) / sizeof(WCHAR) -1))
286         return PORT_IS_COM;
287
288     if (!strcmpW(name, portname_FILE))
289         return PORT_IS_FILE;
290
291     if (name[0] == '/')
292         return PORT_IS_UNIXNAME;
293
294     if (name[0] == '|')
295         return PORT_IS_PIPE;
296
297     if (!strncmpW(name, portname_CUPS, sizeof(portname_CUPS) / sizeof(WCHAR) -1))
298         return PORT_IS_CUPS;
299
300     if (!strncmpW(name, portname_LPR, sizeof(portname_LPR) / sizeof(WCHAR) -1))
301         return PORT_IS_LPR;
302
303     /* Must be a file or a directory. Does the file exist ? */
304     hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
305     TRACE("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
306     if (hfile == INVALID_HANDLE_VALUE) {
307         /* Can we create the file? */
308         hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
309         TRACE("%p for OPEN_ALWAYS\n", hfile);
310     }
311     if (hfile != INVALID_HANDLE_VALUE) {
312         CloseHandle(hfile);
313         return PORT_IS_FILENAME;
314     }
315     /* We can't use the name. use GetLastError() for the reason */
316     return PORT_IS_UNKNOWN;
317 }
318
319 /*****************************************************
320  * get_type_from_local_name (internal)
321  *
322  */
323
324 static DWORD get_type_from_local_name(LPCWSTR nameW)
325 {
326     LPPORT_INFO_1W  pi;
327     LPWSTR  myname = NULL;
328     DWORD   needed = 0;
329     DWORD   numentries = 0;
330     DWORD   id = 0;
331
332     TRACE("(%s)\n", debugstr_w(myname));
333
334     needed = get_ports_from_reg(1, NULL, 0, &numentries);
335     pi = heap_alloc(needed);
336     if (pi)
337         needed = get_ports_from_reg(1, (LPBYTE) pi, needed, &numentries);
338
339     if (pi && needed && numentries > 0) {
340         /* we got a number of valid ports. */
341
342         while ((myname == NULL) && (id < numentries))
343         {
344             if (lstrcmpiW(nameW, pi[id].pName) == 0) {
345                 TRACE("(%u) found %s\n", id, debugstr_w(pi[id].pName));
346                 myname = pi[id].pName;
347             }
348             id++;
349         }
350     }
351
352     id = (myname) ? get_type_from_name(myname) : PORT_IS_UNKNOWN;
353
354     heap_free(pi);
355     return id;
356
357 }
358 /******************************************************************************
359  *   localmon_AddPortExW [exported through MONITOREX]
360  *
361  * Add a Port, without presenting a user interface
362  *
363  * PARAMS
364  *  pName         [I] Servername or NULL (local Computer)
365  *  level         [I] Structure-Level (1) for pBuffer
366  *  pBuffer       [I] PTR to the Input-Data (PORT_INFO_1)
367  *  pMonitorName  [I] Name of the Monitor that manage the Port
368  *
369  * RETURNS
370  *  Success: TRUE
371  *  Failure: FALSE
372  *
373  * NOTES
374  *  Level 2 is documented on MSDN for Portmonitors, but not supported by the
375  *  "Local Port" Portmonitor (localspl.dll / localmon.dll)
376  */
377 static BOOL WINAPI localmon_AddPortExW(LPWSTR pName, DWORD level, LPBYTE pBuffer, LPWSTR pMonitorName)
378 {
379     PORT_INFO_1W * pi;
380     HKEY  hroot;
381     DWORD res;
382
383     pi = (PORT_INFO_1W *) pBuffer;
384     TRACE("(%s, %d, %p, %s) => %s\n", debugstr_w(pName), level, pBuffer,
385             debugstr_w(pMonitorName), debugstr_w(pi ? pi->pName : NULL));
386
387
388     if ((pMonitorName == NULL) || (lstrcmpiW(pMonitorName, LocalPortW) != 0 ) ||
389         (pi == NULL) || (pi->pName == NULL) || (pi->pName[0] == '\0') ) {
390         SetLastError(ERROR_INVALID_PARAMETER);
391         return FALSE;
392     }
393
394     if (level != 1) {
395         SetLastError(ERROR_INVALID_LEVEL);
396         return FALSE;
397     }
398
399     res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
400     if (res == ERROR_SUCCESS) {
401         if (does_port_exist(pi->pName)) {
402             RegCloseKey(hroot);
403             TRACE("=> FALSE with %u\n", ERROR_INVALID_PARAMETER);
404             SetLastError(ERROR_INVALID_PARAMETER);
405             return FALSE;
406         }
407         res = RegSetValueExW(hroot, pi->pName, 0, REG_SZ, (const BYTE *) emptyW, sizeof(emptyW));
408         RegCloseKey(hroot);
409     }
410     if (res != ERROR_SUCCESS) SetLastError(ERROR_INVALID_PARAMETER);
411     TRACE("=> %u with %u\n", (res == ERROR_SUCCESS), GetLastError());
412     return (res == ERROR_SUCCESS);
413 }
414
415 /*****************************************************
416  * localmon_ClosePort [exported through MONITOREX]
417  *
418  * Close a
419  *
420  * PARAMS
421  *  hPort  [i] The Handle to close
422  *
423  * RETURNS
424  *  Success: TRUE
425  *  Failure: FALSE
426  *
427  */
428 static BOOL WINAPI localmon_ClosePort(HANDLE hPort)
429 {
430     port_t * port = hPort;
431
432     TRACE("(%p)\n", port);
433     EnterCriticalSection(&port_handles_cs);
434     list_remove(&port->entry);
435     LeaveCriticalSection(&port_handles_cs);
436     heap_free(port);
437     return TRUE;
438 }
439
440 /*****************************************************
441  *   localmon_EnumPortsW [exported through MONITOREX]
442  *
443  * Enumerate all local Ports
444  *
445  * PARAMS
446  *  pName       [I] Servername (ignored)
447  *  level       [I] Structure-Level (1 or 2)
448  *  pPorts      [O] PTR to Buffer that receives the Result
449  *  cbBuf       [I] Size of Buffer at pPorts
450  *  pcbNeeded   [O] PTR to DWORD that receives the size in Bytes used / required for pPorts
451  *  pcReturned  [O] PTR to DWORD that receives the number of Ports in pPorts
452  *
453  * RETURNS
454  *  Success: TRUE
455  *  Failure: FALSE and in pcbNeeded the Bytes required for pPorts, if cbBuf is too small
456  *
457  * NOTES
458  *|  Windows ignores pName
459  *|  Windows crash the app, when pPorts, pcbNeeded or pcReturned are NULL
460  *|  Windows >NT4.0 does not check for illegal levels (TRUE is returned)
461  *
462  * ToDo
463  *   "HCU\Software\Wine\Spooler\<portname>" - redirection
464  *
465  */
466 static BOOL WINAPI localmon_EnumPortsW(LPWSTR pName, DWORD level, LPBYTE pPorts,
467                                        DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
468 {
469     BOOL    res = FALSE;
470     DWORD   needed;
471     DWORD   numentries;
472
473     TRACE("(%s, %d, %p, %d, %p, %p)\n",
474           debugstr_w(pName), level, pPorts, cbBuf, pcbNeeded, pcReturned);
475
476     numentries = 0;
477     needed = get_ports_from_reg(level, NULL, 0, &numentries);
478     /* we calculated the needed buffersize. now do the error-checks */
479     if (cbBuf < needed) {
480         SetLastError(ERROR_INSUFFICIENT_BUFFER);
481         goto cleanup;
482     }
483
484     /* fill the buffer with the Port-Names */
485     needed = get_ports_from_reg(level, pPorts, cbBuf, &numentries);
486     res = TRUE;
487
488     if (pcReturned) *pcReturned = numentries;
489
490 cleanup:
491     if (pcbNeeded)  *pcbNeeded = needed;
492
493     TRACE("returning %d with %d (%d byte for %d entries)\n", 
494             res, GetLastError(), needed, numentries);
495
496     return (res);
497 }
498
499 /*****************************************************
500  * localmon_OpenPort [exported through MONITOREX]
501  *
502  * Open a Data-Channel for a Port
503  *
504  * PARAMS
505  *  pName     [i] Name of selected Object
506  *  phPort    [o] The resulting Handle is stored here
507  *
508  * RETURNS
509  *  Success: TRUE
510  *  Failure: FALSE
511  *
512  */
513 static BOOL WINAPI localmon_OpenPortW(LPWSTR pName, PHANDLE phPort)
514 {
515     port_t * port;
516     DWORD   len;
517     DWORD   type;
518
519     TRACE("%s, %p)\n", debugstr_w(pName), phPort);
520
521     /* an empty name is invalid */
522     if (!pName[0]) return FALSE;
523
524     /* does the port exist? */
525     type = get_type_from_local_name(pName);
526     if (!type) return FALSE;
527
528     len = (lstrlenW(pName) + 1) * sizeof(WCHAR);
529     port = heap_alloc(sizeof(port_t) + len);
530     if (!port) return FALSE;
531
532     port->type = type;
533     memcpy(port->nameW, pName, len);
534     *phPort = port;
535
536     EnterCriticalSection(&port_handles_cs);
537     list_add_tail(&port_handles, &port->entry);
538     LeaveCriticalSection(&port_handles_cs);
539
540     TRACE("=> %p\n", port);
541     return TRUE;
542 }
543
544 /*****************************************************
545  * localmon_XcvClosePort [exported through MONITOREX]
546  *
547  * Close a Communication-Channel
548  *
549  * PARAMS
550  *  hXcv  [i] The Handle to close
551  *
552  * RETURNS
553  *  Success: TRUE
554  *  Failure: FALSE
555  *
556  */
557 static BOOL WINAPI localmon_XcvClosePort(HANDLE hXcv)
558 {
559     xcv_t * xcv = hXcv;
560
561     TRACE("(%p)\n", xcv);
562     /* No checks are done in Windows */
563     EnterCriticalSection(&xcv_handles_cs);
564     list_remove(&xcv->entry);
565     LeaveCriticalSection(&xcv_handles_cs);
566     heap_free(xcv);
567     return TRUE;
568 }
569
570 /*****************************************************
571  * localmon_XcvDataPort [exported through MONITOREX]
572  *
573  * Execute command through a Communication-Channel
574  *
575  * PARAMS
576  *  hXcv            [i] The Handle to work with
577  *  pszDataName     [i] Name of the command to execute
578  *  pInputData      [i] Buffer for extra Input Data (needed only for some commands)
579  *  cbInputData     [i] Size in Bytes of Buffer at pInputData
580  *  pOutputData     [o] Buffer to receive additional Data (needed only for some commands)
581  *  cbOutputData    [i] Size in Bytes of Buffer at pOutputData
582  *  pcbOutputNeeded [o] PTR to receive the minimal Size in Bytes of the Buffer at pOutputData
583  *
584  * RETURNS
585  *  Success: ERROR_SUCCESS
586  *  Failure: win32 error code
587  *
588  * NOTES
589  *
590  *  Minimal List of commands, that every Printmonitor DLL should support:
591  *
592  *| "MonitorUI" : Return the Name of the Userinterface-DLL as WSTR in pOutputData
593  *| "AddPort"   : Add a Port (Name as WSTR in pInputData)
594  *| "DeletePort": Delete a Port (Name as WSTR in pInputData)
595  *
596  *
597  */
598 static DWORD WINAPI localmon_XcvDataPort(HANDLE hXcv, LPCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData,
599                                          PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded)
600 {
601     WCHAR   buffer[16];     /* buffer for a decimal number */
602     LPWSTR  ptr;
603     DWORD   res;
604     DWORD   needed;
605     HKEY    hroot;
606
607     TRACE("(%p, %s, %p, %d, %p, %d, %p)\n", hXcv, debugstr_w(pszDataName),
608           pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded);
609
610     if (!lstrcmpW(pszDataName, cmd_AddPortW)) {
611         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
612         res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
613         if (res == ERROR_SUCCESS) {
614             if (does_port_exist((LPWSTR) pInputData)) {
615                 RegCloseKey(hroot);
616                 TRACE("=> %u\n", ERROR_ALREADY_EXISTS);
617                 return ERROR_ALREADY_EXISTS;
618             }
619             res = RegSetValueExW(hroot, (LPWSTR) pInputData, 0, REG_SZ, (const BYTE *) emptyW, sizeof(emptyW));
620             RegCloseKey(hroot);
621         }
622         TRACE("=> %u\n", res);
623         return res;
624     }
625
626
627     if (!lstrcmpW(pszDataName, cmd_ConfigureLPTPortCommandOKW)) {
628         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
629         res = RegCreateKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
630         if (res == ERROR_SUCCESS) {
631             res = RegSetValueExW(hroot, TransmissionRetryTimeoutW, 0, REG_SZ, pInputData, cbInputData);
632             RegCloseKey(hroot);
633         }
634         return res;
635     }
636
637     if (!lstrcmpW(pszDataName, cmd_DeletePortW)) {
638         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
639         res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_PortsW, &hroot);
640         if (res == ERROR_SUCCESS) {
641             res = RegDeleteValueW(hroot, (LPWSTR) pInputData);
642             RegCloseKey(hroot);
643             TRACE("=> %u with %u\n", res, GetLastError() );
644             return res;
645         }
646         return ERROR_FILE_NOT_FOUND;
647     }
648
649     if (!lstrcmpW(pszDataName, cmd_GetDefaultCommConfigW)) {
650         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
651         *pcbOutputNeeded = cbOutputData;
652         res = GetDefaultCommConfigW((LPWSTR) pInputData, (LPCOMMCONFIG) pOutputData, pcbOutputNeeded);
653         TRACE("got %u with %u\n", res, GetLastError() );
654         return res ? ERROR_SUCCESS : GetLastError();
655     }
656
657     if (!lstrcmpW(pszDataName, cmd_GetTransmissionRetryTimeoutW)) {
658         * pcbOutputNeeded = sizeof(DWORD);
659         if (cbOutputData >= sizeof(DWORD)) {
660             /* the w2k resource kit documented a default of 90, but that's wrong */
661             *((LPDWORD) pOutputData) = 45;
662
663             res = RegOpenKeyW(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsW, &hroot);
664             if (res == ERROR_SUCCESS) {
665                 needed = sizeof(buffer) - sizeof(WCHAR);
666                 res = RegQueryValueExW(hroot, TransmissionRetryTimeoutW, NULL, NULL, (LPBYTE) buffer, &needed);
667                 if ((res == ERROR_SUCCESS) && (buffer[0])) {
668                     *((LPDWORD) pOutputData) = strtoulW(buffer, NULL, 0);
669                 }
670                 RegCloseKey(hroot);
671             }
672             return ERROR_SUCCESS;
673         }
674         return ERROR_INSUFFICIENT_BUFFER;
675     }
676
677
678     if (!lstrcmpW(pszDataName, cmd_MonitorUIW)) {
679         * pcbOutputNeeded = sizeof(dllnameuiW);
680         if (cbOutputData >= sizeof(dllnameuiW)) {
681             memcpy(pOutputData, dllnameuiW, sizeof(dllnameuiW));
682             return ERROR_SUCCESS;
683         }
684         return ERROR_INSUFFICIENT_BUFFER;
685     }
686
687     if (!lstrcmpW(pszDataName, cmd_PortIsValidW)) {
688         TRACE("InputData (%d): %s\n", cbInputData, debugstr_w( (LPWSTR) pInputData));
689         res = get_type_from_name((LPCWSTR) pInputData);
690         TRACE("detected as %u\n",  res);
691         /* names, that we have recognized, are valid */
692         if (res) return ERROR_SUCCESS;
693
694         /* ERROR_ACCESS_DENIED, ERROR_PATH_NOT_FOUND or something else */
695         TRACE("=> %u\n", GetLastError());
696         return GetLastError();
697     }
698
699     if (!lstrcmpW(pszDataName, cmd_SetDefaultCommConfigW)) {
700         /* get the portname from the Handle */
701         ptr =  strchrW(((xcv_t *)hXcv)->nameW, ' ');
702         if (ptr) {
703             ptr++;  /* skip the space */
704         }
705         else
706         {
707             ptr =  ((xcv_t *)hXcv)->nameW;
708         }
709         lstrcpynW(buffer, ptr, sizeof(buffer)/sizeof(WCHAR));
710         if (buffer[0]) buffer[lstrlenW(buffer)-1] = '\0';  /* remove the ':' */
711         res = SetDefaultCommConfigW(buffer, (LPCOMMCONFIG) pInputData, cbInputData);
712         TRACE("got %u with %u\n", res, GetLastError() );
713         return res ? ERROR_SUCCESS : GetLastError();
714     }
715
716     FIXME("command not supported: %s\n", debugstr_w(pszDataName));
717     return ERROR_INVALID_PARAMETER;
718 }
719
720 /*****************************************************
721  * localmon_XcvOpenPort [exported through MONITOREX]
722  *
723  * Open a Communication-Channel
724  *
725  * PARAMS
726  *  pName         [i] Name of selected Object
727  *  GrantedAccess [i] Access-Rights to use
728  *  phXcv         [o] The resulting Handle is stored here
729  *
730  * RETURNS
731  *  Success: TRUE
732  *  Failure: FALSE
733  *
734  */
735 static BOOL WINAPI localmon_XcvOpenPort(LPCWSTR pName, ACCESS_MASK GrantedAccess, PHANDLE phXcv)
736 {
737     DWORD   len;
738     xcv_t * xcv;
739
740     TRACE("%s, 0x%x, %p)\n", debugstr_w(pName), GrantedAccess, phXcv);
741     /* No checks for any field is done in Windows */
742     len = (lstrlenW(pName) + 1) * sizeof(WCHAR);
743     xcv = heap_alloc( sizeof(xcv_t) + len);
744     if (xcv) {
745         xcv->GrantedAccess = GrantedAccess;
746         memcpy(xcv->nameW, pName, len);
747         *phXcv = xcv;
748         EnterCriticalSection(&xcv_handles_cs);
749         list_add_tail(&xcv_handles, &xcv->entry);
750         LeaveCriticalSection(&xcv_handles_cs);
751         TRACE("=> %p\n", xcv);
752         return TRUE;
753     }
754     else
755     {
756         *phXcv = NULL;
757         return FALSE;
758     }
759 }
760
761 /*****************************************************
762  *      InitializePrintMonitor  (LOCALSPL.@)
763  *
764  * Initialize the Monitor for the Local Ports
765  *
766  * PARAMS
767  *  regroot [I] Registry-Path, where the settings are stored
768  *
769  * RETURNS
770  *  Success: Pointer to a MONITOREX Structure
771  *  Failure: NULL
772  *
773  * NOTES
774  *  The fixed location "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports"
775  *  is used to store the Ports (IniFileMapping from "win.ini", Section "Ports").
776  *  Native localspl.dll fails, when no valid Port-Entry is present.
777  *
778  */
779
780 LPMONITOREX WINAPI InitializePrintMonitor(LPWSTR regroot)
781 {
782     static MONITOREX mymonitorex =
783     {
784         sizeof(MONITOREX) - sizeof(DWORD),
785         {
786             localmon_EnumPortsW,
787             localmon_OpenPortW,
788             NULL,       /* localmon_OpenPortExW */ 
789             NULL,       /* localmon_StartDocPortW */
790             NULL,       /* localmon_WritePortW */
791             NULL,       /* localmon_ReadPortW */
792             NULL,       /* localmon_EndDocPortW */
793             localmon_ClosePort,
794             NULL,       /* Use AddPortUI in localui.dll */
795             localmon_AddPortExW,
796             NULL,       /* Use ConfigurePortUI in localui.dll */
797             NULL,       /* Use DeletePortUI in localui.dll */
798             NULL,       /* localmon_GetPrinterDataFromPort */
799             NULL,       /* localmon_SetPortTimeOuts */
800             localmon_XcvOpenPort,
801             localmon_XcvDataPort,
802             localmon_XcvClosePort
803         }
804     };
805
806     TRACE("(%s)\n", debugstr_w(regroot));
807     /* Parameter "regroot" is ignored on NT4.0 (localmon.dll) */
808     if (!regroot || !regroot[0]) {
809         SetLastError(ERROR_INVALID_PARAMETER);
810         return NULL;
811     }
812     TRACE("=> %p\n", &mymonitorex);
813     /* Native windows returns always the same pointer on success */
814     return &mymonitorex;
815 }