c7e89c5b9350f757245aa7a01d0e8fc02d7f9792
[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 void
37 InitializePrinterList()
38 {
39 const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
40
41 DWORD cbData;
42 DWORD cchPrinterName;
43 DWORD dwSubKeys;
44 DWORD i;
45 HKEY hKey = NULL;
46 HKEY hSubKey = NULL;
47 LONG lStatus;
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 lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
58 if (lStatus != ERROR_SUCCESS)
59 {
60 ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
61 goto Cleanup;
62 }
63
64 // Get the number of subkeys.
65 lStatus = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
66 if (lStatus != ERROR_SUCCESS)
67 {
68 ERR("RegQueryInfoKeyW failed with status %ld!\n", lStatus);
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->pwszDefaultDatatype)
85 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
86
87 if (pPrinter->pwszDescription)
88 DllFreeSplStr(pPrinter->pwszDescription);
89
90 if (pPrinter->pwszPrinterDriver)
91 DllFreeSplStr(pPrinter->pwszPrinterDriver);
92
93 if (pPrinter->pwszPrinterName)
94 DllFreeSplStr(pPrinter->pwszPrinterName);
95
96 DllFreeSplMem(pPrinter);
97 pPrinter = NULL;
98 }
99
100 if (pwszPrintProcessor)
101 {
102 DllFreeSplStr(pwszPrintProcessor);
103 pwszPrintProcessor = NULL;
104 }
105
106 // Get the name of this printer.
107 cchPrinterName = _countof(wszPrinterName);
108 lStatus = RegEnumKeyExW(hKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL);
109 if (lStatus == ERROR_MORE_DATA)
110 {
111 // This printer name exceeds the maximum length and is invalid.
112 continue;
113 }
114 else if (lStatus != ERROR_SUCCESS)
115 {
116 ERR("RegEnumKeyExW failed for iteration %lu with status %ld!\n", i, lStatus);
117 continue;
118 }
119
120 // Open this Printer's registry key.
121 lStatus = RegOpenKeyExW(hKey, wszPrinterName, 0, KEY_READ, &hSubKey);
122 if (lStatus != ERROR_SUCCESS)
123 {
124 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %ld!\n", wszPrinterName, lStatus);
125 continue;
126 }
127
128 // Get the Print Processor.
129 pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor");
130 if (!pwszPrintProcessor)
131 continue;
132
133 // Try to find it in the Print Processor List.
134 pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
135 if (!pPrintProcessor)
136 {
137 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
138 continue;
139 }
140
141 // Create a new LOCAL_PRINTER structure for it.
142 pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER));
143 if (!pPrinter)
144 {
145 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
146 goto Cleanup;
147 }
148
149 pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
150 pPrinter->pPrintProcessor = pPrintProcessor;
151 InitializePrinterJobList(pPrinter);
152
153 // Get the printer driver.
154 pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
155 if (!pPrinter->pwszPrinterDriver)
156 continue;
157
158 // Get the description.
159 pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description");
160 if (!pPrinter->pwszDescription)
161 continue;
162
163 // Get the default datatype.
164 pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype");
165 if (!pPrinter->pwszDefaultDatatype)
166 continue;
167
168 // Verify that it's valid.
169 if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
170 {
171 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
172 continue;
173 }
174
175 // Get the default DevMode.
176 cbData = sizeof(DEVMODEW);
177 lStatus = RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)&pPrinter->DefaultDevMode, &cbData);
178 if (lStatus != ERROR_SUCCESS || cbData != sizeof(DEVMODEW))
179 {
180 ERR("Couldn't query a valid DevMode for Printer \"%S\", status is %ld, cbData is %lu!\n", wszPrinterName, lStatus, cbData);
181 continue;
182 }
183
184 // Get the Attributes.
185 cbData = sizeof(DWORD);
186 lStatus = RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData);
187 if (lStatus != ERROR_SUCCESS)
188 {
189 ERR("Couldn't query Attributes for Printer \"%S\", status is %ld!\n", wszPrinterName, lStatus);
190 continue;
191 }
192
193 // Get the Status.
194 cbData = sizeof(DWORD);
195 lStatus = RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData);
196 if (lStatus != ERROR_SUCCESS)
197 {
198 ERR("Couldn't query Status for Printer \"%S\", status is %ld!\n", wszPrinterName, lStatus);
199 continue;
200 }
201
202 // Add this printer to the printer list.
203 if (!InsertElementSkiplist(&PrinterList, pPrinter))
204 {
205 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
206 goto Cleanup;
207 }
208
209 // Don't let the cleanup routines free this.
210 pPrinter = NULL;
211 }
212
213 Cleanup:
214 // Inside the loop
215 if (hSubKey)
216 RegCloseKey(hSubKey);
217
218 if (pPrinter)
219 {
220 if (pPrinter->pwszDefaultDatatype)
221 DllFreeSplStr(pPrinter->pwszDefaultDatatype);
222
223 if (pPrinter->pwszDescription)
224 DllFreeSplStr(pPrinter->pwszDescription);
225
226 if (pPrinter->pwszPrinterDriver)
227 DllFreeSplStr(pPrinter->pwszPrinterDriver);
228
229 if (pPrinter->pwszPrinterName)
230 DllFreeSplStr(pPrinter->pwszPrinterName);
231
232 DllFreeSplMem(pPrinter);
233 }
234
235 if (pwszPrintProcessor)
236 DllFreeSplStr(pwszPrintProcessor);
237
238 // Outside the loop
239 if (hKey)
240 RegCloseKey(hKey);
241 }
242
243
244 DWORD
245 _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
246 {
247 const WCHAR wszComma[] = L",";
248
249 DWORD cbName;
250 DWORD cbComment;
251 DWORD cbDescription;
252 DWORD cchComputerName = 0;
253 DWORD dwErrorCode;
254 DWORD i;
255 PBYTE pPrinterInfo;
256 PBYTE pPrinterString;
257 PSKIPLIST_NODE pNode;
258 PLOCAL_PRINTER pPrinter;
259 PRINTER_INFO_1W PrinterInfo1;
260 WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1];
261
262 DWORD dwOffsets[] = {
263 FIELD_OFFSET(PRINTER_INFO_1W, pName),
264 FIELD_OFFSET(PRINTER_INFO_1W, pDescription),
265 FIELD_OFFSET(PRINTER_INFO_1W, pComment),
266 MAXDWORD
267 };
268
269 if (Flags & PRINTER_ENUM_NAME)
270 {
271 if (Name)
272 {
273 // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
274 // Only process what's directed at us and dismiss every other request with ERROR_INVALID_NAME.
275 if (Name[0] == L'\\' && Name[1] == L'\\')
276 {
277 // Prepend slashes to the computer name.
278 wszComputerName[0] = L'\\';
279 wszComputerName[1] = L'\\';
280
281 // Get the local computer name for comparison.
282 cchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
283 if (!GetComputerNameW(&wszComputerName[2], &cchComputerName))
284 {
285 dwErrorCode = GetLastError();
286 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
287 goto Cleanup;
288 }
289
290 // Add the leading slashes to the total length.
291 cchComputerName += 2;
292
293 // Now compare this with the local computer name and reject if it doesn't match.
294 if (wcsicmp(&Name[2], &wszComputerName[2]) != 0)
295 {
296 dwErrorCode = ERROR_INVALID_NAME;
297 goto Cleanup;
298 }
299
300 // Add a trailing backslash to wszComputerName, which will later be prepended in front of the printer names.
301 wszComputerName[cchComputerName++] = L'\\';
302 wszComputerName[cchComputerName] = 0;
303 }
304 else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
305 {
306 // The user supplied a name that cannot be processed by the local print provider.
307 dwErrorCode = ERROR_INVALID_NAME;
308 goto Cleanup;
309 }
310 }
311 else
312 {
313 // The caller wants information about this Print Provider.
314 // spoolss packs this into an array of information about all Print Providers.
315 *pcbNeeded = sizeof(PRINTER_INFO_1W);
316
317 for (i = 0; i < 3; i++)
318 *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR);
319
320 // Check if the supplied buffer is large enough.
321 if (cbBuf < *pcbNeeded)
322 {
323 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
324 goto Cleanup;
325 }
326
327 // Copy over the print processor information.
328 ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
329 PackStrings(wszPrintProviderInfo, pPrinterEnum, dwOffsets, &pPrinterEnum[*pcbNeeded]);
330 *pcReturned = 1;
331 dwErrorCode = ERROR_SUCCESS;
332 goto Cleanup;
333 }
334 }
335
336 // Count the required buffer size and the number of printers.
337 i = 0;
338
339 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
340 {
341 pPrinter = (PLOCAL_PRINTER)pNode->Element;
342
343 // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
344 // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
345 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
346 cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
347 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
348 cbDescription = cchComputerName * sizeof(WCHAR) + cbName + cbComment + sizeof(WCHAR);
349
350 *pcbNeeded += sizeof(PRINTER_INFO_1W) + cchComputerName * sizeof(WCHAR) + cbName + cbComment + cbDescription;
351 i++;
352 }
353
354 // Check if the supplied buffer is large enough.
355 if (cbBuf < *pcbNeeded)
356 {
357 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
358 goto Cleanup;
359 }
360
361 // Put the strings right after the last PRINTER_INFO_1W structure.
362 // Due to all the required string processing, we can't just use PackStrings here :(
363 pPrinterInfo = pPrinterEnum;
364 pPrinterString = pPrinterEnum + i * sizeof(PRINTER_INFO_1W);
365
366 // Copy over the printer information.
367 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
368 {
369 pPrinter = (PLOCAL_PRINTER)pNode->Element;
370
371 // FIXME: As for now, the Flags member returns no information.
372 PrinterInfo1.Flags = 0;
373
374 // Copy the printer name.
375 PrinterInfo1.pName = (PWSTR)pPrinterString;
376 CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
377 pPrinterString += cchComputerName * sizeof(WCHAR);
378 cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
379 CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName);
380 pPrinterString += cbName;
381
382 // Copy the printer comment (equals the "Description" registry value).
383 PrinterInfo1.pComment = (PWSTR)pPrinterString;
384 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
385 CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment);
386 pPrinterString += cbComment;
387
388 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
389 PrinterInfo1.pDescription = (PWSTR)pPrinterString;
390 CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
391 pPrinterString += cchComputerName * sizeof(WCHAR);
392 CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName - sizeof(WCHAR));
393 pPrinterString += cbName - sizeof(WCHAR);
394 CopyMemory(pPrinterString, wszComma, sizeof(WCHAR));
395 pPrinterString += sizeof(WCHAR);
396 CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment - sizeof(WCHAR));
397 pPrinterString += cbComment - sizeof(WCHAR);
398 CopyMemory(pPrinterString, wszComma, sizeof(wszComma));
399 pPrinterString += sizeof(wszComma);
400
401 // Finally copy the structure and advance to the next one in the output buffer.
402 CopyMemory(pPrinterInfo, &PrinterInfo1, sizeof(PRINTER_INFO_1W));
403 pPrinterInfo += sizeof(PRINTER_INFO_1W);
404 }
405
406 *pcReturned = i;
407 dwErrorCode = ERROR_SUCCESS;
408
409 Cleanup:
410 return dwErrorCode;
411 }
412
413 BOOL WINAPI
414 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
415 {
416 DWORD dwErrorCode;
417
418 // Do no sanity checks here. This is verified by localspl_apitest!
419
420 // Begin counting.
421 *pcbNeeded = 0;
422 *pcReturned = 0;
423
424 // Think positive :)
425 // Treat it as success if the caller queried no information and we don't need to return any.
426 dwErrorCode = ERROR_SUCCESS;
427
428 if (Flags & PRINTER_ENUM_LOCAL)
429 {
430 // The function behaves quite differently for each level.
431 if (Level == 1)
432 {
433 dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
434 }
435 else
436 {
437 // TODO: Handle other levels.
438 // The caller supplied an invalid level.
439 dwErrorCode = ERROR_INVALID_LEVEL;
440 goto Cleanup;
441 }
442 }
443
444 Cleanup:
445 SetLastError(dwErrorCode);
446 return (dwErrorCode == ERROR_SUCCESS);
447 }
448
449 BOOL WINAPI
450 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
451 {
452 DWORD cchComputerName;
453 DWORD cchPrinterName;
454 DWORD dwErrorCode;
455 DWORD dwJobID;
456 PWSTR p = lpPrinterName;
457 PWSTR pwszPrinterName = NULL;
458 PLOCAL_JOB pJob;
459 PLOCAL_HANDLE pHandle;
460 PLOCAL_PRINTER pPrinter;
461 PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
462 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
463
464 // Sanity checks
465 if (!lpPrinterName || !phPrinter)
466 {
467 dwErrorCode = ERROR_INVALID_PARAMETER;
468 goto Cleanup;
469 }
470
471 // Does lpPrinterName begin with two backslashes to indicate a server name?
472 if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
473 {
474 // Skip these two backslashes.
475 lpPrinterName += 2;
476
477 // Look for the closing backslash.
478 p = wcschr(lpPrinterName, L'\\');
479 if (!p)
480 {
481 // We didn't get a proper server name.
482 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
483 goto Cleanup;
484 }
485
486 // Null-terminate the string here to enable comparison.
487 *p = 0;
488
489 // Get the local computer name for comparison.
490 cchComputerName = _countof(wszComputerName);
491 if (!GetComputerNameW(wszComputerName, &cchComputerName))
492 {
493 dwErrorCode = GetLastError();
494 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
495 goto Cleanup;
496 }
497
498 // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers.
499 if (wcsicmp(lpPrinterName, wszComputerName) != 0)
500 {
501 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
502 goto Cleanup;
503 }
504
505 // We have checked the server name and don't need it anymore.
506 lpPrinterName = p + 1;
507 }
508
509 // Look for a comma. If it exists, it indicates the end of the printer name.
510 p = wcschr(lpPrinterName, L',');
511 if (p)
512 cchPrinterName = p - lpPrinterName;
513 else
514 cchPrinterName = wcslen(lpPrinterName);
515
516 // No printer name and no comma? This is invalid!
517 if (!cchPrinterName && !p)
518 {
519 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
520 goto Cleanup;
521 }
522
523 // Do we have a printer name?
524 if (cchPrinterName)
525 {
526 // Yes, extract it.
527 pwszPrinterName = DllAllocSplMem((cchPrinterName + 1) * sizeof(WCHAR));
528 CopyMemory(pwszPrinterName, lpPrinterName, cchPrinterName * sizeof(WCHAR));
529 pwszPrinterName[cchPrinterName] = 0;
530
531 // Retrieve the associated printer from the list.
532 pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
533 if (!pPrinter)
534 {
535 // The printer does not exist.
536 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
537 goto Cleanup;
538 }
539
540 // Create a new printer handle.
541 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
542 pPrinterHandle->pPrinter = pPrinter;
543
544 // Check if a datatype was given.
545 if (pDefault && pDefault->pDatatype)
546 {
547 // Use the datatype if it's valid.
548 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
549 {
550 dwErrorCode = ERROR_INVALID_DATATYPE;
551 goto Cleanup;
552 }
553
554 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
555 }
556 else
557 {
558 // Use the default datatype.
559 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
560 }
561
562 // Check if a DevMode was given, otherwise use the default.
563 if (pDefault && pDefault->pDevMode)
564 CopyMemory(&pPrinterHandle->DevMode, pDefault->pDevMode, sizeof(DEVMODEW));
565 else
566 CopyMemory(&pPrinterHandle->DevMode, &pPrinter->DefaultDevMode, sizeof(DEVMODEW));
567
568 // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job.
569 if (p)
570 {
571 ++p;
572
573 // Skip whitespace.
574 do
575 {
576 ++p;
577 }
578 while (*p == ' ');
579
580 // The "Job " string has to follow now.
581 if (wcscmp(p, L"Job ") != 0)
582 {
583 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
584 goto Cleanup;
585 }
586
587 // Skip the "Job " string.
588 p += sizeof("Job ") - 1;
589
590 // Skip even more whitespace.
591 while (*p == ' ')
592 ++p;
593
594 // Finally extract the desired Job ID.
595 dwJobID = wcstoul(p, NULL, 10);
596 if (!IS_VALID_JOB_ID(dwJobID))
597 {
598 // The user supplied an invalid Job ID.
599 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
600 goto Cleanup;
601 }
602
603 // Look for this job in the Global Job List.
604 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
605 if (!pJob || pJob->pPrinter != pPrinter)
606 {
607 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
608 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
609 goto Cleanup;
610 }
611
612 pPrinterHandle->pStartedJob = pJob;
613 }
614
615 // Create a new handle that references a printer.
616 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
617 pHandle->HandleType = Printer;
618 pHandle->pSpecificHandle = pPrinterHandle;
619 }
620 else
621 {
622 // No printer name, but we have a comma!
623 // This may be a request to a XcvMonitor or XcvPort handle.
624 ++p;
625
626 // Skip whitespace.
627 do
628 {
629 ++p;
630 }
631 while (*p == ' ');
632
633 // Check if this is a request to a XcvMonitor.
634 if (wcscmp(p, L"XcvMonitor ") == 0)
635 {
636 // Skip the "XcvMonitor " string.
637 p += sizeof("XcvMonitor ") - 1;
638
639 ///////////// TODO /////////////////////
640 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
641 pHandle->HandleType = Monitor;
642 //pHandle->pSpecificHandle = pMonitorHandle;
643 }
644 else if (wcscmp(p, L"XcvPort ") == 0)
645 {
646 // Skip the "XcvPort " string.
647 p += sizeof("XcvPort ") - 1;
648
649 //////////// TODO //////////////////////
650 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
651 pHandle->HandleType = Port;
652 //pHandle->pSpecificHandle = pPortHandle;
653 }
654 else
655 {
656 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
657 goto Cleanup;
658 }
659 }
660
661 *phPrinter = (HANDLE)pHandle;
662 dwErrorCode = ERROR_SUCCESS;
663
664 // Don't let the cleanup routines free this.
665 pPrinterHandle = NULL;
666 pwszPrinterName = NULL;
667
668 Cleanup:
669 if (pPrinterHandle)
670 {
671 if (pPrinterHandle->pwszDatatype)
672 DllFreeSplStr(pPrinterHandle->pwszDatatype);
673
674 DllFreeSplMem(pPrinterHandle);
675 }
676
677 if (pwszPrinterName)
678 DllFreeSplMem(pwszPrinterName);
679
680 SetLastError(dwErrorCode);
681 return (dwErrorCode == ERROR_SUCCESS);
682 }
683
684 DWORD WINAPI
685 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
686 {
687 DWORD dwErrorCode;
688 DWORD dwReturnValue = 0;
689 PDOC_INFO_1W pDocumentInfo1;
690 PLOCAL_HANDLE pHandle;
691 PLOCAL_JOB pJob;
692 PLOCAL_PRINTER_HANDLE pPrinterHandle;
693
694 // Sanity checks
695 if (!pDocInfo)
696 {
697 dwErrorCode = ERROR_INVALID_PARAMETER;
698 goto Cleanup;
699 }
700
701 if (!hPrinter)
702 {
703 dwErrorCode = ERROR_INVALID_HANDLE;
704 goto Cleanup;
705 }
706
707 // Check if this is a printer handle.
708 pHandle = (PLOCAL_HANDLE)hPrinter;
709 if (pHandle->HandleType != Printer)
710 {
711 dwErrorCode = ERROR_INVALID_HANDLE;
712 goto Cleanup;
713 }
714
715 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
716
717 // Check if this is the right document information level.
718 if (Level != 1)
719 {
720 dwErrorCode = ERROR_INVALID_LEVEL;
721 goto Cleanup;
722 }
723
724 pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo;
725
726 // Create a new job.
727 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
728 pJob->pPrinter = pPrinterHandle->pPrinter;
729 pJob->dwPriority = DEF_PRIORITY;
730
731 // Check if a datatype was given.
732 if (pDocumentInfo1->pDatatype)
733 {
734 // Use the datatype if it's valid.
735 if (!FindDatatype(pJob->pPrintProcessor, pDocumentInfo1->pDatatype))
736 {
737 dwErrorCode = ERROR_INVALID_DATATYPE;
738 goto Cleanup;
739 }
740
741 pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
742 }
743 else
744 {
745 // Use the printer handle datatype.
746 pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
747 }
748
749 // Copy over printer defaults.
750 CopyMemory(&pJob->DevMode, &pPrinterHandle->DevMode, sizeof(DEVMODEW));
751
752 // Copy over supplied information.
753 if (pDocumentInfo1->pDocName)
754 pJob->pwszDocumentName = AllocSplStr(pDocumentInfo1->pDocName);
755
756 if (pDocumentInfo1->pOutputFile)
757 pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
758
759 // Get an ID for the new job.
760 if (!GetNextJobID(&pJob->dwJobID))
761 {
762 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
763 goto Cleanup;
764 }
765
766 // Add the job to the Global Job List.
767 if (!InsertElementSkiplist(&GlobalJobList, pJob))
768 {
769 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
770 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
771 goto Cleanup;
772 }
773
774 // Add the job at the end of the Printer's Job List.
775 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
776 if (!InsertTailElementSkiplist(&pJob->pPrinter->JobList, pJob))
777 {
778 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
779 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
780 goto Cleanup;
781 }
782
783 pPrinterHandle->pStartedJob = pJob;
784 dwErrorCode = ERROR_SUCCESS;
785 dwReturnValue = pJob->dwJobID;
786
787 Cleanup:
788 SetLastError(dwErrorCode);
789 return dwReturnValue;
790 }
791
792 BOOL WINAPI
793 LocalStartPagePrinter(HANDLE hPrinter)
794 {
795 ///////////// TODO /////////////////////
796 return FALSE;
797 }
798
799 BOOL WINAPI
800 LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
801 {
802 ///////////// TODO /////////////////////
803 return FALSE;
804 }
805
806 BOOL WINAPI
807 LocalEndPagePrinter(HANDLE hPrinter)
808 {
809 ///////////// TODO /////////////////////
810 return FALSE;
811 }
812
813 BOOL WINAPI
814 LocalEndDocPrinter(HANDLE hPrinter)
815 {
816 ///////////// TODO /////////////////////
817 return FALSE;
818 }
819
820 BOOL WINAPI
821 LocalClosePrinter(HANDLE hPrinter)
822 {
823 PLOCAL_HANDLE pHandle;
824
825 if (!hPrinter)
826 {
827 SetLastError(ERROR_INVALID_HANDLE);
828 return FALSE;
829 }
830
831 pHandle = (PLOCAL_HANDLE)hPrinter;
832
833 ///////////// TODO /////////////////////
834 /// Check the handle type, do thoroughful checks on all data fields and clean them.
835 ////////////////////////////////////////
836
837 DllFreeSplMem(pHandle);
838
839 return TRUE;
840 }