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