[LOCALSPL]
[reactos.git] / reactos / win32ss / printing / providers / localspl / printers.c
1 /*
2 * PROJECT: ReactOS Local Spooler
3 * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation
4 * PURPOSE: Functions related to Printers and printing
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
6 */
7
8 #include "precomp.h"
9
10 // Global Variables
11 SKIPLIST PrinterList;
12
13
14 /**
15 * @name _PrinterListCompareRoutine
16 *
17 * SKIPLIST_COMPARE_ROUTINE for the Printer List.
18 * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
19 */
20 static int WINAPI
21 _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
22 {
23 PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct;
24 PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct;
25
26 return wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
27 }
28
29 /**
30 * @name InitializePrinterList
31 *
32 * Initializes a list of locally available Printers.
33 * The list is searchable by name and returns information about the printers, including their job queues.
34 * During this process, the job queues are also initialized.
35 */
36 BOOL
37 InitializePrinterList()
38 {
39 const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
40
41 DWORD cbData;
42 DWORD cchPrinterName;
43 DWORD dwErrorCode;
44 DWORD dwSubKeys;
45 DWORD i;
46 HKEY hKey = NULL;
47 HKEY hSubKey = NULL;
48 PLOCAL_PRINTER pPrinter = NULL;
49 PLOCAL_PRINT_PROCESSOR pPrintProcessor;
50 PWSTR pwszPrintProcessor = NULL;
51 WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
52
53 // Initialize an empty list for our printers.
54 InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
55
56 // Open our printers registry key. Each subkey is a local printer there.
57 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
58 if (dwErrorCode != ERROR_SUCCESS)
59 {
60 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
61 goto Cleanup;
62 }
63
64 // Get the number of subkeys.
65 dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
66 if (dwErrorCode != ERROR_SUCCESS)
67 {
68 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
69 goto Cleanup;
70 }
71
72 // Loop through all available local printers.
73 for (i = 0; i < dwSubKeys; i++)
74 {
75 // Cleanup tasks from the previous run
76 if (hSubKey)
77 {
78 RegCloseKey(hSubKey);
79 hSubKey = NULL;
80 }
81
82 if (pPrinter)
83 {
84 if (pPrinter->pDefaultDevMode)
85 DllFreeSplMem(pPrinter->pDefaultDevMode);
86
87 if (pPrinter->pwszDefaultDatatype)
88 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
89
90 if (pPrinter->pwszDescription)
91 DllFreeSplStr(pPrinter->pwszDescription);
92
93 if (pPrinter->pwszPrinterDriver)
94 DllFreeSplStr(pPrinter->pwszPrinterDriver);
95
96 if (pPrinter->pwszPrinterName)
97 DllFreeSplStr(pPrinter->pwszPrinterName);
98
99 DllFreeSplMem(pPrinter);
100 pPrinter = NULL;
101 }
102
103 if (pwszPrintProcessor)
104 {
105 DllFreeSplStr(pwszPrintProcessor);
106 pwszPrintProcessor = NULL;
107 }
108
109 // Get the name of this printer.
110 cchPrinterName = _countof(wszPrinterName);
111 dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
112 if (dwErrorCode == ERROR_MORE_DATA)
113 {
114 // This printer name exceeds the maximum length and is invalid.
115 continue;
116 }
117 else if (dwErrorCode != ERROR_SUCCESS)
118 {
119 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
120 continue;
121 }
122
123 // Open this Printer's registry key.
124 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
125 if (dwErrorCode != ERROR_SUCCESS)
126 {
127 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode);
128 continue;
129 }
130
131 // Get the Print Processor.
132 pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor");
133 if (!pwszPrintProcessor)
134 continue;
135
136 // Try to find it in the Print Processor List.
137 pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
138 if (!pPrintProcessor)
139 {
140 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
141 continue;
142 }
143
144 // Create a new LOCAL_PRINTER structure for it.
145 pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
146 if (!pPrinter)
147 {
148 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
149 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
150 goto Cleanup;
151 }
152
153 pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
154 pPrinter->pPrintProcessor = pPrintProcessor;
155 InitializePrinterJobList(pPrinter);
156
157 // Get the printer driver.
158 pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
159 if (!pPrinter->pwszPrinterDriver)
160 continue;
161
162 // Get the description.
163 pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description");
164 if (!pPrinter->pwszDescription)
165 continue;
166
167 // Get the default datatype.
168 pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype");
169 if (!pPrinter->pwszDefaultDatatype)
170 continue;
171
172 // Verify that it's valid.
173 if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
174 {
175 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
176 continue;
177 }
178
179 // Determine the size of the DevMode.
180 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, NULL, &cbData);
181 if (dwErrorCode != ERROR_SUCCESS)
182 {
183 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
184 continue;
185 }
186
187 // Allocate enough memory for the DevMode.
188 pPrinter->pDefaultDevMode = DllAllocSplMem(cbData);
189 if (!pPrinter->pDefaultDevMode)
190 {
191 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
192 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
193 goto Cleanup;
194 }
195
196 // Get the default DevMode.
197 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData);
198 if (dwErrorCode != ERROR_SUCCESS)
199 {
200 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData);
201 continue;
202 }
203
204 // Get the Attributes.
205 cbData = sizeof(DWORD);
206 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData);
207 if (dwErrorCode != ERROR_SUCCESS)
208 {
209 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
210 continue;
211 }
212
213 // Get the Status.
214 cbData = sizeof(DWORD);
215 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
216 if (dwErrorCode != ERROR_SUCCESS)
217 {
218 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode);
219 continue;
220 }
221
222 // Add this printer to the printer list.
223 if (!InsertElementSkiplist(&PrinterList, pPrinter))
224 {
225 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
226 goto Cleanup;
227 }
228
229 // Don't let the cleanup routines free this.
230 pPrinter = NULL;
231 }
232
233 dwErrorCode = ERROR_SUCCESS;
234
235 Cleanup:
236 // Inside the loop
237 if (hSubKey)
238 RegCloseKey(hSubKey);
239
240 if (pPrinter)
241 {
242 if (pPrinter->pDefaultDevMode)
243 DllFreeSplMem(pPrinter->pDefaultDevMode);
244
245 if (pPrinter->pwszDefaultDatatype)
246 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
247
248 if (pPrinter->pwszDescription)
249 DllFreeSplStr(pPrinter->pwszDescription);
250
251 if (pPrinter->pwszPrinterDriver)
252 DllFreeSplStr(pPrinter->pwszPrinterDriver);
253
254 if (pPrinter->pwszPrinterName)
255 DllFreeSplStr(pPrinter->pwszPrinterName);
256
257 DllFreeSplMem(pPrinter);
258 }
259
260 if (pwszPrintProcessor)
261 DllFreeSplStr(pwszPrintProcessor);
262
263 // Outside the loop
264 if (hKey)
265 RegCloseKey(hKey);
266
267 SetLastError(dwErrorCode);
268 return (dwErrorCode == ERROR_SUCCESS);
269 }
270
271
272 DWORD
273 _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
274 {
275 const WCHAR wszComma[] = L",";
276
277 DWORD cbName;
278 DWORD cbComment;
279 DWORD cbDescription;
280 DWORD cchComputerName = 0;
281 DWORD dwErrorCode;
282 DWORD i;
283 PBYTE pPrinterInfo;
284 PBYTE pPrinterString;
285 PSKIPLIST_NODE pNode;
286 PLOCAL_PRINTER pPrinter;
287 PRINTER_INFO_1W PrinterInfo1;
288 WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1];
289
290 DWORD dwOffsets[] = {
291 FIELD_OFFSET(PRINTER_INFO_1W, pName),
292 FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
293 FIELD_OFFSET(PRINTER_INFO_1W, pComment),
294 MAXDWORD
295 };
296
297 if (Flags & PRINTER_ENUM_NAME)
298 {
299 if (Name)
300 {
301 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
302 // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
303 if (Name[0] == L'\\' && Name[1] == L'\\')
304 {
305 // Prepend slashes to the computer name.
306 wszComputerName[0] = L'\\';
307 wszComputerName[1] = L'\\';
308
309 // Get the local computer name for comparison.
310 cchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
311 if (!GetComputerNameW(&wszComputerName[2], &cchComputerName))
312 {
313 dwErrorCode = GetLastError();
314 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
315 goto Cleanup;
316 }
317
318 // Add the leading slashes to the total length.
319 cchComputerName += 2;
320
321 // Now compare this with the local computer name and reject if it doesn't match.
322 if (wcsicmp(&Name[2], &wszComputerName[2]) != 0)
323 {
324 dwErrorCode = ERROR_INVALID_NAME;
325 goto Cleanup;
326 }
327
328 // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
329 wszComputerName[cchComputerName++] = L'\\';
330 wszComputerName[cchComputerName] = 0;
331 }
332 else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
333 {
334 // The user supplied a name that cannot be processed by the local print provider.
335 dwErrorCode = ERROR_INVALID_NAME;
336 goto Cleanup;
337 }
338 }
339 else
340 {
341 // The caller wants information about this Print Provider.
342 // spoolss packs this into an array of information about all Print Providers.
343 *pcbNeeded = sizeof(PRINTER_INFO_1W);
344
345 for (i = 0; i < 3; i++)
346 *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
347
348 // Check if the supplied buffer is large enough.
349 if (cbBuf < *pcbNeeded)
350 {
351 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
352 goto Cleanup;
353 }
354
355 // Copy over the print processor information.
356 ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
357 PackStrings(wszPrintProviderInfo, pPrinterEnum, dwOffsets, &pPrinterEnum[*pcbNeeded]);
358 *pcReturned = 1;
359 dwErrorCode = ERROR_SUCCESS;
360 goto Cleanup;
361 }
362 }
363
364 // Count the required buffer size and the number of printers.
365 i = 0;
366
367 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
368 {
369 pPrinter = (PLOCAL_PRINTER)pNode->Element;
370
371 // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
372 // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
373 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
374 cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
375 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
376 cbDescription = cchComputerName * sizeof(WCHAR) + cbName + cbComment + sizeof(WCHAR);
377
378 *pcbNeeded += sizeof(PRINTER_INFO_1W) + cchComputerName * sizeof(WCHAR) + cbName + cbComment + cbDescription;
379 i++;
380 }
381
382 // Check if the supplied buffer is large enough.
383 if (cbBuf < *pcbNeeded)
384 {
385 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
386 goto Cleanup;
387 }
388
389 // Put the strings right after the last PRINTER_INFO_1W structure.
390 // Due to all the required string processing, we can't just use PackStrings here :(
391 pPrinterInfo = pPrinterEnum;
392 pPrinterString = pPrinterEnum + i * sizeof(PRINTER_INFO_1W);
393
394 // Copy over the printer information.
395 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
396 {
397 pPrinter = (PLOCAL_PRINTER)pNode->Element;
398
399 // FIXME: As for now, the Flags member returns no information.
400 PrinterInfo1.Flags = 0;
401
402 // Copy the printer name.
403 PrinterInfo1.pName = (PWSTR)pPrinterString;
404 CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
405 pPrinterString += cchComputerName * sizeof(WCHAR);
406 cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
407 CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName);
408 pPrinterString += cbName;
409
410 // Copy the printer comment (equals the "Description" registry value).
411 PrinterInfo1.pComment = (PWSTR)pPrinterString;
412 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
413 CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment);
414 pPrinterString += cbComment;
415
416 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
417 PrinterInfo1.pDescription = (PWSTR)pPrinterString;
418 CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
419 pPrinterString += cchComputerName * sizeof(WCHAR);
420 CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName - sizeof(WCHAR));
421 pPrinterString += cbName - sizeof(WCHAR);
422 CopyMemory(pPrinterString, wszComma, sizeof(WCHAR));
423 pPrinterString += sizeof(WCHAR);
424 CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment - sizeof(WCHAR));
425 pPrinterString += cbComment - sizeof(WCHAR);
426 CopyMemory(pPrinterString, wszComma, sizeof(wszComma));
427 pPrinterString += sizeof(wszComma);
428
429 // Finally copy the structure and advance to the next one in the output buffer.
430 CopyMemory(pPrinterInfo, &PrinterInfo1, sizeof(PRINTER_INFO_1W));
431 pPrinterInfo += sizeof(PRINTER_INFO_1W);
432 }
433
434 *pcReturned = i;
435 dwErrorCode = ERROR_SUCCESS;
436
437 Cleanup:
438 return dwErrorCode;
439 }
440
441 BOOL WINAPI
442 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
443 {
444 DWORD dwErrorCode;
445
446 // Do no sanity checks here. This is verified by localspl_apitest!
447
448 // Begin counting.
449 *pcbNeeded = 0;
450 *pcReturned = 0;
451
452 // Think positive :)
453 // Treat it as success if the caller queried no information and we don't need to return any.
454 dwErrorCode = ERROR_SUCCESS;
455
456 if (Flags & PRINTER_ENUM_LOCAL)
457 {
458 // The function behaves quite differently for each level.
459 if (Level == 1)
460 {
461 dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
462 }
463 else
464 {
465 // TODO: Handle other levels.
466 // The caller supplied an invalid level.
467 dwErrorCode = ERROR_INVALID_LEVEL;
468 goto Cleanup;
469 }
470 }
471
472 Cleanup:
473 SetLastError(dwErrorCode);
474 return (dwErrorCode == ERROR_SUCCESS);
475 }
476
477 BOOL WINAPI
478 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
479 {
480 BOOL bReturnValue;
481 DWORD cchComputerName;
482 DWORD cchFirstParameter;
483 DWORD dwErrorCode;
484 DWORD dwJobID;
485 HANDLE hExternalHandle;
486 PWSTR p = lpPrinterName;
487 PWSTR pwszFirstParameter = NULL;
488 PWSTR pwszSecondParameter = NULL;
489 PLOCAL_JOB pJob;
490 PLOCAL_HANDLE pHandle = NULL;
491 PLOCAL_PRINT_MONITOR pPrintMonitor;
492 PLOCAL_PRINTER pPrinter;
493 PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
494 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
495
496 // TODO: lpPrinterName == NULL is supported and means access to the local printer server.
497 // Not sure yet if that is passed down to localspl.dll or processed in advance.
498
499 // Sanity checks
500 if (!lpPrinterName || !phPrinter)
501 {
502 dwErrorCode = ERROR_INVALID_PARAMETER;
503 goto Cleanup;
504 }
505
506 // Skip any server name in the first parameter.
507 // Does lpPrinterName begin with two backslashes to indicate a server name?
508 if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
509 {
510 // Skip these two backslashes.
511 lpPrinterName += 2;
512
513 // Look for the closing backslash.
514 p = wcschr(lpPrinterName, L'\\');
515 if (!p)
516 {
517 // We didn't get a proper server name.
518 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
519 goto Cleanup;
520 }
521
522 // Get the local computer name for comparison.
523 cchComputerName = _countof(wszComputerName);
524 if (!GetComputerNameW(wszComputerName, &cchComputerName))
525 {
526 dwErrorCode = GetLastError();
527 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
528 goto Cleanup;
529 }
530
531 // Now compare this string excerpt with the local computer name.
532 // The input parameter may not be writable, so we can't null-terminate the input string at this point.
533 // This print provider only supports local printers, so both strings have to match.
534 if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0)
535 {
536 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
537 goto Cleanup;
538 }
539
540 // We have checked the server name and don't need it anymore.
541 lpPrinterName = p + 1;
542 }
543
544 // Look for a comma. If it exists, it indicates the end of the first parameter.
545 pwszSecondParameter = wcschr(lpPrinterName, L',');
546 if (pwszSecondParameter)
547 cchFirstParameter = pwszSecondParameter - p;
548 else
549 cchFirstParameter = wcslen(lpPrinterName);
550
551 // We must have at least one parameter.
552 if (!cchFirstParameter && !pwszSecondParameter)
553 {
554 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
555 goto Cleanup;
556 }
557
558 // Do we have a first parameter?
559 if (cchFirstParameter)
560 {
561 // Yes, extract it.
562 pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR));
563 CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR));
564 pwszFirstParameter[cchFirstParameter] = 0;
565 }
566
567 // Do we have a second parameter?
568 if (pwszSecondParameter)
569 {
570 // Yes, skip the comma at the beginning.
571 ++pwszSecondParameter;
572
573 // Skip whitespace as well.
574 while (*pwszSecondParameter == L' ')
575 ++pwszSecondParameter;
576 }
577
578 // Create a new handle.
579 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
580 if (!pHandle)
581 {
582 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
583 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
584 goto Cleanup;
585 }
586
587 // Now we can finally check the type of handle actually requested.
588 if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0)
589 {
590 // The caller wants a port handle and provided a string like:
591 // "LPT1:, Port"
592 // "\\COMPUTERNAME\LPT1:, Port"
593
594 // Look for this port in our Print Monitor Port list.
595 pPrintMonitor = FindPrintMonitorByPort(pwszFirstParameter);
596 if (!pPrintMonitor)
597 {
598 // The supplied port is unknown to all our Print Monitors.
599 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
600 goto Cleanup;
601 }
602
603 // Call the monitor's OpenPort function.
604 if (pPrintMonitor->bIsLevel2)
605 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszFirstParameter, &hExternalHandle);
606 else
607 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszFirstParameter, &hExternalHandle);
608
609 if (!bReturnValue)
610 {
611 // The OpenPort function failed. Return its last error.
612 dwErrorCode = GetLastError();
613 goto Cleanup;
614 }
615
616 // Return the Port handle through our general handle.
617 pHandle->HandleType = HandleType_Port;
618 pHandle->pSpecificHandle = hExternalHandle;
619 }
620 else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0)
621 {
622 // The caller wants an Xcv handle and provided a string like:
623 // ", XcvMonitor Local Port"
624 // "\\COMPUTERNAME\, XcvMonitor Local Port"
625 // ", XcvPort LPT1:"
626 // "\\COMPUTERNAME\, XcvPort LPT1:"
627
628 // Skip the "Xcv" string.
629 pwszSecondParameter += 3;
630
631 // Is XcvMonitor or XcvPort requested?
632 if (wcsncmp(pwszSecondParameter, L"Monitor ", 8) == 0)
633 {
634 // Skip the "Monitor " string.
635 pwszSecondParameter += 8;
636
637 // Look for this monitor in our Print Monitor list.
638 pPrintMonitor = FindPrintMonitor(pwszSecondParameter);
639 if (!pPrintMonitor)
640 {
641 // The caller supplied a non-existing Monitor name.
642 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
643 goto Cleanup;
644 }
645 }
646 else if (wcsncmp(pwszSecondParameter, L"Port ", 5) == 0)
647 {
648 // Skip the "Port " string.
649 pwszSecondParameter += 5;
650
651 // Look for this port in our Print Monitor Port list.
652 pPrintMonitor = FindPrintMonitorByPort(pwszSecondParameter);
653 if (!pPrintMonitor)
654 {
655 // The supplied port is unknown to all our Print Monitors.
656 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
657 goto Cleanup;
658 }
659 }
660 else
661 {
662 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
663 goto Cleanup;
664 }
665
666 // Call the monitor's XcvOpenPort function.
667 if (pPrintMonitor->bIsLevel2)
668 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
669 else
670 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszSecondParameter, SERVER_EXECUTE, &hExternalHandle);
671
672 if (!bReturnValue)
673 {
674 // The XcvOpenPort function failed. Return its last error.
675 dwErrorCode = GetLastError();
676 goto Cleanup;
677 }
678
679 // Return the Xcv handle through our general handle.
680 pHandle->HandleType = HandleType_Xcv;
681 pHandle->pSpecificHandle = hExternalHandle;
682 }
683 else
684 {
685 // The caller wants a Printer or Printer Job handle and provided a string like:
686 // "HP DeskJet"
687 // "\\COMPUTERNAME\HP DeskJet"
688 // "HP DeskJet, Job 5"
689 // "\\COMPUTERNAME\HP DeskJet, Job 5"
690
691 // Retrieve the printer from the list.
692 pPrinter = LookupElementSkiplist(&PrinterList, &pwszFirstParameter, NULL);
693 if (!pPrinter)
694 {
695 // The printer does not exist.
696 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
697 goto Cleanup;
698 }
699
700 // Create a new printer handle.
701 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
702 if (!pPrinterHandle)
703 {
704 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
705 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
706 goto Cleanup;
707 }
708
709 pPrinterHandle->pPrinter = pPrinter;
710
711 // Check if a datatype was given.
712 if (pDefault && pDefault->pDatatype)
713 {
714 // Use the datatype if it's valid.
715 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
716 {
717 dwErrorCode = ERROR_INVALID_DATATYPE;
718 goto Cleanup;
719 }
720
721 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
722 }
723 else
724 {
725 // Use the default datatype.
726 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
727 }
728
729 // Check if a DevMode was given, otherwise use the default.
730 if (pDefault && pDefault->pDevMode)
731 pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
732 else
733 pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
734
735 // Check if the caller wants a handle to an existing job.
736 if (pwszSecondParameter)
737 {
738 // The "Job " string has to follow now.
739 if (wcsncmp(pwszSecondParameter, L"Job ", 4) != 0)
740 {
741 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
742 goto Cleanup;
743 }
744
745 // Skip the "Job " string.
746 pwszSecondParameter += 4;
747
748 // Skip even more whitespace.
749 while (*pwszSecondParameter == ' ')
750 ++pwszSecondParameter;
751
752 // Finally extract the desired Job ID.
753 dwJobID = wcstoul(pwszSecondParameter, NULL, 10);
754 if (!IS_VALID_JOB_ID(dwJobID))
755 {
756 // The user supplied an invalid Job ID.
757 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
758 goto Cleanup;
759 }
760
761 // Look for this job in the Global Job List.
762 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
763 if (!pJob || pJob->pPrinter != pPrinter)
764 {
765 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
766 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
767 goto Cleanup;
768 }
769
770 pPrinterHandle->pStartedJob = pJob;
771 }
772
773 // Return the Printer handle through our general handle.
774 pHandle->HandleType = HandleType_Printer;
775 pHandle->pSpecificHandle = pPrinterHandle;
776 }
777
778 // We were successful! Return the handle.
779 *phPrinter = (HANDLE)pHandle;
780 dwErrorCode = ERROR_SUCCESS;
781
782 // Don't let the cleanup routines free this.
783 pHandle = NULL;
784 pPrinterHandle = NULL;
785
786 Cleanup:
787 if (pHandle)
788 DllFreeSplMem(pHandle);
789
790 if (pPrinterHandle)
791 {
792 if (pPrinterHandle->pwszDatatype)
793 DllFreeSplStr(pPrinterHandle->pwszDatatype);
794
795 if (pPrinterHandle->pDevMode)
796 DllFreeSplMem(pPrinterHandle->pDevMode);
797
798 DllFreeSplMem(pPrinterHandle);
799 }
800
801 if (pwszFirstParameter)
802 DllFreeSplMem(pwszFirstParameter);
803
804 SetLastError(dwErrorCode);
805 return (dwErrorCode == ERROR_SUCCESS);
806 }
807
808 DWORD WINAPI
809 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
810 {
811 DWORD dwErrorCode;
812 DWORD dwReturnValue = 0;
813 PDOC_INFO_1W pDocumentInfo1;
814 PLOCAL_HANDLE pHandle;
815 PLOCAL_JOB pJob;
816 PLOCAL_PRINTER_HANDLE pPrinterHandle;
817
818 // Sanity checks
819 if (!pDocInfo)
820 {
821 dwErrorCode = ERROR_INVALID_PARAMETER;
822 goto Cleanup;
823 }
824
825 if (!hPrinter)
826 {
827 dwErrorCode = ERROR_INVALID_HANDLE;
828 goto Cleanup;
829 }
830
831 // Check if this is a printer handle.
832 pHandle = (PLOCAL_HANDLE)hPrinter;
833 if (pHandle->HandleType != HandleType_Printer)
834 {
835 dwErrorCode = ERROR_INVALID_HANDLE;
836 goto Cleanup;
837 }
838
839 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
840
841 // Check if this is the right document information level.
842 if (Level != 1)
843 {
844 dwErrorCode = ERROR_INVALID_LEVEL;
845 goto Cleanup;
846 }
847
848 pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo;
849
850 // Create a new job.
851 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
852 pJob->pPrinter = pPrinterHandle->pPrinter;
853 pJob->dwPriority = DEF_PRIORITY;
854
855 // Check if a datatype was given.
856 if (pDocumentInfo1->pDatatype)
857 {
858 // Use the datatype if it's valid.
859 if (!FindDatatype(pJob->pPrintProcessor, pDocumentInfo1->pDatatype))
860 {
861 dwErrorCode = ERROR_INVALID_DATATYPE;
862 goto Cleanup;
863 }
864
865 pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
866 }
867 else
868 {
869 // Use the printer handle datatype.
870 pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
871 }
872
873 // Copy over printer defaults.
874 pJob->pDevMode = DuplicateDevMode(pPrinterHandle->pDevMode);
875
876 // Copy over supplied information.
877 if (pDocumentInfo1->pDocName)
878 pJob->pwszDocumentName = AllocSplStr(pDocumentInfo1->pDocName);
879
880 if (pDocumentInfo1->pOutputFile)
881 pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
882
883 // Get an ID for the new job.
884 if (!GetNextJobID(&pJob->dwJobID))
885 {
886 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
887 goto Cleanup;
888 }
889
890 // Add the job to the Global Job List.
891 if (!InsertElementSkiplist(&GlobalJobList, pJob))
892 {
893 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
894 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
895 goto Cleanup;
896 }
897
898 // Add the job at the end of the Printer's Job List.
899 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
900 if (!InsertTailElementSkiplist(&pJob->pPrinter->JobList, pJob))
901 {
902 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
903 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
904 goto Cleanup;
905 }
906
907 pPrinterHandle->pStartedJob = pJob;
908 dwErrorCode = ERROR_SUCCESS;
909 dwReturnValue = pJob->dwJobID;
910
911 Cleanup:
912 SetLastError(dwErrorCode);
913 return dwReturnValue;
914 }
915
916 BOOL WINAPI
917 LocalStartPagePrinter(HANDLE hPrinter)
918 {
919 ///////////// TODO /////////////////////
920 return FALSE;
921 }
922
923 BOOL WINAPI
924 LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
925 {
926 ///////////// TODO /////////////////////
927 return FALSE;
928 }
929
930 BOOL WINAPI
931 LocalEndPagePrinter(HANDLE hPrinter)
932 {
933 ///////////// TODO /////////////////////
934 return FALSE;
935 }
936
937 BOOL WINAPI
938 LocalEndDocPrinter(HANDLE hPrinter)
939 {
940 ///////////// TODO /////////////////////
941 return FALSE;
942 }
943
944 BOOL WINAPI
945 LocalClosePrinter(HANDLE hPrinter)
946 {
947 PLOCAL_HANDLE pHandle;
948
949 if (!hPrinter)
950 {
951 SetLastError(ERROR_INVALID_HANDLE);
952 return FALSE;
953 }
954
955 pHandle = (PLOCAL_HANDLE)hPrinter;
956
957 ///////////// TODO /////////////////////
958 /// Check the handle type, do thoroughful checks on all data fields and clean them.
959 ////////////////////////////////////////
960
961 DllFreeSplMem(pHandle);
962
963 return TRUE;
964 }