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