Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3714 lines
100 KiB

  1. /*++
  2. Copyright (c) 1990 - 1996 Microsoft Corporation
  3. Module Name:
  4. job.c
  5. Abstract:
  6. This module provides all the public exported APIs relating to Printer
  7. and Job management for the Local Print Providor
  8. Author:
  9. Dave Snipp (DaveSn) 15-Mar-1991
  10. Revision History:
  11. MattFe 23-Feb-96 JobInfo3
  12. --*/
  13. #include <precomp.h>
  14. #pragma hdrstop
  15. #include <offsets.h>
  16. #include "jobid.h"
  17. #include "filepool.hxx"
  18. #define JOB_STATUS_INTERNAL 0
  19. #define JOB_STATUS_EXTERNAL 1
  20. DWORD SettableJobStatusMappings[] = {
  21. // INTERNAL: EXTERNAL:
  22. JOB_PAUSED, JOB_STATUS_PAUSED,
  23. JOB_ERROR, JOB_STATUS_ERROR,
  24. JOB_OFFLINE, JOB_STATUS_OFFLINE,
  25. JOB_PAPEROUT, JOB_STATUS_PAPEROUT,
  26. 0, 0
  27. };
  28. DWORD ReadableJobStatusMappings[] = {
  29. // INTERNAL: EXTERNAL:
  30. JOB_PAUSED, JOB_STATUS_PAUSED,
  31. JOB_ERROR, JOB_STATUS_ERROR,
  32. JOB_PENDING_DELETION, JOB_STATUS_DELETING,
  33. JOB_SPOOLING, JOB_STATUS_SPOOLING,
  34. JOB_PRINTING, JOB_STATUS_PRINTING,
  35. JOB_COMPLETE, JOB_STATUS_COMPLETE,
  36. JOB_OFFLINE, JOB_STATUS_OFFLINE,
  37. JOB_PAPEROUT, JOB_STATUS_PAPEROUT,
  38. JOB_PRINTED, JOB_STATUS_PRINTED,
  39. JOB_BLOCKED_DEVQ, JOB_STATUS_BLOCKED_DEVQ,
  40. JOB_DELETED, JOB_STATUS_DELETED,
  41. JOB_HIDDEN, JOB_STATUS_DELETED,
  42. JOB_RESTART, JOB_STATUS_RESTART,
  43. 0, 0
  44. };
  45. DWORD gdwZombieCount = 0;
  46. DWORD
  47. MapJobStatus(
  48. DWORD Type,
  49. DWORD SourceStatus)
  50. {
  51. DWORD TargetStatus;
  52. PDWORD pMappings;
  53. INT MapFrom;
  54. INT MapTo;
  55. if (Type == MAP_READABLE) {
  56. MapFrom = JOB_STATUS_INTERNAL;
  57. MapTo = JOB_STATUS_EXTERNAL;
  58. pMappings = ReadableJobStatusMappings;
  59. } else {
  60. MapFrom = JOB_STATUS_EXTERNAL;
  61. MapTo = JOB_STATUS_INTERNAL;
  62. pMappings = SettableJobStatusMappings;
  63. }
  64. TargetStatus = 0;
  65. while(*pMappings) {
  66. if (SourceStatus & pMappings[MapFrom])
  67. TargetStatus |= pMappings[MapTo];
  68. pMappings += 2;
  69. }
  70. return TargetStatus;
  71. }
  72. PINIJOB
  73. FindJob(
  74. PINIPRINTER pIniPrinter,
  75. DWORD JobId,
  76. PDWORD pPosition)
  77. {
  78. PINIJOB pIniJob;
  79. SplInSem();
  80. for (pIniJob = pIniPrinter->pIniFirstJob, *pPosition = 1;
  81. pIniJob;
  82. pIniJob = pIniJob->pIniNextJob, (*pPosition)++) {
  83. if (pIniJob->JobId == JobId)
  84. return pIniJob;
  85. }
  86. *pPosition = JOB_POSITION_UNSPECIFIED;
  87. return (NULL);
  88. }
  89. PINIJOB
  90. FindServerJob(
  91. PINISPOOLER pIniSpooler,
  92. DWORD JobId,
  93. PDWORD pdwPosition,
  94. PINIPRINTER* ppIniPrinter
  95. )
  96. /*++
  97. Routine Description:
  98. Finds a pIniJob, position, and pIniPrinter based on a JobId and
  99. pIniSpooler. This works because JobIds are unique across pIniSpoolers.
  100. Arguments:
  101. pIniSpooler - pIniSpooler to search
  102. JobId - Job to search for.
  103. pdwPosition - When a valid pIniJob is returned, this is the position in
  104. the queue of the returned job.
  105. ppIniPrinter - When a valid pIniJob is returned, this is the queue
  106. that the job belongs to.
  107. Return Value:
  108. PINIJOB if success,
  109. NULL if not found (LastError NOT set)
  110. --*/
  111. {
  112. DWORD dwPosition;
  113. PINIJOB pIniJob;
  114. SplInSem();
  115. for( *ppIniPrinter = pIniSpooler->pIniPrinter;
  116. *ppIniPrinter;
  117. *ppIniPrinter = (*ppIniPrinter)->pNext ){
  118. if( pIniJob = FindJob( *ppIniPrinter, JobId, pdwPosition )){
  119. return pIniJob;
  120. }
  121. }
  122. return NULL;
  123. }
  124. PINIJOB
  125. FindIniJob (
  126. PSPOOL pSpool,
  127. DWORD JobId
  128. )
  129. {
  130. PINIJOB pIniJob = NULL;
  131. PINIPRINTER pIniPrinter = NULL;
  132. DWORD dwPosition;
  133. if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER)
  134. {
  135. //
  136. // If it's a server handle, then search all jobs on this spooler.
  137. // This call also retrieves the pIniPrinter associated
  138. // with a print job. pIniPrinter is not needed, but FindServerJob
  139. // requires a valid pointer.
  140. //
  141. pIniJob = FindServerJob(pSpool->pIniSpooler,
  142. JobId,
  143. &dwPosition,
  144. &pIniPrinter);
  145. }
  146. else
  147. {
  148. pIniJob = FindJob(pSpool->pIniPrinter, JobId, &dwPosition);
  149. }
  150. return pIniJob;
  151. }
  152. DWORD
  153. GetJobSessionId (
  154. PSPOOL pSpool,
  155. DWORD JobId
  156. )
  157. {
  158. DWORD SessionId = -1;
  159. PINIJOB pIniJob = NULL;
  160. EnterSplSem();
  161. if (ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER))
  162. {
  163. pIniJob = FindIniJob(pSpool, JobId);
  164. if (pIniJob)
  165. {
  166. SessionId = pIniJob->SessionId;
  167. }
  168. }
  169. LeaveSplSem();
  170. return SessionId;
  171. }
  172. BOOL
  173. SetJobPosition(
  174. PINIJOB pIniSetJob,
  175. DWORD NewPosition
  176. )
  177. {
  178. PINIJOB pIniJob;
  179. PINIJOB pIniPrevJob;
  180. DWORD Position;
  181. PINISPOOLER pIniSpooler = NULL;
  182. SPLASSERT( pIniSetJob != NULL );
  183. SPLASSERT( pIniSetJob->pIniPrinter != NULL );
  184. SPLASSERT( pIniSetJob->pIniPrinter->pIniSpooler != NULL );
  185. pIniSpooler = pIniSetJob->pIniPrinter->pIniSpooler;
  186. SplInSem();
  187. /* Remove this job from the linked list, and
  188. * link the jobs either side of the one we're repositioning:
  189. */
  190. if (pIniSetJob->pIniPrevJob)
  191. pIniSetJob->pIniPrevJob->pIniNextJob = pIniSetJob->pIniNextJob;
  192. else
  193. pIniSetJob->pIniPrinter->pIniFirstJob = pIniSetJob->pIniNextJob;
  194. if (pIniSetJob->pIniNextJob)
  195. pIniSetJob->pIniNextJob->pIniPrevJob = pIniSetJob->pIniPrevJob;
  196. else
  197. pIniSetJob->pIniPrinter->pIniLastJob = pIniSetJob->pIniPrevJob;
  198. pIniJob = pIniSetJob->pIniPrinter->pIniFirstJob;
  199. pIniPrevJob = NULL;
  200. /* Find the new position for the job:
  201. */
  202. Position = 1;
  203. while (pIniJob && (Position < NewPosition)) {
  204. pIniPrevJob = pIniJob;
  205. pIniJob = pIniJob->pIniNextJob;
  206. Position++;
  207. }
  208. /* If we're at position 1, pIniPrevJob == NULL,
  209. * if we're at the end of the list, pIniJob == NULL.
  210. */
  211. /* Now fix up the new links:
  212. */
  213. pIniSetJob->pIniPrevJob = pIniPrevJob;
  214. pIniSetJob->pIniNextJob = pIniJob;
  215. if (pIniPrevJob)
  216. pIniPrevJob->pIniNextJob = pIniSetJob;
  217. else
  218. pIniSetJob->pIniPrinter->pIniFirstJob = pIniSetJob;
  219. if (pIniSetJob->pIniNextJob)
  220. pIniSetJob->pIniNextJob->pIniPrevJob = pIniSetJob;
  221. else
  222. pIniSetJob->pIniPrinter->pIniLastJob = pIniSetJob;
  223. INCJOBREF( pIniSetJob );
  224. LogJobInfo(
  225. pIniSpooler,
  226. MSG_DOCUMENT_POSITION_CHANGED,
  227. pIniSetJob->JobId,
  228. pIniSetJob->pDocument,
  229. pIniSetJob->pUser,
  230. pIniSetJob->pIniPrinter->pName,
  231. NewPosition
  232. );
  233. DECJOBREF( pIniSetJob );
  234. return TRUE;
  235. }
  236. #if DBG
  237. /* For the debug message:
  238. */
  239. #define HOUR_FROM_MINUTES(Time) ((Time) / 60)
  240. #define MINUTE_FROM_MINUTES(Time) ((Time) % 60)
  241. /* Format for %02d:%02d replaceable string:
  242. */
  243. #define FORMAT_HOUR_MIN(Time) HOUR_FROM_MINUTES(Time), \
  244. MINUTE_FROM_MINUTES(Time)
  245. #endif
  246. BOOL
  247. ValidateJobTimes(
  248. PINIJOB pIniJob,
  249. LPJOB_INFO_2 pJob2
  250. )
  251. {
  252. BOOL TimesAreValid = FALSE;
  253. PINIPRINTER pIniPrinter;
  254. pIniPrinter = pIniJob->pIniPrinter;
  255. DBGMSG(DBG_TRACE, ("Validating job times\n"
  256. "\tPrinter hours: %02d:%02d to %02d:%02d\n"
  257. "\tJob hours: %02d:%02d to %02d:%02d\n",
  258. FORMAT_HOUR_MIN(pIniPrinter->StartTime),
  259. FORMAT_HOUR_MIN(pIniPrinter->UntilTime),
  260. FORMAT_HOUR_MIN(pJob2->StartTime),
  261. FORMAT_HOUR_MIN(pJob2->UntilTime)));
  262. if ((pJob2->StartTime < ONEDAY) && (pJob2->UntilTime < ONEDAY)) {
  263. if ((pJob2->StartTime == pIniJob->StartTime)
  264. &&(pJob2->UntilTime == pIniJob->UntilTime)) {
  265. DBGMSG(DBG_TRACE, ("Times are unchanged\n"));
  266. TimesAreValid = TRUE;
  267. } else {
  268. /* New time must be wholly within the window between StartTime
  269. * and UntilTime of the printer.
  270. */
  271. if (pIniPrinter->StartTime > pIniPrinter->UntilTime) {
  272. /* E.g. StartTime = 20:00
  273. * UntilTime = 06:00
  274. *
  275. * This spans midnight, so check we're not in the period
  276. * between UntilTime and StartTime:
  277. */
  278. if (pJob2->StartTime > pJob2->UntilTime) {
  279. /* This appears to span midnight too.
  280. * Make sure the window fits in the printer's window:
  281. */
  282. if ((pJob2->StartTime >= pIniPrinter->StartTime)
  283. &&(pJob2->UntilTime <= pIniPrinter->UntilTime)) {
  284. TimesAreValid = TRUE;
  285. } else {
  286. DBGMSG(DBG_TRACE, ("Failed test 2\n"));
  287. }
  288. } else {
  289. if ((pJob2->StartTime >= pIniPrinter->StartTime)
  290. &&(pJob2->UntilTime > pIniPrinter->StartTime)) {
  291. TimesAreValid = TRUE;
  292. } else if ((pJob2->UntilTime < pIniPrinter->UntilTime)
  293. &&(pJob2->StartTime < pIniPrinter->UntilTime)) {
  294. TimesAreValid = TRUE;
  295. } else {
  296. DBGMSG(DBG_TRACE, ("Failed test 3\n"));
  297. }
  298. }
  299. } else if (pIniPrinter->StartTime < pIniPrinter->UntilTime) {
  300. /* E.g. StartTime = 08:00
  301. * UntilTime = 18:00
  302. */
  303. if ((pJob2->StartTime >= pIniPrinter->StartTime)
  304. &&(pJob2->UntilTime <= pIniPrinter->UntilTime)
  305. &&(pJob2->StartTime <= pJob2->UntilTime)) {
  306. TimesAreValid = TRUE;
  307. } else {
  308. DBGMSG(DBG_TRACE, ("Failed test 4\n"));
  309. }
  310. } else {
  311. /* Printer times are round the clock:
  312. */
  313. TimesAreValid = TRUE;
  314. }
  315. }
  316. } else {
  317. TimesAreValid = FALSE;
  318. }
  319. DBGMSG(DBG_TRACE, ("Times are %svalid\n", TimesAreValid ? "" : "in"));
  320. return TimesAreValid;
  321. }
  322. /*++
  323. Routine Name:
  324. CircularChainedJobsList
  325. Routine Description:
  326. Check if chaining 2 jobs together will cause us to have a circular chain of jobs.
  327. This thing is not allowed.
  328. Arguments:
  329. pIniJob - pointer to the job that we want to link
  330. pNextIniJob - pointer to the job to which we want to link
  331. Return Value:
  332. TRUE - if chaining the jobs together builds a circular list
  333. FALSE - if chaining the jobs together is allowed
  334. Last Error:
  335. None
  336. --*/
  337. BOOL
  338. CircularChainedJobsList(
  339. IN PINIJOB pIniJob,
  340. IN PINIJOB pNextIniJob
  341. )
  342. {
  343. BOOL bCircular = FALSE;
  344. //
  345. // Validate input parameters
  346. //
  347. if (pIniJob && pNextIniJob)
  348. {
  349. DWORD Position;
  350. //
  351. // Traverse chained list of jobs. Try to arrive from pNextIniJob->JobId to pIniJob->JobId
  352. //
  353. while (pNextIniJob = FindJob(pIniJob->pIniPrinter, pNextIniJob->NextJobId, &Position))
  354. {
  355. DBGMSG(DBG_TRACE, ("CircularChainedJobsList job %u\n", pNextIniJob->JobId));
  356. if (pNextIniJob->JobId == pIniJob->JobId)
  357. {
  358. bCircular = TRUE;
  359. break;
  360. }
  361. }
  362. }
  363. return bCircular;
  364. }
  365. DWORD
  366. SetLocalJob(
  367. HANDLE hPrinter,
  368. PINIJOB pIniJob,
  369. DWORD Level,
  370. LPBYTE pJob
  371. )
  372. /*++
  373. Routine Description:
  374. Sets information about a localspl job.
  375. Arguments:
  376. hPrinter - Handle to printer OR server. Since this is could be a
  377. server, the pSpool->pIniPrinter is not always valid!
  378. Use pIniJob->pIniPrinter instead of pSpool->pIniPrinter.
  379. pIniJob - Job that should be set
  380. Level - Level of pJob structure
  381. pJob - New information to set
  382. Return Value:
  383. ERROR_SUCCESS for success, else error code.
  384. Notes:
  385. The 3.51 spooler has been changed to accept server handles since
  386. net\dosprint\dosprtw.c does not have a printername, just a job id.
  387. This relies on the fact that job ids are unique across a pIniSpooler.
  388. To move a job with a server pSpool, you need administrative access
  389. on the server handle.
  390. The TotalPages and PagesPrinted fields can no longer be set.
  391. Otherwise, users can change the number of pages in their jobs to 0,
  392. and get charged a lot less (some people charge based on eventlog
  393. pagecounts). Also, hpmon does a GetJob/SetJob to set the status,
  394. and sometimes the page count changes between the Get and Set.
  395. --*/
  396. {
  397. LPJOB_INFO_2 pJob2 = (PJOB_INFO_2)pJob;
  398. LPJOB_INFO_1 pJob1 = (PJOB_INFO_1)pJob;
  399. LPJOB_INFO_3 pJob3 = (PJOB_INFO_3)pJob;
  400. PINIPRINTPROC pIniPrintProc;
  401. PINIJOB pOldJob;
  402. DWORD OldJobId;
  403. PINIJOB pNextIniJob;
  404. DWORD dwPosition;
  405. DWORD ReturnValue = ERROR_SUCCESS;
  406. LPDEVMODE pDevMode;
  407. PINISPOOLER pIniSpooler = NULL;
  408. PINIENVIRONMENT pIniEnvironment = NULL;
  409. PSPOOL pSpool = (PSPOOL)hPrinter;
  410. DWORD OldStatus;
  411. DWORD dwJobVector = 0;
  412. NOTIFYVECTOR NotifyVector;
  413. ZERONV(NotifyVector);
  414. SplInSem();
  415. switch (Level) {
  416. case 1:
  417. if (!pJob1->pDatatype ||
  418. !CheckDataTypes(pIniJob->pIniPrintProc, pJob1->pDatatype)) {
  419. return ERROR_INVALID_DATATYPE;
  420. }
  421. if (pJob1->Position != JOB_POSITION_UNSPECIFIED) {
  422. //
  423. // Check for Administer privilege on the printer
  424. // if the guy wants to reorder the job:
  425. //
  426. if (!AccessGranted(SPOOLER_OBJECT_PRINTER,
  427. PRINTER_ACCESS_ADMINISTER,
  428. pSpool)) {
  429. return ERROR_ACCESS_DENIED;
  430. }
  431. SetJobPosition(pIniJob, pJob1->Position);
  432. dwJobVector |= BIT(I_JOB_POSITION);
  433. }
  434. if (pJob1->Priority <= MAX_PRIORITY) {
  435. if (pIniJob->Priority != pJob1->Priority) {
  436. pIniJob->Priority = pJob1->Priority;
  437. dwJobVector |= BIT(I_JOB_PRIORITY);
  438. }
  439. }
  440. if (UpdateString(&pIniJob->pUser, pJob1->pUserName)) {
  441. dwJobVector |= BIT(I_JOB_USER_NAME);
  442. }
  443. if (UpdateString(&pIniJob->pDocument, pJob1->pDocument)) {
  444. dwJobVector |= BIT(I_JOB_DOCUMENT);
  445. }
  446. if (UpdateString(&pIniJob->pDatatype, pJob1->pDatatype)) {
  447. dwJobVector |= BIT(I_JOB_DATATYPE);
  448. }
  449. if (UpdateString(&pIniJob->pStatus, pJob1->pStatus)) {
  450. dwJobVector |= BIT(I_JOB_STATUS_STRING);
  451. }
  452. OldStatus = pIniJob->Status;
  453. pIniJob->Status &= JOB_STATUS_PRIVATE;
  454. pIniJob->Status |= MapJobStatus(MAP_SETTABLE,
  455. pJob1->Status);
  456. if (OldStatus != pIniJob->Status) {
  457. dwJobVector |= BIT(I_JOB_STATUS);
  458. }
  459. break;
  460. case 2:
  461. //
  462. // The local spooler and cluster spooler do not share the same Environment structures.
  463. //
  464. pIniEnvironment = GetLocalArchEnv(pIniJob->pIniPrinter->pIniSpooler);
  465. pIniPrintProc = FindPrintProc(pJob2->pPrintProcessor, pIniEnvironment);
  466. if (!pIniPrintProc) {
  467. return ERROR_UNKNOWN_PRINTPROCESSOR;
  468. }
  469. if( !pJob2->pDatatype ||
  470. !CheckDataTypes(pIniPrintProc, pJob2->pDatatype)) {
  471. return ERROR_INVALID_DATATYPE;
  472. }
  473. if (pJob2->Position != JOB_POSITION_UNSPECIFIED) {
  474. //
  475. // Check for Administer privilege on the printer
  476. // if the guy wants to reorder the job:
  477. //
  478. if (!AccessGranted(SPOOLER_OBJECT_PRINTER,
  479. PRINTER_ACCESS_ADMINISTER,
  480. pSpool)) {
  481. return ERROR_ACCESS_DENIED;
  482. }
  483. }
  484. if (ValidateJobTimes(pIniJob, pJob2)) {
  485. if (pIniJob->StartTime != pJob2->StartTime) {
  486. pIniJob->StartTime = pJob2->StartTime;
  487. dwJobVector |= BIT(I_JOB_START_TIME);
  488. }
  489. if (pIniJob->UntilTime != pJob2->UntilTime) {
  490. pIniJob->UntilTime = pJob2->UntilTime;
  491. dwJobVector |= BIT(I_JOB_UNTIL_TIME);
  492. }
  493. } else {
  494. return ERROR_INVALID_TIME;
  495. }
  496. if (pJob2->Position != JOB_POSITION_UNSPECIFIED) {
  497. SetJobPosition(pIniJob, pJob2->Position);
  498. dwJobVector |= BIT(I_JOB_POSITION);
  499. }
  500. //
  501. // We really need some error returns here.
  502. //
  503. if (pJob2->Priority <= MAX_PRIORITY) {
  504. if (pIniJob->Priority != pJob2->Priority) {
  505. pIniJob->Priority = pJob2->Priority;
  506. dwJobVector |= BIT(I_JOB_PRIORITY);
  507. }
  508. }
  509. if (pIniJob->pIniPrintProc != pIniPrintProc) {
  510. pIniJob->pIniPrintProc->cRef--;
  511. pIniJob->pIniPrintProc = pIniPrintProc;
  512. pIniJob->pIniPrintProc->cRef++;
  513. dwJobVector |= BIT(I_JOB_PRINT_PROCESSOR);
  514. }
  515. if (UpdateString(&pIniJob->pUser, pJob2->pUserName)) {
  516. dwJobVector |= BIT(I_JOB_USER_NAME);
  517. }
  518. if (UpdateString(&pIniJob->pDocument, pJob2->pDocument)) {
  519. dwJobVector |= BIT(I_JOB_DOCUMENT);
  520. }
  521. if (UpdateString(&pIniJob->pNotify, pJob2->pNotifyName)) {
  522. dwJobVector |= BIT(I_JOB_NOTIFY_NAME);
  523. }
  524. if (UpdateString(&pIniJob->pDatatype, pJob2->pDatatype)) {
  525. dwJobVector |= BIT(I_JOB_DATATYPE);
  526. }
  527. if (UpdateString(&pIniJob->pParameters, pJob2->pParameters)) {
  528. dwJobVector |= BIT(I_JOB_PARAMETERS);
  529. }
  530. // <<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  531. #if 0 // NOT COMPILED BECAUSE SPOOLSS\CLIENT does NOT have a DevMode container
  532. // So its never passed to us correctly, we'd AV
  533. //
  534. // Update the DevMode
  535. //
  536. if (pJob2->pDevMode) {
  537. if ( pIniJob->pDevMode &&
  538. pIniJob->pDevMode->dmSize == pJob2->pDevMode->dmSize &&
  539. pIniJob->pDevMode->dmDriverExtra == pJob2->pDevMode->dmDriverExtra &&
  540. !memcmp( pIniJob->pDevMode, pJob2->pDevMode, ( pIniJob->pDevMode->dmSize + pIniJob->pDevMode->dmDriverExtra ))) {
  541. //
  542. // DevModes are the same don't do anything
  543. //
  544. } else {
  545. pDevMode = AllocDevMode( pJob2->pDevMode );
  546. if ( !pDevMode ) {
  547. return GetLastError();
  548. }
  549. if ( pIniJob->pDevMode )
  550. FreeSplMem( pIniJob->pDevMode );
  551. pIniJob->pDevMode = pDevMode;
  552. dwJobVector |= BIT(I_JOB_DEVMODE);
  553. }
  554. }
  555. #endif // END NOT COMPILED <<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>.
  556. if (UpdateString(&pIniJob->pStatus, pJob2->pStatus)) {
  557. dwJobVector |= BIT(I_JOB_STATUS_STRING);
  558. }
  559. OldStatus = pIniJob->Status;
  560. pIniJob->Status &= JOB_STATUS_PRIVATE;
  561. pIniJob->Status |= MapJobStatus(MAP_SETTABLE,
  562. pJob2->Status);
  563. if (OldStatus != pIniJob->Status) {
  564. dwJobVector |= BIT(I_JOB_STATUS);
  565. }
  566. break;
  567. case 3:
  568. // SetJob with Job_info_3
  569. // The goal is to tell the scheduler the printer order of jobs
  570. // so that they can be chained together. This is first implemented
  571. // so that FAX applications can print multiple cover sheets and point to
  572. // the same print document. Each cover sheet / FAX job might be successful
  573. // or might fail to print - so status will be shown against the MasterJob
  574. // the first job in the chain.
  575. // Subsequent Jobs in the chain are then considered to be part of the main document.
  576. SplInSem();
  577. // Validate that the NextJob exists
  578. pNextIniJob = FindJob( pIniJob->pIniPrinter, pJob3->NextJobId, &dwPosition );
  579. //
  580. // Check for Errors. Note that we only chain jobs that have the same data type.
  581. // Also, once you chain a job, you can't chain it to a different job anymore
  582. //
  583. if (pNextIniJob == NULL ||
  584. pNextIniJob == pIniJob ||
  585. pIniJob->JobId != pJob3->JobId ||
  586. pJob3->Reserved != 0 ||
  587. pIniJob->NextJobId != 0 ||
  588. CircularChainedJobsList(pIniJob, pNextIniJob) ||
  589. _wcsicmp(pIniJob->pDatatype, pNextIniJob->pDatatype)) {
  590. return ERROR_INVALID_PARAMETER;
  591. }
  592. //
  593. // Check Access to the chained job
  594. //
  595. if ( !ValidateObjectAccess( SPOOLER_OBJECT_DOCUMENT,
  596. JOB_ACCESS_ADMINISTER,
  597. pNextIniJob,
  598. NULL,
  599. pNextIniJob->pIniPrinter->pIniSpooler ) ) {
  600. DBGMSG( DBG_WARNING,
  601. ( "LocalSetJob failed ValidateObjectAccess JobId %d pNextIniJob %x, error %d\n",
  602. pNextIniJob->JobId, pNextIniJob, GetLastError()));
  603. return GetLastError();
  604. }
  605. if ( (pIniJob->Status & JOB_DESPOOLING) ||
  606. (pNextIniJob->Status & JOB_DESPOOLING) ) {
  607. return ERROR_INVALID_PRINTER_STATE;
  608. }
  609. // LATER this code has no check for a circular list.
  610. // Save Old Pointer, incase we want to delete it.
  611. OldJobId = pIniJob->NextJobId;
  612. // Point the Current Job to user specified new job
  613. // and increment its reference count.
  614. pIniJob->NextJobId = pJob3->NextJobId;
  615. pNextIniJob->Status |= ( JOB_COMPOUND | JOB_HIDDEN );
  616. INCJOBREF( pNextIniJob );
  617. //
  618. // Page count/Size for the head job should include the other job also
  619. //
  620. pIniJob->cPages += pNextIniJob->cPages;
  621. pIniJob->Size += pNextIniJob->Size;
  622. // If there was an old reference then decrement its reference count
  623. // check for deletion.
  624. if ( OldJobId ) {
  625. pOldJob = FindJob( pIniJob->pIniPrinter, OldJobId, &dwPosition );
  626. DECJOBREF( pOldJob );
  627. if ( (pOldJob->Status & JOB_COMPOUND) &&
  628. (pOldJob->cRef == 0) ) {
  629. pOldJob->Status &= ~( JOB_COMPOUND | JOB_HIDDEN );
  630. WriteShadowJob(pOldJob, FALSE);
  631. }
  632. DeleteJobCheck( pOldJob );
  633. }
  634. //
  635. // Hide the Compound Job from the UI, by making it look deleted
  636. //
  637. SetPrinterChange( pNextIniJob->pIniPrinter,
  638. pNextIniJob,
  639. NVDeletedJob,
  640. PRINTER_CHANGE_DELETE_JOB | PRINTER_CHANGE_SET_PRINTER,
  641. pNextIniJob->pIniPrinter->pIniSpooler );
  642. break;
  643. }
  644. //
  645. // Log an event if the priority of the job changed
  646. //
  647. if (dwJobVector & BIT(I_JOB_PRIORITY)) {
  648. LogJobInfo(pIniJob->pIniPrinter->pIniSpooler,
  649. MSG_DOCUMENT_PRIORITY_CHANGED,
  650. pIniJob->JobId,
  651. pIniJob->pDocument,
  652. pIniJob->pUser,
  653. pIniJob->pIniPrinter->pName,
  654. pIniJob->Priority);
  655. }
  656. CHECK_SCHEDULER();
  657. NotifyVector[JOB_NOTIFY_TYPE] = dwJobVector;
  658. SetPrinterChange(pIniJob->pIniPrinter,
  659. pIniJob,
  660. NotifyVector,
  661. PRINTER_CHANGE_SET_JOB,
  662. pSpool->pIniSpooler);
  663. //
  664. // if something important changed in the Job
  665. // we should update the shadowjob
  666. //
  667. if ( pIniJob &&
  668. ( Level == 3 ||
  669. ( dwJobVector & ~(BIT(I_JOB_STATUS_STRING))))) {
  670. WriteShadowJob( pIniJob, FALSE );
  671. }
  672. return NO_ERROR;
  673. }
  674. BOOL
  675. PauseJob(
  676. PINIJOB pIniJob)
  677. {
  678. PINISPOOLER pIniSpooler = NULL;
  679. PINIPORT pIniPort = NULL;
  680. BOOL ReturnValue = TRUE;
  681. SplInSem();
  682. pIniJob->Status |= JOB_PAUSED;
  683. WriteShadowJob(pIniJob, FALSE);
  684. if(pIniJob->pIniPrintProc)
  685. {
  686. INCJOBREF(pIniJob);
  687. if (pIniJob->pIniPort && !(pIniJob->pIniPort->InCriticalSection & PRINTPROC_PAUSE))
  688. {
  689. //
  690. // Capture the pIniPort so that the InCriticalSection operations we
  691. // apply to it are at least consistent.
  692. //
  693. pIniPort = pIniJob->pIniPort;
  694. INCPORTREF(pIniPort);
  695. LeaveSplSem();
  696. EnterCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  697. EnterSplSem();
  698. pIniPort->InCriticalSection |= PRINTPROC_PAUSE;
  699. if (pIniJob->Status & JOB_PRINTING )
  700. {
  701. if (pIniPort->hProc)
  702. {
  703. LeaveSplSem();
  704. ReturnValue = (*pIniJob->pIniPrintProc->Control)(pIniPort->hProc, JOB_CONTROL_PAUSE );
  705. EnterSplSem();
  706. }
  707. }
  708. pIniPort->InCriticalSection &= ~PRINTPROC_PAUSE;
  709. LeaveCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  710. DECPORTREF(pIniPort);
  711. }
  712. DECJOBREF(pIniJob);
  713. }
  714. DBGMSG( DBG_INFO, ( "Paused Job %d; Status = %08x\n", pIniJob->JobId, pIniJob->Status ) );
  715. SPLASSERT( pIniJob != NULL &&
  716. pIniJob->pIniPrinter != NULL &&
  717. pIniJob->pIniPrinter->pIniSpooler != NULL );
  718. pIniSpooler = pIniJob->pIniPrinter->pIniSpooler;
  719. INCJOBREF( pIniJob );
  720. LogJobInfo(
  721. pIniSpooler,
  722. MSG_DOCUMENT_PAUSED,
  723. pIniJob->JobId,
  724. pIniJob->pDocument,
  725. pIniJob->pUser,
  726. pIniJob->pIniPrinter->pName,
  727. 0);
  728. DECJOBREF( pIniJob );
  729. return ReturnValue;
  730. }
  731. BOOL
  732. ResumeJob(
  733. PINIJOB pIniJob
  734. )
  735. {
  736. PINISPOOLER pIniSpooler = NULL;
  737. PINIPORT pIniPort = NULL;
  738. BOOL ReturnValue = TRUE;
  739. BOOL CheckSchedular = FALSE;
  740. SplInSem();
  741. pIniJob->Status &= ~JOB_PAUSED;
  742. WriteShadowJob(pIniJob, FALSE);
  743. if(pIniJob->pIniPrintProc)
  744. {
  745. INCJOBREF(pIniJob);
  746. if (pIniJob->pIniPort && !(pIniJob->pIniPort->InCriticalSection & PRINTPROC_RESUME))
  747. {
  748. pIniPort = pIniJob->pIniPort;
  749. INCPORTREF(pIniPort);
  750. LeaveSplSem();
  751. EnterCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  752. EnterSplSem();
  753. pIniPort->InCriticalSection |= PRINTPROC_RESUME;
  754. if ( pIniJob->Status & JOB_PRINTING)
  755. {
  756. if ( pIniPort->hProc )
  757. {
  758. LeaveSplSem();
  759. ReturnValue = (*pIniJob->pIniPrintProc->Control)(pIniPort->hProc, JOB_CONTROL_RESUME);
  760. EnterSplSem();
  761. }
  762. }
  763. else
  764. {
  765. CheckSchedular = TRUE;
  766. }
  767. pIniPort->InCriticalSection &= ~PRINTPROC_RESUME;
  768. LeaveCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  769. DECPORTREF(pIniPort);
  770. }
  771. DECJOBREF(pIniJob);
  772. }
  773. else
  774. {
  775. CheckSchedular = TRUE;
  776. }
  777. if(CheckSchedular)
  778. {
  779. CHECK_SCHEDULER();
  780. }
  781. DBGMSG( DBG_INFO, ( "Resumed Job %d; Status = %08x\n", pIniJob->JobId, pIniJob->Status ) );
  782. SPLASSERT( pIniJob != NULL &&
  783. pIniJob->pIniPrinter != NULL &&
  784. pIniJob->pIniPrinter->pIniSpooler != NULL );
  785. pIniSpooler = pIniJob->pIniPrinter->pIniSpooler;
  786. INCJOBREF( pIniJob );
  787. LogJobInfo(
  788. pIniSpooler,
  789. MSG_DOCUMENT_RESUMED,
  790. pIniJob->JobId,
  791. pIniJob->pDocument,
  792. pIniJob->pUser,
  793. pIniJob->pIniPrinter->pName,
  794. 0);
  795. DECJOBREF( pIniJob );
  796. return ReturnValue;
  797. }
  798. DWORD
  799. RestartJob(
  800. PINIJOB pIniJob
  801. )
  802. {
  803. //
  804. // If jobs is pending deletion can't restart. Monitor could call this
  805. // when there is a port error to reprint the job. If user has already
  806. // deleted the job this should fail
  807. //
  808. if ( pIniJob->Status & JOB_PENDING_DELETION )
  809. return ERROR_INVALID_PARAMETER;
  810. //
  811. // A job can be restarted only if:
  812. // it is currently printing or
  813. // it is printed or sent to printer.
  814. //
  815. if (!(pIniJob->Status & JOB_PRINTING) && !(pIniJob->Status & JOB_PRINTED) && !(pIniJob->Status & JOB_COMPLETE))
  816. {
  817. return ERROR_SUCCESS;
  818. }
  819. // JOB_PRINTING - means you have a print processor open
  820. // JOB_DESPOOLING - means a job have been scheduled, it might be PRINTING
  821. // or might have completed PRINTING but we are still logging etc.
  822. // So be careful if you alter the JOB_PRINTING flag to know everywhere
  823. // it is used.
  824. pIniJob->Status |= JOB_RESTART;
  825. if (pIniJob->pIniPort)
  826. {
  827. pIniJob->pIniPort->InCriticalSection = 0;
  828. }
  829. // Release any thread waiting on SeekPrinter
  830. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  831. // Release any thread waiting on LocalSetPort
  832. SetPortErrorEvent(pIniJob->pIniPort);
  833. //
  834. // JOB_DESPOOLING and JOB_RESTART are checked in the PortThread port.c
  835. //
  836. if (!( pIniJob->Status & JOB_DESPOOLING )) {
  837. pIniJob->Status &= ~( JOB_PRINTED | JOB_BLOCKED_DEVQ | JOB_COMPLETE);
  838. //
  839. // Reset cbPrinted and cPagesPrinted.
  840. //
  841. pIniJob->cbPrinted = 0;
  842. pIniJob->cPagesPrinted = 0;
  843. }
  844. if ( pIniJob->Status & JOB_TIMEOUT ) {
  845. pIniJob->Status &= ~( JOB_TIMEOUT | JOB_ABANDON );
  846. FreeSplStr( pIniJob->pStatus );
  847. pIniJob->pStatus = NULL;
  848. }
  849. SetPrinterChange(pIniJob->pIniPrinter,
  850. pIniJob,
  851. NVJobStatusAndString,
  852. PRINTER_CHANGE_SET_JOB,
  853. pIniJob->pIniPrinter->pIniSpooler);
  854. CHECK_SCHEDULER();
  855. DBGMSG( DBG_INFO, ( "Restarted Job %d; Status = %08x\n", pIniJob->JobId, pIniJob->Status ) );
  856. return 0;
  857. }
  858. BOOL
  859. LocalSetJob(
  860. HANDLE hPrinter,
  861. DWORD JobId,
  862. DWORD Level,
  863. LPBYTE pJob,
  864. DWORD Command
  865. )
  866. /*++
  867. Routine Description:
  868. This function will modify the settings of the specified Print Job.
  869. Arguments:
  870. hPrinter - Handle to printer OR server. Since this is could be a
  871. server, the pSpool->pIniPrinter is not always valid!
  872. Use pIniJob->pIniPrinter instead of pSpool->pIniPrinter.
  873. pJob - Points to a valid JOB structure containing at least a valid
  874. pPrinter, and JobId.
  875. Command - Specifies the operation to perform on the specified Job. A value
  876. of FALSE indicates that only the elements of the JOB structure are to
  877. be examined and set.
  878. Return Value:
  879. TRUE - The operation was successful.
  880. FALSE/NULL - The operation failed. Extended error status is available
  881. using GetLastError.
  882. --*/
  883. {
  884. PINIJOB pIniJob = NULL;
  885. PSPOOL pSpool = (PSPOOL)hPrinter;
  886. DWORD LastError = 0;
  887. DWORD Position;
  888. BOOL rc;
  889. PINISPOOLER pIniSpooler = NULL;
  890. PINIPRINTER pIniPrinter = NULL;
  891. LPWSTR pszDatatype = NULL;
  892. BOOL bValidDatatype = TRUE;
  893. DBGMSG( DBG_TRACE, ( "ENTER LocalSetJob\n" ) );
  894. //
  895. // We only allow RAW to go to downlevel machines (StartDocPrinter
  896. // already checks this). We need to check this here since
  897. // the AddJob optimization tries to send an non-RAW (EMF) file, and
  898. // downlevel servers don't like that.
  899. //
  900. switch( Level ){
  901. case 1:
  902. pszDatatype = ((PJOB_INFO_1)pJob)->pDatatype;
  903. break;
  904. case 2:
  905. pszDatatype = ((PJOB_INFO_2)pJob)->pDatatype;
  906. break;
  907. default:
  908. //
  909. // 0 and 3 are the only other valid levels.
  910. //
  911. SPLASSERT( Level == 0 || Level == 3 );
  912. break;
  913. }
  914. EnterSplSem();
  915. if ( ValidateSpoolHandle(pSpool, 0 ) ) {
  916. pIniSpooler = pSpool->pIniSpooler;
  917. if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) {
  918. //
  919. // If it's a server handle, then search all jobs on this spooler.
  920. // This call also retrieves the pIniPrinter associated
  921. // with a print job.
  922. //
  923. pIniJob = FindServerJob( pIniSpooler,
  924. JobId,
  925. &Position,
  926. &pIniPrinter );
  927. } else if (pSpool->pIniPort && !(pSpool->pIniPort->Status & PP_MONITOR)) {
  928. //
  929. // It's a masq printer. Send the call to the port RPC handle.
  930. //
  931. hPrinter = pSpool->hPort;
  932. if( pszDatatype ){
  933. bValidDatatype = ValidRawDatatype( pszDatatype );
  934. }
  935. LeaveSplSem();
  936. if( bValidDatatype ){
  937. rc = SetJob(hPrinter, JobId, Level, pJob, Command);
  938. } else {
  939. rc = FALSE;
  940. SetLastError( ERROR_INVALID_DATATYPE );
  941. }
  942. DBGMSG( DBG_TRACE, ( "EXIT LocalSetJob, rc = %d, %d", rc, GetLastError( ) ) );
  943. return rc;
  944. } else {
  945. //
  946. // It's a regular printer handle.
  947. //
  948. SPLASSERT( pSpool->pIniPrinter->pIniSpooler != NULL );
  949. SPLASSERT( pSpool->pIniPrinter->pIniSpooler->signature == ISP_SIGNATURE );
  950. SPLASSERT( pSpool->pIniPrinter->pIniSpooler == pSpool->pIniSpooler );
  951. pIniPrinter = pSpool->pIniPrinter;
  952. pIniJob = FindJob( pIniPrinter, JobId, &Position );
  953. }
  954. if ( pIniJob ){
  955. DWORD dwError;
  956. BOOL bGrantAccess;
  957. //
  958. // If we are changing the datatype, and this is a RAW_ONLY
  959. // printer, and the datatype is not a valid RAW datatype,
  960. // then fail the call.
  961. //
  962. if( pszDatatype &&
  963. ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_RAW_ONLY ) &&
  964. !ValidRawDatatype( pszDatatype )){
  965. SetLastError( ERROR_INVALID_DATATYPE );
  966. LeaveSplSem();
  967. DBGMSG( DBG_TRACE, ( "Failed to set to non-RAW datatype (RAW_ONLY)\n" ));
  968. return FALSE;
  969. }
  970. //
  971. // If the LocalSetJob comes from inside the spooler, it won't come over RPC.
  972. // The monitor calls SetJob when the job printed so the spooler will let go
  973. // of the job. If we grant printing but not manage doc privileges to a principal
  974. // then the monitor loaded in the context of the user won't have access to set
  975. // the job. In this case, if the LocalSetJob comes from within the spooler, we
  976. // grant privileges.
  977. //
  978. bGrantAccess = !IsCallViaRPC();
  979. if ( bGrantAccess ||
  980. ValidateObjectAccess(SPOOLER_OBJECT_DOCUMENT,
  981. (Command == JOB_CONTROL_CANCEL ||
  982. Command == JOB_CONTROL_DELETE) ?
  983. DELETE : JOB_ACCESS_ADMINISTER,
  984. pIniJob, NULL, pIniSpooler ) ) {
  985. switch (Command) {
  986. case 0:
  987. break;
  988. case JOB_CONTROL_PAUSE:
  989. //
  990. // WMI Trace Event
  991. //
  992. INCJOBREF(pIniJob);
  993. LeaveSplSem();
  994. LogWmiTraceEvent(pIniJob->JobId, EVENT_TRACE_TYPE_SPL_PAUSE, NULL);
  995. EnterSplSem();
  996. DECJOBREF(pIniJob);
  997. PauseJob(pIniJob);
  998. break;
  999. case JOB_CONTROL_RESUME:
  1000. //
  1001. // WMI Trace Event
  1002. //
  1003. INCJOBREF(pIniJob);
  1004. LeaveSplSem();
  1005. LogWmiTraceEvent(pIniJob->JobId, EVENT_TRACE_TYPE_SPL_RESUME, NULL);
  1006. EnterSplSem();
  1007. DECJOBREF(pIniJob);
  1008. ResumeJob(pIniJob);
  1009. break;
  1010. //
  1011. // JOB_CONTROL_DELETE is meant to delete the job.
  1012. // So remove the JOB_RESTART bit and delete the job
  1013. //
  1014. case JOB_CONTROL_DELETE:
  1015. pIniJob->Status &= ~JOB_RESTART;
  1016. // Fall thru
  1017. pIniJob->dwJobControlsPending = 0;
  1018. DeleteJob(pIniJob,BROADCAST);
  1019. break;
  1020. case JOB_CONTROL_CANCEL:
  1021. //
  1022. // JOB_CONTROL_CANCEL was used by old print monitors
  1023. // because of that we can't remove the JOB_RESTART bit
  1024. //
  1025. //
  1026. // Reset dwJobControlsPending
  1027. // Some old port monitors at EndDoc call SetJob with
  1028. // JOB_CONTROL_CANCEL instead of JOB_CONTROL_SENT_TO_PRINTER,
  1029. // Because of this, dwJobControlsPending is not decremented
  1030. // and the job doesn't get deleted after printing.
  1031. //
  1032. if (!(pIniJob->Status & (JOB_INTERRUPTED | JOB_SPOOLING | JOB_ERROR | JOB_PAPEROUT | JOB_OFFLINE))) {
  1033. pIniJob->Status |= JOB_PRINTED;
  1034. pIniJob->Status &= ~JOB_COMPLETE;
  1035. if ( !(pIniJob->Status & JOB_RESTART) &&
  1036. pIniJob->pCurrentIniJob == NULL ) {
  1037. INCJOBREF(pIniJob);
  1038. LeaveSplSem();
  1039. if (!(pIniJob->dwAlert & JOB_NO_ALERT)) {
  1040. SendJobAlert(pIniJob);
  1041. }
  1042. EnterSplSem();
  1043. DECJOBREF(pIniJob);
  1044. }
  1045. }
  1046. pIniJob->Status &= ~JOB_INTERRUPTED;
  1047. pIniJob->dwJobControlsPending = 0;
  1048. DeleteJob(pIniJob,BROADCAST);
  1049. break;
  1050. case JOB_CONTROL_RESTART:
  1051. if (!(pSpool->TypeofHandle & PRINTER_HANDLE_DIRECT))
  1052. {
  1053. //
  1054. // WMI Trace Event.
  1055. //
  1056. INCJOBREF(pIniJob);
  1057. LeaveSplSem();
  1058. LogWmiTraceEvent(pIniJob->JobId,
  1059. EVENT_TRACE_TYPE_SPL_SPOOLJOB,
  1060. NULL);
  1061. EnterSplSem();
  1062. DECJOBREF(pIniJob);
  1063. LastError = RestartJob( pIniJob );
  1064. }
  1065. else
  1066. LastError = ERROR_INVALID_PRINTER_COMMAND;
  1067. break;
  1068. //
  1069. // With the addition of these commands port monitors should
  1070. // send JOB_CONTROL_SENT_TO_PRINTER when last byte is written
  1071. // to printer, and language monitor (if there is one) should
  1072. // send JOB_CONTROL_LAST_PAGE_EJECTED when the last page
  1073. // has ejected
  1074. //
  1075. case JOB_CONTROL_SENT_TO_PRINTER:
  1076. case JOB_CONTROL_LAST_PAGE_EJECTED:
  1077. #if DBG
  1078. if( !(pIniJob->dwJobControlsPending > 0)){
  1079. DBGMSG( DBG_WARN, ( "LocalSetJob: dwJobsControlsPending > 0\n" ));
  1080. }
  1081. #endif
  1082. if ( --pIniJob->dwJobControlsPending ) {
  1083. //
  1084. // We still have controls pending, so do nothing
  1085. //
  1086. } else {
  1087. if (!(pIniJob->Status & (JOB_INTERRUPTED | JOB_SPOOLING | JOB_ERROR | JOB_PAPEROUT | JOB_OFFLINE))){
  1088. if ((Command == JOB_CONTROL_SENT_TO_PRINTER &&
  1089. !(pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI)) ||
  1090. (Command == JOB_CONTROL_LAST_PAGE_EJECTED)) {
  1091. pIniJob->Status |= JOB_PRINTED;
  1092. pIniJob->Status &= ~JOB_COMPLETE;
  1093. if ( !(pIniJob->Status & JOB_RESTART) &&
  1094. pIniJob->pCurrentIniJob == NULL ) {
  1095. INCJOBREF(pIniJob);
  1096. LeaveSplSem();
  1097. if (!(pIniJob->dwAlert & JOB_NO_ALERT)) {
  1098. SendJobAlert(pIniJob);
  1099. }
  1100. EnterSplSem();
  1101. DECJOBREF(pIniJob);
  1102. }
  1103. }
  1104. pIniJob->Status &= ~JOB_INTERRUPTED;
  1105. }
  1106. if ( pIniJob->pIniPrinter->Attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS ) {
  1107. if ( pIniJob->pStatus ) {
  1108. FreeSplStr(pIniJob->pStatus);
  1109. pIniJob->pStatus = NULL;
  1110. SetPrinterChange(pIniJob->pIniPrinter,
  1111. pIniJob,
  1112. NVJobStatusAndString,
  1113. PRINTER_CHANGE_SET_JOB,
  1114. pIniJob->pIniPrinter->pIniSpooler );
  1115. }
  1116. } else if ( pIniJob->pCurrentIniJob == NULL ||
  1117. pIniJob->pCurrentIniJob->NextJobId == 0 ) {
  1118. DeleteJob(pIniJob,BROADCAST);
  1119. }
  1120. }
  1121. break;
  1122. default:
  1123. LastError = ERROR_INVALID_PARAMETER;
  1124. break;
  1125. }
  1126. // If we managed to successfully complete the operation
  1127. // specified by Command, let's go do the set job
  1128. // properties as well.
  1129. if (!LastError) {
  1130. // We must re-validate our pointers as we might have left
  1131. // our semaphore
  1132. if( pIniJob = FindJob( pIniPrinter, JobId, &Position )){
  1133. LastError = SetLocalJob( hPrinter,
  1134. pIniJob,
  1135. Level,
  1136. pJob );
  1137. }
  1138. }
  1139. } else
  1140. LastError = GetLastError();
  1141. } else
  1142. LastError = ERROR_INVALID_PARAMETER;
  1143. } else
  1144. LastError = ERROR_INVALID_HANDLE;
  1145. if (LastError) {
  1146. SetLastError(LastError);
  1147. DBGMSG( DBG_TRACE, ( "EXIT LocalSetJob, rc = FALSE, JobID %d, Status %08x, Error %d\n",
  1148. pIniJob ? pIniJob->JobId : 0,
  1149. pIniJob ? pIniJob->Status : 0,
  1150. LastError ) );
  1151. LeaveSplSem();
  1152. return FALSE;
  1153. } else {
  1154. //
  1155. // (DeleteJob calls SetPrinterChange; so does SetLocalJob)
  1156. //
  1157. if ( Command &&
  1158. pIniJob != NULL ) {
  1159. SetPrinterChange(pIniPrinter,
  1160. pIniJob,
  1161. NVJobStatus,
  1162. PRINTER_CHANGE_SET_JOB,
  1163. pSpool->pIniSpooler );
  1164. }
  1165. DBGMSG( DBG_TRACE, ( "EXIT LocalSetJob, rc = TRUE, JobID %d, Status %08x\n",
  1166. pIniJob ? pIniJob->JobId : 0,
  1167. pIniJob ? pIniJob->Status : 0 ) );
  1168. }
  1169. LeaveSplSem();
  1170. return TRUE;
  1171. }
  1172. #define Nullstrlen(psz) ((psz) ? wcslen(psz)*sizeof(WCHAR)+sizeof(WCHAR) : 0)
  1173. DWORD
  1174. GetJobSize(
  1175. DWORD Level,
  1176. PINIJOB pIniJob
  1177. )
  1178. {
  1179. DWORD cb;
  1180. SplInSem();
  1181. switch (Level) {
  1182. case 1:
  1183. cb = sizeof(JOB_INFO_1) +
  1184. wcslen(pIniJob->pIniPrinter->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
  1185. Nullstrlen(pIniJob->pMachineName) +
  1186. Nullstrlen(pIniJob->pUser) +
  1187. Nullstrlen(pIniJob->pDocument) +
  1188. Nullstrlen(pIniJob->pDatatype) +
  1189. Nullstrlen(pIniJob->pStatus);
  1190. break;
  1191. case 2:
  1192. cb = sizeof(JOB_INFO_2) +
  1193. wcslen(pIniJob->pIniPrinter->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
  1194. Nullstrlen(pIniJob->pMachineName) +
  1195. Nullstrlen(pIniJob->pUser) +
  1196. Nullstrlen(pIniJob->pDocument) +
  1197. Nullstrlen(pIniJob->pNotify) +
  1198. Nullstrlen(pIniJob->pDatatype) +
  1199. wcslen(pIniJob->pIniPrintProc->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
  1200. Nullstrlen(pIniJob->pParameters) +
  1201. wcslen(pIniJob->pIniPrinter->pIniDriver->pName)*sizeof(WCHAR) + sizeof(WCHAR) +
  1202. Nullstrlen(pIniJob->pStatus);
  1203. if (pIniJob->pDevMode) {
  1204. cb += pIniJob->pDevMode->dmSize + pIniJob->pDevMode->dmDriverExtra;
  1205. cb = (cb + sizeof(ULONG_PTR)-1) & ~(sizeof(ULONG_PTR)-1);
  1206. }
  1207. break;
  1208. case 3:
  1209. cb = sizeof(JOB_INFO_3);
  1210. break;
  1211. default:
  1212. cb = 0;
  1213. break;
  1214. }
  1215. return cb;
  1216. }
  1217. LPBYTE
  1218. CopyIniJobToJob(
  1219. PINIJOB pIniJob,
  1220. DWORD Level,
  1221. LPBYTE pJobInfo,
  1222. LPBYTE pEnd,
  1223. LPBOOL pbSuccess
  1224. )
  1225. {
  1226. LPWSTR *pSourceStrings, *SourceStrings;
  1227. LPJOB_INFO_2 pJob = (PJOB_INFO_2)pJobInfo;
  1228. LPJOB_INFO_2 pJob2 = (PJOB_INFO_2)pJobInfo;
  1229. LPJOB_INFO_1 pJob1 = (PJOB_INFO_1)pJobInfo;
  1230. LPJOB_INFO_3 pJob3 = (PJOB_INFO_3)pJobInfo;
  1231. DWORD i, Status;
  1232. DWORD *pOffsets;
  1233. *pbSuccess = FALSE;
  1234. SplInSem();
  1235. switch (Level) {
  1236. case 1:
  1237. pOffsets = JobInfo1Strings;
  1238. break;
  1239. case 2:
  1240. pOffsets = JobInfo2Strings;
  1241. break;
  1242. case 3:
  1243. pOffsets = JobInfo3Strings;
  1244. break;
  1245. default:
  1246. return pEnd;
  1247. }
  1248. Status = MapJobStatus(MAP_READABLE,
  1249. pIniJob->Status);
  1250. for (i=0; pOffsets[i] != -1; i++) {
  1251. }
  1252. SourceStrings = pSourceStrings = AllocSplMem(i * sizeof(LPWSTR));
  1253. if ( pSourceStrings ) {
  1254. switch ( Level ) {
  1255. case 1:
  1256. pJob1->JobId = pIniJob->JobId;
  1257. *pSourceStrings ++= pIniJob->pIniPrinter->pName;
  1258. *pSourceStrings ++= pIniJob->pMachineName;
  1259. *pSourceStrings ++= pIniJob->pUser;
  1260. *pSourceStrings ++= pIniJob->pDocument;
  1261. *pSourceStrings ++= pIniJob->pDatatype;
  1262. *pSourceStrings ++= pIniJob->pStatus;
  1263. pJob1->Status = Status;
  1264. pJob1->Priority = pIniJob->Priority;
  1265. pJob1->Position = 0;
  1266. pJob1->TotalPages = pIniJob->cPages;
  1267. pJob1->PagesPrinted = pIniJob->cPagesPrinted;
  1268. pJob1->Submitted = pIniJob->Submitted;
  1269. // If this job is Printing then report back size remaining
  1270. // rather than the job size. This will allow users to see
  1271. // progress of print jobs from printmanage.
  1272. if (pIniJob->Status & JOB_PRINTING) {
  1273. // For Remote Jobs we are NOT going to have an accurate
  1274. // cPagesPrinted since we are not rendering on the
  1275. // server. So we have to figure out an estimate
  1276. if ((pIniJob->Status & JOB_REMOTE) &&
  1277. (pIniJob->cPagesPrinted == 0) &&
  1278. (pIniJob->Size != 0) &&
  1279. (pIniJob->cPages != 0)) {
  1280. pJob1->PagesPrinted = ((pIniJob->cPages * pIniJob->cbPrinted) / pIniJob->Size);
  1281. }
  1282. if (pJob1->TotalPages < pIniJob->cPagesPrinted) {
  1283. //
  1284. // Never let the total pages drop below zero.
  1285. //
  1286. pJob1->TotalPages = 0;
  1287. } else {
  1288. pJob1->TotalPages -= pIniJob->cPagesPrinted;
  1289. }
  1290. }
  1291. break;
  1292. case 2:
  1293. pJob2->JobId = pIniJob->JobId;
  1294. *pSourceStrings ++= pIniJob->pIniPrinter->pName;
  1295. *pSourceStrings ++= pIniJob->pMachineName;
  1296. *pSourceStrings ++= pIniJob->pUser;
  1297. *pSourceStrings ++= pIniJob->pDocument;
  1298. *pSourceStrings ++= pIniJob->pNotify;
  1299. *pSourceStrings ++= pIniJob->pDatatype;
  1300. *pSourceStrings ++= pIniJob->pIniPrintProc->pName;
  1301. *pSourceStrings ++= pIniJob->pParameters;
  1302. *pSourceStrings ++= pIniJob->pIniPrinter->pIniDriver->pName;
  1303. *pSourceStrings ++= pIniJob->pStatus;
  1304. if (pIniJob->pDevMode) {
  1305. pEnd -= pIniJob->pDevMode->dmSize + pIniJob->pDevMode->dmDriverExtra;
  1306. pEnd = (LPBYTE)ALIGN_PTR_DOWN(pEnd);
  1307. pJob2->pDevMode = (LPDEVMODE)pEnd;
  1308. CopyMemory( pJob2->pDevMode,
  1309. pIniJob->pDevMode,
  1310. pIniJob->pDevMode->dmSize + pIniJob->pDevMode->dmDriverExtra );
  1311. } else {
  1312. pJob2->pDevMode = NULL;
  1313. }
  1314. pJob2->pSecurityDescriptor = NULL; // Not Supported.
  1315. pJob2->Status = Status;
  1316. pJob2->Priority = pIniJob->Priority;
  1317. pJob2->Position = 0;
  1318. pJob2->StartTime = pIniJob->StartTime;
  1319. pJob2->UntilTime = pIniJob->UntilTime;
  1320. pJob2->TotalPages = pIniJob->cPages;
  1321. pJob2->Size = pIniJob->Size;
  1322. pJob2->Submitted = pIniJob->Submitted;
  1323. pJob2->Time = pIniJob->Time;
  1324. pJob2->PagesPrinted = pIniJob->cPagesPrinted;
  1325. // If this job is Printing then report back size remaining
  1326. // rather than the job size. This will allow users to see
  1327. // progress of print jobs from printmanage.
  1328. if ( pIniJob->Status & JOB_PRINTING ) {
  1329. pJob2->Size -= pIniJob->cbPrinted;
  1330. // For Remote Jobs we are NOT going to have an accurate
  1331. // cPagesPrinted since we are not rendering on the
  1332. // server. So we have to figure out an estimate
  1333. if ((pIniJob->Status & JOB_REMOTE) &&
  1334. (pIniJob->cPagesPrinted == 0) &&
  1335. (pIniJob->Size != 0) &&
  1336. (pIniJob->cPages != 0)) {
  1337. pJob2->PagesPrinted = ((pIniJob->cPages * pIniJob->cbPrinted) / pIniJob->Size);
  1338. }
  1339. if (pJob2->TotalPages < pJob2->PagesPrinted) {
  1340. //
  1341. // Never let the total pages drop below zero.
  1342. //
  1343. pJob2->TotalPages = 0;
  1344. } else {
  1345. pJob2->TotalPages -= pJob2->PagesPrinted;
  1346. }
  1347. }
  1348. break;
  1349. case 3:
  1350. pJob3->JobId = pIniJob->JobId;
  1351. if ( pIniJob->pCurrentIniJob == NULL ) {
  1352. pJob3->NextJobId = pIniJob->NextJobId;
  1353. } else {
  1354. //
  1355. // If we are currently Printing this Job, then the
  1356. // FAX Monitor Needs to know if there is another job
  1357. // to know where we are in the chain of jobs
  1358. //
  1359. pJob3->NextJobId = pIniJob->pCurrentIniJob->NextJobId;
  1360. }
  1361. break;
  1362. default:
  1363. return pEnd;
  1364. }
  1365. pEnd = PackStrings( SourceStrings, pJobInfo, pOffsets, pEnd );
  1366. FreeSplMem( SourceStrings );
  1367. *pbSuccess = TRUE;
  1368. } else {
  1369. DBGMSG( DBG_WARNING, ("Failed to alloc Job source strings."));
  1370. }
  1371. return pEnd;
  1372. }
  1373. BOOL
  1374. LocalGetJob(
  1375. HANDLE hPrinter,
  1376. DWORD JobId,
  1377. DWORD Level,
  1378. LPBYTE pJob,
  1379. DWORD cbBuf,
  1380. LPDWORD pcbNeeded
  1381. )
  1382. /*++
  1383. Routine Description:
  1384. This function will retrieve the settings of the specified Print Job.
  1385. Arguments:
  1386. hPrinter - Handle to printer OR server. Since this is could be a
  1387. server, the pSpool->pIniPrinter is not always valid! Use
  1388. pIniJob->pIniPrinter instead of pSpool->pIniPrinter.
  1389. pJob - Points to a valid JOB structure containing at least a valid
  1390. pPrinter, and JobId.
  1391. Return Value:
  1392. TRUE - The operation was successful.
  1393. FALSE/NULL - The operation failed. Extended error status is available
  1394. using GetLastError.
  1395. --*/
  1396. {
  1397. PINIJOB pIniJob;
  1398. DWORD Position;
  1399. DWORD cb;
  1400. LPBYTE pEnd;
  1401. PSPOOL pSpool = (PSPOOL)hPrinter;
  1402. DWORD LastError=0;
  1403. PINIPRINTER pIniPrinter;
  1404. BOOL bSuccess;
  1405. EnterSplSem();
  1406. if ( ValidateSpoolHandle(pSpool, 0 )) {
  1407. if (pSpool->TypeofHandle & PRINTER_HANDLE_SERVER) {
  1408. //
  1409. // If it's a server handle, then search all jobs on this spooler.
  1410. // This call also retrieves the pIniPrinter associated
  1411. // with a print job.
  1412. //
  1413. pIniJob = FindServerJob( pSpool->pIniSpooler,
  1414. JobId,
  1415. &Position,
  1416. &pIniPrinter );
  1417. } else if (pSpool->pIniPort && !(pSpool->pIniPort->Status & PP_MONITOR)) {
  1418. //
  1419. // It's a masq printer. Send the call to the port RPC handle.
  1420. //
  1421. hPrinter = pSpool->hPort;
  1422. LeaveSplSem();
  1423. return GetJob(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded);
  1424. } else {
  1425. //
  1426. // It's a regular printer handle.
  1427. //
  1428. pIniPrinter = pSpool->pIniPrinter;
  1429. pIniJob = FindJob( pIniPrinter, JobId, &Position);
  1430. }
  1431. if( pIniJob ){
  1432. cb=GetJobSize(Level, pIniJob);
  1433. *pcbNeeded=cb;
  1434. if (cbBuf >= cb) {
  1435. pEnd = pJob+cbBuf;
  1436. CopyIniJobToJob(pIniJob, Level, pJob, pEnd, &bSuccess);
  1437. if (bSuccess) {
  1438. switch (Level) {
  1439. case 1:
  1440. ((PJOB_INFO_1)pJob)->Position = Position;
  1441. break;
  1442. case 2:
  1443. ((PJOB_INFO_2)pJob)->Position = Position;
  1444. break;
  1445. }
  1446. } else
  1447. LastError = ERROR_NOT_ENOUGH_MEMORY;
  1448. } else
  1449. LastError = ERROR_INSUFFICIENT_BUFFER;
  1450. } else
  1451. LastError = ERROR_INVALID_PARAMETER;
  1452. } else
  1453. LastError = ERROR_INVALID_HANDLE;
  1454. LeaveSplSem();
  1455. SplOutSem();
  1456. if (LastError) {
  1457. SetLastError(LastError);
  1458. return FALSE;
  1459. }
  1460. return TRUE;
  1461. }
  1462. // This will simply return the first port that is found that has a
  1463. // connection to this printer
  1464. PINIPORT
  1465. FindIniPortFromIniPrinter(
  1466. PINIPRINTER pIniPrinter
  1467. )
  1468. {
  1469. PINIPORT pIniPort;
  1470. DWORD i;
  1471. SPLASSERT( pIniPrinter->signature == IP_SIGNATURE );
  1472. SPLASSERT( pIniPrinter->pIniSpooler != NULL );
  1473. SPLASSERT( pIniPrinter->pIniSpooler->signature == ISP_SIGNATURE );
  1474. pIniPort = pIniPrinter->pIniSpooler->pIniPort;
  1475. while (pIniPort) {
  1476. for (i=0; i<pIniPort->cPrinters; i++) {
  1477. if (pIniPort->ppIniPrinter[i] == pIniPrinter) {
  1478. return pIniPort;
  1479. }
  1480. }
  1481. pIniPort = pIniPort->pNext;
  1482. }
  1483. return NULL;
  1484. }
  1485. BOOL
  1486. LocalEnumJobs(
  1487. HANDLE hPrinter,
  1488. DWORD FirstJob,
  1489. DWORD NoJobs,
  1490. DWORD Level,
  1491. LPBYTE pJob,
  1492. DWORD cbBuf,
  1493. LPDWORD pcbNeeded,
  1494. LPDWORD pcReturned
  1495. )
  1496. {
  1497. PINIJOB pIniJob;
  1498. PINIJOB pIniFirstJob;
  1499. DWORD cb;
  1500. LPBYTE pEnd;
  1501. DWORD cJobs;
  1502. PSPOOL pSpool = (PSPOOL)hPrinter;
  1503. DWORD Position;
  1504. DWORD LastError=0;
  1505. BOOL bSuccess;
  1506. *pcbNeeded = 0;
  1507. *pcReturned = 0;
  1508. SplOutSem();
  1509. EnterSplSem();
  1510. if ( ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  1511. if (pSpool->pIniPort && !(pSpool->pIniPort->Status & PP_MONITOR)) {
  1512. hPrinter = pSpool->hPort;
  1513. LeaveSplSem();
  1514. return EnumJobs(hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf,
  1515. pcbNeeded, pcReturned);
  1516. }
  1517. cb = 0;
  1518. //
  1519. // Find the first Job
  1520. //
  1521. for ( pIniFirstJob = pSpool->pIniPrinter->pIniFirstJob, cJobs = FirstJob;
  1522. pIniFirstJob && cJobs;
  1523. pIniFirstJob = pIniFirstJob->pIniNextJob ) {
  1524. if ( !( pIniFirstJob->Status & JOB_HIDDEN ) || Level == 3 )
  1525. cJobs--;
  1526. }
  1527. //
  1528. // Calc size required
  1529. //
  1530. for ( pIniJob = pIniFirstJob, cJobs = NoJobs;
  1531. pIniJob && cJobs;
  1532. pIniJob = pIniJob->pIniNextJob ) {
  1533. if ( !( pIniJob->Status & JOB_HIDDEN ) || Level == 3 ) {
  1534. cb += GetJobSize( Level, pIniJob );
  1535. cJobs--;
  1536. }
  1537. }
  1538. *pcbNeeded = cb;
  1539. if ( cb <= cbBuf ) {
  1540. pEnd = pJob + cbBuf;
  1541. *pcReturned = 0;
  1542. //
  1543. // Copy in all the Job info into the Users Buffer
  1544. //
  1545. for ( pIniJob = pIniFirstJob, cJobs = NoJobs, Position = FirstJob;
  1546. pIniJob && cJobs;
  1547. pIniJob = pIniJob->pIniNextJob ) {
  1548. //
  1549. // Hide Chained Jobs, unless requesting chaining info
  1550. //
  1551. if ( !( pIniJob->Status & JOB_HIDDEN ) || Level == 3 ) {
  1552. pEnd = CopyIniJobToJob( pIniJob, Level, pJob, pEnd, &bSuccess );
  1553. if (!bSuccess) {
  1554. LastError = ERROR_NOT_ENOUGH_MEMORY;
  1555. break;
  1556. }
  1557. Position++;
  1558. switch (Level) {
  1559. case 1:
  1560. ((PJOB_INFO_1)pJob)->Position = Position;
  1561. pJob += sizeof(JOB_INFO_1);
  1562. break;
  1563. case 2:
  1564. ((PJOB_INFO_2)pJob)->Position = Position;
  1565. pJob += sizeof(JOB_INFO_2);
  1566. break;
  1567. case 3:
  1568. pJob += sizeof(JOB_INFO_3);
  1569. break;
  1570. }
  1571. cJobs--;
  1572. (*pcReturned)++;
  1573. }
  1574. }
  1575. } else
  1576. LastError = ERROR_INSUFFICIENT_BUFFER;
  1577. } else
  1578. LastError = ERROR_INVALID_HANDLE;
  1579. LeaveSplSem();
  1580. SplOutSem();
  1581. if (LastError) {
  1582. SetLastError(LastError);
  1583. return FALSE;
  1584. }
  1585. return TRUE;
  1586. }
  1587. #define BUFFER_LENGTH 10
  1588. VOID LogJobPrinted(
  1589. PINIJOB pIniJob
  1590. )
  1591. {
  1592. CHAR pstrJobId[BUFFER_LENGTH];
  1593. WCHAR pwstrJobId[BUFFER_LENGTH];
  1594. CHAR pstrSize[BUFFER_LENGTH];
  1595. WCHAR pwstrSize[BUFFER_LENGTH];
  1596. CHAR pstrPages[BUFFER_LENGTH];
  1597. WCHAR pwstrPages[BUFFER_LENGTH];
  1598. PINISPOOLER pIniSpooler = NULL;
  1599. SPLASSERT( pIniJob != NULL &&
  1600. pIniJob->pIniPrinter != NULL &&
  1601. pIniJob->pIniPrinter->pIniSpooler != NULL );
  1602. pIniSpooler = pIniJob->pIniPrinter->pIniSpooler;
  1603. _itoa(pIniJob->JobId, pstrJobId, BUFFER_LENGTH);
  1604. AnsiToUnicodeString(pstrJobId, pwstrJobId, NULL_TERMINATED);
  1605. _itoa(pIniJob->cbPrinted, pstrSize, BUFFER_LENGTH);
  1606. AnsiToUnicodeString(pstrSize, pwstrSize, NULL_TERMINATED);
  1607. _itoa(pIniJob->cPagesPrinted, pstrPages, BUFFER_LENGTH);
  1608. AnsiToUnicodeString(pstrPages, pwstrPages, NULL_TERMINATED);
  1609. SplLogEvent( pIniSpooler,
  1610. LOG_INFO,
  1611. MSG_DOCUMENT_PRINTED,
  1612. FALSE,
  1613. pwstrJobId,
  1614. pIniJob->pDocument ? pIniJob->pDocument : L"",
  1615. pIniJob->pUser,
  1616. pIniJob->pIniPrinter->pName,
  1617. pIniJob->pIniPort->pName,
  1618. pwstrSize,
  1619. pwstrPages,
  1620. NULL );
  1621. }
  1622. VOID
  1623. DeleteJobCheck(
  1624. PINIJOB pIniJob
  1625. )
  1626. {
  1627. SplInSem();
  1628. if ((pIniJob->cRef == 0) && (pIniJob->Status & JOB_PENDING_DELETION)) {
  1629. DeleteJob(pIniJob, BROADCAST);
  1630. }
  1631. }
  1632. BOOL
  1633. DeleteJob(
  1634. PINIJOB pIniJob,
  1635. BOOL bBroadcast
  1636. )
  1637. {
  1638. WCHAR szShadowFileName[MAX_PATH];
  1639. WCHAR szSpoolFileName[MAX_PATH];
  1640. BOOL Direct;
  1641. DWORD cJobs;
  1642. DWORD Position;
  1643. PINISPOOLER pIniSpooler = pIniJob->pIniPrinter->pIniSpooler;
  1644. DWORD NextJobId;
  1645. PINIPRINTER pIniPrinter;
  1646. PNOTIFYVECTOR pNotifyVector;
  1647. DWORD SpoolerFlags, JobId;
  1648. BOOL bReturn = TRUE, bDeleteOnClose;
  1649. PMAPPED_JOB *ppMappedJob, pTempMappedJob;
  1650. PSPOOL pPrinterSpool;
  1651. BOOL bDeleteShdFile;
  1652. HANDLE pFileItem = NULL;
  1653. //
  1654. // WMI Trace events vars.
  1655. //
  1656. WMI_SPOOL_DATA WmiData;
  1657. DWORD CreateInfo;
  1658. BOOL bCheckScheduler = FALSE;
  1659. //
  1660. // Increment the pIniPrinter so that it and the pIniSpooler don't
  1661. // potentially get deleted when the job goes away.
  1662. //
  1663. pIniPrinter = pIniJob->pIniPrinter;
  1664. INCPRINTERREF( pIniPrinter );
  1665. do {
  1666. pNotifyVector = &NVJobStatus;
  1667. SplInSem();
  1668. SPLASSERT(pIniJob->signature == IJ_SIGNATURE);
  1669. SPLASSERT(pIniJob->pIniPrinter->signature == IP_SIGNATURE );
  1670. NextJobId = pIniJob->NextJobId;
  1671. DBGMSG(DBG_INFO, ("DeleteJob Deleting job 0x%0x Status 0x%0x cRef = %d\n", pIniJob, pIniJob->Status, pIniJob->cRef));
  1672. if (pIniJob->Status & JOB_RESTART)
  1673. goto Done;
  1674. Direct = pIniJob->Status & JOB_DIRECT;
  1675. //
  1676. // Make sure users see the Pending Deleting bit
  1677. // over any other status string
  1678. //
  1679. if( pIniJob->pStatus ){
  1680. FreeSplStr( pIniJob->pStatus );
  1681. pIniJob->pStatus = NULL;
  1682. pNotifyVector = &NVJobStatusAndString;
  1683. }
  1684. // Update the job alert flag
  1685. if (!(pIniJob->dwAlert & JOB_ENDDOC_CALL)) {
  1686. pIniJob->dwAlert |= JOB_NO_ALERT;
  1687. }
  1688. // Release any thread waiting on LocalSetPort
  1689. SetPortErrorEvent(pIniJob->pIniPort);
  1690. if (!(pIniJob->Status & JOB_PENDING_DELETION)) {
  1691. pIniJob->Status |= JOB_PENDING_DELETION;
  1692. // Release any thread waiting on SeekPrinter
  1693. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  1694. //
  1695. // See that we always are StartDocComplete.
  1696. //
  1697. if ( pIniJob->StartDocComplete ) {
  1698. SetEvent( pIniJob->StartDocComplete );
  1699. }
  1700. //
  1701. // Just pending deletion, so don't use DELETE_JOB.
  1702. //
  1703. SetPrinterChange(pIniJob->pIniPrinter,
  1704. pIniJob,
  1705. *pNotifyVector,
  1706. PRINTER_CHANGE_SET_JOB,
  1707. pIniSpooler );
  1708. if (pIniJob->Status & JOB_PRINTING) {
  1709. BOOL ReturnValue = TRUE;
  1710. PINIPRINTPROC pIniPrintProc = pIniJob->pIniPrintProc;
  1711. PINIPORT pIniPort = NULL;
  1712. INCJOBREF(pIniJob);
  1713. // multiple threads may come in here, but they are all "delete"
  1714. if (pIniJob->pIniPort && !(pIniJob->pIniPort->InCriticalSection & PRINTPROC_CANCEL)) {
  1715. pIniPort = pIniJob->pIniPort;
  1716. INCPORTREF(pIniPort);
  1717. LeaveSplSem();
  1718. EnterCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  1719. EnterSplSem();
  1720. pIniPort->InCriticalSection |= PRINTPROC_CANCEL;
  1721. if (pIniPort->hProc) {
  1722. LeaveSplSem();
  1723. DBGMSG(DBG_TRACE, ("DeleteJob calling %x hProc %x JOB_CONTROL_CANCEL\n",*pIniPrintProc->Control, pIniPort->hProc));
  1724. ReturnValue = (*pIniPrintProc->Control)(pIniPort->hProc, JOB_CONTROL_CANCEL);
  1725. EnterSplSem();
  1726. }
  1727. pIniPort->InCriticalSection &= ~PRINTPROC_CANCEL;
  1728. //
  1729. // Tell any other printproc calls not to call into the print processor.
  1730. //
  1731. pIniPort->InCriticalSection |= PRINTPROC_CANCELLED;
  1732. LeaveCriticalSection(&pIniJob->pIniPrintProc->CriticalSection);
  1733. DECPORTREF(pIniPort);
  1734. }
  1735. DECJOBREF(pIniJob);
  1736. }
  1737. }
  1738. //
  1739. // If we're Pooling, then don't bother with the
  1740. // GetFilenameFromId call
  1741. //
  1742. if ( pIniJob->hFileItem == INVALID_HANDLE_VALUE )
  1743. {
  1744. GetFullNameFromId( pIniJob->pIniPrinter,
  1745. pIniJob->JobId, FALSE, szShadowFileName, FALSE );
  1746. }
  1747. if (pIniJob->cRef) {
  1748. //
  1749. // Instead of writing out the shadow job, let's just delete it.
  1750. // If the spooler restarts, we will just kill the job.
  1751. //
  1752. // Note that we do not delete the file if the SPL_NO_UPDATE_JOBSHD
  1753. // flag is set, this is so cluster failovers do not lose jobs.
  1754. //
  1755. // We also do not delete it if we're using filepools, we recycle the
  1756. // handle.
  1757. //
  1758. if (!(pIniSpooler->SpoolerFlags & SPL_NO_UPDATE_JOBSHD))
  1759. {
  1760. if ( pIniJob->hFileItem != INVALID_HANDLE_VALUE )
  1761. {
  1762. FinishedWriting( pIniJob->hFileItem, FALSE );
  1763. pIniJob->Status |= JOB_SHADOW_DELETED;
  1764. }
  1765. else
  1766. {
  1767. BOOL Deleted = FALSE;
  1768. //
  1769. // We set the flag here so that no one tries to write the job while we're deleting the file.
  1770. //
  1771. pIniJob->Status |= JOB_SHADOW_DELETED;
  1772. INCJOBREF(pIniJob);
  1773. LeaveSplSem();
  1774. Deleted = DeleteFile(szShadowFileName);
  1775. EnterSplSem();
  1776. DECJOBREF(pIniJob);
  1777. //
  1778. // If we fail to delete the file, clear the deleted flag.
  1779. //
  1780. if (!Deleted) {
  1781. DBGMSG(DBG_WARNING, ("DeleteJob DeleteFile(%ws) failed %d\n", szShadowFileName, GetLastError()));
  1782. pIniJob->Status &= ~JOB_SHADOW_DELETED;
  1783. }
  1784. }
  1785. }
  1786. //
  1787. // We don't need an else here because the SPL_NO_UPDATE_JOBSHD
  1788. // will cause a shadowfile not to be written anyway, so trying to write
  1789. // it here is pointless.
  1790. //
  1791. goto Done;
  1792. }
  1793. if (pIniJob->Status & JOB_SPOOLING) {
  1794. DBGMSG(DBG_WARNING,("DeleteJob: returning false because job still spooling\n"));
  1795. bReturn = FALSE;
  1796. goto Done;
  1797. }
  1798. SplInSem();
  1799. SPLASSERT( pIniJob->hWriteFile == INVALID_HANDLE_VALUE );
  1800. // Remove the job from linked list
  1801. // The purpose of this is so the job has no other operations carried out
  1802. // on it whilst we are out of critical section.
  1803. SPLASSERT(pIniJob->cRef == 0);
  1804. if (pIniJob->pIniPrinter->pIniFirstJob == pIniJob)
  1805. pIniJob->pIniPrinter->pIniFirstJob = pIniJob->pIniNextJob;
  1806. SPLASSERT(pIniJob->pIniPrinter->pIniFirstJob != pIniJob);
  1807. if (pIniJob->pIniPrinter->pIniLastJob == pIniJob)
  1808. pIniJob->pIniPrinter->pIniLastJob = pIniJob->pIniPrevJob;
  1809. SPLASSERT(pIniJob->pIniPrinter->pIniLastJob != pIniJob);
  1810. if (pIniJob->pIniPrevJob) {
  1811. pIniJob->pIniPrevJob->pIniNextJob = pIniJob->pIniNextJob;
  1812. SPLASSERT(pIniJob->pIniPrevJob->pIniNextJob != pIniJob);
  1813. }
  1814. if (pIniJob->pIniNextJob) {
  1815. pIniJob->pIniNextJob->pIniPrevJob = pIniJob->pIniPrevJob;
  1816. SPLASSERT(pIniJob->pIniNextJob->pIniPrevJob != pIniJob);
  1817. }
  1818. // MAKE Certain that the Job is gone
  1819. SPLASSERT( pIniJob != FindJob( pIniJob->pIniPrinter, pIniJob->JobId, &Position ) );
  1820. //
  1821. // Only log the Job Deleted Event if the job was not printed
  1822. // Or it was printing but it did not print all the bytes of the job
  1823. // This avoid having multiple event log entries for a job
  1824. // MSG_DOCUMENT_PRINTED and MSG_DOCUMENT_DELETED.
  1825. // If its not PRINTED, then most likely someone has manually
  1826. // deleted the job, so we are interested in logging that event.
  1827. //
  1828. if ( !( pIniJob->Status & JOB_PRINTED ) ||
  1829. ( pIniJob->Status & JOB_PRINTED ) && pIniJob->Size > pIniJob->cbPrinted ) {
  1830. //
  1831. // We are going to leave critical section so up the ref count.
  1832. //
  1833. INCJOBREF(pIniJob);
  1834. SPLASSERT( pIniJob != NULL &&
  1835. pIniJob->pIniPrinter != NULL &&
  1836. pIniSpooler != NULL );
  1837. LogJobInfo(
  1838. pIniSpooler,
  1839. MSG_DOCUMENT_DELETED,
  1840. pIniJob->JobId,
  1841. pIniJob->pDocument,
  1842. pIniJob->pUser,
  1843. pIniJob->pIniPrinter->pName,
  1844. 0
  1845. );
  1846. DECJOBREF(pIniJob);
  1847. }
  1848. SPLASSERT( pIniJob->cRef == 0 );
  1849. if ( pIniJob->hFileItem != INVALID_HANDLE_VALUE )
  1850. {
  1851. pFileItem = pIniJob->hFileItem;
  1852. }
  1853. else
  1854. {
  1855. GetFullNameFromId( pIniJob->pIniPrinter,
  1856. pIniJob->JobId, TRUE, szSpoolFileName, FALSE );
  1857. }
  1858. //
  1859. // WMI Trace Events
  1860. //
  1861. if (GetFileCreationInfo(pFileItem, &CreateInfo) != S_OK)
  1862. {
  1863. // Assume all file created.
  1864. CreateInfo = FP_ALL_FILES_CREATED;
  1865. }
  1866. SplWmiCopyEndJobData(&WmiData, pIniJob, CreateInfo);
  1867. FreeSplStr( pIniJob->pDocument );
  1868. FreeSplStr( pIniJob->pUser );
  1869. FreeSplStr( pIniJob->pNotify );
  1870. FreeSplStr( pIniJob->pDatatype );
  1871. FreeSplStr( pIniJob->pMachineName );
  1872. FreeSplStr( pIniJob->pParameters );
  1873. FreeSplStr( pIniJob->pStatus );
  1874. FreeSplStr( pIniJob->pOutputFile );
  1875. FreeSplStr( pIniJob->pszSplFileName );
  1876. if (pIniJob->pDevMode)
  1877. FreeSplMem(pIniJob->pDevMode);
  1878. if (!CloseHandle(pIniJob->hToken))
  1879. DBGMSG( DBG_WARNING, ("CloseHandle(hToken) failed %d\n", GetLastError() ));
  1880. if( pIniJob->pIniPort && pIniJob->pIniPort->hErrorEvent != NULL ){
  1881. CloseHandle(pIniJob->pIniPort->hErrorEvent);
  1882. pIniJob->pIniPort->hErrorEvent = NULL;
  1883. }
  1884. SPLASSERT( pIniJob->pIniPrinter->cJobs != 0 );
  1885. SPLASSERT( pIniJob->pIniPrintProc->cRef != 0 );
  1886. SPLASSERT( !pIniJob->pIniPort );
  1887. //
  1888. // Freeup the JobId before we decrement cJobs. We won't delete
  1889. // the printer if cJobs is non-zero. Since the pIniPrinter holds
  1890. // a reference to pIniSpooler, we know pIniSpooler is valid at this
  1891. // point.
  1892. //
  1893. // Record the JobId for updating the Id map
  1894. JobId = pIniJob->JobId;
  1895. //
  1896. // If the printer in in pending deletion and
  1897. // this is the last job in the queue, tell the driver that the printer
  1898. // is beeing deleted.
  1899. //
  1900. if (pIniJob->pIniPrinter->cJobs == 1 &&
  1901. pIniJob->pIniPrinter->Status & PRINTER_PENDING_DELETION) {
  1902. INCPRINTERREF(pIniPrinter);
  1903. LeaveSplSem();
  1904. SplOutSem();
  1905. PrinterDriverEvent( pIniPrinter, PRINTER_EVENT_DELETE, (LPARAM)NULL );
  1906. EnterSplSem();
  1907. SplInSem();
  1908. DECPRINTERREF(pIniPrinter);
  1909. }
  1910. pIniJob->pIniPrinter->cJobs--;
  1911. DECDRIVERREF( pIniJob->pIniDriver );
  1912. pIniJob->pIniPrintProc->cRef--;
  1913. cJobs = pIniJob->pIniPrinter->cJobs;
  1914. if (pIniJob->pSecurityDescriptor)
  1915. DeleteDocumentSecurity(pIniJob);
  1916. // If we are doing a Purge Printer we don't want to set a printer change
  1917. // event for each job being deleted
  1918. if ( bBroadcast == BROADCAST ) {
  1919. //
  1920. // Flip on the JOB_STATUS_DELETED bit so that it can be reported.
  1921. //
  1922. pIniJob->Status |= JOB_DELETED;
  1923. SetPrinterChange( pIniJob->pIniPrinter,
  1924. pIniJob,
  1925. NVDeletedJob,
  1926. PRINTER_CHANGE_DELETE_JOB | PRINTER_CHANGE_SET_PRINTER,
  1927. pIniSpooler );
  1928. }
  1929. // On Inspection it might look as though a Printer which is pending
  1930. // deletion which is then purged might case the printer to be deleted
  1931. // and Purge Printer to access violate or access a dead pIniPrinter.
  1932. // However in order to do a purge there must be a valid active
  1933. // hPrinter which would mean the cRef != 0.
  1934. //
  1935. // Check whether we should delete the spool files.
  1936. //
  1937. SpoolerFlags = pIniSpooler->SpoolerFlags;
  1938. DeletePrinterCheck( pIniJob->pIniPrinter );
  1939. SplInSem();
  1940. SPLASSERT(pIniJob->cRef == 0);
  1941. // If the job was being printed whilst spooling it will have
  1942. // some syncronization handles which need to be cleaned up
  1943. if ( pIniJob->WaitForWrite != NULL ){
  1944. DBGMSG( DBG_TRACE, ("DeleteJob Closing WaitForWrite handle %x\n", pIniJob->WaitForWrite));
  1945. CloseHandle( pIniJob->WaitForWrite );
  1946. pIniJob->WaitForWrite = NULL;
  1947. }
  1948. if ( pIniJob->WaitForSeek != NULL ){
  1949. DBGMSG( DBG_TRACE, ("DeleteJob Closing WaitForSeek handle %x\n", pIniJob->WaitForSeek));
  1950. CloseHandle( pIniJob->WaitForSeek );
  1951. pIniJob->WaitForSeek = NULL;
  1952. }
  1953. if ( pIniJob->WaitForRead != NULL ){
  1954. DBGMSG( DBG_TRACE, ("DeleteJob Closing WaitForRead handle %x\n", pIniJob->WaitForRead));
  1955. CloseHandle( pIniJob->WaitForRead );
  1956. pIniJob->WaitForRead = NULL;
  1957. }
  1958. bDeleteShdFile = pIniJob->Status & JOB_SHADOW_DELETED;
  1959. SPLASSERT( pIniJob->hWriteFile == INVALID_HANDLE_VALUE );
  1960. DELETEJOBREF(pIniJob);
  1961. FreeSplMem(pIniJob);
  1962. pIniJob = NULL;
  1963. // This flag indicates if the spool file is to be deleted on ClosePrinter
  1964. bDeleteOnClose = FALSE;
  1965. if (!Direct) {
  1966. //
  1967. // Don't delete the files if we don't want JOBSHD changes.
  1968. // This happens when we are taking a cluster offline: we want
  1969. // to free the pIniJobs, but leave the spool files intact so they
  1970. // can be restarted on the other node.
  1971. //
  1972. if( !( SpoolerFlags & SPL_NO_UPDATE_JOBSHD )){
  1973. HANDLE hToken;
  1974. LeaveSplSem();
  1975. hToken = RevertToPrinterSelf();
  1976. //
  1977. // Delete the spool and shadow files.
  1978. //
  1979. if (!bDeleteShdFile)
  1980. {
  1981. if ( pFileItem )
  1982. {
  1983. FinishedWriting( pFileItem, FALSE );
  1984. }
  1985. else
  1986. {
  1987. if (!DeleteFile(szShadowFileName)) {
  1988. DBGMSG(DBG_WARNING, ("DeleteJob DeleteFile(%ws) failed %d\n", szShadowFileName, GetLastError()));
  1989. }
  1990. }
  1991. }
  1992. if ( pFileItem )
  1993. {
  1994. FinishedWriting( pFileItem, TRUE );
  1995. FinishedReading( pFileItem );
  1996. //
  1997. // This releases the shadow and Spool files ate the same time.
  1998. //
  1999. ReleasePoolHandle( &pFileItem );
  2000. //
  2001. // We need to check the scheduler to insure that this file item
  2002. // gets removed from the pool if there is no other printing in
  2003. // the system.
  2004. //
  2005. bCheckScheduler = TRUE;
  2006. }
  2007. else
  2008. {
  2009. if (!DeleteFile(szSpoolFileName)) {
  2010. bDeleteOnClose = TRUE;
  2011. DBGMSG(DBG_WARNING, ("DeleteJob DeleteFile(%ws) failed %d\n", szSpoolFileName, GetLastError()));
  2012. }
  2013. }
  2014. ImpersonatePrinterClient(hToken);
  2015. EnterSplSem();
  2016. }
  2017. }
  2018. // If the spool file could not be deleted and it must be deleted on ClosePrinter
  2019. if (!bDeleteOnClose)
  2020. {
  2021. // Free the job id from the id map
  2022. vMarkOff(pIniSpooler->hJobIdMap, JobId);
  2023. // Remove the job info from any of the pSpool structures, since the spool file
  2024. // does not have to be deleted on ClosePrinter.
  2025. for (pPrinterSpool = pIniPrinter->pSpool;
  2026. pPrinterSpool;
  2027. pPrinterSpool = pPrinterSpool->pNext)
  2028. {
  2029. for (ppMappedJob = &(pPrinterSpool->pMappedJob);
  2030. *ppMappedJob;
  2031. ppMappedJob = &((*ppMappedJob)->pNext))
  2032. {
  2033. if ((*ppMappedJob)->JobId == JobId)
  2034. {
  2035. // Delete this entry
  2036. pTempMappedJob = *ppMappedJob;
  2037. *ppMappedJob = pTempMappedJob->pNext;
  2038. FreeSplMem(pTempMappedJob->pszSpoolFile);
  2039. FreeSplMem(pTempMappedJob);
  2040. // There are no duplicates in this list
  2041. break;
  2042. }
  2043. }
  2044. }
  2045. }
  2046. if ( bBroadcast == BROADCAST && dwEnableBroadcastSpoolerStatus ){
  2047. BroadcastChange( pIniSpooler,WM_SPOOLERSTATUS, PR_JOBSTATUS, (LPARAM)cJobs);
  2048. }
  2049. //
  2050. // Chained Jobs
  2051. // If the Job we just deleted is part of a chain we need to go along
  2052. // the chain decrementing the reference count and potentially deleting the
  2053. // next job in the chain.
  2054. //
  2055. if ( NextJobId != 0 ) {
  2056. //
  2057. // Decrement the reference count of the NextJobId
  2058. //
  2059. SplInSem();
  2060. pIniJob = FindJob( pIniPrinter, NextJobId, &Position );
  2061. if ( pIniJob != NULL ) {
  2062. //
  2063. // was incremented in SetJob job_info_3
  2064. //
  2065. DECJOBREF( pIniJob );
  2066. //
  2067. // Do not attempt to delete the NextJob until its ref count is Zero
  2068. //
  2069. if ( pIniJob->cRef != 0 ) {
  2070. pIniJob = NULL;
  2071. }
  2072. } else {
  2073. DBGMSG(DBG_WARNING, ("DeleteJob pIniJob %x NextJobId %d not found\n", pIniJob, pIniJob->NextJobId ));
  2074. }
  2075. }
  2076. //
  2077. // WMI Trace Events
  2078. //
  2079. if (pIniJob)
  2080. {
  2081. INCJOBREF(pIniJob);
  2082. }
  2083. LeaveSplSem();
  2084. LogWmiTraceEvent(JobId, EVENT_TRACE_TYPE_SPL_DELETEJOB, &WmiData);
  2085. EnterSplSem();
  2086. if (pIniJob)
  2087. {
  2088. DECJOBREF(pIniJob);
  2089. }
  2090. } while ( pIniJob != NULL );
  2091. Done:
  2092. //
  2093. // Matches the increment at the beginning of this function.
  2094. //
  2095. DECPRINTERREF( pIniPrinter );
  2096. DeletePrinterCheck( pIniPrinter );
  2097. if (bCheckScheduler) {
  2098. CHECK_SCHEDULER();
  2099. }
  2100. return bReturn;
  2101. }
  2102. VOID
  2103. LogJobInfo(
  2104. PINISPOOLER pIniSpooler,
  2105. NTSTATUS EventId,
  2106. DWORD JobId,
  2107. LPWSTR pDocumentName,
  2108. LPWSTR pUser,
  2109. LPWSTR pPrinterName,
  2110. DWORD dwArgument
  2111. )
  2112. /*++
  2113. Routine Description:
  2114. Performs generic event logging for all job based events.
  2115. Arguments:
  2116. DWORD EventId
  2117. DWORD JobId
  2118. LPWSTR
  2119. Return Value:
  2120. VOID
  2121. Note:
  2122. --*/
  2123. {
  2124. WCHAR pwstrJobId[BUFFER_LENGTH];
  2125. WCHAR pwstrBuffer[BUFFER_LENGTH];
  2126. _itow(JobId, pwstrJobId, 10);
  2127. switch (EventId) {
  2128. case MSG_DOCUMENT_DELETED:
  2129. case MSG_DOCUMENT_PAUSED:
  2130. case MSG_DOCUMENT_RESUMED:
  2131. SplLogEvent( pIniSpooler,
  2132. LOG_INFO,
  2133. EventId,
  2134. TRUE,
  2135. pwstrJobId,
  2136. pDocumentName ? pDocumentName : L"",
  2137. pUser,
  2138. pPrinterName,
  2139. NULL );
  2140. break;
  2141. case MSG_DOCUMENT_POSITION_CHANGED:
  2142. case MSG_DOCUMENT_PRIORITY_CHANGED:
  2143. _itow(dwArgument, pwstrBuffer, 10);
  2144. SplLogEvent( pIniSpooler,
  2145. LOG_INFO,
  2146. EventId,
  2147. TRUE,
  2148. pwstrJobId,
  2149. pDocumentName ? pDocumentName : L"",
  2150. pUser,
  2151. pwstrBuffer,
  2152. pPrinterName,
  2153. NULL );
  2154. break;
  2155. case MSG_DOCUMENT_TIMEOUT:
  2156. _itow(dwArgument, pwstrBuffer, 10);
  2157. SplLogEvent( pIniSpooler,
  2158. LOG_WARNING,
  2159. EventId,
  2160. TRUE,
  2161. pwstrJobId,
  2162. pDocumentName ? pDocumentName : L"",
  2163. pUser,
  2164. pPrinterName,
  2165. pwstrBuffer,
  2166. NULL );
  2167. break;
  2168. default:
  2169. DBGMSG( DBG_ERROR, ("LogJobInfo EventId %x not supported\n", EventId ));
  2170. }
  2171. }
  2172. BOOL
  2173. bAddMachineName(
  2174. PSPOOL pSpool,
  2175. PINIJOB pIniJob,
  2176. LPCWSTR pMachineName
  2177. )
  2178. /*++
  2179. Routine Description:
  2180. Add a machine name to a pIniJob.
  2181. Arguments:
  2182. pSpool - Handle of session.
  2183. pIniJob - pIniJob to update (pMachineName field).
  2184. pMachineName - Name passed in from ADDJOB_INFO_2 structure. OPTIONAL
  2185. Return Value:
  2186. TRUE - Success
  2187. FALSE - Failure -- last error set.
  2188. --*/
  2189. {
  2190. DWORD Status;
  2191. LPWSTR pszRpcFree = NULL;
  2192. LPCWSTR pszPartialName = NULL;
  2193. if( pMachineName ){
  2194. //
  2195. // We have a machine name passed in from the client.
  2196. //
  2197. pszPartialName = pMachineName;
  2198. } else {
  2199. handle_t serverBinding;
  2200. LPWSTR pszBinding;
  2201. Status = RpcBindingServerFromClient( NULL, &serverBinding );
  2202. if( Status != ERROR_SUCCESS ){
  2203. DBGMSG( DBG_WARN,
  2204. ( "RpcBindingServerFromClient failed with Status %d\n",
  2205. Status ));
  2206. } else {
  2207. Status = RpcBindingToStringBinding( serverBinding, &pszBinding );
  2208. if( Status != ERROR_SUCCESS ){
  2209. DBGMSG( DBG_WARN,
  2210. ( "RpcBindingToStringBinding failed with Status %d\n",
  2211. Status ));
  2212. } else {
  2213. //
  2214. // Acquire just the network address.
  2215. //
  2216. Status = RpcStringBindingParse( pszBinding,
  2217. NULL,
  2218. NULL,
  2219. &pszRpcFree,
  2220. NULL,
  2221. NULL );
  2222. if( Status == RPC_S_OK ){
  2223. pszPartialName = pszRpcFree;
  2224. } else {
  2225. DBGMSG( DBG_WARN,
  2226. ( "RpcStringBindingParse failed with Status %d\n",
  2227. Status ));
  2228. }
  2229. Status = RpcStringFree( &pszBinding );
  2230. if( Status != ERROR_SUCCESS ){
  2231. DBGMSG( DBG_WARN,
  2232. ( "RpcStringFree failed with Status %d\n",
  2233. Status ));
  2234. }
  2235. }
  2236. Status = RpcBindingFree( &serverBinding );
  2237. if( Status != ERROR_SUCCESS ){
  2238. DBGMSG( DBG_WARN,
  2239. ( "RpcBindingFree failed with Status %d\n",
  2240. Status ));
  2241. }
  2242. }
  2243. //
  2244. // If no partial name from RPC, use the client info.
  2245. //
  2246. if( !pszPartialName ){
  2247. //
  2248. // Unable to retrieve name; rely on handle's passed in name.
  2249. //
  2250. if( pSpool->SplClientInfo1.pMachineName ){
  2251. pIniJob->pMachineName = AllocSplStr( pSpool->SplClientInfo1.pMachineName );
  2252. }
  2253. //
  2254. // Very last resort, use local machine name. This is completely
  2255. // bogus, but backward compatible.
  2256. //
  2257. if( !pIniJob->pMachineName ){
  2258. pIniJob->pMachineName = AllocSplStr(pSpool->pIniSpooler->pMachineName);
  2259. }
  2260. }
  2261. }
  2262. //
  2263. // If it's a partial name, make sure it starts with two backslashes.
  2264. //
  2265. if( pszPartialName ){
  2266. if( pszPartialName[0] != '\\' ){
  2267. UINT cb = ( wcslen( pszPartialName ) + 3 ) *
  2268. sizeof( pszPartialName[0] );
  2269. pIniJob->pMachineName = (LPWSTR)AllocSplMem( cb );
  2270. if( pIniJob->pMachineName ){
  2271. pIniJob->pMachineName[0] = TEXT( '\\' );
  2272. pIniJob->pMachineName[1] = TEXT( '\\' );
  2273. wcscpy( &pIniJob->pMachineName[2], pszPartialName );
  2274. }
  2275. } else {
  2276. pIniJob->pMachineName = AllocSplStr( pszPartialName );
  2277. }
  2278. }
  2279. //
  2280. // Free off any necessary buffers.
  2281. //
  2282. if( pszRpcFree ){
  2283. Status = RpcStringFree( &pszRpcFree );
  2284. if( Status != ERROR_SUCCESS ){
  2285. DBGMSG( DBG_WARN,
  2286. ( "RpcStringFree failed with Status %d\n", Status ));
  2287. }
  2288. }
  2289. return pIniJob->pMachineName != NULL;
  2290. }
  2291. PINIJOB
  2292. CreateJobEntry(
  2293. PSPOOL pSpool,
  2294. DWORD Level,
  2295. LPBYTE pDocInfo,
  2296. DWORD JobId,
  2297. BOOL bRemote,
  2298. DWORD JobStatus,
  2299. LPWSTR pMachineName)
  2300. {
  2301. PDOC_INFO_1 pDocInfo1 = (PDOC_INFO_1)pDocInfo;
  2302. PINIJOB pIniJob = NULL;
  2303. PINIPRINTPROC pIniPrintProc;
  2304. BOOL bUserName;
  2305. WCHAR UserName[MAX_PATH];
  2306. DWORD cbUserName = MAX_PATH;
  2307. PDEVMODE pDevMode;
  2308. LPWSTR pDefaultDatatype;
  2309. DWORD cchCount;
  2310. LPWSTR pName;
  2311. PDEVMODE pDevModeFree = NULL;
  2312. LPWSTR pMachineNameFixup = NULL;
  2313. //
  2314. // Assert that we are in Spooler Semaphore
  2315. //
  2316. SplInSem();
  2317. //
  2318. // Sorry You cannot print whilst Upgrading
  2319. //
  2320. if ( dwUpgradeFlag != 0 ) {
  2321. SetLastError( ERROR_PRINTQ_FULL );
  2322. goto Fail;
  2323. }
  2324. //
  2325. // Do the check for the printer pending deletion first
  2326. //
  2327. if (pSpool->pIniPrinter->Status & (PRINTER_PENDING_DELETION | PRINTER_NO_MORE_JOBS )) {
  2328. DBGMSG(DBG_WARNING, ("The printer is pending deletion %ws\n", pSpool->pIniPrinter->pName));
  2329. SetLastError(ERROR_PRINTER_DELETED);
  2330. goto Fail;
  2331. }
  2332. //
  2333. // NT FAX Requires that you not be able to remotely print to a FAX
  2334. // printer unless you've installed the FAX Server
  2335. //
  2336. if ( bRemote &&
  2337. pSpool->pIniPrinter->pIniSpooler->pNoRemotePrintDrivers ) {
  2338. for ( cchCount = pSpool->pIniSpooler->cchNoRemotePrintDrivers, pName = pSpool->pIniSpooler->pNoRemotePrintDrivers;
  2339. cchCount && *pName;
  2340. cchCount -= wcslen( pName ) + 1, pName += wcslen( pName ) + 1 ) {
  2341. if ( _wcsicmp( pSpool->pIniPrinter->pIniDriver->pName, pName ) == STRINGS_ARE_EQUAL ) {
  2342. SetLastError( ERROR_NETWORK_ACCESS_DENIED );
  2343. DBGMSG( DBG_WARN, ("CreateJobEntry failing because driver %ws used, error %d\n", pName, GetLastError() ));
  2344. goto Fail;
  2345. }
  2346. }
  2347. }
  2348. pIniJob = AllocSplMem( sizeof( INIJOB ));
  2349. if ( pIniJob == NULL ) {
  2350. DBGMSG( DBG_WARNING, ("AllocSplMem for the IniJob failed in CreateJobEntry\n"));
  2351. goto Fail;
  2352. }
  2353. pIniJob->signature = IJ_SIGNATURE;
  2354. pIniJob->pIniNextJob = pIniJob->pIniPrevJob = NULL;
  2355. pIniJob->hFileItem = INVALID_HANDLE_VALUE;
  2356. pIniJob->pszSplFileName = NULL;
  2357. pIniJob->AddJobLevel = 0;
  2358. #ifdef _HYDRA_
  2359. //
  2360. // Must set the Job SessionId
  2361. //
  2362. pIniJob->SessionId = pSpool->SessionId;
  2363. #endif
  2364. //
  2365. // Pickup the default datatype/printproc if not in pSpool or
  2366. // DocInfo.
  2367. //
  2368. pIniPrintProc = pSpool->pIniPrintProc ?
  2369. pSpool->pIniPrintProc :
  2370. pSpool->pIniPrinter->pIniPrintProc;
  2371. if ( pDocInfo1 && pDocInfo1->pDatatype ) {
  2372. if (!(pIniJob->pDatatype = AllocSplStr( pDocInfo1->pDatatype ))) {
  2373. goto Fail;
  2374. }
  2375. } else {
  2376. pDefaultDatatype = pSpool->pDatatype ?
  2377. pSpool->pDatatype :
  2378. pSpool->pIniPrinter->pDatatype;
  2379. //
  2380. // If going direct, we must use a RAW datatype.
  2381. //
  2382. if ((JobStatus & JOB_DIRECT) &&
  2383. (!ValidRawDatatype(pDefaultDatatype))) {
  2384. //
  2385. // Can't use a non-raw, so fail with invalid datatype.
  2386. // Cleanup and exit.
  2387. //
  2388. SetLastError( ERROR_INVALID_DATATYPE );
  2389. goto Fail;
  2390. } else {
  2391. if (!(pIniJob->pDatatype = AllocSplStr( pDefaultDatatype ))) {
  2392. goto Fail;
  2393. }
  2394. }
  2395. }
  2396. pIniJob->pIniPrintProc = FindDatatype( pIniPrintProc,
  2397. pIniJob->pDatatype );
  2398. if ( !pIniJob->pIniPrintProc ) {
  2399. SetLastError( ERROR_INVALID_DATATYPE );
  2400. goto Fail;
  2401. }
  2402. pIniJob->pIniPrintProc->cRef++;
  2403. //
  2404. // cRef is decremented in LocalEndDocPrinter and
  2405. // in LocalScheduleJob
  2406. //
  2407. INITJOBREFONE(pIniJob);
  2408. if ( bRemote ) {
  2409. JobStatus |= JOB_REMOTE;
  2410. }
  2411. pIniJob->JobId = JobId;
  2412. pIniJob->Status = JobStatus;
  2413. //
  2414. // Get the name of the user, leave critical section, this might take a long time to call LSA.
  2415. //
  2416. LeaveSplSem();
  2417. SplOutSem();
  2418. bUserName = GetUserName( UserName, &cbUserName );
  2419. EnterSplSem();
  2420. if ( bUserName ) {
  2421. //
  2422. // If we got user name from remote handle check it is the same we get here
  2423. //
  2424. #if DBG
  2425. if( pSpool->SplClientInfo1.pUserName &&
  2426. _wcsicmp( UserName, pSpool->SplClientInfo1.pUserName ) &&
  2427. _wcsicmp( UserName, L"ANONYMOUS LOGON" )){
  2428. DBGMSG( DBG_WARN,
  2429. ( "CreateJobEntry: Bad UserName pSpool= "TSTR" Curent= "TSTR"\n",
  2430. DBGSTR( pSpool->SplClientInfo1.pUserName ),
  2431. DBGSTR( UserName )));
  2432. }
  2433. #endif
  2434. if (!(pIniJob->pUser = AllocSplStr( UserName ))) {
  2435. goto Fail;
  2436. }
  2437. if (!(pIniJob->pNotify = AllocSplStr( UserName ))) {
  2438. goto Fail;
  2439. }
  2440. } else {
  2441. DBGMSG(DBG_WARNING, ("CreateJobEntry GetUserName failed: %d\n", GetLastError()));
  2442. goto Fail;
  2443. }
  2444. //
  2445. // Create a document security descriptor
  2446. //
  2447. pIniJob->pSecurityDescriptor = CreateDocumentSecurityDescriptor( pSpool->pIniPrinter->pSecurityDescriptor );
  2448. if( !pIniJob->pSecurityDescriptor ){
  2449. goto Fail;
  2450. }
  2451. //
  2452. // Now process the DocInfo structure passed in
  2453. //
  2454. if (pDocInfo1 && pDocInfo1->pDocName)
  2455. pIniJob->pDocument = AllocSplStr(pDocInfo1->pDocName);
  2456. else
  2457. pIniJob->pDocument = AllocSplStr(L"No Document Name");
  2458. if (!pIniJob->pDocument)
  2459. goto Fail;
  2460. if (pDocInfo1 && pDocInfo1->pOutputFile) {
  2461. if (!(pIniJob->pOutputFile = AllocSplStr(pDocInfo1->pOutputFile)))
  2462. goto Fail;
  2463. }
  2464. else
  2465. pIniJob->pOutputFile = NULL;
  2466. GetSid( &pIniJob->hToken );
  2467. //
  2468. // Pickup default if none specified.
  2469. // (Default at time of job submission.)
  2470. //
  2471. if( pSpool->pDevMode ){
  2472. pDevMode = pSpool->pDevMode;
  2473. } else {
  2474. if( bGetDevModePerUser( NULL,
  2475. pSpool->pIniPrinter->pName,
  2476. &pDevModeFree )){
  2477. pDevMode = pDevModeFree;
  2478. } else {
  2479. pDevMode = pSpool->pIniPrinter->pDevMode;
  2480. }
  2481. }
  2482. if ( pDevMode ) {
  2483. pIniJob->pDevMode = AllocDevMode( pDevMode );
  2484. if ( pIniJob->pDevMode == NULL )
  2485. goto Fail;
  2486. FreeSplMem( pDevModeFree );
  2487. }
  2488. GetSystemTime( &pIniJob->Submitted );
  2489. pIniJob->pIniPrinter = pSpool->pIniPrinter;
  2490. pSpool->pIniPrinter->cJobs++;
  2491. pSpool->pIniPrinter->cTotalJobs++;
  2492. pIniJob->pIniDriver = pSpool->pIniPrinter->pIniDriver;
  2493. INCDRIVERREF( pIniJob->pIniDriver );
  2494. pIniJob->pIniPort = NULL;
  2495. pIniJob->pParameters = NULL;
  2496. if( !bAddMachineName( pSpool, pIniJob, pMachineName )){
  2497. goto Fail;
  2498. }
  2499. pIniJob->pStatus = NULL;
  2500. pIniJob->cPages = pIniJob->Size = 0;
  2501. pIniJob->cPagesPrinted = 0;
  2502. pIniJob->Priority = pSpool->pIniPrinter->DefaultPriority == NO_PRIORITY ?
  2503. DEF_PRIORITY : pSpool->pIniPrinter->DefaultPriority;
  2504. pIniJob->StartTime = pSpool->pIniPrinter->StartTime;
  2505. pIniJob->UntilTime = pSpool->pIniPrinter->UntilTime;
  2506. pIniJob->cbPrinted = 0;
  2507. pIniJob->WaitForWrite = NULL;
  2508. pIniJob->WaitForRead = NULL;
  2509. pIniJob->hWriteFile = INVALID_HANDLE_VALUE;
  2510. pIniJob->dwJobNumberOfPagesPerSide = 0;
  2511. pIniJob->dwDrvNumberOfPagesPerSide = 0;
  2512. pIniJob->cLogicalPages = 0;
  2513. pIniJob->cLogicalPagesPrinted = 0;
  2514. // Additional fields for SeekPrinter.
  2515. pIniJob->WaitForSeek = NULL;
  2516. pIniJob->bWaitForEnd = FALSE;
  2517. pIniJob->bWaitForSeek = FALSE;
  2518. pIniJob->liFileSeekPosn.u.HighPart = 0;
  2519. pIniJob->liFileSeekPosn.u.LowPart = 0;
  2520. if( dwEnableBroadcastSpoolerStatus ){
  2521. BroadcastChange( pIniJob->pIniPrinter->pIniSpooler,
  2522. WM_SPOOLERSTATUS,
  2523. PR_JOBSTATUS,
  2524. pIniJob->pIniPrinter->cJobs );
  2525. }
  2526. return pIniJob;
  2527. Fail:
  2528. if (pIniJob) {
  2529. FreeSplStr(pIniJob->pDatatype);
  2530. FreeSplStr(pIniJob->pUser);
  2531. FreeSplStr(pIniJob->pNotify);
  2532. FreeSplStr(pIniJob->pDocument);
  2533. FreeSplStr(pIniJob->pOutputFile);
  2534. FreeSplStr(pIniJob->pMachineName);
  2535. if ( pIniJob->pDevMode != NULL )
  2536. FreeSplMem(pIniJob->pDevMode);
  2537. if (pIniJob->pSecurityDescriptor)
  2538. DeleteDocumentSecurity(pIniJob);
  2539. if (pIniJob->hToken)
  2540. CloseHandle(pIniJob->hToken);
  2541. FreeSplMem(pIniJob);
  2542. }
  2543. return NULL;
  2544. }
  2545. BOOL
  2546. DeletePrinterCheck(
  2547. PINIPRINTER pIniPrinter
  2548. )
  2549. {
  2550. //
  2551. // Enough space for printer, DWORD. (Zombie string)
  2552. //
  2553. WCHAR TempName[MAX_PATH + 20];
  2554. BOOL bReturn = FALSE;
  2555. SplInSem();
  2556. if ( pIniPrinter->Status & PRINTER_PENDING_DELETION ) {
  2557. if ( pIniPrinter->cJobs == 0 ) {
  2558. if ( pIniPrinter->cRef == 0 ) {
  2559. return DeletePrinterForReal( pIniPrinter, NON_INIT_TIME );
  2560. }
  2561. //
  2562. // If we don't want to update PRINTERINI, then don't
  2563. // zombie the printer.
  2564. //
  2565. if( pIniPrinter->pIniSpooler->SpoolerFlags & SPL_OFFLINE ){
  2566. return TRUE;
  2567. }
  2568. //
  2569. // We will have zombie printers only if we should fail OpenPrinter
  2570. // on printers pending deletion. Because when marking a printer
  2571. // as zombie printer we change the name
  2572. //
  2573. if ( pIniPrinter->pIniSpooler->SpoolerFlags &
  2574. SPL_FAIL_OPEN_PRINTERS_PENDING_DELETION ) {
  2575. if ( !( pIniPrinter->Status & PRINTER_ZOMBIE_OBJECT )) {
  2576. if ( !pIniPrinter->cZombieRef ) {
  2577. PWSTR pNameTemp = NULL;
  2578. PWSTR pShareNameTemp = NULL;
  2579. wsprintf( TempName, L"%ws,%d", pIniPrinter->pName, gdwZombieCount++ );
  2580. pNameTemp = AllocSplStr(TempName);
  2581. pShareNameTemp = AllocSplStr(TempName);
  2582. if (pNameTemp && pShareNameTemp) {
  2583. UpdateWinIni( pIniPrinter );
  2584. // Change "PrinterName" to "PrinterName,UniqueId"
  2585. // Since comma is not legal in a printer name
  2586. // the name will continue to be unique, but different
  2587. // so that OpenPrinters will still fail.
  2588. // We have to have a unique ID appended in case someone is crazy enough
  2589. // to create / delete / create / delete the same printer over and over.
  2590. CopyPrinterIni( pIniPrinter, TempName );
  2591. DeletePrinterIni( pIniPrinter );
  2592. FreeSplStr(pIniPrinter->pName);
  2593. FreeSplStr(pIniPrinter->pShareName);
  2594. pIniPrinter->pName = pNameTemp;
  2595. pIniPrinter->pShareName = pShareNameTemp;
  2596. if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) {
  2597. pIniPrinter->Attributes &= ~PRINTER_ATTRIBUTE_SHARED;
  2598. pIniPrinter->Status |= PRINTER_WAS_SHARED;
  2599. }
  2600. pIniPrinter->Status |= PRINTER_ZOMBIE_OBJECT;
  2601. UpdatePrinterIni( pIniPrinter , UPDATE_CHANGEID );
  2602. UpdateWinIni( pIniPrinter );
  2603. bReturn = TRUE;
  2604. } else {
  2605. FreeSplStr(pNameTemp);
  2606. FreeSplStr(pShareNameTemp);
  2607. DBGMSG(DBG_WARNING, ("%ws printer object could not be zombied\n", pIniPrinter->pName));
  2608. }
  2609. } else {
  2610. DBGMSG(DBG_WARNING, ("%ws Printer object should be zombied but is locked with %d ZombieRefs\n", pIniPrinter->pName, pIniPrinter->cZombieRef));
  2611. }
  2612. } else {
  2613. DBGMSG(DBG_TRACE, ("%ws zombie printer object\n", pIniPrinter->pName));
  2614. bReturn = TRUE;
  2615. }
  2616. DBGMSG( DBG_TRACE, ("%ws pending deletion: There %s still %d reference%s waiting\n",
  2617. pIniPrinter->pName,
  2618. pIniPrinter->cRef == 1 ? "is" : "are",
  2619. pIniPrinter->cRef,
  2620. pIniPrinter->cRef == 1 ? "" : "s"));
  2621. }
  2622. } else {
  2623. DBGMSG( DBG_TRACE, ("%ws pending deletion: There %s still %d jobs%s\n",
  2624. pIniPrinter->pName,
  2625. pIniPrinter->cJobs == 1 ? "is" : "are",
  2626. pIniPrinter->cJobs,
  2627. pIniPrinter->cJobs == 1 ? "" : "s"));
  2628. }
  2629. }
  2630. return bReturn;
  2631. }
  2632. VOID
  2633. UpdateReferencesToChainedJobs(
  2634. PINISPOOLER pIniSpooler
  2635. )
  2636. /*++
  2637. Routine Description:
  2638. Walks through all printers and all jobs associated with those printers
  2639. Once it finds a job with a NextJobId, it increments the reference on the
  2640. NextJob.
  2641. Called on reboot
  2642. Arguments:
  2643. pIniSpooer Pointer to the Spooler
  2644. Return Value:
  2645. NONE
  2646. --*/
  2647. {
  2648. PINIJOB pIniJob;
  2649. PINIJOB pNextJob;
  2650. PINIPRINTER pIniPrinter;
  2651. DWORD Position;
  2652. SPLASSERT( pIniSpooler->signature == ISP_SIGNATURE );
  2653. for ( pIniPrinter = pIniSpooler->pIniPrinter;
  2654. pIniPrinter;
  2655. pIniPrinter = pIniPrinter->pNext ) {
  2656. SPLASSERT( pIniPrinter->signature == IP_SIGNATURE );
  2657. for ( pIniJob = pIniPrinter->pIniFirstJob;
  2658. pIniJob;
  2659. pIniJob = pIniJob->pIniNextJob ) {
  2660. SPLASSERT( pIniJob->signature == IJ_SIGNATURE );
  2661. if ( pIniJob->NextJobId ) {
  2662. pNextJob = FindJob( pIniPrinter, pIniJob->NextJobId, &Position );
  2663. if ( pNextJob ) {
  2664. pNextJob->Status |= ( JOB_COMPOUND | JOB_HIDDEN );
  2665. DBGMSG( DBG_TRACE, ("UpdateReferencesToChainedJobs Found pNextJob %x JobId %d\n",pNextJob, pNextJob->JobId));
  2666. INCJOBREF( pNextJob );
  2667. } else {
  2668. DBGMSG( DBG_WARNING, ("UpdateReferenesToChainedJobs unable to find Job %d\n", pIniJob->NextJobId ));
  2669. pIniJob->NextJobId = 0;
  2670. }
  2671. }
  2672. }
  2673. }
  2674. }
  2675. VOID UpdateJobAttributes(
  2676. PINIJOB pIniJob
  2677. )
  2678. /*++
  2679. Function Description: Updates the nup attributes in the pIniJob struct
  2680. Parameters: pIniJob - job struct to be updated
  2681. Return Values: NONE
  2682. --*/
  2683. {
  2684. ATTRIBUTE_INFO_2 AttributeInfo;
  2685. HANDLE hDrvPrinter = NULL;
  2686. FARPROC pfnDrvQueryJobAttributes;
  2687. HINSTANCE hDrvLib = NULL;
  2688. fnWinSpoolDrv fnList;
  2689. SplOutSem();
  2690. //
  2691. // No job or the job has already been initialized or we're printing Raw
  2692. //
  2693. if (!pIniJob || !pIniJob->pIniPrinter ||
  2694. pIniJob->dwDrvNumberOfPagesPerSide ||
  2695. pIniJob->dwJobNumberOfPagesPerSide ||
  2696. ValidRawDatatype(pIniJob->pDatatype))
  2697. {
  2698. return;
  2699. }
  2700. // Initialize job attributes;
  2701. pIniJob->dwJobNumberOfPagesPerSide = 1;
  2702. pIniJob->dwDrvNumberOfPagesPerSide = 1;
  2703. // Get the pointer to the client side functions from the router
  2704. if (!SplInitializeWinSpoolDrv(&fnList)) {
  2705. return;
  2706. }
  2707. // Get a client side printer handle to pass to the driver
  2708. if (!(* (fnList.pfnOpenPrinter))(pIniJob->pIniPrinter->pName, &hDrvPrinter, NULL)) {
  2709. DBGMSG(DBG_WARNING, ("Open printer failed\n"));
  2710. goto CleanUp;
  2711. }
  2712. // Load the driver config file
  2713. if (!(hDrvLib = (* (fnList.pfnLoadPrinterDriver))(hDrvPrinter))) {
  2714. DBGMSG(DBG_WARNING, ("Driver could not be loaded\n"));
  2715. goto CleanUp;
  2716. }
  2717. // Call the DrvQueryJobAtributes function in the driver
  2718. if (pfnDrvQueryJobAttributes = GetProcAddress(hDrvLib, "DrvQueryJobAttributes")) {
  2719. if ((* pfnDrvQueryJobAttributes) (hDrvPrinter,
  2720. pIniJob->pDevMode,
  2721. 1,
  2722. (LPBYTE) &AttributeInfo)) {
  2723. pIniJob->dwJobNumberOfPagesPerSide = AttributeInfo.dwJobNumberOfPagesPerSide;
  2724. pIniJob->dwDrvNumberOfPagesPerSide = AttributeInfo.dwDrvNumberOfPagesPerSide;
  2725. }
  2726. }
  2727. CleanUp:
  2728. if (hDrvPrinter) {
  2729. (* (fnList.pfnClosePrinter))(hDrvPrinter);
  2730. }
  2731. if (hDrvLib) {
  2732. (* (fnList.pfnRefCntUnloadDriver))(hDrvLib, TRUE);
  2733. }
  2734. return;
  2735. }