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.

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