1 /*
2  * PROJECT:     ReactOS Local Spooler
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Functions related to Printers and printing
5  * COPYRIGHT:   Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 // Global Variables
11 SKIPLIST PrinterList;
12 
13 // Forward Declarations
14 static void _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
15 static void _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
16 static void _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
17 static void _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
18 static void _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
19 static void _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
20 static void _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
21 static void _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
22 static void _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
23 static void _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName);
24 
25 // Local Constants
26 typedef void (*PLocalGetPrinterLevelFunc)(PLOCAL_PRINTER, PVOID, PBYTE*, PDWORD, DWORD, PCWSTR);
27 
28 static const PLocalGetPrinterLevelFunc pfnGetPrinterLevels[] = {
29     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel0,
30     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel1,
31     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel2,
32     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel3,
33     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel4,
34     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel5,
35     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel6,
36     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel7,
37     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel8,
38     (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel9
39 };
40 
41 static DWORD dwPrinterInfo0Offsets[] = {
42     FIELD_OFFSET(PRINTER_INFO_STRESS, pPrinterName),
43     MAXDWORD
44 };
45 
46 static DWORD dwPrinterInfo1Offsets[] = {
47     FIELD_OFFSET(PRINTER_INFO_1W, pName),
48     FIELD_OFFSET(PRINTER_INFO_1W, pComment),
49     FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
50     MAXDWORD
51 };
52 
53 static DWORD dwPrinterInfo2Offsets[] = {
54     FIELD_OFFSET(PRINTER_INFO_2W, pPrinterName),
55     FIELD_OFFSET(PRINTER_INFO_2W, pShareName),
56     FIELD_OFFSET(PRINTER_INFO_2W, pPortName),
57     FIELD_OFFSET(PRINTER_INFO_2W, pDriverName),
58     FIELD_OFFSET(PRINTER_INFO_2W, pComment),
59     FIELD_OFFSET(PRINTER_INFO_2W, pLocation),
60     FIELD_OFFSET(PRINTER_INFO_2W, pSepFile),
61     FIELD_OFFSET(PRINTER_INFO_2W, pPrintProcessor),
62     FIELD_OFFSET(PRINTER_INFO_2W, pDatatype),
63     FIELD_OFFSET(PRINTER_INFO_2W, pParameters),
64     MAXDWORD
65 };
66 
67 static DWORD dwPrinterInfo4Offsets[] = {
68     FIELD_OFFSET(PRINTER_INFO_4W, pPrinterName),
69     MAXDWORD
70 };
71 
72 static DWORD dwPrinterInfo5Offsets[] = {
73     FIELD_OFFSET(PRINTER_INFO_5W, pPrinterName),
74     FIELD_OFFSET(PRINTER_INFO_5W, pPortName),
75     MAXDWORD
76 };
77 
78 /** These values serve no purpose anymore, but are still used in PRINTER_INFO_5 and
79     HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts */
80 static const DWORD dwDeviceNotSelectedTimeout = 15000;
81 static const DWORD dwTransmissionRetryTimeout = 45000;
82 
83 
84 /**
85  * @name _PrinterListCompareRoutine
86  *
87  * SKIPLIST_COMPARE_ROUTINE for the Printer List.
88  * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
89  */
90 static int WINAPI
_PrinterListCompareRoutine(PVOID FirstStruct,PVOID SecondStruct)91 _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
92 {
93     PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct;
94     PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct;
95 
96     return _wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
97 }
98 
99 /**
100  * @name InitializePrinterList
101  *
102  * Initializes a list of locally available Printers.
103  * The list is searchable by name and returns information about the printers, including their job queues.
104  * During this process, the job queues are also initialized.
105  */
106 BOOL
InitializePrinterList(VOID)107 InitializePrinterList(VOID)
108 {
109     DWORD cbData;
110     DWORD cchPrinterName;
111     DWORD dwErrorCode;
112     DWORD dwSubKeys;
113     DWORD i;
114     HKEY hSubKey = NULL;
115     PLOCAL_PORT pPort;
116     PLOCAL_PRINTER pPrinter = NULL;
117     PLOCAL_PRINT_PROCESSOR pPrintProcessor;
118     PWSTR pwszPort = NULL;
119     PWSTR pwszPrintProcessor = NULL;
120     WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
121 
122     TRACE("InitializePrinterList()\n");
123 
124     // Initialize an empty list for our printers.
125     InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
126 
127     // Get the number of subkeys of the printers registry key. Each subkey is a local printer there.
128     dwErrorCode = (DWORD)RegQueryInfoKeyW(hPrintersKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
129     if (dwErrorCode != ERROR_SUCCESS)
130     {
131         ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
132         goto Cleanup;
133     }
134 
135     // Loop through all available local printers.
136     for (i = 0; i < dwSubKeys; i++)
137     {
138         // Cleanup tasks from the previous run
139         if (hSubKey)
140         {
141             RegCloseKey(hSubKey);
142             hSubKey = NULL;
143         }
144 
145         if (pPrinter)
146         {
147             if (pPrinter->pDefaultDevMode)
148                 DllFreeSplMem(pPrinter->pDefaultDevMode);
149 
150             if (pPrinter->pwszDefaultDatatype)
151                 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
152 
153             if (pPrinter->pwszDescription)
154                 DllFreeSplStr(pPrinter->pwszDescription);
155 
156             if (pPrinter->pwszPrinterDriver)
157                 DllFreeSplStr(pPrinter->pwszPrinterDriver);
158 
159             if (pPrinter->pwszPrinterName)
160                 DllFreeSplStr(pPrinter->pwszPrinterName);
161 
162             DllFreeSplMem(pPrinter);
163             pPrinter = NULL;
164         }
165 
166         if (pwszPrintProcessor)
167         {
168             DllFreeSplStr(pwszPrintProcessor);
169             pwszPrintProcessor = NULL;
170         }
171 
172         // Get the name of this printer.
173         cchPrinterName = _countof(wszPrinterName);
174         dwErrorCode = (DWORD)RegEnumKeyExW(hPrintersKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
175         if (dwErrorCode == ERROR_MORE_DATA)
176         {
177             // This printer name exceeds the maximum length and is invalid.
178             continue;
179         }
180         else if (dwErrorCode != ERROR_SUCCESS)
181         {
182             ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
183             continue;
184         }
185 
186         // Open this Printer's registry key.
187         dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, wszPrinterName, 0, KEY_READ, &hSubKey);
188         if (dwErrorCode != ERROR_SUCCESS)
189         {
190             ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
191             continue;
192         }
193 
194         // Get the Print Processor.
195         pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor");
196         if (!pwszPrintProcessor)
197             continue;
198 
199         // Try to find it in the Print Processor List.
200         pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
201         if (!pPrintProcessor)
202         {
203             ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
204             continue;
205         }
206 
207         // Get the Port.
208         pwszPort = AllocAndRegQueryWSZ(hSubKey, L"Port");
209         if (!pwszPort)
210             continue;
211 
212         // Try to find it in the Port List.
213         pPort = FindPort(pwszPort);
214         if (!pPort)
215         {
216             ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort, wszPrinterName);
217             continue;
218         }
219 
220         // Create a new LOCAL_PRINTER structure for it.
221         pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
222         if (!pPrinter)
223         {
224             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
225             ERR("DllAllocSplMem failed!\n");
226             goto Cleanup;
227         }
228 
229         pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
230         pPrinter->pPrintProcessor = pPrintProcessor;
231         pPrinter->pPort = pPort;
232         InitializePrinterJobList(pPrinter);
233 
234         // Get the location.
235         pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location");
236         if (!pPrinter->pwszLocation)
237             continue;
238 
239         // Get the printer driver.
240         pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
241         if (!pPrinter->pwszPrinterDriver)
242             continue;
243 
244         // Get the description.
245         pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description");
246         if (!pPrinter->pwszDescription)
247             continue;
248 
249         // Get the default datatype.
250         pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype");
251         if (!pPrinter->pwszDefaultDatatype)
252             continue;
253 
254         // Verify that it's valid.
255         if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
256         {
257             ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
258             continue;
259         }
260 
261         // Determine the size of the DevMode.
262         dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, NULL, &cbData);
263         if (dwErrorCode != ERROR_SUCCESS)
264         {
265             ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
266             continue;
267         }
268 
269         // Allocate enough memory for the DevMode.
270         pPrinter->pDefaultDevMode = DllAllocSplMem(cbData);
271         if (!pPrinter->pDefaultDevMode)
272         {
273             dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
274             ERR("DllAllocSplMem failed!\n");
275             goto Cleanup;
276         }
277 
278         // Get the default DevMode.
279         dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData);
280         if (dwErrorCode != ERROR_SUCCESS)
281         {
282             ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
283             continue;
284         }
285 
286         // Get the Attributes.
287         cbData = sizeof(DWORD);
288         dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData);
289         if (dwErrorCode != ERROR_SUCCESS)
290         {
291             ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
292             continue;
293         }
294 
295         // Get the Status.
296         cbData = sizeof(DWORD);
297         dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
298         if (dwErrorCode != ERROR_SUCCESS)
299         {
300             ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
301             continue;
302         }
303 
304         // Add this printer to the printer list.
305         if (!InsertElementSkiplist(&PrinterList, pPrinter))
306         {
307             ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
308             goto Cleanup;
309         }
310 
311         // Don't let the cleanup routines free this.
312         pPrinter = NULL;
313     }
314 
315     dwErrorCode = ERROR_SUCCESS;
316 
317 Cleanup:
318     // Inside the loop
319     if (hSubKey)
320         RegCloseKey(hSubKey);
321 
322     if (pPrinter)
323     {
324         if (pPrinter->pDefaultDevMode)
325             DllFreeSplMem(pPrinter->pDefaultDevMode);
326 
327         if (pPrinter->pwszDefaultDatatype)
328             DllFreeSplStr(pPrinter->pwszDefaultDatatype);
329 
330         if (pPrinter->pwszDescription)
331             DllFreeSplStr(pPrinter->pwszDescription);
332 
333         if (pPrinter->pwszPrinterDriver)
334             DllFreeSplStr(pPrinter->pwszPrinterDriver);
335 
336         if (pPrinter->pwszPrinterName)
337             DllFreeSplStr(pPrinter->pwszPrinterName);
338 
339         DllFreeSplMem(pPrinter);
340     }
341 
342     if (pwszPrintProcessor)
343         DllFreeSplStr(pwszPrintProcessor);
344 
345     SetLastError(dwErrorCode);
346     return (dwErrorCode == ERROR_SUCCESS);
347 }
348 
349 VOID
BroadcastChange(PLOCAL_HANDLE pHandle)350 BroadcastChange(PLOCAL_HANDLE pHandle)
351 {
352     PLOCAL_PRINTER pPrinter;
353     PSKIPLIST_NODE pNode;
354     DWORD cchMachineName = 0;
355     WCHAR wszMachineName[MAX_PATH] = {0}; // if not local, use Machine Name then Printer Name... pPrinter->pJob->pwszMachineName?
356 
357     for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
358     {
359         pPrinter = (PLOCAL_PRINTER)pNode->Element;
360 
361         StringCchCopyW( &wszMachineName[cchMachineName], sizeof(wszMachineName), pPrinter->pwszPrinterName );
362 
363         PostMessageW( HWND_BROADCAST, WM_DEVMODECHANGE, 0, (LPARAM)&wszMachineName );
364     }
365 }
366 
367 /**
368  * @name _LocalEnumPrintersCheckName
369  *
370  * Checks the Name parameter supplied to a call to EnumPrinters.
371  *
372  * @param Flags
373  * Flags parameter of EnumPrinters.
374  *
375  * @param Name
376  * Name parameter of EnumPrinters to check.
377  *
378  * @param pwszComputerName
379  * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
380  * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
381  *
382  * @param pcchComputerName
383  * If a string to prepend is returned, this pointer receives its length in characters.
384  *
385  * @return
386  * ERROR_SUCCESS if processing in EnumPrinters can be continued.
387  * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
388  * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
389  */
390 static DWORD
_LocalEnumPrintersCheckName(DWORD Flags,PCWSTR Name,PWSTR pwszComputerName,PDWORD pcchComputerName)391 _LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
392 {
393     PCWSTR pName;
394     PCWSTR pComputerName;
395 
396     // If there is no Name parameter to check, we can just continue in EnumPrinters.
397     if (!Name)
398         return ERROR_SUCCESS;
399 
400     // Check if Name does not begin with two backslashes (required for specifying Computer Names).
401     if (Name[0] != L'\\' || Name[1] != L'\\')
402     {
403         if (Flags & PRINTER_ENUM_NAME)
404         {
405             // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
406             // Print Provider Name or the local Computer Name.
407 
408             // Compare with the Print Provider Name.
409             if (_wcsicmp(Name, wszPrintProviderInfo[0]) == 0)
410                 return ERROR_SUCCESS;
411 
412             // Dismiss anything else.
413             return ERROR_INVALID_NAME;
414         }
415         else
416         {
417             // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
418             return ERROR_SUCCESS;
419         }
420     }
421 
422     // Prepend the backslashes to the output computer name.
423     pwszComputerName[0] = L'\\';
424     pwszComputerName[1] = L'\\';
425 
426     // Get the local computer name for comparison.
427     *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
428     if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
429     {
430         ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
431         return GetLastError();
432     }
433 
434     // Add the leading slashes to the total length.
435     *pcchComputerName += 2;
436 
437     // Compare both names.
438     pComputerName = &pwszComputerName[2];
439     pName = &Name[2];
440     for (;;)
441     {
442         // Are we at the end of the local Computer Name string?
443         if (!*pComputerName)
444         {
445             // Are we also at the end of the supplied Name parameter?
446             // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
447             if (!*pName)
448             {
449                 // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
450                 // Add a trailing backslash for that.
451                 pwszComputerName[(*pcchComputerName)++] = L'\\';
452                 pwszComputerName[*pcchComputerName] = 0;
453                 return ERROR_SUCCESS;
454             }
455             else if (*pName == L'\\')
456             {
457                 if (Flags & PRINTER_ENUM_NAME)
458                 {
459                     // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
460                     // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
461                     return ERROR_INVALID_NAME;
462                 }
463                 else
464                 {
465                     // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
466                     // Only the Computer Name between the backslashes is checked then.
467                     // This is largely undocumented, but verified by tests (see winspool_apitest).
468                     // In this case, no computer name is prepended in EnumPrinters though.
469                     *pwszComputerName = 0;
470                     *pcchComputerName = 0;
471                     return ERROR_SUCCESS;
472                 }
473             }
474         }
475 
476         // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
477         if (towlower(*pName) != towlower(*pComputerName))
478             return ERROR_INVALID_NAME;
479 
480         pName++;
481         pComputerName++;
482     }
483 }
484 
485 static DWORD
_DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum,DWORD cbBuf,PDWORD pcbNeeded,PDWORD pcReturned)486 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
487 {
488     int i;
489 
490     // Count the needed bytes for Print Provider information.
491     *pcbNeeded = sizeof(PRINTER_INFO_1W);
492 
493     for (i = 0; i < 3; i++)
494         *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
495 
496     // Check if the supplied buffer is large enough.
497     if (cbBuf < *pcbNeeded)
498         return ERROR_INSUFFICIENT_BUFFER;
499 
500     // Copy over the Print Provider information.
501     ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
502     PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]);
503     *pcReturned = 1;
504 
505     return ERROR_SUCCESS;
506 }
507 
508 static void
_LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_STRESS * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)509 _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
510 {
511     size_t cbName;
512     PWSTR p, Allocation;
513     PCWSTR pwszStrings[1];
514     SYSTEM_INFO SystemInfo;
515 
516     // Calculate the string lengths.
517     cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
518 
519     if (!ppPrinterInfo)
520     {
521         *pcbNeeded += sizeof(PRINTER_INFO_STRESS) + cbName;
522         return;
523     }
524 
525     // Set the general fields.
526     ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_STRESS));
527     (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount;
528     (*ppPrinterInfo)->dwGetVersion = GetVersion();
529     (*ppPrinterInfo)->Status = pPrinter->dwStatus;
530 
531 #if !DBG
532     (*ppPrinterInfo)->fFreeBuild = 1;
533 #endif
534 
535     GetSystemInfo(&SystemInfo);
536     (*ppPrinterInfo)->dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors;
537     (*ppPrinterInfo)->dwProcessorType = SystemInfo.dwProcessorType;
538     (*ppPrinterInfo)->wProcessorArchitecture = SystemInfo.wProcessorArchitecture;
539     (*ppPrinterInfo)->wProcessorLevel = SystemInfo.wProcessorLevel;
540 
541     // Copy the Printer Name.
542     p = Allocation = DllAllocSplMem(cbName);
543     pwszStrings[0] = Allocation;
544     StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
545     StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
546 
547     // Finally copy the structure and advance to the next one in the output buffer.
548     *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo0Offsets, *ppPrinterInfoEnd);
549     (*ppPrinterInfo)++;
550 
551     // Free the memory for temporary strings.
552     DllFreeSplMem(Allocation);
553 }
554 
555 static void
_LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_1W * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)556 _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
557 {
558     const WCHAR wszComma[] = L",";
559 
560     size_t cbName;
561     size_t cbComment;
562     size_t cbDescription;
563     PWSTR p, Allocation1, Allocation2;
564     PCWSTR pwszStrings[3];
565 
566     // Calculate the string lengths.
567     // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
568     // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
569     cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
570     cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
571     cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
572 
573     if (!ppPrinterInfo)
574     {
575         *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
576         return;
577     }
578 
579     // Indicate that this is a Printer.
580     (*ppPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
581 
582     // Copy the Printer Name.
583     p = Allocation1 = DllAllocSplMem(cbName);
584     pwszStrings[0] = Allocation1;
585     StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
586     StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
587 
588     // Copy the Printer comment (equals the "Description" registry value).
589     pwszStrings[1] = pPrinter->pwszDescription;
590 
591     // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
592     p = Allocation2 = DllAllocSplMem(cbDescription);
593     pwszStrings[2] = Allocation2;
594     StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
595     StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
596     StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
597     StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
598     StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
599     StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
600 
601     // Finally copy the structure and advance to the next one in the output buffer.
602     *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo1Offsets, *ppPrinterInfoEnd);
603     (*ppPrinterInfo)++;
604 
605     // Free the memory for temporary strings.
606     DllFreeSplMem(Allocation1);
607     DllFreeSplMem(Allocation2);
608 }
609 
610 static void
_LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_2W * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)611 _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
612 {
613     WCHAR wszEmpty[] = L"";
614 
615     size_t cbDevMode;
616     size_t cbPrinterName;
617     size_t cbShareName;
618     size_t cbPortName;
619     size_t cbDriverName;
620     size_t cbComment;
621     size_t cbLocation;
622     size_t cbSepFile;
623     size_t cbPrintProcessor;
624     size_t cbDatatype;
625     size_t cbParameters;
626     PWSTR p, Allocation;
627     PCWSTR pwszStrings[10];
628     FIXME("LocalGetPrinterLevel2\n");
629     // Calculate the string lengths.
630     cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
631     cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
632 
633     if (!ppPrinterInfo)
634     {
635         // Attention: pComment equals the "Description" registry value.
636         cbShareName = sizeof(wszEmpty);
637         cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
638         cbDriverName = (wcslen(pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
639         cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
640         cbLocation = (wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
641         cbSepFile = sizeof(wszEmpty);
642         cbPrintProcessor = (wcslen(pPrinter->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
643         cbDatatype = (wcslen(pPrinter->pwszDefaultDatatype) + 1) * sizeof(WCHAR);
644         cbParameters = sizeof(wszEmpty);
645 
646         *pcbNeeded += sizeof(PRINTER_INFO_2W) + cbDevMode + cbPrinterName + cbShareName + cbPortName + cbDriverName + cbComment + cbLocation + cbSepFile + cbPrintProcessor + cbDatatype + cbParameters;
647         FIXME("LocalGetPrinterLevel2 Needed %d\n",*pcbNeeded);
648         return;
649     }
650 
651     // Set the general fields.
652     ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_2W));
653     (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
654     (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount;
655     (*ppPrinterInfo)->Status = pPrinter->dwStatus;
656 
657     // Set the pDevMode field (and copy the DevMode).
658     *ppPrinterInfoEnd -= cbDevMode;
659     CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
660     (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
661 
662     // Set the pPrinterName field.
663     p = Allocation = DllAllocSplMem(cbPrinterName);
664     pwszStrings[0] = Allocation;
665     StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
666     StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
667 
668     // Set the pShareName field.
669     pwszStrings[1] = wszEmpty;
670 
671     // Set the pPortName field.
672     pwszStrings[2] = pPrinter->pPort->pwszName;
673 
674     // Set the pDriverName field.
675     pwszStrings[3] = pPrinter->pwszPrinterDriver;
676 
677     // Set the pComment field ((equals the "Description" registry value).
678     pwszStrings[4] = pPrinter->pwszDescription;
679 
680     // Set the pLocation field.
681     pwszStrings[5] = pPrinter->pwszLocation;
682 
683     // Set the pSepFile field.
684     pwszStrings[6] = wszEmpty;
685 
686     // Set the pPrintProcessor field.
687     pwszStrings[7] = pPrinter->pPrintProcessor->pwszName;
688 
689     // Set the pDatatype field.
690     pwszStrings[8] = pPrinter->pwszDefaultDatatype;
691 
692     // Set the pParameters field.
693     pwszStrings[9] = wszEmpty;
694 
695     // Finally copy the structure and advance to the next one in the output buffer.
696     *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo2Offsets, *ppPrinterInfoEnd);
697     (*ppPrinterInfo)++;
698 
699     // Free the memory for temporary strings.
700     DllFreeSplMem(Allocation);
701 }
702 
703 static void
_LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_3 * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)704 _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
705 {
706     SECURITY_DESCRIPTOR SecurityDescriptor = { 0 };
707 
708     if (!ppPrinterInfo)
709     {
710         *pcbNeeded += sizeof(PRINTER_INFO_3) + sizeof(SECURITY_DESCRIPTOR);
711         return;
712     }
713 
714     FIXME("Return a valid security descriptor for PRINTER_INFO_3\n");
715 
716     // Set the pSecurityDescriptor field (and copy the Security Descriptor).
717     *ppPrinterInfoEnd -= sizeof(SECURITY_DESCRIPTOR);
718     CopyMemory(*ppPrinterInfoEnd, &SecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
719     (*ppPrinterInfo)->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)(*ppPrinterInfoEnd);
720 
721     // Advance to the next structure.
722     (*ppPrinterInfo)++;
723 }
724 
725 static void
_LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_4W * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)726 _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
727 {
728     size_t cbPrinterName;
729     PWSTR p, Allocation;
730     PCWSTR pwszStrings[1];
731 
732     // Calculate the string lengths.
733     cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
734 
735     if (!ppPrinterInfo)
736     {
737         *pcbNeeded += sizeof(PRINTER_INFO_4W) + cbPrinterName;
738         return;
739     }
740 
741     // Set the general fields.
742     (*ppPrinterInfo)->pServerName = NULL;
743     (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
744 
745     // Set the pPrinterName field.
746     p = Allocation = DllAllocSplMem(cbPrinterName);
747     pwszStrings[0] = Allocation;
748     StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
749     StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
750 
751     // Finally copy the structure and advance to the next one in the output buffer.
752     *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo4Offsets, *ppPrinterInfoEnd);
753     (*ppPrinterInfo)++;
754 
755     // Free the memory for temporary strings.
756     DllFreeSplMem(Allocation);
757 }
758 
759 static void
_LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_5W * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)760 _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
761 {
762     size_t cbPrinterName;
763     size_t cbPortName;
764     PWSTR p, Allocation;
765     PCWSTR pwszStrings[2];
766 
767     // Calculate the string lengths.
768     cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
769 
770     if (!ppPrinterInfo)
771     {
772         cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
773 
774         *pcbNeeded += sizeof(PRINTER_INFO_5W) + cbPrinterName + cbPortName;
775         return;
776     }
777 
778     // Set the general fields.
779     (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
780     (*ppPrinterInfo)->DeviceNotSelectedTimeout = dwDeviceNotSelectedTimeout;
781     (*ppPrinterInfo)->TransmissionRetryTimeout = dwTransmissionRetryTimeout;
782 
783     // Set the pPrinterName field.
784     p = Allocation = DllAllocSplMem(cbPrinterName);
785     pwszStrings[0] = Allocation;
786     StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
787     StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
788 
789     // Set the pPortName field.
790     pwszStrings[1] = pPrinter->pPort->pwszName;
791 
792     // Finally copy the structure and advance to the next one in the output buffer.
793     *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo5Offsets, *ppPrinterInfoEnd);
794     (*ppPrinterInfo)++;
795 
796     // Free the memory for temporary strings.
797     DllFreeSplMem(Allocation);
798 }
799 
800 static void
_LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_6 * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)801 _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
802 {
803     if (!ppPrinterInfo)
804     {
805         *pcbNeeded += sizeof(PRINTER_INFO_6);
806         return;
807     }
808 
809     // Set the general fields.
810     (*ppPrinterInfo)->dwStatus = pPrinter->dwStatus;
811 
812     // Advance to the next structure.
813     (*ppPrinterInfo)++;
814 }
815 
816 static void
_LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_7W * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)817 _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
818 {
819     if (!ppPrinterInfo)
820     {
821         *pcbNeeded += sizeof(PRINTER_INFO_7W);
822         return;
823     }
824 
825     FIXME("No Directory Support, returning DSPRINT_UNPUBLISH for PRINTER_INFO_7 all the time!\n");
826 
827     // Set the general fields.
828     (*ppPrinterInfo)->dwAction = DSPRINT_UNPUBLISH;
829     (*ppPrinterInfo)->pszObjectGUID = NULL;
830 
831     // Advance to the next structure.
832     (*ppPrinterInfo)++;
833 }
834 
835 static void
_LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_8W * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)836 _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
837 {
838     DWORD cbDevMode;
839 
840     // Calculate the string lengths.
841     cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
842 
843     if (!ppPrinterInfo)
844     {
845         *pcbNeeded += sizeof(PRINTER_INFO_8W) + cbDevMode;
846         return;
847     }
848 
849     // Set the pDevMode field (and copy the DevMode).
850     *ppPrinterInfoEnd -= cbDevMode;
851     CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
852     (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
853 
854     // Advance to the next structure.
855     (*ppPrinterInfo)++;
856 }
857 
858 static void
_LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter,PPRINTER_INFO_9W * ppPrinterInfo,PBYTE * ppPrinterInfoEnd,PDWORD pcbNeeded,DWORD cchComputerName,PCWSTR wszComputerName)859 _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName)
860 {
861     DWORD cbDevMode;
862 
863     // Calculate the string lengths.
864     cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
865 
866     if (!ppPrinterInfo)
867     {
868         *pcbNeeded += sizeof(PRINTER_INFO_9W) + cbDevMode;
869         return;
870     }
871 
872     FIXME("Per-user settings are not yet implemented, returning the global DevMode for PRINTER_INFO_9!\n");
873 
874     // Set the pDevMode field (and copy the DevMode).
875     *ppPrinterInfoEnd -= cbDevMode;
876     CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
877     (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
878 
879     // Advance to the next structure.
880     (*ppPrinterInfo)++;
881 }
882 
883 BOOL WINAPI
LocalEnumPrinters(DWORD Flags,LPWSTR Name,DWORD Level,LPBYTE pPrinterEnum,DWORD cbBuf,LPDWORD pcbNeeded,LPDWORD pcReturned)884 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
885 {
886     DWORD cchComputerName = 0;
887     DWORD dwErrorCode;
888     PBYTE pPrinterInfoEnd;
889     PSKIPLIST_NODE pNode;
890     WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
891     PLOCAL_PRINTER pPrinter;
892 
893     FIXME("LocalEnumPrinters(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
894 
895     // Do no sanity checks or assertions for pcbNeeded and pcReturned here.
896     // This is verified and required by localspl_apitest!
897 
898     // Begin counting.
899     *pcbNeeded = 0;
900     *pcReturned = 0;
901 
902     if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK)
903     {
904         // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
905         // This is the internal way for a Print Provider to signal that it doesn't handle this request.
906         dwErrorCode = ERROR_INVALID_NAME;
907         goto Cleanup;
908     }
909 
910     if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME))
911     {
912         // The Local Print Provider is the right destination for the request, but without any of these flags,
913         // there is no information that can be returned.
914         // So just signal a successful request.
915         dwErrorCode = ERROR_SUCCESS;
916         goto Cleanup;
917     }
918 
919     if (Level == 3 || Level > 5)
920     {
921         // The caller supplied an invalid level for EnumPrinters.
922         dwErrorCode = ERROR_INVALID_LEVEL;
923         goto Cleanup;
924     }
925 
926     if (Level == 1 && Flags & PRINTER_ENUM_NAME && !Name)
927     {
928         // The caller wants information about this Print Provider.
929         // spoolss packs this into an array of information about all Print Providers.
930         dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
931         goto Cleanup;
932     }
933 
934     // Check the supplied Name parameter (if any).
935     // This may return a Computer Name string we later prepend to the output.
936     dwErrorCode = _LocalEnumPrintersCheckName(Flags, Name, wszComputerName, &cchComputerName);
937     if (dwErrorCode != ERROR_SUCCESS)
938         goto Cleanup;
939 
940     // Count the required buffer size and the number of printers.
941     for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
942     {
943         pPrinter = (PLOCAL_PRINTER)pNode->Element;
944 
945         // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
946         if (Flags & PRINTER_ENUM_SHARED)
947         {
948             FIXME("Printer Sharing is not supported yet, returning no printers!\n");
949             continue;
950         }
951 
952         pfnGetPrinterLevels[Level](pPrinter, NULL, NULL, pcbNeeded, cchComputerName, wszComputerName);
953     }
954 
955     // Check if the supplied buffer is large enough.
956     if (cbBuf < *pcbNeeded)
957     {
958         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
959         goto Cleanup;
960     }
961 
962     // Copy over the Printer information.
963     pPrinterInfoEnd = &pPrinterEnum[*pcbNeeded];
964 
965     for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
966     {
967         pPrinter = (PLOCAL_PRINTER)pNode->Element;
968 
969         // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
970         if (Flags & PRINTER_ENUM_SHARED)
971             continue;
972 
973         pfnGetPrinterLevels[Level](pPrinter, &pPrinterEnum, &pPrinterInfoEnd, NULL, cchComputerName, wszComputerName);
974         (*pcReturned)++;
975     }
976 
977     dwErrorCode = ERROR_SUCCESS;
978 
979 Cleanup:
980     SetLastError(dwErrorCode);
981     return (dwErrorCode == ERROR_SUCCESS);
982 }
983 
984 BOOL WINAPI
LocalGetPrinter(HANDLE hPrinter,DWORD Level,LPBYTE pPrinter,DWORD cbBuf,LPDWORD pcbNeeded)985 LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
986 {
987     // We never prepend a Computer Name to the output, but need to provide an empty string,
988     // because this variable is passed to StringCbCopyExW.
989     const WCHAR wszDummyComputerName[] = L"";
990 
991     DWORD dwErrorCode;
992     PBYTE pPrinterEnd;
993     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
994     PLOCAL_PRINTER_HANDLE pPrinterHandle;
995 
996     TRACE("LocalGetPrinter(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
997 
998     // Sanity checks.
999     if (!pHandle)
1000     {
1001         dwErrorCode = ERROR_INVALID_HANDLE;
1002         goto Cleanup;
1003     }
1004 
1005     // Check if this is a printer handle.
1006     if (pHandle->HandleType != HandleType_Printer)
1007     {
1008         dwErrorCode = ERROR_INVALID_HANDLE;
1009         goto Cleanup;
1010     }
1011 
1012     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1013 
1014     if (Level > 9)
1015     {
1016         // The caller supplied an invalid level for GetPrinter.
1017         dwErrorCode = ERROR_INVALID_LEVEL;
1018         goto Cleanup;
1019     }
1020 
1021     // Count the required buffer size.
1022     *pcbNeeded = 0;
1023     pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, NULL, NULL, pcbNeeded, 0, wszDummyComputerName);
1024 
1025     // Check if the supplied buffer is large enough.
1026     if (cbBuf < *pcbNeeded)
1027     {
1028         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1029         goto Cleanup;
1030     }
1031 
1032     // Copy over the Printer information.
1033     pPrinterEnd = &pPrinter[*pcbNeeded];
1034     pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, &pPrinter, &pPrinterEnd, NULL, 0, wszDummyComputerName);
1035     dwErrorCode = ERROR_SUCCESS;
1036 
1037 Cleanup:
1038     SetLastError(dwErrorCode);
1039     return (dwErrorCode == ERROR_SUCCESS);
1040 }
1041 
1042 static DWORD
_LocalOpenPortHandle(PWSTR pwszPortName,PHANDLE phPrinter)1043 _LocalOpenPortHandle(PWSTR pwszPortName, PHANDLE phPrinter)
1044 {
1045     BOOL bReturnValue;
1046     DWORD dwErrorCode;
1047     HANDLE hPort;
1048     PLOCAL_HANDLE pHandle = NULL;
1049     PLOCAL_PORT pPort;
1050     PLOCAL_PORT_HANDLE pPortHandle = NULL;
1051     PLOCAL_PRINT_MONITOR pPrintMonitor;
1052 
1053     // Look for this port in our Print Monitor Port list.
1054     pPort = FindPort(pwszPortName);
1055     if (!pPort)
1056     {
1057         // The supplied port is unknown to all our Print Monitors.
1058         dwErrorCode = ERROR_INVALID_NAME;
1059         goto Failure;
1060     }
1061 
1062     pPrintMonitor = pPort->pPrintMonitor;
1063 
1064     // Call the monitor's OpenPort function.
1065     if (pPrintMonitor->bIsLevel2)
1066         bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszPortName, &hPort);
1067     else
1068         bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszPortName, &hPort);
1069 
1070     if (!bReturnValue)
1071     {
1072         // The OpenPort function failed. Return its last error.
1073         dwErrorCode = GetLastError();
1074         goto Failure;
1075     }
1076 
1077     // Create a new generic handle.
1078     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1079     if (!pHandle)
1080     {
1081         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1082         ERR("DllAllocSplMem failed!\n");
1083         goto Failure;
1084     }
1085 
1086     // Create a new LOCAL_PORT_HANDLE.
1087     pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
1088     if (!pPortHandle)
1089     {
1090         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1091         ERR("DllAllocSplMem failed!\n");
1092         goto Failure;
1093     }
1094 
1095     pPortHandle->hPort = hPort;
1096     pPortHandle->pPort = pPort;
1097 
1098     // Make the generic handle a Port handle.
1099     pHandle->HandleType = HandleType_Port;
1100     pHandle->pSpecificHandle = pPortHandle;
1101 
1102     // Return it.
1103     *phPrinter = (HANDLE)pHandle;
1104     return ERROR_SUCCESS;
1105 
1106 Failure:
1107     if (pHandle)
1108         DllFreeSplMem(pHandle);
1109 
1110     if (pPortHandle)
1111         DllFreeSplMem(pPortHandle);
1112 
1113     return dwErrorCode;
1114 }
1115 
1116 static DWORD
_LocalOpenPrinterHandle(PWSTR pwszPrinterName,PWSTR pwszJobParameter,PHANDLE phPrinter,PPRINTER_DEFAULTSW pDefault)1117 _LocalOpenPrinterHandle(PWSTR pwszPrinterName, PWSTR pwszJobParameter, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
1118 {
1119     DWORD dwErrorCode;
1120     DWORD dwJobID;
1121     PLOCAL_HANDLE pHandle = NULL;
1122     PLOCAL_JOB pJob;
1123     PLOCAL_PRINTER pPrinter;
1124     PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
1125     WCHAR wszFullPath[MAX_PATH];
1126 
1127     // Retrieve the printer from the list.
1128     pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
1129     if (!pPrinter)
1130     {
1131         // The printer does not exist.
1132         dwErrorCode = ERROR_INVALID_NAME;
1133         goto Failure;
1134     }
1135 
1136     // Create a new generic handle.
1137     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1138     if (!pHandle)
1139     {
1140         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1141         ERR("DllAllocSplMem failed!\n");
1142         goto Failure;
1143     }
1144 
1145     // Create a new LOCAL_PRINTER_HANDLE.
1146     pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
1147     if (!pPrinterHandle)
1148     {
1149         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1150         ERR("DllAllocSplMem failed!\n");
1151         goto Failure;
1152     }
1153 
1154     pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
1155     pPrinterHandle->pPrinter = pPrinter;
1156 
1157     // Check if a datatype was given.
1158     if (pDefault && pDefault->pDatatype)
1159     {
1160         // Use the datatype if it's valid.
1161         if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
1162         {
1163             dwErrorCode = ERROR_INVALID_DATATYPE;
1164             goto Failure;
1165         }
1166 
1167         pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
1168     }
1169     else
1170     {
1171         // Use the default datatype.
1172         pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
1173     }
1174 
1175     // Check if a DevMode was given, otherwise use the default.
1176     if (pDefault && pDefault->pDevMode)
1177         pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
1178     else
1179         pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
1180 
1181     // Check if the caller wants a handle to an existing Print Job.
1182     if (pwszJobParameter)
1183     {
1184         // The "Job " string has to follow now.
1185         if (wcsncmp(pwszJobParameter, L"Job ", 4) != 0)
1186         {
1187             dwErrorCode = ERROR_INVALID_NAME;
1188             goto Failure;
1189         }
1190 
1191         // Skip the "Job " string.
1192         pwszJobParameter += 4;
1193 
1194         // Skip even more whitespace.
1195         while (*pwszJobParameter == ' ')
1196             ++pwszJobParameter;
1197 
1198         // Finally extract the desired Job ID.
1199         dwJobID = wcstoul(pwszJobParameter, NULL, 10);
1200         if (!IS_VALID_JOB_ID(dwJobID))
1201         {
1202             // The user supplied an invalid Job ID.
1203             dwErrorCode = ERROR_INVALID_NAME;
1204             goto Failure;
1205         }
1206 
1207         // Look for this job in the Global Job List.
1208         pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
1209         if (!pJob || pJob->pPrinter != pPrinter)
1210         {
1211             // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
1212             dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1213             goto Failure;
1214         }
1215 
1216         // Try to open its SPL file.
1217         GetJobFilePath(L"SPL", dwJobID, wszFullPath);
1218         pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1219         if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
1220         {
1221             dwErrorCode = GetLastError();
1222             ERR("CreateFileW failed with error %lu for \"%S\"!\n", dwErrorCode, wszFullPath);
1223             goto Failure;
1224         }
1225 
1226         // Associate the job to our Printer Handle, but don't set bStartedDoc.
1227         // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
1228         pPrinterHandle->pJob = pJob;
1229     }
1230 
1231     // Make the generic handle a Printer handle.
1232     pHandle->HandleType = HandleType_Printer;
1233     pHandle->pSpecificHandle = pPrinterHandle;
1234 
1235     // Return it.
1236     *phPrinter = (HANDLE)pHandle;
1237     return ERROR_SUCCESS;
1238 
1239 Failure:
1240     if (pHandle)
1241         DllFreeSplMem(pHandle);
1242 
1243     if (pPrinterHandle)
1244     {
1245         if (pPrinterHandle->pwszDatatype)
1246             DllFreeSplStr(pPrinterHandle->pwszDatatype);
1247 
1248         if (pPrinterHandle->pDevMode)
1249             DllFreeSplMem(pPrinterHandle->pDevMode);
1250 
1251         DllFreeSplMem(pPrinterHandle);
1252     }
1253 
1254     return dwErrorCode;
1255 }
1256 
1257 static DWORD
_LocalOpenPrintServerHandle(PHANDLE phPrinter)1258 _LocalOpenPrintServerHandle(PHANDLE phPrinter)
1259 {
1260     PLOCAL_HANDLE pHandle;
1261 
1262     // Create a new generic handle.
1263     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1264     if (!pHandle)
1265     {
1266         ERR("DllAllocSplMem failed!\n");
1267         return ERROR_NOT_ENOUGH_MEMORY;
1268     }
1269 
1270     // Make the generic handle a Print Server handle.
1271     pHandle->HandleType = HandleType_PrintServer;
1272     pHandle->pSpecificHandle = NULL;
1273 
1274     // Return it.
1275     *phPrinter = (HANDLE)pHandle;
1276     return ERROR_SUCCESS;
1277 }
1278 
1279 static DWORD
_LocalOpenXcvHandle(PWSTR pwszParameter,PHANDLE phPrinter)1280 _LocalOpenXcvHandle(PWSTR pwszParameter, PHANDLE phPrinter)
1281 {
1282     BOOL bReturnValue;
1283     DWORD dwErrorCode;
1284     HANDLE hXcv;
1285     PLOCAL_HANDLE pHandle = NULL;
1286     PLOCAL_PORT pPort;
1287     PLOCAL_PRINT_MONITOR pPrintMonitor;
1288     PLOCAL_XCV_HANDLE pXcvHandle = NULL;
1289 
1290     // Skip the "Xcv" string.
1291     pwszParameter += 3;
1292 
1293     // Is XcvMonitor or XcvPort requested?
1294     if (wcsncmp(pwszParameter, L"Monitor ", 8) == 0)
1295     {
1296         // Skip the "Monitor " string.
1297         pwszParameter += 8;
1298 
1299         // Look for this monitor in our Print Monitor list.
1300         pPrintMonitor = FindPrintMonitor(pwszParameter);
1301         if (!pPrintMonitor)
1302         {
1303             // The caller supplied a non-existing Monitor name.
1304             dwErrorCode = ERROR_INVALID_NAME;
1305             ERR("OpenXcvHandle failed on Monitor name! %lu\n", dwErrorCode);
1306             goto Failure;
1307         }
1308     }
1309     else if (wcsncmp(pwszParameter, L"Port ", 5) == 0)
1310     {
1311         // Skip the "Port " string.
1312         pwszParameter += 5;
1313 
1314         // Look for this port in our Print Monitor Port list.
1315         pPort = FindPort(pwszParameter);
1316         if (!pPort)
1317         {
1318             // The supplied port is unknown to all our Print Monitors.
1319             dwErrorCode = ERROR_INVALID_NAME;
1320             ERR("OpenXcvHandle failed on Port name! %lu\n", dwErrorCode);
1321             goto Failure;
1322         }
1323 
1324         pPrintMonitor = pPort->pPrintMonitor;
1325     }
1326     else
1327     {
1328         dwErrorCode = ERROR_INVALID_NAME;
1329         ERR("OpenXcvHandle failed on bad name! %lu\n", dwErrorCode);
1330         goto Failure;
1331     }
1332 
1333     // Call the monitor's XcvOpenPort function.
1334     if (pPrintMonitor->bIsLevel2)
1335         bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszParameter, SERVER_EXECUTE, &hXcv);
1336     else
1337         bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszParameter, SERVER_EXECUTE, &hXcv);
1338 
1339     if (!bReturnValue)
1340     {
1341         // The XcvOpenPort function failed. Return its last error.
1342         dwErrorCode = GetLastError();
1343         ERR("XcvOpenPort function failed! %lu\n", dwErrorCode);
1344         goto Failure;
1345     }
1346 
1347     // Create a new generic handle.
1348     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1349     if (!pHandle)
1350     {
1351         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1352         ERR("DllAllocSplMem failed!\n");
1353         goto Failure;
1354     }
1355 
1356     // Create a new LOCAL_XCV_HANDLE.
1357     pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
1358     if (!pXcvHandle)
1359     {
1360         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1361         ERR("DllAllocSplMem failed!\n");
1362         goto Failure;
1363     }
1364 
1365     pXcvHandle->hXcv = hXcv;
1366     pXcvHandle->pPrintMonitor = pPrintMonitor;
1367 
1368     // Make the generic handle a Xcv handle.
1369     pHandle->HandleType = HandleType_Xcv;
1370     pHandle->pSpecificHandle = pXcvHandle;
1371 
1372     // Return it.
1373     *phPrinter = (HANDLE)pHandle;
1374     ERR("OpenXcvHandle Success! %p\n", pXcvHandle);
1375     return ERROR_SUCCESS;
1376 
1377 Failure:
1378     if (pHandle)
1379         DllFreeSplMem(pHandle);
1380 
1381     if (pXcvHandle)
1382         DllFreeSplMem(pXcvHandle);
1383 
1384     return dwErrorCode;
1385 }
1386 
1387 //
1388 // Dead API
1389 //
1390 DWORD WINAPI
LocalPrinterMessageBox(HANDLE hPrinter,DWORD Error,HWND hWnd,LPWSTR pText,LPWSTR pCaption,DWORD dwType)1391 LocalPrinterMessageBox(HANDLE hPrinter, DWORD Error, HWND hWnd, LPWSTR pText, LPWSTR pCaption, DWORD dwType)
1392 {
1393     SetLastError(ERROR_INVALID_HANDLE); // Yes....
1394     return 0;
1395 }
1396 
1397 BOOL WINAPI
LocalOpenPrinter(PWSTR lpPrinterName,HANDLE * phPrinter,PPRINTER_DEFAULTSW pDefault)1398 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
1399 {
1400     DWORD cchComputerName;
1401     DWORD cchFirstParameter;
1402     DWORD dwErrorCode;
1403     PWSTR p = lpPrinterName;
1404     PWSTR pwszFirstParameter = NULL;
1405     PWSTR pwszSecondParameter;
1406     WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
1407 
1408     TRACE("LocalOpenPrinter(%S, %p, %p)\n", lpPrinterName, phPrinter, pDefault);
1409 
1410     ASSERT(phPrinter);
1411     *phPrinter = NULL;
1412 
1413     if (!lpPrinterName)
1414     {
1415         // The caller wants a Print Server handle and provided a NULL string.
1416         dwErrorCode = _LocalOpenPrintServerHandle(phPrinter);
1417         goto Cleanup;
1418     }
1419 
1420     // Skip any server name in the first parameter.
1421     // Does lpPrinterName begin with two backslashes to indicate a server name?
1422     if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
1423     {
1424         // Skip these two backslashes.
1425         lpPrinterName += 2;
1426 
1427         // Look for the terminating null character or closing backslash.
1428         p = lpPrinterName;
1429         while (*p != L'\0' && *p != L'\\')
1430             p++;
1431 
1432         // Get the local computer name for comparison.
1433         cchComputerName = _countof(wszComputerName);
1434         if (!GetComputerNameW(wszComputerName, &cchComputerName))
1435         {
1436             dwErrorCode = GetLastError();
1437             ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
1438             goto Cleanup;
1439         }
1440 
1441         // Now compare this string excerpt with the local computer name.
1442         // The input parameter may not be writable, so we can't null-terminate the input string at this point.
1443         // This print provider only supports local printers, so both strings have to match.
1444         if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0)
1445         {
1446             dwErrorCode = ERROR_INVALID_NAME;
1447             goto Cleanup;
1448         }
1449 
1450         // If lpPrinterName is only "\\COMPUTERNAME" with nothing more, the caller wants a handle to the local Print Server.
1451         if (!*p)
1452         {
1453             // The caller wants a Print Server handle and provided a string like:
1454             //    "\\COMPUTERNAME"
1455             dwErrorCode = _LocalOpenPrintServerHandle(phPrinter);
1456             goto Cleanup;
1457         }
1458 
1459         // We have checked the server name and don't need it anymore.
1460         lpPrinterName = p + 1;
1461     }
1462 
1463     // Look for a comma. If it exists, it indicates the end of the first parameter.
1464     pwszSecondParameter = wcschr(lpPrinterName, L',');
1465     if (pwszSecondParameter)
1466         cchFirstParameter = pwszSecondParameter - p;
1467     else
1468         cchFirstParameter = wcslen(lpPrinterName);
1469 
1470     // We must have at least one parameter.
1471     if (!cchFirstParameter && !pwszSecondParameter)
1472     {
1473         dwErrorCode = ERROR_INVALID_NAME;
1474         goto Cleanup;
1475     }
1476 
1477     // Do we have a first parameter?
1478     if (cchFirstParameter)
1479     {
1480         // Yes, extract it.
1481         // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
1482         pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR));
1483         CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR));
1484     }
1485 
1486     // Do we have a second parameter?
1487     if (pwszSecondParameter)
1488     {
1489         // Yes, skip the comma at the beginning.
1490         ++pwszSecondParameter;
1491 
1492         // Skip whitespace as well.
1493         while (*pwszSecondParameter == L' ')
1494             ++pwszSecondParameter;
1495     }
1496 
1497     // Now we can finally check the type of handle actually requested.
1498     if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0)
1499     {
1500         // The caller wants a port handle and provided a string like:
1501         //    "LPT1:, Port"
1502         //    "\\COMPUTERNAME\LPT1:, Port"
1503         dwErrorCode = _LocalOpenPortHandle(pwszFirstParameter, phPrinter);
1504     }
1505     else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
1506     {
1507         // The caller wants an Xcv handle and provided a string like:
1508         //    ", XcvMonitor Local Port"
1509         //    "\\COMPUTERNAME\, XcvMonitor Local Port"
1510         //    ", XcvPort LPT1:"
1511         //    "\\COMPUTERNAME\, XcvPort LPT1:"
1512         FIXME("OpenXcvHandle : %S\n",pwszSecondParameter);
1513         dwErrorCode = _LocalOpenXcvHandle(pwszSecondParameter, phPrinter);
1514     }
1515     else
1516     {
1517         // The caller wants a Printer or Printer Job handle and provided a string like:
1518         //    "HP DeskJet"
1519         //    "\\COMPUTERNAME\HP DeskJet"
1520         //    "HP DeskJet, Job 5"
1521         //    "\\COMPUTERNAME\HP DeskJet, Job 5"
1522         dwErrorCode = _LocalOpenPrinterHandle(pwszFirstParameter, pwszSecondParameter, phPrinter, pDefault);
1523     }
1524 
1525 Cleanup:
1526     if (pwszFirstParameter)
1527         DllFreeSplMem(pwszFirstParameter);
1528 
1529     SetLastError(dwErrorCode);
1530     return (dwErrorCode == ERROR_SUCCESS);
1531 }
1532 
1533 BOOL WINAPI
LocalReadPrinter(HANDLE hPrinter,PVOID pBuf,DWORD cbBuf,PDWORD pNoBytesRead)1534 LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
1535 {
1536     BOOL bReturnValue;
1537     DWORD dwErrorCode;
1538     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1539     PLOCAL_PORT_HANDLE pPortHandle;
1540     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1541 
1542     TRACE("LocalReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
1543 
1544     // Sanity checks.
1545     if (!pHandle)
1546     {
1547         dwErrorCode = ERROR_INVALID_HANDLE;
1548         goto Cleanup;
1549     }
1550 
1551     // Port handles are an entirely different thing.
1552     if (pHandle->HandleType == HandleType_Port)
1553     {
1554         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1555 
1556         // Call the monitor's ReadPort function.
1557         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1558             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
1559         else
1560             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
1561 
1562         if (!bReturnValue)
1563         {
1564             // The ReadPort function failed. Return its last error.
1565             dwErrorCode = GetLastError();
1566             goto Cleanup;
1567         }
1568 
1569         // We were successful!
1570         dwErrorCode = ERROR_SUCCESS;
1571         goto Cleanup;
1572     }
1573 
1574     // The remaining function deals with Printer handles only.
1575     if (pHandle->HandleType != HandleType_Printer)
1576     {
1577         dwErrorCode = ERROR_INVALID_HANDLE;
1578         goto Cleanup;
1579     }
1580 
1581     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1582 
1583     // ReadPrinter needs an opened SPL file to work.
1584     // This only works if a Printer Job Handle was requested in OpenPrinter.
1585     if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
1586     {
1587         dwErrorCode = ERROR_INVALID_HANDLE;
1588         goto Cleanup;
1589     }
1590 
1591     // Pass the parameters to ReadFile.
1592     if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
1593     {
1594         dwErrorCode = GetLastError();
1595         ERR("ReadFile failed with error %lu!\n", dwErrorCode);
1596         goto Cleanup;
1597     }
1598 
1599     dwErrorCode = ERROR_SUCCESS;
1600 
1601 Cleanup:
1602     SetLastError(dwErrorCode);
1603     return (dwErrorCode == ERROR_SUCCESS);
1604 }
1605 
1606 DWORD WINAPI
LocalStartDocPrinter(HANDLE hPrinter,DWORD Level,PBYTE pDocInfo)1607 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1608 {
1609     BOOL bReturnValue;
1610     DWORD dwErrorCode;
1611     DWORD dwReturnValue = 0;
1612     PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1613     PLOCAL_JOB pJob;
1614     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1615     PLOCAL_PORT_HANDLE pPortHandle;
1616     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1617 
1618     TRACE("LocalStartDocPrinter(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
1619 
1620     // Sanity checks.
1621     if (!pHandle)
1622     {
1623         dwErrorCode = ERROR_INVALID_HANDLE;
1624         goto Cleanup;
1625     }
1626 
1627     // Port handles are an entirely different thing.
1628     if (pHandle->HandleType == HandleType_Port)
1629     {
1630         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1631 
1632         // This call should come from a Print Processor and the job this port is going to print was assigned to us before opening the Print Processor.
1633         // Claim it exclusively for this port handle.
1634         pJob = pPortHandle->pPort->pNextJobToProcess;
1635         pPortHandle->pPort->pNextJobToProcess = NULL;
1636         ASSERT(pJob);
1637 
1638         // Call the monitor's StartDocPort function.
1639         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1640             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1641         else
1642             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1643 
1644         if (!bReturnValue)
1645         {
1646             // The StartDocPort function failed. Return its last error.
1647             dwErrorCode = GetLastError();
1648             goto Cleanup;
1649         }
1650 
1651         // We were successful!
1652         dwErrorCode = ERROR_SUCCESS;
1653         dwReturnValue = pJob->dwJobID;
1654         goto Cleanup;
1655     }
1656 
1657     // The remaining function deals with Printer handles only.
1658     if (pHandle->HandleType != HandleType_Printer)
1659     {
1660         dwErrorCode = ERROR_INVALID_HANDLE;
1661         goto Cleanup;
1662     }
1663 
1664     if (!pDocInfo1)
1665     {
1666         dwErrorCode = ERROR_INVALID_PARAMETER;
1667         goto Cleanup;
1668     }
1669 
1670     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1671 
1672     // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1673     if (pPrinterHandle->pJob)
1674     {
1675         dwErrorCode = ERROR_INVALID_PARAMETER;
1676         goto Cleanup;
1677     }
1678 
1679     // Check the validity of the datatype if we got one.
1680     if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
1681     {
1682         dwErrorCode = ERROR_INVALID_DATATYPE;
1683         goto Cleanup;
1684     }
1685 
1686     // Check if this is the right document information level.
1687     if (Level != 1)
1688     {
1689         dwErrorCode = ERROR_INVALID_LEVEL;
1690         goto Cleanup;
1691     }
1692 
1693     // All requirements are met - create a new job.
1694     dwErrorCode = CreateJob(pPrinterHandle);
1695     if (dwErrorCode != ERROR_SUCCESS)
1696         goto Cleanup;
1697 
1698     // Use any given datatype.
1699     if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
1700     {
1701         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1702         ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1703         goto Cleanup;
1704     }
1705 
1706     // Use any given document name.
1707     if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
1708     {
1709         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1710         ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1711         goto Cleanup;
1712     }
1713 
1714     // We were successful!
1715     dwErrorCode = ERROR_SUCCESS;
1716     dwReturnValue = pPrinterHandle->pJob->dwJobID;
1717 
1718 Cleanup:
1719     SetLastError(dwErrorCode);
1720     return dwReturnValue;
1721 }
1722 
1723 BOOL WINAPI
LocalStartPagePrinter(HANDLE hPrinter)1724 LocalStartPagePrinter(HANDLE hPrinter)
1725 {
1726     DWORD dwErrorCode;
1727     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1728     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1729 
1730     TRACE("LocalStartPagePrinter(%p)\n", hPrinter);
1731 
1732     // Sanity checks.
1733     if (!pHandle || pHandle->HandleType != HandleType_Printer)
1734     {
1735         dwErrorCode = ERROR_INVALID_HANDLE;
1736         goto Cleanup;
1737     }
1738 
1739     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1740 
1741     // We require StartDocPrinter or AddJob to be called first.
1742     if (!pPrinterHandle->bStartedDoc)
1743     {
1744         dwErrorCode = ERROR_SPL_NO_STARTDOC;
1745         goto Cleanup;
1746     }
1747 
1748     // Increase the page count.
1749     ++pPrinterHandle->pJob->dwTotalPages;
1750     dwErrorCode = ERROR_SUCCESS;
1751 
1752 Cleanup:
1753     SetLastError(dwErrorCode);
1754     return (dwErrorCode == ERROR_SUCCESS);
1755 }
1756 
1757 BOOL WINAPI
LocalWritePrinter(HANDLE hPrinter,PVOID pBuf,DWORD cbBuf,PDWORD pcWritten)1758 LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1759 {
1760     BOOL bReturnValue;
1761     DWORD dwErrorCode;
1762     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1763     PLOCAL_PORT_HANDLE pPortHandle;
1764     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1765 
1766     TRACE("LocalWritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1767 
1768     // Sanity checks.
1769     if (!pHandle)
1770     {
1771         dwErrorCode = ERROR_INVALID_HANDLE;
1772         goto Cleanup;
1773     }
1774 
1775     // Port handles are an entirely different thing.
1776     if (pHandle->HandleType == HandleType_Port)
1777     {
1778         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1779 
1780         // Call the monitor's WritePort function.
1781         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1782             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1783         else
1784             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1785 
1786         if (!bReturnValue)
1787         {
1788             // The WritePort function failed. Return its last error.
1789             dwErrorCode = GetLastError();
1790             goto Cleanup;
1791         }
1792 
1793         // We were successful!
1794         dwErrorCode = ERROR_SUCCESS;
1795         goto Cleanup;
1796     }
1797 
1798     // The remaining function deals with Printer handles only.
1799     if (pHandle->HandleType != HandleType_Printer)
1800     {
1801         dwErrorCode = ERROR_INVALID_HANDLE;
1802         goto Cleanup;
1803     }
1804 
1805     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1806 
1807     // We require StartDocPrinter or AddJob to be called first.
1808     if (!pPrinterHandle->bStartedDoc)
1809     {
1810         dwErrorCode = ERROR_SPL_NO_STARTDOC;
1811         goto Cleanup;
1812     }
1813 
1814     // TODO: This function is only called when doing non-spooled printing.
1815     // This needs to be investigated further. We can't just use pPrinterHandle->hSPLFile here, because that's currently reserved for Printer Job handles (see LocalReadPrinter).
1816 #if 0
1817     // Pass the parameters to WriteFile.
1818     if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL))
1819     {
1820         dwErrorCode = GetLastError();
1821         ERR("WriteFile failed with error %lu!\n", GetLastError());
1822         goto Cleanup;
1823     }
1824 #endif
1825 
1826     dwErrorCode = ERROR_SUCCESS;
1827 
1828 Cleanup:
1829     SetLastError(dwErrorCode);
1830     return (dwErrorCode == ERROR_SUCCESS);
1831 }
1832 
1833 BOOL WINAPI
LocalEndPagePrinter(HANDLE hPrinter)1834 LocalEndPagePrinter(HANDLE hPrinter)
1835 {
1836     DWORD dwErrorCode;
1837     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1838 
1839     TRACE("LocalEndPagePrinter(%p)\n", hPrinter);
1840 
1841     // Sanity checks.
1842     if (!pHandle || pHandle->HandleType != HandleType_Printer)
1843     {
1844         dwErrorCode = ERROR_INVALID_HANDLE;
1845         goto Cleanup;
1846     }
1847 
1848     // This function doesn't do anything else for now.
1849     dwErrorCode = ERROR_SUCCESS;
1850 
1851 Cleanup:
1852     SetLastError(dwErrorCode);
1853     return (dwErrorCode == ERROR_SUCCESS);
1854 }
1855 
1856 BOOL WINAPI
LocalEndDocPrinter(HANDLE hPrinter)1857 LocalEndDocPrinter(HANDLE hPrinter)
1858 {
1859     BOOL bReturnValue;
1860     DWORD dwErrorCode;
1861     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1862     PLOCAL_PORT_HANDLE pPortHandle;
1863     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1864 
1865     TRACE("LocalEndDocPrinter(%p)\n", hPrinter);
1866 
1867     // Sanity checks.
1868     if (!pHandle)
1869     {
1870         dwErrorCode = ERROR_INVALID_HANDLE;
1871         goto Cleanup;
1872     }
1873 
1874     // Port handles are an entirely different thing.
1875     if (pHandle->HandleType == HandleType_Port)
1876     {
1877         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1878 
1879         // Call the monitor's EndDocPort function.
1880         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1881             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort);
1882         else
1883             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort);
1884 
1885         if (!bReturnValue)
1886         {
1887             // The EndDocPort function failed. Return its last error.
1888             dwErrorCode = GetLastError();
1889             goto Cleanup;
1890         }
1891 
1892         // We were successful!
1893         dwErrorCode = ERROR_SUCCESS;
1894         goto Cleanup;
1895     }
1896 
1897     // The remaining function deals with Printer handles only.
1898     if (pHandle->HandleType != HandleType_Printer)
1899     {
1900         dwErrorCode = ERROR_INVALID_HANDLE;
1901         goto Cleanup;
1902     }
1903 
1904     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1905 
1906     // We require StartDocPrinter or AddJob to be called first.
1907     if (!pPrinterHandle->bStartedDoc)
1908     {
1909         dwErrorCode = ERROR_SPL_NO_STARTDOC;
1910         goto Cleanup;
1911     }
1912 
1913     // TODO: Something like ScheduleJob
1914 
1915     // Finish the job.
1916     pPrinterHandle->bStartedDoc = FALSE;
1917     pPrinterHandle->pJob = NULL;
1918     dwErrorCode = ERROR_SUCCESS;
1919 
1920 Cleanup:
1921     SetLastError(dwErrorCode);
1922     return (dwErrorCode == ERROR_SUCCESS);
1923 }
1924 
1925 static void
_LocalClosePortHandle(PLOCAL_PORT_HANDLE pPortHandle)1926 _LocalClosePortHandle(PLOCAL_PORT_HANDLE pPortHandle)
1927 {
1928     FIXME("LocalClosePortHandle\n");
1929     // Call the monitor's ClosePort function.
1930     if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1931         ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort);
1932     else
1933         ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort);
1934 }
1935 
1936 static void
_LocalClosePrinterHandle(PLOCAL_PRINTER_HANDLE pPrinterHandle)1937 _LocalClosePrinterHandle(PLOCAL_PRINTER_HANDLE pPrinterHandle)
1938 {
1939     FIXME("LocalClosePrinterHandle\n");
1940     // Terminate any started job.
1941     if (pPrinterHandle->pJob)
1942         FreeJob(pPrinterHandle->pJob);
1943 
1944     // Free memory for the fields.
1945     DllFreeSplMem(pPrinterHandle->pDevMode);
1946     DllFreeSplStr(pPrinterHandle->pwszDatatype);
1947 }
1948 
1949 static void
_LocalCloseXcvHandle(PLOCAL_XCV_HANDLE pXcvHandle)1950 _LocalCloseXcvHandle(PLOCAL_XCV_HANDLE pXcvHandle)
1951 {
1952     // Call the monitor's XcvClosePort function.
1953     if (pXcvHandle->pPrintMonitor->bIsLevel2)
1954         ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
1955     else
1956         ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
1957 }
1958 
1959 BOOL WINAPI
LocalClosePrinter(HANDLE hPrinter)1960 LocalClosePrinter(HANDLE hPrinter)
1961 {
1962     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1963 
1964     FIXME("LocalClosePrinter(%p)\n", hPrinter);
1965 
1966     if (!pHandle)
1967     {
1968         SetLastError(ERROR_INVALID_HANDLE);
1969         return FALSE;
1970     }
1971 
1972     if (pHandle->HandleType == HandleType_Port)
1973     {
1974         _LocalClosePortHandle(pHandle->pSpecificHandle);
1975     }
1976     else if (pHandle->HandleType == HandleType_Printer)
1977     {
1978         _LocalClosePrinterHandle(pHandle->pSpecificHandle);
1979     }
1980     else if (pHandle->HandleType == HandleType_PrintServer)
1981     {
1982         // Nothing to do.
1983     }
1984     else if (pHandle->HandleType == HandleType_Xcv)
1985     {
1986         _LocalCloseXcvHandle(pHandle->pSpecificHandle);
1987     }
1988     FIXME("LocalClosePrinter 1\n");
1989     // Free memory for the handle and the specific handle (if any).
1990     if (pHandle->pSpecificHandle)
1991         DllFreeSplMem(pHandle->pSpecificHandle);
1992     FIXME("LocalClosePrinter 2\n");
1993     DllFreeSplMem(pHandle);
1994     FIXME("LocalClosePrinter 3\n");
1995     return TRUE;
1996 }
1997