ReactOS  0.4.12-dev-712-ge6be187
jobs.c
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS Local Spooler
3  * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE: Functions for managing print jobs
5  * COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 // Global Variables
12 
13 // Local Variables
15 
16 // Local Constants
18  FIELD_OFFSET(JOB_INFO_1W, pPrinterName),
19  FIELD_OFFSET(JOB_INFO_1W, pMachineName),
20  FIELD_OFFSET(JOB_INFO_1W, pUserName),
21  FIELD_OFFSET(JOB_INFO_1W, pDocument),
22  FIELD_OFFSET(JOB_INFO_1W, pDatatype),
23  FIELD_OFFSET(JOB_INFO_1W, pStatus),
24  MAXDWORD
25 };
26 
28  FIELD_OFFSET(JOB_INFO_2W, pPrinterName),
29  FIELD_OFFSET(JOB_INFO_2W, pMachineName),
30  FIELD_OFFSET(JOB_INFO_2W, pUserName),
31  FIELD_OFFSET(JOB_INFO_2W, pDocument),
32  FIELD_OFFSET(JOB_INFO_2W, pNotifyName),
33  FIELD_OFFSET(JOB_INFO_2W, pDatatype),
34  FIELD_OFFSET(JOB_INFO_2W, pPrintProcessor),
35  FIELD_OFFSET(JOB_INFO_2W, pParameters),
36  FIELD_OFFSET(JOB_INFO_2W, pDriverName),
37  FIELD_OFFSET(JOB_INFO_2W, pStatus),
38  MAXDWORD
39 };
40 
41 
57 static __inline BOOL
59 {
60  if (!pwszA && !pwszB)
61  return TRUE;
62 
63  if (pwszA && !pwszB)
64  return FALSE;
65 
66  if (!pwszA && pwszB)
67  return FALSE;
68 
69  return (wcscmp(pwszA, pwszB) == 0);
70 }
71 
72 static BOOL
74 {
75  ++_dwLastJobID;
76 
78  {
79  // This ID is already taken. Try the next one.
80  ++_dwLastJobID;
81  }
82 
84  {
85  ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
86  return FALSE;
87  }
88 
89  *dwJobID = _dwLastJobID;
90  return TRUE;
91 }
92 
99 static int WINAPI
101 {
104 
105  return A->dwJobID - B->dwJobID;
106 }
107 
114 static int WINAPI
116 {
119  int iComparison;
120  FILETIME ftSubmittedA;
121  FILETIME ftSubmittedB;
122 
123  // First compare the priorities to determine the order.
124  // The job with a higher priority shall come first.
125  iComparison = A->dwPriority - B->dwPriority;
126  if (iComparison != 0)
127  return iComparison;
128 
129  // Both have the same priority, so go by creation time.
130  if (!SystemTimeToFileTime(&A->stSubmitted, &ftSubmittedA))
131  {
132  ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
133  return 0;
134  }
135 
136  if (!SystemTimeToFileTime(&B->stSubmitted, &ftSubmittedB))
137  {
138  ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
139  return 0;
140  }
141 
142  return CompareFileTime(&ftSubmittedA, &ftSubmittedB);
143 }
144 
145 DWORD
146 GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
147 {
148  TRACE("GetJobFilePath(%S, %lu, %p)\n", pwszExtension, dwJobID, pwszOutput);
149 
150  if (pwszOutput)
151  {
152  CopyMemory(pwszOutput, wszJobDirectory, cchJobDirectory * sizeof(WCHAR));
153  swprintf(&pwszOutput[cchJobDirectory], L"\\%05lu.%s", dwJobID, pwszExtension);
154  }
155 
156  // pwszExtension may be L"SPL" or L"SHD", same length for both!
157  return (cchJobDirectory + sizeof("\\?????.SPL")) * sizeof(WCHAR);
158 }
159 
160 BOOL
162 {
163  const WCHAR wszPath[] = L"\\?????.SHD";
164  const DWORD cchPath = _countof(wszPath) - 1;
165 
166  DWORD dwErrorCode;
167  DWORD dwJobID;
168  HANDLE hFind;
169  PLOCAL_JOB pJob = NULL;
170  PWSTR p;
171  WCHAR wszFullPath[MAX_PATH];
172  WIN32_FIND_DATAW FindData;
173 
174  TRACE("InitializeGlobalJobList()\n");
175 
176  // This one is incremented in _GetNextJobID.
177  _dwLastJobID = 0;
178 
179  // Initialize an empty list for all jobs of all local printers.
180  // We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
182 
183  // Construct the full path search pattern.
184  CopyMemory(wszFullPath, wszJobDirectory, cchJobDirectory * sizeof(WCHAR));
185  CopyMemory(&wszFullPath[cchJobDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
186 
187  // Use the search pattern to look for unfinished jobs serialized in shadow files (.SHD)
188  hFind = FindFirstFileW(wszFullPath, &FindData);
189  if (hFind == INVALID_HANDLE_VALUE)
190  {
191  // No unfinished jobs found.
192  dwErrorCode = ERROR_SUCCESS;
193  goto Cleanup;
194  }
195 
196  do
197  {
198  // Skip possible subdirectories.
199  if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
200  continue;
201 
202  // Extract the Job ID and verify the file name format at the same time.
203  // This includes all valid names (like "00005.SHD") and excludes invalid ones (like "10ABC.SHD").
204  dwJobID = wcstoul(FindData.cFileName, &p, 10);
205  if (!IS_VALID_JOB_ID(dwJobID))
206  continue;
207 
208  if (wcsicmp(p, L".SHD") != 0)
209  continue;
210 
211  // This shadow file has a valid name. Construct the full path and try to load it.
212  GetJobFilePath(L"SHD", dwJobID, wszFullPath);
213  pJob = ReadJobShadowFile(wszFullPath);
214  if (!pJob)
215  continue;
216 
217  // Add it to the Global Job List.
219  {
220  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
221  ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
222  goto Cleanup;
223  }
224 
225  // Add it to the Printer's Job List.
226  if (!InsertElementSkiplist(&pJob->pPrinter->JobList, pJob))
227  {
228  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
229  ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
230  goto Cleanup;
231  }
232  }
233  while (FindNextFileW(hFind, &FindData));
234 
235  dwErrorCode = ERROR_SUCCESS;
236 
237 Cleanup:
238  // Outside the loop
239  if (hFind)
240  FindClose(hFind);
241 
242  SetLastError(dwErrorCode);
243  return (dwErrorCode == ERROR_SUCCESS);
244 }
245 
246 void
248 {
249  TRACE("InitializePrinterJobList(%p)\n", pPrinter);
250 
251  // Initialize an empty list for this printer's jobs.
252  // This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
254 }
255 
258 {
259  const WCHAR wszDoubleBackslash[] = L"\\";
260  const DWORD cchDoubleBackslash = _countof(wszDoubleBackslash) - 1;
261 
262  DWORD cchMachineName;
263  DWORD cchUserName;
264  DWORD dwErrorCode;
265  PLOCAL_JOB pJob;
266  RPC_BINDING_HANDLE hServerBinding = NULL;
267  RPC_WSTR pwszBinding = NULL;
268  RPC_WSTR pwszMachineName = NULL;
269 
270  TRACE("CreateJob(%p)\n", pPrinterHandle);
271 
272  // Create a new job.
273  pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
274  if (!pJob)
275  {
276  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
277  ERR("DllAllocSplMem failed!\n");
278  goto Cleanup;
279  }
280 
281  // Reserve an ID for this job.
282  if (!_GetNextJobID(&pJob->dwJobID))
283  {
284  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
285  goto Cleanup;
286  }
287 
288  // Copy over defaults to the LOCAL_JOB structure.
289  pJob->pPrinter = pPrinterHandle->pPrinter;
290  pJob->pPrintProcessor = pPrinterHandle->pPrinter->pPrintProcessor;
291  pJob->dwPriority = DEF_PRIORITY;
293  pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
295  pJob->pDevMode = DuplicateDevMode(pPrinterHandle->pDevMode);
296  GetSystemTime(&pJob->stSubmitted);
297 
298  // Get the user name for the Job.
299  cchUserName = UNLEN + 1;
300  pJob->pwszUserName = DllAllocSplMem(cchUserName * sizeof(WCHAR));
301  if (!GetUserNameW(pJob->pwszUserName, &cchUserName))
302  {
303  dwErrorCode = GetLastError();
304  ERR("GetUserNameW failed with error %lu!\n", dwErrorCode);
305  goto Cleanup;
306  }
307 
308  // FIXME: For now, pwszNotifyName equals pwszUserName.
309  pJob->pwszNotifyName = AllocSplStr(pJob->pwszUserName);
310 
311  // Get the name of the machine that submitted the Job over RPC.
312  dwErrorCode = RpcBindingServerFromClient(NULL, &hServerBinding);
313  if (dwErrorCode != RPC_S_OK)
314  {
315  ERR("RpcBindingServerFromClient failed with status %lu!\n", dwErrorCode);
316  goto Cleanup;
317  }
318 
319  dwErrorCode = RpcBindingToStringBindingW(hServerBinding, &pwszBinding);
320  if (dwErrorCode != RPC_S_OK)
321  {
322  ERR("RpcBindingToStringBindingW failed with status %lu!\n", dwErrorCode);
323  goto Cleanup;
324  }
325 
326  dwErrorCode = RpcStringBindingParseW(pwszBinding, NULL, NULL, &pwszMachineName, NULL, NULL);
327  if (dwErrorCode != RPC_S_OK)
328  {
329  ERR("RpcStringBindingParseW failed with status %lu!\n", dwErrorCode);
330  goto Cleanup;
331  }
332 
333  cchMachineName = wcslen(pwszMachineName);
334  pJob->pwszMachineName = DllAllocSplMem((cchMachineName + cchDoubleBackslash + 1) * sizeof(WCHAR));
335  CopyMemory(pJob->pwszMachineName, wszDoubleBackslash, cchDoubleBackslash * sizeof(WCHAR));
336  CopyMemory(&pJob->pwszMachineName[cchDoubleBackslash], pwszMachineName, (cchMachineName + 1) * sizeof(WCHAR));
337 
338  // Add the job to the Global Job List.
340  {
341  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
342  ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
343  goto Cleanup;
344  }
345 
346  // Add the job at the end of the Printer's Job List.
347  // As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
348  if (!InsertTailElementSkiplist(&pJob->pPrinter->JobList, pJob))
349  {
350  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
351  ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
352  goto Cleanup;
353  }
354 
355  // We were successful!
356  pPrinterHandle->bStartedDoc = TRUE;
357  pPrinterHandle->pJob = pJob;
358  dwErrorCode = ERROR_SUCCESS;
359 
360  // Don't let the cleanup routine free this.
361  pJob = NULL;
362 
363 Cleanup:
364  if (pJob)
365  DllFreeSplMem(pJob);
366 
367  if (pwszMachineName)
368  RpcStringFreeW(&pwszMachineName);
369 
370  if (pwszBinding)
371  RpcStringFreeW(&pwszBinding);
372 
373  if (hServerBinding)
374  RpcBindingFree(&hServerBinding);
375 
376  return dwErrorCode;
377 }
378 
379 BOOL WINAPI
381 {
382  ADDJOB_INFO_1W AddJobInfo1;
383  DWORD dwErrorCode;
384  PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
385  PLOCAL_PRINTER_HANDLE pPrinterHandle;
386 
387  TRACE("LocalAddJob(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pData, cbBuf, pcbNeeded);
388 
389  // Check if this is a printer handle.
390  if (pHandle->HandleType != HandleType_Printer)
391  {
392  dwErrorCode = ERROR_INVALID_HANDLE;
393  goto Cleanup;
394  }
395 
396  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
397 
398  // This handle must not have started a job yet!
399  if (pPrinterHandle->pJob)
400  {
401  dwErrorCode = ERROR_INVALID_HANDLE;
402  goto Cleanup;
403  }
404 
405  // Check if this is the right structure level.
406  if (Level != 1)
407  {
408  dwErrorCode = ERROR_INVALID_LEVEL;
409  goto Cleanup;
410  }
411 
412  // Check if the printer is set to do direct printing.
413  // The Job List isn't used in this case.
414  if (pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT)
415  {
416  dwErrorCode = ERROR_INVALID_ACCESS;
417  goto Cleanup;
418  }
419 
420  // Check if the supplied buffer is large enough.
421  *pcbNeeded = sizeof(ADDJOB_INFO_1W) + GetJobFilePath(L"SPL", 0, NULL);
422  if (cbBuf < *pcbNeeded)
423  {
424  dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
425  goto Cleanup;
426  }
427 
428  // All requirements are met - create a new job.
429  dwErrorCode = CreateJob(pPrinterHandle);
430  if (dwErrorCode != ERROR_SUCCESS)
431  goto Cleanup;
432 
433  // Mark that this job was started with AddJob (so that it can be scheduled for printing with ScheduleJob).
434  pPrinterHandle->pJob->bAddedJob = TRUE;
435 
436  // Return a proper ADDJOB_INFO_1W structure.
437  AddJobInfo1.JobId = pPrinterHandle->pJob->dwJobID;
438  AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
439 
440  CopyMemory(pData, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
441  GetJobFilePath(L"SPL", AddJobInfo1.JobId, AddJobInfo1.Path);
442 
443 Cleanup:
444  SetLastError(dwErrorCode);
445  return (dwErrorCode == ERROR_SUCCESS);
446 }
447 
448 
449 static void
451 {
452  DWORD cbDatatype;
453  DWORD cbDocumentName = 0;
454  DWORD cbMachineName;
455  DWORD cbPrinterName;
456  DWORD cbStatus = 0;
457  DWORD cbUserName = 0;
458  PCWSTR pwszStrings[6];
459 
460  // Calculate the string lengths.
461  if (!ppJobInfo)
462  {
463  cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
464  cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
465  cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
466 
467  // These values are optional.
468  if (pJob->pwszDocumentName)
469  cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
470 
471  if (pJob->pwszStatus)
472  cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
473 
474  if (pJob->pwszUserName)
475  cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
476 
477  *pcbNeeded += sizeof(JOB_INFO_1W) + cbDatatype + cbDocumentName + cbMachineName + cbPrinterName + cbStatus + cbUserName;
478  return;
479  }
480 
481  // Set the general fields.
482  (*ppJobInfo)->JobId = pJob->dwJobID;
483  (*ppJobInfo)->Status = pJob->dwStatus;
484  (*ppJobInfo)->Priority = pJob->dwPriority;
485  (*ppJobInfo)->TotalPages = pJob->dwTotalPages;
486  (*ppJobInfo)->PagesPrinted = pJob->dwPagesPrinted;
487  CopyMemory(&(*ppJobInfo)->Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
488 
489  // Position in JOB_INFO_1W is the 1-based index of the job in the processing queue.
490  // Retrieve this through the element index of the job in the Printer's Job List.
491  if (!LookupElementSkiplist(&pJob->pPrinter->JobList, pJob, &(*ppJobInfo)->Position))
492  {
493  ERR("pJob could not be located in the Printer's Job List!\n");
494  return;
495  }
496 
497  // Make the index 1-based.
498  ++(*ppJobInfo)->Position;
499 
500  // Set the pPrinterName field.
501  pwszStrings[0] = pJob->pPrinter->pwszPrinterName;
502 
503  // Set the pMachineName field.
504  pwszStrings[1] = pJob->pwszMachineName;
505 
506  // Set the pUserName field.
507  pwszStrings[2] = pJob->pwszUserName;
508 
509  // Set the pDocument field.
510  pwszStrings[3] = pJob->pwszDocumentName;
511 
512  // Set the pDatatype field.
513  pwszStrings[4] = pJob->pwszDatatype;
514 
515  // Set the pStatus field.
516  pwszStrings[5] = pJob->pwszStatus;
517 
518  // Finally copy the structure and advance to the next one in the output buffer.
519  *ppJobInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppJobInfo), dwJobInfo1Offsets, *ppJobInfoEnd);
520  (*ppJobInfo)++;
521 }
522 
523 static void
525 {
526  DWORD cbDatatype;
527  DWORD cbDevMode;
528  DWORD cbDocumentName = 0;
529  DWORD cbDriverName;
530  DWORD cbMachineName;
531  DWORD cbNotifyName = 0;
532  DWORD cbPrinterName;
533  DWORD cbPrintProcessor;
534  DWORD cbPrintProcessorParameters = 0;
535  DWORD cbStatus = 0;
536  DWORD cbUserName = 0;
537  FILETIME ftNow;
538  FILETIME ftSubmitted;
539  PCWSTR pwszStrings[10];
540  ULARGE_INTEGER uliNow;
541  ULARGE_INTEGER uliSubmitted;
542 
543  // Calculate the string lengths.
544  cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
545 
546  if (!ppJobInfo)
547  {
548  cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
549  cbDriverName = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
550  cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
551  cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
552  cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
553 
554  // These values are optional.
555  if (pJob->pwszDocumentName)
556  cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
557 
558  if (pJob->pwszNotifyName)
559  cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
560 
562  cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
563 
564  if (pJob->pwszStatus)
565  cbStatus = (wcslen(pJob->pwszStatus) + 1) * sizeof(WCHAR);
566 
567  if (pJob->pwszUserName)
568  cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
569 
570  *pcbNeeded += sizeof(JOB_INFO_2W) + cbDatatype + cbDevMode + cbDocumentName + cbDriverName + cbMachineName + cbNotifyName + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbStatus + cbUserName;
571  return;
572  }
573 
574  // Set the general fields.
575  (*ppJobInfo)->JobId = pJob->dwJobID;
576  (*ppJobInfo)->Status = pJob->dwStatus;
577  (*ppJobInfo)->Priority = pJob->dwPriority;
578  (*ppJobInfo)->StartTime = pJob->dwStartTime;
579  (*ppJobInfo)->UntilTime = pJob->dwUntilTime;
580  (*ppJobInfo)->TotalPages = pJob->dwTotalPages;
581  (*ppJobInfo)->PagesPrinted = pJob->dwPagesPrinted;
582  CopyMemory(&(*ppJobInfo)->Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
583 
584  // Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
585  if (!SystemTimeToFileTime(&pJob->stSubmitted, &ftSubmitted))
586  {
587  ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
588  return;
589  }
590 
591  GetSystemTimeAsFileTime(&ftNow);
592  uliSubmitted.LowPart = ftSubmitted.dwLowDateTime;
593  uliSubmitted.HighPart = ftSubmitted.dwHighDateTime;
594  uliNow.LowPart = ftNow.dwLowDateTime;
595  uliNow.HighPart = ftNow.dwHighDateTime;
596  (*ppJobInfo)->Time = (DWORD)((uliNow.QuadPart - uliSubmitted.QuadPart) / 10000);
597 
598  // Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
599  // Retrieve this through the element index of the job in the Printer's Job List.
600  if (!LookupElementSkiplist(&pJob->pPrinter->JobList, pJob, &(*ppJobInfo)->Position))
601  {
602  ERR("pJob could not be located in the Printer's Job List!\n");
603  return;
604  }
605 
606  // Make the index 1-based.
607  ++(*ppJobInfo)->Position;
608 
609  // FIXME!
610  FIXME("Setting pSecurityDescriptor and Size to 0 for now!\n");
611  (*ppJobInfo)->pSecurityDescriptor = NULL;
612  (*ppJobInfo)->Size = 0;
613 
614  // Set the pDevMode field (and copy the DevMode).
615  *ppJobInfoEnd -= cbDevMode;
616  CopyMemory(*ppJobInfoEnd, pJob->pDevMode, cbDevMode);
617  (*ppJobInfo)->pDevMode = (PDEVMODEW)(*ppJobInfoEnd);
618 
619  // Set the pPrinterName field.
620  pwszStrings[0] = pJob->pPrinter->pwszPrinterName;
621 
622  // Set the pMachineName field.
623  pwszStrings[1] = pJob->pwszMachineName;
624 
625  // Set the pUserName field.
626  pwszStrings[2] = pJob->pwszUserName;
627 
628  // Set the pDocument field.
629  pwszStrings[3] = pJob->pwszDocumentName;
630 
631  // Set the pNotifyName field.
632  pwszStrings[4] = pJob->pwszNotifyName;
633 
634  // Set the pDatatype field.
635  pwszStrings[5] = pJob->pwszDatatype;
636 
637  // Set the pPrintProcessor field.
638  pwszStrings[6] = pJob->pPrintProcessor->pwszName;
639 
640  // Set the pParameters field.
641  pwszStrings[7] = pJob->pwszPrintProcessorParameters;
642 
643  // Set the pDriverName field.
644  pwszStrings[8] = pJob->pPrinter->pwszPrinterDriver;
645 
646  // Set the pStatus field.
647  pwszStrings[9] = pJob->pwszStatus;
648 
649  // Finally copy the structure and advance to the next one in the output buffer.
650  *ppJobInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppJobInfo), dwJobInfo2Offsets, *ppJobInfoEnd);
651  (*ppJobInfo)++;
652 }
653 
654 BOOL WINAPI
655 LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded)
656 {
657  DWORD dwErrorCode;
658  PBYTE pEnd = &pStart[cbBuf];
659  PLOCAL_HANDLE pHandle;
660  PLOCAL_JOB pJob;
661  PLOCAL_PRINTER_HANDLE pPrinterHandle;
662 
663  TRACE("LocalGetJob(%p, %lu, %lu, %p, %lu, %p)\n", hPrinter, JobId, Level, pStart, cbBuf, pcbNeeded);
664 
665  // Check if this is a printer handle.
666  pHandle = (PLOCAL_HANDLE)hPrinter;
667  if (pHandle->HandleType != HandleType_Printer)
668  {
669  dwErrorCode = ERROR_INVALID_HANDLE;
670  goto Cleanup;
671  }
672 
673  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
674 
675  // Get the desired job.
676  pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
677  if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
678  {
679  dwErrorCode = ERROR_INVALID_PARAMETER;
680  goto Cleanup;
681  }
682 
683  if (Level > 2)
684  {
685  // The caller supplied an invalid level for GetJob.
686  dwErrorCode = ERROR_INVALID_LEVEL;
687  goto Cleanup;
688  }
689 
690  // Count the required buffer size.
691  *pcbNeeded = 0;
692 
693  if (Level == 1)
695  else if (Level == 2)
697 
698  // Check if the supplied buffer is large enough.
699  if (cbBuf < *pcbNeeded)
700  {
701  dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
702  goto Cleanup;
703  }
704 
705  // Copy over the Job information.
706  pEnd = &pStart[*pcbNeeded];
707 
708  if (Level == 1)
709  _LocalGetJobLevel1(pJob, (PJOB_INFO_1W*)&pStart, &pEnd, NULL);
710  else if (Level == 2)
711  _LocalGetJobLevel2(pJob, (PJOB_INFO_2W*)&pStart, &pEnd, NULL);
712 
713  dwErrorCode = ERROR_SUCCESS;
714 
715 Cleanup:
716  SetLastError(dwErrorCode);
717  return (dwErrorCode == ERROR_SUCCESS);
718 }
719 
720 static DWORD
722 {
723  DWORD dwErrorCode;
724 
725  // First check the validity of the input before changing anything.
726  if (!FindDatatype(pJob->pPrintProcessor, pJobInfo->pDatatype))
727  {
728  dwErrorCode = ERROR_INVALID_DATATYPE;
729  goto Cleanup;
730  }
731 
732  // Check if the datatype has changed.
733  if (!_EqualStrings(pJob->pwszDatatype, pJobInfo->pDatatype))
734  {
735  // Use the new value.
736  if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
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 document name has changed. An empty string is permitted here!
745  if (!_EqualStrings(pJob->pwszDocumentName, pJobInfo->pDocument))
746  {
747  // Use the new value.
748  if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
749  {
750  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
751  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
752  goto Cleanup;
753  }
754  }
755 
756  // Check if the status message has changed. An empty string is permitted here!
757  if (!_EqualStrings(pJob->pwszStatus, pJobInfo->pStatus))
758  {
759  // Use the new value.
760  if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
761  {
762  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
763  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
764  goto Cleanup;
765  }
766  }
767 
768  // Check if the user name has changed. An empty string is permitted here!
769  if (!_EqualStrings(pJob->pwszUserName, pJobInfo->pUserName))
770  {
771  // The new user name doesn't need to exist, so no additional verification is required.
772 
773  // Use the new value.
774  if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
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 priority has changed.
783  if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
784  {
785  // Set the new priority.
786  pJob->dwPriority = pJobInfo->Priority;
787 
788  // Remove and reinsert the job in the Printer's Job List.
789  // The Compare function will be used to find the right position now considering the new priority.
790  DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
791  InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
792  }
793 
794  // Check if the status flags have changed.
795  if (pJob->dwStatus != pJobInfo->Status)
796  {
797  // Only add status flags that make sense.
798  if (pJobInfo->Status & JOB_STATUS_PAUSED)
799  pJob->dwStatus |= JOB_STATUS_PAUSED;
800 
801  if (pJobInfo->Status & JOB_STATUS_ERROR)
802  pJob->dwStatus |= JOB_STATUS_ERROR;
803 
804  if (pJobInfo->Status & JOB_STATUS_OFFLINE)
805  pJob->dwStatus |= JOB_STATUS_OFFLINE;
806 
807  if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
808  pJob->dwStatus |= JOB_STATUS_PAPEROUT;
809  }
810 
811  dwErrorCode = ERROR_SUCCESS;
812 
813 Cleanup:
814  return dwErrorCode;
815 }
816 
817 static DWORD
819 {
820  DWORD dwErrorCode;
821  PLOCAL_PRINT_PROCESSOR pPrintProcessor;
822 
823  // First check the validity of the input before changing anything.
824  pPrintProcessor = FindPrintProcessor(pJobInfo->pPrintProcessor);
825  if (!pPrintProcessor)
826  {
827  dwErrorCode = ERROR_UNKNOWN_PRINTPROCESSOR;
828  goto Cleanup;
829  }
830 
831  if (!FindDatatype(pPrintProcessor, pJobInfo->pDatatype))
832  {
833  dwErrorCode = ERROR_INVALID_DATATYPE;
834  goto Cleanup;
835  }
836 
837  // Check if the datatype has changed.
838  if (!_EqualStrings(pJob->pwszDatatype, pJobInfo->pDatatype))
839  {
840  // Use the new value.
841  if (!ReallocSplStr(&pJob->pwszDatatype, pJobInfo->pDatatype))
842  {
843  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
844  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
845  goto Cleanup;
846  }
847  }
848 
849  // Check if the document name has changed. An empty string is permitted here!
850  if (!_EqualStrings(pJob->pwszDocumentName, pJobInfo->pDocument))
851  {
852  // Use the new value.
853  if (!ReallocSplStr(&pJob->pwszDocumentName, pJobInfo->pDocument))
854  {
855  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
856  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
857  goto Cleanup;
858  }
859  }
860 
861  // Check if the notify name has changed. An empty string is permitted here!
862  if (!_EqualStrings(pJob->pwszNotifyName, pJobInfo->pNotifyName))
863  {
864  // The new notify name doesn't need to exist, so no additional verification is required.
865 
866  // Use the new value.
867  if (!ReallocSplStr(&pJob->pwszNotifyName, pJobInfo->pNotifyName))
868  {
869  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
870  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
871  goto Cleanup;
872  }
873  }
874 
875  // Check if the Print Processor Parameters have changed. An empty string is permitted here!
877  {
878  // Use the new value.
880  {
881  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
882  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
883  goto Cleanup;
884  }
885  }
886 
887  // Check if the Status Message has changed. An empty string is permitted here!
888  if (!_EqualStrings(pJob->pwszStatus, pJobInfo->pStatus))
889  {
890  // Use the new value.
891  if (!ReallocSplStr(&pJob->pwszStatus, pJobInfo->pStatus))
892  {
893  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
894  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
895  goto Cleanup;
896  }
897  }
898 
899  // Check if the user name has changed. An empty string is permitted here!
900  if (!_EqualStrings(pJob->pwszUserName, pJobInfo->pUserName))
901  {
902  // The new user name doesn't need to exist, so no additional verification is required.
903 
904  // Use the new value.
905  if (!ReallocSplStr(&pJob->pwszUserName, pJobInfo->pUserName))
906  {
907  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
908  ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError());
909  goto Cleanup;
910  }
911  }
912 
913  // Check if the priority has changed.
914  if (pJob->dwPriority != pJobInfo->Priority && IS_VALID_PRIORITY(pJobInfo->Priority))
915  {
916  // Set the new priority.
917  pJob->dwPriority = pJobInfo->Priority;
918 
919  // Remove and reinsert the job in the Printer's Job List.
920  // The Compare function will be used to find the right position now considering the new priority.
921  DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
922  InsertElementSkiplist(&pJob->pPrinter->JobList, pJob);
923  }
924 
925  // Check if the status flags have changed.
926  if (pJob->dwStatus != pJobInfo->Status)
927  {
928  // Only add status flags that make sense.
929  if (pJobInfo->Status & JOB_STATUS_PAUSED)
930  pJob->dwStatus |= JOB_STATUS_PAUSED;
931 
932  if (pJobInfo->Status & JOB_STATUS_ERROR)
933  pJob->dwStatus |= JOB_STATUS_ERROR;
934 
935  if (pJobInfo->Status & JOB_STATUS_OFFLINE)
936  pJob->dwStatus |= JOB_STATUS_OFFLINE;
937 
938  if (pJobInfo->Status & JOB_STATUS_PAPEROUT)
939  pJob->dwStatus |= JOB_STATUS_PAPEROUT;
940  }
941 
942  dwErrorCode = ERROR_SUCCESS;
943 
944 Cleanup:
945  return dwErrorCode;
946 }
947 
948 BOOL WINAPI
949 LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
950 {
951  DWORD dwErrorCode = ERROR_SUCCESS;
952  PLOCAL_HANDLE pHandle;
953  PLOCAL_JOB pJob;
954  PLOCAL_PRINTER_HANDLE pPrinterHandle;
955  WCHAR wszFullPath[MAX_PATH];
956 
957  TRACE("LocalSetJob(%p, %lu, %lu, %p, %lu)\n", hPrinter, JobId, Level, pJobInfo, Command);
958 
959  // Check if this is a printer handle.
960  pHandle = (PLOCAL_HANDLE)hPrinter;
961  if (!pHandle || pHandle->HandleType != HandleType_Printer)
962  {
963  dwErrorCode = ERROR_INVALID_HANDLE;
964  goto Cleanup;
965  }
966 
967  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
968 
969  // Get the desired job.
970  pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
971  if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
972  {
973  dwErrorCode = ERROR_INVALID_PARAMETER;
974  goto Cleanup;
975  }
976 
977  // Set new job information if a valid level was given.
978  if (Level == 1)
979  dwErrorCode = _LocalSetJobLevel1(pPrinterHandle, pJob, (PJOB_INFO_1W)pJobInfo);
980  else if (Level == 2)
981  dwErrorCode = _LocalSetJobLevel2(pPrinterHandle, pJob, (PJOB_INFO_2W)pJobInfo);
982 
983  if (dwErrorCode != ERROR_SUCCESS)
984  goto Cleanup;
985 
986  // If we do spooled printing, the job information is written down into a shadow file.
987  if (!(pPrinterHandle->pPrinter->dwAttributes & PRINTER_ATTRIBUTE_DIRECT))
988  {
989  // Write the job data into the shadow file.
990  GetJobFilePath(L"SHD", JobId, wszFullPath);
991  WriteJobShadowFile(wszFullPath, pJob);
992  }
993 
994  // Perform an additional command if desired.
995  if (Command)
996  {
998  {
999  // This indicates the end of the Print Job.
1000 
1001  // Cancel the Job at the Print Processor.
1002  if (pJob->hPrintProcessor)
1004 
1005  FreeJob(pJob);
1006 
1007  // TODO: All open handles associated with the job need to be invalidated.
1008  // This certainly needs handle tracking...
1009  }
1010  else
1011  {
1012  ERR("Unimplemented SetJob Command: %lu!\n", Command);
1013  }
1014  }
1015 
1016 Cleanup:
1017  SetLastError(dwErrorCode);
1018  return (dwErrorCode == ERROR_SUCCESS);
1019 }
1020 
1021 BOOL WINAPI
1022 LocalEnumJobs(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
1023 {
1024  DWORD dwErrorCode;
1025  DWORD i;
1026  PBYTE pEnd;
1027  PLOCAL_HANDLE pHandle;
1028  PLOCAL_JOB pJob;
1029  PSKIPLIST_NODE pFirstJobNode;
1030  PSKIPLIST_NODE pNode;
1031  PLOCAL_PRINTER_HANDLE pPrinterHandle;
1032 
1033  TRACE("LocalEnumJobs(%p, %lu, %lu, %lu, %p, %lu, %p, %p)\n", hPrinter, FirstJob, NoJobs, Level, pStart, cbBuf, pcbNeeded, pcReturned);
1034 
1035  // Check if this is a printer handle.
1036  pHandle = (PLOCAL_HANDLE)hPrinter;
1037  if (pHandle->HandleType != HandleType_Printer)
1038  {
1039  dwErrorCode = ERROR_INVALID_HANDLE;
1040  goto Cleanup;
1041  }
1042 
1043  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1044 
1045  // Check the level.
1046  if (Level > 2)
1047  {
1048  dwErrorCode = ERROR_INVALID_LEVEL;
1049  goto Cleanup;
1050  }
1051 
1052  // Begin counting.
1053  *pcbNeeded = 0;
1054  *pcReturned = 0;
1055 
1056  // Lookup the node of the first job requested by the caller in the Printer's Job List.
1057  pFirstJobNode = LookupNodeByIndexSkiplist(&pPrinterHandle->pPrinter->JobList, FirstJob);
1058 
1059  // Count the required buffer size and the number of jobs.
1060  i = 0;
1061  pNode = pFirstJobNode;
1062 
1063  while (i < NoJobs && pNode)
1064  {
1065  pJob = (PLOCAL_JOB)pNode->Element;
1066 
1067  if (Level == 1)
1069  else if (Level == 2)
1071 
1072  // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1073  i++;
1074  pNode = pNode->Next[0];
1075  }
1076 
1077  // Check if the supplied buffer is large enough.
1078  if (cbBuf < *pcbNeeded)
1079  {
1080  dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
1081  goto Cleanup;
1082  }
1083 
1084  // Copy over the Job information.
1085  i = 0;
1086  pNode = pFirstJobNode;
1087  pEnd = &pStart[*pcbNeeded];
1088 
1089  while (i < NoJobs && pNode)
1090  {
1091  pJob = (PLOCAL_JOB)pNode->Element;
1092 
1093  if (Level == 1)
1094  _LocalGetJobLevel1(pJob, (PJOB_INFO_1W*)&pStart, &pEnd, NULL);
1095  else if (Level == 2)
1096  _LocalGetJobLevel2(pJob, (PJOB_INFO_2W*)&pStart, &pEnd, NULL);
1097 
1098  // We stop either when there are no more jobs in the list or when the caller didn't request more, whatever comes first.
1099  i++;
1100  pNode = pNode->Next[0];
1101  }
1102 
1103  *pcReturned = i;
1104  dwErrorCode = ERROR_SUCCESS;
1105 
1106 Cleanup:
1107  SetLastError(dwErrorCode);
1108  return (dwErrorCode == ERROR_SUCCESS);
1109 }
1110 
1111 BOOL WINAPI
1112 LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
1113 {
1115  DWORD dwErrorCode;
1116  HANDLE hThread;
1117  PLOCAL_JOB pJob;
1118  PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
1119  PLOCAL_PRINTER_HANDLE pPrinterHandle;
1120  WCHAR wszFullPath[MAX_PATH];
1121 
1122  TRACE("LocalScheduleJob(%p, %lu)\n", hPrinter, dwJobID);
1123 
1124  // Check if this is a printer handle.
1125  if (pHandle->HandleType != HandleType_Printer)
1126  {
1127  dwErrorCode = ERROR_INVALID_HANDLE;
1128  goto Cleanup;
1129  }
1130 
1131  pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
1132 
1133  // Check if the Job ID is valid.
1134  pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
1135  if (!pJob || pJob->pPrinter != pPrinterHandle->pPrinter)
1136  {
1137  dwErrorCode = ERROR_INVALID_PARAMETER;
1138  goto Cleanup;
1139  }
1140 
1141  // Check if this Job was started with AddJob.
1142  if (!pJob->bAddedJob)
1143  {
1144  dwErrorCode = ERROR_SPL_NO_ADDJOB;
1145  goto Cleanup;
1146  }
1147 
1148  // Construct the full path to the spool file.
1149  GetJobFilePath(L"SPL", dwJobID, wszFullPath);
1150 
1151  // Check if it exists.
1152  dwAttributes = GetFileAttributesW(wszFullPath);
1154  {
1155  dwErrorCode = ERROR_SPOOL_FILE_NOT_FOUND;
1156  goto Cleanup;
1157  }
1158 
1159  // Spooling is finished at this point.
1160  pJob->dwStatus &= ~JOB_STATUS_SPOOLING;
1161 
1162  // Write the job data into the shadow file.
1163  wcscpy(wcsrchr(wszFullPath, L'.'), L".SHD");
1164  WriteJobShadowFile(wszFullPath, pJob);
1165 
1166  // Create the thread for performing the printing process.
1168  if (!hThread)
1169  {
1170  dwErrorCode = GetLastError();
1171  ERR("CreateThread failed with error %lu!\n", dwErrorCode);
1172  goto Cleanup;
1173  }
1174 
1175  // We don't need the thread handle. Keeping it open blocks the thread from terminating.
1177 
1178  // ScheduleJob has done its job. The rest happens inside the thread.
1179  dwErrorCode = ERROR_SUCCESS;
1180 
1181 Cleanup:
1182  SetLastError(dwErrorCode);
1183  return (dwErrorCode == ERROR_SUCCESS);
1184 }
1185 
1186 PLOCAL_JOB
1188 {
1189  DWORD cbFileSize;
1190  DWORD cbRead;
1192  PLOCAL_JOB pJob;
1193  PLOCAL_JOB pReturnValue = NULL;
1194  PLOCAL_PRINTER pPrinter;
1195  PLOCAL_PRINT_PROCESSOR pPrintProcessor;
1196  PSHD_HEADER pShadowFile = NULL;
1197  PWSTR pwszPrinterName;
1198  PWSTR pwszPrintProcessor;
1199 
1200  TRACE("ReadJobShadowFile(%S)\n", pwszFilePath);
1201 
1202  // Try to open the file.
1204  if (hFile == INVALID_HANDLE_VALUE)
1205  {
1206  ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1207  goto Cleanup;
1208  }
1209 
1210  // Get its file size (small enough for a single DWORD) and allocate memory for all of it.
1211  cbFileSize = GetFileSize(hFile, NULL);
1212  pShadowFile = DllAllocSplMem(cbFileSize);
1213  if (!pShadowFile)
1214  {
1215  ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
1216  goto Cleanup;
1217  }
1218 
1219  // Read the entire file.
1220  if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL))
1221  {
1222  ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1223  goto Cleanup;
1224  }
1225 
1226  // Check signature and header size.
1227  if (pShadowFile->dwSignature != SHD_WIN2003_SIGNATURE || pShadowFile->cbHeader != sizeof(SHD_HEADER))
1228  {
1229  ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath);
1230  goto Cleanup;
1231  }
1232 
1233  // Retrieve the associated printer from the list.
1234  pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName);
1235  pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
1236  if (!pPrinter)
1237  {
1238  ERR("Shadow file \"%S\" references a non-existing printer \"%S\"!\n", pwszFilePath, pwszPrinterName);
1239  goto Cleanup;
1240  }
1241 
1242  // Retrieve the associated Print Processor from the list.
1243  pwszPrintProcessor = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrintProcessor);
1244  pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
1245  if (!pPrintProcessor)
1246  {
1247  ERR("Shadow file \"%S\" references a non-existing Print Processor \"%S\"!\n", pwszFilePath, pwszPrintProcessor);
1248  goto Cleanup;
1249  }
1250 
1251  // Create a new job structure and copy over the relevant fields.
1252  pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
1253  if (!pJob)
1254  {
1255  ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
1256  goto Cleanup;
1257  }
1258 
1259  pJob->dwJobID = pShadowFile->dwJobID;
1260  pJob->dwPriority = pShadowFile->dwPriority;
1261  pJob->dwStartTime = pShadowFile->dwStartTime;
1262  pJob->dwTotalPages = pShadowFile->dwTotalPages;
1263  pJob->dwUntilTime = pShadowFile->dwUntilTime;
1264  pJob->pPrinter = pPrinter;
1265  pJob->pPrintProcessor = pPrintProcessor;
1266  pJob->pDevMode = DuplicateDevMode((PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode));
1267  pJob->pwszDatatype = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype));
1268  pJob->pwszMachineName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offMachineName));
1269  CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
1270 
1271  // Copy the optional values.
1272  if (pShadowFile->offDocumentName)
1273  pJob->pwszDocumentName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName));
1274 
1275  if (pShadowFile->offNotifyName)
1276  pJob->pwszNotifyName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offNotifyName));
1277 
1278  if (pShadowFile->offPrintProcessorParameters)
1280 
1281  if (pShadowFile->offUserName)
1282  pJob->pwszUserName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offUserName));
1283 
1284  // Jobs read from shadow files were always added using AddJob.
1285  pJob->bAddedJob = TRUE;
1286 
1287  pReturnValue = pJob;
1288 
1289 Cleanup:
1290  if (pShadowFile)
1291  DllFreeSplMem(pShadowFile);
1292 
1293  if (hFile != INVALID_HANDLE_VALUE)
1294  CloseHandle(hFile);
1295 
1296  return pReturnValue;
1297 }
1298 
1299 BOOL
1300 WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob)
1301 {
1302  BOOL bReturnValue = FALSE;
1303  DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
1304  DWORD cbDevMode = pJob->pDevMode->dmSize + pJob->pDevMode->dmDriverExtra;
1305  DWORD cbDocumentName = 0;
1306  DWORD cbFileSize;
1307  DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
1308  DWORD cbNotifyName = 0;
1309  DWORD cbPrinterDriver = (wcslen(pJob->pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
1310  DWORD cbPrinterName = (wcslen(pJob->pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
1311  DWORD cbPrintProcessor = (wcslen(pJob->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
1312  DWORD cbPrintProcessorParameters = 0;
1313  DWORD cbUserName = 0;
1314  DWORD cbWritten;
1315  DWORD dwCurrentOffset;
1316  HANDLE hSHDFile = INVALID_HANDLE_VALUE;
1317  HANDLE hSPLFile = INVALID_HANDLE_VALUE;
1318  PSHD_HEADER pShadowFile = NULL;
1319 
1320  TRACE("WriteJobShadowFile(%S, %p)\n", pwszFilePath, pJob);
1321 
1322  // Try to open the SHD file.
1323  hSHDFile = CreateFileW(pwszFilePath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
1324  if (hSHDFile == INVALID_HANDLE_VALUE)
1325  {
1326  ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1327  goto Cleanup;
1328  }
1329 
1330  // Calculate the lengths of the optional values and the total size of the shadow file.
1331  if (pJob->pwszDocumentName)
1332  cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
1333 
1334  if (pJob->pwszNotifyName)
1335  cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
1336 
1337  if (pJob->pwszPrintProcessorParameters)
1338  cbPrintProcessorParameters = (wcslen(pJob->pwszPrintProcessorParameters) + 1) * sizeof(WCHAR);
1339 
1340  if (pJob->pwszUserName)
1341  cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
1342 
1343  cbFileSize = sizeof(SHD_HEADER) + cbDatatype + cbDocumentName + cbDevMode + cbMachineName + cbNotifyName + cbPrinterDriver + cbPrinterName + cbPrintProcessor + cbPrintProcessorParameters + cbUserName;
1344 
1345  // Allocate memory for it.
1346  pShadowFile = DllAllocSplMem(cbFileSize);
1347  if (!pShadowFile)
1348  {
1349  ERR("DllAllocSplMem failed for file \"%S\"!\n", pwszFilePath);
1350  goto Cleanup;
1351  }
1352 
1353  // Fill out the shadow file header information.
1354  pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE;
1355  pShadowFile->cbHeader = sizeof(SHD_HEADER);
1356 
1357  // Copy the values.
1358  pShadowFile->dwJobID = pJob->dwJobID;
1359  pShadowFile->dwPriority = pJob->dwPriority;
1360  pShadowFile->dwStartTime = pJob->dwStartTime;
1361  pShadowFile->dwTotalPages = pJob->dwTotalPages;
1362  pShadowFile->dwUntilTime = pJob->dwUntilTime;
1363  CopyMemory(&pShadowFile->stSubmitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
1364 
1365  // Determine the file size of the .SPL file
1366  wcscpy(wcsrchr(pwszFilePath, L'.'), L".SPL");
1367  hSPLFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1368  if (hSPLFile != INVALID_HANDLE_VALUE)
1369  pShadowFile->dwSPLSize = GetFileSize(hSPLFile, NULL);
1370 
1371  // Add the extra values that are stored as offsets in the shadow file.
1372  // The first value begins right after the shadow file header.
1373  dwCurrentOffset = sizeof(SHD_HEADER);
1374 
1375  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype);
1376  pShadowFile->offDatatype = dwCurrentOffset;
1377  dwCurrentOffset += cbDatatype;
1378 
1379  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pDevMode, cbDevMode);
1380  pShadowFile->offDevMode = dwCurrentOffset;
1381  dwCurrentOffset += cbDevMode;
1382 
1383  // offDriverName is only written, but automatically determined through offPrinterName when reading.
1384  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterDriver, cbPrinterDriver);
1385  pShadowFile->offDriverName = dwCurrentOffset;
1386  dwCurrentOffset += cbPrinterDriver;
1387 
1388  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszMachineName, cbMachineName);
1389  pShadowFile->offMachineName = dwCurrentOffset;
1390  dwCurrentOffset += cbMachineName;
1391 
1392  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrinter->pwszPrinterName, cbPrinterName);
1393  pShadowFile->offPrinterName = dwCurrentOffset;
1394  dwCurrentOffset += cbPrinterName;
1395 
1396  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pPrintProcessor->pwszName, cbPrintProcessor);
1397  pShadowFile->offPrintProcessor = dwCurrentOffset;
1398  dwCurrentOffset += cbPrintProcessor;
1399 
1400  // Copy the optional values.
1401  if (cbDocumentName)
1402  {
1403  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDocumentName, cbDocumentName);
1404  pShadowFile->offDocumentName = dwCurrentOffset;
1405  dwCurrentOffset += cbDocumentName;
1406  }
1407 
1408  if (cbNotifyName)
1409  {
1410  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszNotifyName, cbNotifyName);
1411  pShadowFile->offNotifyName = dwCurrentOffset;
1412  dwCurrentOffset += cbNotifyName;
1413  }
1414 
1415  if (cbPrintProcessorParameters)
1416  {
1417  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszPrintProcessorParameters, cbPrintProcessorParameters);
1418  pShadowFile->offPrintProcessorParameters = dwCurrentOffset;
1419  dwCurrentOffset += cbPrintProcessorParameters;
1420  }
1421 
1422  if (cbUserName)
1423  {
1424  CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszUserName, cbUserName);
1425  pShadowFile->offUserName = dwCurrentOffset;
1426  dwCurrentOffset += cbUserName;
1427  }
1428 
1429  // Write the file.
1430  if (!WriteFile(hSHDFile, pShadowFile, cbFileSize, &cbWritten, NULL))
1431  {
1432  ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
1433  goto Cleanup;
1434  }
1435 
1436  bReturnValue = TRUE;
1437 
1438 Cleanup:
1439  if (pShadowFile)
1440  DllFreeSplMem(pShadowFile);
1441 
1442  if (hSHDFile != INVALID_HANDLE_VALUE)
1443  CloseHandle(hSHDFile);
1444 
1445  if (hSPLFile != INVALID_HANDLE_VALUE)
1446  CloseHandle(hSPLFile);
1447 
1448  return bReturnValue;
1449 }
1450 
1451 void
1453 {
1454  PWSTR pwszSHDFile;
1455 
1456  TRACE("FreeJob(%p)\n", pJob);
1457 
1458  // Remove the Job from both Job Lists.
1459  DeleteElementSkiplist(&pJob->pPrinter->JobList, pJob);
1461 
1462  // Try to delete the corresponding .SHD file.
1463  pwszSHDFile = DllAllocSplMem(GetJobFilePath(L"SHD", 0, NULL));
1464  if (pwszSHDFile && GetJobFilePath(L"SHD", pJob->dwJobID, pwszSHDFile))
1465  DeleteFileW(pwszSHDFile);
1466 
1467  // Free memory for the mandatory fields.
1468  DllFreeSplMem(pJob->pDevMode);
1469  DllFreeSplStr(pJob->pwszDatatype);
1473  DllFreeSplStr(pJob->pwszUserName);
1474 
1475  // Free memory for the optional fields if they are present.
1476  if (pJob->pwszOutputFile)
1478 
1479  if (pJob->pwszPrintProcessorParameters)
1481 
1482  if (pJob->pwszStatus)
1483  DllFreeSplStr(pJob->pwszStatus);
1484 
1485  // Finally free the job structure itself.
1486  DllFreeSplMem(pJob);
1487 }
LPWSTR pUserName
Definition: winspool.h:298
static DWORD _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_2W pJobInfo)
Definition: jobs.c:818
BOOL WINAPI LocalEnumJobs(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
Definition: jobs.c:1022
#define ERROR_SPOOL_FILE_NOT_FOUND
Definition: winerror.h:1207
#define ERROR_INVALID_DATATYPE
Definition: winerror.h:1111
#define ERROR_INVALID_PARAMETER
Definition: compat.h:91
SYSTEMTIME stSubmitted
Definition: precomp.h:143
static __inline BOOL _EqualStrings(PCWSTR pwszA, PCWSTR pwszB)
Definition: jobs.c:58
BOOL WINAPI FindNextFileW(IN HANDLE hFindFile, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:382
PDEVMODEW pDevMode
Definition: precomp.h:170
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
const uint16_t * PCWSTR
Definition: typedefs.h:55
DWORD offDocumentName
Definition: precomp.h:222
PWSTR WINAPI AllocSplStr(PCWSTR pwszInput)
Definition: memory.c:56
VOID WINAPI GetSystemTimeAsFileTime(OUT PFILETIME lpFileTime)
Definition: time.c:128
DWORD dwStartTime
Definition: precomp.h:231
RPC_STATUS WINAPI RpcBindingFree(RPC_BINDING_HANDLE *Binding)
Definition: rpc_binding.c:784
DWORD(WINAPI * LPTHREAD_START_ROUTINE)(LPVOID)
Definition: winbase.h:707
#define TRUE
Definition: types.h:120
DWORD offNotifyName
Definition: precomp.h:221
#define CloseHandle
Definition: compat.h:398
struct _devicemodeW * PDEVMODEW
BOOL InitializeGlobalJobList(void)
Definition: jobs.c:161
DWORD offDriverName
Definition: precomp.h:225
#define ERROR_SUCCESS
Definition: deptool.c:10
#define JOB_STATUS_PAUSED
Definition: winspool.h:338
BOOL bAddedJob
Definition: precomp.h:138
BOOL InsertTailElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
Definition: skiplist.c:308
DWORD offUserName
Definition: precomp.h:220
RPC_STATUS WINAPI RpcStringFreeW(RPC_WSTR *String)
Definition: rpcrt4_main.c:177
DWORD dwStartTime
Definition: precomp.h:153
PWSTR pwszOutputFile
Definition: precomp.h:148
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define DWORD
Definition: msvc.h:34
uint16_t * PWSTR
Definition: typedefs.h:54
static BOOL _GetNextJobID(PDWORD dwJobID)
Definition: jobs.c:73
LPWSTR pDatatype
Definition: winspool.h:254
#define _countof(array)
Definition: fontsub.cpp:30
DWORD dwAttributes
Definition: precomp.h:117
PWSTR pwszPrinterDriver
Definition: precomp.h:120
BOOL WINAPI DllFreeSplMem(PVOID pMem)
Definition: memory.c:112
#define ERROR_INVALID_HANDLE
Definition: compat.h:88
PWSTR pwszMachineName
Definition: precomp.h:156
#define JOB_STATUS_PAPEROUT
Definition: winspool.h:344
struct _ADDJOB_INFO_1W ADDJOB_INFO_1W
WORD dmDriverExtra
Definition: wingdi.h:1599
#define JOB_CONTROL_CANCEL
Definition: winspool.h:332
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
$ULONG LowPart
Definition: ntbasedef.h:576
DWORD WINAPI PrintingThreadProc(PLOCAL_JOB pJob)
SKIPLIST JobList
Definition: precomp.h:126
_IRQL_requires_same_ typedef _In_ ULONG _In_ UCHAR Level
Definition: wmitypes.h:55
#define DEF_PRIORITY
Definition: winspool.h:230
static DWORD dwJobInfo2Offsets[]
Definition: jobs.c:27
Definition: shell.h:41
_Check_return_ unsigned long __cdecl wcstoul(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, _In_ int _Radix)
#define FILE_SHARE_WRITE
Definition: nt_native.h:681
unsigned short * RPC_WSTR
Definition: rpcdce.h:46
void FreeJob(PLOCAL_JOB pJob)
Definition: jobs.c:1452
#define WCHAR
Definition: msvc.h:43
PSKIPLIST_NODE LookupNodeByIndexSkiplist(PSKIPLIST Skiplist, DWORD ElementIndex)
Definition: skiplist.c:412
DWORD dwPriority
Definition: precomp.h:142
PDEVMODEW pDevMode
Definition: precomp.h:157
DWORD dwAttributes
Definition: vdmdbg.h:34
#define FILE_SHARE_READ
Definition: compat.h:125
#define JOB_STATUS_SPOOLING
Definition: winspool.h:341
LPWSTR pUserName
Definition: winspool.h:252
uint32_t ULONG_PTR
Definition: typedefs.h:63
BOOL WINAPI ReallocSplStr(PWSTR *ppwszString, PCWSTR pwszInput)
Definition: memory.c:192
void InitializeSkiplist(PSKIPLIST Skiplist, PSKIPLIST_ALLOCATE_ROUTINE AllocateRoutine, PSKIPLIST_COMPARE_ROUTINE CompareRoutine, PSKIPLIST_FREE_ROUTINE FreeRoutine)
Definition: skiplist.c:220
DWORD dwSignature
Definition: precomp.h:214
void InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
Definition: jobs.c:247
DWORD dwHighDateTime
Definition: mapidefs.h:66
static DWORD _dwLastJobID
Definition: jobs.c:14
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
DWORD dwJobID
Definition: precomp.h:136
LPWSTR pDocument
Definition: winspool.h:299
struct _LOCAL_HANDLE * PLOCAL_HANDLE
Definition: precomp.h:54
LPWSTR pDatatype
Definition: winspool.h:301
ULONGLONG QuadPart
Definition: ms-dtyp.idl:185
PLOCAL_JOB ReadJobShadowFile(PCWSTR pwszFilePath)
Definition: jobs.c:1187
DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
Definition: fileinfo.c:802
#define GENERIC_WRITE
Definition: nt_native.h:90
BOOL InsertElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
Definition: skiplist.c:250
LPWSTR pStatus
Definition: winspool.h:306
#define FIXME(fmt,...)
Definition: debug.h:110
BOOL WINAPI DeleteFileW(IN LPCWSTR lpFileName)
Definition: delete.c:39
BOOL WINAPI LocalScheduleJob(HANDLE hPrinter, DWORD dwJobID)
Definition: jobs.c:1112
PWSTR pwszDatatype
Definition: precomp.h:147
#define JOB_STATUS_OFFLINE
Definition: winspool.h:343
HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId)
Definition: thread.c:112
PLOCAL_PRINT_PROCESSOR pPrintProcessor
Definition: precomp.h:124
smooth NULL
Definition: ftsmooth.c:416
DWORD dwUntilTime
Definition: precomp.h:154
PVOID pSpecificHandle
Definition: precomp.h:204
#define MAXDWORD
_Check_return_ _CRTIMP _CONST_RETURN wchar_t *__cdecl wcsrchr(_In_z_ const wchar_t *_Str, _In_ wchar_t _Ch)
DWORD GetJobFilePath(PCWSTR pwszExtension, DWORD dwJobID, PWSTR pwszOutput)
Definition: jobs.c:146
static void _LocalGetJobLevel1(PLOCAL_JOB pJob, PJOB_INFO_1W *ppJobInfo, PBYTE *ppJobInfoEnd, PDWORD pcbNeeded)
Definition: jobs.c:450
LPWSTR pNotifyName
Definition: winspool.h:300
DWORD offPrintProcessor
Definition: precomp.h:227
BOOL WINAPI LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pStart, DWORD cbBuf, LPDWORD pcbNeeded)
Definition: jobs.c:655
#define OPEN_EXISTING
Definition: compat.h:426
BOOL WriteJobShadowFile(PWSTR pwszFilePath, const PLOCAL_JOB pJob)
Definition: jobs.c:1300
RPCRTAPI RPC_STATUS RPC_ENTRY RpcBindingServerFromClient(RPC_BINDING_HANDLE ClientBinding, RPC_BINDING_HANDLE *ServerBinding)
Definition: rpc_binding.c:1643
PWSTR pwszPrintProcessorParameters
Definition: precomp.h:149
#define FILE_ATTRIBUTE_DIRECTORY
Definition: nt_native.h:705
BOOL WINAPI LocalAddJob(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded)
Definition: jobs.c:380
static void _LocalGetJobLevel2(PLOCAL_JOB pJob, PJOB_INFO_2W *ppJobInfo, PBYTE *ppJobInfoEnd, PDWORD pcbNeeded)
Definition: jobs.c:524
DWORD offDevMode
Definition: precomp.h:226
struct _SHD_HEADER SHD_HEADER
Definition: precomp.h:63
DWORD dwStatus
Definition: precomp.h:155
VOID WINAPI GetSystemTime(OUT LPSYSTEMTIME lpSystemTime)
Definition: time.c:317
RPC_STATUS WINAPI RpcStringBindingParseW(RPC_WSTR StringBinding, RPC_WSTR *ObjUuid, RPC_WSTR *Protseq, RPC_WSTR *NetworkAddr, RPC_WSTR *Endpoint, RPC_WSTR *Options)
Definition: rpc_binding.c:675
DWORD Priority
Definition: winspool.h:309
BOOL WINAPI GetUserNameW(LPWSTR lpszName, LPDWORD lpSize)
Definition: misc.c:291
#define TRACE(s)
Definition: solgame.cpp:4
RPC_STATUS WINAPI RpcBindingToStringBindingW(RPC_BINDING_HANDLE Binding, RPC_WSTR *StringBinding)
Definition: rpc_binding.c:944
$ULONG HighPart
Definition: ntbasedef.h:577
unsigned int BOOL
Definition: ntddk_ex.h:94
LONG WINAPI CompareFileTime(IN CONST FILETIME *lpFileTime1, IN CONST FILETIME *lpFileTime2)
Definition: time.c:106
_IRQL_requires_same_ _In_ PVOID _In_ PVOID SecondStruct
Definition: rtltypes.h:379
if(!(yy_init))
Definition: macro.lex.yy.c:714
DWORD Priority
Definition: winspool.h:257
static int WINAPI _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
Definition: jobs.c:115
PBYTE WINAPI PackStrings(PCWSTR *pSource, PBYTE pDest, const DWORD *DestOffsets, PBYTE pEnd)
Definition: tools.c:39
PLOCAL_PRINTER pPrinter
Definition: precomp.h:140
DWORD dwTotalPages
Definition: precomp.h:151
#define SHD_WIN2003_SIGNATURE
Definition: precomp.h:41
WORD dmSize
Definition: wingdi.h:1598
#define MAX_PATH
Definition: compat.h:26
#define swprintf(buf, format,...)
Definition: sprintf.c:56
struct _SKIPLIST_NODE * Next[SKIPLIST_LEVELS]
Definition: skiplist.h:30
BOOL FindDatatype(const PLOCAL_PRINT_PROCESSOR pPrintProcessor, PCWSTR pwszDatatype)
#define CopyMemory
Definition: winbase.h:1633
SKIPLIST PrinterList
Definition: printers.c:11
DWORD cchJobDirectory
Definition: main.c:14
unsigned long DWORD
Definition: ntddk_ex.h:95
#define PRINTER_ATTRIBUTE_DIRECT
Definition: winspool.h:209
DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
Definition: fileinfo.c:481
#define SetLastError(x)
Definition: compat.h:409
DWORD dwSPLSize
Definition: precomp.h:241
#define INVALID_FILE_ATTRIBUTES
Definition: vfdcmd.c:23
void(WINAPI * PSKIPLIST_FREE_ROUTINE)(PVOID)
Definition: skiplist.h:24
Definition: ttei1.cpp:12
_CRTIMP wchar_t *__cdecl wcscpy(_Out_writes_z_(_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
PWSTR pwszNotifyName
Definition: precomp.h:145
static const WCHAR L[]
Definition: oid.c:1250
DWORD offDatatype
Definition: precomp.h:228
LPWSTR pParameters
Definition: winspool.h:303
#define JOB_CONTROL_SENT_TO_PRINTER
Definition: winspool.h:335
_IRQL_requires_same_ _In_ PVOID FirstStruct
Definition: rtltypes.h:379
#define ERROR_INVALID_ACCESS
Definition: winerror.h:115
DWORD dwPriority
Definition: precomp.h:219
#define GENERIC_READ
Definition: compat.h:124
LPWSTR pPrintProcessor
Definition: winspool.h:302
enum _LOCAL_HANDLE::@4112 HandleType
static const WCHAR Cleanup[]
Definition: register.c:80
_In_ HANDLE hFile
Definition: mswsock.h:90
#define WINAPI
Definition: msvc.h:20
#define wcsicmp
Definition: string.h:1152
LPWSTR pDocument
Definition: winspool.h:253
struct _LOCAL_JOB * PLOCAL_JOB
Definition: precomp.h:55
DWORD WINAPI CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle)
Definition: jobs.c:257
#define ERR(fmt,...)
Definition: debug.h:109
PVOID LookupElementSkiplist(PSKIPLIST Skiplist, PVOID Element, PDWORD ElementIndex)
Definition: skiplist.c:357
_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
LPWSTR pStatus
Definition: winspool.h:255
#define ERROR_UNKNOWN_PRINTPROCESSOR
Definition: winerror.h:1105
#define CREATE_ALWAYS
Definition: disk.h:72
BOOL WINAPI SystemTimeToFileTime(IN CONST SYSTEMTIME *lpSystemTime, OUT LPFILETIME lpFileTime)
Definition: time.c:148
SYSTEMTIME stSubmitted
Definition: precomp.h:230
_In_ DWORD _Out_ PDWORD pcbNeeded
Definition: winddi.h:3827
DWORD dwTotalPages
Definition: precomp.h:234
PWSTR pwszDocumentName
Definition: precomp.h:146
#define IS_VALID_JOB_ID(ID)
Definition: precomp.h:37
HANDLE hPrintProcessor
Definition: precomp.h:139
#define UNLEN
Definition: sspi.c:28
PDEVMODEW DuplicateDevMode(PDEVMODEW pInput)
Definition: tools.c:61
DWORD Status
Definition: winspool.h:308
WCHAR wszJobDirectory[MAX_PATH]
Definition: main.c:13
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:254
#define B(row, col)
DWORD offMachineName
Definition: precomp.h:240
PVOID Element
Definition: skiplist.h:31
static int WINAPI _GlobalJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
Definition: jobs.c:100
HANDLE hThread
Definition: wizard.c:27
DWORD * PDWORD
Definition: pedump.c:68
BOOL WINAPI DllFreeSplStr(PWSTR pwszString)
Definition: memory.c:129
#define CreateFileW
Definition: compat.h:400
#define ERROR_SPL_NO_ADDJOB
Definition: winerror.h:1209
PLOCAL_JOB pJob
Definition: precomp.h:168
DWORD dwPagesPrinted
Definition: precomp.h:152
PLOCAL_PRINT_PROCESSOR pPrintProcessor
Definition: precomp.h:141
#define IS_VALID_PRIORITY(P)
Definition: precomp.h:38
uint32_t * LPDWORD
Definition: typedefs.h:57
DWORD cbHeader
Definition: precomp.h:215
PVOID DeleteElementSkiplist(PSKIPLIST Skiplist, PVOID Element)
Definition: skiplist.c:146
struct _LOCAL_PRINTER_HANDLE * PLOCAL_PRINTER_HANDLE
Definition: precomp.h:61
PVOID WINAPI DllAllocSplMem(DWORD dwBytes)
Definition: memory.c:95
BOOL WINAPI LocalSetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command)
Definition: jobs.c:949
#define ERROR_INVALID_LEVEL
Definition: winerror.h:196
PControlPrintProcessor pfnControlPrintProcessor
Definition: precomp.h:101
GLfloat GLfloat p
Definition: glext.h:8902
DWORD dwUntilTime
Definition: precomp.h:232
PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PCWSTR pwszName)
PLOCAL_PRINTER pPrinter
Definition: precomp.h:167
TW_UINT32 TW_UINT16 TW_UINT16 TW_MEMREF pData
Definition: twain.h:1827
static DWORD _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_1W pJobInfo)
Definition: jobs.c:721
BOOL WINAPI ReadFile(IN HANDLE hFile, IN LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:123
PWSTR pwszStatus
Definition: precomp.h:150
BYTE * PBYTE
Definition: pedump.c:66
struct _JOB_INFO_2W JOB_INFO_2W
const WCHAR wszDefaultDocumentName[]
Definition: main.c:25
PWSTR pwszPrinterName
Definition: precomp.h:115
DWORD Status
Definition: winspool.h:256
size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
SKIPLIST GlobalJobList
Definition: jobs.c:11
DWORD dwLowDateTime
Definition: mapidefs.h:65
#define JOB_STATUS_ERROR
Definition: winspool.h:339
PWSTR pwszUserName
Definition: precomp.h:144
DWORD offPrinterName
Definition: precomp.h:224
#define RPC_S_OK
Definition: rpcnterr.h:22
static DWORD dwJobInfo1Offsets[]
Definition: jobs.c:17
struct _JOB_INFO_1W JOB_INFO_1W
DWORD offPrintProcessorParameters
Definition: precomp.h:229
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
#define ERROR_INSUFFICIENT_BUFFER
Definition: dderror.h:10
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
DWORD dwJobID
Definition: precomp.h:218