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