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