2fa40990ff1b301d6f81664f57abd03cbc8ddeb9
[reactos.git] / reactos / win32ss / printing / providers / localspl / jobs.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 for managing print jobs
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
6 */
7
8 #include "precomp.h"
9
10 // Global Variables
11 SKIPLIST GlobalJobList;
12
13 // Local Variables
14 static DWORD _dwLastJobID;
15
16
17 static BOOL
18 _GetNextJobID(PDWORD dwJobID)
19 {
20 ++_dwLastJobID;
21
22 while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL))
23 {
24 // This ID is already taken. Try the next one.
25 ++_dwLastJobID;
26 }
27
28 if (!IS_VALID_JOB_ID(_dwLastJobID))
29 {
30 ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
31 return FALSE;
32 }
33
34 *dwJobID = _dwLastJobID;
35 return TRUE;
36 }
37
38 /**
39 * @name _GlobalJobListCompareRoutine
40 *
41 * SKIPLIST_COMPARE_ROUTINE for the Global Job List.
42 * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
43 */
44 static int WINAPI
45 _GlobalJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
46 {
47 PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
48 PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
49
50 return A->dwJobID - B->dwJobID;
51 }
52
53 /**
54 * @name _PrinterJobListCompareRoutine
55 *
56 * SKIPLIST_COMPARE_ROUTINE for the each Printer's Job List.
57 * Jobs in this list are sorted in the desired order of processing.
58 */
59 static int WINAPI
60 _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
61 {
62 PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
63 PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
64 int iComparison;
65 FILETIME ftSubmittedA;
66 FILETIME ftSubmittedB;
67 ULARGE_INTEGER uliSubmittedA;
68 ULARGE_INTEGER uliSubmittedB;
69 ULONGLONG ullResult;
70
71 // First compare the priorities to determine the order.
72 // The job with a higher priority shall come first.
73 iComparison = A->dwPriority - B->dwPriority;
74 if (iComparison != 0)
75 return iComparison;
76
77 // Both have the same priority, so go by creation time.
78 // Comparison is done using the MSDN-recommended way for comparing SYSTEMTIMEs.
79 if (!SystemTimeToFileTime(&A->stSubmitted, &ftSubmittedA))
80 {
81 ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
82 return 0;
83 }
84
85 if (!SystemTimeToFileTime(&B->stSubmitted, &ftSubmittedB))
86 {
87 ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
88 return 0;
89 }
90
91 uliSubmittedA.LowPart = ftSubmittedA.dwLowDateTime;
92 uliSubmittedA.HighPart = ftSubmittedA.dwHighDateTime;
93 uliSubmittedB.LowPart = ftSubmittedB.dwLowDateTime;
94 uliSubmittedB.HighPart = ftSubmittedB.dwHighDateTime;
95 ullResult = uliSubmittedA.QuadPart - uliSubmittedB.QuadPart;
96
97 if (ullResult < 0)
98 return -1;
99 else if (ullResult > 0)
100 return 1;
101
102 return 0;
103 }
104
105 DWORD
106 GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
107 {
108 const WCHAR wszPrintersPath[] = L"\\PRINTERS\\";
109 const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1;
110 const DWORD cchSpoolerFile = sizeof("?????.") - 1;
111 const DWORD cchExtension = sizeof("SPL") - 1; // pwszExtension may be L"SPL" or L"SHD", same length for both!
112
113 if (pwszOutput)
114 {
115 CopyMemory(pwszOutput, wszSpoolDirectory, cchSpoolDirectory);
116 CopyMemory(&pwszOutput[cchSpoolDirectory], wszPrintersPath, cchPrintersPath);
117 swprintf(&pwszOutput[cchSpoolDirectory + cchPrintersPath], L"%05lu.", dwJobID);
118 CopyMemory(&pwszOutput[cchSpoolDirectory + cchPrintersPath + cchSpoolerFile], pwszExtension, (cchExtension + 1) * sizeof(WCHAR));
119 }
120
121 return (cchSpoolDirectory + cchPrintersPath + cchSpoolerFile + cchExtension + 1) * sizeof(WCHAR);
122 }
123
124 BOOL
125 InitializeGlobalJobList()
126 {
127 const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
128 const DWORD cchPath = _countof(wszPath) - 1;
129
130 DWORD dwErrorCode;
131 DWORD dwJobID;
132 HANDLE hFind;
133 PLOCAL_JOB pJob = NULL;
134 PWSTR p;
135 WCHAR wszFullPath[MAX_PATH];
136 WIN32_FIND_DATAW FindData;
137
138 // This one is incremented in _GetNextJobID.
139 _dwLastJobID = 0;
140
141 // Initialize an empty list for all jobs of all local printers.
142 // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
143 InitializeSkiplist(&GlobalJobList, DllAllocSplMem, _GlobalJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
144
145 // Construct the full path search pattern.
146 CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
147 CopyMemory(&wszFullPath[cchSpoolDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
148
149 // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
150 hFind = FindFirstFileW(wszFullPath, &FindData);
151 if (hFind == INVALID_HANDLE_VALUE)
152 {
153 // No unfinished jobs found.
154 dwErrorCode = ERROR_SUCCESS;
155 goto Cleanup;
156 }
157
158 do
159 {
160 // Skip possible subdirectories.
161 if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
162 continue;
163
164 // Extract the Job ID and verify the file name format at the same time.
165 // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
166 dwJobID = wcstoul(FindData.cFileName, &p, 10);
167 if (!IS_VALID_JOB_ID(dwJobID))
168 continue;
169
170 if (wcsicmp(p, L".SHD") != 0)
171 continue;
172
173 // This shadow file has a valid name. Construct the full path and try to load it.
174 GetJobFilePath(L"SHD", dwJobID, wszFullPath);
175 pJob = ReadJobShadowFile(wszFullPath);
176 if (!pJob)
177 continue;
178
179 // Add it to the Global Job List.
180 if (!InsertElementSkiplist(&GlobalJobList, pJob))
181 {
182 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
183 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
184 goto Cleanup;
185 }
186
187 // Add it to the Printer's Job List.
188 if (!InsertElementSkiplist(&pJob->pPrinter->JobList, pJob))
189 {
190 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
191 ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
192 goto Cleanup;
193 }
194 }
195 while (FindNextFileW(hFind, &FindData));
196
197 dwErrorCode = ERROR_SUCCESS;
198
199 Cleanup:
200 // Outside the loop
201 if (hFind)
202 FindClose(hFind);
203
204 SetLastError(dwErrorCode);
205 return (dwErrorCode == ERROR_SUCCESS);
206 }
207
208 void
209 InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
210 {
211 // Initialize an empty list for this printer's jobs.
212 // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
213 InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
214 }
215
216 DWORD WINAPI
217 CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle)
218 {
219 const WCHAR wszDoubleBackslash[] = L"\\";
220 const DWORD cchDoubleBackslash = _countof(wszDoubleBackslash) - 1;
221
222 DWORD cchMachineName;
223 DWORD cchUserName;
224 DWORD dwErrorCode;
225 PLOCAL_JOB pJob;
226 RPC_BINDING_HANDLE hServerBinding = NULL;
227 RPC_WSTR pwszBinding = NULL;
228 RPC_WSTR pwszMachineName = NULL;
229
230 // Create a new job.
231 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
232 if (!pJob)
233 {
234 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
235 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
236 goto Cleanup;
237 }
238
239 // Reserve an ID for this job.
240 if (!_GetNextJobID(&pJob->dwJobID))
241 {
242 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
243 goto Cleanup;
244 }
245
246 // Copy over defaults to the LOCAL_JOB structure.
247 pJob->pPrinter = pPrinterHandle->pPrinter;
248 pJob->pPrintProcessor = pPrinterHandle->pPrinter->pPrintProcessor;
249 pJob->dwPriority = DEF_PRIORITY;
250 pJob->dwStatus = JOB_STATUS_SPOOLING;
251 pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
252 pJob->pwszDocumentName = AllocSplStr(wszDefaultDocumentName);
253 pJob->pDevMode = DuplicateDevMode(pPrinterHandle->pDevMode);
254 GetSystemTime(&pJob->stSubmitted);
255
256 // Get the user name for the Job.
257 cchUserName = UNLEN + 1;
258 pJob->pwszUserName = DllAllocSplMem(cchUserName * sizeof(WCHAR));
259 if (!GetUserNameW(pJob->pwszUserName, &cchUserName))
260 {
261 dwErrorCode = GetLastError();
262 ERR("GetUserNameW failed with error %lu!\n", dwErrorCode);
263 goto Cleanup;
264 }
265
266 // FIXME: For now, pwszNotifyName equals pwszUserName.
267 pJob->pwszNotifyName = AllocSplStr(pJob->pwszUserName);
268
269 // Get the name of the machine that submitted the Job over RPC.
270 dwErrorCode = RpcBindingServerFromClient(NULL, &hServerBinding);
271 if (dwErrorCode != RPC_S_OK)
272 {
273 ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode);
274 goto Cleanup;
275 }
276
277 dwErrorCode = RpcBindingToStringBindingW(hServerBinding, &pwszBinding);
278 if (dwErrorCode != RPC_S_OK)
279 {
280 ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode);
281 goto Cleanup;
282 }
283
284 dwErrorCode = RpcStringBindingParseW(pwszBinding, NULL, NULL, &pwszMachineName, NULL, NULL);
285 if (dwErrorCode != RPC_S_OK)
286 {
287 ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode);
288 goto Cleanup;
289 }
290
291 cchMachineName = wcslen(pwszMachineName);
292 pJob->pwszMachineName = DllAllocSplMem((cchMachineName + cchDoubleBackslash + 1) * sizeof(WCHAR));
293 CopyMemory(pJob->pwszMachineName, wszDoubleBackslash, cchDoubleBackslash * sizeof(WCHAR));
294 CopyMemory(pJob->pwszMachineName + cchDoubleBackslash, pwszMachineName, (cchMachineName + 1) * sizeof(WCHAR));
295
296 // Add the job to the Global Job List.
297 if (!InsertElementSkiplist(&GlobalJobList, pJob))
298 {
299 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
300 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
301 goto Cleanup;
302 }
303
304 // Add the job at the end of the Printer's Job List.
305 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
306 if (!InsertTailElementSkiplist(&pJob->pPrinter->JobList, pJob))
307 {
308 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
309 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
310 goto Cleanup;
311 }
312
313 // We were successful!
314 pPrinterHandle->bStartedDoc = TRUE;
315 pPrinterHandle->pJob = pJob;
316 dwErrorCode = ERROR_SUCCESS;
317
318 // Don't let the cleanup routine free this.
319 pJob = NULL;
320
321 Cleanup:
322 if (pJob)
323 DllFreeSplMem(pJob);
324
325 if (pwszMachineName)
326 RpcStringFreeW(&pwszMachineName);
327
328 if (pwszBinding)
329 RpcStringFreeW(&pwszBinding);
330
331 if (hServerBinding)
332 RpcBindingFree(&hServerBinding);
333
334 return dwErrorCode;
335 }
336
337 BOOL WINAPI
338 LocalAddJob(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
339 {
340 ADDJOB_INFO_1W AddJobInfo1;
341 DWORD dwErrorCode;
342 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
343 PLOCAL_PRINTER_HANDLE pPrinterHandle;
344
345 // Check if this is a printer handle.
346 if (pHandle->HandleType != HandleType_Printer)
347 {
348 dwErrorCode = ERROR_INVALID_HANDLE;
349 goto Cleanup;
350 }
351
352 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
353
354 // This handle must not have started a job yet!
355 if (pPrinterHandle->pJob)
356 {
357 dwErrorCode = ERROR_INVALID_HANDLE;
358 goto Cleanup;
359 }
360
361 // Check if this is the right structure level.
362 if (Level != 1)
363 {
364 dwErrorCode = ERROR_INVALID_LEVEL;
365 goto Cleanup;
366 }
367
368 // Check if the printer is set to do direct printing.
369 // The Job List isn't used in this case.
370 if (pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT)
371 {
372 dwErrorCode = ERROR_INVALID_ACCESS;
373 goto Cleanup;
374 }
375
376 // Check if the supplied buffer is large enough.
377 *pcbNeeded = sizeof(ADDJOB_INFO_1W) + GetJobFilePath(L"SPL", 0, NULL);
378 if (cbBuf < *pcbNeeded)
379 {
380 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
381 goto Cleanup;
382 }
383
384 // All requirements are met - create a new job.
385 dwErrorCode = CreateJob(pPrinterHandle);
386 if (dwErrorCode != ERROR_SUCCESS)
387 goto Cleanup;
388
389 // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
390 pPrinterHandle->pJob->bAddedJob = TRUE;
391
392 // Return a proper ADDJOB_INFO_1W structure.
393 AddJobInfo1.JobId = pPrinterHandle->pJob->dwJobID;
394 AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
395
396 CopyMemory(pData, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
397 GetJobFilePath(L"SPL", AddJobInfo1.JobId, AddJobInfo1.Path);
398
399 Cleanup:
400 SetLastError(dwErrorCode);
401 return (dwErrorCode == ERROR_SUCCESS);
402 }
403
404
405 static DWORD
406 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE* ppStart, PBYTE* ppEnd, DWORD cbBuf, PDWORD pcbNeeded)
407 {
408 DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
409 DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
410 DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
411 DWORD cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
412 DWORD cbStatus = 0;
413 DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
414 DWORD dwErrorCode;
415 JOB_INFO_1W JobInfo1 = { 0 };
416
417 // A Status Message is optional.
418 if (pJob->pwszStatus)
419 cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
420
421 // Check if the supplied buffer is large enough.
422 *pcbNeeded += sizeof(JOB_INFO_1W) + cbDatatype + cbDocumentName + cbMachineName + cbPrinterName + cbStatus + cbUserName;
423 if (cbBuf < *pcbNeeded)
424 {
425 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
426 goto Cleanup;
427 }
428
429 // Put the strings at the end of the buffer.
430 *ppEnd -= cbDatatype;
431 JobInfo1.pDatatype = (PWSTR)*ppEnd;
432 CopyMemory(*ppEnd, pJob->pwszDatatype, cbDatatype);
433
434 *ppEnd -= cbDocumentName;
435 JobInfo1.pDocument = (PWSTR)*ppEnd;
436 CopyMemory(*ppEnd, pJob->pwszDocumentName, cbDocumentName);
437
438 *ppEnd -= cbMachineName;
439 JobInfo1.pMachineName = (PWSTR)*ppEnd;
440 CopyMemory(*ppEnd, pJob->pwszMachineName, cbMachineName);
441
442 *ppEnd -= cbPrinterName;
443 JobInfo1.pPrinterName = (PWSTR)*ppEnd;
444 CopyMemory(*ppEnd, pJob->pPrinter->pwszPrinterName, cbPrinterName);
445
446 if (cbStatus)
447 {
448 *ppEnd -= cbStatus;
449 JobInfo1.pStatus = (PWSTR)*ppEnd;
450 CopyMemory(*ppEnd, pJob->pwszStatus, cbStatus);
451 }
452
453 *ppEnd -= cbUserName;
454 JobInfo1.pUserName = (PWSTR)*ppEnd;
455 CopyMemory(*ppEnd, pJob->pwszUserName, cbUserName);
456
457 // Fill the rest of the structure.
458 JobInfo1.JobId = pJob->dwJobID;
459 JobInfo1.Priority = pJob->dwPriority;
460 JobInfo1.Status = pJob->dwStatus;
461 JobInfo1.TotalPages = pJob->dwTotalPages;
462 CopyMemory(&JobInfo1.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
463
464 // Finally copy the structure to the output pointer.
465 CopyMemory(*ppStart, &JobInfo1, sizeof(JOB_INFO_1W));
466 *ppStart += sizeof(JOB_INFO_1W);
467 dwErrorCode = ERROR_SUCCESS;
468
469 Cleanup:
470 return dwErrorCode;
471 }
472
473 static DWORD
474 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE* ppStart, PBYTE* ppEnd, DWORD cbBuf, PDWORD pcbNeeded)
475 {
476 DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
477 DWORD cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
478 DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
479 DWORD cbDriverName = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
480 DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
481 DWORD cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
482 DWORD cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
483 DWORD cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
484 DWORD cbPrintProcessorParameters = 0;
485 DWORD cbStatus = 0;
486 DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
487 DWORD dwErrorCode;
488 FILETIME ftNow;
489 FILETIME ftSubmitted;
490 JOB_INFO_2W JobInfo2 = { 0 };
491 ULARGE_INTEGER uliNow;
492 ULARGE_INTEGER uliSubmitted;
493
494 // Print Processor Parameters and Status Message are optional.
495 if (pJob->pwszPrintProcessorParameters)
496 cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
497
498 if (pJob->pwszStatus)
499 cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
500
501 // Check if the supplied buffer is large enough.
502 *pcbNeeded += sizeof(JOB_INFO_2W) + cbDatatype + cbDevMode + cbDocumentName + cbDriverName + cbMachineName + cbNotifyName + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbStatus + cbUserName;
503 if (cbBuf < *pcbNeeded)
504 {
505 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
506 goto Cleanup;
507 }
508
509 // Put the strings at the end of the buffer.
510 *ppEnd -= cbDatatype;
511 JobInfo2.pDatatype = (PWSTR)*ppEnd;
512 CopyMemory(*ppEnd, pJob->pwszDatatype, cbDatatype);
513
514 *ppEnd -= cbDevMode;
515 JobInfo2.pDevMode = (PDEVMODEW)*ppEnd;
516 CopyMemory(*ppEnd, pJob->pDevMode, cbDevMode);
517
518 *ppEnd -= cbDocumentName;
519 JobInfo2.pDocument = (PWSTR)*ppEnd;
520 CopyMemory(*ppEnd, pJob->pwszDocumentName, cbDocumentName);
521
522 *ppEnd -= cbDriverName;
523 JobInfo2.pDriverName = (PWSTR)*ppEnd;
524 CopyMemory(*ppEnd, pJob->pPrinter->pwszPrinterDriver, cbDriverName);
525
526 *ppEnd -= cbMachineName;
527 JobInfo2.pMachineName = (PWSTR)*ppEnd;
528 CopyMemory(*ppEnd, pJob->pwszMachineName, cbMachineName);
529
530 *ppEnd -= cbNotifyName;
531 JobInfo2.pNotifyName = (PWSTR)*ppEnd;
532 CopyMemory(*ppEnd, pJob->pwszNotifyName, cbNotifyName);
533
534 *ppEnd -= cbPrinterName;
535 JobInfo2.pPrinterName = (PWSTR)*ppEnd;
536 CopyMemory(*ppEnd, pJob->pPrinter->pwszPrinterName, cbPrinterName);
537
538 *ppEnd -= cbPrintProcessor;
539 JobInfo2.pPrintProcessor = (PWSTR)*ppEnd;
540 CopyMemory(*ppEnd, pJob->pPrintProcessor->pwszName, cbPrintProcessor);
541
542 if (cbPrintProcessorParameters)
543 {
544 *ppEnd -= cbPrintProcessorParameters;
545 JobInfo2.pParameters = (PWSTR)*ppEnd;
546 CopyMemory(*ppEnd, pJob->pwszPrintProcessorParameters, cbPrintProcessorParameters);
547 }
548
549 if (cbStatus)
550 {
551 *ppEnd -= cbStatus;
552 JobInfo2.pStatus = (PWSTR)*ppEnd;
553 CopyMemory(*ppEnd, pJob->pwszStatus, cbStatus);
554 }
555
556 *ppEnd -= cbUserName;
557 JobInfo2.pUserName = (PWSTR)*ppEnd;
558 CopyMemory(*ppEnd, pJob->pwszUserName, cbUserName);
559
560 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
561 if (!SystemTimeToFileTime(&pJob->stSubmitted, &ftSubmitted))
562 {
563 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
564 return FALSE;
565 }
566
567 GetSystemTimeAsFileTime(&ftNow);
568 uliSubmitted.LowPart = ftSubmitted.dwLowDateTime;
569 uliSubmitted.HighPart = ftSubmitted.dwHighDateTime;
570 uliNow.LowPart = ftNow.dwLowDateTime;
571 uliNow.HighPart = ftNow.dwHighDateTime;
572 JobInfo2.Time = (DWORD)((uliNow.QuadPart - uliSubmitted.QuadPart) / 10000);
573
574 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
575 // Retrieve this through the element index of the job in the Printer's Job List.
576 if (!LookupElementSkiplist(&pJob->pPrinter->JobList, pJob, &JobInfo2.Position))
577 {
578 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
579 ERR("pJob could not be located in the Printer's Job List!\n");
580 goto Cleanup;
581 }
582
583 // Make the index 1-based.
584 ++JobInfo2.Position;
585
586 // Fill the rest of the structure.
587 JobInfo2.JobId = pJob->dwJobID;
588 JobInfo2.PagesPrinted = pJob->dwPagesPrinted;
589 JobInfo2.Priority = pJob->dwPriority;
590 JobInfo2.StartTime = pJob->dwStartTime;
591 JobInfo2.Status = pJob->dwStatus;
592 JobInfo2.TotalPages = pJob->dwTotalPages;
593 JobInfo2.UntilTime = pJob->dwUntilTime;
594 CopyMemory(&JobInfo2.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
595
596 // Finally copy the structure to the output pointer.
597 CopyMemory(*ppStart, &JobInfo2, sizeof(JOB_INFO_2W));
598 *ppStart += sizeof(JOB_INFO_2W);
599 dwErrorCode = ERROR_SUCCESS;
600
601 Cleanup:
602 return dwErrorCode;
603 }
604
605 BOOL WINAPI
606 LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded)
607 {
608 DWORD dwErrorCode;
609 PBYTE pEnd = &pStart[cbBuf];
610 PLOCAL_HANDLE pHandle;
611 PLOCAL_JOB pJob;
612 PLOCAL_PRINTER_HANDLE pPrinterHandle;
613
614 // Check if this is a printer handle.
615 pHandle = (PLOCAL_HANDLE)hPrinter;
616 if (pHandle->HandleType != HandleType_Printer)
617 {
618 dwErrorCode = ERROR_INVALID_HANDLE;
619 goto Cleanup;
620 }
621
622 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
623
624 // Get the desired job.
625 pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
626 if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
627 {
628 dwErrorCode = ERROR_INVALID_PARAMETER;
629 goto Cleanup;
630 }
631
632 // Begin counting.
633 *pcbNeeded = 0;
634
635 // The function behaves differently for each level.
636 if (Level == 1)
637 dwErrorCode = _LocalGetJobLevel1(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
638 else if (Level == 2)
639 dwErrorCode = _LocalGetJobLevel2(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
640 else
641 dwErrorCode = ERROR_INVALID_LEVEL;
642
643 Cleanup:
644 SetLastError(dwErrorCode);
645 return (dwErrorCode == ERROR_SUCCESS);
646 }
647
648 static DWORD
649 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_1W pJobInfo)
650 {
651 DWORD dwErrorCode;
652
653 // First check the validity of the input before changing anything.
654 if (!FindDatatype(pJob->pPrintProcessor, pJobInfo->pDatatype))
655 {
656 dwErrorCode = ERROR_INVALID_DATATYPE;
657 goto Cleanup;
658 }
659
660 // Check if the datatype has changed.
661 if (wcscmp(pJob->pwszDatatype, pJobInfo->pDatatype) != 0)
662 {
663 // Use the new value.
664 if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
665 {
666 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
667 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
668 goto Cleanup;
669 }
670 }
671
672 // Check if the document name has changed.
673 if (wcscmp(pJob->pwszDocumentName, pJobInfo->pDocument) != 0)
674 {
675 // Use the new value.
676 if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
677 {
678 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
679 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
680 goto Cleanup;
681 }
682 }
683
684 // Check if the status message has changed.
685 if ((!pJob->pwszStatus && pJobInfo->pStatus) || wcscmp(pJob->pwszStatus, pJobInfo->pStatus) != 0)
686 {
687 // Use the new value.
688 if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
689 {
690 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
691 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
692 goto Cleanup;
693 }
694 }
695
696 // Check if the user name has changed.
697 if (wcscmp(pJob->pwszUserName, pJobInfo->pUserName) != 0)
698 {
699 // The new user name doesn't need to exist, so no additional verification is required.
700
701 // Use the new value.
702 if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
703 {
704 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
705 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
706 goto Cleanup;
707 }
708 }
709
710 // Check if the priority has changed.
711 if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
712 {
713 // Set the new priority.
714 pJob->dwPriority = pJobInfo->Priority;
715
716 // Remove and reinsert the job in the Printer's Job List.
717 // The Compare function will be used to find the right position now considering the new priority.
718 DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
719 InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
720 }
721
722 // Check if the status flags have changed.
723 if (pJob->dwStatus != pJobInfo->Status)
724 {
725 // Only add status flags that make sense.
726 if (pJobInfo->Status & JOB_STATUS_PAUSED)
727 pJob->dwStatus |= JOB_STATUS_PAUSED;
728
729 if (pJobInfo->Status & JOB_STATUS_ERROR)
730 pJob->dwStatus |= JOB_STATUS_ERROR;
731
732 if (pJobInfo->Status & JOB_STATUS_OFFLINE)
733 pJob->dwStatus |= JOB_STATUS_OFFLINE;
734
735 if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
736 pJob->dwStatus |= JOB_STATUS_PAPEROUT;
737 }
738
739 dwErrorCode = ERROR_SUCCESS;
740
741 Cleanup:
742 return dwErrorCode;
743 }
744
745 static DWORD
746 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_2W pJobInfo)
747 {
748 DWORD dwErrorCode;
749 PLOCAL_PRINT_PROCESSOR pPrintProcessor;
750
751 // First check the validity of the input before changing anything.
752 pPrintProcessor = FindPrintProcessor(pJobInfo->pPrintProcessor);
753 if (!pPrintProcessor)
754 {
755 dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR;
756 goto Cleanup;
757 }
758
759 if (!FindDatatype(pPrintProcessor, pJobInfo->pDatatype))
760 {
761 dwErrorCode = ERROR_INVALID_DATATYPE;
762 goto Cleanup;
763 }
764
765 // Check if the datatype has changed.
766 if (wcscmp(pJob->pwszDatatype, pJobInfo->pDatatype) != 0)
767 {
768 // Use the new value.
769 if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
770 {
771 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
772 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
773 goto Cleanup;
774 }
775 }
776
777 // Check if the document name has changed.
778 if (wcscmp(pJob->pwszDocumentName, pJobInfo->pDocument) != 0)
779 {
780 // Use the new value.
781 if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
782 {
783 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
784 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
785 goto Cleanup;
786 }
787 }
788
789 // Check if the notify name has changed.
790 if (wcscmp(pJob->pwszNotifyName, pJobInfo->pNotifyName) != 0)
791 {
792 // The new notify name doesn't need to exist, so no additional verification is required.
793
794 // Use the new value.
795 if (!ReallocSplStr(&pJob->pwszNotifyName, pJobInfo->pNotifyName))
796 {
797 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
798 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
799 goto Cleanup;
800 }
801 }
802
803 // Check if the (optional) Print Processor Parameters have changed.
804 if ((!pJob->pwszPrintProcessorParameters && pJobInfo->pParameters) || wcscmp(pJob->pwszPrintProcessorParameters, pJobInfo->pParameters) != 0)
805 {
806 // Use the new value.
807 if (!ReallocSplStr(&pJob->pwszPrintProcessorParameters, pJobInfo->pParameters))
808 {
809 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
810 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
811 goto Cleanup;
812 }
813 }
814
815 // Check if the (optional) Status Message has changed.
816 if ((!pJob->pwszStatus && pJobInfo->pStatus) || wcscmp(pJob->pwszStatus, pJobInfo->pStatus) != 0)
817 {
818 // Use the new value.
819 if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
820 {
821 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
822 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
823 goto Cleanup;
824 }
825 }
826
827 // Check if the user name has changed.
828 if (wcscmp(pJob->pwszUserName, pJobInfo->pUserName) != 0)
829 {
830 // The new user name doesn't need to exist, so no additional verification is required.
831
832 // Use the new value.
833 if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
834 {
835 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
836 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
837 goto Cleanup;
838 }
839 }
840
841 // Check if the priority has changed.
842 if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
843 {
844 // Set the new priority.
845 pJob->dwPriority = pJobInfo->Priority;
846
847 // Remove and reinsert the job in the Printer's Job List.
848 // The Compare function will be used to find the right position now considering the new priority.
849 DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
850 InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
851 }
852
853 // Check if the status flags have changed.
854 if (pJob->dwStatus != pJobInfo->Status)
855 {
856 // Only add status flags that make sense.
857 if (pJobInfo->Status & JOB_STATUS_PAUSED)
858 pJob->dwStatus |= JOB_STATUS_PAUSED;
859
860 if (pJobInfo->Status & JOB_STATUS_ERROR)
861 pJob->dwStatus |= JOB_STATUS_ERROR;
862
863 if (pJobInfo->Status & JOB_STATUS_OFFLINE)
864 pJob->dwStatus |= JOB_STATUS_OFFLINE;
865
866 if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
867 pJob->dwStatus |= JOB_STATUS_PAPEROUT;
868 }
869
870 dwErrorCode = ERROR_SUCCESS;
871
872 Cleanup:
873 return dwErrorCode;
874 }
875
876 BOOL WINAPI
877 LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
878 {
879 DWORD dwErrorCode;
880 PLOCAL_HANDLE pHandle;
881 PLOCAL_JOB pJob;
882 PLOCAL_PRINTER_HANDLE pPrinterHandle;
883 WCHAR wszFullPath[MAX_PATH];
884
885 // Check if this is a printer handle.
886 pHandle = (PLOCAL_HANDLE)hPrinter;
887 if (pHandle->HandleType != HandleType_Printer)
888 {
889 dwErrorCode = ERROR_INVALID_HANDLE;
890 goto Cleanup;
891 }
892
893 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
894
895 // Get the desired job.
896 pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
897 if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
898 {
899 dwErrorCode = ERROR_INVALID_PARAMETER;
900 goto Cleanup;
901 }
902
903 // Setting job information is handled differently for each level.
904 if (Level)
905 {
906 if (Level == 1)
907 dwErrorCode = _LocalSetJobLevel1(pPrinterHandle, pJob, (PJOB_INFO_1W)pJobInfo);
908 else if (Level == 2)
909 dwErrorCode = _LocalSetJobLevel2(pPrinterHandle, pJob, (PJOB_INFO_2W)pJobInfo);
910 else
911 dwErrorCode = ERROR_INVALID_LEVEL;
912 }
913
914 if (dwErrorCode != ERROR_SUCCESS)
915 goto Cleanup;
916
917 // If we do spooled printing, the job information is written down into a shadow file.
918 if (!(pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT))
919 {
920 // Write the job data into the shadow file.
921 GetJobFilePath(L"SHD", JobId, wszFullPath);
922 WriteJobShadowFile(wszFullPath, pJob);
923 }
924
925 // Perform an additional command if desired.
926 if (Command)
927 {
928 if (Command == JOB_CONTROL_SENT_TO_PRINTER)
929 {
930 // This indicates the end of the Print Job.
931
932 // Cancel the Job at the Print Processor.
933 if (pJob->hPrintProcessor)
934 pJob->pPrintProcessor->pfnControlPrintProcessor(pJob->hPrintProcessor, JOB_CONTROL_CANCEL);
935
936 FreeJob(pJob);
937
938 // TODO: All open handles associated with the job need to be invalidated.
939 // This certainly needs handle tracking...
940 }
941 else
942 {
943 ERR("Unimplemented SetJob Command: %lu!\n", Command);
944 }
945 }
946
947 dwErrorCode = ERROR_SUCCESS;
948
949 Cleanup:
950 SetLastError(dwErrorCode);
951 return (dwErrorCode == ERROR_SUCCESS);
952 }
953
954 BOOL WINAPI
955 LocalEnumJobs(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
956 {
957 DWORD dwErrorCode;
958 DWORD i;
959 PBYTE pEnd = &pStart[cbBuf];
960 PLOCAL_HANDLE pHandle;
961 PLOCAL_JOB pJob;
962 PSKIPLIST_NODE pFirstJobNode;
963 PSKIPLIST_NODE pNode;
964 PLOCAL_PRINTER_HANDLE pPrinterHandle;
965
966 // Check if this is a printer handle.
967 pHandle = (PLOCAL_HANDLE)hPrinter;
968 if (pHandle->HandleType != HandleType_Printer)
969 {
970 dwErrorCode = ERROR_INVALID_HANDLE;
971 goto Cleanup;
972 }
973
974 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
975
976 // Check the level.
977 if (Level > 2)
978 {
979 dwErrorCode = ERROR_INVALID_LEVEL;
980 goto Cleanup;
981 }
982
983 // Begin counting.
984 *pcbNeeded = 0;
985 *pcReturned = 0;
986
987 // Lookup the node of the first job requested by the caller in the Printer's Job List.
988 pFirstJobNode = LookupNodeByIndexSkiplist(&pPrinterHandle->pPrinter->JobList, FirstJob);
989
990 // Count the required buffer size and the number of jobs.
991 i = 0;
992 pNode = pFirstJobNode;
993
994 while (i < NoJobs && pNode)
995 {
996 pJob = (PLOCAL_JOB)pNode->Element;
997
998 // The function behaves differently for each level.
999 if (Level == 1)
1000 _LocalGetJobLevel1(pPrinterHandle, pJob, NULL, NULL, 0, pcbNeeded);
1001 else if (Level == 2)
1002 _LocalGetJobLevel2(pPrinterHandle, pJob, NULL, NULL, 0, pcbNeeded);
1003
1004 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1005 i++;
1006 pNode = pNode->Next[0];
1007 }
1008
1009 // Check if the supplied buffer is large enough.
1010 if (cbBuf < *pcbNeeded)
1011 {
1012 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1013 goto Cleanup;
1014 }
1015
1016 // Begin counting again and also empty the given buffer.
1017 *pcbNeeded = 0;
1018 ZeroMemory(pStart, cbBuf);
1019
1020 // Now call the same functions again to copy the actual data for each job into the buffer.
1021 i = 0;
1022 pNode = pFirstJobNode;
1023
1024 while (i < NoJobs && pNode)
1025 {
1026 pJob = (PLOCAL_JOB)pNode->Element;
1027
1028 // The function behaves differently for each level.
1029 if (Level == 1)
1030 dwErrorCode = _LocalGetJobLevel1(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
1031 else if (Level == 2)
1032 dwErrorCode = _LocalGetJobLevel2(pPrinterHandle, pJob, &pStart, &pEnd, cbBuf, pcbNeeded);
1033
1034 if (dwErrorCode != ERROR_SUCCESS)
1035 goto Cleanup;
1036
1037 // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1038 i++;
1039 pNode = pNode->Next[0];
1040 }
1041
1042 *pcReturned = i;
1043 dwErrorCode = ERROR_SUCCESS;
1044
1045 Cleanup:
1046 SetLastError(dwErrorCode);
1047 return (dwErrorCode == ERROR_SUCCESS);
1048 }
1049
1050 BOOL WINAPI
1051 LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
1052 {
1053 DWORD dwAttributes;
1054 DWORD dwErrorCode;
1055 HANDLE hThread;
1056 PLOCAL_JOB pJob;
1057 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1058 PLOCAL_PRINTER_HANDLE pPrinterHandle;
1059 WCHAR wszFullPath[MAX_PATH];
1060
1061 // Check if this is a printer handle.
1062 if (pHandle->HandleType != HandleType_Printer)
1063 {
1064 dwErrorCode = ERROR_INVALID_HANDLE;
1065 goto Cleanup;
1066 }
1067
1068 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1069
1070 // Check if the Job ID is valid.
1071 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
1072 if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
1073 {
1074 dwErrorCode = ERROR_INVALID_PARAMETER;
1075 goto Cleanup;
1076 }
1077
1078 // Check if this Job was started with AddJob.
1079 if (!pJob->bAddedJob)
1080 {
1081 dwErrorCode = ERROR_SPL_NO_ADDJOB;
1082 goto Cleanup;
1083 }
1084
1085 // Construct the full path to the spool file.
1086 GetJobFilePath(L"SPL", dwJobID, wszFullPath);
1087
1088 // Check if it exists.
1089 dwAttributes = GetFileAttributesW(wszFullPath);
1090 if (dwAttributes == INVALID_FILE_ATTRIBUTES || dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
1091 {
1092 dwErrorCode = ERROR_SPOOL_FILE_NOT_FOUND;
1093 goto Cleanup;
1094 }
1095
1096 // Switch from spooling to printing.
1097 pJob->dwStatus &= ~JOB_STATUS_SPOOLING;
1098 pJob->dwStatus |= JOB_STATUS_PRINTING;
1099
1100 // Write the job data into the shadow file.
1101 wcscpy(wcsrchr(wszFullPath, L'.'), L".SHD");
1102 WriteJobShadowFile(wszFullPath, pJob);
1103
1104 // Create the thread for performing the printing process.
1105 hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PrintingThreadProc, pJob, 0, NULL);
1106 if (!hThread)
1107 {
1108 dwErrorCode = GetLastError();
1109 ERR("CreateThread failed with error %lu!\n", dwErrorCode);
1110 goto Cleanup;
1111 }
1112
1113 // We don't need the thread handle. Keeping it open blocks the thread from terminating.
1114 CloseHandle(hThread);
1115
1116 // ScheduleJob has done its job. The rest happens inside the thread.
1117 dwErrorCode = ERROR_SUCCESS;
1118
1119 Cleanup:
1120 SetLastError(dwErrorCode);
1121 return (dwErrorCode == ERROR_SUCCESS);
1122 }
1123
1124 PLOCAL_JOB
1125 ReadJobShadowFile(PCWSTR pwszFilePath)
1126 {
1127 DWORD cbFileSize;
1128 DWORD cbRead;
1129 HANDLE hFile = INVALID_HANDLE_VALUE;
1130 PLOCAL_JOB pJob;
1131 PLOCAL_JOB pReturnValue = NULL;
1132 PLOCAL_PRINTER pPrinter;
1133 PLOCAL_PRINT_PROCESSOR pPrintProcessor;
1134 PSHD_HEADER pShadowFile = NULL;
1135 PWSTR pwszPrinterName;
1136 PWSTR pwszPrintProcessor;
1137
1138 // Try to open the file.
1139 hFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
1140 if (hFile == INVALID_HANDLE_VALUE)
1141 {
1142 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1143 goto Cleanup;
1144 }
1145
1146 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1147 cbFileSize = GetFileSize(hFile, NULL);
1148 pShadowFile = DllAllocSplMem(cbFileSize);
1149 if (!pShadowFile)
1150 {
1151 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1152 goto Cleanup;
1153 }
1154
1155 // Read the entire file.
1156 if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL))
1157 {
1158 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1159 goto Cleanup;
1160 }
1161
1162 // Check signature and header size.
1163 if (pShadowFile->dwSignature != SHD_WIN2003_SIGNATURE || pShadowFile->cbHeader != sizeof(SHD_HEADER))
1164 {
1165 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath);
1166 goto Cleanup;
1167 }
1168
1169 // Retrieve the associated printer from the list.
1170 pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName);
1171 pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
1172 if (!pPrinter)
1173 {
1174 ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath, pwszPrinterName);
1175 goto Cleanup;
1176 }
1177
1178 // Retrieve the associated Print Processor from the list.
1179 pwszPrintProcessor = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrintProcessor);
1180 pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
1181 if (!pPrintProcessor)
1182 {
1183 ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath, pwszPrintProcessor);
1184 goto Cleanup;
1185 }
1186
1187 // Create a new job structure and copy over the relevant fields.
1188 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
1189 if (!pJob)
1190 {
1191 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1192 goto Cleanup;
1193 }
1194
1195 pJob->dwJobID = pShadowFile->dwJobID;
1196 pJob->dwPriority = pShadowFile->dwPriority;
1197 pJob->dwStartTime = pShadowFile->dwStartTime;
1198 pJob->dwTotalPages = pShadowFile->dwTotalPages;
1199 pJob->dwUntilTime = pShadowFile->dwUntilTime;
1200 pJob->pPrinter = pPrinter;
1201 pJob->pPrintProcessor = pPrintProcessor;
1202 pJob->pDevMode = DuplicateDevMode((PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode));
1203 pJob->pwszDatatype = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype));
1204 pJob->pwszDocumentName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName));
1205 pJob->pwszMachineName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offMachineName));
1206 pJob->pwszNotifyName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offNotifyName));
1207
1208 if (pShadowFile->offPrintProcessorParameters)
1209 pJob->pwszPrintProcessorParameters = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrintProcessorParameters));
1210
1211 pJob->pwszUserName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offUserName));
1212 CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
1213
1214 // Jobs read from shadow files were always added using AddJob.
1215 pJob->bAddedJob = TRUE;
1216
1217 pReturnValue = pJob;
1218
1219 Cleanup:
1220 if (pShadowFile)
1221 DllFreeSplMem(pShadowFile);
1222
1223 if (hFile != INVALID_HANDLE_VALUE)
1224 CloseHandle(hFile);
1225
1226 return pReturnValue;
1227 }
1228
1229 BOOL
1230 WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob)
1231 {
1232 BOOL bReturnValue = FALSE;
1233 DWORD cbDatatype;
1234 DWORD cbDevMode;
1235 DWORD cbDocumentName;
1236 DWORD cbFileSize;
1237 DWORD cbMachineName;
1238 DWORD cbNotifyName;
1239 DWORD cbPrinterDriver;
1240 DWORD cbPrinterName;
1241 DWORD cbPrintProcessor;
1242 DWORD cbPrintProcessorParameters = 0;
1243 DWORD cbUserName;
1244 DWORD cbWritten;
1245 DWORD dwCurrentOffset;
1246 HANDLE hSHDFile = INVALID_HANDLE_VALUE;
1247 HANDLE hSPLFile = INVALID_HANDLE_VALUE;
1248 PSHD_HEADER pShadowFile = NULL;
1249
1250 // Try to open the SHD file.
1251 hSHDFile = CreateFileW(pwszFilePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
1252 if (hSHDFile == INVALID_HANDLE_VALUE)
1253 {
1254 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1255 goto Cleanup;
1256 }
1257
1258 // Compute the total size of the shadow file.
1259 cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
1260 cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
1261 cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
1262 cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
1263 cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
1264 cbPrinterDriver = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
1265 cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
1266 cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
1267 cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
1268
1269 // Print Processor Parameters are optional.
1270 if (pJob->pwszPrintProcessorParameters)
1271 cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
1272
1273 cbFileSize = sizeof(SHD_HEADER) + cbDatatype + cbDocumentName + cbDevMode + cbMachineName + cbNotifyName + cbPrinterDriver + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbUserName;
1274
1275 // Allocate memory for it.
1276 pShadowFile = DllAllocSplMem(cbFileSize);
1277 if (!pShadowFile)
1278 {
1279 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1280 goto Cleanup;
1281 }
1282
1283 // Fill out the shadow file header information.
1284 pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE;
1285 pShadowFile->cbHeader = sizeof(SHD_HEADER);
1286
1287 // Copy the values.
1288 pShadowFile->dwJobID = pJob->dwJobID;
1289 pShadowFile->dwPriority = pJob->dwPriority;
1290 pShadowFile->dwStartTime = pJob->dwStartTime;
1291 pShadowFile->dwTotalPages = pJob->dwTotalPages;
1292 pShadowFile->dwUntilTime = pJob->dwUntilTime;
1293 CopyMemory(&pShadowFile->stSubmitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
1294
1295 // Determine the file size of the .SPL file
1296 wcscpy(wcsrchr(pwszFilePath, L'.'), L".SPL");
1297 hSPLFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1298 if (hSPLFile != INVALID_HANDLE_VALUE)
1299 pShadowFile->dwSPLSize = GetFileSize(hSPLFile, NULL);
1300
1301 // Add the extra values that are stored as offsets in the shadow file.
1302 // The first value begins right after the shadow file header.
1303 dwCurrentOffset = sizeof(SHD_HEADER);
1304
1305 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype);
1306 pShadowFile->offDatatype = dwCurrentOffset;
1307 dwCurrentOffset += cbDatatype;
1308
1309 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDocumentName, cbDocumentName);
1310 pShadowFile->offDocumentName = dwCurrentOffset;
1311 dwCurrentOffset += cbDocumentName;
1312
1313 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pDevMode, cbDevMode);
1314 pShadowFile->offDevMode = dwCurrentOffset;
1315 dwCurrentOffset += cbDevMode;
1316
1317 // offDriverName is only written, but automatically determined through offPrinterName when reading.
1318 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterDriver, cbPrinterDriver);
1319 pShadowFile->offDriverName = dwCurrentOffset;
1320 dwCurrentOffset += cbPrinterDriver;
1321
1322 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszMachineName, cbMachineName);
1323 pShadowFile->offMachineName = dwCurrentOffset;
1324 dwCurrentOffset += cbMachineName;
1325
1326 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszNotifyName, cbNotifyName);
1327 pShadowFile->offNotifyName = dwCurrentOffset;
1328 dwCurrentOffset += cbNotifyName;
1329
1330 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterName, cbPrinterName);
1331 pShadowFile->offPrinterName = dwCurrentOffset;
1332 dwCurrentOffset += cbPrinterName;
1333
1334 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrintProcessor->pwszName, cbPrintProcessor);
1335 pShadowFile->offPrintProcessor = dwCurrentOffset;
1336 dwCurrentOffset += cbPrintProcessor;
1337
1338 if (cbPrintProcessorParameters)
1339 {
1340 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszPrintProcessorParameters, cbPrintProcessorParameters);
1341 pShadowFile->offPrintProcessorParameters = dwCurrentOffset;
1342 dwCurrentOffset += cbPrintProcessorParameters;
1343 }
1344
1345 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszUserName, cbUserName);
1346 pShadowFile->offUserName = dwCurrentOffset;
1347 dwCurrentOffset += cbUserName;
1348
1349 // Write the file.
1350 if (!WriteFile(hSHDFile, pShadowFile, cbFileSize, &cbWritten, NULL))
1351 {
1352 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1353 goto Cleanup;
1354 }
1355
1356 bReturnValue = TRUE;
1357
1358 Cleanup:
1359 if (pShadowFile)
1360 DllFreeSplMem(pShadowFile);
1361
1362 if (hSHDFile != INVALID_HANDLE_VALUE)
1363 CloseHandle(hSHDFile);
1364
1365 if (hSPLFile != INVALID_HANDLE_VALUE)
1366 CloseHandle(hSPLFile);
1367
1368 return bReturnValue;
1369 }
1370
1371 void
1372 FreeJob(PLOCAL_JOB pJob)
1373 {
1374 PWSTR pwszSHDFile;
1375
1376 // Remove the Job from both Job Lists.
1377 DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
1378 DeleteElementSkiplist(&GlobalJobList, pJob);
1379
1380 // Try to delete the corresponding .SHD file.
1381 pwszSHDFile = DllAllocSplMem(GetJobFilePath(L"SHD", 0, NULL));
1382 if (pwszSHDFile && GetJobFilePath(L"SHD", pJob->dwJobID, pwszSHDFile))
1383 DeleteFileW(pwszSHDFile);
1384
1385 // Free memory for the mandatory fields.
1386 DllFreeSplMem(pJob->pDevMode);
1387 DllFreeSplStr(pJob->pwszDatatype);
1388 DllFreeSplStr(pJob->pwszDocumentName);
1389 DllFreeSplStr(pJob->pwszMachineName);
1390 DllFreeSplStr(pJob->pwszNotifyName);
1391 DllFreeSplStr(pJob->pwszUserName);
1392
1393 // Free memory for the optional fields if they are present.
1394 if (pJob->pwszOutputFile)
1395 DllFreeSplStr(pJob->pwszOutputFile);
1396
1397 if (pJob->pwszPrintProcessorParameters)
1398 DllFreeSplStr(pJob->pwszPrintProcessorParameters);
1399
1400 if (pJob->pwszStatus)
1401 DllFreeSplStr(pJob->pwszStatus);
1402
1403 // Finally free the job structure itself.
1404 DllFreeSplMem(pJob);
1405 }