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 11 SKIPLIST GlobalJobList; 12 13 // Local Variables 14 static DWORD _dwLastJobID; 15 16 // Local Constants 17 static DWORD dwJobInfo1Offsets[] = { 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 27 static DWORD dwJobInfo2Offsets[] = { 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 42 /** 43 * @name _EqualStrings 44 * 45 * Returns whether two strings are equal. 46 * Unlike wcscmp, this function also works with NULL strings. 47 * 48 * @param pwszA 49 * First string to compare. 50 * 51 * @param pwszB 52 * Second string to compare. 53 * 54 * @return 55 * TRUE if the strings are equal, FALSE if they differ. 56 */ 57 static __inline BOOL 58 _EqualStrings(PCWSTR pwszA, PCWSTR pwszB) 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 73 _GetNextJobID(PDWORD dwJobID) 74 { 75 ++_dwLastJobID; 76 77 while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL)) 78 { 79 // This ID is already taken. Try the next one. 80 ++_dwLastJobID; 81 } 82 83 if (!IS_VALID_JOB_ID(_dwLastJobID)) 84 { 85 ERR("Job ID %lu isn't valid!\n", _dwLastJobID); 86 return FALSE; 87 } 88 89 *dwJobID = _dwLastJobID; 90 return TRUE; 91 } 92 93 /** 94 * @name _GlobalJobListCompareRoutine 95 * 96 * SKIPLIST_COMPARE_ROUTINE for the Global Job List. 97 * We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID. 98 */ 99 static int WINAPI 100 _GlobalJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct) 101 { 102 PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct; 103 PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct; 104 105 return A->dwJobID - B->dwJobID; 106 } 107 108 /** 109 * @name _PrinterJobListCompareRoutine 110 * 111 * SKIPLIST_COMPARE_ROUTINE for each Printer's Job List. 112 * Jobs in this list are sorted in the desired order of processing. 113 */ 114 static int WINAPI 115 _PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct) 116 { 117 PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct; 118 PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct; 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 161 InitializeGlobalJobList(void) 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). 181 InitializeSkiplist(&GlobalJobList, DllAllocSplMem, _GlobalJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem); 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. 218 if (!InsertElementSkiplist(&GlobalJobList, pJob)) 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 247 InitializePrinterJobList(PLOCAL_PRINTER pPrinter) 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. 253 InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem); 254 } 255 256 DWORD WINAPI 257 CreateJob(PLOCAL_PRINTER_HANDLE pPrinterHandle) 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; 292 pJob->dwStatus = JOB_STATUS_SPOOLING; 293 pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype); 294 pJob->pwszDocumentName = AllocSplStr(wszDefaultDocumentName); 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. 339 if (!InsertElementSkiplist(&GlobalJobList, pJob)) 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 380 LocalAddJob(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded) 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 450 _LocalGetJobLevel1(PLOCAL_JOB pJob, PJOB_INFO_1W* ppJobInfo, PBYTE* ppJobInfoEnd, PDWORD pcbNeeded) 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 524 _LocalGetJobLevel2(PLOCAL_JOB pJob, PJOB_INFO_2W* ppJobInfo, PBYTE* ppJobInfoEnd, PDWORD pcbNeeded) 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 561 if (pJob->pwszPrintProcessorParameters) 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) 694 _LocalGetJobLevel1(pJob, NULL, NULL, pcbNeeded); 695 else if (Level == 2) 696 _LocalGetJobLevel2(pJob, NULL, NULL, pcbNeeded); 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 721 _LocalSetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_1W pJobInfo) 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 818 _LocalSetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PJOB_INFO_2W pJobInfo) 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! 876 if (!_EqualStrings(pJob->pwszPrintProcessorParameters, pJobInfo->pParameters)) 877 { 878 // Use the new value. 879 if (!ReallocSplStr(&pJob->pwszPrintProcessorParameters, pJobInfo->pParameters)) 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 { 997 if (Command == JOB_CONTROL_SENT_TO_PRINTER) 998 { 999 // This indicates the end of the Print Job. 1000 1001 // Cancel the Job at the Print Processor. 1002 if (pJob->hPrintProcessor) 1003 pJob->pPrintProcessor->pfnControlPrintProcessor(pJob->hPrintProcessor, JOB_CONTROL_CANCEL); 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) 1068 _LocalGetJobLevel1(pJob, NULL, NULL, pcbNeeded); 1069 else if (Level == 2) 1070 _LocalGetJobLevel2(pJob, NULL, NULL, pcbNeeded); 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 { 1114 DWORD dwAttributes; 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); 1153 if (dwAttributes == INVALID_FILE_ATTRIBUTES || dwAttributes & FILE_ATTRIBUTE_DIRECTORY) 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. 1167 hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PrintingThreadProc, pJob, 0, NULL); 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. 1176 CloseHandle(hThread); 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 1187 ReadJobShadowFile(PCWSTR pwszFilePath) 1188 { 1189 DWORD cbFileSize; 1190 DWORD cbRead; 1191 HANDLE hFile = INVALID_HANDLE_VALUE; 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. 1203 hFile = CreateFileW(pwszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 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) 1279 pJob->pwszPrintProcessorParameters = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + 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 1452 FreeJob(PLOCAL_JOB pJob) 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); 1460 DeleteElementSkiplist(&GlobalJobList, 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); 1470 DllFreeSplStr(pJob->pwszDocumentName); 1471 DllFreeSplStr(pJob->pwszMachineName); 1472 DllFreeSplStr(pJob->pwszNotifyName); 1473 DllFreeSplStr(pJob->pwszUserName); 1474 1475 // Free memory for the optional fields if they are present. 1476 if (pJob->pwszOutputFile) 1477 DllFreeSplStr(pJob->pwszOutputFile); 1478 1479 if (pJob->pwszPrintProcessorParameters) 1480 DllFreeSplStr(pJob->pwszPrintProcessorParameters); 1481 1482 if (pJob->pwszStatus) 1483 DllFreeSplStr(pJob->pwszStatus); 1484 1485 // Finally free the job structure itself. 1486 DllFreeSplMem(pJob); 1487 } 1488