2290 lines
63 KiB
C++
2290 lines
63 KiB
C++
/***************************************************************************
|
|
FILE spool.cpp
|
|
|
|
MODULE Printers ISAPI DLL
|
|
|
|
PURPOSE Spool Print Jobs
|
|
|
|
DESCRIBED IN
|
|
|
|
HISTORY 01/16/96 ccteng Stub
|
|
02/14/97 weihaic
|
|
11/11/97 sylvan IPP PrintJobRequest
|
|
11/20/97 chriswil Asynchronous read rewrite
|
|
****************************************************************************/
|
|
|
|
#include "pch.h"
|
|
#include "spool.h"
|
|
#include "printers.h"
|
|
|
|
|
|
#ifndef HSE_REQ_ASYNC_READ_CLIENT
|
|
#define HSE_REQ_ASYNC_READ_CLIENT ((DWORD)1010)
|
|
#endif
|
|
|
|
PINIJOB pIniFirstJob = NULL;
|
|
|
|
/*****************************************************************************
|
|
* EnterSplSem
|
|
* LeaveSplSem
|
|
*
|
|
*****************************************************************************/
|
|
#define EnterSplSem() EnterCriticalSection(&SplCritSect)
|
|
#define LeaveSplSem() LeaveCriticalSection(&SplCritSect)
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_StrSize (Local Routine)
|
|
*
|
|
* Returns the size (in bytes) of the string (includes null-terminator).
|
|
*
|
|
*****************************************************************************/
|
|
inline DWORD Spl_StrSize(
|
|
LPCTSTR lpszStr)
|
|
{
|
|
return (lpszStr ? ((lstrlen(lpszStr) + 1) * sizeof(TCHAR)) : 0);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_CallSSF (Local Routine)
|
|
*
|
|
* Calls the ISAPI ServerSupportFunction
|
|
*
|
|
*****************************************************************************/
|
|
inline BOOL Spl_CallSSF(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
DWORD dwCmd,
|
|
LPVOID lpvBuf,
|
|
LPDWORD lpdwBuf,
|
|
LPDWORD lpdwType)
|
|
{
|
|
return pECB->ServerSupportFunction(pECB->ConnID, dwCmd, lpvBuf, lpdwBuf, lpdwType);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_SetAsyncCB (Local Routine)
|
|
*
|
|
* Calls the ISAPI ServerSupportFunction to set an asynchronous callback.
|
|
*
|
|
*****************************************************************************/
|
|
inline BOOL Spl_SetAsyncCB(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
LPVOID pfnCallback,
|
|
LPDWORD lpdwCtx)
|
|
{
|
|
return pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_IO_COMPLETION, pfnCallback, NULL, lpdwCtx);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_ReadClient (Local Routine)
|
|
*
|
|
* Calls the ISAPI ServerSupportFunction to do an asynchronous read.
|
|
*
|
|
*****************************************************************************/
|
|
inline BOOL Spl_ReadClient(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
LPVOID lpvBuf,
|
|
DWORD cbBuf)
|
|
{
|
|
DWORD dwType = HSE_IO_ASYNC;
|
|
|
|
return pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_ASYNC_READ_CLIENT, lpvBuf, &cbBuf, &dwType);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_WriteClient (Local Routine)
|
|
*
|
|
* Calls the ISAPI WriteClient to do a write.
|
|
*
|
|
*****************************************************************************/
|
|
inline BOOL Spl_WriteClient(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
LPVOID lpvBuf,
|
|
DWORD cbBuf)
|
|
{
|
|
return pECB->WriteClient(pECB->ConnID, lpvBuf, &cbBuf, (DWORD)NULL);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_EndSession (Local Routine)
|
|
*
|
|
* Calls the ISAPI ServerSupportFunction to end our session.
|
|
*
|
|
*****************************************************************************/
|
|
inline BOOL Spl_EndSession(
|
|
LPEXTENSION_CONTROL_BLOCK pECB)
|
|
{
|
|
DWORD dwStatus = HSE_STATUS_SUCCESS;
|
|
return pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, &dwStatus, NULL, NULL);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_WriteJob (Local Routine)
|
|
*
|
|
* Writes the byte-stream for a print-job.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_WriteJob(
|
|
DWORD dwJobId,
|
|
LPBYTE lpbData,
|
|
DWORD cbBytes)
|
|
{
|
|
DWORD cbLeft;
|
|
DWORD cbWritten;
|
|
BOOL fRet = TRUE;
|
|
|
|
|
|
for (cbLeft = cbBytes; (cbLeft > 0) && fRet; ) {
|
|
|
|
if (fRet = WriteJob(dwJobId, lpbData, cbBytes, &cbWritten)) {
|
|
|
|
lpbData += cbWritten;
|
|
cbLeft -= cbWritten;
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARN, ("Spl_WriteJob() call failed.\r\n"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_AllocPrtUri (Local Routine)
|
|
*
|
|
* Returns a PrinterURI string.
|
|
*
|
|
*****************************************************************************/
|
|
LPTSTR Spl_AllocPrtUri(
|
|
LPCTSTR lpszShare,
|
|
LPDWORD lpcbUri,
|
|
BOOL bSecure)
|
|
{
|
|
|
|
DWORD cch;
|
|
DWORD cbSize;
|
|
LPTSTR lpszUri;
|
|
|
|
|
|
// Get the size necessary to hold the printer-uri.
|
|
//
|
|
*lpcbUri = 0;
|
|
cch = 0;
|
|
|
|
GetWebpnpUrl(g_szHttpServerName, lpszShare, NULL, bSecure, NULL, &cch);
|
|
|
|
if (cch && (lpszUri = (LPTSTR)AllocSplMem(sizeof(TCHAR) * cch))) {
|
|
|
|
if (GetWebpnpUrl(g_szHttpServerName, lpszShare, NULL, bSecure, lpszUri, &cch)) {
|
|
|
|
*lpcbUri = cch * sizeof(TCHAR);
|
|
|
|
return lpszUri;
|
|
}
|
|
|
|
FreeSplMem(lpszUri, cch * sizeof(TCHAR));
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_AllocJobUri (Local Routine)
|
|
*
|
|
* Returns a JobURI string.
|
|
*
|
|
*****************************************************************************/
|
|
LPTSTR Spl_AllocJobUri(
|
|
LPCTSTR lpszShare,
|
|
DWORD idJob,
|
|
LPDWORD lpcbUri,
|
|
BOOL bBase,
|
|
BOOL bSecure)
|
|
{
|
|
LPTSTR lpszPrt;
|
|
DWORD cbSize;
|
|
DWORD cbPrt;
|
|
DWORD cch;
|
|
LPTSTR lpszUri = NULL;
|
|
|
|
static CONST TCHAR s_szFmt1[] = TEXT("%s?IPP&JobId=%d");
|
|
static CONST TCHAR s_szFmt2[] = TEXT("%s?IPP&JobId=");
|
|
|
|
|
|
// Set our return-count to zero.
|
|
//
|
|
*lpcbUri = 0;
|
|
|
|
|
|
// Get the printer-uri, and append a job-id to the end
|
|
// as our job-uri.
|
|
//
|
|
cbPrt = 0;
|
|
if (lpszPrt = Spl_AllocPrtUri(lpszShare, &cbPrt, bSecure)) {
|
|
|
|
cbSize = cbPrt + sizeof(s_szFmt1) + 40;
|
|
|
|
if (lpszUri = (LPTSTR)AllocSplMem(cbSize)) {
|
|
|
|
if (bBase)
|
|
cch = wsprintf(lpszUri, s_szFmt2, lpszPrt);
|
|
else
|
|
cch = wsprintf(lpszUri, s_szFmt1, lpszPrt, idJob);
|
|
|
|
// *lpcbUri = (cch * sizeof(TCHAR)); This is the number of bytes written, not
|
|
// the amount of memory allocated
|
|
*lpcbUri = cbSize;
|
|
}
|
|
|
|
FreeSplMem(lpszPrt, cbPrt);
|
|
}
|
|
|
|
return lpszUri;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_GetJI2 (Local Routine)
|
|
*
|
|
* Returns a JOB_INFO_2 struct.
|
|
*
|
|
*****************************************************************************/
|
|
LPJOB_INFO_2 Spl_GetJI2(
|
|
HANDLE hPrinter,
|
|
DWORD idJob,
|
|
LPDWORD lpcbSize)
|
|
{
|
|
DWORD cbSize;
|
|
DWORD dwLE;
|
|
LPJOB_INFO_2 pji2 = NULL;
|
|
|
|
|
|
// Clear return-size.
|
|
//
|
|
*lpcbSize = 0;
|
|
|
|
|
|
// Get the size necessary for the job.
|
|
//
|
|
cbSize = 0;
|
|
GetJob(hPrinter, idJob, 2, NULL, 0, &cbSize);
|
|
|
|
|
|
// Get the job-information.
|
|
//
|
|
if (cbSize && (pji2 = (LPJOB_INFO_2)AllocSplMem(cbSize))) {
|
|
|
|
if (GetJob(hPrinter, idJob, 2, (LPBYTE)pji2, cbSize, &cbSize)) {
|
|
|
|
*lpcbSize = cbSize;
|
|
|
|
} else {
|
|
|
|
dwLE = GetLastError();
|
|
|
|
FreeSplMem(pji2, cbSize);
|
|
pji2 = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
dwLE = GetLastError();
|
|
}
|
|
|
|
|
|
if (pji2 == NULL)
|
|
SetLastError(dwLE);
|
|
|
|
return pji2;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_AllocAsync
|
|
*
|
|
* Allocate a spool-async-read structure. This is basically a structure
|
|
* that we use to track where we are in the asynchronous read processing.
|
|
*
|
|
* Parameter/Field descriptions
|
|
* ----------------------------
|
|
* wReq - IPP Request identifier.
|
|
*
|
|
* hPrinter - handle to printer. We are in charge of closing this when
|
|
* we're done processing the asynchronous reads.
|
|
*
|
|
* lpszShare - share-name of printer. This is necessary when we respond
|
|
* back to the client when done processing the job.
|
|
*
|
|
* cbTotal - Total number of bytes to expect in job.
|
|
*
|
|
* cbRead - Current bytes read during async read.
|
|
*
|
|
* cbBuf - Size of read-buffer.
|
|
*
|
|
* lpbRet - Return-Buffer dependent upon IPP Request identifier.
|
|
*
|
|
*****************************************************************************/
|
|
LPSPLASYNC Spl_AllocAsync(
|
|
WORD wReq,
|
|
HANDLE hPrinter,
|
|
LPCTSTR lpszShare,
|
|
DWORD cbTotal)
|
|
{
|
|
LPSPLASYNC pAsync;
|
|
|
|
|
|
if (pAsync = (LPSPLASYNC)AllocSplMem(sizeof(SPLASYNC))) {
|
|
|
|
if (pAsync->hIpp = WebIppRcvOpen(wReq)) {
|
|
|
|
if (pAsync->lpbBuf = (LPBYTE)AllocSplMem(SPL_ASYNC_BUF)) {
|
|
|
|
if (pAsync->lpszShare = AllocSplStr(lpszShare)) {
|
|
|
|
pAsync->wReq = wReq;
|
|
pAsync->hPrinter = hPrinter;
|
|
pAsync->cbTotal = cbTotal;
|
|
pAsync->cbRead = 0;
|
|
pAsync->cbBuf = SPL_ASYNC_BUF;
|
|
pAsync->lpbRet = NULL;
|
|
|
|
return pAsync;
|
|
}
|
|
|
|
FreeSplMem(pAsync->lpbBuf, SPL_ASYNC_BUF);
|
|
}
|
|
|
|
WebIppRcvClose(pAsync->hIpp);
|
|
}
|
|
|
|
FreeSplMem(pAsync, sizeof(SPLASYNC));
|
|
}
|
|
|
|
DBGMSG(DBG_ERROR, ("Spl_AllocAsync() : Out of Memory\r\n"));
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_FreeAsync
|
|
*
|
|
* Free our asynchronous read structure. This also closes our printer
|
|
* handle that was setup prior to the beginning of the job.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_FreeAsync(
|
|
LPSPLASYNC pAsync)
|
|
{
|
|
LPIPPRET_JOB pj;
|
|
|
|
|
|
// Close the printer-handle. We do this here as oppose to in
|
|
// (msw3prt.cxx), since if we had performed asynchronous reads
|
|
// we would need to leave the scope the HttpExtensionProc() call.
|
|
//
|
|
// NOTE: CloseJob() closes the printer-handle. Only in the case
|
|
// where we were not able to open a job should we close it
|
|
// here.
|
|
//
|
|
pj = (LPIPPRET_JOB)pAsync->lpbRet;
|
|
|
|
if ((pAsync->wReq == IPP_REQ_PRINTJOB) && pj && pj->bRet) {
|
|
|
|
CloseJob((DWORD)pj->bRet);
|
|
|
|
} else {
|
|
|
|
ClosePrinter(pAsync->hPrinter);
|
|
}
|
|
|
|
|
|
// Free up our Ipp-handle, and all resources allocated.
|
|
//
|
|
if (pAsync->lpbBuf)
|
|
FreeSplMem(pAsync->lpbBuf, pAsync->cbBuf);
|
|
|
|
if (pAsync->lpszShare)
|
|
FreeSplStr(pAsync->lpszShare);
|
|
|
|
if (pAsync->lpbRet)
|
|
WebIppFreeMem(pAsync->lpbRet);
|
|
|
|
if (pAsync->hIpp)
|
|
WebIppRcvClose(pAsync->hIpp);
|
|
|
|
FreeSplMem(pAsync, sizeof(SPLASYNC));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_OpenPrn (Local Routine)
|
|
*
|
|
* Opens a printer-handle with administrator rights.
|
|
*
|
|
*****************************************************************************/
|
|
HANDLE Spl_OpenPrn(
|
|
HANDLE hPrinter)
|
|
{
|
|
PPRINTER_INFO_1 ppi;
|
|
PRINTER_DEFAULTS pa;
|
|
DWORD cbSize;
|
|
HANDLE hPrn = NULL;
|
|
|
|
|
|
cbSize = 0;
|
|
GetPrinter(hPrinter, 1, NULL, 0, &cbSize);
|
|
|
|
if (cbSize && (ppi = (PPRINTER_INFO_1)AllocSplMem(cbSize))) {
|
|
|
|
if (GetPrinter(hPrinter, 1, (LPBYTE)ppi, cbSize, &cbSize)) {
|
|
|
|
// Since the OpenPrinter call in msw3prt.cxx has been
|
|
// opened with the share-name, the (pName) field of this
|
|
// call will already have the server-name prepended to the
|
|
// friendly-name. We do not need to do any further work
|
|
// on the friendly-name to accomodate clustering. If in the
|
|
// future the OpenPrinter() specifies the friendly-name over
|
|
// the share-name, then this routine will need to call
|
|
// genFrnName() to convert the friendly to <server>\friendly.
|
|
//
|
|
ZeroMemory(&pa, sizeof(PRINTER_DEFAULTS));
|
|
pa.DesiredAccess = PRINTER_ALL_ACCESS;
|
|
|
|
OpenPrinter(ppi->pName, &hPrn, &pa);
|
|
}
|
|
|
|
FreeSplMem(ppi, cbSize);
|
|
}
|
|
|
|
return hPrn;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
** Spl_AllocSplMemFn (Local Routine)
|
|
**
|
|
** The WebIppPackJI2 call takes an allocator, however AllocSplMem is a #define
|
|
** if we are not using a debug library, so in this case, we have to create
|
|
** a small stub function ourselves.
|
|
**
|
|
*****************************************************************************/
|
|
#ifdef DEBUG
|
|
#define Spl_AllocSplMemFn AllocSplMem
|
|
#else
|
|
LPVOID Spl_AllocSplMemFn(DWORD cb) {
|
|
return LocalAlloc(LPTR, cb);
|
|
}
|
|
#endif // #ifdef DEBUG
|
|
|
|
/*****************************************************************************
|
|
* Spl_CreateJobInfo2 (Local Routine)
|
|
*
|
|
* This creates a JobInfo2 structure from the various printer details that have
|
|
* been passed in to us.
|
|
*
|
|
*****************************************************************************/
|
|
LPJOB_INFO_2 Spl_CreateJobInfo2(
|
|
IN PIPPREQ_PRTJOB ppj, // This provides some info that is useful for constructing
|
|
// our own JOB_INFO_2 if necessary
|
|
IN PINIJOB pInijob, // This is also used for constructing a JOB_INFO_2
|
|
OUT LPDWORD lpcbSize
|
|
) {
|
|
ASSERT(ppj); // This should never be NULL if this code path is reached
|
|
ASSERT(lpcbSize);
|
|
ASSERT(*lpcbSize == 0); // This should be passed in zero
|
|
|
|
|
|
LPJOB_INFO_2 pji2 = NULL; // The packed and allocated JI2
|
|
|
|
if (pInijob) { // This could conceivably be NULL
|
|
DWORD cbNeeded = 0;
|
|
|
|
GetPrinter( pInijob->hPrinter, 2, NULL, 0, &cbNeeded );
|
|
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && cbNeeded) {
|
|
LPPRINTER_INFO_2 ppi2; // The printer info to fill out
|
|
DWORD cbNextNeeded;
|
|
|
|
ppi2 = (LPPRINTER_INFO_2)AllocSplMem( cbNeeded );
|
|
|
|
if (ppi2 && GetPrinter( pInijob->hPrinter, 2, (LPBYTE)ppi2, cbNeeded, &cbNextNeeded) ) {
|
|
JOB_INFO_2 ji2; // The ji2 we will fill out, a properly packed one will
|
|
// be returned
|
|
ZeroMemory( &ji2, sizeof(ji2) );
|
|
|
|
ji2.JobId = pInijob->JobId;
|
|
ji2.pPrinterName = ppi2->pPrinterName;
|
|
ji2.pMachineName = ppi2->pServerName;
|
|
ji2.pUserName = ppj->pUserName;
|
|
ji2.pNotifyName = ppj->pUserName;
|
|
ji2.pDocument = ppj->pDocument;
|
|
ji2.pDatatype = ppi2->pDatatype;
|
|
ji2.pPrintProcessor = ppi2->pPrintProcessor;
|
|
ji2.pParameters = ppi2->pParameters;
|
|
ji2.pDriverName = ppi2->pDriverName;
|
|
ji2.Status = pInijob->dwStatus;
|
|
ji2.Priority = ppi2->Priority;
|
|
ji2.StartTime = pInijob->dwStartTime;
|
|
ji2.UntilTime = pInijob->dwStartTime;
|
|
GetSystemTime (&ji2.Submitted);
|
|
|
|
pji2 = WebIppPackJI2(&ji2, lpcbSize, Spl_AllocSplMemFn);
|
|
}
|
|
|
|
if (ppi2)
|
|
FreeSplMem( ppi2, cbNeeded );
|
|
|
|
}
|
|
}
|
|
|
|
return pji2;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_ConfirmJob (Local Routine)
|
|
*
|
|
* This confirms that a Job has been printed if it returns a JOB_INFO_2 if
|
|
* this is possible. GSNW printers can return an ERROR_PRINT_CANCELLED for the
|
|
* first GetJob call and then fail on the second call. In this case we need to
|
|
* build a shell JOB_INFO_2 with whatever data we can find and return it. We pass
|
|
* this in to Web FILL IN HERE which packs the correct strings and returns a
|
|
* Legitimate JOB_INFO_2 structure.
|
|
*
|
|
*****************************************************************************/
|
|
LPJOB_INFO_2 Spl_ConfirmJob(
|
|
IN HANDLE hPrinter, // This is the printer handle we are using
|
|
IN DWORD idJob, // This is the job id given to us by StartDocPrinter
|
|
OUT LPDWORD lpcbSize, // This is the size of the allocated block
|
|
IN PIPPREQ_PRTJOB ppj, // This provides some info that is useful for constructing
|
|
// our own JOB_INFO_2 if necessary
|
|
IN PINIJOB pInijob // This is also used for constructing a JOB_INFO_2
|
|
)
|
|
{
|
|
DWORD cbSize;
|
|
DWORD dwLE;
|
|
LPJOB_INFO_2 pji2 = NULL;
|
|
|
|
|
|
ASSERT(lpcbSize);
|
|
// Clear return-size.
|
|
//
|
|
*lpcbSize = 0;
|
|
|
|
// Get the size necessary for the job.
|
|
//
|
|
cbSize = 0;
|
|
GetJob(hPrinter, idJob, 2, NULL, 0, &cbSize);
|
|
|
|
dwLE = GetLastError();
|
|
|
|
switch(dwLE) {
|
|
case ERROR_INSUFFICIENT_BUFFER:
|
|
// Get the job-information.
|
|
//
|
|
if (cbSize && (pji2 = (LPJOB_INFO_2)AllocSplMem(cbSize))) {
|
|
|
|
if (GetJob(hPrinter, idJob, 2, (LPBYTE)pji2, cbSize, &cbSize)) {
|
|
|
|
*lpcbSize = cbSize;
|
|
|
|
} else {
|
|
|
|
dwLE = GetLastError();
|
|
|
|
FreeSplMem(pji2, cbSize);
|
|
pji2 = NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
dwLE = GetLastError();
|
|
}
|
|
|
|
break;
|
|
|
|
case ERROR_PRINT_CANCELLED:
|
|
// This is special-cased for GSNW masq printers where the job cannot
|
|
// be retrieved from the Server, but we do not want to fail the EndDocPrinter
|
|
// call
|
|
|
|
if (pji2 = Spl_CreateJobInfo2( ppj, pInijob, lpcbSize) )
|
|
SetLastError(dwLE = ERROR_SUCCESS);
|
|
|
|
break;
|
|
|
|
case ERROR_SUCCESS:
|
|
// What the?
|
|
|
|
dwLE = ERROR_INVALID_PARAMETER;
|
|
|
|
break;
|
|
}
|
|
|
|
if (pji2 == NULL)
|
|
SetLastError(dwLE);
|
|
|
|
return pji2;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobDataPrt (Local Routine)
|
|
*
|
|
* Handles the IPP_REQ_PRINTJOB request.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppJobDataPrt(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
HANDLE hPrinter,
|
|
LPCTSTR lpszShare,
|
|
LPBYTE lpbHdr,
|
|
DWORD cbHdr,
|
|
LPBYTE lpbDta,
|
|
DWORD cbDta,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
PIPPREQ_PRTJOB ppj;
|
|
JOB_INFO_IPP ipp;
|
|
DWORD cbUri;
|
|
DWORD cbPrn;
|
|
DWORD cbJI2;
|
|
WORD wError;
|
|
DWORD idJob = 0;
|
|
LPJOB_INFO_2 pji2 = NULL;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if (ppj = (PIPPREQ_PRTJOB)lpbHdr) {
|
|
|
|
// Initialize job-information.
|
|
//
|
|
ZeroMemory(&ipp, sizeof(JOB_INFO_IPP));
|
|
|
|
|
|
// See if we're only to validate the job.
|
|
//
|
|
if (ppj->bValidate) {
|
|
|
|
// NOTE: We'll return only a success for now until
|
|
// we can build a table of validation criteria.
|
|
//
|
|
// 30-Jul-1998 : ChrisWil
|
|
//
|
|
wError = IPPRSP_SUCCESS;
|
|
idJob = (DWORD)TRUE;
|
|
|
|
} else {
|
|
|
|
// Start the job.
|
|
//
|
|
PINIJOB pInijob;
|
|
|
|
if (idJob = OpenJob(pECB, hPrinter, ppj, cbHdr, &pInijob)) {
|
|
|
|
if (pji2 = Spl_ConfirmJob(hPrinter, idJob, &cbJI2, ppj, pInijob)) {
|
|
|
|
wError = IPPRSP_SUCCESS;
|
|
ipp.pJobUri = Spl_AllocJobUri(lpszShare, idJob, &cbUri, FALSE, IsSecureReq(pECB));
|
|
ipp.pPrnUri = Spl_AllocPrtUri(lpszShare, &cbPrn, IsSecureReq(pECB));
|
|
|
|
} else {
|
|
|
|
wError = WebIppLeToRsp(GetLastError());
|
|
}
|
|
|
|
// Delete the pIniJob (if it has been allocated)
|
|
|
|
if (pInijob)
|
|
FreeSplMem( pInijob, sizeof(INIJOB) );
|
|
} else {
|
|
|
|
wError = WebIppLeToRsp(GetLastError());
|
|
}
|
|
}
|
|
|
|
|
|
// Build the return structure.
|
|
//
|
|
*lplpbRet = (LPBYTE)WebIppCreateJobRet(wError,
|
|
(BOOL)idJob,
|
|
ppj->bValidate,
|
|
pji2,
|
|
&ipp);
|
|
|
|
// Free allocated resources.
|
|
//
|
|
WebIppFreeMem(lpbHdr);
|
|
|
|
if (pji2)
|
|
FreeSplMem(pji2, cbJI2);
|
|
|
|
if (ipp.pJobUri)
|
|
FreeSplMem(ipp.pJobUri, cbUri);
|
|
|
|
if (ipp.pPrnUri)
|
|
FreeSplMem(ipp.pPrnUri, cbPrn);
|
|
|
|
|
|
// If we failed to get a job-id, then we need to
|
|
// return with no further processing.
|
|
//
|
|
if (idJob == 0)
|
|
return FALSE;
|
|
|
|
bRet = TRUE;
|
|
|
|
} else {
|
|
|
|
// If we had no header, then we are processing stream data
|
|
// for the job. In this case we would have already been through
|
|
// the code-path above where the job-id was set as our return
|
|
// code.
|
|
//
|
|
if (*lplpbRet)
|
|
idJob = (DWORD)((PIPPRET_JOB)*lplpbRet)->bRet;
|
|
}
|
|
|
|
|
|
// If we were able to get a data-stream, then we
|
|
// need to process that in the write. If we are chunking
|
|
// data and the lpbHdr is NULL, then the (lpdwJobId) is
|
|
// passed in as input to this routine to be used in chunk
|
|
// writes.
|
|
//
|
|
if (lpbDta)
|
|
bRet = Spl_WriteJob(idJob, lpbDta, cbDta);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobDataSet (Local Routine)
|
|
*
|
|
* Handles the SetJob requests.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppJobDataSet(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
HANDLE hPrinter,
|
|
LPBYTE lpbHdr,
|
|
DWORD cbHdr,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
PIPPREQ_SETJOB psj;
|
|
JOB_INFO_2 ji2;
|
|
WORD wError;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if (psj = (PIPPREQ_SETJOB)lpbHdr) {
|
|
|
|
// Initialize job-information.
|
|
//
|
|
ZeroMemory(&ji2, sizeof(JOB_INFO_2));
|
|
|
|
|
|
// Perform the SetJob command.
|
|
//
|
|
bRet = SetJob(hPrinter, psj->idJob, 0, NULL, psj->dwCmd);
|
|
|
|
|
|
// Get LastError for return to the client.
|
|
//
|
|
wError = (bRet ? IPPRSP_SUCCESS : WebIppLeToRsp(GetLastError()));
|
|
|
|
|
|
// Return the SetJobRet structure.
|
|
//
|
|
*lplpbRet = (LPBYTE)WebIppCreateJobRet(wError, bRet, FALSE, &ji2, NULL);
|
|
|
|
|
|
// Free allocated resources.
|
|
//
|
|
WebIppFreeMem(lpbHdr);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobDataAth (Local Routine)
|
|
*
|
|
* Handles the Authentication request.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppJobDataAth(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
LPBYTE lpbHdr,
|
|
DWORD cbHdr,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
PIPPREQ_AUTH pfa;
|
|
WORD wError;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if (pfa = (PIPPREQ_AUTH)lpbHdr) {
|
|
|
|
// Call authentication check.
|
|
//
|
|
bRet = !IsUserAnonymous(pECB);
|
|
|
|
|
|
// Get LastError for return to the client.
|
|
//
|
|
wError = (bRet ? IPPRSP_SUCCESS : IPPRSP_ERROR_401);
|
|
|
|
|
|
// Return the SetJobRet structure.
|
|
//
|
|
*lplpbRet = (LPBYTE)WebIppCreateAuthRet(wError, bRet);
|
|
|
|
|
|
// Free allocated resources.
|
|
//
|
|
WebIppFreeMem(lpbHdr);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobDataEnu (Local Routine)
|
|
*
|
|
* Handles the IPP_REQ_ENUJOB request. This returns a complete enumeration
|
|
* of jobs. It is up to the client to determine which job they are
|
|
* interested in (if they're only interested in one-job).
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppJobDataEnu(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
HANDLE hPrinter,
|
|
LPCTSTR lpszShare,
|
|
LPBYTE lpbHdr,
|
|
DWORD cbHdr,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
LPIPPREQ_ENUJOB pgj;
|
|
LPIPPJI2 lpIppJi2;
|
|
LPTSTR lpszJobBase;
|
|
LPJOB_INFO_2 lpji2;
|
|
DWORD cbJobs;
|
|
DWORD cJobs;
|
|
DWORD cbNeed;
|
|
DWORD cNeed;
|
|
DWORD cbUri;
|
|
WORD wError;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if (pgj = (LPIPPREQ_ENUJOB)lpbHdr) {
|
|
|
|
// Initialize IPP return variables.
|
|
//
|
|
cbJobs = 0;
|
|
cJobs = 0;
|
|
lpji2 = NULL;
|
|
|
|
|
|
// Get the size necessary to hold the enumerated jobs. We
|
|
// will return JOB_INFO_2, since that has the most information.
|
|
//
|
|
cbNeed = 0;
|
|
bRet = EnumJobs(hPrinter, 0, pgj->cJobs, 2, NULL, 0, &cbNeed, &cNeed);
|
|
|
|
|
|
// If we have jobs to enumerate, then grab them.
|
|
//
|
|
if (cbNeed && (lpji2 = (LPJOB_INFO_2)AllocSplMem(cbNeed))) {
|
|
|
|
bRet = EnumJobs(hPrinter,
|
|
0,
|
|
pgj->cJobs,
|
|
2,
|
|
(LPBYTE)lpji2,
|
|
cbNeed,
|
|
&cbJobs,
|
|
&cJobs);
|
|
|
|
DBGMSG(DBG_INFO,("Spl_IppJobDataEnu(): cJobs(%d), cbJobs(%d)\r\n", cJobs, cbJobs));
|
|
}
|
|
|
|
wError = (bRet ? IPPRSP_SUCCESS : WebIppLeToRsp(GetLastError()));
|
|
|
|
|
|
// Convert the enumerated-jobs to an IPPJI2 structure. This
|
|
// allows us to pass information that is not part of a JOB_INFO_2.
|
|
//
|
|
lpszJobBase = Spl_AllocJobUri(lpszShare, 0, &cbUri, TRUE, IsSecureReq (pECB));
|
|
|
|
lpIppJi2 = WebIppCvtJI2toIPPJI2(lpszJobBase, &cbJobs, cJobs, lpji2);
|
|
|
|
if (lpszJobBase)
|
|
FreeSplMem(lpszJobBase, cbUri);
|
|
|
|
|
|
// Return the EnuJobRet structure as an IPP stream.
|
|
//
|
|
*lplpbRet = (LPBYTE)WebIppCreateEnuJobRet(wError,
|
|
bRet,
|
|
cbJobs,
|
|
cJobs,
|
|
lpIppJi2);
|
|
|
|
|
|
// Free allocated resources.
|
|
//
|
|
WebIppFreeMem(lpbHdr);
|
|
|
|
if (lpIppJi2)
|
|
WebIppFreeMem(lpIppJi2);
|
|
|
|
if (lpji2)
|
|
FreeSplMem(lpji2, cbNeed);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobDataGet (Local Routine)
|
|
*
|
|
* Handles the IPP_REQ_GETJOB request. This returns the information for a single
|
|
* job.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppJobDataGet(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
HANDLE hPrinter,
|
|
LPCTSTR lpszShare,
|
|
LPBYTE lpbHdr,
|
|
DWORD cbHdr,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
PIPPREQ_GETJOB pgj;
|
|
LPJOB_INFO_2 pji2;
|
|
JOB_INFO_IPP ipp;
|
|
DWORD cbUri;
|
|
DWORD cbPrn;
|
|
WORD wError;
|
|
DWORD cbJI2;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if (pgj = (PIPPREQ_GETJOB)lpbHdr) {
|
|
|
|
// Initialize job-information.
|
|
//
|
|
ZeroMemory(&ipp, sizeof(JOB_INFO_IPP));
|
|
|
|
|
|
if (pji2 = Spl_GetJI2(hPrinter, pgj->idJob, &cbJI2)) {
|
|
|
|
wError = IPPRSP_SUCCESS;
|
|
ipp.pJobUri = Spl_AllocJobUri(lpszShare, pgj->idJob, &cbUri, FALSE, IsSecureReq(pECB));
|
|
ipp.pPrnUri = Spl_AllocPrtUri(lpszShare, &cbPrn, IsSecureReq(pECB));
|
|
|
|
} else {
|
|
|
|
wError = WebIppLeToRsp(GetLastError());
|
|
}
|
|
|
|
|
|
// Set the return value.
|
|
//
|
|
*lplpbRet = (LPBYTE)WebIppCreateJobRet(wError, bRet, FALSE, pji2, &ipp);
|
|
|
|
|
|
// Free allocated resources.
|
|
//
|
|
WebIppFreeMem(lpbHdr);
|
|
|
|
if (pji2)
|
|
FreeSplMem(pji2, cbJI2);
|
|
|
|
if (ipp.pJobUri)
|
|
FreeSplMem(ipp.pJobUri, cbUri);
|
|
|
|
if (ipp.pPrnUri)
|
|
FreeSplMem(ipp.pPrnUri, cbPrn);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppPrnDataGet (Local Routine)
|
|
*
|
|
* Handles the IPP_REQ_GETPRN request.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppPrnDataGet(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
HANDLE hPrinter,
|
|
LPCTSTR lpszShare,
|
|
LPBYTE lpbHdr,
|
|
DWORD cbHdr,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
LPIPPREQ_GETPRN pgp;
|
|
LPPRINTER_INFO_2 lppi2;
|
|
PRINTER_INFO_IPP ipp;
|
|
DWORD cbSize;
|
|
DWORD cbUri;
|
|
WORD wError;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if (pgp = (LPIPPREQ_GETPRN)lpbHdr) {
|
|
|
|
// Initialize the default information.
|
|
//
|
|
ZeroMemory(&ipp, sizeof(PRINTER_INFO_IPP));
|
|
ipp.pPrnUri = Spl_AllocPrtUri(lpszShare, &cbUri, IsSecureReq(pECB));
|
|
|
|
|
|
// Get PRINTER_INFO_2 information.
|
|
//
|
|
cbSize = 0;
|
|
GetPrinter(hPrinter, 2, NULL, 0, &cbSize);
|
|
|
|
if (lppi2 = (LPPRINTER_INFO_2)AllocSplMem(cbSize)) {
|
|
bRet = GetPrinter(hPrinter, 2, (LPBYTE)lppi2, cbSize, &cbSize);
|
|
if (!bRet) { // lppi2 might be full of garbage, so free it and pass NULL
|
|
FreeSplMem( lppi2, cbSize );
|
|
lppi2 = NULL;
|
|
}
|
|
}
|
|
|
|
// Grab last-error if call failed.
|
|
//
|
|
wError = (bRet ? IPPRSP_SUCCESS : WebIppLeToRsp(GetLastError()));
|
|
|
|
|
|
// Return the printer-structure.
|
|
//
|
|
*lplpbRet = (LPBYTE)WebIppCreatePrnRet(wError, bRet, lppi2, &ipp);
|
|
|
|
|
|
// Free allocated resources.
|
|
//
|
|
if (lppi2)
|
|
FreeSplMem(lppi2, cbSize);
|
|
|
|
if (ipp.pPrnUri)
|
|
FreeSplMem(ipp.pPrnUri, cbUri);
|
|
|
|
WebIppFreeMem(lpbHdr);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppPrnDataSet (Local Routine)
|
|
*
|
|
* Handles SetPrinter Requests.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppPrnDataSet(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
HANDLE hPrinter,
|
|
LPBYTE lpbHdr,
|
|
DWORD cbHdr,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
PIPPREQ_SETPRN psp;
|
|
PRINTER_INFO_2 pi2;
|
|
HANDLE hPrn;
|
|
WORD wError;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
if (psp = (PIPPREQ_SETPRN)lpbHdr) {
|
|
|
|
// Initialize default information.
|
|
//
|
|
ZeroMemory(&pi2, sizeof(PRINTER_INFO_2));
|
|
|
|
|
|
// Open the printer with admin-priviledges to get
|
|
// the printer information.
|
|
//
|
|
if (hPrn = Spl_OpenPrn(hPrinter)) {
|
|
|
|
// Set the job for SetPrinter.
|
|
//
|
|
if ((bRet = SetPrinter(hPrn, 0, NULL, psp->dwCmd)) == FALSE)
|
|
wError = WebIppLeToRsp(GetLastError());
|
|
else
|
|
wError = IPPRSP_SUCCESS;
|
|
|
|
ClosePrinter(hPrn);
|
|
|
|
} else {
|
|
|
|
wError = WebIppLeToRsp(GetLastError());
|
|
}
|
|
|
|
|
|
// Return the printer-information structure.
|
|
//
|
|
*lplpbRet = (LPBYTE)WebIppCreatePrnRet(wError, bRet, &pi2, NULL);
|
|
|
|
|
|
// Free allocated resources.
|
|
//
|
|
WebIppFreeMem(lpbHdr);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobData (Local Routine)
|
|
*
|
|
* Processes ipp stream data. This returns a structure specific to the
|
|
* type of request.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppJobData(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
WORD wReq,
|
|
HANDLE hPrinter,
|
|
LPCTSTR lpszShare,
|
|
HANDLE hIpp,
|
|
LPBYTE lpbBuf,
|
|
DWORD cbBuf,
|
|
LPBYTE* lplpbRet)
|
|
{
|
|
DWORD dwIpp;
|
|
LPBYTE lpbHdr;
|
|
DWORD cbHdr;
|
|
LPBYTE lpbDta;
|
|
DWORD cbDta;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
// Convert the stream.
|
|
//
|
|
dwIpp = WebIppRcvData(hIpp, lpbBuf, cbBuf, &lpbHdr, &cbHdr, &lpbDta, &cbDta);
|
|
|
|
|
|
// See how to process it.
|
|
//
|
|
switch (dwIpp) {
|
|
|
|
case WEBIPP_OK:
|
|
|
|
switch (wReq) {
|
|
|
|
case IPP_REQ_FORCEAUTH:
|
|
bRet = Spl_IppJobDataAth(pECB, lpbHdr, cbHdr, lplpbRet);
|
|
break;
|
|
|
|
case IPP_REQ_PRINTJOB:
|
|
case IPP_REQ_VALIDATEJOB:
|
|
bRet = Spl_IppJobDataPrt(pECB,
|
|
hPrinter,
|
|
lpszShare,
|
|
lpbHdr,
|
|
cbHdr,
|
|
lpbDta,
|
|
cbDta,
|
|
lplpbRet);
|
|
break;
|
|
|
|
case IPP_REQ_CANCELJOB:
|
|
case IPP_REQ_PAUSEJOB:
|
|
case IPP_REQ_RESUMEJOB:
|
|
case IPP_REQ_RESTARTJOB:
|
|
bRet = Spl_IppJobDataSet(pECB, hPrinter, lpbHdr, cbHdr, lplpbRet);
|
|
break;
|
|
|
|
case IPP_REQ_ENUJOB:
|
|
bRet = Spl_IppJobDataEnu(pECB, hPrinter, lpszShare, lpbHdr, cbHdr, lplpbRet);
|
|
break;
|
|
|
|
case IPP_REQ_GETJOB:
|
|
bRet = Spl_IppJobDataGet(pECB, hPrinter, lpszShare, lpbHdr, cbHdr, lplpbRet);
|
|
break;
|
|
|
|
case IPP_REQ_GETPRN:
|
|
bRet = Spl_IppPrnDataGet(pECB, hPrinter, lpszShare, lpbHdr, cbHdr, lplpbRet);
|
|
break;
|
|
|
|
case IPP_REQ_PAUSEPRN:
|
|
case IPP_REQ_RESUMEPRN:
|
|
case IPP_REQ_CANCELPRN:
|
|
bRet = Spl_IppPrnDataSet(pECB, hPrinter, lpbHdr, cbHdr, lplpbRet);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WEBIPP_MOREDATA:
|
|
|
|
// More processing. Do nothing here.
|
|
//
|
|
*lplpbRet = NULL;
|
|
bRet = TRUE;
|
|
break;
|
|
|
|
case WEBIPP_NOMEMORY:
|
|
DBGMSG(DBG_WARN, ("Spl_IppJobData() failed (%d)\r\n", dwIpp));
|
|
|
|
*lplpbRet = NULL;
|
|
bRet = FALSE;
|
|
break;
|
|
|
|
case WEBIPP_BADHANDLE:
|
|
*lplpbRet = (LPBYTE)WebIppCreateBadRet(IPPRSP_ERROR_500, FALSE);
|
|
bRet = FALSE;
|
|
break;
|
|
|
|
default:
|
|
case WEBIPP_FAIL:
|
|
*lplpbRet = (LPBYTE)WebIppCreateBadRet(WebIppGetError(hIpp), FALSE);
|
|
bRet = TRUE;
|
|
break;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobRsp
|
|
*
|
|
* Sends back a job-response in IPP format.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL Spl_IppJobRsp(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
WORD wReq,
|
|
LPREQINFO lpri,
|
|
LPBYTE lpbRet)
|
|
{
|
|
LPBYTE lpIpp;
|
|
DWORD cbIpp;
|
|
DWORD cbHdr;
|
|
DWORD dwIpp;
|
|
DWORD cch;
|
|
LPCSTR lpszErr;
|
|
CHAR szHdr[1024];
|
|
BOOL bRet = FALSE;
|
|
|
|
static CONST CHAR s_szErr400[] = "400 Failed Response";
|
|
static CONST CHAR s_szErr401[] = "401 Authentication Required";
|
|
static CONST CHAR s_szHtpHdr[] = "Content-Type: application/ipp\r\nContent-Length: %d\r\n\r\n";
|
|
|
|
|
|
if (lpbRet) {
|
|
|
|
// Convert to an IPP-Buffer from the return-buffer structure. For
|
|
// failure cases, the last-error is initialized in the (lpbRet)
|
|
// structure so that the appropriate stream can be generated.
|
|
//
|
|
dwIpp = WebIppSndData((IPP_RESPONSE | wReq),
|
|
lpri,
|
|
lpbRet,
|
|
*((LPDWORD)lpbRet),
|
|
&lpIpp,
|
|
&cbIpp);
|
|
|
|
if (dwIpp == WEBIPP_OK) {
|
|
|
|
// If we had an access-denied, then we will need to include
|
|
// error 401. This will force the client to prompt for
|
|
// validation.
|
|
//
|
|
if (((LPIPPRET_ALL)lpbRet)->dwLastError == ERROR_ACCESS_DENIED)
|
|
lpszErr = s_szErr401;
|
|
else
|
|
lpszErr = NULL;
|
|
|
|
|
|
// Build header information.
|
|
//
|
|
cch = wsprintfA(szHdr, s_szHtpHdr, cbIpp);
|
|
|
|
|
|
// First we send a standard SEND_RESPONSE_HEADER w/our
|
|
// content-type ServerSupportFunction only handles szText,
|
|
// ANSI ??? see URL:
|
|
//
|
|
// http://www.microsoft.com/WIN32DEV/APIEXT/ISAPIREF.HTM
|
|
//
|
|
// see include httpfilt.h
|
|
//
|
|
Spl_CallSSF(pECB,
|
|
HSE_REQ_SEND_RESPONSE_HEADER,
|
|
(LPVOID)lpszErr,
|
|
(LPDWORD)&cch,
|
|
(LPDWORD)szHdr);
|
|
|
|
|
|
// For binary data we use WriteClient.
|
|
//
|
|
bRet = Spl_WriteClient(pECB, lpIpp, cbIpp);
|
|
|
|
WebIppFreeMem(lpIpp);
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARN, ("Warn: WebIppSndData failed (%d)", dwIpp));
|
|
}
|
|
}
|
|
|
|
|
|
// Send an HTTP error header if we had big problems...
|
|
//
|
|
if (bRet == FALSE) {
|
|
|
|
cch = lstrlenA(s_szErr400);
|
|
|
|
Spl_CallSSF(pECB,
|
|
HSE_REQ_SEND_RESPONSE_HEADER,
|
|
(LPVOID)s_szErr400,
|
|
(LPDWORD)&cch,
|
|
(LPDWORD)NULL);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobAsyncCB
|
|
*
|
|
* Process the asynchronous reads. This is called by a random ISAPI thread.
|
|
*
|
|
*****************************************************************************/
|
|
VOID Spl_IppJobAsyncCB(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
PVOID pInfo,
|
|
DWORD cbIO,
|
|
DWORD dwError)
|
|
{
|
|
LPSPLASYNC pAsync;
|
|
REQINFO ri;
|
|
BOOL bRet;
|
|
|
|
|
|
if (pAsync = (LPSPLASYNC)pInfo) {
|
|
|
|
if ((dwError == 0) && cbIO) {
|
|
|
|
// Process the return from the IPP-Receive. This will
|
|
// process the bytes to the job.
|
|
//
|
|
bRet = Spl_IppJobData(pECB,
|
|
pAsync->wReq,
|
|
pAsync->hPrinter,
|
|
pAsync->lpszShare,
|
|
pAsync->hIpp,
|
|
pAsync->lpbBuf,
|
|
cbIO,
|
|
&pAsync->lpbRet);
|
|
|
|
|
|
// Read another chunk if we haven't read it all yet..
|
|
//
|
|
pAsync->cbRead += cbIO;
|
|
|
|
|
|
// If an error occured, or we reached the end of our reads,
|
|
// then we need to bail out of the asynchronous callback.
|
|
//
|
|
if ((bRet == FALSE) || (pAsync->cbRead >= pAsync->cbTotal)) {
|
|
|
|
goto SplCBDone;
|
|
}
|
|
|
|
|
|
// Read another chunk.
|
|
//
|
|
Spl_ReadClient(pECB, pAsync->lpbBuf, pAsync->cbBuf);
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_WARN, ("Spl_IppJobAsyncCB() : Called with error or zero-bytes\r\n"));
|
|
|
|
bRet = (pAsync->cbRead >= pAsync->cbTotal);
|
|
|
|
SplCBDone:
|
|
|
|
// Send our response-header.
|
|
//
|
|
WebIppGetReqInfo(pAsync->hIpp, &ri);
|
|
|
|
Spl_IppJobRsp(pECB, pAsync->wReq, &ri, pAsync->lpbRet);
|
|
|
|
Spl_FreeAsync(pAsync);
|
|
|
|
Spl_EndSession(pECB);
|
|
}
|
|
|
|
} else {
|
|
|
|
DBGMSG(DBG_ERROR, ("Spl_IppJobAsyncCB() : No Context Value\r\n"));
|
|
|
|
Spl_EndSession(pECB);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobAsync
|
|
*
|
|
* This routine processes the job as an asynchronous read.
|
|
* It is only called once, the rest of the packets are handled by the async call back.
|
|
*
|
|
* How IIS's Async reads work:
|
|
* 1) ISAPI's HTTPExtensionProc gets called for the first chunk of data as usual.
|
|
* 2) In that call:
|
|
* - The ISAPI sets up a context, allocs a buffer and registers a call back for
|
|
* async reads.
|
|
* - Consumes the first chunk of the data.
|
|
* - Calls ServerSupportFunction( HSE_REQ_ASYNC_READ_CLIENT...) passing the buffer
|
|
* for IIS to write to. This call returns immediately with no data. When
|
|
* IIS has got more data from the client, it writes it to the ISAPI's buffer, then
|
|
* calls the call back passing the context handle that points to the buffer.
|
|
* 3) The call back consumes the data, then calls ServerSupportFunction(
|
|
* HSE_REQ_ASYNC_READ_CLIENT) again to repeat the cycle. IIS calls the call back
|
|
* once per ISAPI's call to ServerSupportFunction( HSE_REQ_ASYNC_READ_CLIENT ).
|
|
*
|
|
*****************************************************************************/
|
|
DWORD Spl_IppJobAsync(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
WORD wReq,
|
|
LPCTSTR lpszShare,
|
|
HANDLE hPrinter)
|
|
{
|
|
LPSPLASYNC pAsync;
|
|
REQINFO ri;
|
|
BOOL bRet = FALSE;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
// Allocate our structure that contains our state
|
|
// info during asynchronous reads.
|
|
//
|
|
if (pAsync = Spl_AllocAsync(wReq, hPrinter, lpszShare, pECB->cbTotalBytes)) {
|
|
|
|
// Set our asynchronous callback. Specify our (pAsync) structure
|
|
// as the context which is passed to each callback.
|
|
//
|
|
if (Spl_SetAsyncCB(pECB, (LPVOID)Spl_IppJobAsyncCB, (LPDWORD)pAsync)) {
|
|
|
|
// Process our first buffer. Our first chunk will utilize
|
|
// what's already in the ECB-structure. For other chunks,
|
|
// we will specify our own buffer.
|
|
//
|
|
bSuccess = Spl_IppJobData(pECB,
|
|
wReq,
|
|
pAsync->hPrinter,
|
|
pAsync->lpszShare,
|
|
pAsync->hIpp,
|
|
pECB->lpbData,
|
|
pECB->cbAvailable,
|
|
&pAsync->lpbRet);
|
|
|
|
if (bSuccess) {
|
|
|
|
// Increment our read-count for the bytes we've
|
|
// just processed.
|
|
//
|
|
pAsync->cbRead += pECB->cbAvailable;
|
|
|
|
|
|
// Do our first asynchronous read. Return if all is
|
|
// successful.
|
|
//
|
|
if (Spl_ReadClient(pECB, pAsync->lpbBuf, pAsync->cbBuf))
|
|
return HSE_STATUS_PENDING;
|
|
}
|
|
|
|
WebIppGetReqInfo(pAsync->hIpp, &ri);
|
|
|
|
Spl_IppJobRsp(pECB, wReq, &ri, pAsync->lpbRet);
|
|
|
|
Spl_EndSession(pECB);
|
|
|
|
bRet = TRUE; // We must return HSE_STATUS_PENDING if we call
|
|
// HSE_REQ_DONE_WITH_SESSION
|
|
}
|
|
|
|
|
|
// Free our async-structure. This indirectly frees the
|
|
// return buffer as well.
|
|
//
|
|
Spl_FreeAsync(pAsync);
|
|
|
|
} else {
|
|
|
|
ClosePrinter(hPrinter);
|
|
}
|
|
|
|
return (bRet ? HSE_STATUS_PENDING : HSE_STATUS_ERROR);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Spl_IppJobSync
|
|
*
|
|
* This routine processes the job as a synchronous-read. This implies that
|
|
* our entire job made it in one-post, and thus doesn't need to perform
|
|
* any reads.
|
|
*
|
|
*****************************************************************************/
|
|
DWORD Spl_IppJobSync(
|
|
LPEXTENSION_CONTROL_BLOCK pECB,
|
|
WORD wReq,
|
|
LPCTSTR lpszShare,
|
|
HANDLE hPrinter)
|
|
{
|
|
HANDLE hIpp;
|
|
LPIPPRET_JOB pj;
|
|
REQINFO ri;
|
|
LPBYTE lpbRet = NULL;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
// Initialize request structure.
|
|
//
|
|
ZeroMemory(&ri, sizeof(REQINFO));
|
|
ri.idReq = 0;
|
|
ri.cpReq = CP_UTF8;
|
|
ri.pwlUns = NULL;
|
|
ri.bFidelity = FALSE;
|
|
|
|
ri.fReq[0] = IPP_REQALL;
|
|
ri.fReq[1] = IPP_REQALL;
|
|
|
|
// Open an IPP-Receive channel and call the routine to process
|
|
// the job.
|
|
//
|
|
if (hIpp = WebIppRcvOpen(wReq)) {
|
|
|
|
bRet = Spl_IppJobData(pECB,
|
|
wReq,
|
|
hPrinter,
|
|
lpszShare,
|
|
hIpp,
|
|
pECB->lpbData,
|
|
pECB->cbAvailable,
|
|
&lpbRet);
|
|
|
|
WebIppGetReqInfo(hIpp, &ri);
|
|
}
|
|
|
|
|
|
// Send the job-response back to the client. If we weren't able
|
|
// to open an IPP-Receive handle, or our job-processing failed,
|
|
// then our error is FALSE.
|
|
//
|
|
bRet = Spl_IppJobRsp(pECB, wReq, &ri, lpbRet);
|
|
|
|
|
|
// Free up the receive-handle only after the response. We need to
|
|
// insure the integrity of the unsupported-list-handle.
|
|
//
|
|
if (hIpp)
|
|
WebIppRcvClose(hIpp);
|
|
|
|
|
|
// Close the printer-handle. We do this here as oppose to in
|
|
// (msw3prt.cxx), since if we had performed asynchronous reads
|
|
// we would need to leave the scope the HttpExtensionProc() call.
|
|
//
|
|
// NOTE: CloseJob() closes the printer-handle. Only in the case
|
|
// where we were not able to open a job should we close it
|
|
// here.
|
|
//
|
|
pj = (LPIPPRET_JOB)lpbRet;
|
|
|
|
if((wReq == IPP_REQ_PRINTJOB) && pj && pj->bRet) {
|
|
|
|
CloseJob((DWORD)pj->bRet);
|
|
|
|
} else {
|
|
|
|
ClosePrinter(hPrinter);
|
|
}
|
|
|
|
|
|
// Free our return-structure.
|
|
//
|
|
if (lpbRet)
|
|
WebIppFreeMem(lpbRet);
|
|
|
|
return (bRet ? HSE_STATUS_SUCCESS : HSE_STATUS_ERROR);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* SplIppJob
|
|
*
|
|
* Process the IPP Job Request.
|
|
*
|
|
* Get the print-job. If we can't handle the entire post within this
|
|
* scope, then we setup for asynchronous reads.
|
|
*
|
|
*****************************************************************************/
|
|
DWORD SplIppJob(
|
|
WORD wReq,
|
|
PALLINFO pAllInfo,
|
|
PPRINTERPAGEINFO pPageInfo)
|
|
{
|
|
DWORD dwRet;
|
|
|
|
|
|
// If our bytes aren't contained in one-chunk, then
|
|
// we need to start an asynchronous read.
|
|
//
|
|
// Otherwise, if our available amounts to the total-bytes
|
|
// of the job, then we can process the entire command sychronousely.
|
|
//
|
|
if (pAllInfo->pECB->cbAvailable < pAllInfo->pECB->cbTotalBytes) {
|
|
|
|
dwRet = Spl_IppJobAsync(pAllInfo->pECB,
|
|
wReq,
|
|
pPageInfo->pPrinterInfo->pShareName,
|
|
pPageInfo->hPrinter);
|
|
} else {
|
|
|
|
dwRet = Spl_IppJobSync(pAllInfo->pECB,
|
|
wReq,
|
|
pPageInfo->pPrinterInfo->pShareName,
|
|
pPageInfo->hPrinter);
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* OpenJob
|
|
*
|
|
* Starts a job. This creates a new spool-job-entry, the returns a jobid.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
DWORD OpenJob(
|
|
IN LPEXTENSION_CONTROL_BLOCK pECB,
|
|
IN HANDLE hPrinter,
|
|
IN PIPPREQ_PRTJOB pipr,
|
|
IN DWORD dwSize,
|
|
OUT PINIJOB *ppCopyIniJob)
|
|
{
|
|
PINIJOB pIniJob;
|
|
DWORD JobId = 0;
|
|
LS_HANDLE hLicense;
|
|
|
|
|
|
if ((NULL == hPrinter) || (NULL == pipr) || (dwSize < sizeof(IPPREQ_PRTJOB)))
|
|
return 0;
|
|
|
|
|
|
if( RequestLicense( &hLicense, pECB )) { // Enforce the Client Access Licensing
|
|
|
|
if (pIniJob = (PINIJOB)AllocSplMem(sizeof(INIJOB))) {
|
|
|
|
DWORD dwNeeded;
|
|
DOC_INFO_1 di = {0, 0, 0};
|
|
|
|
ZeroMemory( pIniJob, sizeof(INIJOB) ); // This ensures that unset fields are NULL
|
|
|
|
pIniJob->signature = IJ_SIGNATURE;
|
|
pIniJob->cb = sizeof(INIJOB);
|
|
pIniJob->hLicense = hLicense;
|
|
|
|
|
|
di.pDocName = pipr->pDocument;
|
|
|
|
|
|
if (JobId = StartDocPrinter(hPrinter, 1, (LPBYTE)&di)) {
|
|
|
|
// we successfully add a job to spooler
|
|
//
|
|
pIniJob->JobId = JobId;
|
|
|
|
#if 0
|
|
//This is a long, complicated way of doing nothing!
|
|
//We do a GetJob with no call to SetJob or any other side effect.
|
|
// -- MLAWRENC
|
|
|
|
// set User name
|
|
|
|
// =======================================================
|
|
// CCTENG 2/5/96
|
|
//
|
|
// The way we set user name here may not work on NT.
|
|
// We do this because we don't have client impersonation.
|
|
//
|
|
// This also require a UPDATED SPOOLSS.DLL to work,
|
|
// it doesn't work on WIN95.
|
|
// =======================================================
|
|
|
|
// MAKE SURE ALL THE LEVEL PARAMETERS ARE THE SAME !!!
|
|
//
|
|
if (!GetJob(hPrinter, pIniJob->JobId, 1, NULL, 0, &dwNeeded) &&
|
|
(GetLastError() == ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
PJOB_INFO_1 pJobInfo;
|
|
DWORD cb = dwNeeded;
|
|
|
|
if(pJobInfo = (PJOB_INFO_1)AllocSplMem(cb)) {
|
|
|
|
if (GetJob(hPrinter, pIniJob->JobId, 1, (LPBYTE)pJobInfo, cb, &dwNeeded)) {
|
|
|
|
pJobInfo->pUserName = (pipr->pUserName ? pipr->pUserName : TEXT("Unknown"));
|
|
|
|
//
|
|
// If you do not want to set a printer job's position in that printer queue, you
|
|
// should set the Position member to be JOB_POSITION_UNSPECIFIED
|
|
//
|
|
// weihaic 07/09/98
|
|
//pJobInfo->Position = JOB_POSITION_UNSPECIFIED;
|
|
|
|
//SetJob(hPrinter, pIniJob->JobId, 1, (LPBYTE)pJobInfo, 0);
|
|
}
|
|
|
|
FreeSplMem(pJobInfo, cb);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// keep the hPrinter until CloseJob
|
|
//
|
|
pIniJob->hPrinter = hPrinter;
|
|
|
|
pIniJob->dwStartTime = GetCurrentMinute();
|
|
pIniJob->dwStatus = JOB_READY;
|
|
pIniJob->pECB = pECB;
|
|
|
|
|
|
if (ppCopyIniJob)
|
|
// Allocate and copy the new ppIniJob structure out, some of the elements
|
|
// will be null
|
|
if (*ppCopyIniJob = (PINIJOB)AllocSplMem( sizeof(INIJOB) ) )
|
|
CopyMemory( *ppCopyIniJob, pIniJob, sizeof(INIJOB) );
|
|
|
|
AddJobEntry(pIniJob);
|
|
|
|
} else {
|
|
|
|
// StartDocPrinter Failed
|
|
//
|
|
DBGMSG(DBG_WARN, ("StartDocPrinter Failed %d\n", GetLastError()));
|
|
|
|
FreeSplMem(pIniJob, pIniJob->cb);
|
|
|
|
FreeLicense( hLicense );
|
|
}
|
|
}
|
|
else // if alloc failed
|
|
FreeLicense( hLicense );
|
|
|
|
} else { // if failed to update a license
|
|
|
|
|
|
// Spl_IppJobRsp() will check for this and send down
|
|
// proper error to the client.
|
|
//
|
|
SetLastError( ERROR_LICENSE_QUOTA_EXCEEDED );
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (JobId)
|
|
DBGMSG(DBG_INFO,("OpenJob : succeed, JobID == %d\r\n", JobId));
|
|
else
|
|
DBGMSG(DBG_WARN,("OpenJob : failed!\r\n"));
|
|
#endif
|
|
|
|
// what is this for in the failure case ???
|
|
//
|
|
// AuthenticateUser(pAllInfo);
|
|
//
|
|
|
|
|
|
return JobId;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* WriteJob
|
|
*
|
|
* Write the job.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL WriteJob(
|
|
DWORD JobId,
|
|
LPBYTE pBuf,
|
|
DWORD dwSize,
|
|
LPDWORD pWritten)
|
|
{
|
|
PINIJOB pIniJob;
|
|
BOOL bRet = FALSE;
|
|
|
|
if (pIniJob = FindJob(JobId, JOB_BUSY)) {
|
|
|
|
// need to add code to check *pWritten == dwSize
|
|
// No need to check. The caller will check if all the bytes are written - weihaic
|
|
//
|
|
bRet = WritePrinter(pIniJob->hPrinter, pBuf, dwSize, pWritten);
|
|
pIniJob->dwStatus = JOB_READY;
|
|
return bRet;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CloseJob
|
|
*
|
|
* Close job and remove from the list.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL CloseJob(
|
|
DWORD JobId)
|
|
{
|
|
PINIJOB pIniJob;
|
|
BOOL ret = FALSE;
|
|
|
|
if (pIniJob = FindJob(JobId, JOB_BUSY)) {
|
|
|
|
ret = EndDocPrinter(pIniJob->hPrinter);
|
|
|
|
ClosePrinter(pIniJob->hPrinter);
|
|
|
|
DeleteJobEntry(pIniJob);
|
|
|
|
// CleanupOldJob needs to do the same to take care of orphan Async jobs.
|
|
FreeLicense( pIniJob->hLicense );
|
|
|
|
FreeSplMem(pIniJob, pIniJob->cb);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* DeleteJob
|
|
*
|
|
* TBD : Unimplemented.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL DeleteJob(
|
|
DWORD JobId)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* AddJobEntryToLinkList
|
|
*
|
|
* Add an entry from a double linked list
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VOID AddJobEntryToLinkList(
|
|
PINIJOB &pIniFirstJob,
|
|
PINIJOB pIniJob)
|
|
{
|
|
PINIJOB pIniJobTmp;
|
|
|
|
pIniJob->pNext = NULL;
|
|
pIniJob->pPrevious = NULL;
|
|
|
|
if (!(pIniJobTmp = pIniFirstJob))
|
|
{
|
|
pIniFirstJob = pIniJob;
|
|
}
|
|
else
|
|
{
|
|
// add pIniJob to the end of the list
|
|
|
|
for (; pIniJobTmp->pNext; pIniJobTmp = pIniJobTmp->pNext)
|
|
;
|
|
|
|
pIniJob->pPrevious = pIniJobTmp;
|
|
pIniJobTmp->pNext = pIniJob;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* DeleteJobEntryFromLinkList
|
|
*
|
|
* Delete an entry from a double linked list
|
|
*
|
|
*****************************************************************************/
|
|
VOID DeleteJobEntryFromLinkList(
|
|
PINIJOB &pIniFirstJob,
|
|
PINIJOB pIniJob)
|
|
{
|
|
if (pIniJob->pPrevious)
|
|
pIniJob->pPrevious->pNext = pIniJob->pNext;
|
|
else
|
|
// pIniJob must be the first job
|
|
pIniFirstJob = pIniJob->pNext;
|
|
|
|
if (pIniJob->pNext)
|
|
pIniJob->pNext->pPrevious = pIniJob->pPrevious;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* AddJobEntry
|
|
*
|
|
* I just use a simple double linked list for now. Can be changed to
|
|
* something else such as a hash table later, if desired.
|
|
*
|
|
*****************************************************************************/
|
|
VOID AddJobEntry(
|
|
PINIJOB pIniJob)
|
|
{
|
|
EnterSplSem();
|
|
|
|
AddJobEntryToLinkList(pIniFirstJob, pIniJob);
|
|
|
|
LeaveSplSem();
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* DeleteJobEntry
|
|
*
|
|
* Delete job from the job-list.
|
|
*
|
|
*****************************************************************************/
|
|
VOID DeleteJobEntry(
|
|
PINIJOB pIniJob)
|
|
{
|
|
EnterSplSem();
|
|
|
|
DeleteJobEntryFromLinkList (pIniFirstJob, pIniJob);
|
|
|
|
LeaveSplSem();
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FindJob
|
|
*
|
|
* Looks for job in the job-list and dwStatus
|
|
*
|
|
*****************************************************************************/
|
|
PINIJOB FindJob(
|
|
DWORD JobId,
|
|
DWORD dwStatus)
|
|
{
|
|
PINIJOB pIniJob;
|
|
|
|
EnterSplSem();
|
|
|
|
// pIniJob will end up being NULL if a match is not found
|
|
|
|
for (pIniJob = pIniFirstJob; pIniJob; pIniJob = pIniJob->pNext)
|
|
{
|
|
if (pIniJob->dwStatus == JOB_READY && pIniJob->JobId == JobId) {
|
|
// found the match, break and return pIniJob
|
|
// Set the status
|
|
pIniJob->dwStatus = dwStatus;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
return pIniJob;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CleanupOldJob
|
|
*
|
|
* This function is called by Sleeper->Work() about every 15 minutes to cleanup
|
|
* the pending unclosed jobs due to the failure of the network or any other
|
|
* possible reasons.
|
|
*
|
|
*****************************************************************************/
|
|
BOOL CleanupOldJob()
|
|
{
|
|
DWORD dwCurrentTime = GetCurrentMinute();
|
|
PINIJOB pIniJob;
|
|
PINIJOB pIniTmpJob;
|
|
PINIJOB pIniFirstOldJob = NULL;
|
|
|
|
if (!pIniFirstJob) return TRUE;
|
|
|
|
DBGMSG (DBG_WARN, ("Enter Cleanup...\r\n"));
|
|
|
|
EnterSplSem();
|
|
|
|
for (pIniJob = pIniFirstJob; pIniJob; pIniJob = pIniTmpJob) {
|
|
pIniTmpJob = pIniJob->pNext;
|
|
if (pIniJob->dwStatus == JOB_READY) {
|
|
DWORD dwDiff = (1440 + dwCurrentTime - pIniJob->dwStartTime) % 1440;
|
|
|
|
if (dwDiff > MAX_JOB_MINUTE) {
|
|
DBGMSG (DBG_WARN, ("OldJob found %x\r\n", pIniJob->hPrinter));
|
|
DeleteJobEntry (pIniJob);
|
|
AddJobEntryToLinkList (pIniFirstOldJob, pIniJob);
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveSplSem();
|
|
|
|
DWORD dwStatus = HTTP_STATUS_REQUEST_TIMEOUT;
|
|
|
|
// Delete the job outside the critical section
|
|
for (pIniJob = pIniFirstOldJob; pIniJob; pIniJob = pIniTmpJob) {
|
|
pIniTmpJob = pIniJob->pNext;
|
|
EndDocPrinter(pIniJob->hPrinter);
|
|
ClosePrinter(pIniJob->hPrinter);
|
|
FreeLicense( pIniJob->hLicense ); // CleanupOldJob needs to do the same to take care of orphan Async jobs.
|
|
|
|
|
|
#ifdef ASYNC_READ_ENABLED
|
|
|
|
// Disable it because we're not trying to manage the cleanup for
|
|
// http sessions. If there is a session pending because we close
|
|
// the job, the callback function (Spl_JobPrintCB)
|
|
// will close the session itself.
|
|
//
|
|
pIniJob->pECB->ServerSupportFunction(pIniJob->pECB->ConnID,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&dwStatus,
|
|
NULL,
|
|
NULL);
|
|
#endif
|
|
FreeSplMem(pIniJob, pIniJob->cb);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* GetCurrentMinute
|
|
*
|
|
* Get the current minute since midnight
|
|
*
|
|
*****************************************************************************/
|
|
DWORD GetCurrentMinute ()
|
|
{
|
|
SYSTEMTIME CurTime;
|
|
|
|
GetSystemTime (&CurTime);
|
|
return CurTime.wHour * 60 + CurTime.wMinute;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
DWORD dwSplHeapSize = 0;
|
|
|
|
/*****************************************************************************
|
|
* AllocSplMem (Helper)
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This function will allocate local memory. It will possibly allocate extra
|
|
* memory and fill this with debugging information for the debugging version.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* cb - The amount of memory to allocate
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NON-NULL - A pointer to the allocated memory
|
|
*
|
|
* FALSE/NULL - The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
LPVOID AllocSplMem(
|
|
DWORD cb)
|
|
{
|
|
PDWORD pMem;
|
|
DWORD cbNew;
|
|
|
|
cbNew = cb+2*sizeof(DWORD);
|
|
if (cbNew & 3)
|
|
cbNew += sizeof(DWORD) - (cbNew & 3);
|
|
|
|
pMem=(PDWORD)LocalAlloc(LPTR, cbNew);
|
|
|
|
if (!pMem)
|
|
{
|
|
DBGMSG(DBG_ERROR, ("Memory Allocation failed for %d bytes\n", cbNew));
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
*pMem=cb;
|
|
*(PDWORD)((PBYTE)pMem+cbNew-sizeof(DWORD))=0xdeadbeef;
|
|
|
|
dwSplHeapSize += cbNew;
|
|
|
|
return (LPVOID)(pMem+1);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* FreeSplMem (Helper)
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
BOOL FreeSplMem(
|
|
LPVOID pMem,
|
|
DWORD cb)
|
|
{
|
|
DWORD cbNew;
|
|
LPDWORD pNewMem;
|
|
|
|
if (!pMem)
|
|
return FALSE;
|
|
|
|
pNewMem = (LPDWORD)pMem;
|
|
pNewMem--;
|
|
|
|
cbNew = cb+2*sizeof(DWORD);
|
|
if (cbNew & 3)
|
|
cbNew += sizeof(DWORD) - (cbNew & 3);
|
|
|
|
if (*pNewMem != cb) {
|
|
DBGMSG(DBG_ERROR, ("Corrupt Memory Size in inetsrv-spool : %0lx %0lx != %0lx\n",
|
|
pNewMem, *pNewMem, cb));
|
|
return FALSE;
|
|
}
|
|
|
|
if (*(LPDWORD)((LPBYTE)pNewMem + cbNew - sizeof(DWORD)) != 0xdeadbeef) {
|
|
DBGMSG(DBG_ERROR, ("Memory Overrun in inetsrv-spool : %0lx\n", pNewMem));
|
|
return FALSE;
|
|
}
|
|
|
|
LocalFree((LPVOID)pNewMem);
|
|
|
|
dwSplHeapSize -= cbNew;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
/*****************************************************************************
|
|
* AllocSplStr (Helper)
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This function will allocate enough local memory to store the specified
|
|
* string, and copy that string to the allocated memory
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pStr - Pointer to the string that needs to be allocated and stored
|
|
*
|
|
* Return Value:
|
|
*
|
|
* NON-NULL - A pointer to the allocated memory containing the string
|
|
*
|
|
* FALSE/NULL - The operation failed. Extended error status is available
|
|
* using GetLastError.
|
|
*****************************************************************************/
|
|
LPTSTR AllocSplStr(
|
|
LPCTSTR lpszStr)
|
|
{
|
|
DWORD cbSize;
|
|
LPTSTR lpszCpy = NULL;
|
|
|
|
|
|
if (cbSize = Spl_StrSize(lpszStr)) {
|
|
|
|
if (lpszCpy = (LPTSTR)AllocSplMem(cbSize))
|
|
CopyMemory((PVOID)lpszCpy, (PVOID)lpszStr, cbSize);
|
|
}
|
|
|
|
return lpszCpy;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* FreeSplStr (Helper)
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
#ifdef DEBUG
|
|
#define FREE_PTR_TO_LONG(X) (X)
|
|
#else
|
|
#define FREE_PTR_TO_LONG(X) (PtrToLong(X))
|
|
#endif
|
|
|
|
BOOL FreeSplStr(
|
|
LPTSTR lpszStr)
|
|
{
|
|
DWORD cbSize;
|
|
|
|
cbSize = Spl_StrSize(lpszStr);
|
|
|
|
return (BOOL)(lpszStr ? FREE_PTR_TO_LONG(FreeSplMem(lpszStr, cbSize)) : FALSE);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* AuthenticateUser (Helper)
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
BOOL AuthenticateUser(
|
|
PALLINFO pAllInfo)
|
|
{
|
|
// Wade says if we don't specify a header (szAuthHdr), and just submit a 401 error, IIS
|
|
// would include (in the automatically generated header) what authenticaitons it is setup
|
|
// to use (NTLM and/or Basic). So the client can pick the first one on the list and use it
|
|
// (this is what IE does).
|
|
//
|
|
// Note: for NTLM to work, you need adirect socket connection. So it won't work across
|
|
// firewalls (IIS admins are supposed to know this). Secure socket seems to do it though,
|
|
// so for the new MS Proxy, it might be doable.
|
|
//
|
|
return Spl_CallSSF(pAllInfo->pECB,
|
|
HSE_REQ_SEND_RESPONSE_HEADER,
|
|
(LPVOID)"401 Authentication Required",
|
|
(LPDWORD)NULL,
|
|
(LPDWORD)NULL);
|
|
|
|
}
|