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