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

560 lines
14 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. SplOutSem();
  54. switch( Level ){
  55. case 1:
  56. break;
  57. case 2:
  58. case 3:
  59. pMachineName = (LPWSTR)( ((PBYTE)pData) +
  60. (ULONG_PTR)((PADDJOB_INFO_2W)pData)->pData );
  61. //
  62. // Validate string.
  63. //
  64. if( pMachineName > (LPWSTR)( ((PBYTE)pData)+cbBuf )){
  65. SetLastError( ERROR_INVALID_LEVEL );
  66. return FALSE;
  67. }
  68. //
  69. // Ensure NULL termination.
  70. //
  71. *(PWCHAR)(((ULONG_PTR)(pData + cbBuf - sizeof( WCHAR ))&~1)) = 0;
  72. break;
  73. default:
  74. SetLastError( ERROR_INVALID_LEVEL );
  75. return FALSE;
  76. }
  77. //
  78. // memset docinfo
  79. //
  80. memset((LPBYTE)&DocInfo1, 0, sizeof(DOC_INFO_1));
  81. //
  82. // Figure out whether the job is a remote or local job
  83. //
  84. if (!IsLocalCall()) {
  85. bRemote = TRUE;
  86. }
  87. //
  88. // Get the name of the user
  89. //
  90. if (bRemote) {
  91. DocInfo1.pDocName = szRemoteDoc;
  92. } else{
  93. DocInfo1.pDocName = szLocalDoc;
  94. }
  95. EnterSplSem();
  96. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  97. LeaveSplSem();
  98. return(FALSE);
  99. }
  100. //
  101. // We're interested if this is a remote call (not if it was opened
  102. // via \\server\remote). The server process does this.
  103. //
  104. if (pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL) {
  105. LeaveSplSem();
  106. SetLastError(ERROR_INVALID_PARAMETER);
  107. return(FALSE);
  108. }
  109. if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) {
  110. if (pSpool->pIniPort->Status & PP_MONITOR) {
  111. LeaveSplSem();
  112. SetLastError(ERROR_INVALID_PARAMETER);
  113. return(FALSE);
  114. } else {
  115. //
  116. // If we had level == 2 (passing in the computer name), then
  117. // convert back down to level 1 for old print providers.
  118. // We don't need to fix up the structure since level 1 and 2
  119. // are identical; it's just that level 2 is an in-out buffer.
  120. //
  121. //
  122. if (Level == 2 || Level == 3) {
  123. Level = 1;
  124. }
  125. //
  126. // This is the "Local Printer masquerading as a Remote Printer"
  127. //
  128. LeaveSplSem();
  129. bRet = AddJob(pSpool->hPort, Level, pData, cbBuf, pcbNeeded);
  130. return(bRet);
  131. }
  132. }
  133. pIniPrinter = pSpool->pIniPrinter;
  134. SPLASSERT(pIniPrinter);
  135. if (pIniPrinter->Attributes & PRINTER_ATTRIBUTE_DIRECT) {
  136. LeaveSplSem();
  137. SetLastError(ERROR_INVALID_ACCESS);
  138. return(FALSE);
  139. }
  140. //
  141. // Disallow EMF if PRINTER_ATTRIBUTE_RAW_ONLY is set.
  142. //
  143. if( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_RAW_ONLY ){
  144. LPWSTR pszDatatype = pSpool->pDatatype ?
  145. pSpool->pDatatype :
  146. pIniPrinter->pDatatype;
  147. if( !ValidRawDatatype( pszDatatype )){
  148. LeaveSplSem();
  149. SetLastError( ERROR_INVALID_DATATYPE );
  150. return FALSE;
  151. }
  152. }
  153. NextId = GetNextId( pIniPrinter->pIniSpooler->hJobIdMap );
  154. GetFullNameFromId(pIniPrinter, NextId, TRUE, szFileName,
  155. pSpool->TypeofHandle & PRINTER_HANDLE_REMOTE_CALL);
  156. cb = wcslen(szFileName)*sizeof(WCHAR) + sizeof(WCHAR) +
  157. sizeof(ADDJOB_INFO_1);
  158. *pcbNeeded = cb;
  159. if (cb > cbBuf) {
  160. // Freeup the JobId.
  161. vMarkOff( pIniPrinter->pIniSpooler->hJobIdMap, NextId);
  162. LeaveSplSem();
  163. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  164. return(FALSE);
  165. }
  166. //
  167. // WMI Trace Event
  168. //
  169. LeaveSplSem();
  170. LogWmiTraceEvent(NextId, EVENT_TRACE_TYPE_SPL_SPOOLJOB, NULL);
  171. EnterSplSem();
  172. SplInSem();
  173. dwStatus = JOB_SPOOLING | JOB_TYPE_ADDJOB;
  174. if (Level == 2 || Level ==3) {
  175. dwStatus |= JOB_DOWNLEVEL;
  176. }
  177. if ((pIniJob = CreateJobEntry(pSpool,
  178. 1,
  179. (LPBYTE)&DocInfo1,
  180. NextId,
  181. bRemote,
  182. dwStatus,
  183. pMachineName)) == NULL) {
  184. // Freeup the JobId.
  185. vMarkOff( pIniPrinter->pIniSpooler->hJobIdMap, NextId);
  186. DBGMSG(DBG_WARNING,("Error: CreateJobEntry failed in LocalAddJob\n"));
  187. LeaveSplSem();
  188. return(FALSE);
  189. }
  190. //
  191. // Level 3 is called only by RDR/SRV. For details see LocalScheduleJob
  192. //
  193. pIniJob->AddJobLevel = Level;
  194. pIniPrinter->cSpooling++;
  195. if (pIniPrinter->cSpooling > pIniPrinter->cMaxSpooling) {
  196. pIniPrinter->cMaxSpooling = pIniPrinter->cSpooling;
  197. }
  198. AddJobEntry(pIniPrinter, pIniJob);
  199. pEnd = (LPBYTE)pAddJob+cbBuf;
  200. pEnd -= wcslen(szFileName)*sizeof(WCHAR)+sizeof(WCHAR);
  201. WORD_ALIGN_DOWN(pEnd);
  202. wcscpy((LPWSTR)pEnd, szFileName);
  203. pAddJob->Path = (LPWSTR)pEnd;
  204. pAddJob->JobId = pIniJob->JobId;
  205. //
  206. // Now we want to add the job into the spools list of current jobs.
  207. // This is so that the spool file can be deleted correctly at the end
  208. // of the job, even if we have aborted.
  209. //
  210. pMappedJob = AllocSplMem(sizeof( MAPPED_JOB ));
  211. pszSpoolFile = AllocSplMem(MAX_PATH * sizeof( WCHAR ));
  212. if (pMappedJob && pszSpoolFile)
  213. {
  214. BOOL bDuplicate = FALSE;
  215. DWORD TempJobId = pIniJob->JobId;
  216. PMAPPED_JOB pTempMappedJob;
  217. wcscpy(pszSpoolFile, szFileName);
  218. //
  219. // Run through the list and make sure we have no duplicates
  220. //
  221. for (pTempMappedJob = pSpool->pMappedJob;
  222. pTempMappedJob;
  223. pTempMappedJob = pTempMappedJob->pNext) {
  224. if (pTempMappedJob->JobId == TempJobId) {
  225. bDuplicate = TRUE;
  226. break;
  227. }
  228. }
  229. //
  230. // No duplicates, add this job to the linked list.
  231. //
  232. if (!bDuplicate) {
  233. pMappedJob->pszSpoolFile = pszSpoolFile;
  234. pMappedJob->JobId = TempJobId;
  235. pMappedJob->pNext = pSpool->pMappedJob;
  236. pSpool->pMappedJob = pMappedJob;
  237. } else {
  238. FreeSplMem(pszSpoolFile);
  239. FreeSplMem(pMappedJob);
  240. }
  241. }
  242. else
  243. {
  244. FreeSplMem(pMappedJob);
  245. FreeSplMem(pszSpoolFile);
  246. }
  247. //
  248. //
  249. // Storing pIniJob in pSpool is bogus since you can call AddJob multiple
  250. // times. We should have a linked list here. If the client calls AddJob
  251. // two times then closes the handle (no ScheduleJob), then only the last
  252. // job is rundown and eliminated.
  253. //
  254. // This bug has been here since 3.1, and probably isn't worth fixing.
  255. //
  256. pSpool->pIniJob = pIniJob;
  257. pSpool->Status |= SPOOL_STATUS_ADDJOB;
  258. SetPrinterChange(pSpool->pIniPrinter,
  259. pIniJob,
  260. NVAddJob,
  261. PRINTER_CHANGE_ADD_JOB | PRINTER_CHANGE_SET_PRINTER,
  262. pSpool->pIniSpooler );
  263. //
  264. // If necessary Start Downlevel Size Detection thread
  265. //
  266. CheckSizeDetectionThread();
  267. LeaveSplSem();
  268. SplOutSem();
  269. return TRUE;
  270. }
  271. BOOL
  272. LocalScheduleJob(
  273. HANDLE hPrinter,
  274. DWORD JobId)
  275. /*++
  276. Routine Description:
  277. Arguments:
  278. Returns:
  279. --*/
  280. {
  281. PSPOOL pSpool=(PSPOOL)hPrinter;
  282. WCHAR szFileName[MAX_PATH];
  283. PINIJOB pIniJob;
  284. DWORD Position;
  285. DWORD LastError = FALSE;
  286. HANDLE hPort;
  287. BOOL bRet;
  288. NOTIFYVECTOR NotifyVector;
  289. WIN32_FILE_ATTRIBUTE_DATA FileAttributeData;
  290. COPYNV(NotifyVector, NVJobStatus);
  291. //
  292. // WMI Trace Event.
  293. //
  294. LogWmiTraceEvent(JobId, EVENT_TRACE_TYPE_SPL_TRACKTHREAD, NULL);
  295. SplOutSem();
  296. EnterSplSem();
  297. if (!ValidateSpoolHandle(pSpool, PRINTER_HANDLE_SERVER )) {
  298. LeaveSplSem();
  299. return (FALSE);
  300. }
  301. if (pSpool->Status & SPOOL_STATUS_STARTDOC) {
  302. SetLastError(ERROR_SPL_NO_ADDJOB);
  303. LeaveSplSem();
  304. return(FALSE);
  305. }
  306. if (pSpool->TypeofHandle & PRINTER_HANDLE_PORT) {
  307. if (pSpool->pIniPort->Status & PP_MONITOR) {
  308. SetLastError(ERROR_INVALID_ACCESS);
  309. LeaveSplSem();
  310. return(FALSE);
  311. }
  312. //
  313. // This is the "Local Printer masquerading as the Network Printer"
  314. //
  315. hPort = pSpool->hPort;
  316. LeaveSplSem();
  317. bRet = ScheduleJob(hPort, JobId);
  318. return(bRet);
  319. }
  320. if ((pIniJob = FindJob(pSpool->pIniPrinter, JobId, &Position)) == NULL) {
  321. SetLastError(ERROR_INVALID_PARAMETER);
  322. LeaveSplSem();
  323. return(FALSE);
  324. }
  325. if (pIniJob->Status & JOB_SCHEDULE_JOB) {
  326. DBGMSG(DBG_WARNING, ("ScheduleJob: job 0x%x (id = %d) already scheduled\n",
  327. pIniJob, pIniJob->JobId));
  328. SetLastError(ERROR_INVALID_PARAMETER);
  329. LeaveSplSem();
  330. return FALSE;
  331. }
  332. if (!(pIniJob->Status & JOB_TYPE_ADDJOB)) {
  333. DBGMSG(DBG_WARNING, ("ScheduleJob: job 0x%x (id = %d) no addjob\n",
  334. pIniJob, pIniJob->JobId));
  335. SetLastError(ERROR_SPL_NO_ADDJOB);
  336. LeaveSplSem();
  337. return(FALSE);
  338. }
  339. pIniJob->Status |= JOB_SCHEDULE_JOB;
  340. if (pIniJob->Status & JOB_SPOOLING) {
  341. pIniJob->Status &= ~JOB_SPOOLING;
  342. pIniJob->pIniPrinter->cSpooling--;
  343. }
  344. if ( pIniJob->Status & JOB_TIMEOUT ) {
  345. pIniJob->Status &= ~( JOB_TIMEOUT | JOB_ABANDON );
  346. FreeSplStr(pIniJob->pStatus);
  347. pIniJob->pStatus = NULL;
  348. }
  349. SplInSem();
  350. // Despooling whilst spooling requires us to wake the writing
  351. // thread if it is waiting.
  352. if ( pIniJob->WaitForWrite != NULL )
  353. SetEvent(pIniJob->WaitForWrite);
  354. // Release any thread waiting on SeekPrinter for this job.
  355. SeekPrinterSetEvent(pIniJob, NULL, TRUE);
  356. SPLASSERT(pIniJob->cRef != 0);
  357. DECJOBREF(pIniJob);
  358. DBGMSG(DBG_TRACE, ("ScheduleJob:cRef = %d\n", pIniJob->cRef));
  359. //
  360. // FP Change
  361. // For File pools, we know the Filename of the spool file, so
  362. // we can just copy it in.
  363. //
  364. if ( pIniJob->pszSplFileName )
  365. {
  366. wcsncpy(szFileName, pIniJob->pszSplFileName, COUNTOF(szFileName));
  367. }
  368. else
  369. {
  370. GetFullNameFromId(pSpool->pIniPrinter, pIniJob->JobId, TRUE,
  371. szFileName, FALSE);
  372. }
  373. bRet = GetFileAttributesEx(szFileName,
  374. GetFileExInfoStandard,
  375. &FileAttributeData);
  376. //
  377. // According to MSDN: The ScheduleJob function checks for a valid spool file.
  378. // If there is an invalid spool file, or if it is empty, ScheduleJob deletes
  379. // both the spool file and the corresponding print job entry in the print spooler.
  380. //
  381. // The RDR/SRV will call AddJob even if the caller of CreateFile did noy request
  382. // WRITE access. This will cause us at add a job, but nobody will ever write to
  383. // the spooler file. In this case, we delete the job. For this reason we have
  384. // level 3 for AddJob. Level 3 is meant to be used only by RDR/SRV.
  385. //
  386. if (!bRet ||
  387. !(FileAttributeData.nFileSizeLow || FileAttributeData.nFileSizeHigh) && pIniJob->AddJobLevel == 3) {
  388. DBGMSG(DBG_WARNING, ("Could not GetFileAttributesEx %ws in ScheduleJob or file size is 0\n", szFileName));
  389. DeleteJob(pIniJob, BROADCAST);
  390. pSpool->pIniJob = NULL;
  391. pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
  392. LeaveSplSem();
  393. //
  394. // If we deleted the job because the spool file was empty and the job came via RDR/SRV
  395. // In this case we return success.
  396. //
  397. if (bRet)
  398. {
  399. return TRUE;
  400. }
  401. //
  402. // We delete the job because the spool is not found
  403. //
  404. SetLastError(ERROR_SPOOL_FILE_NOT_FOUND);
  405. return(FALSE);
  406. }
  407. //
  408. // If size changed, we must update our size
  409. // and potentially notify people.
  410. //
  411. if (pIniJob->Size != FileAttributeData.nFileSizeLow) {
  412. ADDNV(NotifyVector, NVSpoolJob);
  413. pIniJob->Size = FileAttributeData.nFileSizeLow;
  414. }
  415. WriteShadowJob(pIniJob, FALSE);
  416. if (pIniJob->Status & JOB_PENDING_DELETION) {
  417. DBGMSG(DBG_TRACE, ("LocalScheduleJob: Deleting Job because its pending deletion\n"));
  418. DeleteJob(pIniJob, BROADCAST);
  419. } else {
  420. CHECK_SCHEDULER();
  421. SetPrinterChange(pIniJob->pIniPrinter,
  422. pIniJob,
  423. NotifyVector,
  424. PRINTER_CHANGE_SET_JOB,
  425. pIniJob->pIniPrinter->pIniSpooler );
  426. }
  427. pSpool->pIniJob = NULL;
  428. pSpool->Status &= ~SPOOL_STATUS_ADDJOB;
  429. LeaveSplSem();
  430. SplOutSem();
  431. return(TRUE);
  432. }