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