05407352c88f7209224e290c2121f218e6ec66fa
[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 DWORD cchComputerName;
481 DWORD cchPrinterName;
482 DWORD dwErrorCode;
483 DWORD dwJobID;
484 PWSTR p = lpPrinterName;
485 PWSTR pwszPrinterName = NULL;
486 PLOCAL_JOB pJob;
487 PLOCAL_HANDLE pHandle;
488 PLOCAL_PRINTER pPrinter;
489 PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
490 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
491
492 // Sanity checks
493 if (!lpPrinterName || !phPrinter)
494 {
495 dwErrorCode = ERROR_INVALID_PARAMETER;
496 goto Cleanup;
497 }
498
499 // Does lpPrinterName begin with two backslashes to indicate a server name?
500 if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
501 {
502 // Skip these two backslashes.
503 lpPrinterName += 2;
504
505 // Look for the closing backslash.
506 p = wcschr(lpPrinterName, L'\\');
507 if (!p)
508 {
509 // We didn't get a proper server name.
510 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
511 goto Cleanup;
512 }
513
514 // Null-terminate the string here to enable comparison.
515 *p = 0;
516
517 // Get the local computer name for comparison.
518 cchComputerName = _countof(wszComputerName);
519 if (!GetComputerNameW(wszComputerName, &cchComputerName))
520 {
521 dwErrorCode = GetLastError();
522 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
523 goto Cleanup;
524 }
525
526 // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers.
527 if (wcsicmp(lpPrinterName, wszComputerName) != 0)
528 {
529 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
530 goto Cleanup;
531 }
532
533 // We have checked the server name and don't need it anymore.
534 lpPrinterName = p + 1;
535 }
536
537 // Look for a comma. If it exists, it indicates the end of the printer name.
538 p = wcschr(lpPrinterName, L',');
539 if (p)
540 cchPrinterName = p - lpPrinterName;
541 else
542 cchPrinterName = wcslen(lpPrinterName);
543
544 // No printer name and no comma? This is invalid!
545 if (!cchPrinterName && !p)
546 {
547 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
548 goto Cleanup;
549 }
550
551 // Do we have a printer name?
552 if (cchPrinterName)
553 {
554 // Yes, extract it.
555 pwszPrinterName = DllAllocSplMem((cchPrinterName + 1) * sizeof(WCHAR));
556 CopyMemory(pwszPrinterName, lpPrinterName, cchPrinterName * sizeof(WCHAR));
557 pwszPrinterName[cchPrinterName] = 0;
558
559 // Retrieve the associated printer from the list.
560 pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
561 if (!pPrinter)
562 {
563 // The printer does not exist.
564 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
565 goto Cleanup;
566 }
567
568 // Create a new printer handle.
569 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
570 pPrinterHandle->pPrinter = pPrinter;
571
572 // Check if a datatype was given.
573 if (pDefault && pDefault->pDatatype)
574 {
575 // Use the datatype if it's valid.
576 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
577 {
578 dwErrorCode = ERROR_INVALID_DATATYPE;
579 goto Cleanup;
580 }
581
582 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
583 }
584 else
585 {
586 // Use the default datatype.
587 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
588 }
589
590 // Check if a DevMode was given, otherwise use the default.
591 if (pDefault && pDefault->pDevMode)
592 pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode);
593 else
594 pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode);
595
596 // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job.
597 if (p)
598 {
599 ++p;
600
601 // Skip whitespace.
602 do
603 {
604 ++p;
605 }
606 while (*p == ' ');
607
608 // The "Job " string has to follow now.
609 if (wcscmp(p, L"Job ") != 0)
610 {
611 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
612 goto Cleanup;
613 }
614
615 // Skip the "Job " string.
616 p += sizeof("Job ") - 1;
617
618 // Skip even more whitespace.
619 while (*p == ' ')
620 ++p;
621
622 // Finally extract the desired Job ID.
623 dwJobID = wcstoul(p, NULL, 10);
624 if (!IS_VALID_JOB_ID(dwJobID))
625 {
626 // The user supplied an invalid Job ID.
627 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
628 goto Cleanup;
629 }
630
631 // Look for this job in the Global Job List.
632 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
633 if (!pJob || pJob->pPrinter != pPrinter)
634 {
635 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
636 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
637 goto Cleanup;
638 }
639
640 pPrinterHandle->pStartedJob = pJob;
641 }
642
643 // Create a new handle that references a printer.
644 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
645 pHandle->HandleType = Printer;
646 pHandle->pSpecificHandle = pPrinterHandle;
647 }
648 else
649 {
650 // No printer name, but we have a comma!
651 // This may be a request to a XcvMonitor or XcvPort handle.
652 ++p;
653
654 // Skip whitespace.
655 do
656 {
657 ++p;
658 }
659 while (*p == ' ');
660
661 // Check if this is a request to a XcvMonitor.
662 if (wcscmp(p, L"XcvMonitor ") == 0)
663 {
664 // Skip the "XcvMonitor " string.
665 p += sizeof("XcvMonitor ") - 1;
666
667 ///////////// TODO /////////////////////
668 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
669 pHandle->HandleType = Monitor;
670 //pHandle->pSpecificHandle = pMonitorHandle;
671 }
672 else if (wcscmp(p, L"XcvPort ") == 0)
673 {
674 // Skip the "XcvPort " string.
675 p += sizeof("XcvPort ") - 1;
676
677 //////////// TODO //////////////////////
678 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
679 pHandle->HandleType = Port;
680 //pHandle->pSpecificHandle = pPortHandle;
681 }
682 else
683 {
684 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
685 goto Cleanup;
686 }
687 }
688
689 *phPrinter = (HANDLE)pHandle;
690 dwErrorCode = ERROR_SUCCESS;
691
692 // Don't let the cleanup routines free this.
693 pPrinterHandle = NULL;
694 pwszPrinterName = NULL;
695
696 Cleanup:
697 if (pPrinterHandle)
698 {
699 if (pPrinterHandle->pwszDatatype)
700 DllFreeSplStr(pPrinterHandle->pwszDatatype);
701
702 DllFreeSplMem(pPrinterHandle);
703 }
704
705 if (pwszPrinterName)
706 DllFreeSplMem(pwszPrinterName);
707
708 SetLastError(dwErrorCode);
709 return (dwErrorCode == ERROR_SUCCESS);
710 }
711
712 DWORD WINAPI
713 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
714 {
715 DWORD dwErrorCode;
716 DWORD dwReturnValue = 0;
717 PDOC_INFO_1W pDocumentInfo1;
718 PLOCAL_HANDLE pHandle;
719 PLOCAL_JOB pJob;
720 PLOCAL_PRINTER_HANDLE pPrinterHandle;
721
722 // Sanity checks
723 if (!pDocInfo)
724 {
725 dwErrorCode = ERROR_INVALID_PARAMETER;
726 goto Cleanup;
727 }
728
729 if (!hPrinter)
730 {
731 dwErrorCode = ERROR_INVALID_HANDLE;
732 goto Cleanup;
733 }
734
735 // Check if this is a printer handle.
736 pHandle = (PLOCAL_HANDLE)hPrinter;
737 if (pHandle->HandleType != Printer)
738 {
739 dwErrorCode = ERROR_INVALID_HANDLE;
740 goto Cleanup;
741 }
742
743 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
744
745 // Check if this is the right document information level.
746 if (Level != 1)
747 {
748 dwErrorCode = ERROR_INVALID_LEVEL;
749 goto Cleanup;
750 }
751
752 pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo;
753
754 // Create a new job.
755 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
756 pJob->pPrinter = pPrinterHandle->pPrinter;
757 pJob->dwPriority = DEF_PRIORITY;
758
759 // Check if a datatype was given.
760 if (pDocumentInfo1->pDatatype)
761 {
762 // Use the datatype if it's valid.
763 if (!FindDatatype(pJob->pPrintProcessor, pDocumentInfo1->pDatatype))
764 {
765 dwErrorCode = ERROR_INVALID_DATATYPE;
766 goto Cleanup;
767 }
768
769 pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
770 }
771 else
772 {
773 // Use the printer handle datatype.
774 pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
775 }
776
777 // Copy over printer defaults.
778 pJob->pDevMode = DuplicateDevMode(pPrinterHandle->pDevMode);
779
780 // Copy over supplied information.
781 if (pDocumentInfo1->pDocName)
782 pJob->pwszDocumentName = AllocSplStr(pDocumentInfo1->pDocName);
783
784 if (pDocumentInfo1->pOutputFile)
785 pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
786
787 // Get an ID for the new job.
788 if (!GetNextJobID(&pJob->dwJobID))
789 {
790 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
791 goto Cleanup;
792 }
793
794 // Add the job to the Global Job List.
795 if (!InsertElementSkiplist(&GlobalJobList, pJob))
796 {
797 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
798 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
799 goto Cleanup;
800 }
801
802 // Add the job at the end of the Printer's Job List.
803 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
804 if (!InsertTailElementSkiplist(&pJob->pPrinter->JobList, pJob))
805 {
806 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
807 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
808 goto Cleanup;
809 }
810
811 pPrinterHandle->pStartedJob = pJob;
812 dwErrorCode = ERROR_SUCCESS;
813 dwReturnValue = pJob->dwJobID;
814
815 Cleanup:
816 SetLastError(dwErrorCode);
817 return dwReturnValue;
818 }
819
820 BOOL WINAPI
821 LocalStartPagePrinter(HANDLE hPrinter)
822 {
823 ///////////// TODO /////////////////////
824 return FALSE;
825 }
826
827 BOOL WINAPI
828 LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
829 {
830 ///////////// TODO /////////////////////
831 return FALSE;
832 }
833
834 BOOL WINAPI
835 LocalEndPagePrinter(HANDLE hPrinter)
836 {
837 ///////////// TODO /////////////////////
838 return FALSE;
839 }
840
841 BOOL WINAPI
842 LocalEndDocPrinter(HANDLE hPrinter)
843 {
844 ///////////// TODO /////////////////////
845 return FALSE;
846 }
847
848 BOOL WINAPI
849 LocalClosePrinter(HANDLE hPrinter)
850 {
851 PLOCAL_HANDLE pHandle;
852
853 if (!hPrinter)
854 {
855 SetLastError(ERROR_INVALID_HANDLE);
856 return FALSE;
857 }
858
859 pHandle = (PLOCAL_HANDLE)hPrinter;
860
861 ///////////// TODO /////////////////////
862 /// Check the handle type, do thoroughful checks on all data fields and clean them.
863 ////////////////////////////////////////
864
865 DllFreeSplMem(pHandle);
866
867 return TRUE;
868 }