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