1285 lines
31 KiB
C++
1285 lines
31 KiB
C++
/*****************************************************************************\
|
|
* MODULE: ppprn.c
|
|
*
|
|
* This module contains the routines which control the printer during the
|
|
* course of a single job.
|
|
*
|
|
*
|
|
* Copyright (C) 1996-1997 Microsoft Corporation
|
|
* Copyright (C) 1996-1997 Hewlett Packard
|
|
*
|
|
* History:
|
|
* 09-Jun-1993 JonMarb Created
|
|
* 07-Oct-1996 HWP-Guys Initiated port from win95 to winNT
|
|
*
|
|
\*****************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#include "priv.h"
|
|
|
|
/*****************************************************************************\
|
|
* _ppprn_free_hprinter (Local Routine)
|
|
*
|
|
* Free up the hPrinter handle.
|
|
*
|
|
\*****************************************************************************/
|
|
_inline VOID _ppprn_free_hprinter(
|
|
HANDLE hPrinter)
|
|
{
|
|
LPINET_HPRINTER pPrt;
|
|
|
|
|
|
if (pPrt = (LPINET_HPRINTER)hPrinter) {
|
|
|
|
#ifdef WINNT32
|
|
DeleteHandleFromList (pPrt);
|
|
#endif
|
|
|
|
if (pPrt->lpszName)
|
|
memFreeStr(pPrt->lpszName);
|
|
|
|
#ifdef WINNT32
|
|
|
|
if (pPrt->hUser)
|
|
delete ( pPrt->hUser );
|
|
|
|
#endif
|
|
|
|
memFree(pPrt, sizeof(INET_HPRINTER));
|
|
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* _ppprn_inc_user_refcount (Local Routine)
|
|
*
|
|
* Increment the reference count for the Port on the current printer
|
|
*
|
|
\*****************************************************************************/
|
|
#ifdef WINNT32
|
|
_inline DWORD __ppprn_inc_user_refcout(
|
|
HANDLE hPrinter ) {
|
|
|
|
LPINET_HPRINTER pPrt;
|
|
DWORD dwRet = MAXDWORD;
|
|
|
|
if (pPrt = (LPINET_HPRINTER)hPrinter) {
|
|
dwRet = (PCINETMONPORT (pPrt->hPort))->IncUserRefCount(pPrt->hUser );
|
|
|
|
} else
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************\
|
|
* _ppprn_make_hprinter (Local Routine)
|
|
*
|
|
* Returns a printer handle.
|
|
*
|
|
\*****************************************************************************/
|
|
HANDLE _ppprn_make_hprinter(
|
|
HANDLE hPort,
|
|
LPCTSTR lpszPrnName)
|
|
{
|
|
LPINET_HPRINTER pPrt;
|
|
|
|
|
|
if (pPrt = (LPINET_HPRINTER)memAlloc(sizeof(INET_HPRINTER))) {
|
|
|
|
pPrt->dwSignature = IPO_SIGNATURE;
|
|
pPrt->lpszName = memAllocStr(lpszPrnName);
|
|
pPrt->hPort = hPort;
|
|
pPrt->dwStatus = 0;
|
|
pPrt->pjmJob = NULL;
|
|
#ifdef WINNT32
|
|
pPrt->hUser = new CLogonUserData;
|
|
|
|
|
|
#endif
|
|
|
|
if (
|
|
pPrt->lpszName
|
|
#ifdef WINNT32
|
|
&& pPrt->hUser
|
|
&& pPrt->hUser->bValid ()
|
|
&& AddHandleToList (pPrt)
|
|
#endif
|
|
) {
|
|
|
|
return (HANDLE)pPrt;
|
|
|
|
} else {
|
|
|
|
if (pPrt->lpszName)
|
|
memFreeStr (pPrt->lpszName);
|
|
|
|
#ifdef WINNT32
|
|
if (pPrt->hUser) {
|
|
delete ( pPrt->hUser );
|
|
}
|
|
#endif
|
|
memFree (pPrt, sizeof(INET_HPRINTER));
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef WINNT32
|
|
|
|
/*****************************************************************************\
|
|
* _ppprn_free_xcv_hprinter (Local Routine)
|
|
*
|
|
* Free a xcv printer handle.
|
|
*
|
|
\*****************************************************************************/
|
|
_inline VOID _ppprn_free_xcv_hprinter(
|
|
HANDLE hPrinter)
|
|
{
|
|
LPINET_XCV_HPRINTER pPrt;
|
|
|
|
|
|
if (pPrt = (LPINET_XCV_HPRINTER)hPrinter) {
|
|
|
|
memFreeStr (pPrt->lpszName);
|
|
memFree(pPrt, sizeof(INET_XCV_HPRINTER));
|
|
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* _ppprn_make_xcv_hprinter (Local Routine)
|
|
*
|
|
* Returns a xcv printer handle.
|
|
*
|
|
\*****************************************************************************/
|
|
HANDLE _ppprn_make_xcv_hprinter(
|
|
PCINETMONPORT pIniPort)
|
|
{
|
|
LPINET_XCV_HPRINTER pPrt;
|
|
|
|
|
|
if (pPrt = (LPINET_XCV_HPRINTER)memAlloc(sizeof(INET_XCV_HPRINTER))) {
|
|
|
|
pPrt->dwSignature = IPO_XCV_SIGNATURE;
|
|
pPrt->lpszName = memAllocStr(pIniPort->GetPortName());
|
|
}
|
|
|
|
return pPrt;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************\
|
|
* ppprn_IppSetRsp (Local Callback Routine)
|
|
*
|
|
* Retrieves a SetPrinter response from the IPP server
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL CALLBACK ppprn_IppSetRsp(
|
|
CAnyConnection *pConnection,
|
|
HINTERNET hReq,
|
|
PCINETMONPORT pIniPort,
|
|
LPARAM lParam)
|
|
{
|
|
HANDLE hIpp;
|
|
DWORD dwRet;
|
|
DWORD cbRd;
|
|
LPBYTE lpDta;
|
|
DWORD cbDta;
|
|
LPIPPRET_PRN lpRsp;
|
|
DWORD cbRsp;
|
|
BYTE bBuf[MAX_IPP_BUFFER];
|
|
BOOL bRet = FALSE;
|
|
|
|
if (hIpp = WebIppRcvOpen((WORD)(LPARAM)lParam)) {
|
|
|
|
while (TRUE) {
|
|
|
|
cbRd = 0;
|
|
if (pIniPort->ReadFile (pConnection, hReq, (LPVOID)bBuf, sizeof(bBuf), &cbRd) && cbRd) {
|
|
|
|
dwRet = WebIppRcvData(hIpp, bBuf, cbRd, (LPBYTE*)&lpRsp, &cbRsp, &lpDta, &cbDta);
|
|
|
|
switch (dwRet) {
|
|
|
|
case WEBIPP_OK:
|
|
|
|
if ((bRet = lpRsp->bRet) == FALSE)
|
|
SetLastError(lpRsp->dwLastError);
|
|
|
|
WebIppFreeMem(lpRsp);
|
|
|
|
goto EndSetRsp;
|
|
|
|
case WEBIPP_MOREDATA:
|
|
|
|
// Need to do another read to fullfill our header-response.
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DBG_MSG(DBG_LEV_ERROR, (TEXT("ppprn_IppSetRsp - Err : Receive Data Error (dwRet=%d, LE=%d)"),
|
|
dwRet, WebIppGetError(hIpp)));
|
|
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
|
|
goto EndSetRsp;
|
|
}
|
|
|
|
} else {
|
|
|
|
goto EndSetRsp;
|
|
}
|
|
}
|
|
|
|
EndSetRsp:
|
|
|
|
WebIppRcvClose(hIpp);
|
|
|
|
} else {
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* ppprn_Set (Local Routine)
|
|
*
|
|
* Sets a printer command.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL ppprn_Set(
|
|
PCINETMONPORT pIniPort,
|
|
DWORD dwCmd)
|
|
{
|
|
PIPPREQ_SETPRN psp;
|
|
REQINFO ri;
|
|
DWORD dwRet;
|
|
LPBYTE lpIpp;
|
|
DWORD cbIpp;
|
|
WORD wReq;
|
|
LPTSTR lpszUsrName;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
// Create our ipp-reqest-structure.
|
|
//
|
|
if (lpszUsrName = GetUserName()) {
|
|
|
|
psp = WebIppCreateSetPrnReq(dwCmd,
|
|
lpszUsrName,
|
|
pIniPort->GetPortName());
|
|
|
|
|
|
memFreeStr(lpszUsrName);
|
|
|
|
if (psp) {
|
|
|
|
switch (dwCmd) {
|
|
case PRINTER_CONTROL_PAUSE:
|
|
wReq = IPP_REQ_PAUSEPRN;
|
|
break;
|
|
|
|
case PRINTER_CONTROL_RESUME:
|
|
wReq = IPP_REQ_RESUMEPRN;
|
|
break;
|
|
|
|
case PRINTER_CONTROL_PURGE:
|
|
wReq = IPP_REQ_CANCELPRN;
|
|
break;
|
|
|
|
default:
|
|
wReq = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
// Convert the reqest to IPP, and perform the
|
|
// post.
|
|
//
|
|
ZeroMemory(&ri, sizeof(REQINFO));
|
|
ri.cpReq = CP_UTF8;
|
|
ri.idReq = wReq;
|
|
|
|
ri.fReq[0] = IPP_REQALL;
|
|
ri.fReq[1] = IPP_REQALL;
|
|
|
|
dwRet = WebIppSndData(wReq,
|
|
&ri,
|
|
(LPBYTE)psp,
|
|
psp->cbSize,
|
|
&lpIpp,
|
|
&cbIpp);
|
|
|
|
|
|
// The request-structure has been converted to IPP format,
|
|
// so it is ready to go to the server.
|
|
//
|
|
if (dwRet == WEBIPP_OK) {
|
|
|
|
bRet = pIniPort->SendReq(lpIpp,
|
|
cbIpp,
|
|
ppprn_IppSetRsp,
|
|
(LPARAM)(wReq | IPP_RESPONSE),
|
|
TRUE);
|
|
|
|
WebIppFreeMem(lpIpp);
|
|
|
|
} else {
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
|
|
WebIppFreeMem(psp);
|
|
|
|
} else {
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
#ifdef WINNT32
|
|
|
|
void
|
|
_ppprn_working_thread (
|
|
PENDDOCTHREADCONTEXT pThreadData)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
PJOBMAP pjm = pThreadData->pjmJob;
|
|
PCINETMONPORT pIniPort = pThreadData->pIniPort;
|
|
static DWORD cdwWaitTime = 15000;
|
|
|
|
DBGMSGT (DBG_LEV_CALLTREE, ("Enter _ppprn_working_thread (%p)\n", pThreadData));
|
|
|
|
pThreadData->pSidToken->SetCurrentSid ();
|
|
delete pThreadData->pSidToken;
|
|
pThreadData->pSidToken = NULL;
|
|
|
|
semEnterCrit();
|
|
|
|
pjmUpdateLocalJobStatus (pjm, JOB_STATUS_PRINTING);
|
|
|
|
//
|
|
// The document is cancelled
|
|
//
|
|
if (pjmChkState(pThreadData->pjmJob, PJM_CANCEL)) {
|
|
|
|
bRet = TRUE;
|
|
}
|
|
else {
|
|
|
|
// Refresh the notification handle
|
|
//
|
|
RefreshNotificationPort (pIniPort);
|
|
|
|
|
|
bRet = pIniPort->EndDocPort(pjm);
|
|
|
|
#if 0
|
|
bRet = FALSE;
|
|
//
|
|
// This is for testing
|
|
//
|
|
semLeaveCrit();
|
|
Sleep (3000);
|
|
semEnterCrit();
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// Check this flags again, since we left CriticalSection during file transfer
|
|
//
|
|
if (pjmChkState(pThreadData->pjmJob, PJM_CANCEL)) {
|
|
|
|
bRet = TRUE;
|
|
}
|
|
|
|
if (bRet) {
|
|
//
|
|
// Clear our spooling-state. This will free up any spool-job-resources.
|
|
//
|
|
pjmClrState(pjm, PJM_SPOOLING);
|
|
|
|
//
|
|
// Invalidate both job and printer caches
|
|
//
|
|
pIniPort->InvalidateGetPrinterCache ();
|
|
pIniPort->InvalidateEnumJobsCache ();
|
|
}
|
|
else {
|
|
pjmUpdateLocalJobStatus (pjm, JOB_STATUS_ERROR);
|
|
|
|
}
|
|
|
|
// Refresh the notification handle
|
|
//
|
|
RefreshNotificationPort (pIniPort);
|
|
|
|
//
|
|
// Clean the async thread flag
|
|
//
|
|
pjmClrState(pjm, PJM_ASYNCON);
|
|
|
|
pIniPort->DecRef();
|
|
|
|
semLeaveCrit();
|
|
|
|
delete pThreadData;
|
|
|
|
DBGMSGT (DBG_LEV_CALLTREE, ("Leave _ppprn_working_thread (ret = %d)\n", bRet));
|
|
}
|
|
|
|
|
|
BOOL
|
|
_ppprn_end_docprinter_async (
|
|
PCINETMONPORT pIniPort,
|
|
PJOBMAP pjmJob)
|
|
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
PENDDOCTHREADCONTEXT pThreadData = new ENDDOCTHREADCONTEXT;
|
|
|
|
|
|
if (pThreadData) {
|
|
pThreadData->pIniPort = pIniPort;
|
|
pThreadData->pjmJob = pjmJob;
|
|
pThreadData->pSidToken = new CSid;
|
|
|
|
|
|
if (pThreadData->pSidToken && pThreadData->pSidToken->bValid ()) {
|
|
HANDLE hThread;
|
|
|
|
pjmSetState(pjmJob, PJM_ASYNCON);
|
|
|
|
//
|
|
// Increase the ref count of the port to make sure it is not deleted
|
|
//
|
|
|
|
pIniPort->IncRef();
|
|
|
|
if (pIniPort->CreateTerminateEvent() &&
|
|
(hThread = CreateThread (NULL,
|
|
COMMITTED_STACK_SIZE,
|
|
(LPTHREAD_START_ROUTINE)_ppprn_working_thread,
|
|
(PVOID) pThreadData, 0, NULL))) {
|
|
CloseHandle (hThread);
|
|
bRet = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Fail to create the thread
|
|
//
|
|
pIniPort->DecRef ();
|
|
|
|
pjmClrState(pjmJob, PJM_ASYNCON);
|
|
}
|
|
}
|
|
|
|
if (!bRet) {
|
|
if (pThreadData->pSidToken) {
|
|
delete (pThreadData->pSidToken);
|
|
pThreadData->pSidToken = NULL;
|
|
}
|
|
|
|
delete (pThreadData);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#endif
|
|
/*****************************************************************************\
|
|
* PP_OpenJobInfo
|
|
*
|
|
* Opens a job-information. This is called at StartDoc timeframe when we
|
|
* need to start a spool-job.
|
|
*
|
|
\*****************************************************************************/
|
|
PJOBMAP PP_OpenJobInfo(
|
|
HANDLE hPrinter,
|
|
PCINETMONPORT pIniPort,
|
|
LPTSTR pDocName)
|
|
{
|
|
PJOBMAP* pjmList;
|
|
LPINET_HPRINTER lpPrt = (LPINET_HPRINTER)hPrinter;
|
|
LPTSTR pUserName = GetUserName();
|
|
|
|
|
|
pjmList = pIniPort->GetPJMList();
|
|
|
|
if (lpPrt->pjmJob = pjmAdd(pjmList, pIniPort, pUserName, pDocName)) {
|
|
|
|
// Set the state to spooling for our job-entry. This wil
|
|
// create the spool-file object.
|
|
//
|
|
pjmSetState(lpPrt->pjmJob, PJM_SPOOLING);
|
|
|
|
}
|
|
|
|
memFreeStr (pUserName);
|
|
|
|
return lpPrt->pjmJob;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* PP_CloseJobInfo
|
|
*
|
|
* Close our spool-job and clear out the information regarding a printjob from
|
|
* the printer.
|
|
*
|
|
\*****************************************************************************/
|
|
VOID PP_CloseJobInfo(
|
|
HANDLE hPrinter)
|
|
{
|
|
PJOBMAP* pjmList;
|
|
LPINET_HPRINTER lpPrt = (LPINET_HPRINTER)hPrinter;
|
|
|
|
|
|
// Clear our spooling-state. This will free up any spool-job-resources.
|
|
//
|
|
pjmClrState(lpPrt->pjmJob, PJM_SPOOLING);
|
|
|
|
|
|
// If we had cancelled our print-job, then we can remove the
|
|
// entry.
|
|
//
|
|
if (pjmChkState(lpPrt->pjmJob, PJM_CANCEL)) {
|
|
|
|
pjmList = ((PCINETMONPORT)(lpPrt->hPort))->GetPJMList();
|
|
|
|
pjmDel(pjmList, lpPrt->pjmJob);
|
|
}
|
|
|
|
|
|
// Clear out or spool-status.
|
|
//
|
|
lpPrt->pjmJob = NULL;
|
|
}
|
|
|
|
VOID PP_CloseJobInfo2(
|
|
HANDLE hPrinter)
|
|
{
|
|
PJOBMAP* pjmList;
|
|
LPINET_HPRINTER lpPrt = (LPINET_HPRINTER)hPrinter;
|
|
|
|
// Clear out or spool-status.
|
|
//
|
|
lpPrt->pjmJob = NULL;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* PPAbortPrinter
|
|
*
|
|
* Deletes a printer's spool file if the printer is configured for spooling.
|
|
* Returns TRUE if successful, FALSE if an error occurs.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL PPAbortPrinter(
|
|
HANDLE hPrinter)
|
|
{
|
|
PCINETMONPORT pIniPort;
|
|
BOOL bRet = FALSE;
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPAbortPrinter(%08lX)"), hPrinter));
|
|
|
|
|
|
semEnterCrit();
|
|
|
|
if (pIniPort = utlValidatePrinterHandle(hPrinter)) {
|
|
|
|
if ( PP_ChkStatus(hPrinter, PP_STARTDOC) &&
|
|
!PP_ChkStatus(hPrinter, PP_ENDDOC)) {
|
|
|
|
if (bRet = pIniPort->AbortPort(PP_GetJobInfo(hPrinter))) {
|
|
|
|
// If this call was successful, the job-info
|
|
// will have been freed. Therefore, it is OK
|
|
// to set the printer-jobreq to NULL.
|
|
//
|
|
PP_SetStatus(hPrinter, PP_CANCELLED);
|
|
PP_ClrStatus(hPrinter, PP_STARTDOC);
|
|
PP_CloseJobInfo(hPrinter);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (PP_ChkStatus(hPrinter, PP_CANCELLED))
|
|
SetLastError(ERROR_PRINT_CANCELLED);
|
|
else
|
|
SetLastError(ERROR_SPL_NO_STARTDOC);
|
|
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* PPClosePrinter
|
|
*
|
|
* Closes a printer that was previously opened with PPOpenPrinter. Returns
|
|
* TRUE if successful, FALSE if an error occurs.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL PPClosePrinter(
|
|
HANDLE hPrinter)
|
|
{
|
|
PCINETMONPORT pIniPort;
|
|
BOOL bRet = FALSE;
|
|
BOOL bDeletePending = FALSE;
|
|
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPClosePrinter: Printer(%08lX)"), hPrinter));
|
|
|
|
semEnterCrit();
|
|
|
|
if (pIniPort = utlValidatePrinterHandleForClose(hPrinter, &bDeletePending) ) {
|
|
|
|
if (bDeletePending) {
|
|
|
|
bRet = gpInetMon->InetmonReleasePort(pIniPort);
|
|
_ppprn_free_hprinter(hPrinter);
|
|
|
|
}
|
|
else {
|
|
|
|
|
|
if ( PP_ChkStatus(hPrinter, PP_STARTDOC) &&
|
|
!PP_ChkStatus(hPrinter, PP_ENDDOC)) {
|
|
|
|
PP_SetStatus(hPrinter, PP_ENDDOC);
|
|
|
|
pIniPort->EndDocPort(PP_GetJobInfo(hPrinter));
|
|
|
|
PP_ClrStatus(hPrinter, (PP_STARTDOC | PP_ENDDOC));
|
|
PP_CloseJobInfo(hPrinter);
|
|
}
|
|
|
|
|
|
// Our write-port does leave the crit-sect. If this
|
|
// routine is called while we're still in an end-doc, then
|
|
// we will set a zombie-flag and let the End-Doc clean up
|
|
// the handle for us.
|
|
//
|
|
if (PP_ChkStatus(hPrinter, PP_ENDDOC)) {
|
|
|
|
bRet = TRUE;
|
|
|
|
PP_SetStatus(hPrinter, PP_ZOMBIE);
|
|
|
|
} else {
|
|
|
|
bRet = gpInetMon->InetmonClosePort(pIniPort, hPrinter );
|
|
_ppprn_free_hprinter(hPrinter);
|
|
}
|
|
}
|
|
}
|
|
#ifdef WINNT32
|
|
else if (utlValidateXcvPrinterHandle(hPrinter) ) {
|
|
|
|
//
|
|
// We don't need to dec-ref on the http port for XCV handle
|
|
//
|
|
|
|
//
|
|
// Free memory
|
|
//
|
|
_ppprn_free_xcv_hprinter(hPrinter);
|
|
}
|
|
#endif
|
|
|
|
semLeaveCrit();
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* PPEndDocPrinter
|
|
*
|
|
* Ends a print job on the specified printer. Returns TRUE if successful,
|
|
* FALSE otherwise.
|
|
*
|
|
\*****************************************************************************/
|
|
|
|
BOOL PPEndDocPrinter(
|
|
HANDLE hPrinter)
|
|
{
|
|
PCINETMONPORT pIniPort;
|
|
PJOBMAP pjmJob;
|
|
DWORD dwLE;
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPEndDocPrinter: Printer(%08lX)"), hPrinter));
|
|
|
|
semEnterCrit();
|
|
|
|
if (pIniPort = utlValidatePrinterHandle(hPrinter)) {
|
|
|
|
// Verify that we are in a StartDoc.
|
|
//
|
|
if ( PP_ChkStatus(hPrinter, PP_STARTDOC) &&
|
|
!PP_ChkStatus(hPrinter, PP_ENDDOC)) {
|
|
|
|
PP_SetStatus(hPrinter, PP_ENDDOC);
|
|
|
|
|
|
// Get the job we're dealing with.
|
|
//
|
|
pjmJob = PP_GetJobInfo(hPrinter);
|
|
|
|
|
|
// End the job. This closes our spooling
|
|
// and submits it to the server. If our job was
|
|
// cancelled at anytime prior to EndDoc(), then we should
|
|
// only remove the local-spool-job and not hit the server.
|
|
//
|
|
if (pjmChkState(pjmJob, PJM_CANCEL)) {
|
|
|
|
bRet = TRUE;
|
|
|
|
} else {
|
|
|
|
#ifdef WINNT32
|
|
if ((bRet = _ppprn_end_docprinter_async(pIniPort, pjmJob)) == FALSE)
|
|
dwLE = ERROR_CAN_NOT_COMPLETE;
|
|
#else
|
|
if ((bRet = pIniPort->EndDocPort(pjmJob)) == FALSE)
|
|
dwLE = ERROR_CAN_NOT_COMPLETE;
|
|
#endif
|
|
|
|
}
|
|
|
|
// Clear our flags so others can use the
|
|
// printer.
|
|
//
|
|
PP_ClrStatus(hPrinter, (PP_STARTDOC | PP_ENDDOC));
|
|
|
|
#ifdef WINNT32
|
|
PP_CloseJobInfo2(hPrinter);
|
|
#else
|
|
PP_CloseJobInfo(hPrinter);
|
|
|
|
// Invalidate both job and printer caches
|
|
//
|
|
pIniPort->InvalidateGetPrinterCache ();
|
|
pIniPort->InvalidateEnumJobsCache ();
|
|
#endif
|
|
|
|
// Since the end-doc-port leaves the crit-sect, there's
|
|
// the possibility that the printer-handle has been
|
|
// closed. If so, check for zombie-status and delete
|
|
// the printer-handle accordingly.
|
|
//
|
|
if (PP_ChkStatus(hPrinter, PP_ZOMBIE)) {
|
|
|
|
gpInetMon->InetmonClosePort(pIniPort, hPrinter);
|
|
|
|
_ppprn_free_hprinter(hPrinter);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (PP_ChkStatus(hPrinter, PP_CANCELLED))
|
|
dwLE = ERROR_PRINT_CANCELLED;
|
|
else
|
|
dwLE = ERROR_SPL_NO_STARTDOC;
|
|
}
|
|
|
|
} else {
|
|
|
|
dwLE = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
if (bRet == FALSE)
|
|
SetLastError(dwLE);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
* PPEndPagePrinter
|
|
*
|
|
* Informs the printer that the data sent with WritePrinter since the last
|
|
* BeginPage functions, constitutes a page. Returns TRUE if successful.
|
|
* Otherwise, it returns FALSE.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL PPEndPagePrinter(
|
|
HANDLE hPrinter)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPEndPagePrinter: hPrinter(%08lX)"), hPrinter));
|
|
|
|
semEnterCrit();
|
|
|
|
if (utlValidatePrinterHandle(hPrinter) != NULL) {
|
|
|
|
if (PP_ChkStatus(hPrinter, PP_CANCELLED))
|
|
SetLastError(ERROR_PRINT_CANCELLED);
|
|
else
|
|
bRet = TRUE;
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* PPOpenPrinter
|
|
*
|
|
* Obtains a handle for the specified printer (queue).
|
|
*
|
|
* NOTE: We're going to delay the validation of the printer-port-name until
|
|
* later (StartDoc), as to handle cases where the server is down. If
|
|
* this is not done, we appear to hang at the UI as we attempt to
|
|
* send a request to the server.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* We have to return the correct router code to the spooler
|
|
*
|
|
* ROUTER_* status code:
|
|
*
|
|
* ROUTER_SUCCESS, phPrinter holds return handle, name cached
|
|
* ROUTER_UNKNOWN, printer not recognized, error updated
|
|
* ROUTER_STOP_ROUTING, printer recognized, but failure, error updated
|
|
*
|
|
*
|
|
\*****************************************************************************/
|
|
#ifdef WINNT32
|
|
|
|
BOOL PPOpenPrinter(
|
|
LPTSTR lpszPrnName,
|
|
LPHANDLE phPrinter,
|
|
LPPRINTER_DEFAULTS pDefaults)
|
|
{
|
|
PCINETMONPORT pIniPort;
|
|
BOOL bRet = FALSE;
|
|
DWORD dwRet = ROUTER_UNKNOWN;
|
|
DWORD dwLE;
|
|
BOOL bXcv = FALSE;
|
|
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPOpenPrinter: Name(%s)"), lpszPrnName));
|
|
|
|
semEnterCrit();
|
|
|
|
// Open the port for the printer, and create the true
|
|
// printer handle.
|
|
//
|
|
|
|
if (pIniPort = gpInetMon->InetmonOpenPort(lpszPrnName, &bXcv)) {
|
|
|
|
if (!bXcv) {
|
|
// Not an XcvOpen call
|
|
|
|
if (*phPrinter = _ppprn_make_hprinter(pIniPort, lpszPrnName)) {
|
|
|
|
if (__ppprn_inc_user_refcout( *phPrinter ) != MAXDWORD ) {
|
|
dwRet = ROUTER_SUCCESS;
|
|
} else {
|
|
_ppprn_free_hprinter( *phPrinter );
|
|
// This will also free the hUser
|
|
*phPrinter = NULL;
|
|
// Make sure we don't return anything and the router stops.
|
|
dwRet = ROUTER_STOP_ROUTING;
|
|
}
|
|
|
|
} else {
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
gpInetMon->InetmonClosePort(pIniPort, NULL );
|
|
dwRet = ROUTER_STOP_ROUTING;
|
|
}
|
|
}
|
|
else {
|
|
// XcvOpen call
|
|
|
|
if (*phPrinter = _ppprn_make_xcv_hprinter(pIniPort)) {
|
|
dwRet = ROUTER_SUCCESS;
|
|
}
|
|
else {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
//
|
|
// We don't need to dec-ref port since we never add-ref in XCV Open
|
|
//
|
|
|
|
dwRet = ROUTER_STOP_ROUTING;
|
|
}
|
|
}
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPOpenPrinter : Return Value(%d), LastError(%d)"), dwRet, GetLastError()));
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
#else
|
|
// Win9X Code
|
|
|
|
BOOL PPOpenPrinter(
|
|
LPTSTR lpszPrnName,
|
|
LPHANDLE phPrinter,
|
|
LPPRINTER_DEFAULTS pDefaults)
|
|
{
|
|
PCINETMONPORT pIniPort;
|
|
BOOL bRet = FALSE;
|
|
DWORD dwRet = ROUTER_UNKNOWN;
|
|
DWORD dwLE;
|
|
BOOL bXcv = FALSE;
|
|
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPOpenPrinter: Name(%s)"), lpszPrnName));
|
|
|
|
semEnterCrit();
|
|
|
|
// Open the port for the printer, and create the true
|
|
// printer handle.
|
|
//
|
|
if ((pIniPort = gpInetMon->InetmonOpenPort(lpszPrnName, &bXcv)) && !bXcv) {
|
|
|
|
if (*phPrinter = _ppprn_make_hprinter(pIniPort, lpszPrnName)) {
|
|
|
|
dwRet = ROUTER_SUCCESS;
|
|
|
|
} else {
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
|
|
gpInetMon->InetmonClosePort(pIniPort, NULL );
|
|
}
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPOpenPrinter : Return Value(%d), LastError(%d)"), dwRet, GetLastError()));
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************\
|
|
* PPStartDocPrinter
|
|
*
|
|
* Ends a print job on the specified printer. Returns a print job ID if
|
|
* successful. Otherwise, it returns zero.
|
|
*
|
|
\*****************************************************************************/
|
|
DWORD PPStartDocPrinter(
|
|
HANDLE hPrinter,
|
|
DWORD dwLevel,
|
|
LPBYTE pDocInfo)
|
|
{
|
|
PCINETMONPORT pIniPort = NULL;
|
|
PJOBMAP pjmJob;
|
|
DWORD idJob = 0;
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPStartDocPrinter: Printer(%08lX) dwLevel(%d)"), hPrinter, dwLevel));
|
|
|
|
|
|
semEnterCrit();
|
|
|
|
if (pIniPort = utlValidatePrinterHandle(hPrinter)) {
|
|
|
|
// Look at the support levels, then do the StartDocPrinter on
|
|
// the port.
|
|
//
|
|
switch (dwLevel) {
|
|
|
|
case PRINT_LEVEL_1:
|
|
|
|
// Serialize access to the port. Don't allow startdoc on
|
|
// the printer if one is already in progress.
|
|
//
|
|
if (PP_ChkStatus(hPrinter, PP_STARTDOC)) {
|
|
|
|
SetLastError(ERROR_BUSY);
|
|
|
|
} else {
|
|
|
|
if (pjmJob = PP_OpenJobInfo(hPrinter, pIniPort, ((PDOC_INFO_1) pDocInfo)->pDocName)) {
|
|
|
|
// Get the JobId for the start-doc, then set the info
|
|
// into the printer-handle.
|
|
//
|
|
if (pIniPort->StartDocPort(dwLevel, pDocInfo, pjmJob)) {
|
|
|
|
idJob = pjmJobId(pjmJob, PJM_LOCALID);
|
|
|
|
PP_ClrStatus(hPrinter, PP_CANCELLED);
|
|
PP_SetStatus(hPrinter, (PP_STARTDOC | PP_FIRSTWRITE));
|
|
|
|
} else {
|
|
|
|
PP_CloseJobInfo(hPrinter);
|
|
}
|
|
|
|
} else {
|
|
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DBG_MSG(DBG_LEV_WARN, (TEXT("Warn: PPStartDocPrinter: Invalid Level (%d)"), dwLevel));
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
return idJob;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* PPStartPagePrinter
|
|
*
|
|
* Informs the spool subsystem that a page is about to be started on this
|
|
* printer. Returns TRUE if successful. Otherwise, FALSE if an error occurs.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL PPStartPagePrinter(
|
|
HANDLE hPrinter)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPStartPagePrinter: hPrinter(%08lX)"), hPrinter));
|
|
|
|
semEnterCrit();
|
|
|
|
if (utlValidatePrinterHandle(hPrinter) != NULL) {
|
|
|
|
if (PP_ChkStatus(hPrinter, PP_CANCELLED))
|
|
SetLastError(ERROR_PRINT_CANCELLED);
|
|
else
|
|
bRet = TRUE;
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* PPWritePrinter
|
|
*
|
|
* Sends the data pointed to by pBuf to the specified printer. Returns TRUE
|
|
* if successful. Otherwise, it returns FALSE.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL PPWritePrinter(
|
|
HANDLE hPrinter,
|
|
LPVOID lpvBuf,
|
|
DWORD cbBuf,
|
|
LPDWORD pcbWr)
|
|
{
|
|
PCINETMONPORT pIniPort;
|
|
PJOBMAP pjmJob;
|
|
BOOL bRet = FALSE;
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPWritePrinter: Printer(%08lX)"), hPrinter));
|
|
|
|
|
|
semEnterCrit();
|
|
|
|
*pcbWr = 0;
|
|
|
|
if (pIniPort = utlValidatePrinterHandle(hPrinter)) {
|
|
|
|
// If we're in a start-doc, and end-doc hasn't been
|
|
// called, then we can still write to the port.
|
|
//
|
|
if ( PP_ChkStatus(hPrinter, PP_STARTDOC) &&
|
|
!PP_ChkStatus(hPrinter, PP_ENDDOC)) {
|
|
|
|
|
|
pjmJob = PP_GetJobInfo(hPrinter);
|
|
|
|
|
|
// If we received a SetJob(CANCEL), during the print-spooling
|
|
// process, then we need to mark our job as done.
|
|
//
|
|
if (!pjmChkState(pjmJob, PJM_CANCEL)) {
|
|
|
|
bRet = pIniPort->WritePort(pjmJob, (LPBYTE) lpvBuf, cbBuf, pcbWr);
|
|
|
|
#ifdef WINNT32
|
|
pjmAddJobSize (pjmJob, *pcbWr);
|
|
|
|
// We do not need to update the cache since the job info is stored locally
|
|
//
|
|
RefreshNotificationPort (pIniPort);
|
|
#endif
|
|
|
|
} else {
|
|
|
|
bRet = TRUE;
|
|
|
|
if (pIniPort->AbortPort(pjmJob)) {
|
|
|
|
// If this call was successful, the job-info
|
|
// will have been freed. Therefore, it is OK
|
|
// to set the printer-jobreq to NULL.
|
|
//
|
|
PP_SetStatus(hPrinter, PP_CANCELLED);
|
|
PP_ClrStatus(hPrinter, PP_STARTDOC);
|
|
PP_CloseJobInfo(hPrinter);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if (PP_ChkStatus(hPrinter, PP_CANCELLED))
|
|
SetLastError(ERROR_PRINT_CANCELLED);
|
|
else
|
|
SetLastError(ERROR_SPL_NO_STARTDOC);
|
|
}
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
* PPSetPrinter
|
|
*
|
|
* Set printer command.
|
|
*
|
|
\*****************************************************************************/
|
|
BOOL PPSetPrinter(
|
|
HANDLE hPrinter,
|
|
DWORD dwLevel,
|
|
LPBYTE pbPrinter,
|
|
DWORD dwCmd)
|
|
{
|
|
PCINETMONPORT pIniPort;
|
|
BOOL bRet = FALSE;
|
|
|
|
DBG_MSG(DBG_LEV_CALLTREE, (TEXT("Call: PPSetPrinter: Printer(%08lX)"), hPrinter));
|
|
|
|
|
|
semEnterCrit();
|
|
|
|
if (pIniPort = utlValidatePrinterHandle(hPrinter)) {
|
|
|
|
#ifdef WINNT32
|
|
|
|
// Set printer parameters.
|
|
//
|
|
switch (dwLevel) {
|
|
|
|
case PRINT_LEVEL_0:
|
|
|
|
// Do not set parameters. (0) represents "no-command".
|
|
//
|
|
switch (dwCmd) {
|
|
|
|
case PRINTER_CONTROL_PAUSE:
|
|
case PRINTER_CONTROL_RESUME:
|
|
case PRINTER_CONTROL_PURGE:
|
|
bRet = ppprn_Set(pIniPort, dwCmd);
|
|
|
|
if (bRet) {
|
|
pIniPort->InvalidateGetPrinterCache ();
|
|
|
|
if (dwCmd == PRINTER_CONTROL_PURGE) {
|
|
//
|
|
// Clean job cache if the command is to cancel all documents
|
|
//
|
|
pIniPort->InvalidateEnumJobsCache ();
|
|
}
|
|
|
|
RefreshNotification((LPINET_HPRINTER)hPrinter);
|
|
}
|
|
|
|
break;
|
|
|
|
case 0:
|
|
bRet = TRUE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DBG_MSG(DBG_LEV_WARN, (TEXT("Warn: PPSetPrinter: Invalid Level (%d)"), dwLevel));
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
#else
|
|
|
|
if (dwCmd) {
|
|
|
|
// Do not set parameters. (0) represents "no-command".
|
|
//
|
|
switch (dwCmd) {
|
|
|
|
case PRINTER_CONTROL_PAUSE:
|
|
case PRINTER_CONTROL_RESUME:
|
|
case PRINTER_CONTROL_PURGE:
|
|
bRet = ppprn_Set(pIniPort, dwCmd);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (dwLevel) {
|
|
|
|
case PRINT_LEVEL_1:
|
|
case PRINT_LEVEL_2:
|
|
|
|
case PRINT_LEVEL_0:
|
|
default:
|
|
DBG_MSG(DBG_LEV_WARN, (TEXT("Warn: PPSetPrinter: Invalid Level (%d)"), dwLevel));
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
semLeaveCrit();
|
|
|
|
return bRet;
|
|
}
|