Leaked source code of windows server 2003
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.

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