[WINSPOOL]
[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 *pcReturned = 1;
321
322 // Check if the supplied buffer is large enough.
323 if (cbBuf < *pcbNeeded)
324 {
325 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
326 goto Cleanup;
327 }
328
329 // Copy over the print processor information.
330 ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0;
331 PackStrings(wszPrintProviderInfo, pPrinterEnum, dwOffsets, &pPrinterEnum[*pcbNeeded]);
332 dwErrorCode = ERROR_SUCCESS;
333 goto Cleanup;
334 }
335 }
336
337 // Count the required buffer size and the number of printers.
338 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
339 {
340 pPrinter = (PLOCAL_PRINTER)pNode->Element;
341
342 // This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
343 // But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
344 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
345 cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
346 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
347 cbDescription = cchComputerName * sizeof(WCHAR) + cbName + cbComment + sizeof(WCHAR);
348
349 *pcbNeeded += sizeof(PRINTER_INFO_1W) + cchComputerName * sizeof(WCHAR) + cbName + cbComment + cbDescription;
350 *pcReturned++;
351 }
352
353 // Check if the supplied buffer is large enough.
354 if (cbBuf < *pcbNeeded)
355 {
356 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
357 goto Cleanup;
358 }
359
360 // Put the strings right after the last PRINTER_INFO_1W structure.
361 // Due to all the required string processing, we can't just use PackStrings here :(
362 pPrinterInfo = pPrinterEnum;
363 pPrinterString = pPrinterEnum + *pcReturned * sizeof(PRINTER_INFO_1W);
364
365 // Copy over the printer information.
366 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
367 {
368 pPrinter = (PLOCAL_PRINTER)pNode->Element;
369
370 // FIXME: As for now, the Flags member returns no information.
371 PrinterInfo1.Flags = 0;
372
373 // Copy the printer name.
374 PrinterInfo1.pName = (PWSTR)pPrinterString;
375 CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
376 pPrinterString += cchComputerName * sizeof(WCHAR);
377 cbName = (wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
378 CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName);
379 pPrinterString += cbName;
380
381 // Copy the printer comment (equals the "Description" registry value).
382 PrinterInfo1.pComment = (PWSTR)pPrinterString;
383 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
384 CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment);
385 pPrinterString += cbComment;
386
387 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Comment,"
388 PrinterInfo1.pDescription = (PWSTR)pPrinterString;
389 CopyMemory(pPrinterString, wszComputerName, cchComputerName * sizeof(WCHAR));
390 pPrinterString += cchComputerName * sizeof(WCHAR);
391 CopyMemory(pPrinterString, pPrinter->pwszPrinterName, cbName - sizeof(WCHAR));
392 pPrinterString += cbName - sizeof(WCHAR);
393 CopyMemory(pPrinterString, wszComma, sizeof(WCHAR));
394 pPrinterString += sizeof(WCHAR);
395 CopyMemory(pPrinterString, pPrinter->pwszDescription, cbComment - sizeof(WCHAR));
396 pPrinterString += cbComment - sizeof(WCHAR);
397 CopyMemory(pPrinterString, wszComma, sizeof(wszComma));
398 pPrinterString += sizeof(wszComma);
399
400 // Finally copy the structure and advance to the next one in the output buffer.
401 CopyMemory(pPrinterInfo, &PrinterInfo1, sizeof(PRINTER_INFO_1W));
402 pPrinterInfo += sizeof(PRINTER_INFO_1W);
403 }
404
405 dwErrorCode = ERROR_SUCCESS;
406
407 Cleanup:
408 return dwErrorCode;
409 }
410
411 BOOL WINAPI
412 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
413 {
414 DWORD dwErrorCode;
415
416 // Do no sanity checks here. This is verified by localspl_apitest!
417
418 // Begin counting.
419 *pcbNeeded = 0;
420 *pcReturned = 0;
421
422 // Think positive :)
423 // Treat it as success if the caller queried no information and we don't need to return any.
424 dwErrorCode = ERROR_SUCCESS;
425
426 if (Flags & PRINTER_ENUM_LOCAL)
427 {
428 // The function behaves quite differently for each level.
429 if (Level == 1)
430 {
431 dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
432 }
433 else
434 {
435 // TODO: Handle other levels.
436 // The caller supplied an invalid level.
437 dwErrorCode = ERROR_INVALID_LEVEL;
438 goto Cleanup;
439 }
440 }
441
442 Cleanup:
443 SetLastError(dwErrorCode);
444 return (dwErrorCode == ERROR_SUCCESS);
445 }
446
447 BOOL WINAPI
448 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault)
449 {
450 DWORD cchComputerName;
451 DWORD cchPrinterName;
452 DWORD dwErrorCode;
453 DWORD dwJobID;
454 PWSTR p = lpPrinterName;
455 PWSTR pwszPrinterName = NULL;
456 PLOCAL_JOB pJob;
457 PLOCAL_HANDLE pHandle;
458 PLOCAL_PRINTER pPrinter;
459 PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
460 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
461
462 // Sanity checks
463 if (!lpPrinterName || !phPrinter)
464 {
465 dwErrorCode = ERROR_INVALID_PARAMETER;
466 goto Cleanup;
467 }
468
469 // Does lpPrinterName begin with two backslashes to indicate a server name?
470 if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\')
471 {
472 // Skip these two backslashes.
473 lpPrinterName += 2;
474
475 // Look for the closing backslash.
476 p = wcschr(lpPrinterName, L'\\');
477 if (!p)
478 {
479 // We didn't get a proper server name.
480 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
481 goto Cleanup;
482 }
483
484 // Null-terminate the string here to enable comparison.
485 *p = 0;
486
487 // Get the local computer name for comparison.
488 cchComputerName = _countof(wszComputerName);
489 if (!GetComputerNameW(wszComputerName, &cchComputerName))
490 {
491 dwErrorCode = GetLastError();
492 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
493 goto Cleanup;
494 }
495
496 // Now compare this with the local computer name and reject if it doesn't match, because this print provider only supports local printers.
497 if (wcsicmp(lpPrinterName, wszComputerName) != 0)
498 {
499 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
500 goto Cleanup;
501 }
502
503 // We have checked the server name and don't need it anymore.
504 lpPrinterName = p + 1;
505 }
506
507 // Look for a comma. If it exists, it indicates the end of the printer name.
508 p = wcschr(lpPrinterName, L',');
509 if (p)
510 cchPrinterName = p - lpPrinterName;
511 else
512 cchPrinterName = wcslen(lpPrinterName);
513
514 // No printer name and no comma? This is invalid!
515 if (!cchPrinterName && !p)
516 {
517 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
518 goto Cleanup;
519 }
520
521 // Do we have a printer name?
522 if (cchPrinterName)
523 {
524 // Yes, extract it.
525 pwszPrinterName = DllAllocSplMem((cchPrinterName + 1) * sizeof(WCHAR));
526 CopyMemory(pwszPrinterName, lpPrinterName, cchPrinterName * sizeof(WCHAR));
527 pwszPrinterName[cchPrinterName] = 0;
528
529 // Retrieve the associated printer from the list.
530 pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
531 if (!pPrinter)
532 {
533 // The printer does not exist.
534 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
535 goto Cleanup;
536 }
537
538 // Create a new printer handle.
539 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE));
540 pPrinterHandle->Printer = pPrinter;
541
542 // Check if a datatype was given.
543 if (pDefault && pDefault->pDatatype)
544 {
545 // Use the datatype if it's valid.
546 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
547 {
548 dwErrorCode = ERROR_INVALID_DATATYPE;
549 goto Cleanup;
550 }
551
552 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype);
553 }
554 else
555 {
556 // Use the default datatype.
557 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype);
558 }
559
560 // Check if a DevMode was given, otherwise use the default.
561 if (pDefault && pDefault->pDevMode)
562 CopyMemory(&pPrinterHandle->DevMode, pDefault->pDevMode, sizeof(DEVMODEW));
563 else
564 CopyMemory(&pPrinterHandle->DevMode, &pPrinter->DefaultDevMode, sizeof(DEVMODEW));
565
566 // Did we have a comma? Then the user may want a handle to an existing job instead of creating a new job.
567 if (p)
568 {
569 ++p;
570
571 // Skip whitespace.
572 do
573 {
574 ++p;
575 }
576 while (*p == ' ');
577
578 // The "Job " string has to follow now.
579 if (wcscmp(p, L"Job ") != 0)
580 {
581 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
582 goto Cleanup;
583 }
584
585 // Skip the "Job " string.
586 p += sizeof("Job ") - 1;
587
588 // Skip even more whitespace.
589 while (*p == ' ')
590 ++p;
591
592 // Finally extract the desired Job ID.
593 dwJobID = wcstoul(p, NULL, 10);
594 if (!IS_VALID_JOB_ID(dwJobID))
595 {
596 // The user supplied an invalid Job ID.
597 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
598 goto Cleanup;
599 }
600
601 // Look for this job in the Global Job List.
602 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
603 if (!pJob || pJob->Printer != pPrinter)
604 {
605 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
606 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
607 goto Cleanup;
608 }
609
610 pPrinterHandle->StartedJob = pJob;
611 }
612
613 // Create a new handle that references a printer.
614 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
615 pHandle->HandleType = Printer;
616 pHandle->SpecificHandle = pPrinterHandle;
617 }
618 else
619 {
620 // No printer name, but we have a comma!
621 // This may be a request to a XcvMonitor or XcvPort handle.
622 ++p;
623
624 // Skip whitespace.
625 do
626 {
627 ++p;
628 }
629 while (*p == ' ');
630
631 // Check if this is a request to a XcvMonitor.
632 if (wcscmp(p, L"XcvMonitor ") == 0)
633 {
634 // Skip the "XcvMonitor " string.
635 p += sizeof("XcvMonitor ") - 1;
636
637 ///////////// TODO /////////////////////
638 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
639 pHandle->HandleType = Monitor;
640 //pHandle->SpecificHandle = pMonitorHandle;
641 }
642 else if (wcscmp(p, L"XcvPort ") == 0)
643 {
644 // Skip the "XcvPort " string.
645 p += sizeof("XcvPort ") - 1;
646
647 //////////// TODO //////////////////////
648 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE));
649 pHandle->HandleType = Port;
650 //pHandle->SpecificHandle = pPortHandle;
651 }
652 else
653 {
654 dwErrorCode = ERROR_INVALID_PRINTER_NAME;
655 goto Cleanup;
656 }
657 }
658
659 *phPrinter = (HANDLE)pHandle;
660 dwErrorCode = ERROR_SUCCESS;
661
662 // Don't let the cleanup routines free this.
663 pPrinterHandle = NULL;
664 pwszPrinterName = NULL;
665
666 Cleanup:
667 if (pPrinterHandle)
668 {
669 if (pPrinterHandle->pwszDatatype)
670 DllFreeSplStr(pPrinterHandle->pwszDatatype);
671
672 DllFreeSplMem(pPrinterHandle);
673 }
674
675 if (pwszPrinterName)
676 DllFreeSplMem(pwszPrinterName);
677
678 SetLastError(dwErrorCode);
679 return (dwErrorCode == ERROR_SUCCESS);
680 }
681
682 DWORD WINAPI
683 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
684 {
685 DWORD dwErrorCode;
686 DWORD dwReturnValue = 0;
687 PDOC_INFO_1W pDocumentInfo1;
688 PLOCAL_HANDLE pHandle;
689 PLOCAL_JOB pJob;
690 PLOCAL_PRINTER_HANDLE pPrinterHandle;
691
692 // Sanity checks
693 if (!pDocInfo)
694 {
695 dwErrorCode = ERROR_INVALID_PARAMETER;
696 goto Cleanup;
697 }
698
699 if (!hPrinter)
700 {
701 dwErrorCode = ERROR_INVALID_HANDLE;
702 goto Cleanup;
703 }
704
705 // Check if this is a printer handle.
706 pHandle = (PLOCAL_HANDLE)hPrinter;
707 if (pHandle->HandleType != Printer)
708 {
709 dwErrorCode = ERROR_INVALID_HANDLE;
710 goto Cleanup;
711 }
712
713 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle;
714
715 // Check if this is the right document information level.
716 if (Level != 1)
717 {
718 dwErrorCode = ERROR_INVALID_LEVEL;
719 goto Cleanup;
720 }
721
722 pDocumentInfo1 = (PDOC_INFO_1W)pDocInfo;
723
724 // Create a new job.
725 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
726 pJob->Printer = pPrinterHandle->Printer;
727 pJob->dwPriority = DEF_PRIORITY;
728
729 // Check if a datatype was given.
730 if (pDocumentInfo1->pDatatype)
731 {
732 // Use the datatype if it's valid.
733 if (!FindDatatype(pJob->Printer->pPrintProcessor, pDocumentInfo1->pDatatype))
734 {
735 dwErrorCode = ERROR_INVALID_DATATYPE;
736 goto Cleanup;
737 }
738
739 pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
740 }
741 else
742 {
743 // Use the printer handle datatype.
744 pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
745 }
746
747 // Copy over printer defaults.
748 CopyMemory(&pJob->DevMode, &pPrinterHandle->DevMode, sizeof(DEVMODEW));
749
750 // Copy over supplied information.
751 if (pDocumentInfo1->pDocName)
752 pJob->pwszDocumentName = AllocSplStr(pDocumentInfo1->pDocName);
753
754 if (pDocumentInfo1->pOutputFile)
755 pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
756
757 // Get an ID for the new job.
758 if (!GetNextJobID(&pJob->dwJobID))
759 {
760 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
761 goto Cleanup;
762 }
763
764 // Add the job to the Global Job List.
765 if (!InsertElementSkiplist(&GlobalJobList, pJob))
766 {
767 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
768 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
769 goto Cleanup;
770 }
771
772 // Add the job at the end of the Printer's Job List.
773 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
774 if (!InsertTailElementSkiplist(&pJob->Printer->JobList, pJob))
775 {
776 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
777 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
778 goto Cleanup;
779 }
780
781 pPrinterHandle->StartedJob = pJob;
782 dwErrorCode = ERROR_SUCCESS;
783 dwReturnValue = pJob->dwJobID;
784
785 Cleanup:
786 SetLastError(dwErrorCode);
787 return dwReturnValue;
788 }
789
790 BOOL WINAPI
791 LocalStartPagePrinter(HANDLE hPrinter)
792 {
793 ///////////// TODO /////////////////////
794 return FALSE;
795 }
796
797 BOOL WINAPI
798 LocalWritePrinter(HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten)
799 {
800 ///////////// TODO /////////////////////
801 return FALSE;
802 }
803
804 BOOL WINAPI
805 LocalEndPagePrinter(HANDLE hPrinter)
806 {
807 ///////////// TODO /////////////////////
808 return FALSE;
809 }
810
811 BOOL WINAPI
812 LocalEndDocPrinter(HANDLE hPrinter)
813 {
814 ///////////// TODO /////////////////////
815 return FALSE;
816 }
817
818 BOOL WINAPI
819 LocalClosePrinter(HANDLE hPrinter)
820 {
821 PLOCAL_HANDLE pHandle;
822
823 if (!hPrinter)
824 {
825 SetLastError(ERROR_INVALID_HANDLE);
826 return FALSE;
827 }
828
829 pHandle = (PLOCAL_HANDLE)hPrinter;
830
831 ///////////// TODO /////////////////////
832 /// Check the handle type, do thoroughful checks on all data fields and clean them.
833 ////////////////////////////////////////
834
835 DllFreeSplMem(pHandle);
836
837 return TRUE;
838 }