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, PWSTR wszComputerName);
15 static void _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
16 static void _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
17 static void _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
18 static void _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
19 static void _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
20 static void _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
21 static void _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
22 static void _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
23 static void _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
24 
25 // Local Constants
26 typedef void (*PLocalGetPrinterLevelFunc)(PLOCAL_PRINTER, PVOID, PBYTE*, PDWORD, DWORD, PWSTR);
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()
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, PWSTR wszComputerName)
492 {
493     size_t cbName;
494     PWSTR p;
495     PWSTR 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     pwszStrings[0] = DllAllocSplMem(cbName);
525     p = pwszStrings[0];
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(pwszStrings[0]);
535 }
536 
537 static void
538 _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
539 {
540     const WCHAR wszComma[] = L",";
541 
542     size_t cbName;
543     size_t cbComment;
544     size_t cbDescription;
545     PWSTR p;
546     PWSTR 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     pwszStrings[0] = DllAllocSplMem(cbName);
566     p = pwszStrings[0];
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     pwszStrings[2] = DllAllocSplMem(cbDescription);
575     p = pwszStrings[2];
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(pwszStrings[0]);
589     DllFreeSplMem(pwszStrings[2]);
590 }
591 
592 static void
593 _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR 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;
609     PWSTR 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     pwszStrings[0] = DllAllocSplMem(cbPrinterName);
645     p = pwszStrings[0];
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(pwszStrings[0]);
682 }
683 
684 static void
685 _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR 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, PWSTR wszComputerName)
708 {
709     size_t cbPrinterName;
710     PWSTR p;
711     PWSTR 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     pwszStrings[0] = DllAllocSplMem(cbPrinterName);
728     p = pwszStrings[0];
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(pwszStrings[0]);
738 }
739 
740 static void
741 _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
742 {
743     size_t cbPrinterName;
744     size_t cbPortName;
745     PWSTR p;
746     PWSTR 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     pwszStrings[0] = DllAllocSplMem(cbPrinterName);
766     p = pwszStrings[0];
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(pwszStrings[0]);
779 }
780 
781 static void
782 _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR 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, PWSTR 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, PWSTR 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, PWSTR 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     DWORD dwErrorCode;
969     PBYTE pPrinterEnd;
970     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
971     PLOCAL_PRINTER_HANDLE pPrinterHandle;
972 
973     TRACE("LocalGetPrinter(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
974 
975     // Sanity checks.
976     if (!pHandle)
977     {
978         dwErrorCode = ERROR_INVALID_HANDLE;
979         goto Cleanup;
980     }
981 
982     // Check if this is a printer handle.
983     if (pHandle->HandleType != HandleType_Printer)
984     {
985         dwErrorCode = ERROR_INVALID_HANDLE;
986         goto Cleanup;
987     }
988 
989     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
990 
991     if (Level > 9)
992     {
993         // The caller supplied an invalid level for GetPrinter.
994         dwErrorCode = ERROR_INVALID_LEVEL;
995         goto Cleanup;
996     }
997 
998     // Count the required buffer size.
999     *pcbNeeded = 0;
1000     pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, NULL, NULL, pcbNeeded, 0, NULL);
1001 
1002     // Check if the supplied buffer is large enough.
1003     if (cbBuf < *pcbNeeded)
1004     {
1005         dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1006         goto Cleanup;
1007     }
1008 
1009     // Copy over the Printer information.
1010     pPrinterEnd = &pPrinter[*pcbNeeded];
1011     pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, &pPrinter, &pPrinterEnd, NULL, 0, NULL);
1012     dwErrorCode = ERROR_SUCCESS;
1013 
1014 Cleanup:
1015     SetLastError(dwErrorCode);
1016     return (dwErrorCode == ERROR_SUCCESS);
1017 }
1018 
1019 static DWORD
1020 _LocalOpenPortHandle(PWSTR pwszPortName, PHANDLE phPrinter)
1021 {
1022     BOOL bReturnValue;
1023     DWORD dwErrorCode;
1024     HANDLE hPort;
1025     PLOCAL_HANDLE pHandle = NULL;
1026     PLOCAL_PORT pPort;
1027     PLOCAL_PORT_HANDLE pPortHandle = NULL;
1028     PLOCAL_PRINT_MONITOR pPrintMonitor;
1029 
1030     // Look for this port in our Print Monitor Port list.
1031     pPort = FindPort(pwszPortName);
1032     if (!pPort)
1033     {
1034         // The supplied port is unknown to all our Print Monitors.
1035         dwErrorCode = ERROR_INVALID_NAME;
1036         goto Failure;
1037     }
1038 
1039     pPrintMonitor = pPort->pPrintMonitor;
1040 
1041     // Call the monitor's OpenPort function.
1042     if (pPrintMonitor->bIsLevel2)
1043         bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszPortName, &hPort);
1044     else
1045         bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszPortName, &hPort);
1046 
1047     if (!bReturnValue)
1048     {
1049         // The OpenPort function failed. Return its last error.
1050         dwErrorCode = GetLastError();
1051         goto Failure;
1052     }
1053 
1054     // Create a new generic handle.
1055     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1056     if (!pHandle)
1057     {
1058         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1059         ERR("DllAllocSplMem failed!\n");
1060         goto Failure;
1061     }
1062 
1063     // Create a new LOCAL_PORT_HANDLE.
1064     pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
1065     if (!pPortHandle)
1066     {
1067         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1068         ERR("DllAllocSplMem failed!\n");
1069         goto Failure;
1070     }
1071 
1072     pPortHandle->hPort = hPort;
1073     pPortHandle->pPort = pPort;
1074 
1075     // Make the generic handle a Port handle.
1076     pHandle->HandleType = HandleType_Port;
1077     pHandle->pSpecificHandle = pPortHandle;
1078 
1079     // Return it.
1080     *phPrinter = (HANDLE)pHandle;
1081     return ERROR_SUCCESS;
1082 
1083 Failure:
1084     if (pHandle)
1085         DllFreeSplMem(pHandle);
1086 
1087     if (pPortHandle)
1088         DllFreeSplMem(pPortHandle);
1089 
1090     return dwErrorCode;
1091 }
1092 
1093 static DWORD
1094 _LocalOpenPrinterHandle(PWSTR pwszPrinterName, PWSTR pwszJobParameter, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault)
1095 {
1096     DWORD dwErrorCode;
1097     DWORD dwJobID;
1098     PLOCAL_HANDLE pHandle = NULL;
1099     PLOCAL_JOB pJob;
1100     PLOCAL_PRINTER pPrinter;
1101     PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
1102     WCHAR wszFullPath[MAX_PATH];
1103 
1104     // Retrieve the printer from the list.
1105     pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
1106     if (!pPrinter)
1107     {
1108         // The printer does not exist.
1109         dwErrorCode = ERROR_INVALID_NAME;
1110         goto Failure;
1111     }
1112 
1113     // Create a new generic handle.
1114     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1115     if (!pHandle)
1116     {
1117         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1118         ERR("DllAllocSplMem failed!\n");
1119         goto Failure;
1120     }
1121 
1122     // Create a new LOCAL_PRINTER_HANDLE.
1123     pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
1124     if (!pPrinterHandle)
1125     {
1126         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1127         ERR("DllAllocSplMem failed!\n");
1128         goto Failure;
1129     }
1130 
1131     pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
1132     pPrinterHandle->pPrinter = pPrinter;
1133 
1134     // Check if a datatype was given.
1135     if (pDefault && pDefault->pDatatype)
1136     {
1137         // Use the datatype if it's valid.
1138         if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
1139         {
1140             dwErrorCode = ERROR_INVALID_DATATYPE;
1141             goto Failure;
1142         }
1143 
1144         pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
1145     }
1146     else
1147     {
1148         // Use the default datatype.
1149         pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
1150     }
1151 
1152     // Check if a DevMode was given, otherwise use the default.
1153     if (pDefault && pDefault->pDevMode)
1154         pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
1155     else
1156         pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
1157 
1158     // Check if the caller wants a handle to an existing Print Job.
1159     if (pwszJobParameter)
1160     {
1161         // The "Job " string has to follow now.
1162         if (wcsncmp(pwszJobParameter, L"Job ", 4) != 0)
1163         {
1164             dwErrorCode = ERROR_INVALID_NAME;
1165             goto Failure;
1166         }
1167 
1168         // Skip the "Job " string.
1169         pwszJobParameter += 4;
1170 
1171         // Skip even more whitespace.
1172         while (*pwszJobParameter == ' ')
1173             ++pwszJobParameter;
1174 
1175         // Finally extract the desired Job ID.
1176         dwJobID = wcstoul(pwszJobParameter, NULL, 10);
1177         if (!IS_VALID_JOB_ID(dwJobID))
1178         {
1179             // The user supplied an invalid Job ID.
1180             dwErrorCode = ERROR_INVALID_NAME;
1181             goto Failure;
1182         }
1183 
1184         // Look for this job in the Global Job List.
1185         pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
1186         if (!pJob || pJob->pPrinter != pPrinter)
1187         {
1188             // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
1189             dwErrorCode = ERROR_INVALID_PRINTER_NAME;
1190             goto Failure;
1191         }
1192 
1193         // Try to open its SPL file.
1194         GetJobFilePath(L"SPL", dwJobID, wszFullPath);
1195         pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1196         if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
1197         {
1198             dwErrorCode = GetLastError();
1199             ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath);
1200             goto Failure;
1201         }
1202 
1203         // Associate the job to our Printer Handle, but don't set bStartedDoc.
1204         // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
1205         pPrinterHandle->pJob = pJob;
1206     }
1207 
1208     // Make the generic handle a Printer handle.
1209     pHandle->HandleType = HandleType_Printer;
1210     pHandle->pSpecificHandle = pPrinterHandle;
1211 
1212     // Return it.
1213     *phPrinter = (HANDLE)pHandle;
1214     return ERROR_SUCCESS;
1215 
1216 Failure:
1217     if (pHandle)
1218         DllFreeSplMem(pHandle);
1219 
1220     if (pPrinterHandle)
1221     {
1222         if (pPrinterHandle->pwszDatatype)
1223             DllFreeSplStr(pPrinterHandle->pwszDatatype);
1224 
1225         if (pPrinterHandle->pDevMode)
1226             DllFreeSplMem(pPrinterHandle->pDevMode);
1227 
1228         DllFreeSplMem(pPrinterHandle);
1229     }
1230 
1231     return dwErrorCode;
1232 }
1233 
1234 static DWORD
1235 _LocalOpenPrintServerHandle(PHANDLE phPrinter)
1236 {
1237     PLOCAL_HANDLE pHandle;
1238 
1239     // Create a new generic handle.
1240     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1241     if (!pHandle)
1242     {
1243         ERR("DllAllocSplMem failed!\n");
1244         return ERROR_NOT_ENOUGH_MEMORY;
1245     }
1246 
1247     // Make the generic handle a Print Server handle.
1248     pHandle->HandleType = HandleType_PrintServer;
1249     pHandle->pSpecificHandle = NULL;
1250 
1251     // Return it.
1252     *phPrinter = (HANDLE)pHandle;
1253     return ERROR_SUCCESS;
1254 }
1255 
1256 static DWORD
1257 _LocalOpenXcvHandle(PWSTR pwszParameter, PHANDLE phPrinter)
1258 {
1259     BOOL bReturnValue;
1260     DWORD dwErrorCode;
1261     HANDLE hXcv;
1262     PLOCAL_HANDLE pHandle = NULL;
1263     PLOCAL_PORT pPort;
1264     PLOCAL_PRINT_MONITOR pPrintMonitor;
1265     PLOCAL_XCV_HANDLE pXcvHandle = NULL;
1266 
1267     // Skip the "Xcv" string.
1268     pwszParameter += 3;
1269 
1270     // Is XcvMonitor or XcvPort requested?
1271     if (wcsncmp(pwszParameter, L"Monitor ", 8) == 0)
1272     {
1273         // Skip the "Monitor " string.
1274         pwszParameter += 8;
1275 
1276         // Look for this monitor in our Print Monitor list.
1277         pPrintMonitor = FindPrintMonitor(pwszParameter);
1278         if (!pPrintMonitor)
1279         {
1280             // The caller supplied a non-existing Monitor name.
1281             dwErrorCode = ERROR_INVALID_NAME;
1282             goto Failure;
1283         }
1284     }
1285     else if (wcsncmp(pwszParameter, L"Port ", 5) == 0)
1286     {
1287         // Skip the "Port " string.
1288         pwszParameter += 5;
1289 
1290         // Look for this port in our Print Monitor Port list.
1291         pPort = FindPort(pwszParameter);
1292         if (!pPort)
1293         {
1294             // The supplied port is unknown to all our Print Monitors.
1295             dwErrorCode = ERROR_INVALID_NAME;
1296             goto Failure;
1297         }
1298 
1299         pPrintMonitor = pPort->pPrintMonitor;
1300     }
1301     else
1302     {
1303         dwErrorCode = ERROR_INVALID_NAME;
1304         goto Failure;
1305     }
1306 
1307     // Call the monitor's XcvOpenPort function.
1308     if (pPrintMonitor->bIsLevel2)
1309         bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszParameter, SERVER_EXECUTE, &hXcv);
1310     else
1311         bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszParameter, SERVER_EXECUTE, &hXcv);
1312 
1313     if (!bReturnValue)
1314     {
1315         // The XcvOpenPort function failed. Return its last error.
1316         dwErrorCode = GetLastError();
1317         goto Failure;
1318     }
1319 
1320     // Create a new generic handle.
1321     pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
1322     if (!pHandle)
1323     {
1324         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1325         ERR("DllAllocSplMem failed!\n");
1326         goto Failure;
1327     }
1328 
1329     // Create a new LOCAL_XCV_HANDLE.
1330     pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
1331     if (!pXcvHandle)
1332     {
1333         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1334         ERR("DllAllocSplMem failed!\n");
1335         goto Failure;
1336     }
1337 
1338     pXcvHandle->hXcv = hXcv;
1339     pXcvHandle->pPrintMonitor = pPrintMonitor;
1340 
1341     // Make the generic handle a Xcv handle.
1342     pHandle->HandleType = HandleType_Xcv;
1343     pHandle->pSpecificHandle = pXcvHandle;
1344 
1345     // Return it.
1346     *phPrinter = (HANDLE)pHandle;
1347     return ERROR_SUCCESS;
1348 
1349 Failure:
1350     if (pHandle)
1351         DllFreeSplMem(pHandle);
1352 
1353     if (pXcvHandle)
1354         DllFreeSplMem(pXcvHandle);
1355 
1356     return dwErrorCode;
1357 }
1358 
1359 BOOL WINAPI
1360 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
1361 {
1362     DWORD cchComputerName;
1363     DWORD cchFirstParameter;
1364     DWORD dwErrorCode;
1365     PWSTR p = lpPrinterName;
1366     PWSTR pwszFirstParameter = NULL;
1367     PWSTR pwszSecondParameter;
1368     WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
1369 
1370     TRACE("LocalOpenPrinter(%S, %p, %p)\n", lpPrinterName, phPrinter, pDefault);
1371 
1372     ASSERT(phPrinter);
1373     *phPrinter = NULL;
1374 
1375     if (!lpPrinterName)
1376     {
1377         // The caller wants a Print Server handle and provided a NULL string.
1378         dwErrorCode = _LocalOpenPrintServerHandle(phPrinter);
1379         goto Cleanup;
1380     }
1381 
1382     // Skip any server name in the first parameter.
1383     // Does lpPrinterName begin with two backslashes to indicate a server name?
1384     if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
1385     {
1386         // Skip these two backslashes.
1387         lpPrinterName += 2;
1388 
1389         // Look for the terminating null character or closing backslash.
1390         p = lpPrinterName;
1391         while (*p != L'\0' && *p != L'\\')
1392             p++;
1393 
1394         // Get the local computer name for comparison.
1395         cchComputerName = _countof(wszComputerName);
1396         if (!GetComputerNameW(wszComputerName, &cchComputerName))
1397         {
1398             dwErrorCode = GetLastError();
1399             ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
1400             goto Cleanup;
1401         }
1402 
1403         // Now compare this string excerpt with the local computer name.
1404         // The input parameter may not be writable, so we can't null-terminate the input string at this point.
1405         // This print provider only supports local printers, so both strings have to match.
1406         if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0)
1407         {
1408             dwErrorCode = ERROR_INVALID_NAME;
1409             goto Cleanup;
1410         }
1411 
1412         // If lpPrinterName is only "\\COMPUTERNAME" with nothing more, the caller wants a handle to the local Print Server.
1413         if (!*p)
1414         {
1415             // The caller wants a Print Server handle and provided a string like:
1416             //    "\\COMPUTERNAME"
1417             dwErrorCode = _LocalOpenPrintServerHandle(phPrinter);
1418             goto Cleanup;
1419         }
1420 
1421         // We have checked the server name and don't need it anymore.
1422         lpPrinterName = p + 1;
1423     }
1424 
1425     // Look for a comma. If it exists, it indicates the end of the first parameter.
1426     pwszSecondParameter = wcschr(lpPrinterName, L',');
1427     if (pwszSecondParameter)
1428         cchFirstParameter = pwszSecondParameter - p;
1429     else
1430         cchFirstParameter = wcslen(lpPrinterName);
1431 
1432     // We must have at least one parameter.
1433     if (!cchFirstParameter && !pwszSecondParameter)
1434     {
1435         dwErrorCode = ERROR_INVALID_NAME;
1436         goto Cleanup;
1437     }
1438 
1439     // Do we have a first parameter?
1440     if (cchFirstParameter)
1441     {
1442         // Yes, extract it.
1443         // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
1444         pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR));
1445         CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR));
1446     }
1447 
1448     // Do we have a second parameter?
1449     if (pwszSecondParameter)
1450     {
1451         // Yes, skip the comma at the beginning.
1452         ++pwszSecondParameter;
1453 
1454         // Skip whitespace as well.
1455         while (*pwszSecondParameter == L' ')
1456             ++pwszSecondParameter;
1457     }
1458 
1459     // Now we can finally check the type of handle actually requested.
1460     if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0)
1461     {
1462         // The caller wants a port handle and provided a string like:
1463         //    "LPT1:, Port"
1464         //    "\\COMPUTERNAME\LPT1:, Port"
1465         dwErrorCode = _LocalOpenPortHandle(pwszFirstParameter, phPrinter);
1466     }
1467     else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
1468     {
1469         // The caller wants an Xcv handle and provided a string like:
1470         //    ", XcvMonitor Local Port"
1471         //    "\\COMPUTERNAME\, XcvMonitor Local Port"
1472         //    ", XcvPort LPT1:"
1473         //    "\\COMPUTERNAME\, XcvPort LPT1:"
1474         dwErrorCode = _LocalOpenXcvHandle(pwszSecondParameter, phPrinter);
1475     }
1476     else
1477     {
1478         // The caller wants a Printer or Printer Job handle and provided a string like:
1479         //    "HP DeskJet"
1480         //    "\\COMPUTERNAME\HP DeskJet"
1481         //    "HP DeskJet, Job 5"
1482         //    "\\COMPUTERNAME\HP DeskJet, Job 5"
1483         dwErrorCode = _LocalOpenPrinterHandle(pwszFirstParameter, pwszSecondParameter, phPrinter, pDefault);
1484     }
1485 
1486 Cleanup:
1487     if (pwszFirstParameter)
1488         DllFreeSplMem(pwszFirstParameter);
1489 
1490     SetLastError(dwErrorCode);
1491     return (dwErrorCode == ERROR_SUCCESS);
1492 }
1493 
1494 BOOL WINAPI
1495 LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
1496 {
1497     BOOL bReturnValue;
1498     DWORD dwErrorCode;
1499     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1500     PLOCAL_PORT_HANDLE pPortHandle;
1501     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1502 
1503     TRACE("LocalReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
1504 
1505     // Sanity checks.
1506     if (!pHandle)
1507     {
1508         dwErrorCode = ERROR_INVALID_HANDLE;
1509         goto Cleanup;
1510     }
1511 
1512     // Port handles are an entirely different thing.
1513     if (pHandle->HandleType == HandleType_Port)
1514     {
1515         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1516 
1517         // Call the monitor's ReadPort function.
1518         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1519             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
1520         else
1521             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
1522 
1523         if (!bReturnValue)
1524         {
1525             // The ReadPort function failed. Return its last error.
1526             dwErrorCode = GetLastError();
1527             goto Cleanup;
1528         }
1529 
1530         // We were successful!
1531         dwErrorCode = ERROR_SUCCESS;
1532         goto Cleanup;
1533     }
1534 
1535     // The remaining function deals with Printer handles only.
1536     if (pHandle->HandleType != HandleType_Printer)
1537     {
1538         dwErrorCode = ERROR_INVALID_HANDLE;
1539         goto Cleanup;
1540     }
1541 
1542     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1543 
1544     // ReadPrinter needs an opened SPL file to work.
1545     // This only works if a Printer Job Handle was requested in OpenPrinter.
1546     if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
1547     {
1548         dwErrorCode = ERROR_INVALID_HANDLE;
1549         goto Cleanup;
1550     }
1551 
1552     // Pass the parameters to ReadFile.
1553     if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
1554     {
1555         dwErrorCode = GetLastError();
1556         ERR("ReadFile failed with error %lu!\n", dwErrorCode);
1557         goto Cleanup;
1558     }
1559 
1560     dwErrorCode = ERROR_SUCCESS;
1561 
1562 Cleanup:
1563     SetLastError(dwErrorCode);
1564     return (dwErrorCode == ERROR_SUCCESS);
1565 }
1566 
1567 DWORD WINAPI
1568 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1569 {
1570     BOOL bReturnValue;
1571     DWORD dwErrorCode;
1572     DWORD dwReturnValue = 0;
1573     PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1574     PLOCAL_JOB pJob;
1575     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1576     PLOCAL_PORT_HANDLE pPortHandle;
1577     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1578 
1579     TRACE("LocalStartDocPrinter(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
1580 
1581     // Sanity checks.
1582     if (!pHandle)
1583     {
1584         dwErrorCode = ERROR_INVALID_HANDLE;
1585         goto Cleanup;
1586     }
1587 
1588     // Port handles are an entirely different thing.
1589     if (pHandle->HandleType == HandleType_Port)
1590     {
1591         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1592 
1593         // 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.
1594         // Claim it exclusively for this port handle.
1595         pJob = pPortHandle->pPort->pNextJobToProcess;
1596         pPortHandle->pPort->pNextJobToProcess = NULL;
1597         ASSERT(pJob);
1598 
1599         // Call the monitor's StartDocPort function.
1600         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1601             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1602         else
1603             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1604 
1605         if (!bReturnValue)
1606         {
1607             // The StartDocPort function failed. Return its last error.
1608             dwErrorCode = GetLastError();
1609             goto Cleanup;
1610         }
1611 
1612         // We were successful!
1613         dwErrorCode = ERROR_SUCCESS;
1614         dwReturnValue = pJob->dwJobID;
1615         goto Cleanup;
1616     }
1617 
1618     // The remaining function deals with Printer handles only.
1619     if (pHandle->HandleType != HandleType_Printer)
1620     {
1621         dwErrorCode = ERROR_INVALID_HANDLE;
1622         goto Cleanup;
1623     }
1624 
1625     if (!pDocInfo1)
1626     {
1627         dwErrorCode = ERROR_INVALID_PARAMETER;
1628         goto Cleanup;
1629     }
1630 
1631     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1632 
1633     // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1634     if (pPrinterHandle->pJob)
1635     {
1636         dwErrorCode = ERROR_INVALID_PARAMETER;
1637         goto Cleanup;
1638     }
1639 
1640     // Check the validity of the datatype if we got one.
1641     if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
1642     {
1643         dwErrorCode = ERROR_INVALID_DATATYPE;
1644         goto Cleanup;
1645     }
1646 
1647     // Check if this is the right document information level.
1648     if (Level != 1)
1649     {
1650         dwErrorCode = ERROR_INVALID_LEVEL;
1651         goto Cleanup;
1652     }
1653 
1654     // All requirements are met - create a new job.
1655     dwErrorCode = CreateJob(pPrinterHandle);
1656     if (dwErrorCode != ERROR_SUCCESS)
1657         goto Cleanup;
1658 
1659     // Use any given datatype.
1660     if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
1661     {
1662         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1663         ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1664         goto Cleanup;
1665     }
1666 
1667     // Use any given document name.
1668     if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
1669     {
1670         dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1671         ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1672         goto Cleanup;
1673     }
1674 
1675     // We were successful!
1676     dwErrorCode = ERROR_SUCCESS;
1677     dwReturnValue = pPrinterHandle->pJob->dwJobID;
1678 
1679 Cleanup:
1680     SetLastError(dwErrorCode);
1681     return dwReturnValue;
1682 }
1683 
1684 BOOL WINAPI
1685 LocalStartPagePrinter(HANDLE hPrinter)
1686 {
1687     DWORD dwErrorCode;
1688     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1689     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1690 
1691     TRACE("LocalStartPagePrinter(%p)\n", hPrinter);
1692 
1693     // Sanity checks.
1694     if (!pHandle || pHandle->HandleType != HandleType_Printer)
1695     {
1696         dwErrorCode = ERROR_INVALID_HANDLE;
1697         goto Cleanup;
1698     }
1699 
1700     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1701 
1702     // We require StartDocPrinter or AddJob to be called first.
1703     if (!pPrinterHandle->bStartedDoc)
1704     {
1705         dwErrorCode = ERROR_SPL_NO_STARTDOC;
1706         goto Cleanup;
1707     }
1708 
1709     // Increase the page count.
1710     ++pPrinterHandle->pJob->dwTotalPages;
1711     dwErrorCode = ERROR_SUCCESS;
1712 
1713 Cleanup:
1714     SetLastError(dwErrorCode);
1715     return (dwErrorCode == ERROR_SUCCESS);
1716 }
1717 
1718 BOOL WINAPI
1719 LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1720 {
1721     BOOL bReturnValue;
1722     DWORD dwErrorCode;
1723     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1724     PLOCAL_PORT_HANDLE pPortHandle;
1725     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1726 
1727     TRACE("LocalWritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
1728 
1729     // Sanity checks.
1730     if (!pHandle)
1731     {
1732         dwErrorCode = ERROR_INVALID_HANDLE;
1733         goto Cleanup;
1734     }
1735 
1736     // Port handles are an entirely different thing.
1737     if (pHandle->HandleType == HandleType_Port)
1738     {
1739         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1740 
1741         // Call the monitor's WritePort function.
1742         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1743             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1744         else
1745             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1746 
1747         if (!bReturnValue)
1748         {
1749             // The WritePort function failed. Return its last error.
1750             dwErrorCode = GetLastError();
1751             goto Cleanup;
1752         }
1753 
1754         // We were successful!
1755         dwErrorCode = ERROR_SUCCESS;
1756         goto Cleanup;
1757     }
1758 
1759     // The remaining function deals with Printer handles only.
1760     if (pHandle->HandleType != HandleType_Printer)
1761     {
1762         dwErrorCode = ERROR_INVALID_HANDLE;
1763         goto Cleanup;
1764     }
1765 
1766     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1767 
1768     // We require StartDocPrinter or AddJob to be called first.
1769     if (!pPrinterHandle->bStartedDoc)
1770     {
1771         dwErrorCode = ERROR_SPL_NO_STARTDOC;
1772         goto Cleanup;
1773     }
1774 
1775     // TODO: This function is only called when doing non-spooled printing.
1776     // 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).
1777 #if 0
1778     // Pass the parameters to WriteFile.
1779     if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL))
1780     {
1781         dwErrorCode = GetLastError();
1782         ERR("WriteFile failed with error %lu!\n", GetLastError());
1783         goto Cleanup;
1784     }
1785 #endif
1786 
1787     dwErrorCode = ERROR_SUCCESS;
1788 
1789 Cleanup:
1790     SetLastError(dwErrorCode);
1791     return (dwErrorCode == ERROR_SUCCESS);
1792 }
1793 
1794 BOOL WINAPI
1795 LocalEndPagePrinter(HANDLE hPrinter)
1796 {
1797     DWORD dwErrorCode;
1798     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1799 
1800     TRACE("LocalEndPagePrinter(%p)\n", hPrinter);
1801 
1802     // Sanity checks.
1803     if (!pHandle || pHandle->HandleType != HandleType_Printer)
1804     {
1805         dwErrorCode = ERROR_INVALID_HANDLE;
1806         goto Cleanup;
1807     }
1808 
1809     // This function doesn't do anything else for now.
1810     dwErrorCode = ERROR_SUCCESS;
1811 
1812 Cleanup:
1813     SetLastError(dwErrorCode);
1814     return (dwErrorCode == ERROR_SUCCESS);
1815 }
1816 
1817 BOOL WINAPI
1818 LocalEndDocPrinter(HANDLE hPrinter)
1819 {
1820     BOOL bReturnValue;
1821     DWORD dwErrorCode;
1822     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1823     PLOCAL_PORT_HANDLE pPortHandle;
1824     PLOCAL_PRINTER_HANDLE pPrinterHandle;
1825 
1826     TRACE("LocalEndDocPrinter(%p)\n", hPrinter);
1827 
1828     // Sanity checks.
1829     if (!pHandle)
1830     {
1831         dwErrorCode = ERROR_INVALID_HANDLE;
1832         goto Cleanup;
1833     }
1834 
1835     // Port handles are an entirely different thing.
1836     if (pHandle->HandleType == HandleType_Port)
1837     {
1838         pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1839 
1840         // Call the monitor's EndDocPort function.
1841         if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1842             bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort);
1843         else
1844             bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort);
1845 
1846         if (!bReturnValue)
1847         {
1848             // The EndDocPort function failed. Return its last error.
1849             dwErrorCode = GetLastError();
1850             goto Cleanup;
1851         }
1852 
1853         // We were successful!
1854         dwErrorCode = ERROR_SUCCESS;
1855         goto Cleanup;
1856     }
1857 
1858     // The remaining function deals with Printer handles only.
1859     if (pHandle->HandleType != HandleType_Printer)
1860     {
1861         dwErrorCode = ERROR_INVALID_HANDLE;
1862         goto Cleanup;
1863     }
1864 
1865     pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1866 
1867     // We require StartDocPrinter or AddJob to be called first.
1868     if (!pPrinterHandle->bStartedDoc)
1869     {
1870         dwErrorCode = ERROR_SPL_NO_STARTDOC;
1871         goto Cleanup;
1872     }
1873 
1874     // TODO: Something like ScheduleJob
1875 
1876     // Finish the job.
1877     pPrinterHandle->bStartedDoc = FALSE;
1878     pPrinterHandle->pJob = NULL;
1879     dwErrorCode = ERROR_SUCCESS;
1880 
1881 Cleanup:
1882     SetLastError(dwErrorCode);
1883     return (dwErrorCode == ERROR_SUCCESS);
1884 }
1885 
1886 static void
1887 _LocalClosePortHandle(PLOCAL_PORT_HANDLE pPortHandle)
1888 {
1889     // Call the monitor's ClosePort function.
1890     if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1891         ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort);
1892     else
1893         ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort);
1894 }
1895 
1896 static void
1897 _LocalClosePrinterHandle(PLOCAL_PRINTER_HANDLE pPrinterHandle)
1898 {
1899     // Terminate any started job.
1900     if (pPrinterHandle->pJob)
1901         FreeJob(pPrinterHandle->pJob);
1902 
1903     // Free memory for the fields.
1904     DllFreeSplMem(pPrinterHandle->pDevMode);
1905     DllFreeSplStr(pPrinterHandle->pwszDatatype);
1906 }
1907 
1908 static void
1909 _LocalCloseXcvHandle(PLOCAL_XCV_HANDLE pXcvHandle)
1910 {
1911     // Call the monitor's XcvClosePort function.
1912     if (pXcvHandle->pPrintMonitor->bIsLevel2)
1913         ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
1914     else
1915         ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
1916 }
1917 
1918 BOOL WINAPI
1919 LocalClosePrinter(HANDLE hPrinter)
1920 {
1921     PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1922 
1923     TRACE("LocalClosePrinter(%p)\n", hPrinter);
1924 
1925     if (!pHandle)
1926     {
1927         SetLastError(ERROR_INVALID_HANDLE);
1928         return FALSE;
1929     }
1930 
1931     if (pHandle->HandleType == HandleType_Port)
1932     {
1933         _LocalClosePortHandle(pHandle->pSpecificHandle);
1934     }
1935     else if (pHandle->HandleType == HandleType_Printer)
1936     {
1937         _LocalClosePrinterHandle(pHandle->pSpecificHandle);
1938     }
1939     else if (pHandle->HandleType == HandleType_PrintServer)
1940     {
1941         // Nothing to do.
1942     }
1943     else if (pHandle->HandleType == HandleType_Xcv)
1944     {
1945         _LocalCloseXcvHandle(pHandle->pSpecificHandle);
1946     }
1947 
1948     // Free memory for the handle and the specific handle (if any).
1949     if (pHandle->pSpecificHandle)
1950         DllFreeSplMem(pHandle->pSpecificHandle);
1951 
1952     DllFreeSplMem(pHandle);
1953 
1954     return TRUE;
1955 }
1956