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