[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->Printer->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 BOOL bReturnValue = FALSE;
200 DWORD cchMachineName;
201 DWORD cchUserName;
202 PBYTE p;
203 PLOCAL_HANDLE pHandle;
204 PLOCAL_JOB pJob;
205 PLOCAL_PRINTER_HANDLE pPrinterHandle;
206 RPC_BINDING_HANDLE hServerBinding = NULL;
207 RPC_STATUS Status;
208 RPC_WSTR pwszBinding = NULL;
209 RPC_WSTR pwszMachineName = NULL;
210
211 // Check if this is a printer handle.
212 pHandle = (PLOCAL_HANDLE)hPrinter;
213 if (pHandle->HandleType != Printer)
214 goto Cleanup;
215
216 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle;
217
218 // Check if this is the right structure level.
219 if (Level != 1)
220 goto Cleanup;
221
222 // FIXME: This needs to fail if the printer is set to do direct printing.
223
224 // Check if the supplied buffer is large enough.
225 *pcbNeeded = sizeof(ADDJOB_INFO_1W) + (cchSpoolDirectory + cchPrintersPath + cchSpl + 1) * sizeof(WCHAR);
226 if (cbBuf < *pcbNeeded)
227 {
228 SetLastError(ERROR_INSUFFICIENT_BUFFER);
229 goto Cleanup;
230 }
231
232 // Create a new job.
233 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
234 if (!pJob)
235 {
236 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
237 goto Cleanup;
238 }
239
240 // Reserve an ID for this job.
241 if (!GetNextJobID(&pJob->dwJobID))
242 goto Cleanup;
243
244 // Copy over defaults to the LOCAL_JOB structure.
245 pJob->Printer = pPrinterHandle->Printer;
246 pJob->dwPriority = DEF_PRIORITY;
247 pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
248 pJob->pwszDocumentName = AllocSplStr(wszDefaultDocumentName);
249 CopyMemory(&pJob->DevMode, &pPrinterHandle->DevMode, sizeof(DEVMODEW));
250 GetSystemTime(&pJob->stSubmitted);
251
252 // Get the user name for the Job.
253 cchUserName = UNLEN + 1;
254 pJob->pwszUserName = DllAllocSplMem(cchUserName * sizeof(WCHAR));
255 if (!GetUserNameW(pJob->pwszUserName, &cchUserName))
256 {
257 ERR("GetUserNameW failed with error %lu!\n", GetLastError());
258 goto Cleanup;
259 }
260
261 // FIXME: For now, pwszNotifyName equals pwszUserName.
262 pJob->pwszNotifyName = AllocSplStr(pJob->pwszUserName);
263
264 // Get the name of the machine that submitted the Job over RPC.
265 Status = RpcBindingServerFromClient(NULL, &hServerBinding);
266 if (Status != RPC_S_OK)
267 {
268 ERR("RpcBindingServerFromClient failed with status %lu!\n", Status);
269 goto Cleanup;
270 }
271
272 Status = RpcBindingToStringBindingW(hServerBinding, &pwszBinding);
273 if (Status != RPC_S_OK)
274 {
275 ERR("RpcBindingToStringBindingW failed with status %lu!\n", Status);
276 goto Cleanup;
277 }
278
279 Status = RpcStringBindingParseW(pwszBinding, NULL, NULL, &pwszMachineName, NULL, NULL);
280 if (Status != RPC_S_OK)
281 {
282 ERR("RpcStringBindingParseW failed with status %lu!\n", Status);
283 goto Cleanup;
284 }
285
286 cchMachineName = wcslen(pwszMachineName);
287 pJob->pwszMachineName = DllAllocSplMem((cchMachineName + cchDoubleBackslash + 1) * sizeof(WCHAR));
288 CopyMemory(pJob->pwszMachineName, wszDoubleBackslash, cchDoubleBackslash * sizeof(WCHAR));
289 CopyMemory(pJob->pwszMachineName + cchDoubleBackslash, pwszMachineName, (cchMachineName + 1) * sizeof(WCHAR));
290
291 // Add the job to the Global Job List.
292 if (!InsertElementSkiplist(&GlobalJobList, pJob))
293 {
294 ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
295 goto Cleanup;
296 }
297
298 // Add the job at the end of the Printer's Job List.
299 // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
300 if (!InsertTailElementSkiplist(&pJob->Printer->JobList, pJob))
301 {
302 ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
303 goto Cleanup;
304 }
305
306 // Return a proper ADDJOB_INFO_1W structure.
307 AddJobInfo1.JobId = pJob->dwJobID;
308 AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
309 p = pData;
310 CopyMemory(p, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
311 p += sizeof(ADDJOB_INFO_1W);
312 CopyMemory(p, wszSpoolDirectory, cchSpoolDirectory);
313 p += cchSpoolDirectory;
314 CopyMemory(p, wszPrintersPath, cchPrintersPath);
315 p += cchPrintersPath;
316 swprintf((PWSTR)p, L"%05lu.SPL", pJob->dwJobID);
317
318 bReturnValue = TRUE;
319
320 Cleanup:
321 if (pwszMachineName)
322 RpcStringFreeW(&pwszMachineName);
323
324 if (pwszBinding)
325 RpcStringFreeW(&pwszBinding);
326
327 if (hServerBinding)
328 RpcBindingFree(&hServerBinding);
329
330 return bReturnValue;
331 }
332
333
334 static BOOL
335 _LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE pOutput, DWORD cbBuf, PDWORD pcbNeeded)
336 {
337 DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
338 DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
339 DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
340 DWORD cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
341 DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
342 JOB_INFO_1W JobInfo1 = { 0 };
343 PBYTE pString;
344
345 // Check if the supplied buffer is large enough.
346 *pcbNeeded = sizeof(JOB_INFO_1W) + cbDatatype + cbDocumentName + cbMachineName + cbPrinterName + cbUserName;
347 if (cbBuf < *pcbNeeded)
348 {
349 SetLastError(ERROR_INSUFFICIENT_BUFFER);
350 return FALSE;
351 }
352
353 // Put the strings right after the JOB_INFO_1W structure.
354 pString = pOutput + sizeof(JOB_INFO_1W);
355
356 JobInfo1.pDatatype = (PWSTR)pString;
357 CopyMemory(pString, pJob->pwszDatatype, cbDatatype);
358 pString += cbDatatype;
359
360 JobInfo1.pDocument = (PWSTR)pString;
361 CopyMemory(pString, pJob->pwszDocumentName, cbDocumentName);
362 pString += cbDocumentName;
363
364 JobInfo1.pMachineName = (PWSTR)pString;
365 CopyMemory(pString, pJob->pwszMachineName, cbMachineName);
366 pString += cbMachineName;
367
368 JobInfo1.pPrinterName = (PWSTR)pString;
369 CopyMemory(pString, pJob->Printer->pwszPrinterName, cbPrinterName);
370 pString += cbPrinterName;
371
372 JobInfo1.pUserName = (PWSTR)pString;
373 CopyMemory(pString, pJob->pwszUserName, cbUserName);
374 pString += cbUserName;
375
376 // Fill the structure and copy it as well.
377 JobInfo1.JobId = pJob->dwJobID;
378 JobInfo1.Priority = pJob->dwPriority;
379 JobInfo1.Status = pJob->dwStatus;
380 JobInfo1.TotalPages = pJob->dwTotalPages;
381 CopyMemory(&JobInfo1.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
382 CopyMemory(pOutput, &JobInfo1, sizeof(JOB_INFO_1W));
383
384 return TRUE;
385 }
386
387 static BOOL
388 _LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE pOutput, DWORD cbBuf, PDWORD pcbNeeded)
389 {
390 DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
391 DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
392 DWORD cbDriverName = (wcslen(pJob->Printer->pwszPrinterDriver) + 1) * sizeof(WCHAR);
393 DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
394 DWORD cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
395 DWORD cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
396 DWORD cbPrintProcessor = (wcslen(pJob->Printer->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
397 DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
398 FILETIME ftNow;
399 FILETIME ftSubmitted;
400 JOB_INFO_2W JobInfo2 = { 0 };
401 PBYTE pString;
402 ULARGE_INTEGER uliNow;
403 ULARGE_INTEGER uliSubmitted;
404
405 // Check if the supplied buffer is large enough.
406 *pcbNeeded = sizeof(JOB_INFO_2W) + cbDatatype + sizeof(DEVMODEW) + cbDocumentName + cbDriverName + cbMachineName + cbNotifyName + cbPrinterName + cbPrintProcessor + cbUserName;
407 if (cbBuf < *pcbNeeded)
408 {
409 SetLastError(ERROR_INSUFFICIENT_BUFFER);
410 return FALSE;
411 }
412
413 // Put the strings right after the JOB_INFO_2W structure.
414 pString = pOutput + sizeof(JOB_INFO_2W);
415
416 JobInfo2.pDatatype = (PWSTR)pString;
417 CopyMemory(pString, pJob->pwszDatatype, cbDatatype);
418 pString += cbDatatype;
419
420 JobInfo2.pDevMode = (PDEVMODEW)pString;
421 CopyMemory(pString, &pJob->DevMode, sizeof(DEVMODEW));
422 pString += sizeof(DEVMODEW);
423
424 JobInfo2.pDocument = (PWSTR)pString;
425 CopyMemory(pString, pJob->pwszDocumentName, cbDocumentName);
426 pString += cbDocumentName;
427
428 JobInfo2.pDriverName = (PWSTR)pString;
429 CopyMemory(pString, pJob->Printer->pwszPrinterDriver, cbDriverName);
430 pString += cbDriverName;
431
432 JobInfo2.pMachineName = (PWSTR)pString;
433 CopyMemory(pString, pJob->pwszMachineName, cbMachineName);
434 pString += cbMachineName;
435
436 JobInfo2.pNotifyName = (PWSTR)pString;
437 CopyMemory(pString, pJob->pwszNotifyName, cbNotifyName);
438 pString += cbNotifyName;
439
440 JobInfo2.pPrinterName = (PWSTR)pString;
441 CopyMemory(pString, pJob->Printer->pwszPrinterName, cbPrinterName);
442 pString += cbPrinterName;
443
444 JobInfo2.pPrintProcessor = (PWSTR)pString;
445 CopyMemory(pString, pJob->Printer->pPrintProcessor->pwszName, cbPrintProcessor);
446 pString += cbPrintProcessor;
447
448 JobInfo2.pUserName = (PWSTR)pString;
449 CopyMemory(pString, pJob->pwszUserName, cbUserName);
450 pString += cbUserName;
451
452 // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
453 if (!SystemTimeToFileTime(&pJob->stSubmitted, &ftSubmitted))
454 {
455 ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
456 return FALSE;
457 }
458
459 GetSystemTimeAsFileTime(&ftNow);
460 uliSubmitted.LowPart = ftSubmitted.dwLowDateTime;
461 uliSubmitted.HighPart = ftSubmitted.dwHighDateTime;
462 uliNow.LowPart = ftNow.dwLowDateTime;
463 uliNow.HighPart = ftNow.dwHighDateTime;
464 JobInfo2.Time = (DWORD)((uliNow.QuadPart - uliSubmitted.QuadPart) / 10000);
465
466 // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
467 // Retrieve this through the element index of the job in the Printer's Job List.
468 if (!LookupElementSkiplist(&pJob->Printer->JobList, pJob, &JobInfo2.Position))
469 {
470 ERR("pJob could not be located in the Printer's Job List!\n");
471 return FALSE;
472 }
473
474 // Make the index 1-based.
475 ++JobInfo2.Position;
476
477 // Fill the rest of the structure.
478 JobInfo2.JobId = pJob->dwJobID;
479 JobInfo2.PagesPrinted = pJob->dwPagesPrinted;
480 JobInfo2.Priority = pJob->dwPriority;
481 JobInfo2.StartTime = pJob->dwStartTime;
482 JobInfo2.Status = pJob->dwStatus;
483 JobInfo2.TotalPages = pJob->dwTotalPages;
484 JobInfo2.UntilTime = pJob->dwUntilTime;
485 CopyMemory(&JobInfo2.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
486
487 // Finally copy the structure to the output pointer.
488 CopyMemory(pOutput, &JobInfo2, sizeof(JOB_INFO_2W));
489 return TRUE;
490 }
491
492 BOOL WINAPI
493 LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pOutput, DWORD cbBuf, LPDWORD pcbNeeded)
494 {
495 PLOCAL_HANDLE pHandle;
496 PLOCAL_JOB pJob;
497 PLOCAL_PRINTER_HANDLE pPrinterHandle;
498
499 // Check if this is a printer handle.
500 pHandle = (PLOCAL_HANDLE)hPrinter;
501 if (pHandle->HandleType != Printer)
502 return FALSE;
503
504 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle;
505
506 // Get the desired job.
507 pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
508 if (!pJob || pJob->Printer != pPrinterHandle->Printer)
509 {
510 SetLastError(ERROR_INVALID_PARAMETER);
511 return FALSE;
512 }
513
514 // The function behaves differently for each level.
515 if (Level == 1)
516 return _LocalGetJobLevel1(pPrinterHandle, pJob, pOutput, cbBuf, pcbNeeded);
517 else if (Level == 2)
518 return _LocalGetJobLevel2(pPrinterHandle, pJob, pOutput, cbBuf, pcbNeeded);
519
520 return FALSE;
521 }
522
523 PLOCAL_JOB
524 ReadJobShadowFile(PCWSTR pwszFilePath)
525 {
526 DWORD cbFileSize;
527 DWORD cbRead;
528 HANDLE hFile = INVALID_HANDLE_VALUE;
529 PLOCAL_JOB pJob;
530 PLOCAL_JOB pReturnValue = NULL;
531 PLOCAL_PRINTER pPrinter;
532 PSHD_HEADER pShadowFile = NULL;
533 PWSTR pwszPrinterName;
534
535 // Try to open the file.
536 hFile = CreateFileW(pwszFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
537 if (hFile == INVALID_HANDLE_VALUE)
538 {
539 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
540 goto Cleanup;
541 }
542
543 // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
544 cbFileSize = GetFileSize(hFile, NULL);
545 pShadowFile = DllAllocSplMem(cbFileSize);
546 if (!pShadowFile)
547 {
548 ERR("DllAllocSplMem failed with error %lufor file \"%S\"!\n", GetLastError(), pwszFilePath);
549 goto Cleanup;
550 }
551
552 // Read the entire file.
553 if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL))
554 {
555 ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
556 goto Cleanup;
557 }
558
559 // Check signature and header size.
560 if (pShadowFile->dwSignature != SHD_WIN2003_SIGNATURE || pShadowFile->cbHeader != sizeof(SHD_HEADER))
561 {
562 ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath);
563 goto Cleanup;
564 }
565
566 // Retrieve the associated printer from the list.
567 pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName);
568 pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
569 if (!pPrinter)
570 {
571 ERR("Shadow file \"%S\" references a non-existing printer!\n", pwszFilePath);
572 goto Cleanup;
573 }
574
575 // Create a new job structure and copy over the relevant fields.
576 pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
577 if (!pJob)
578 {
579 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
580 goto Cleanup;
581 }
582
583 pJob->dwJobID = pShadowFile->dwJobID;
584 pJob->dwTotalPages = pShadowFile->dwTotalPages;
585 pJob->dwPriority = pShadowFile->dwPriority;
586 pJob->Printer = pPrinter;
587 pJob->pwszDatatype = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype));
588 pJob->pwszDocumentName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName));
589 pJob->pwszOutputFile = NULL;
590 CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
591 CopyMemory(&pJob->DevMode, (PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode), sizeof(DEVMODEW));
592
593 pReturnValue = pJob;
594
595 Cleanup:
596 if (pShadowFile)
597 DllFreeSplMem(pShadowFile);
598
599 if (hFile != INVALID_HANDLE_VALUE)
600 CloseHandle(hFile);
601
602 return pReturnValue;
603 }
604
605 BOOL
606 WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob)
607 {
608 BOOL bReturnValue = FALSE;
609 DWORD cbDatatype;
610 DWORD cbDocumentName;
611 DWORD cbFileSize;
612 DWORD cbPrinterName;
613 DWORD cbWritten;
614 DWORD dwCurrentOffset;
615 HANDLE hFile = INVALID_HANDLE_VALUE;
616 PSHD_HEADER pShadowFile = NULL;
617
618 // Try to open the file.
619 hFile = CreateFileW(pwszFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
620 if (hFile == INVALID_HANDLE_VALUE)
621 {
622 ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
623 goto Cleanup;
624 }
625
626 // Compute the total size of the shadow file.
627 cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
628 cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
629 cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
630 cbFileSize = sizeof(SHD_HEADER) + cbPrinterName + cbDatatype + cbDocumentName + sizeof(DEVMODEW);
631
632 // Allocate memory for it.
633 pShadowFile = DllAllocSplMem(cbFileSize);
634 if (!pShadowFile)
635 {
636 ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
637 goto Cleanup;
638 }
639
640 // Fill out the shadow file header information.
641 pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE;
642 pShadowFile->cbHeader = sizeof(SHD_HEADER);
643
644 // Copy the values.
645 pShadowFile->dwJobID = pJob->dwJobID;
646 pShadowFile->dwTotalPages = pJob->dwTotalPages;
647 pShadowFile->dwPriority = pJob->dwPriority;
648 CopyMemory(&pShadowFile->stSubmitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
649
650 // Add the extra values that are stored as offsets in the shadow file.
651 // The first value begins right after the shadow file header.
652 dwCurrentOffset = sizeof(SHD_HEADER);
653
654 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->Printer->pwszPrinterName, cbPrinterName);
655 pShadowFile->offPrinterName = dwCurrentOffset;
656 dwCurrentOffset += cbPrinterName;
657
658 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype);
659 pShadowFile->offDatatype = dwCurrentOffset;
660 dwCurrentOffset += cbDatatype;
661
662 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDocumentName, cbDocumentName);
663 pShadowFile->offDocumentName = dwCurrentOffset;
664 dwCurrentOffset += cbDocumentName;
665
666 CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, &pJob->DevMode, sizeof(DEVMODEW));
667 pShadowFile->offDevMode = dwCurrentOffset;
668 dwCurrentOffset += sizeof(DEVMODEW);
669
670 // Write the file.
671 if (!WriteFile(hFile, pShadowFile, cbFileSize, &cbWritten, NULL))
672 {
673 ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
674 goto Cleanup;
675 }
676
677 bReturnValue = TRUE;
678
679 Cleanup:
680 if (pShadowFile)
681 DllFreeSplMem(pShadowFile);
682
683 if (hFile != INVALID_HANDLE_VALUE)
684 CloseHandle(hFile);
685
686 return bReturnValue;
687 }
688
689 BOOL
690 FreeJob(PLOCAL_JOB pJob)
691 {
692 ////////// TODO /////////
693 /// Add some checks
694 DllFreeSplStr(pJob->pwszDatatype);
695 DllFreeSplStr(pJob->pwszDocumentName);
696 DllFreeSplStr(pJob->pwszOutputFile);
697 DllFreeSplMem(pJob);
698
699 return TRUE;
700 }