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.

2236 lines
63 KiB

  1. /***************************************************************************
  2. FILE spool.cpp
  3. MODULE Printers ISAPI DLL
  4. PURPOSE Spool Print Jobs
  5. DESCRIBED IN
  6. HISTORY 01/16/96 ccteng Stub
  7. 02/14/97 weihaic
  8. 11/11/97 sylvan IPP PrintJobRequest
  9. 11/20/97 chriswil Asynchronous read rewrite
  10. ****************************************************************************/
  11. #include "pch.h"
  12. #include "spool.h"
  13. #include "printers.h"
  14. #ifndef HSE_REQ_ASYNC_READ_CLIENT
  15. #define HSE_REQ_ASYNC_READ_CLIENT ((DWORD)1010)
  16. #endif
  17. PINIJOB pIniFirstJob = NULL;
  18. /*****************************************************************************
  19. * EnterSplSem
  20. * LeaveSplSem
  21. *
  22. *****************************************************************************/
  23. #define EnterSplSem() EnterCriticalSection(&SplCritSect)
  24. #define LeaveSplSem() LeaveCriticalSection(&SplCritSect)
  25. /*****************************************************************************
  26. * Spl_StrSize (Local Routine)
  27. *
  28. * Returns the size (in bytes) of the string (includes null-terminator).
  29. *
  30. *****************************************************************************/
  31. inline DWORD Spl_StrSize(
  32. LPCTSTR lpszStr)
  33. {
  34. return (lpszStr ? ((lstrlen(lpszStr) + 1) * sizeof(TCHAR)) : 0);
  35. }
  36. /*****************************************************************************
  37. * Spl_CallSSF (Local Routine)
  38. *
  39. * Calls the ISAPI ServerSupportFunction
  40. *
  41. *****************************************************************************/
  42. inline BOOL Spl_CallSSF(
  43. LPEXTENSION_CONTROL_BLOCK pECB,
  44. DWORD dwCmd,
  45. LPVOID lpvBuf,
  46. LPDWORD lpdwBuf,
  47. LPDWORD lpdwType)
  48. {
  49. return pECB->ServerSupportFunction(pECB->ConnID, dwCmd, lpvBuf, lpdwBuf, lpdwType);
  50. }
  51. /*****************************************************************************
  52. * Spl_SetAsyncCB (Local Routine)
  53. *
  54. * Calls the ISAPI ServerSupportFunction to set an asynchronous callback.
  55. *
  56. *****************************************************************************/
  57. inline BOOL Spl_SetAsyncCB(
  58. LPEXTENSION_CONTROL_BLOCK pECB,
  59. LPVOID pfnCallback,
  60. LPDWORD lpdwCtx)
  61. {
  62. return pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_IO_COMPLETION, pfnCallback, NULL, lpdwCtx);
  63. }
  64. /*****************************************************************************
  65. * Spl_ReadClient (Local Routine)
  66. *
  67. * Calls the ISAPI ServerSupportFunction to do an asynchronous read.
  68. *
  69. *****************************************************************************/
  70. inline BOOL Spl_ReadClient(
  71. LPEXTENSION_CONTROL_BLOCK pECB,
  72. LPVOID lpvBuf,
  73. DWORD cbBuf)
  74. {
  75. DWORD dwType = HSE_IO_ASYNC;
  76. return pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_ASYNC_READ_CLIENT, lpvBuf, &cbBuf, &dwType);
  77. }
  78. /*****************************************************************************
  79. * Spl_WriteClient (Local Routine)
  80. *
  81. * Calls the ISAPI WriteClient to do a write.
  82. *
  83. *****************************************************************************/
  84. inline BOOL Spl_WriteClient(
  85. LPEXTENSION_CONTROL_BLOCK pECB,
  86. LPVOID lpvBuf,
  87. DWORD cbBuf)
  88. {
  89. return pECB->WriteClient(pECB->ConnID, lpvBuf, &cbBuf, (DWORD)NULL);
  90. }
  91. /*****************************************************************************
  92. * Spl_EndSession (Local Routine)
  93. *
  94. * Calls the ISAPI ServerSupportFunction to end our session.
  95. *
  96. *****************************************************************************/
  97. inline BOOL Spl_EndSession(
  98. LPEXTENSION_CONTROL_BLOCK pECB)
  99. {
  100. DWORD dwStatus = HSE_STATUS_SUCCESS;
  101. return pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, &dwStatus, NULL, NULL);
  102. }
  103. /*****************************************************************************
  104. * Spl_WriteJob (Local Routine)
  105. *
  106. * Writes the byte-stream for a print-job.
  107. *
  108. *****************************************************************************/
  109. BOOL Spl_WriteJob(
  110. DWORD dwJobId,
  111. LPBYTE lpbData,
  112. DWORD cbBytes)
  113. {
  114. DWORD cbLeft;
  115. DWORD cbWritten;
  116. BOOL fRet = TRUE;
  117. for (cbLeft = cbBytes; (cbLeft > 0) && fRet; ) {
  118. if (fRet = WriteJob(dwJobId, lpbData, cbBytes, &cbWritten)) {
  119. lpbData += cbWritten;
  120. cbLeft -= cbWritten;
  121. } else {
  122. DBGMSG(DBG_WARN, ("Spl_WriteJob() call failed.\r\n"));
  123. break;
  124. }
  125. }
  126. return fRet;
  127. }
  128. /*****************************************************************************
  129. * Spl_AllocPrtUri (Local Routine)
  130. *
  131. * Returns a PrinterURI string.
  132. *
  133. *****************************************************************************/
  134. LPTSTR Spl_AllocPrtUri(
  135. LPCTSTR lpszShare,
  136. LPDWORD lpcbUri,
  137. BOOL bSecure)
  138. {
  139. DWORD cch;
  140. DWORD cbSize;
  141. LPTSTR lpszUri;
  142. // Get the size necessary to hold the printer-uri.
  143. //
  144. *lpcbUri = 0;
  145. cch = 0;
  146. GetWebpnpUrl(g_szHttpServerName, lpszShare, NULL, bSecure, NULL, &cch);
  147. if (cch && (lpszUri = (LPTSTR)AllocSplMem(sizeof(TCHAR) * cch))) {
  148. if (GetWebpnpUrl(g_szHttpServerName, lpszShare, NULL, bSecure, lpszUri, &cch)) {
  149. *lpcbUri = cch * sizeof(TCHAR);
  150. return lpszUri;
  151. }
  152. FreeSplMem(lpszUri, cch * sizeof(TCHAR));
  153. }
  154. return NULL;
  155. }
  156. /*****************************************************************************
  157. * Spl_AllocJobUri (Local Routine)
  158. *
  159. * Returns a JobURI string.
  160. *
  161. *****************************************************************************/
  162. LPTSTR Spl_AllocJobUri(
  163. LPCTSTR lpszShare,
  164. DWORD idJob,
  165. LPDWORD lpcbUri,
  166. BOOL bBase,
  167. BOOL bSecure)
  168. {
  169. LPTSTR lpszPrt;
  170. DWORD cbSize;
  171. DWORD cbPrt;
  172. DWORD cch;
  173. LPTSTR lpszUri = NULL;
  174. static CONST TCHAR s_szFmt1[] = TEXT("%s?IPP&JobId=%d");
  175. static CONST TCHAR s_szFmt2[] = TEXT("%s?IPP&JobId=");
  176. // Set our return-count to zero.
  177. //
  178. *lpcbUri = 0;
  179. // Get the printer-uri, and append a job-id to the end
  180. // as our job-uri.
  181. //
  182. cbPrt = 0;
  183. if (lpszPrt = Spl_AllocPrtUri(lpszShare, &cbPrt, bSecure)) {
  184. cbSize = cbPrt + sizeof(s_szFmt1) + 40;
  185. if (lpszUri = (LPTSTR)AllocSplMem(cbSize)) {
  186. if (bBase)
  187. StringCbPrintf(lpszUri, cbSize, s_szFmt2, lpszPrt);
  188. else
  189. StringCbPrintf(lpszUri, cbSize, s_szFmt1, lpszPrt, idJob);
  190. *lpcbUri = cbSize;
  191. }
  192. FreeSplMem(lpszPrt, cbPrt);
  193. }
  194. return lpszUri;
  195. }
  196. /*****************************************************************************
  197. * Spl_GetJI2 (Local Routine)
  198. *
  199. * Returns a JOB_INFO_2 struct.
  200. *
  201. *****************************************************************************/
  202. LPJOB_INFO_2 Spl_GetJI2(
  203. HANDLE hPrinter,
  204. DWORD idJob,
  205. LPDWORD lpcbSize)
  206. {
  207. DWORD cbSize;
  208. DWORD dwLE;
  209. LPJOB_INFO_2 pji2 = NULL;
  210. // Clear return-size.
  211. //
  212. *lpcbSize = 0;
  213. // Get the size necessary for the job.
  214. //
  215. cbSize = 0;
  216. GetJob(hPrinter, idJob, 2, NULL, 0, &cbSize);
  217. // Get the job-information.
  218. //
  219. if (cbSize && (pji2 = (LPJOB_INFO_2)AllocSplMem(cbSize))) {
  220. if (GetJob(hPrinter, idJob, 2, (LPBYTE)pji2, cbSize, &cbSize)) {
  221. *lpcbSize = cbSize;
  222. } else {
  223. dwLE = GetLastError();
  224. FreeSplMem(pji2, cbSize);
  225. pji2 = NULL;
  226. }
  227. } else {
  228. dwLE = GetLastError();
  229. }
  230. if (pji2 == NULL)
  231. SetLastError(dwLE);
  232. return pji2;
  233. }
  234. /*****************************************************************************
  235. * Spl_AllocAsync
  236. *
  237. * Allocate a spool-async-read structure. This is basically a structure
  238. * that we use to track where we are in the asynchronous read processing.
  239. *
  240. * Parameter/Field descriptions
  241. * ----------------------------
  242. * wReq - IPP Request identifier.
  243. *
  244. * hPrinter - handle to printer. We are in charge of closing this when
  245. * we're done processing the asynchronous reads.
  246. *
  247. * lpszShare - share-name of printer. This is necessary when we respond
  248. * back to the client when done processing the job.
  249. *
  250. * cbTotal - Total number of bytes to expect in job.
  251. *
  252. * cbRead - Current bytes read during async read.
  253. *
  254. * cbBuf - Size of read-buffer.
  255. *
  256. * lpbRet - Return-Buffer dependent upon IPP Request identifier.
  257. *
  258. *****************************************************************************/
  259. LPSPLASYNC Spl_AllocAsync(
  260. WORD wReq,
  261. HANDLE hPrinter,
  262. LPCTSTR lpszShare,
  263. DWORD cbTotal)
  264. {
  265. LPSPLASYNC pAsync;
  266. if (pAsync = (LPSPLASYNC)AllocSplMem(sizeof(SPLASYNC))) {
  267. if (pAsync->hIpp = WebIppRcvOpen(wReq)) {
  268. if (pAsync->lpbBuf = (LPBYTE)AllocSplMem(SPL_ASYNC_BUF)) {
  269. if (pAsync->lpszShare = AllocSplStr(lpszShare)) {
  270. pAsync->wReq = wReq;
  271. pAsync->hPrinter = hPrinter;
  272. pAsync->cbTotal = cbTotal;
  273. pAsync->cbRead = 0;
  274. pAsync->cbBuf = SPL_ASYNC_BUF;
  275. pAsync->lpbRet = NULL;
  276. return pAsync;
  277. }
  278. FreeSplMem(pAsync->lpbBuf, SPL_ASYNC_BUF);
  279. }
  280. WebIppRcvClose(pAsync->hIpp);
  281. }
  282. FreeSplMem(pAsync, sizeof(SPLASYNC));
  283. }
  284. DBGMSG(DBG_ERROR, ("Spl_AllocAsync() : Out of Memory\r\n"));
  285. SetLastError(ERROR_OUTOFMEMORY);
  286. return NULL;
  287. }
  288. /*****************************************************************************
  289. * Spl_FreeAsync
  290. *
  291. * Free our asynchronous read structure. This also closes our printer
  292. * handle that was setup prior to the beginning of the job.
  293. *
  294. *****************************************************************************/
  295. BOOL Spl_FreeAsync(
  296. LPSPLASYNC pAsync)
  297. {
  298. LPIPPRET_JOB pj;
  299. // Close the printer-handle. We do this here as oppose to in
  300. // (msw3prt.cxx), since if we had performed asynchronous reads
  301. // we would need to leave the scope the HttpExtensionProc() call.
  302. //
  303. // NOTE: CloseJob() closes the printer-handle. Only in the case
  304. // where we were not able to open a job should we close it
  305. // here.
  306. //
  307. pj = (LPIPPRET_JOB)pAsync->lpbRet;
  308. if ((pAsync->wReq == IPP_REQ_PRINTJOB) && pj && pj->bRet) {
  309. CloseJob((DWORD)pj->bRet);
  310. } else {
  311. ClosePrinter(pAsync->hPrinter);
  312. }
  313. // Free up our Ipp-handle, and all resources allocated.
  314. //
  315. if (pAsync->lpbBuf)
  316. FreeSplMem(pAsync->lpbBuf, pAsync->cbBuf);
  317. if (pAsync->lpszShare)
  318. FreeSplStr(pAsync->lpszShare);
  319. if (pAsync->lpbRet)
  320. WebIppFreeMem(pAsync->lpbRet);
  321. if (pAsync->hIpp)
  322. WebIppRcvClose(pAsync->hIpp);
  323. FreeSplMem(pAsync, sizeof(SPLASYNC));
  324. return TRUE;
  325. }
  326. /*****************************************************************************
  327. * Spl_OpenPrn (Local Routine)
  328. *
  329. * Opens a printer-handle with administrator rights.
  330. *
  331. *****************************************************************************/
  332. HANDLE Spl_OpenPrn(
  333. HANDLE hPrinter)
  334. {
  335. PPRINTER_INFO_1 ppi;
  336. PRINTER_DEFAULTS pa;
  337. DWORD cbSize;
  338. HANDLE hPrn = NULL;
  339. cbSize = 0;
  340. GetPrinter(hPrinter, 1, NULL, 0, &cbSize);
  341. if (cbSize && (ppi = (PPRINTER_INFO_1)AllocSplMem(cbSize))) {
  342. if (GetPrinter(hPrinter, 1, (LPBYTE)ppi, cbSize, &cbSize)) {
  343. // Since the OpenPrinter call in msw3prt.cxx has been
  344. // opened with the share-name, the (pName) field of this
  345. // call will already have the server-name prepended to the
  346. // friendly-name. We do not need to do any further work
  347. // on the friendly-name to accomodate clustering. If in the
  348. // future the OpenPrinter() specifies the friendly-name over
  349. // the share-name, then this routine will need to call
  350. // genFrnName() to convert the friendly to <server>\friendly.
  351. //
  352. ZeroMemory(&pa, sizeof(PRINTER_DEFAULTS));
  353. pa.DesiredAccess = PRINTER_ALL_ACCESS;
  354. OpenPrinter(ppi->pName, &hPrn, &pa);
  355. }
  356. FreeSplMem(ppi, cbSize);
  357. }
  358. return hPrn;
  359. }
  360. /*****************************************************************************
  361. ** Spl_AllocSplMemFn (Local Routine)
  362. **
  363. ** The WebIppPackJI2 call takes an allocator, however AllocSplMem is a #define
  364. ** if we are not using a debug library, so in this case, we have to create
  365. ** a small stub function ourselves.
  366. **
  367. *****************************************************************************/
  368. #ifdef DEBUG
  369. #define Spl_AllocSplMemFn AllocSplMem
  370. #else
  371. LPVOID Spl_AllocSplMemFn(DWORD cb) {
  372. return LocalAlloc(LPTR, cb);
  373. }
  374. #endif // #ifdef DEBUG
  375. /*****************************************************************************
  376. * Spl_CreateJobInfo2 (Local Routine)
  377. *
  378. * This creates a JobInfo2 structure from the various printer details that have
  379. * been passed in to us.
  380. *
  381. *****************************************************************************/
  382. LPJOB_INFO_2 Spl_CreateJobInfo2(
  383. IN PIPPREQ_PRTJOB ppj, // This provides some info that is useful for constructing
  384. // our own JOB_INFO_2 if necessary
  385. IN PINIJOB pInijob, // This is also used for constructing a JOB_INFO_2
  386. OUT LPDWORD lpcbSize
  387. ) {
  388. ASSERT(ppj); // This should never be NULL if this code path is reached
  389. ASSERT(lpcbSize);
  390. ASSERT(*lpcbSize == 0); // This should be passed in zero
  391. LPJOB_INFO_2 pji2 = NULL; // The packed and allocated JI2
  392. if (pInijob) { // This could conceivably be NULL
  393. DWORD cbNeeded = 0;
  394. GetPrinter( pInijob->hPrinter, 2, NULL, 0, &cbNeeded );
  395. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && cbNeeded) {
  396. LPPRINTER_INFO_2 ppi2; // The printer info to fill out
  397. DWORD cbNextNeeded;
  398. ppi2 = (LPPRINTER_INFO_2)AllocSplMem( cbNeeded );
  399. if (ppi2 && GetPrinter( pInijob->hPrinter, 2, (LPBYTE)ppi2, cbNeeded, &cbNextNeeded) ) {
  400. JOB_INFO_2 ji2; // The ji2 we will fill out, a properly packed one will
  401. // be returned
  402. ZeroMemory( &ji2, sizeof(ji2) );
  403. ji2.JobId = pInijob->JobId;
  404. ji2.pPrinterName = ppi2->pPrinterName;
  405. ji2.pMachineName = ppi2->pServerName;
  406. ji2.pUserName = ppj->pUserName;
  407. ji2.pNotifyName = ppj->pUserName;
  408. ji2.pDocument = ppj->pDocument;
  409. ji2.pDatatype = ppi2->pDatatype;
  410. ji2.pPrintProcessor = ppi2->pPrintProcessor;
  411. ji2.pParameters = ppi2->pParameters;
  412. ji2.pDriverName = ppi2->pDriverName;
  413. ji2.Status = pInijob->dwStatus;
  414. ji2.Priority = ppi2->Priority;
  415. ji2.StartTime = pInijob->dwStartTime;
  416. ji2.UntilTime = pInijob->dwStartTime;
  417. GetSystemTime (&ji2.Submitted);
  418. pji2 = WebIppPackJI2(&ji2, lpcbSize, Spl_AllocSplMemFn);
  419. }
  420. if (ppi2)
  421. FreeSplMem( ppi2, cbNeeded );
  422. }
  423. }
  424. return pji2;
  425. }
  426. /*****************************************************************************
  427. * Spl_ConfirmJob (Local Routine)
  428. *
  429. * This confirms that a Job has been printed if it returns a JOB_INFO_2 if
  430. * this is possible. GSNW printers can return an ERROR_PRINT_CANCELLED for the
  431. * first GetJob call and then fail on the second call. In this case we need to
  432. * build a shell JOB_INFO_2 with whatever data we can find and return it. We pass
  433. * this in to WebIppCreateJobRet which packs the correct strings and returns a
  434. * Legitimate JOB_INFO_2 structure.
  435. *
  436. *****************************************************************************/
  437. LPJOB_INFO_2 Spl_ConfirmJob(
  438. IN HANDLE hPrinter, // This is the printer handle we are using
  439. IN DWORD idJob, // This is the job id given to us by StartDocPrinter
  440. OUT LPDWORD lpcbSize, // This is the size of the allocated block
  441. IN PIPPREQ_PRTJOB ppj, // This provides some info that is useful for constructing
  442. // our own JOB_INFO_2 if necessary
  443. IN PINIJOB pInijob // This is also used for constructing a JOB_INFO_2
  444. )
  445. {
  446. DWORD cbSize;
  447. DWORD dwLE;
  448. LPJOB_INFO_2 pji2 = NULL;
  449. ASSERT(lpcbSize);
  450. // Clear return-size.
  451. //
  452. *lpcbSize = 0;
  453. // Get the size necessary for the job.
  454. //
  455. cbSize = 0;
  456. GetJob(hPrinter, idJob, 2, NULL, 0, &cbSize);
  457. dwLE = GetLastError();
  458. switch(dwLE) {
  459. case ERROR_INSUFFICIENT_BUFFER:
  460. // Get the job-information.
  461. //
  462. if (cbSize && (pji2 = (LPJOB_INFO_2)AllocSplMem(cbSize))) {
  463. if (GetJob(hPrinter, idJob, 2, (LPBYTE)pji2, cbSize, &cbSize)) {
  464. *lpcbSize = cbSize;
  465. } else {
  466. dwLE = GetLastError();
  467. FreeSplMem(pji2, cbSize);
  468. pji2 = NULL;
  469. }
  470. } else {
  471. dwLE = GetLastError();
  472. }
  473. break;
  474. case ERROR_PRINT_CANCELLED:
  475. // This is special-cased for GSNW masq printers where the job cannot
  476. // be retrieved from the Server, but we do not want to fail the EndDocPrinter
  477. // call
  478. if (pji2 = Spl_CreateJobInfo2( ppj, pInijob, lpcbSize) )
  479. SetLastError(dwLE = ERROR_SUCCESS);
  480. break;
  481. case ERROR_SUCCESS:
  482. dwLE = ERROR_INVALID_PARAMETER;
  483. break;
  484. }
  485. if (pji2 == NULL)
  486. SetLastError(dwLE);
  487. return pji2;
  488. }
  489. /*****************************************************************************
  490. * Spl_IppJobDataPrt (Local Routine)
  491. *
  492. * Handles the IPP_REQ_PRINTJOB request.
  493. *
  494. *****************************************************************************/
  495. BOOL Spl_IppJobDataPrt(
  496. LPEXTENSION_CONTROL_BLOCK pECB,
  497. HANDLE hPrinter,
  498. LPCTSTR lpszShare,
  499. LPBYTE lpbHdr,
  500. DWORD cbHdr,
  501. LPBYTE lpbDta,
  502. DWORD cbDta,
  503. LPBYTE* lplpbRet)
  504. {
  505. PIPPREQ_PRTJOB ppj;
  506. JOB_INFO_IPP ipp;
  507. DWORD cbUri;
  508. DWORD cbPrn;
  509. DWORD cbJI2;
  510. WORD wError;
  511. DWORD idJob = 0;
  512. LPJOB_INFO_2 pji2 = NULL;
  513. BOOL bRet = FALSE;
  514. if (ppj = (PIPPREQ_PRTJOB)lpbHdr) {
  515. // Initialize job-information.
  516. //
  517. ZeroMemory(&ipp, sizeof(JOB_INFO_IPP));
  518. // See if we're only to validate the job.
  519. //
  520. if (ppj->bValidate) {
  521. // NOTE: We'll return only a success for now until
  522. // we can build a table of validation criteria.
  523. //
  524. // 30-Jul-1998 : ChrisWil
  525. //
  526. wError = IPPRSP_SUCCESS;
  527. idJob = (DWORD)TRUE;
  528. } else {
  529. // Start the job.
  530. //
  531. PINIJOB pInijob;
  532. if (idJob = OpenJob(pECB, hPrinter, ppj, cbHdr, &pInijob)) {
  533. if (pji2 = Spl_ConfirmJob(hPrinter, idJob, &cbJI2, ppj, pInijob)) {
  534. wError = IPPRSP_SUCCESS;
  535. ipp.pJobUri = Spl_AllocJobUri(lpszShare, idJob, &cbUri, FALSE, IsSecureReq(pECB));
  536. ipp.pPrnUri = Spl_AllocPrtUri(lpszShare, &cbPrn, IsSecureReq(pECB));
  537. } else {
  538. wError = WebIppLeToRsp(GetLastError());
  539. }
  540. // Delete the pIniJob (if it has been allocated)
  541. if (pInijob)
  542. FreeSplMem( pInijob, sizeof(INIJOB) );
  543. } else {
  544. wError = WebIppLeToRsp(GetLastError());
  545. }
  546. }
  547. // Build the return structure.
  548. //
  549. *lplpbRet = (LPBYTE)WebIppCreateJobRet(wError,
  550. (BOOL)idJob,
  551. ppj->bValidate,
  552. pji2,
  553. &ipp);
  554. // Free allocated resources.
  555. //
  556. WebIppFreeMem(lpbHdr);
  557. if (pji2)
  558. FreeSplMem(pji2, cbJI2);
  559. if (ipp.pJobUri)
  560. FreeSplMem(ipp.pJobUri, cbUri);
  561. if (ipp.pPrnUri)
  562. FreeSplMem(ipp.pPrnUri, cbPrn);
  563. // If we failed to get a job-id, then we need to
  564. // return with no further processing.
  565. //
  566. if (idJob == 0)
  567. return FALSE;
  568. bRet = TRUE;
  569. } else {
  570. // If we had no header, then we are processing stream data
  571. // for the job. In this case we would have already been through
  572. // the code-path above where the job-id was set as our return
  573. // code.
  574. //
  575. if (*lplpbRet)
  576. idJob = (DWORD)((PIPPRET_JOB)*lplpbRet)->bRet;
  577. }
  578. // If we were able to get a data-stream, then we
  579. // need to process that in the write. If we are chunking
  580. // data and the lpbHdr is NULL, then the (lpdwJobId) is
  581. // passed in as input to this routine to be used in chunk
  582. // writes.
  583. //
  584. if (lpbDta)
  585. bRet = Spl_WriteJob(idJob, lpbDta, cbDta);
  586. return bRet;
  587. }
  588. /*****************************************************************************
  589. * Spl_IppJobDataSet (Local Routine)
  590. *
  591. * Handles the SetJob requests.
  592. *
  593. *****************************************************************************/
  594. BOOL Spl_IppJobDataSet(
  595. LPEXTENSION_CONTROL_BLOCK pECB,
  596. HANDLE hPrinter,
  597. LPBYTE lpbHdr,
  598. DWORD cbHdr,
  599. LPBYTE* lplpbRet)
  600. {
  601. PIPPREQ_SETJOB psj;
  602. JOB_INFO_2 ji2;
  603. WORD wError;
  604. BOOL bRet = FALSE;
  605. if (psj = (PIPPREQ_SETJOB)lpbHdr) {
  606. // Initialize job-information.
  607. //
  608. ZeroMemory(&ji2, sizeof(JOB_INFO_2));
  609. // Perform the SetJob command.
  610. //
  611. bRet = SetJob(hPrinter, psj->idJob, 0, NULL, psj->dwCmd);
  612. // Get LastError for return to the client.
  613. //
  614. wError = (bRet ? IPPRSP_SUCCESS : WebIppLeToRsp(GetLastError()));
  615. // Return the SetJobRet structure.
  616. //
  617. *lplpbRet = (LPBYTE)WebIppCreateJobRet(wError, bRet, FALSE, &ji2, NULL);
  618. // Free allocated resources.
  619. //
  620. WebIppFreeMem(lpbHdr);
  621. }
  622. return bRet;
  623. }
  624. /*****************************************************************************
  625. * Spl_IppJobDataAth (Local Routine)
  626. *
  627. * Handles the Authentication request.
  628. *
  629. *****************************************************************************/
  630. BOOL Spl_IppJobDataAth(
  631. LPEXTENSION_CONTROL_BLOCK pECB,
  632. LPBYTE lpbHdr,
  633. DWORD cbHdr,
  634. LPBYTE* lplpbRet)
  635. {
  636. PIPPREQ_AUTH pfa;
  637. WORD wError;
  638. BOOL bRet = FALSE;
  639. if (pfa = (PIPPREQ_AUTH)lpbHdr) {
  640. // Call authentication check.
  641. //
  642. bRet = !IsUserAnonymous(pECB);
  643. // Get LastError for return to the client.
  644. //
  645. wError = (bRet ? IPPRSP_SUCCESS : IPPRSP_ERROR_401);
  646. // Return the SetJobRet structure.
  647. //
  648. *lplpbRet = (LPBYTE)WebIppCreateAuthRet(wError, bRet);
  649. // Free allocated resources.
  650. //
  651. WebIppFreeMem(lpbHdr);
  652. }
  653. return bRet;
  654. }
  655. /*****************************************************************************
  656. * Spl_IppJobDataEnu (Local Routine)
  657. *
  658. * Handles the IPP_REQ_ENUJOB request. This returns a complete enumeration
  659. * of jobs. It is up to the client to determine which job they are
  660. * interested in (if they're only interested in one-job).
  661. *
  662. *****************************************************************************/
  663. BOOL Spl_IppJobDataEnu(
  664. LPEXTENSION_CONTROL_BLOCK pECB,
  665. HANDLE hPrinter,
  666. LPCTSTR lpszShare,
  667. LPBYTE lpbHdr,
  668. DWORD cbHdr,
  669. LPBYTE* lplpbRet)
  670. {
  671. LPIPPREQ_ENUJOB pgj;
  672. LPIPPJI2 lpIppJi2;
  673. LPTSTR lpszJobBase;
  674. LPJOB_INFO_2 lpji2;
  675. DWORD cbJobs;
  676. DWORD cJobs;
  677. DWORD cbNeed;
  678. DWORD cNeed;
  679. DWORD cbUri;
  680. WORD wError;
  681. BOOL bRet = FALSE;
  682. if (pgj = (LPIPPREQ_ENUJOB)lpbHdr) {
  683. // Initialize IPP return variables.
  684. //
  685. cbJobs = 0;
  686. cJobs = 0;
  687. lpji2 = NULL;
  688. // Get the size necessary to hold the enumerated jobs. We
  689. // will return JOB_INFO_2, since that has the most information.
  690. //
  691. cbNeed = 0;
  692. bRet = EnumJobs(hPrinter, 0, pgj->cJobs, 2, NULL, 0, &cbNeed, &cNeed);
  693. // If we have jobs to enumerate, then grab them.
  694. //
  695. if (cbNeed && (lpji2 = (LPJOB_INFO_2)AllocSplMem(cbNeed))) {
  696. bRet = EnumJobs(hPrinter,
  697. 0,
  698. pgj->cJobs,
  699. 2,
  700. (LPBYTE)lpji2,
  701. cbNeed,
  702. &cbJobs,
  703. &cJobs);
  704. DBGMSG(DBG_INFO,("Spl_IppJobDataEnu(): cJobs(%d), cbJobs(%d)\r\n", cJobs, cbJobs));
  705. }
  706. wError = (bRet ? IPPRSP_SUCCESS : WebIppLeToRsp(GetLastError()));
  707. // Convert the enumerated-jobs to an IPPJI2 structure. This
  708. // allows us to pass information that is not part of a JOB_INFO_2.
  709. //
  710. lpszJobBase = Spl_AllocJobUri(lpszShare, 0, &cbUri, TRUE, IsSecureReq (pECB));
  711. lpIppJi2 = WebIppCvtJI2toIPPJI2(lpszJobBase, &cbJobs, cJobs, lpji2);
  712. if (lpszJobBase)
  713. FreeSplMem(lpszJobBase, cbUri);
  714. // Return the EnuJobRet structure as an IPP stream.
  715. //
  716. *lplpbRet = (LPBYTE)WebIppCreateEnuJobRet(wError,
  717. bRet,
  718. cbJobs,
  719. cJobs,
  720. lpIppJi2);
  721. // Free allocated resources.
  722. //
  723. WebIppFreeMem(lpbHdr);
  724. if (lpIppJi2)
  725. WebIppFreeMem(lpIppJi2);
  726. if (lpji2)
  727. FreeSplMem(lpji2, cbNeed);
  728. }
  729. return bRet;
  730. }
  731. /*****************************************************************************
  732. * Spl_IppJobDataGet (Local Routine)
  733. *
  734. * Handles the IPP_REQ_GETJOB request. This returns the information for a single
  735. * job.
  736. *
  737. *****************************************************************************/
  738. BOOL Spl_IppJobDataGet(
  739. LPEXTENSION_CONTROL_BLOCK pECB,
  740. HANDLE hPrinter,
  741. LPCTSTR lpszShare,
  742. LPBYTE lpbHdr,
  743. DWORD cbHdr,
  744. LPBYTE* lplpbRet)
  745. {
  746. PIPPREQ_GETJOB pgj;
  747. LPJOB_INFO_2 pji2;
  748. JOB_INFO_IPP ipp;
  749. DWORD cbUri;
  750. DWORD cbPrn;
  751. WORD wError;
  752. DWORD cbJI2;
  753. BOOL bRet = FALSE;
  754. if (pgj = (PIPPREQ_GETJOB)lpbHdr) {
  755. // Initialize job-information.
  756. //
  757. ZeroMemory(&ipp, sizeof(JOB_INFO_IPP));
  758. if (pji2 = Spl_GetJI2(hPrinter, pgj->idJob, &cbJI2)) {
  759. wError = IPPRSP_SUCCESS;
  760. ipp.pJobUri = Spl_AllocJobUri(lpszShare, pgj->idJob, &cbUri, FALSE, IsSecureReq(pECB));
  761. ipp.pPrnUri = Spl_AllocPrtUri(lpszShare, &cbPrn, IsSecureReq(pECB));
  762. } else {
  763. wError = WebIppLeToRsp(GetLastError());
  764. }
  765. // Set the return value.
  766. //
  767. *lplpbRet = (LPBYTE)WebIppCreateJobRet(wError, bRet, FALSE, pji2, &ipp);
  768. // Free allocated resources.
  769. //
  770. WebIppFreeMem(lpbHdr);
  771. if (pji2)
  772. FreeSplMem(pji2, cbJI2);
  773. if (ipp.pJobUri)
  774. FreeSplMem(ipp.pJobUri, cbUri);
  775. if (ipp.pPrnUri)
  776. FreeSplMem(ipp.pPrnUri, cbPrn);
  777. }
  778. return bRet;
  779. }
  780. /*****************************************************************************
  781. * Spl_IppPrnDataGet (Local Routine)
  782. *
  783. * Handles the IPP_REQ_GETPRN request.
  784. *
  785. *****************************************************************************/
  786. BOOL Spl_IppPrnDataGet(
  787. LPEXTENSION_CONTROL_BLOCK pECB,
  788. HANDLE hPrinter,
  789. LPCTSTR lpszShare,
  790. LPBYTE lpbHdr,
  791. DWORD cbHdr,
  792. LPBYTE* lplpbRet)
  793. {
  794. LPIPPREQ_GETPRN pgp;
  795. LPPRINTER_INFO_2 lppi2;
  796. PRINTER_INFO_IPP ipp;
  797. DWORD cbSize;
  798. DWORD cbUri;
  799. WORD wError;
  800. BOOL bRet = FALSE;
  801. if (pgp = (LPIPPREQ_GETPRN)lpbHdr) {
  802. // Initialize the default information.
  803. //
  804. ZeroMemory(&ipp, sizeof(PRINTER_INFO_IPP));
  805. ipp.pPrnUri = Spl_AllocPrtUri(lpszShare, &cbUri, IsSecureReq(pECB));
  806. // Get PRINTER_INFO_2 information.
  807. //
  808. cbSize = 0;
  809. GetPrinter(hPrinter, 2, NULL, 0, &cbSize);
  810. if (lppi2 = (LPPRINTER_INFO_2)AllocSplMem(cbSize)) {
  811. bRet = GetPrinter(hPrinter, 2, (LPBYTE)lppi2, cbSize, &cbSize);
  812. if (!bRet) { // lppi2 might be full of garbage, so free it and pass NULL
  813. FreeSplMem( lppi2, cbSize );
  814. lppi2 = NULL;
  815. }
  816. }
  817. // Grab last-error if call failed.
  818. //
  819. wError = (bRet ? IPPRSP_SUCCESS : WebIppLeToRsp(GetLastError()));
  820. // Return the printer-structure.
  821. //
  822. *lplpbRet = (LPBYTE)WebIppCreatePrnRet(wError, bRet, lppi2, &ipp);
  823. // Free allocated resources.
  824. //
  825. if (lppi2)
  826. FreeSplMem(lppi2, cbSize);
  827. if (ipp.pPrnUri)
  828. FreeSplMem(ipp.pPrnUri, cbUri);
  829. WebIppFreeMem(lpbHdr);
  830. }
  831. return bRet;
  832. }
  833. /*****************************************************************************
  834. * Spl_IppPrnDataSet (Local Routine)
  835. *
  836. * Handles SetPrinter Requests.
  837. *
  838. *****************************************************************************/
  839. BOOL Spl_IppPrnDataSet(
  840. LPEXTENSION_CONTROL_BLOCK pECB,
  841. HANDLE hPrinter,
  842. LPBYTE lpbHdr,
  843. DWORD cbHdr,
  844. LPBYTE* lplpbRet)
  845. {
  846. PIPPREQ_SETPRN psp;
  847. PRINTER_INFO_2 pi2;
  848. HANDLE hPrn;
  849. WORD wError;
  850. BOOL bRet = FALSE;
  851. if (psp = (PIPPREQ_SETPRN)lpbHdr) {
  852. // Initialize default information.
  853. //
  854. ZeroMemory(&pi2, sizeof(PRINTER_INFO_2));
  855. // Open the printer with admin-priviledges to get
  856. // the printer information.
  857. //
  858. if (hPrn = Spl_OpenPrn(hPrinter)) {
  859. // Set the job for SetPrinter.
  860. //
  861. if ((bRet = SetPrinter(hPrn, 0, NULL, psp->dwCmd)) == FALSE)
  862. wError = WebIppLeToRsp(GetLastError());
  863. else
  864. wError = IPPRSP_SUCCESS;
  865. ClosePrinter(hPrn);
  866. } else {
  867. wError = WebIppLeToRsp(GetLastError());
  868. }
  869. // Return the printer-information structure.
  870. //
  871. *lplpbRet = (LPBYTE)WebIppCreatePrnRet(wError, bRet, &pi2, NULL);
  872. // Free allocated resources.
  873. //
  874. WebIppFreeMem(lpbHdr);
  875. }
  876. return bRet;
  877. }
  878. /*****************************************************************************
  879. * Spl_IppJobData (Local Routine)
  880. *
  881. * Processes ipp stream data. This returns a structure specific to the
  882. * type of request.
  883. *
  884. *****************************************************************************/
  885. BOOL Spl_IppJobData(
  886. LPEXTENSION_CONTROL_BLOCK pECB,
  887. WORD wReq,
  888. HANDLE hPrinter,
  889. LPCTSTR lpszShare,
  890. HANDLE hIpp,
  891. LPBYTE lpbBuf,
  892. DWORD cbBuf,
  893. LPBYTE* lplpbRet)
  894. {
  895. DWORD dwIpp;
  896. LPBYTE lpbHdr;
  897. DWORD cbHdr;
  898. LPBYTE lpbDta;
  899. DWORD cbDta;
  900. BOOL bRet = FALSE;
  901. // Convert the stream.
  902. //
  903. dwIpp = WebIppRcvData(hIpp, lpbBuf, cbBuf, &lpbHdr, &cbHdr, &lpbDta, &cbDta);
  904. // See how to process it.
  905. //
  906. switch (dwIpp) {
  907. case WEBIPP_OK:
  908. switch (wReq) {
  909. case IPP_REQ_FORCEAUTH:
  910. bRet = Spl_IppJobDataAth(pECB, lpbHdr, cbHdr, lplpbRet);
  911. break;
  912. case IPP_REQ_PRINTJOB:
  913. case IPP_REQ_VALIDATEJOB:
  914. bRet = Spl_IppJobDataPrt(pECB,
  915. hPrinter,
  916. lpszShare,
  917. lpbHdr,
  918. cbHdr,
  919. lpbDta,
  920. cbDta,
  921. lplpbRet);
  922. break;
  923. case IPP_REQ_CANCELJOB:
  924. case IPP_REQ_PAUSEJOB:
  925. case IPP_REQ_RESUMEJOB:
  926. case IPP_REQ_RESTARTJOB:
  927. bRet = Spl_IppJobDataSet(pECB, hPrinter, lpbHdr, cbHdr, lplpbRet);
  928. break;
  929. case IPP_REQ_ENUJOB:
  930. bRet = Spl_IppJobDataEnu(pECB, hPrinter, lpszShare, lpbHdr, cbHdr, lplpbRet);
  931. break;
  932. case IPP_REQ_GETJOB:
  933. bRet = Spl_IppJobDataGet(pECB, hPrinter, lpszShare, lpbHdr, cbHdr, lplpbRet);
  934. break;
  935. case IPP_REQ_GETPRN:
  936. bRet = Spl_IppPrnDataGet(pECB, hPrinter, lpszShare, lpbHdr, cbHdr, lplpbRet);
  937. break;
  938. case IPP_REQ_PAUSEPRN:
  939. case IPP_REQ_RESUMEPRN:
  940. case IPP_REQ_CANCELPRN:
  941. bRet = Spl_IppPrnDataSet(pECB, hPrinter, lpbHdr, cbHdr, lplpbRet);
  942. break;
  943. }
  944. break;
  945. case WEBIPP_MOREDATA:
  946. // More processing. Do nothing here.
  947. //
  948. *lplpbRet = NULL;
  949. bRet = TRUE;
  950. break;
  951. case WEBIPP_NOMEMORY:
  952. DBGMSG(DBG_WARN, ("Spl_IppJobData() failed (%d)\r\n", dwIpp));
  953. *lplpbRet = NULL;
  954. bRet = FALSE;
  955. break;
  956. case WEBIPP_BADHANDLE:
  957. *lplpbRet = (LPBYTE)WebIppCreateBadRet(IPPRSP_ERROR_500, FALSE);
  958. bRet = FALSE;
  959. break;
  960. default:
  961. case WEBIPP_FAIL:
  962. *lplpbRet = (LPBYTE)WebIppCreateBadRet(WebIppGetError(hIpp), FALSE);
  963. bRet = TRUE;
  964. break;
  965. }
  966. return bRet;
  967. }
  968. /*****************************************************************************
  969. * Spl_IppJobRsp
  970. *
  971. * Sends back a job-response in IPP format.
  972. *
  973. *****************************************************************************/
  974. BOOL Spl_IppJobRsp(
  975. LPEXTENSION_CONTROL_BLOCK pECB,
  976. WORD wReq,
  977. LPREQINFO lpri,
  978. LPBYTE lpbRet)
  979. {
  980. LPBYTE lpIpp;
  981. DWORD cbIpp;
  982. DWORD cbHdr;
  983. DWORD dwIpp;
  984. DWORD cch;
  985. LPCSTR lpszErr;
  986. CHAR szHdr[1024];
  987. BOOL bRet = FALSE;
  988. static CONST CHAR s_szErr400[] = "400 Failed Response";
  989. static CONST CHAR s_szErr401[] = "401 Authentication Required";
  990. static CONST CHAR s_szHtpHdr[] = "Content-Type: application/ipp\r\nContent-Length: %d\r\n\r\n";
  991. if (lpbRet) {
  992. // Convert to an IPP-Buffer from the return-buffer structure. For
  993. // failure cases, the last-error is initialized in the (lpbRet)
  994. // structure so that the appropriate stream can be generated.
  995. //
  996. dwIpp = WebIppSndData((IPP_RESPONSE | wReq),
  997. lpri,
  998. lpbRet,
  999. *((LPDWORD)lpbRet),
  1000. &lpIpp,
  1001. &cbIpp);
  1002. if (dwIpp == WEBIPP_OK) {
  1003. // If we had an access-denied, then we will need to include
  1004. // error 401. This will force the client to prompt for
  1005. // validation.
  1006. //
  1007. if (((LPIPPRET_ALL)lpbRet)->dwLastError == ERROR_ACCESS_DENIED)
  1008. lpszErr = s_szErr401;
  1009. else
  1010. lpszErr = NULL;
  1011. // Build header information.
  1012. //
  1013. StringCbPrintfA( szHdr, sizeof(szHdr), s_szHtpHdr, cbIpp);
  1014. cch = lstrlenA(szHdr);
  1015. // First we send a standard SEND_RESPONSE_HEADER w/our
  1016. // content-type ServerSupportFunction only handles szText,
  1017. // ANSI ??? see URL:
  1018. //
  1019. // http://www.microsoft.com/WIN32DEV/APIEXT/ISAPIREF.HTM
  1020. //
  1021. // see include httpfilt.h
  1022. //
  1023. Spl_CallSSF(pECB,
  1024. HSE_REQ_SEND_RESPONSE_HEADER,
  1025. (LPVOID)lpszErr,
  1026. (LPDWORD)&cch,
  1027. (LPDWORD)szHdr);
  1028. // For binary data we use WriteClient.
  1029. //
  1030. bRet = Spl_WriteClient(pECB, lpIpp, cbIpp);
  1031. WebIppFreeMem(lpIpp);
  1032. } else {
  1033. DBGMSG(DBG_WARN, ("Warn: WebIppSndData failed (%d)", dwIpp));
  1034. }
  1035. }
  1036. // Send an HTTP error header if we had big problems...
  1037. //
  1038. if (bRet == FALSE) {
  1039. cch = lstrlenA(s_szErr400);
  1040. Spl_CallSSF(pECB,
  1041. HSE_REQ_SEND_RESPONSE_HEADER,
  1042. (LPVOID)s_szErr400,
  1043. (LPDWORD)&cch,
  1044. (LPDWORD)NULL);
  1045. }
  1046. return bRet;
  1047. }
  1048. /*****************************************************************************
  1049. * Spl_IppJobAsyncCB
  1050. *
  1051. * Process the asynchronous reads. This is called by a random ISAPI thread.
  1052. *
  1053. *****************************************************************************/
  1054. VOID Spl_IppJobAsyncCB(
  1055. LPEXTENSION_CONTROL_BLOCK pECB,
  1056. PVOID pInfo,
  1057. DWORD cbIO,
  1058. DWORD dwError)
  1059. {
  1060. LPSPLASYNC pAsync;
  1061. REQINFO ri;
  1062. BOOL bRet;
  1063. if (pAsync = (LPSPLASYNC)pInfo) {
  1064. if ((dwError == 0) && cbIO) {
  1065. // Process the return from the IPP-Receive. This will
  1066. // process the bytes to the job.
  1067. //
  1068. bRet = Spl_IppJobData(pECB,
  1069. pAsync->wReq,
  1070. pAsync->hPrinter,
  1071. pAsync->lpszShare,
  1072. pAsync->hIpp,
  1073. pAsync->lpbBuf,
  1074. cbIO,
  1075. &pAsync->lpbRet);
  1076. // Read another chunk if we haven't read it all yet..
  1077. //
  1078. pAsync->cbRead += cbIO;
  1079. // If an error occured, or we reached the end of our reads,
  1080. // then we need to bail out of the asynchronous callback.
  1081. //
  1082. if ((bRet == FALSE) || (pAsync->cbRead >= pAsync->cbTotal)) {
  1083. goto SplCBDone;
  1084. }
  1085. // Read another chunk.
  1086. //
  1087. Spl_ReadClient(pECB, pAsync->lpbBuf, pAsync->cbBuf);
  1088. } else {
  1089. DBGMSG(DBG_WARN, ("Spl_IppJobAsyncCB() : Called with error or zero-bytes\r\n"));
  1090. bRet = (pAsync->cbRead >= pAsync->cbTotal);
  1091. SplCBDone:
  1092. // Send our response-header.
  1093. //
  1094. WebIppGetReqInfo(pAsync->hIpp, &ri);
  1095. Spl_IppJobRsp(pECB, pAsync->wReq, &ri, pAsync->lpbRet);
  1096. Spl_FreeAsync(pAsync);
  1097. Spl_EndSession(pECB);
  1098. }
  1099. } else {
  1100. DBGMSG(DBG_ERROR, ("Spl_IppJobAsyncCB() : No Context Value\r\n"));
  1101. Spl_EndSession(pECB);
  1102. }
  1103. }
  1104. /*****************************************************************************
  1105. * Spl_IppJobAsync
  1106. *
  1107. * This routine processes the job as an asynchronous read.
  1108. * It is only called once, the rest of the packets are handled by the async call back.
  1109. *
  1110. * How IIS's Async reads work:
  1111. * 1) ISAPI's HTTPExtensionProc gets called for the first chunk of data as usual.
  1112. * 2) In that call:
  1113. * - The ISAPI sets up a context, allocs a buffer and registers a call back for
  1114. * async reads.
  1115. * - Consumes the first chunk of the data.
  1116. * - Calls ServerSupportFunction( HSE_REQ_ASYNC_READ_CLIENT...) passing the buffer
  1117. * for IIS to write to. This call returns immediately with no data. When
  1118. * IIS has got more data from the client, it writes it to the ISAPI's buffer, then
  1119. * calls the call back passing the context handle that points to the buffer.
  1120. * 3) The call back consumes the data, then calls ServerSupportFunction(
  1121. * HSE_REQ_ASYNC_READ_CLIENT) again to repeat the cycle. IIS calls the call back
  1122. * once per ISAPI's call to ServerSupportFunction( HSE_REQ_ASYNC_READ_CLIENT ).
  1123. *
  1124. *****************************************************************************/
  1125. DWORD Spl_IppJobAsync(
  1126. LPEXTENSION_CONTROL_BLOCK pECB,
  1127. WORD wReq,
  1128. LPCTSTR lpszShare,
  1129. HANDLE hPrinter)
  1130. {
  1131. LPSPLASYNC pAsync;
  1132. REQINFO ri;
  1133. BOOL bRet = FALSE;
  1134. BOOL bSuccess = FALSE;
  1135. // Allocate our structure that contains our state
  1136. // info during asynchronous reads.
  1137. //
  1138. if (pAsync = Spl_AllocAsync(wReq, hPrinter, lpszShare, pECB->cbTotalBytes)) {
  1139. // Set our asynchronous callback. Specify our (pAsync) structure
  1140. // as the context which is passed to each callback.
  1141. //
  1142. if (Spl_SetAsyncCB(pECB, (LPVOID)Spl_IppJobAsyncCB, (LPDWORD)pAsync)) {
  1143. // Process our first buffer. Our first chunk will utilize
  1144. // what's already in the ECB-structure. For other chunks,
  1145. // we will specify our own buffer.
  1146. //
  1147. bSuccess = Spl_IppJobData(pECB,
  1148. wReq,
  1149. pAsync->hPrinter,
  1150. pAsync->lpszShare,
  1151. pAsync->hIpp,
  1152. pECB->lpbData,
  1153. pECB->cbAvailable,
  1154. &pAsync->lpbRet);
  1155. if (bSuccess) {
  1156. // Increment our read-count for the bytes we've
  1157. // just processed.
  1158. //
  1159. pAsync->cbRead += pECB->cbAvailable;
  1160. // Do our first asynchronous read. Return if all is
  1161. // successful.
  1162. //
  1163. if (Spl_ReadClient(pECB, pAsync->lpbBuf, pAsync->cbBuf))
  1164. return HSE_STATUS_PENDING;
  1165. }
  1166. WebIppGetReqInfo(pAsync->hIpp, &ri);
  1167. Spl_IppJobRsp(pECB, wReq, &ri, pAsync->lpbRet);
  1168. Spl_EndSession(pECB);
  1169. bRet = TRUE; // We must return HSE_STATUS_PENDING if we call
  1170. // HSE_REQ_DONE_WITH_SESSION
  1171. }
  1172. // Free our async-structure. This indirectly frees the
  1173. // return buffer as well.
  1174. //
  1175. Spl_FreeAsync(pAsync);
  1176. } else {
  1177. ClosePrinter(hPrinter);
  1178. }
  1179. return (bRet ? HSE_STATUS_PENDING : HSE_STATUS_ERROR);
  1180. }
  1181. /*****************************************************************************
  1182. * Spl_IppJobSync
  1183. *
  1184. * This routine processes the job as a synchronous-read. This implies that
  1185. * our entire job made it in one-post, and thus doesn't need to perform
  1186. * any reads.
  1187. *
  1188. *****************************************************************************/
  1189. DWORD Spl_IppJobSync(
  1190. LPEXTENSION_CONTROL_BLOCK pECB,
  1191. WORD wReq,
  1192. LPCTSTR lpszShare,
  1193. HANDLE hPrinter)
  1194. {
  1195. HANDLE hIpp;
  1196. LPIPPRET_JOB pj;
  1197. REQINFO ri;
  1198. LPBYTE lpbRet = NULL;
  1199. BOOL bRet = FALSE;
  1200. // Initialize request structure.
  1201. //
  1202. ZeroMemory(&ri, sizeof(REQINFO));
  1203. ri.idReq = 0;
  1204. ri.cpReq = CP_UTF8;
  1205. ri.pwlUns = NULL;
  1206. ri.bFidelity = FALSE;
  1207. ri.fReq[0] = IPP_REQALL;
  1208. ri.fReq[1] = IPP_REQALL;
  1209. // Open an IPP-Receive channel and call the routine to process
  1210. // the job.
  1211. //
  1212. if (hIpp = WebIppRcvOpen(wReq)) {
  1213. bRet = Spl_IppJobData(pECB,
  1214. wReq,
  1215. hPrinter,
  1216. lpszShare,
  1217. hIpp,
  1218. pECB->lpbData,
  1219. pECB->cbAvailable,
  1220. &lpbRet);
  1221. WebIppGetReqInfo(hIpp, &ri);
  1222. }
  1223. // Send the job-response back to the client. If we weren't able
  1224. // to open an IPP-Receive handle, or our job-processing failed,
  1225. // then our error is FALSE.
  1226. //
  1227. bRet = Spl_IppJobRsp(pECB, wReq, &ri, lpbRet);
  1228. // Free up the receive-handle only after the response. We need to
  1229. // insure the integrity of the unsupported-list-handle.
  1230. //
  1231. if (hIpp)
  1232. WebIppRcvClose(hIpp);
  1233. // Close the printer-handle. We do this here as oppose to in
  1234. // (msw3prt.cxx), since if we had performed asynchronous reads
  1235. // we would need to leave the scope the HttpExtensionProc() call.
  1236. //
  1237. // NOTE: CloseJob() closes the printer-handle. Only in the case
  1238. // where we were not able to open a job should we close it
  1239. // here.
  1240. //
  1241. pj = (LPIPPRET_JOB)lpbRet;
  1242. if((wReq == IPP_REQ_PRINTJOB) && pj && pj->bRet) {
  1243. CloseJob((DWORD)pj->bRet);
  1244. } else {
  1245. ClosePrinter(hPrinter);
  1246. }
  1247. // Free our return-structure.
  1248. //
  1249. if (lpbRet)
  1250. WebIppFreeMem(lpbRet);
  1251. return (bRet ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR);
  1252. }
  1253. /*****************************************************************************
  1254. * SplIppJob
  1255. *
  1256. * Process the IPP Job Request.
  1257. *
  1258. * Get the print-job. If we can't handle the entire post within this
  1259. * scope, then we setup for asynchronous reads.
  1260. *
  1261. *****************************************************************************/
  1262. DWORD SplIppJob(
  1263. WORD wReq,
  1264. PALLINFO pAllInfo,
  1265. PPRINTERPAGEINFO pPageInfo)
  1266. {
  1267. DWORD dwRet;
  1268. // If our bytes aren't contained in one-chunk, then
  1269. // we need to start an asynchronous read.
  1270. //
  1271. // Otherwise, if our available amounts to the total-bytes
  1272. // of the job, then we can process the entire command sychronousely.
  1273. //
  1274. if (pAllInfo->pECB->cbAvailable < pAllInfo->pECB->cbTotalBytes) {
  1275. dwRet = Spl_IppJobAsync(pAllInfo->pECB,
  1276. wReq,
  1277. pPageInfo->pPrinterInfo->pShareName,
  1278. pPageInfo->hPrinter);
  1279. } else {
  1280. dwRet = Spl_IppJobSync(pAllInfo->pECB,
  1281. wReq,
  1282. pPageInfo->pPrinterInfo->pShareName,
  1283. pPageInfo->hPrinter);
  1284. }
  1285. return dwRet;
  1286. }
  1287. /*****************************************************************************
  1288. * OpenJob
  1289. *
  1290. * Starts a job. This creates a new spool-job-entry, the returns a jobid.
  1291. *
  1292. *
  1293. *****************************************************************************/
  1294. DWORD OpenJob(
  1295. IN LPEXTENSION_CONTROL_BLOCK pECB,
  1296. IN HANDLE hPrinter,
  1297. IN PIPPREQ_PRTJOB pipr,
  1298. IN DWORD dwSize,
  1299. OUT PINIJOB *ppCopyIniJob)
  1300. {
  1301. PINIJOB pIniJob;
  1302. DWORD JobId = 0;
  1303. LS_HANDLE hLicense;
  1304. if ((NULL == hPrinter) || (NULL == pipr) || (dwSize < sizeof(IPPREQ_PRTJOB)))
  1305. return 0;
  1306. if( RequestLicense( &hLicense, pECB )) { // Enforce the Client Access Licensing
  1307. if (pIniJob = (PINIJOB)AllocSplMem(sizeof(INIJOB))) {
  1308. DWORD dwNeeded;
  1309. DOC_INFO_1 di = {0, 0, 0};
  1310. ZeroMemory( pIniJob, sizeof(INIJOB) ); // This ensures that unset fields are NULL
  1311. pIniJob->signature = IJ_SIGNATURE;
  1312. pIniJob->cb = sizeof(INIJOB);
  1313. pIniJob->hLicense = hLicense;
  1314. di.pDocName = pipr->pDocument;
  1315. if (JobId = StartDocPrinter(hPrinter, 1, (LPBYTE)&di)) {
  1316. // we successfully add a job to spooler
  1317. //
  1318. pIniJob->JobId = JobId;
  1319. // keep the hPrinter until CloseJob
  1320. //
  1321. pIniJob->hPrinter = hPrinter;
  1322. pIniJob->dwStartTime = GetCurrentMinute();
  1323. pIniJob->dwStatus = JOB_READY;
  1324. pIniJob->pECB = pECB;
  1325. if (ppCopyIniJob)
  1326. // Allocate and copy the new ppIniJob structure out, some of the elements
  1327. // will be null
  1328. if (*ppCopyIniJob = (PINIJOB)AllocSplMem( sizeof(INIJOB) ) )
  1329. CopyMemory( *ppCopyIniJob, pIniJob, sizeof(INIJOB) );
  1330. AddJobEntry(pIniJob);
  1331. } else {
  1332. // StartDocPrinter Failed
  1333. //
  1334. DBGMSG(DBG_WARN, ("StartDocPrinter Failed %d\n", GetLastError()));
  1335. FreeSplMem(pIniJob, pIniJob->cb);
  1336. FreeLicense( hLicense );
  1337. }
  1338. }
  1339. else // if alloc failed
  1340. FreeLicense( hLicense );
  1341. } else { // if failed to update a license
  1342. // Spl_IppJobRsp() will check for this and send down
  1343. // proper error to the client.
  1344. //
  1345. SetLastError( ERROR_LICENSE_QUOTA_EXCEEDED );
  1346. }
  1347. #ifdef DEBUG
  1348. if (JobId)
  1349. DBGMSG(DBG_INFO,("OpenJob : succeed, JobID == %d\r\n", JobId));
  1350. else
  1351. DBGMSG(DBG_WARN,("OpenJob : failed!\r\n"));
  1352. #endif
  1353. // what is this for in the failure case ???
  1354. //
  1355. // AuthenticateUser(pAllInfo);
  1356. //
  1357. return JobId;
  1358. }
  1359. /*****************************************************************************
  1360. * WriteJob
  1361. *
  1362. * Write the job.
  1363. *
  1364. *****************************************************************************/
  1365. BOOL WriteJob(
  1366. DWORD JobId,
  1367. LPBYTE pBuf,
  1368. DWORD dwSize,
  1369. LPDWORD pWritten)
  1370. {
  1371. PINIJOB pIniJob;
  1372. BOOL bRet = FALSE;
  1373. if (pIniJob = FindJob(JobId, JOB_BUSY))
  1374. {
  1375. bRet = WritePrinter(pIniJob->hPrinter, pBuf, dwSize, pWritten);
  1376. pIniJob->dwStatus = JOB_READY;
  1377. return bRet;
  1378. }
  1379. return FALSE;
  1380. }
  1381. /*****************************************************************************
  1382. * CloseJob
  1383. *
  1384. * Close job and remove from the list.
  1385. *
  1386. *****************************************************************************/
  1387. BOOL CloseJob(
  1388. DWORD JobId)
  1389. {
  1390. PINIJOB pIniJob;
  1391. BOOL ret = FALSE;
  1392. if (pIniJob = FindJob(JobId, JOB_BUSY)) {
  1393. ret = EndDocPrinter(pIniJob->hPrinter);
  1394. ClosePrinter(pIniJob->hPrinter);
  1395. DeleteJobEntry(pIniJob);
  1396. // CleanupOldJob needs to do the same to take care of orphan Async jobs.
  1397. FreeLicense( pIniJob->hLicense );
  1398. FreeSplMem(pIniJob, pIniJob->cb);
  1399. }
  1400. return ret;
  1401. }
  1402. /*****************************************************************************
  1403. * DeleteJob
  1404. *
  1405. * TBD : Unimplemented.
  1406. *
  1407. *****************************************************************************/
  1408. BOOL DeleteJob(
  1409. DWORD JobId)
  1410. {
  1411. return TRUE;
  1412. }
  1413. /*****************************************************************************
  1414. * AddJobEntryToLinkList
  1415. *
  1416. * Add an entry from a double linked list
  1417. *
  1418. *****************************************************************************/
  1419. VOID AddJobEntryToLinkList(
  1420. PINIJOB &pIniFirstJob,
  1421. PINIJOB pIniJob)
  1422. {
  1423. PINIJOB pIniJobTmp;
  1424. pIniJob->pNext = NULL;
  1425. pIniJob->pPrevious = NULL;
  1426. if (!(pIniJobTmp = pIniFirstJob))
  1427. {
  1428. pIniFirstJob = pIniJob;
  1429. }
  1430. else
  1431. {
  1432. // add pIniJob to the end of the list
  1433. for (; pIniJobTmp->pNext; pIniJobTmp = pIniJobTmp->pNext)
  1434. ;
  1435. pIniJob->pPrevious = pIniJobTmp;
  1436. pIniJobTmp->pNext = pIniJob;
  1437. }
  1438. }
  1439. /*****************************************************************************
  1440. * DeleteJobEntryFromLinkList
  1441. *
  1442. * Delete an entry from a double linked list
  1443. *
  1444. *****************************************************************************/
  1445. VOID DeleteJobEntryFromLinkList(
  1446. PINIJOB &pIniFirstJob,
  1447. PINIJOB pIniJob)
  1448. {
  1449. if (pIniJob->pPrevious)
  1450. pIniJob->pPrevious->pNext = pIniJob->pNext;
  1451. else
  1452. // pIniJob must be the first job
  1453. pIniFirstJob = pIniJob->pNext;
  1454. if (pIniJob->pNext)
  1455. pIniJob->pNext->pPrevious = pIniJob->pPrevious;
  1456. }
  1457. /*****************************************************************************
  1458. * AddJobEntry
  1459. *
  1460. * I just use a simple double linked list for now. Can be changed to
  1461. * something else such as a hash table later, if desired.
  1462. *
  1463. *****************************************************************************/
  1464. VOID AddJobEntry(
  1465. PINIJOB pIniJob)
  1466. {
  1467. EnterSplSem();
  1468. AddJobEntryToLinkList(pIniFirstJob, pIniJob);
  1469. LeaveSplSem();
  1470. }
  1471. /*****************************************************************************
  1472. * DeleteJobEntry
  1473. *
  1474. * Delete job from the job-list.
  1475. *
  1476. *****************************************************************************/
  1477. VOID DeleteJobEntry(
  1478. PINIJOB pIniJob)
  1479. {
  1480. EnterSplSem();
  1481. DeleteJobEntryFromLinkList (pIniFirstJob, pIniJob);
  1482. LeaveSplSem();
  1483. }
  1484. /*****************************************************************************
  1485. *
  1486. * FindJob
  1487. *
  1488. * Looks for job in the job-list and dwStatus
  1489. *
  1490. *****************************************************************************/
  1491. PINIJOB FindJob(
  1492. DWORD JobId,
  1493. DWORD dwStatus)
  1494. {
  1495. PINIJOB pIniJob;
  1496. EnterSplSem();
  1497. // pIniJob will end up being NULL if a match is not found
  1498. for (pIniJob = pIniFirstJob; pIniJob; pIniJob = pIniJob->pNext)
  1499. {
  1500. if (pIniJob->dwStatus == JOB_READY && pIniJob->JobId == JobId) {
  1501. // found the match, break and return pIniJob
  1502. // Set the status
  1503. pIniJob->dwStatus = dwStatus;
  1504. break;
  1505. }
  1506. }
  1507. LeaveSplSem();
  1508. return pIniJob;
  1509. }
  1510. /*****************************************************************************
  1511. * CleanupOldJob
  1512. *
  1513. * This function is called by Sleeper->Work() about every 15 minutes to cleanup
  1514. * the pending unclosed jobs due to the failure of the network or any other
  1515. * possible reasons.
  1516. *
  1517. *****************************************************************************/
  1518. BOOL CleanupOldJob()
  1519. {
  1520. DWORD dwCurrentTime = GetCurrentMinute();
  1521. PINIJOB pIniJob;
  1522. PINIJOB pIniTmpJob;
  1523. PINIJOB pIniFirstOldJob = NULL;
  1524. if (!pIniFirstJob) return TRUE;
  1525. DBGMSG (DBG_WARN, ("Enter Cleanup...\r\n"));
  1526. EnterSplSem();
  1527. for (pIniJob = pIniFirstJob; pIniJob; pIniJob = pIniTmpJob) {
  1528. pIniTmpJob = pIniJob->pNext;
  1529. if (pIniJob->dwStatus == JOB_READY) {
  1530. DWORD dwDiff = (1440 + dwCurrentTime - pIniJob->dwStartTime) % 1440;
  1531. if (dwDiff > MAX_JOB_MINUTE) {
  1532. DBGMSG (DBG_WARN, ("OldJob found %x\r\n", pIniJob->hPrinter));
  1533. DeleteJobEntry (pIniJob);
  1534. AddJobEntryToLinkList (pIniFirstOldJob, pIniJob);
  1535. }
  1536. }
  1537. }
  1538. LeaveSplSem();
  1539. DWORD dwStatus = HTTP_STATUS_REQUEST_TIMEOUT;
  1540. // Delete the job outside the critical section
  1541. for (pIniJob = pIniFirstOldJob; pIniJob; pIniJob = pIniTmpJob) {
  1542. pIniTmpJob = pIniJob->pNext;
  1543. EndDocPrinter(pIniJob->hPrinter);
  1544. ClosePrinter(pIniJob->hPrinter);
  1545. FreeLicense( pIniJob->hLicense ); // CleanupOldJob needs to do the same to take care of orphan Async jobs.
  1546. #ifdef ASYNC_READ_ENABLED
  1547. // Disable it because we're not trying to manage the cleanup for
  1548. // http sessions. If there is a session pending because we close
  1549. // the job, the callback function (Spl_JobPrintCB)
  1550. // will close the session itself.
  1551. //
  1552. pIniJob->pECB->ServerSupportFunction(pIniJob->pECB->ConnID,
  1553. HSE_REQ_DONE_WITH_SESSION,
  1554. &dwStatus,
  1555. NULL,
  1556. NULL);
  1557. #endif
  1558. FreeSplMem(pIniJob, pIniJob->cb);
  1559. }
  1560. return TRUE;
  1561. }
  1562. /*****************************************************************************
  1563. * GetCurrentMinute
  1564. *
  1565. * Get the current minute since midnight
  1566. *
  1567. *****************************************************************************/
  1568. DWORD GetCurrentMinute ()
  1569. {
  1570. SYSTEMTIME CurTime;
  1571. GetSystemTime (&CurTime);
  1572. return CurTime.wHour * 60 + CurTime.wMinute;
  1573. }
  1574. #ifdef DEBUG
  1575. DWORD dwSplHeapSize = 0;
  1576. /*****************************************************************************
  1577. * AllocSplMem (Helper)
  1578. *
  1579. * Routine Description:
  1580. *
  1581. * This function will allocate local memory. It will possibly allocate extra
  1582. * memory and fill this with debugging information for the debugging version.
  1583. *
  1584. * Arguments:
  1585. *
  1586. * cb - The amount of memory to allocate
  1587. *
  1588. * Return Value:
  1589. *
  1590. * NON-NULL - A pointer to the allocated memory
  1591. *
  1592. * FALSE/NULL - The operation failed. Extended error status is available
  1593. * using GetLastError.
  1594. *
  1595. *
  1596. *****************************************************************************/
  1597. LPVOID AllocSplMem(
  1598. DWORD cb)
  1599. {
  1600. PDWORD pMem;
  1601. DWORD cbNew;
  1602. cbNew = cb+2*sizeof(DWORD);
  1603. if (cbNew & 3)
  1604. cbNew += sizeof(DWORD) - (cbNew & 3);
  1605. pMem=(PDWORD)LocalAlloc(LPTR, cbNew);
  1606. if (!pMem)
  1607. {
  1608. DBGMSG(DBG_ERROR, ("Memory Allocation failed for %d bytes\n", cbNew));
  1609. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1610. return 0;
  1611. }
  1612. *pMem=cb;
  1613. *(PDWORD)((PBYTE)pMem+cbNew-sizeof(DWORD))=0xdeadbeef;
  1614. dwSplHeapSize += cbNew;
  1615. return (LPVOID)(pMem+1);
  1616. }
  1617. /*****************************************************************************
  1618. * FreeSplMem (Helper)
  1619. *
  1620. *
  1621. *****************************************************************************/
  1622. BOOL FreeSplMem(
  1623. LPVOID pMem,
  1624. DWORD cb)
  1625. {
  1626. DWORD cbNew;
  1627. LPDWORD pNewMem;
  1628. if (!pMem)
  1629. return FALSE;
  1630. pNewMem = (LPDWORD)pMem;
  1631. pNewMem--;
  1632. cbNew = cb+2*sizeof(DWORD);
  1633. if (cbNew & 3)
  1634. cbNew += sizeof(DWORD) - (cbNew & 3);
  1635. if (*pNewMem != cb) {
  1636. DBGMSG(DBG_ERROR, ("Corrupt Memory Size in inetsrv-spool : %0lx %0lx != %0lx\n",
  1637. pNewMem, *pNewMem, cb));
  1638. return FALSE;
  1639. }
  1640. if (*(LPDWORD)((LPBYTE)pNewMem + cbNew - sizeof(DWORD)) != 0xdeadbeef) {
  1641. DBGMSG(DBG_ERROR, ("Memory Overrun in inetsrv-spool : %0lx\n", pNewMem));
  1642. return FALSE;
  1643. }
  1644. LocalFree((LPVOID)pNewMem);
  1645. dwSplHeapSize -= cbNew;
  1646. return TRUE;
  1647. }
  1648. #endif // DEBUG
  1649. /*****************************************************************************
  1650. * AllocSplStr (Helper)
  1651. *
  1652. * Routine Description:
  1653. *
  1654. * This function will allocate enough local memory to store the specified
  1655. * string, and copy that string to the allocated memory
  1656. *
  1657. * Arguments:
  1658. *
  1659. * pStr - Pointer to the string that needs to be allocated and stored
  1660. *
  1661. * Return Value:
  1662. *
  1663. * NON-NULL - A pointer to the allocated memory containing the string
  1664. *
  1665. * FALSE/NULL - The operation failed. Extended error status is available
  1666. * using GetLastError.
  1667. *****************************************************************************/
  1668. LPTSTR AllocSplStr(
  1669. LPCTSTR lpszStr)
  1670. {
  1671. DWORD cbSize;
  1672. LPTSTR lpszCpy = NULL;
  1673. if (cbSize = Spl_StrSize(lpszStr)) {
  1674. if (lpszCpy = (LPTSTR)AllocSplMem(cbSize))
  1675. StringCbCopy(lpszCpy, cbSize, lpszStr);
  1676. }
  1677. return lpszCpy;
  1678. }
  1679. /*****************************************************************************
  1680. * FreeSplStr (Helper)
  1681. *
  1682. *
  1683. *****************************************************************************/
  1684. #ifdef DEBUG
  1685. #define FREE_PTR_TO_LONG(X) (X)
  1686. #else
  1687. #define FREE_PTR_TO_LONG(X) (PtrToLong(X))
  1688. #endif
  1689. BOOL FreeSplStr(
  1690. LPTSTR lpszStr)
  1691. {
  1692. DWORD cbSize;
  1693. cbSize = Spl_StrSize(lpszStr);
  1694. return (BOOL)(lpszStr ? FREE_PTR_TO_LONG(FreeSplMem(lpszStr, cbSize)) : FALSE);
  1695. }
  1696. /*****************************************************************************
  1697. * AuthenticateUser (Helper)
  1698. *
  1699. *
  1700. *****************************************************************************/
  1701. BOOL AuthenticateUser(
  1702. PALLINFO pAllInfo)
  1703. {
  1704. // If we don't specify a header (szAuthHdr), and just submit a 401 error, IIS
  1705. // would include (in the automatically generated header) what authenticaitons it is setup
  1706. // to use (NTLM and/or Basic). So the client can pick the first one on the list and use it
  1707. // (this is what IE does).
  1708. //
  1709. // Note: for NTLM to work, you need adirect socket connection. So it won't work across
  1710. // firewalls (IIS admins are supposed to know this). Secure socket seems to do it though,
  1711. // so for the new MS Proxy, it might be doable.
  1712. //
  1713. return Spl_CallSSF(pAllInfo->pECB,
  1714. HSE_REQ_SEND_RESPONSE_HEADER,
  1715. (LPVOID)"401 Authentication Required",
  1716. (LPDWORD)NULL,
  1717. (LPDWORD)NULL);
  1718. }