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