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