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