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.

637 lines
16 KiB

  1. /*++
  2. Copyright (c) 1990-1994 Microsoft Corporation
  3. Module Name:
  4. addjob.c
  5. Abstract:
  6. This module provides all the public exported APIs relating to Printer
  7. and Job management for the Local Print Providor. This module contains
  8. LocalSpl's implementation of the following spooler apis
  9. LocalAddJob
  10. LocalScheduleJob
  11. Author:
  12. Dave Snipp (DaveSn) 15-Mar-1991
  13. Revision History:
  14. Rewritten both apis -- Krishna Ganugapati (KrishnaG) 5-Apr-1994
  15. RapidPrint -- Matthew A Felton (mattfe) June 1994
  16. --*/
  17. #include <precomp.h>
  18. #pragma hdrstop
  19. #include "jobid.h"
  20. #include "winsprlp.h"
  21. #include "filepool.hxx"
  22. VOID
  23. AddJobEntry(
  24. PINIPRINTER pIniPrinter,
  25. PINIJOB pIniJob
  26. );
  27. BOOL
  28. LocalAddJob(
  29. HANDLE hPrinter,
  30. DWORD Level,
  31. LPBYTE pData,
  32. DWORD cbBuf,
  33. LPDWORD pcbNeeded
  34. )
  35. {
  36. PINIPRINTER pIniPrinter;
  37. PINIJOB pIniJob;
  38. PSPOOL pSpool=(PSPOOL)hPrinter;
  39. DWORD cb;
  40. WCHAR szFileName[MAX_PATH];
  41. LPBYTE pEnd;
  42. DWORD LastError=0;
  43. LPADDJOB_INFO_1 pAddJob = (LPADDJOB_INFO_1)pData;
  44. DWORD NextId;
  45. BOOL bRemote = FALSE;
  46. DOC_INFO_1 DocInfo1;
  47. BOOL bRet;
  48. DWORD dwStatus = 0;
  49. HANDLE hFile = INVALID_HANDLE_VALUE;
  50. LPWSTR pMachineName = NULL;
  51. LPWSTR pszSpoolFile = NULL;
  52. PMAPPED_JOB pMappedJob = NULL;
  53. SIZE_T FileNameLength = 0;
  54. SplOutSem();
  55. switch( Level ){
  56. case 1:
  57. break;
  58. case 2:
  59. case 3:
  60. pMachineName = (LPWSTR)( ((PBYTE)pData) +
  61. (ULONG_PTR)((PADDJOB_INFO_2W)pData)->pData );
  62. //
  63. // Validate string.
  64. //
  65. if( pMachineName > (LPWSTR)( ((PBYTE)pData)+cbBuf )){
  66. SetLastError( ERROR_INVALID_LEVEL );
  67. return FALSE;
  68. }
  69. //
  70. // Ensure NULL termination.
  71. //
  72. *(PWCHAR)(((ULONG_PTR)(pData + cbBuf - sizeof( WCHAR ))&~1)) = 0;
  73. break;
  74. default:
  75. SetLastError( ERROR_INVALID_LEVEL );
  76. return FALSE;
  77. }
  78. //
  79. // memset docinfo
  80. //
  81. memset((LPBYTE)&DocInfo1, 0, sizeof(DOC_INFO_1));
  82. //
  83. // Figure out whether the job is a remote or local job
  84. //
  85. {
  86. HRESULT hRes = CheckLocalCall();
  87. if (hRes == S_FALSE)
  88. {
  89. bRemote = TRUE;
  90. }
  91. else if (hRes != S_OK)
  92. {
  93. SetLastError(SCODE_CODE(hRes));
  94. return FALSE;
  95. }
  96. }
  97. //
  98. // Get the name of the user
  99. //
  100. if (bRemote) {
  101. DocInfo1.pDocName = szRemoteDoc;
  102. } else{
  103. DocInfo1.pDocName = szLocalDoc;
  104. }
  105. EnterSplSem();
  106. //
  107. // We should not be calling addjob on a Job Handle.
  108. //
  109. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER | PRINTER_HANDLE_JOB )) {
  110. LeaveSplSem();
  111. return(FALSE);
  112. }
  113. //
  114. // We're interested if this is a remote call (not if it was opened
  115. // via \\server\remote). The server process does this.
  116. //
  117. if (pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) {
  118. LeaveSplSem();
  119. SetLastError(ERROR_INVALID_PARAMETER);
  120. return(FALSE);
  121. }
  122. if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) {
  123. if (pSpool->pIniPort->Status & PP_MONITOR) {
  124. LeaveSplSem();
  125. SetLastError(ERROR_INVALID_PARAMETER);
  126. return(FALSE);
  127. } else {
  128. //
  129. // If we had level == 2 (passing in the computer name), then
  130. // convert back down to level 1 for old print providers.
  131. // We don't need to fix up the structure since level 1 and 2
  132. // are identical; it's just that level 2 is an in-out buffer.
  133. //
  134. //
  135. if (Level == 2 || Level == 3) {
  136. Level = 1;
  137. }
  138. //
  139. // This is the "Local Printer masquerading as a Remote Printer"
  140. //
  141. LeaveSplSem();
  142. bRet = AddJob(pSpool->hPort, Level, pData, cbBuf, pcbNeeded);
  143. if(bRet)
  144. {
  145. EnterSplSem();
  146. pSpool->Status |= SPOOL_STATUS_ADDJOB;
  147. LeaveSplSem();
  148. }
  149. return(bRet);
  150. }
  151. }
  152. pIniPrinter = pSpool->pIniPrinter;
  153. SPLASSERT(pIniPrinter);
  154. if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT) {
  155. LeaveSplSem();
  156. SetLastError(ERROR_INVALID_ACCESS);
  157. return(FALSE);
  158. }
  159. //
  160. // Disallow EMF if PRINTER_ATTRIBUTE_RAW_ONLY is set.
  161. //
  162. if( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_RAW_ONLY ){
  163. LPWSTR pszDatatype = pSpool->pDatatype ?
  164. pSpool->pDatatype :
  165. pIniPrinter->pDatatype;
  166. if( !ValidRawDatatype( pszDatatype )){
  167. LeaveSplSem();
  168. SetLastError( ERROR_INVALID_DATATYPE );
  169. return FALSE;
  170. }
  171. }
  172. NextId = GetNextId( pIniPrinter->pIniSpooler->hJobIdMap );
  173. GetFullNameFromId(pIniPrinter, NextId, TRUE, szFileName, COUNTOF(szFileName), pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL);
  174. cb = wcslen(szFileName)*sizeof(WCHAR) + sizeof(WCHAR) +
  175. sizeof(ADDJOB_INFO_1);
  176. *pcbNeeded = cb;
  177. if (cb > cbBuf) {
  178. // Freeup the JobId.
  179. vMarkOff( pIniPrinter->pIniSpooler->hJobIdMap, NextId);
  180. LeaveSplSem();
  181. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  182. return(FALSE);
  183. }
  184. //
  185. // WMI Trace Event
  186. //
  187. LeaveSplSem();
  188. LogWmiTraceEvent(NextId, EVENT_TRACE_TYPE_SPL_SPOOLJOB, NULL);
  189. EnterSplSem();
  190. SplInSem();
  191. dwStatus = JOB_SPOOLING | JOB_TYPE_ADDJOB;
  192. if (Level == 2 || Level ==3) {
  193. dwStatus |= JOB_DOWNLEVEL;
  194. }
  195. if ((pIniJob = CreateJobEntry(pSpool,
  196. 1,
  197. (LPBYTE)&DocInfo1,
  198. NextId,
  199. bRemote,
  200. dwStatus,
  201. pMachineName)) == NULL) {
  202. //
  203. // Free up the JobId.
  204. //
  205. vMarkOff( pIniPrinter->pIniSpooler->hJobIdMap, NextId);
  206. DBGMSG(DBG_WARNING,("Error: CreateJobEntry failed in LocalAddJob\n"));
  207. LeaveSplSem();
  208. return(FALSE);
  209. }
  210. //
  211. // Level 3 is called only by RDR/SRV. For details see LocalScheduleJob
  212. //
  213. pIniJob->AddJobLevel = Level;
  214. pIniPrinter->cSpooling++;
  215. if (pIniPrinter->cSpooling > pIniPrinter->cMaxSpooling) {
  216. pIniPrinter->cMaxSpooling = pIniPrinter->cSpooling;
  217. }
  218. AddJobEntry(pIniPrinter, pIniJob);
  219. pEnd = (LPBYTE)pAddJob+cbBuf;
  220. FileNameLength = (wcslen(szFileName) + 1)*sizeof(WCHAR);
  221. pEnd -= FileNameLength;
  222. WORD_ALIGN_DOWN(pEnd);
  223. //
  224. // This is OK because we have already checked that the buffer is long enough
  225. // to contain this string. Completely reworking this function now is too risky.
  226. //
  227. StringCchCopy((LPWSTR)pEnd, FileNameLength, szFileName);
  228. pAddJob->Path = (LPWSTR)pEnd;
  229. pAddJob->JobId = pIniJob->JobId;
  230. //
  231. // Now we want to add the job into the spools list of current jobs.
  232. // This is so that the spool file can be deleted correctly at the end
  233. // of the job, even if we have aborted.
  234. //
  235. pMappedJob = AllocSplMem(sizeof(MAPPED_JOB));
  236. pszSpoolFile = AllocSplMem(MAX_PATH * sizeof( WCHAR ));
  237. if (pMappedJob && pszSpoolFile)
  238. {
  239. BOOL bDuplicate = FALSE;
  240. DWORD TempJobId = pIniJob->JobId;
  241. PMAPPED_JOB pTempMappedJob;
  242. StringCchCopy(pszSpoolFile, MAX_PATH, szFileName);
  243. //
  244. // Run through the list and make sure we have no duplicates.
  245. // It is not at all obvious why this would ever be the case.
  246. //
  247. for (pTempMappedJob = pSpool->pMappedJob;
  248. pTempMappedJob;
  249. pTempMappedJob = pTempMappedJob->pNext) {
  250. if (pTempMappedJob->JobId == TempJobId) {
  251. //
  252. // Set the mapped job to record that it was added with AddJob.
  253. //
  254. pTempMappedJob->fStatus |= kMappedJobAddJob;
  255. bDuplicate = TRUE;
  256. break;
  257. }
  258. }
  259. //
  260. // No duplicates, add this job to the linked list.
  261. //
  262. if (!bDuplicate) {
  263. pMappedJob->pszSpoolFile = pszSpoolFile;
  264. pMappedJob->fStatus = kMappedJobAddJob;
  265. pMappedJob->JobId = TempJobId;
  266. pMappedJob->pNext = pSpool->pMappedJob;
  267. pSpool->pMappedJob = pMappedJob;
  268. } else {
  269. FreeSplMem(pszSpoolFile);
  270. FreeSplMem(pMappedJob);
  271. }
  272. }
  273. else
  274. {
  275. FreeSplMem(pMappedJob);
  276. FreeSplMem(pszSpoolFile);
  277. }
  278. //
  279. //
  280. // Storing pIniJob in pSpool is bogus since you can call AddJob multiple
  281. // times. We should have a linked list here. If the client calls AddJob
  282. // two times then closes the handle (no ScheduleJob), then only the last
  283. // job is rundown and eliminated.
  284. //
  285. // This bug has been here since 3.1, and probably isn't worth fixing.
  286. //
  287. pSpool->pIniJob = pIniJob;
  288. pSpool->Status |= SPOOL_STATUS_ADDJOB;
  289. SetPrinterChange(pSpool->pIniPrinter,
  290. pIniJob,
  291. NVAddJob,
  292. PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER,
  293. pSpool->pIniSpooler );
  294. //
  295. // If necessary Start Downlevel Size Detection thread
  296. //
  297. CheckSizeDetectionThread();
  298. LeaveSplSem();
  299. SplOutSem();
  300. return TRUE;
  301. }
  302. BOOL
  303. LocalScheduleJob(
  304. HANDLE hPrinter,
  305. DWORD JobId)
  306. /*++
  307. Routine Description:
  308. Arguments:
  309. Returns:
  310. --*/
  311. {
  312. PSPOOL pSpool=(PSPOOL)hPrinter;
  313. WCHAR szFileName[MAX_PATH];
  314. PINIJOB pIniJob;
  315. DWORD Position;
  316. DWORD LastError = FALSE;
  317. HANDLE hPort;
  318. BOOL bRet;
  319. NOTIFYVECTOR NotifyVector;
  320. WIN32_FILE_ATTRIBUTE_DATA FileAttributeData;
  321. PMAPPED_JOB pMappedJob = NULL;
  322. COPYNV(NotifyVector, NVJobStatus);
  323. //
  324. // WMI Trace Event.
  325. //
  326. LogWmiTraceEvent(JobId, EVENT_TRACE_TYPE_SPL_TRACKTHREAD, NULL);
  327. SplOutSem();
  328. EnterSplSem();
  329. //
  330. // We should not be calling schedulejob on a Job Handle.
  331. //
  332. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER | PRINTER_HANDLE_JOB )) {
  333. LeaveSplSem();
  334. return (FALSE);
  335. }
  336. if (pSpool->Status & SPOOL_STATUS_STARTDOC) {
  337. SetLastError(ERROR_SPL_NO_ADDJOB);
  338. LeaveSplSem();
  339. return(FALSE);
  340. }
  341. if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) {
  342. if (pSpool->pIniPort->Status & PP_MONITOR) {
  343. SetLastError(ERROR_INVALID_ACCESS);
  344. LeaveSplSem();
  345. return(FALSE);
  346. }
  347. //
  348. // This is the "Local Printer masquerading as the Network Printer"
  349. //
  350. hPort = pSpool->hPort;
  351. LeaveSplSem();
  352. bRet = ScheduleJob(hPort, JobId);
  353. return(bRet);
  354. }
  355. if ((pIniJob = FindJob(pSpool->pIniPrinter, JobId, &Position)) == NULL) {
  356. SetLastError(ERROR_INVALID_PARAMETER);
  357. LeaveSplSem();
  358. return(FALSE);
  359. }
  360. if (pIniJob->Status & JOB_SCHEDULE_JOB) {
  361. DBGMSG(DBG_WARNING, ("ScheduleJob: job 0x%x (id = %d) already scheduled\n",
  362. pIniJob, pIniJob->JobId));
  363. SetLastError(ERROR_INVALID_PARAMETER);
  364. LeaveSplSem();
  365. return FALSE;
  366. }
  367. if (!(pIniJob->Status & JOB_TYPE_ADDJOB)) {
  368. DBGMSG(DBG_WARNING, ("ScheduleJob: job 0x%x (id = %d) no addjob\n",
  369. pIniJob, pIniJob->JobId));
  370. SetLastError(ERROR_SPL_NO_ADDJOB);
  371. LeaveSplSem();
  372. return(FALSE);
  373. }
  374. //
  375. // Check to see whether this job was added with AddJob in the past on this
  376. // handle, if it was, then we can go ahead and schedule it. If it was not,
  377. // then we fail with Access denied.
  378. //
  379. for(pMappedJob = pSpool->pMappedJob; pMappedJob; pMappedJob = pMappedJob->pNext) {
  380. //
  381. // If we found the job on the same handle, clear the Addjob bit.
  382. //
  383. if (pMappedJob->JobId == JobId) {
  384. pMappedJob->fStatus &= ~kMappedJobAddJob;
  385. break;
  386. }
  387. }
  388. if (!pMappedJob) {
  389. SetLastError(ERROR_ACCESS_DENIED);
  390. LeaveSplSem();
  391. return FALSE;
  392. }
  393. InterlockedOr((LONG*)&(pIniJob->Status), JOB_SCHEDULE_JOB);
  394. if (pIniJob->Status & JOB_SPOOLING) {
  395. InterlockedAnd((LONG*)&(pIniJob->Status), ~JOB_SPOOLING);
  396. pIniJob->pIniPrinter->cSpooling--;
  397. }
  398. if ( pIniJob->Status & JOB_TIMEOUT ) {
  399. InterlockedAnd((LONG*)&(pIniJob->Status), ~(JOB_TIMEOUT | JOB_ABANDON));
  400. FreeSplStr(pIniJob->pStatus);
  401. pIniJob->pStatus = NULL;
  402. }
  403. SplInSem();
  404. //
  405. // Despooling whilst spooling requires us to wake the writing
  406. // thread if it is waiting.
  407. //
  408. if ( pIniJob->WaitForWrite != NULL )
  409. SetEvent(pIniJob->WaitForWrite);
  410. //
  411. // Release any thread waiting on SeekPrinter for this job.
  412. //
  413. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  414. SPLASSERT(pIniJob->cRef != 0);
  415. DECJOBREF(pIniJob);
  416. DBGMSG(DBG_TRACE, ("ScheduleJob:cRef = %d\n", pIniJob->cRef));
  417. //
  418. // FP Change
  419. // For File pools, we know the Filename of the spool file, so
  420. // we can just copy it in.
  421. //
  422. if ( pIniJob->pszSplFileName )
  423. {
  424. StringCchCopy(szFileName, COUNTOF(szFileName), pIniJob->pszSplFileName);
  425. }
  426. else
  427. {
  428. GetFullNameFromId(pSpool->pIniPrinter, pIniJob->JobId, TRUE, szFileName, COUNTOF(szFileName), FALSE);
  429. }
  430. bRet = GetFileAttributesEx(szFileName,
  431. GetFileExInfoStandard,
  432. &FileAttributeData);
  433. //
  434. // According to MSDN: The ScheduleJob function checks for a valid spool file.
  435. // If there is an invalid spool file, or if it is empty, ScheduleJob deletes
  436. // both the spool file and the corresponding print job entry in the print spooler.
  437. //
  438. // The RDR/SRV will call AddJob even if the caller of CreateFile did noy request
  439. // WRITE access. This will cause us at add a job, but nobody will ever write to
  440. // the spooler file. In this case, we delete the job. For this reason we have
  441. // level 3 for AddJob. Level 3 is meant to be used only by RDR/SRV.
  442. //
  443. if (!bRet ||
  444. !(FileAttributeData.nFileSizeLow || FileAttributeData.nFileSizeHigh) && pIniJob->AddJobLevel == 3) {
  445. DBGMSG(DBG_WARNING, ("Could not GetFileAttributesEx %ws in ScheduleJob or file size is 0\n", szFileName));
  446. DeleteJob(pIniJob, BROADCAST);
  447. pSpool->pIniJob = NULL;
  448. pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
  449. LeaveSplSem();
  450. //
  451. // If we deleted the job because the spool file was empty and the job came via RDR/SRV
  452. // In this case we return success.
  453. //
  454. if (bRet)
  455. {
  456. return TRUE;
  457. }
  458. //
  459. // We delete the job because the spool is not found
  460. //
  461. SetLastError(ERROR_SPOOL_FILE_NOT_FOUND);
  462. return(FALSE);
  463. }
  464. //
  465. // Do not accept spool files larger than 4GB
  466. //
  467. if (FileAttributeData.nFileSizeHigh && !ValidRawDatatype(pIniJob->pDatatype))
  468. {
  469. DeleteJob(pIniJob, BROADCAST);
  470. pSpool->pIniJob = NULL;
  471. pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
  472. LeaveSplSem();
  473. SetLastError(ERROR_ARITHMETIC_OVERFLOW);
  474. return FALSE;
  475. }
  476. //
  477. // If size changed, we must update our size
  478. // and potentially notify people.
  479. //
  480. if (pIniJob->Size != FileAttributeData.nFileSizeLow) {
  481. ADDNV(NotifyVector, NVSpoolJob);
  482. pIniJob->Size = FileAttributeData.nFileSizeLow;
  483. }
  484. WriteShadowJob(pIniJob, FALSE);
  485. if (pIniJob->Status & JOB_PENDING_DELETION) {
  486. DBGMSG(DBG_TRACE, ("LocalScheduleJob: Deleting Job because its pending deletion\n"));
  487. DeleteJob(pIniJob, BROADCAST);
  488. } else {
  489. CHECK_SCHEDULER();
  490. SetPrinterChange(pIniJob->pIniPrinter,
  491. pIniJob,
  492. NotifyVector,
  493. PRINTER_CHANGE_SET_JOB,
  494. pIniJob->pIniPrinter->pIniSpooler );
  495. }
  496. pSpool->pIniJob = NULL;
  497. pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
  498. LeaveSplSem();
  499. SplOutSem();
  500. return(TRUE);
  501. }