339c138208fd7f62d3045b5fcaca5320b0eb50ab
[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 // Local Constants
14 static DWORD dwPrinterInfo1Offsets[] = {
15 FIELD_OFFSET(PRINTER_INFO_1W, pName),
16 FIELD_OFFSET(PRINTER_INFO_1W, pComment),
17 FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
18 MAXDWORD
19 };
20
21 /**
22 * @name _PrinterListCompareRoutine
23 *
24 * SKIPLIST_COMPARE_ROUTINE for the Printer List.
25 * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
26 */
27 static int WINAPI
28 _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
29 {
30 PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct;
31 PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct;
32
33 return wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
34 }
35
36 /**
37 * @name InitializePrinterList
38 *
39 * Initializes a list of locally available Printers.
40 * The list is searchable by name and returns information about the printers, including their job queues.
41 * During this process, the job queues are also initialized.
42 */
43 BOOL
44 InitializePrinterList()
45 {
46 const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
47
48 DWORD cbData;
49 DWORD cchPrinterName;
50 DWORD dwErrorCode;
51 DWORD dwSubKeys;
52 DWORD i;
53 HKEY hKey = NULL;
54 HKEY hSubKey = NULL;
55 PLOCAL_PORT pPort;
56 PLOCAL_PRINTER pPrinter = NULL;
57 PLOCAL_PRINT_PROCESSOR pPrintProcessor;
58 PWSTR pwszPort = NULL;
59 PWSTR pwszPrintProcessor = NULL;
60 WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
61
62 // Initialize an empty list for our printers.
63 InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
64
65 // Open our printers registry key. Each subkey is a local printer there.
66 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
67 if (dwErrorCode != ERROR_SUCCESS)
68 {
69 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
70 goto Cleanup;
71 }
72
73 // Get the number of subkeys.
74 dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
75 if (dwErrorCode != ERROR_SUCCESS)
76 {
77 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
78 goto Cleanup;
79 }
80
81 // Loop through all available local printers.
82 for (i = 0; i < dwSubKeys; i++)
83 {
84 // Cleanup tasks from the previous run
85 if (hSubKey)
86 {
87 RegCloseKey(hSubKey);
88 hSubKey = NULL;
89 }
90
91 if (pPrinter)
92 {
93 if (pPrinter->pDefaultDevMode)
94 DllFreeSplMem(pPrinter->pDefaultDevMode);
95
96 if (pPrinter->pwszDefaultDatatype)
97 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
98
99 if (pPrinter->pwszDescription)
100 DllFreeSplStr(pPrinter->pwszDescription);
101
102 if (pPrinter->pwszPrinterDriver)
103 DllFreeSplStr(pPrinter->pwszPrinterDriver);
104
105 if (pPrinter->pwszPrinterName)
106 DllFreeSplStr(pPrinter->pwszPrinterName);
107
108 DllFreeSplMem(pPrinter);
109 pPrinter = NULL;
110 }
111
112 if (pwszPrintProcessor)
113 {
114 DllFreeSplStr(pwszPrintProcessor);
115 pwszPrintProcessor = NULL;
116 }
117
118 // Get the name of this printer.
119 cchPrinterName = _countof(wszPrinterName);
120 dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
121 if (dwErrorCode == ERROR_MORE_DATA)
122 {
123 // This printer name exceeds the maximum length and is invalid.
124 continue;
125 }
126 else if (dwErrorCode != ERROR_SUCCESS)
127 {
128 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
129 continue;
130 }
131
132 // Open this Printer's registry key.
133 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
134 if (dwErrorCode != ERROR_SUCCESS)
135 {
136 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
137 continue;
138 }
139
140 // Get the Print Processor.
141 pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor");
142 if (!pwszPrintProcessor)
143 continue;
144
145 // Try to find it in the Print Processor List.
146 pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
147 if (!pPrintProcessor)
148 {
149 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
150 continue;
151 }
152
153 // Get the Port.
154 pwszPort = AllocAndRegQueryWSZ(hSubKey, L"Port");
155 if (!pwszPort)
156 continue;
157
158 // Try to find it in the Port List.
159 pPort = FindPort(pwszPort);
160 if (!pPort)
161 {
162 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort, wszPrinterName);
163 continue;
164 }
165
166 // Create a new LOCAL_PRINTER structure for it.
167 pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
168 if (!pPrinter)
169 {
170 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
171 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
172 goto Cleanup;
173 }
174
175 pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
176 pPrinter->pPrintProcessor = pPrintProcessor;
177 pPrinter->pPort = pPort;
178 InitializePrinterJobList(pPrinter);
179
180 // Get the location.
181 pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location");
182 if (!pPrinter->pwszLocation)
183 continue;
184
185 // Get the printer driver.
186 pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
187 if (!pPrinter->pwszPrinterDriver)
188 continue;
189
190 // Get the description.
191 pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description");
192 if (!pPrinter->pwszDescription)
193 continue;
194
195 // Get the default datatype.
196 pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype");
197 if (!pPrinter->pwszDefaultDatatype)
198 continue;
199
200 // Verify that it's valid.
201 if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
202 {
203 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
204 continue;
205 }
206
207 // Determine the size of the DevMode.
208 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, NULL, &cbData);
209 if (dwErrorCode != ERROR_SUCCESS)
210 {
211 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
212 continue;
213 }
214
215 // Allocate enough memory for the DevMode.
216 pPrinter->pDefaultDevMode = DllAllocSplMem(cbData);
217 if (!pPrinter->pDefaultDevMode)
218 {
219 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
220 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
221 goto Cleanup;
222 }
223
224 // Get the default DevMode.
225 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData);
226 if (dwErrorCode != ERROR_SUCCESS)
227 {
228 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
229 continue;
230 }
231
232 // Get the Attributes.
233 cbData = sizeof(DWORD);
234 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData);
235 if (dwErrorCode != ERROR_SUCCESS)
236 {
237 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
238 continue;
239 }
240
241 // Get the Status.
242 cbData = sizeof(DWORD);
243 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
244 if (dwErrorCode != ERROR_SUCCESS)
245 {
246 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
247 continue;
248 }
249
250 // Add this printer to the printer list.
251 if (!InsertElementSkiplist(&PrinterList, pPrinter))
252 {
253 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
254 goto Cleanup;
255 }
256
257 // Don't let the cleanup routines free this.
258 pPrinter = NULL;
259 }
260
261 dwErrorCode = ERROR_SUCCESS;
262
263 Cleanup:
264 // Inside the loop
265 if (hSubKey)
266 RegCloseKey(hSubKey);
267
268 if (pPrinter)
269 {
270 if (pPrinter->pDefaultDevMode)
271 DllFreeSplMem(pPrinter->pDefaultDevMode);
272
273 if (pPrinter->pwszDefaultDatatype)
274 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
275
276 if (pPrinter->pwszDescription)
277 DllFreeSplStr(pPrinter->pwszDescription);
278
279 if (pPrinter->pwszPrinterDriver)
280 DllFreeSplStr(pPrinter->pwszPrinterDriver);
281
282 if (pPrinter->pwszPrinterName)
283 DllFreeSplStr(pPrinter->pwszPrinterName);
284
285 DllFreeSplMem(pPrinter);
286 }
287
288 if (pwszPrintProcessor)
289 DllFreeSplStr(pwszPrintProcessor);
290
291 // Outside the loop
292 if (hKey)
293 RegCloseKey(hKey);
294
295 SetLastError(dwErrorCode);
296 return (dwErrorCode == ERROR_SUCCESS);
297 }
298
299 /**
300 * @name _LocalEnumPrintersCheckName
301 *
302 * Checks the Name parameter supplied to a call to EnumPrinters.
303 *
304 * @param Flags
305 * Flags parameter of EnumPrinters.
306 *
307 * @param Name
308 * Name parameter of EnumPrinters to check.
309 *
310 * @param pwszComputerName
311 * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
312 * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
313 *
314 * @param pcchComputerName
315 * If a string to prepend is returned, this pointer receives its length in characters.
316 *
317 * @return
318 * ERROR_SUCCESS if processing in EnumPrinters can be continued.
319 * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
320 * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
321 */
322 static DWORD
323 _LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
324 {
325 PCWSTR pName;
326 PCWSTR pComputerName;
327
328 // If there is no Name parameter to check, we can just continue in EnumPrinters.
329 if (!Name)
330 return ERROR_SUCCESS;
331
332 // Check if Name does not begin with two backslashes (required for specifying Computer Names).
333 if (Name[0] != L'\\' || Name[1] != L'\\')
334 {
335 if (Flags & PRINTER_ENUM_NAME)
336 {
337 // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
338 // Print Provider Name or the local Computer Name.
339
340 // Compare with the Print Provider Name.
341 if (wcsicmp(Name, wszPrintProviderInfo[0]) == 0)
342 return ERROR_SUCCESS;
343
344 // Dismiss anything else.
345 return ERROR_INVALID_NAME;
346 }
347 else
348 {
349 // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
350 return ERROR_SUCCESS;
351 }
352 }
353
354 // Prepend the backslashes to the output computer name.
355 pwszComputerName[0] = L'\\';
356 pwszComputerName[1] = L'\\';
357
358 // Get the local computer name for comparison.
359 *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
360 if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
361 {
362 ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
363 return GetLastError();
364 }
365
366 // Add the leading slashes to the total length.
367 *pcchComputerName += 2;
368
369 // Compare both names.
370 pComputerName = &pwszComputerName[2];
371 pName = &Name[2];
372 for (;;)
373 {
374 // Are we at the end of the local Computer Name string?
375 if (!*pComputerName)
376 {
377 // Are we also at the end of the supplied Name parameter?
378 // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
379 if (!*pName)
380 {
381 // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
382 // Add a trailing backslash for that.
383 pwszComputerName[(*pcchComputerName)++] = L'\\';
384 pwszComputerName[*pcchComputerName] = 0;
385 return ERROR_SUCCESS;
386 }
387 else if (*pName == L'\\')
388 {
389 if (Flags & PRINTER_ENUM_NAME)
390 {
391 // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
392 // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
393 return ERROR_INVALID_NAME;
394 }
395 else
396 {
397 // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
398 // Only the Computer Name between the backslashes is checked then.
399 // This is largely undocumented, but verified by tests (see winspool_apitest).
400 // In this case, no computer name is prepended in EnumPrinters though.
401 *pwszComputerName = 0;
402 *pcchComputerName = 0;
403 return ERROR_SUCCESS;
404 }
405 }
406 }
407
408 // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
409 if (towlower(*pName) != towlower(*pComputerName))
410 return ERROR_INVALID_NAME;
411
412 pName++;
413 pComputerName++;
414 }
415 }
416
417 static DWORD
418 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
419 {
420 int i;
421
422 // Count the needed bytes for Print Provider information.
423 *pcbNeeded = sizeof(PRINTER_INFO_1W);
424
425 for (i = 0; i < 3; i++)
426 *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
427
428 // Check if the supplied buffer is large enough.
429 if (cbBuf < *pcbNeeded)
430 return ERROR_INSUFFICIENT_BUFFER;
431
432 // Copy over the Print Provider information.
433 ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
434 PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]);
435 *pcReturned = 1;
436
437 return ERROR_SUCCESS;
438 }
439
440 static DWORD
441 _LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
442 {
443 return ERROR_INVALID_LEVEL;
444 }
445
446 static DWORD
447 _LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
448 {
449 const WCHAR wszComma[] = L",";
450
451 size_t cbName;
452 size_t cbComment;
453 size_t cbDescription;
454 DWORD dwErrorCode;
455 DWORD i;
456 PBYTE pPrinterInfo;
457 PBYTE pPrinterStrings;
458 PSKIPLIST_NODE pNode;
459 PLOCAL_PRINTER pPrinter;
460 PWSTR p;
461 PWSTR pwszStrings[3];
462
463 if (Flags & PRINTER_ENUM_NAME && !Name)
464 {
465 // The caller wants information about this Print Provider.
466 // spoolss packs this into an array of information about all Print Providers.
467 dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
468 goto Cleanup;
469 }
470
471 // Count the required buffer size and the number of printers.
472 i = 0;
473
474 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
475 {
476 pPrinter = (PLOCAL_PRINTER)pNode->Element;
477
478 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
479 if (Flags & PRINTER_ENUM_SHARED)
480 continue;
481
482 // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
483 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
484 cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
485 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
486 cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
487
488 *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
489 i++;
490 }
491
492 // Check if the supplied buffer is large enough.
493 if (cbBuf < *pcbNeeded)
494 {
495 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
496 goto Cleanup;
497 }
498
499 // Initialize the variables for filling the output buffer using PackStrings.
500 pPrinterInfo = pPrinterEnum;
501 pPrinterStrings = &pPrinterEnum[*pcbNeeded];
502
503 // Copy over the Printer information.
504 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
505 {
506 pPrinter = (PLOCAL_PRINTER)pNode->Element;
507
508 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
509 if (Flags & PRINTER_ENUM_SHARED)
510 continue;
511
512 // Indicate that this is a Printer.
513 ((PPRINTER_INFO_1W)pPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
514
515 // Calculate the string lengths.
516 cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
517 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
518 cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
519
520 // Copy the Printer Name.
521 pwszStrings[0] = DllAllocSplMem(cbName);
522 p = pwszStrings[0];
523 StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
524 StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
525
526 // Copy the Printer comment (equals the "Description" registry value).
527 pwszStrings[1] = pPrinter->pwszDescription;
528
529 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
530 pwszStrings[2] = DllAllocSplMem(cbDescription);
531 p = pwszStrings[2];
532 StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
533 StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
534 StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
535 StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
536 StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
537 StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
538
539 // Finally copy the structure and advance to the next one in the output buffer.
540 pPrinterStrings = PackStrings(pwszStrings, pPrinterInfo, dwPrinterInfo1Offsets, pPrinterStrings);
541 pPrinterInfo += sizeof(PRINTER_INFO_1W);
542
543 // Free the memory for temporary strings.
544 DllFreeSplMem(pwszStrings[0]);
545 DllFreeSplMem(pwszStrings[2]);
546 }
547
548 *pcReturned = i;
549 dwErrorCode = ERROR_SUCCESS;
550
551 Cleanup:
552 return dwErrorCode;
553 }
554
555 static DWORD
556 _LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
557 {
558 return ERROR_INVALID_LEVEL;
559 }
560
561 static DWORD
562 _LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
563 {
564 return ERROR_INVALID_LEVEL;
565 }
566
567 static DWORD
568 _LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
569 {
570 return ERROR_INVALID_LEVEL;
571 }
572
573 BOOL WINAPI
574 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
575 {
576 DWORD cchComputerName = 0;
577 DWORD dwErrorCode;
578 WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
579
580 ASSERT(pcbNeeded);
581 ASSERT(pcReturned);
582
583 // Begin counting.
584 *pcbNeeded = 0;
585 *pcReturned = 0;
586
587 if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK)
588 {
589 // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME.
590 // This is the internal way for a Print Provider to signal that it doesn't handle this request.
591 dwErrorCode = ERROR_INVALID_NAME;
592 goto Cleanup;
593 }
594
595 if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME))
596 {
597 // The Local Print Provider is the right destination for the request, but without any of these flags,
598 // there is no information that can be returned.
599 // So just signal a successful request.
600 dwErrorCode = ERROR_SUCCESS;
601 goto Cleanup;
602 }
603
604 // Check the supplied Name parameter (if any).
605 // This may return a Computer Name string we later prepend to the output.
606 dwErrorCode = _LocalEnumPrintersCheckName(Flags, Name, wszComputerName, &cchComputerName);
607 if (dwErrorCode != ERROR_SUCCESS)
608 goto Cleanup;
609
610 if (Level == 0)
611 {
612 dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
613 }
614 else if (Level == 1)
615 {
616 dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
617 }
618 else if (Level == 2)
619 {
620 dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
621 }
622 else if (Level == 4)
623 {
624 dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
625 }
626 else if (Level == 5)
627 {
628 dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
629 }
630 else
631 {
632 // The caller supplied an invalid level.
633 dwErrorCode = ERROR_INVALID_LEVEL;
634 }
635
636 Cleanup:
637 SetLastError(dwErrorCode);
638 return (dwErrorCode == ERROR_SUCCESS);
639 }
640
641 BOOL WINAPI
642 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
643 {
644 BOOL bReturnValue;
645 DWORD cchComputerName;
646 DWORD cchFirstParameter;
647 DWORD dwErrorCode;
648 DWORD dwJobID;
649 HANDLE hExternalHandle;
650 PWSTR p = lpPrinterName;
651 PWSTR pwszFirstParameter = NULL;
652 PWSTR pwszSecondParameter = NULL;
653 PLOCAL_JOB pJob;
654 PLOCAL_HANDLE pHandle = NULL;
655 PLOCAL_PORT pPort;
656 PLOCAL_PORT_HANDLE pPortHandle = NULL;
657 PLOCAL_PRINT_MONITOR pPrintMonitor;
658 PLOCAL_PRINTER pPrinter;
659 PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
660 PLOCAL_XCV_HANDLE pXcvHandle = NULL;
661 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
662 WCHAR wszFullPath[MAX_PATH];
663
664 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
665 // Not sure yet if that is passed down to localspl.dll or processed in advance.
666
667 // Sanity checks
668 if (!lpPrinterName || !phPrinter)
669 {
670 dwErrorCode = ERROR_INVALID_PARAMETER;
671 goto Cleanup;
672 }
673
674 *phPrinter = NULL;
675
676 // Skip any server name in the first parameter.
677 // Does lpPrinterName begin with two backslashes to indicate a server name?
678 if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
679 {
680 // Skip these two backslashes.
681 lpPrinterName += 2;
682
683 // Look for the closing backslash.
684 p = wcschr(lpPrinterName, L'\\');
685 if (!p)
686 {
687 // We didn't get a proper server name.
688 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
689 goto Cleanup;
690 }
691
692 // Get the local computer name for comparison.
693 cchComputerName = _countof(wszComputerName);
694 if (!GetComputerNameW(wszComputerName, &cchComputerName))
695 {
696 dwErrorCode = GetLastError();
697 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
698 goto Cleanup;
699 }
700
701 // Now compare this string excerpt with the local computer name.
702 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
703 // This print provider only supports local printers, so both strings have to match.
704 if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0)
705 {
706 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
707 goto Cleanup;
708 }
709
710 // We have checked the server name and don't need it anymore.
711 lpPrinterName = p + 1;
712 }
713
714 // Look for a comma. If it exists, it indicates the end of the first parameter.
715 pwszSecondParameter = wcschr(lpPrinterName, L',');
716 if (pwszSecondParameter)
717 cchFirstParameter = pwszSecondParameter - p;
718 else
719 cchFirstParameter = wcslen(lpPrinterName);
720
721 // We must have at least one parameter.
722 if (!cchFirstParameter && !pwszSecondParameter)
723 {
724 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
725 goto Cleanup;
726 }
727
728 // Do we have a first parameter?
729 if (cchFirstParameter)
730 {
731 // Yes, extract it.
732 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer.
733 pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR));
734 CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR));
735 }
736
737 // Do we have a second parameter?
738 if (pwszSecondParameter)
739 {
740 // Yes, skip the comma at the beginning.
741 ++pwszSecondParameter;
742
743 // Skip whitespace as well.
744 while (*pwszSecondParameter == L' ')
745 ++pwszSecondParameter;
746 }
747
748 // Create a new handle.
749 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
750 if (!pHandle)
751 {
752 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
753 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
754 goto Cleanup;
755 }
756
757 // Now we can finally check the type of handle actually requested.
758 if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0)
759 {
760 // The caller wants a port handle and provided a string like:
761 // "LPT1:, Port"
762 // "\\COMPUTERNAME\LPT1:, Port"
763
764 // Look for this port in our Print Monitor Port list.
765 pPort = FindPort(pwszFirstParameter);
766 if (!pPort)
767 {
768 // The supplied port is unknown to all our Print Monitors.
769 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
770 goto Cleanup;
771 }
772
773 pPrintMonitor = pPort->pPrintMonitor;
774
775 // Call the monitor's OpenPort function.
776 if (pPrintMonitor->bIsLevel2)
777 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
778 else
779 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszFirstParameter, &hExternalHandle);
780
781 if (!bReturnValue)
782 {
783 // The OpenPort function failed. Return its last error.
784 dwErrorCode = GetLastError();
785 goto Cleanup;
786 }
787
788 // Create a new LOCAL_PORT_HANDLE.
789 pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE));
790 if (!pPortHandle)
791 {
792 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
793 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
794 goto Cleanup;
795 }
796
797 pPortHandle->hPort = hExternalHandle;
798 pPortHandle->pPort = pPort;
799
800 // Return the Port handle through our general handle.
801 pHandle->HandleType = HandleType_Port;
802 pHandle->pSpecificHandle = pPortHandle;
803 }
804 else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
805 {
806 // The caller wants an Xcv handle and provided a string like:
807 // ", XcvMonitor Local Port"
808 // "\\COMPUTERNAME\, XcvMonitor Local Port"
809 // ", XcvPort LPT1:"
810 // "\\COMPUTERNAME\, XcvPort LPT1:"
811
812 // Skip the "Xcv" string.
813 pwszSecondParameter += 3;
814
815 // Is XcvMonitor or XcvPort requested?
816 if (wcsncmp(pwszSecondParameter, L"Monitor ", 8) == 0)
817 {
818 // Skip the "Monitor " string.
819 pwszSecondParameter += 8;
820
821 // Look for this monitor in our Print Monitor list.
822 pPrintMonitor = FindPrintMonitor(pwszSecondParameter);
823 if (!pPrintMonitor)
824 {
825 // The caller supplied a non-existing Monitor name.
826 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
827 goto Cleanup;
828 }
829 }
830 else if (wcsncmp(pwszSecondParameter, L"Port ", 5) == 0)
831 {
832 // Skip the "Port " string.
833 pwszSecondParameter += 5;
834
835 // Look for this port in our Print Monitor Port list.
836 pPort = FindPort(pwszFirstParameter);
837 if (!pPort)
838 {
839 // The supplied port is unknown to all our Print Monitors.
840 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
841 goto Cleanup;
842 }
843
844 pPrintMonitor = pPort->pPrintMonitor;
845 }
846 else
847 {
848 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
849 goto Cleanup;
850 }
851
852 // Call the monitor's XcvOpenPort function.
853 if (pPrintMonitor->bIsLevel2)
854 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
855 else
856 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
857
858 if (!bReturnValue)
859 {
860 // The XcvOpenPort function failed. Return its last error.
861 dwErrorCode = GetLastError();
862 goto Cleanup;
863 }
864
865 // Create a new LOCAL_XCV_HANDLE.
866 pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE));
867 if (!pXcvHandle)
868 {
869 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
870 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
871 goto Cleanup;
872 }
873
874 pXcvHandle->hXcv = hExternalHandle;
875 pXcvHandle->pPrintMonitor = pPrintMonitor;
876
877 // Return the Xcv handle through our general handle.
878 pHandle->HandleType = HandleType_Xcv;
879 pHandle->pSpecificHandle = pXcvHandle;
880 }
881 else
882 {
883 // The caller wants a Printer or Printer Job handle and provided a string like:
884 // "HP DeskJet"
885 // "\\COMPUTERNAME\HP DeskJet"
886 // "HP DeskJet, Job 5"
887 // "\\COMPUTERNAME\HP DeskJet, Job 5"
888
889 // Retrieve the printer from the list.
890 pPrinter = LookupElementSkiplist(&PrinterList, &pwszFirstParameter, NULL);
891 if (!pPrinter)
892 {
893 // The printer does not exist.
894 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
895 goto Cleanup;
896 }
897
898 // Create a new LOCAL_PRINTER_HANDLE.
899 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
900 if (!pPrinterHandle)
901 {
902 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
903 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
904 goto Cleanup;
905 }
906
907 pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE;
908 pPrinterHandle->pPrinter = pPrinter;
909
910 // Check if a datatype was given.
911 if (pDefault && pDefault->pDatatype)
912 {
913 // Use the datatype if it's valid.
914 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
915 {
916 dwErrorCode = ERROR_INVALID_DATATYPE;
917 goto Cleanup;
918 }
919
920 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
921 }
922 else
923 {
924 // Use the default datatype.
925 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
926 }
927
928 // Check if a DevMode was given, otherwise use the default.
929 if (pDefault && pDefault->pDevMode)
930 pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
931 else
932 pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
933
934 // Check if the caller wants a handle to an existing Print Job.
935 if (pwszSecondParameter)
936 {
937 // The "Job " string has to follow now.
938 if (wcsncmp(pwszSecondParameter, L"Job ", 4) != 0)
939 {
940 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
941 goto Cleanup;
942 }
943
944 // Skip the "Job " string.
945 pwszSecondParameter += 4;
946
947 // Skip even more whitespace.
948 while (*pwszSecondParameter == ' ')
949 ++pwszSecondParameter;
950
951 // Finally extract the desired Job ID.
952 dwJobID = wcstoul(pwszSecondParameter, NULL, 10);
953 if (!IS_VALID_JOB_ID(dwJobID))
954 {
955 // The user supplied an invalid Job ID.
956 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
957 goto Cleanup;
958 }
959
960 // Look for this job in the Global Job List.
961 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
962 if (!pJob || pJob->pPrinter != pPrinter)
963 {
964 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
965 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
966 goto Cleanup;
967 }
968
969 // Try to open its SPL file.
970 GetJobFilePath(L"SPL", dwJobID, wszFullPath);
971 pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
972 if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
973 {
974 dwErrorCode = GetLastError();
975 ERR("CreateFileW failed with error %lu for \"%S\"!", dwErrorCode, wszFullPath);
976 goto Cleanup;
977 }
978
979 // Associate the job to our Printer Handle, but don't set bStartedDoc.
980 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it.
981 pPrinterHandle->pJob = pJob;
982 }
983
984 // Return the Printer handle through our general handle.
985 pHandle->HandleType = HandleType_Printer;
986 pHandle->pSpecificHandle = pPrinterHandle;
987 }
988
989 // We were successful! Return the handle.
990 *phPrinter = (HANDLE)pHandle;
991 dwErrorCode = ERROR_SUCCESS;
992
993 // Don't let the cleanup routines free this.
994 pHandle = NULL;
995 pPrinterHandle = NULL;
996
997 Cleanup:
998 if (pHandle)
999 DllFreeSplMem(pHandle);
1000
1001 if (pPrinterHandle)
1002 {
1003 if (pPrinterHandle->pwszDatatype)
1004 DllFreeSplStr(pPrinterHandle->pwszDatatype);
1005
1006 if (pPrinterHandle->pDevMode)
1007 DllFreeSplMem(pPrinterHandle->pDevMode);
1008
1009 DllFreeSplMem(pPrinterHandle);
1010 }
1011
1012 if (pwszFirstParameter)
1013 DllFreeSplMem(pwszFirstParameter);
1014
1015 SetLastError(dwErrorCode);
1016 return (dwErrorCode == ERROR_SUCCESS);
1017 }
1018
1019 BOOL WINAPI
1020 LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
1021 {
1022 BOOL bReturnValue;
1023 DWORD dwErrorCode;
1024 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1025 PLOCAL_PORT_HANDLE pPortHandle;
1026 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1027
1028 // Sanity checks.
1029 if (!pHandle)
1030 {
1031 dwErrorCode = ERROR_INVALID_HANDLE;
1032 goto Cleanup;
1033 }
1034
1035 // Port handles are an entirely different thing.
1036 if (pHandle->HandleType == HandleType_Port)
1037 {
1038 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1039
1040 // Call the monitor's ReadPort function.
1041 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1042 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
1043 else
1044 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead);
1045
1046 if (!bReturnValue)
1047 {
1048 // The ReadPort function failed. Return its last error.
1049 dwErrorCode = GetLastError();
1050 goto Cleanup;
1051 }
1052
1053 // We were successful!
1054 dwErrorCode = ERROR_SUCCESS;
1055 goto Cleanup;
1056 }
1057
1058 // The remaining function deals with Printer handles only.
1059 if (pHandle->HandleType != HandleType_Printer)
1060 {
1061 dwErrorCode = ERROR_INVALID_HANDLE;
1062 goto Cleanup;
1063 }
1064
1065 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1066
1067 // ReadPrinter needs an opened SPL file to work.
1068 // This only works if a Printer Job Handle was requested in OpenPrinter.
1069 if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE)
1070 {
1071 dwErrorCode = ERROR_INVALID_HANDLE;
1072 goto Cleanup;
1073 }
1074
1075 // Pass the parameters to ReadFile.
1076 if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL))
1077 {
1078 dwErrorCode = GetLastError();
1079 ERR("ReadFile failed with error %lu!\n", dwErrorCode);
1080 goto Cleanup;
1081 }
1082
1083 dwErrorCode = ERROR_SUCCESS;
1084
1085 Cleanup:
1086 SetLastError(dwErrorCode);
1087 return (dwErrorCode == ERROR_SUCCESS);
1088 }
1089
1090 DWORD WINAPI
1091 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
1092 {
1093 BOOL bReturnValue;
1094 DWORD dwErrorCode;
1095 DWORD dwReturnValue = 0;
1096 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
1097 PLOCAL_JOB pJob;
1098 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1099 PLOCAL_PORT_HANDLE pPortHandle;
1100 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1101
1102 // Sanity checks.
1103 if (!pHandle)
1104 {
1105 dwErrorCode = ERROR_INVALID_HANDLE;
1106 goto Cleanup;
1107 }
1108
1109 // Port handles are an entirely different thing.
1110 if (pHandle->HandleType == HandleType_Port)
1111 {
1112 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1113
1114 // 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.
1115 // Claim it exclusively for this port handle.
1116 pJob = pPortHandle->pPort->pNextJobToProcess;
1117 pPortHandle->pPort->pNextJobToProcess = NULL;
1118 ASSERT(pJob);
1119
1120 // Call the monitor's StartDocPort function.
1121 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1122 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1123 else
1124 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo);
1125
1126 if (!bReturnValue)
1127 {
1128 // The StartDocPort function failed. Return its last error.
1129 dwErrorCode = GetLastError();
1130 goto Cleanup;
1131 }
1132
1133 // We were successful!
1134 dwErrorCode = ERROR_SUCCESS;
1135 dwReturnValue = pJob->dwJobID;
1136 goto Cleanup;
1137 }
1138
1139 // The remaining function deals with Printer handles only.
1140 if (pHandle->HandleType != HandleType_Printer)
1141 {
1142 dwErrorCode = ERROR_INVALID_HANDLE;
1143 goto Cleanup;
1144 }
1145
1146 if (!pDocInfo1)
1147 {
1148 dwErrorCode = ERROR_INVALID_PARAMETER;
1149 goto Cleanup;
1150 }
1151
1152 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1153
1154 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail.
1155 if (pPrinterHandle->pJob)
1156 {
1157 dwErrorCode = ERROR_INVALID_PARAMETER;
1158 goto Cleanup;
1159 }
1160
1161 // Check the validity of the datatype if we got one.
1162 if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype))
1163 {
1164 dwErrorCode = ERROR_INVALID_DATATYPE;
1165 goto Cleanup;
1166 }
1167
1168 // Check if this is the right document information level.
1169 if (Level != 1)
1170 {
1171 dwErrorCode = ERROR_INVALID_LEVEL;
1172 goto Cleanup;
1173 }
1174
1175 // All requirements are met - create a new job.
1176 dwErrorCode = CreateJob(pPrinterHandle);
1177 if (dwErrorCode != ERROR_SUCCESS)
1178 goto Cleanup;
1179
1180 // Use any given datatype.
1181 if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype))
1182 {
1183 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1184 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1185 goto Cleanup;
1186 }
1187
1188 // Use any given document name.
1189 if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName))
1190 {
1191 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1192 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
1193 goto Cleanup;
1194 }
1195
1196 // We were successful!
1197 dwErrorCode = ERROR_SUCCESS;
1198 dwReturnValue = pPrinterHandle->pJob->dwJobID;
1199
1200 Cleanup:
1201 SetLastError(dwErrorCode);
1202 return dwReturnValue;
1203 }
1204
1205 BOOL WINAPI
1206 LocalStartPagePrinter(HANDLE hPrinter)
1207 {
1208 DWORD dwErrorCode;
1209 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1210 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1211
1212 // Sanity checks.
1213 if (!pHandle || pHandle->HandleType != HandleType_Printer)
1214 {
1215 dwErrorCode = ERROR_INVALID_HANDLE;
1216 goto Cleanup;
1217 }
1218
1219 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1220
1221 // We require StartDocPrinter or AddJob to be called first.
1222 if (!pPrinterHandle->bStartedDoc)
1223 {
1224 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1225 goto Cleanup;
1226 }
1227
1228 // Increase the page count.
1229 ++pPrinterHandle->pJob->dwTotalPages;
1230 dwErrorCode = ERROR_SUCCESS;
1231
1232 Cleanup:
1233 SetLastError(dwErrorCode);
1234 return (dwErrorCode == ERROR_SUCCESS);
1235 }
1236
1237 BOOL WINAPI
1238 LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
1239 {
1240 BOOL bReturnValue;
1241 DWORD dwErrorCode;
1242 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1243 PLOCAL_PORT_HANDLE pPortHandle;
1244 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1245
1246 // Sanity checks.
1247 if (!pHandle)
1248 {
1249 dwErrorCode = ERROR_INVALID_HANDLE;
1250 goto Cleanup;
1251 }
1252
1253 // Port handles are an entirely different thing.
1254 if (pHandle->HandleType == HandleType_Port)
1255 {
1256 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1257
1258 // Call the monitor's WritePort function.
1259 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1260 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1261 else
1262 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten);
1263
1264 if (!bReturnValue)
1265 {
1266 // The WritePort function failed. Return its last error.
1267 dwErrorCode = GetLastError();
1268 goto Cleanup;
1269 }
1270
1271 // We were successful!
1272 dwErrorCode = ERROR_SUCCESS;
1273 goto Cleanup;
1274 }
1275
1276 // The remaining function deals with Printer handles only.
1277 if (pHandle->HandleType != HandleType_Printer)
1278 {
1279 dwErrorCode = ERROR_INVALID_HANDLE;
1280 goto Cleanup;
1281 }
1282
1283 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1284
1285 // We require StartDocPrinter or AddJob to be called first.
1286 if (!pPrinterHandle->bStartedDoc)
1287 {
1288 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1289 goto Cleanup;
1290 }
1291
1292 // TODO: This function is only called when doing non-spooled printing.
1293 // 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).
1294 #if 0
1295 // Pass the parameters to WriteFile.
1296 if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL))
1297 {
1298 dwErrorCode = GetLastError();
1299 ERR("WriteFile failed with error %lu!\n", GetLastError());
1300 goto Cleanup;
1301 }
1302 #endif
1303
1304 dwErrorCode = ERROR_SUCCESS;
1305
1306 Cleanup:
1307 SetLastError(dwErrorCode);
1308 return (dwErrorCode == ERROR_SUCCESS);
1309 }
1310
1311 BOOL WINAPI
1312 LocalEndPagePrinter(HANDLE hPrinter)
1313 {
1314 DWORD dwErrorCode;
1315 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1316
1317 // Sanity checks.
1318 if (!pHandle || pHandle->HandleType != HandleType_Printer)
1319 {
1320 dwErrorCode = ERROR_INVALID_HANDLE;
1321 goto Cleanup;
1322 }
1323
1324 // This function doesn't do anything else for now.
1325 dwErrorCode = ERROR_SUCCESS;
1326
1327 Cleanup:
1328 SetLastError(dwErrorCode);
1329 return (dwErrorCode == ERROR_SUCCESS);
1330 }
1331
1332 BOOL WINAPI
1333 LocalEndDocPrinter(HANDLE hPrinter)
1334 {
1335 BOOL bReturnValue;
1336 DWORD dwErrorCode;
1337 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1338 PLOCAL_PORT_HANDLE pPortHandle;
1339 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1340
1341 // Sanity checks.
1342 if (!pHandle)
1343 {
1344 dwErrorCode = ERROR_INVALID_HANDLE;
1345 goto Cleanup;
1346 }
1347
1348 // Port handles are an entirely different thing.
1349 if (pHandle->HandleType == HandleType_Port)
1350 {
1351 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1352
1353 // Call the monitor's EndDocPort function.
1354 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1355 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort);
1356 else
1357 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort);
1358
1359 if (!bReturnValue)
1360 {
1361 // The EndDocPort function failed. Return its last error.
1362 dwErrorCode = GetLastError();
1363 goto Cleanup;
1364 }
1365
1366 // We were successful!
1367 dwErrorCode = ERROR_SUCCESS;
1368 goto Cleanup;
1369 }
1370
1371 // The remaining function deals with Printer handles only.
1372 if (pHandle->HandleType != HandleType_Printer)
1373 {
1374 dwErrorCode = ERROR_INVALID_HANDLE;
1375 goto Cleanup;
1376 }
1377
1378 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1379
1380 // We require StartDocPrinter or AddJob to be called first.
1381 if (!pPrinterHandle->bStartedDoc)
1382 {
1383 dwErrorCode = ERROR_SPL_NO_STARTDOC;
1384 goto Cleanup;
1385 }
1386
1387 // TODO: Something like ScheduleJob
1388
1389 // Finish the job.
1390 pPrinterHandle->bStartedDoc = FALSE;
1391 pPrinterHandle->pJob = NULL;
1392 dwErrorCode = ERROR_SUCCESS;
1393
1394 Cleanup:
1395 SetLastError(dwErrorCode);
1396 return (dwErrorCode == ERROR_SUCCESS);
1397 }
1398
1399 BOOL WINAPI
1400 LocalClosePrinter(HANDLE hPrinter)
1401 {
1402 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1403 PLOCAL_PORT_HANDLE pPortHandle;
1404 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1405 PLOCAL_XCV_HANDLE pXcvHandle;
1406
1407 if (!pHandle)
1408 {
1409 SetLastError(ERROR_INVALID_HANDLE);
1410 return FALSE;
1411 }
1412
1413 if (pHandle->HandleType == HandleType_Port)
1414 {
1415 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle;
1416
1417 // Call the monitor's ClosePort function.
1418 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2)
1419 ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort);
1420 else
1421 ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort);
1422 }
1423 else if (pHandle->HandleType == HandleType_Printer)
1424 {
1425 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1426
1427 // Terminate any started job.
1428 if (pPrinterHandle->pJob)
1429 FreeJob(pPrinterHandle->pJob);
1430
1431 // Free memory for the fields.
1432 DllFreeSplMem(pPrinterHandle->pDevMode);
1433 DllFreeSplStr(pPrinterHandle->pwszDatatype);
1434 }
1435 else if (pHandle->HandleType == HandleType_Xcv)
1436 {
1437 pXcvHandle = (PLOCAL_XCV_HANDLE)pHandle->pSpecificHandle;
1438
1439 // Call the monitor's XcvClosePort function.
1440 if (pXcvHandle->pPrintMonitor->bIsLevel2)
1441 ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv);
1442 else
1443 ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv);
1444 }
1445
1446 // Free memory for the handle and the specific handle.
1447 DllFreeSplMem(pHandle->pSpecificHandle);
1448 DllFreeSplMem(pHandle);
1449
1450 return TRUE;
1451 }