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.
811 lines
19 KiB
811 lines
19 KiB
/*****************************************************************/
|
|
/** Copyright(c) 1989 Microsoft Corporation. **/
|
|
/*****************************************************************/
|
|
|
|
//***
|
|
//
|
|
// Filename: job.c
|
|
//
|
|
// Description: This module contains the entry points for the AppleTalk
|
|
// monitor that manipulate jobs.
|
|
//
|
|
// The following are the functions contained in this module.
|
|
// All these functions are exported.
|
|
//
|
|
// StartDocPort
|
|
// ReadPort
|
|
// WritePort
|
|
// EndDocPort
|
|
// History:
|
|
//
|
|
// Aug 26,1992 frankb Initial version
|
|
// June 11,1993. NarenG Bug fixes/clean up
|
|
//
|
|
|
|
#include <windows.h>
|
|
#include <winspool.h>
|
|
#include <winsplp.h>
|
|
#include <winsock.h>
|
|
#include <atalkwsh.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <lmcons.h>
|
|
|
|
#include <prtdefs.h>
|
|
|
|
#include "atalkmon.h"
|
|
#include "atmonmsg.h"
|
|
#include <bltrc.h>
|
|
#include "dialogs.h"
|
|
|
|
//**
|
|
//
|
|
// Call: StartDocPort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// This routine is called by the print manager to
|
|
// mark the beginning of a job to be sent to the printer on
|
|
// this port. Any performance monitoring counts are cleared,
|
|
// a check is made to insure that the printer is still open,
|
|
//
|
|
// open issues:
|
|
//
|
|
// In order to allow for the stack to be shutdown when printing is not
|
|
// happening, the first access to the AppleTalk stack happens in this
|
|
// call. A socket is created and bound to a dynamic address, and an
|
|
// attempt to connect to the NBP name of the port is made here. If
|
|
// the connection succeeds, this routine returns TRUE. If it fails, the
|
|
// socket is cleaned up and the routine returns FALSE. It is assumed that
|
|
// Winsockets will set the appropriate Win32 failure codes.
|
|
//
|
|
// Do we want to do any performance stuff? If so, what?
|
|
//
|
|
BOOL
|
|
StartDocPort(
|
|
IN HANDLE hPort,
|
|
IN LPWSTR pPrinterName,
|
|
IN DWORD JobId,
|
|
IN DWORD Level,
|
|
IN LPBYTE pDocInfo
|
|
)
|
|
{
|
|
PATALKPORT pWalker;
|
|
PATALKPORT pPort;
|
|
DWORD dwRetCode;
|
|
|
|
DBGPRINT(("Entering StartDocPort\n")) ;
|
|
|
|
pPort = (PATALKPORT)hPort;
|
|
|
|
if (pPort == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Make sure the job is valid and not marked for deletion
|
|
//
|
|
|
|
dwRetCode = ERROR_UNKNOWN_PORT;
|
|
|
|
WaitForSingleObject(hmutexPortList, INFINITE);
|
|
|
|
for (pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext)
|
|
{
|
|
if (pWalker == pPort)
|
|
{
|
|
if (pWalker->fPortFlags & SFM_PORT_IN_USE)
|
|
dwRetCode = ERROR_DEVICE_IN_USE;
|
|
else
|
|
{
|
|
dwRetCode = NO_ERROR;
|
|
pWalker->fPortFlags |= SFM_PORT_IN_USE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
ReleaseMutex(hmutexPortList);
|
|
|
|
if (dwRetCode != NO_ERROR)
|
|
{
|
|
SetLastError(dwRetCode);
|
|
return(FALSE);
|
|
}
|
|
|
|
do
|
|
{
|
|
//
|
|
// get a handle to the printer. Used to delete job and
|
|
// update job status
|
|
//
|
|
|
|
if (!OpenPrinter(pPrinterName, &(pWalker->hPrinter), NULL))
|
|
{
|
|
dwRetCode = GetLastError();
|
|
break;
|
|
}
|
|
|
|
pWalker->dwJobId = JobId;
|
|
|
|
pWalker->fJobFlags |= (SFM_JOB_FIRST_WRITE | SFM_JOB_OPEN_PENDING);
|
|
|
|
//
|
|
// open and bind status socket
|
|
//
|
|
|
|
dwRetCode = OpenAndBindAppleTalkSocket(&(pWalker->sockStatus));
|
|
|
|
if (dwRetCode != NO_ERROR)
|
|
{
|
|
ReportEvent(
|
|
hEventLog,
|
|
EVENTLOG_WARNING_TYPE,
|
|
EVENT_CATEGORY_USAGE,
|
|
EVENT_ATALKMON_STACK_NOT_STARTED,
|
|
NULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL) ;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// get a socket for I/O
|
|
//
|
|
|
|
dwRetCode = OpenAndBindAppleTalkSocket(&(pWalker->sockIo));
|
|
|
|
if (dwRetCode != NO_ERROR)
|
|
{
|
|
ReportEvent(
|
|
hEventLog,
|
|
EVENTLOG_WARNING_TYPE,
|
|
EVENT_CATEGORY_USAGE,
|
|
EVENT_ATALKMON_STACK_NOT_STARTED,
|
|
NULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
break;
|
|
}
|
|
} while(FALSE);
|
|
|
|
if (dwRetCode != NO_ERROR)
|
|
{
|
|
if (pWalker->hPrinter != INVALID_HANDLE_VALUE)
|
|
ClosePrinter(pWalker->hPrinter);
|
|
|
|
if (pWalker->sockStatus != INVALID_SOCKET)
|
|
closesocket(pWalker->sockStatus);
|
|
|
|
if (pWalker->sockIo != INVALID_SOCKET)
|
|
closesocket(pWalker->sockIo);
|
|
|
|
pWalker->hPrinter = INVALID_HANDLE_VALUE;
|
|
pWalker->dwJobId = 0;
|
|
pWalker->fJobFlags = 0;
|
|
|
|
WaitForSingleObject(hmutexPortList, INFINITE);
|
|
pWalker->fPortFlags &= ~SFM_PORT_IN_USE;
|
|
ReleaseMutex(hmutexPortList);
|
|
|
|
SetLastError(dwRetCode);
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: ReadPort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// Synchronously reads data from the printer.
|
|
//
|
|
// open issues:
|
|
// the DLC implementation does not implement reads.
|
|
// The local implementation implements reads with generic ReadFile
|
|
// semantics. It's not clear from the winhelp file if ReadPort
|
|
// should return an error if there is no data to read from
|
|
// the printer. Also, since PAP is read driven, there will be no
|
|
// data waiting until a read is posted. Should we pre-post a
|
|
// read on StartDocPort?
|
|
//
|
|
BOOL
|
|
ReadPort(
|
|
IN HANDLE hPort,
|
|
IN LPBYTE pBuffer,
|
|
IN DWORD cbBuffer,
|
|
IN LPDWORD pcbRead
|
|
){
|
|
|
|
DBGPRINT(("Entering ReadPort\n")) ;
|
|
|
|
//
|
|
// if data not available, wait up to a few seconds for a read to complete
|
|
//
|
|
|
|
//
|
|
// copy requested amount of data to caller's buffer
|
|
//
|
|
|
|
//
|
|
// if all data copied, post another read
|
|
//
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: WritePort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// Synchronously writes data to the printer.
|
|
//
|
|
BOOL
|
|
WritePort(
|
|
IN HANDLE hPort,
|
|
IN LPBYTE pBuffer,
|
|
IN DWORD cbBuffer,
|
|
IN LPDWORD pcbWritten
|
|
)
|
|
{
|
|
LPBYTE pchTemp;
|
|
PATALKPORT pPort;
|
|
DWORD dwIndex;
|
|
DWORD dwRetCode;
|
|
INT wsErr;
|
|
fd_set writefds;
|
|
fd_set readfds;
|
|
struct timeval timeout;
|
|
INT Flags = 0;
|
|
LPBYTE pBufToSend;
|
|
DWORD cbTotalBytesToSend;
|
|
BOOLEAN fJobCameFromMac;
|
|
BOOLEAN fPostScriptJob;
|
|
|
|
|
|
pPort = (PATALKPORT)hPort;
|
|
|
|
// Set this to zero. We add incrementally later.
|
|
*pcbWritten = 0;
|
|
|
|
if (pPort == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
|
|
pBufToSend = pBuffer;
|
|
cbTotalBytesToSend = cbBuffer;
|
|
|
|
//
|
|
// Maximum number of bytes we can write in one send is 4K. This is the
|
|
// limit in the AppleTalk (PAP) protocol.
|
|
//
|
|
|
|
if (cbTotalBytesToSend > 4096)
|
|
{
|
|
cbTotalBytesToSend = 4096;
|
|
}
|
|
|
|
// If we have not connected to the printer yet.
|
|
|
|
if (pPort->fJobFlags & SFM_JOB_OPEN_PENDING)
|
|
{
|
|
// Make sure that the capture thread is done with this job.
|
|
|
|
WaitForSingleObject(pPort->hmutexPort, INFINITE);
|
|
ReleaseMutex(pPort->hmutexPort);
|
|
|
|
// set status to connecting
|
|
|
|
DBGPRINT(("no connection yet, retry connect\n")) ;
|
|
|
|
dwRetCode = ConnectToPrinter(pPort, ATALKMON_DEFAULT_TIMEOUT);
|
|
|
|
if (dwRetCode != NO_ERROR)
|
|
{
|
|
DBGPRINT(("Connect returns %d\n", dwRetCode)) ;
|
|
|
|
//
|
|
// Wait 15 seconds before trying to reconnect. Each
|
|
// ConnectToPrinter does an expensive NBPLookup
|
|
//
|
|
|
|
Sleep(ATALKMON_DEFAULT_TIMEOUT*3);
|
|
|
|
*pcbWritten = 0;
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
else
|
|
{
|
|
pPort->fJobFlags &= ~SFM_JOB_OPEN_PENDING;
|
|
|
|
WaitForSingleObject(hmutexPortList, INFINITE);
|
|
pPort->fPortFlags |= SFM_PORT_POST_READ;
|
|
ReleaseMutex(hmutexPortList);
|
|
|
|
SetEvent(hevPrimeRead);
|
|
|
|
SetPrinterStatus(pPort, wchPrinting);
|
|
}
|
|
}
|
|
|
|
// if first write, determine filter control. We filter
|
|
// CTRL-D from non-mac jobs, and leave them in from Macintosh
|
|
// originated jobs
|
|
if (pPort->fJobFlags & SFM_JOB_FIRST_WRITE)
|
|
{
|
|
DBGPRINT(("first write for this job. Do filter test\n")) ;
|
|
|
|
fJobCameFromMac = IsJobFromMac(pPort);
|
|
|
|
// Consume the FILTERCONTROL string
|
|
//
|
|
// the older spoolers will put this string in: go ahead and leave
|
|
// this code in so if this job came from an older SFM spooler, we
|
|
// strip that line!
|
|
//
|
|
if ((cbTotalBytesToSend >= SIZE_FC) &&
|
|
(strncmp(pBufToSend, FILTERCONTROL, SIZE_FC) == 0))
|
|
{
|
|
*pcbWritten += SIZE_FC;
|
|
pBufToSend += SIZE_FC;
|
|
cbTotalBytesToSend -= SIZE_FC;
|
|
fJobCameFromMac = TRUE;
|
|
}
|
|
else if ((cbTotalBytesToSend >= SIZE_FCOLD) &&
|
|
strncmp(pBufToSend, FILTERCONTROL_OLD, SIZE_FCOLD) == 0)
|
|
{
|
|
*pcbWritten += SIZE_FCOLD;
|
|
pBufToSend += SIZE_FCOLD;
|
|
cbTotalBytesToSend -= SIZE_FCOLD;
|
|
fJobCameFromMac = TRUE;
|
|
}
|
|
|
|
//
|
|
// Need for hack: there are two reasons:
|
|
// 1) control characters (most commonly ctrl-d, but ctrl-c, etc. too)
|
|
// cause postscript printers to choke. we need to "filter" them out
|
|
// 2) if we're printing to a dual-mode HP printer then it's
|
|
// driver puts in a bunch of PJL commands that causes printer to go to
|
|
// postscript mode etc. It works great if this goes over lpt or com port
|
|
// but if it goes over appletalk (which is what we do) then the printer
|
|
// expects *only* postscript and seeing the PJL commands, it chokes!
|
|
// The output that goes out to the printer looks like this:
|
|
//
|
|
// <....separator page data....>
|
|
//
|
|
// $%-12345X@PJL JOB
|
|
// @PJL SET RESOLUTION=600
|
|
// @PJL ENTER LANGUAGE = POSTSCRIPT
|
|
// %!PS-Adobe-3.0
|
|
//
|
|
// <.... Postscript data....>
|
|
//
|
|
// $%-12345X@PJL EOJ
|
|
//
|
|
// (The escape character is denoted by the '$' sign above.)
|
|
// The first 3 lines and the last line are the ones that cause problem
|
|
//
|
|
// Since it's a pain in the neck to parse all of the data and try and
|
|
// remove the unwanted characters, we just prepend a few postscript
|
|
// commands to the data that tell the printer to ignore ctrl-d,
|
|
// ctrl-c etc. characters, and to ignore any line(s) starting with @PJL.
|
|
//
|
|
|
|
//
|
|
// Begin filtering hack
|
|
//
|
|
|
|
//
|
|
// make sure the string doesn't already exist (it can if the job goes
|
|
// monitor->spooler->monitor->printer instead of monitor->printer)
|
|
//
|
|
// Again, older SFM monitors would prepend this string: since we got a
|
|
// chance here, strip that out!
|
|
//
|
|
if ((cbTotalBytesToSend >= SIZE_PS_HEADER) &&
|
|
strncmp(pBufToSend, PS_HEADER, SIZE_PS_HEADER) == 0)
|
|
{
|
|
*pcbWritten += SIZE_PS_HEADER;
|
|
pBufToSend += SIZE_PS_HEADER;
|
|
cbTotalBytesToSend -= SIZE_PS_HEADER;
|
|
}
|
|
|
|
//
|
|
// WfW starts its job with a CTRL_D. Replace it with a space
|
|
//
|
|
if (pBufToSend[0] == CTRL_D)
|
|
{
|
|
*pcbWritten += 1;
|
|
pBufToSend += 1;
|
|
cbTotalBytesToSend -= 1;
|
|
}
|
|
|
|
//
|
|
// see if this job has a hdr that looks like a conventional postscript hdr
|
|
//
|
|
fPostScriptJob = TRUE;
|
|
|
|
if (cbTotalBytesToSend > 2)
|
|
{
|
|
if (pBufToSend[0] == '%' && pBufToSend[1] == '!')
|
|
{
|
|
fPostScriptJob = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fPostScriptJob = FALSE;
|
|
}
|
|
}
|
|
//
|
|
// Mac always sends a postscript job. Also, we peeked at the data to
|
|
// see if we recognize a postscript hdr. If the job came from a non-Mac
|
|
// client and doesn't look like a conventional postscript job, send a
|
|
// control string telling the printer to ignore the PJL commands.
|
|
//
|
|
if (!fJobCameFromMac && !fPostScriptJob)
|
|
{
|
|
//
|
|
// Now send the PS header
|
|
//
|
|
FD_ZERO(&writefds);
|
|
FD_SET(pPort->sockIo, &writefds);
|
|
|
|
//
|
|
// can I send?
|
|
//
|
|
timeout.tv_sec = ATALKMON_DEFAULT_TIMEOUT_SEC;
|
|
timeout.tv_usec = 0;
|
|
|
|
wsErr = select(0, NULL, &writefds, NULL, &timeout);
|
|
|
|
if (wsErr == 1)
|
|
{
|
|
// can send, send the data & set return count
|
|
wsErr = send(pPort->sockIo,
|
|
PS_HEADER,
|
|
SIZE_PS_HEADER,
|
|
MSG_PARTIAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// End filtering hack
|
|
//
|
|
|
|
pPort->fJobFlags &= ~SFM_JOB_FIRST_WRITE;
|
|
}
|
|
|
|
|
|
// many postscript jobs from pc's end with a ctrl-d which we don't want to send.
|
|
// Since we are given only 1 byte and it is ctrl-d, we assume (FOR NOW) that it's the
|
|
// last byte of the job. So lie to the spooler that we sent it.
|
|
//
|
|
if (cbTotalBytesToSend == 1)
|
|
{
|
|
if (pBufToSend[0] == CTRL_D)
|
|
{
|
|
*pcbWritten = 1;
|
|
pPort->OnlyOneByteAsCtrlD++;
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
cbTotalBytesToSend += 1; // we subtract 1 in the next line, so adjust here
|
|
}
|
|
}
|
|
|
|
//
|
|
// if this job is for dual-mode printer, there is that $%-12345X@PJL EOJ command
|
|
// at the end. There is a ctrl-d just before that (which is really the end
|
|
// of the actual job).
|
|
//
|
|
if (cbTotalBytesToSend > PJL_ENDING_COMMAND_LEN)
|
|
{
|
|
if (strncmp(&pBufToSend[cbTotalBytesToSend - PJL_ENDING_COMMAND_LEN],
|
|
PJL_ENDING_COMMAND,
|
|
PJL_ENDING_COMMAND_LEN) == 0)
|
|
{
|
|
if (pBufToSend[cbTotalBytesToSend-PJL_ENDING_COMMAND_LEN-1] == CTRL_D)
|
|
{
|
|
pBufToSend[cbTotalBytesToSend-PJL_ENDING_COMMAND_LEN-1] = CR;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// send 1 less byte so eventually we'll catch the last byte (and see if it's ctrl-D)
|
|
//
|
|
cbTotalBytesToSend -= 1;
|
|
|
|
|
|
//
|
|
// Earlier we may have got just 1 byte which was ctrl-D but was not really the last byte!
|
|
// This is a very rare case, but in theory possible. If that's what happened, send
|
|
// that one ctrl-D byte now, and continue on with the rest of the job
|
|
// (Actually being paranoid here and making provision for the spooler handing us a series
|
|
// of ctrl-D bytes, 1 at a time!!)
|
|
//
|
|
if (pPort->OnlyOneByteAsCtrlD != 0)
|
|
{
|
|
BYTE TmpArray[20];
|
|
DWORD i;
|
|
|
|
i=0;
|
|
while (i < pPort->OnlyOneByteAsCtrlD)
|
|
{
|
|
TmpArray[i++] = CTRL_D;
|
|
}
|
|
|
|
FD_ZERO(&writefds);
|
|
FD_SET(pPort->sockIo, &writefds);
|
|
|
|
timeout.tv_sec = ATALKMON_DEFAULT_TIMEOUT_SEC;
|
|
timeout.tv_usec = 0;
|
|
|
|
wsErr = select(0, NULL, &writefds, NULL, &timeout);
|
|
|
|
if (wsErr == 1)
|
|
{
|
|
TmpArray[0] = CTRL_D;
|
|
wsErr = send(pPort->sockIo,
|
|
TmpArray,
|
|
pPort->OnlyOneByteAsCtrlD,
|
|
MSG_PARTIAL);
|
|
}
|
|
|
|
pPort->OnlyOneByteAsCtrlD = 0;
|
|
}
|
|
|
|
//
|
|
// can I send?
|
|
//
|
|
FD_ZERO(&writefds);
|
|
FD_SET(pPort->sockIo, &writefds);
|
|
|
|
timeout.tv_sec = ATALKMON_DEFAULT_TIMEOUT_SEC;
|
|
timeout.tv_usec = 0;
|
|
|
|
wsErr = select(0, NULL, &writefds, NULL, &timeout);
|
|
|
|
if (wsErr == 1)
|
|
{
|
|
// can send, send the data & set return count
|
|
wsErr = send(pPort->sockIo,
|
|
pBufToSend,
|
|
cbTotalBytesToSend,
|
|
MSG_PARTIAL);
|
|
|
|
if (wsErr != SOCKET_ERROR)
|
|
{
|
|
*pcbWritten += cbTotalBytesToSend;
|
|
|
|
if (pPort->fJobFlags & SFM_JOB_ERROR)
|
|
{
|
|
pPort->fJobFlags &= ~SFM_JOB_ERROR;
|
|
SetPrinterStatus(pPort, wchPrinting);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// can I read? - check for disconnect
|
|
//
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(pPort->sockIo, &readfds);
|
|
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 0;
|
|
|
|
wsErr = select(0, &readfds, NULL, NULL, &timeout);
|
|
|
|
if (wsErr == 1)
|
|
{
|
|
wsErr = WSARecvEx(pPort->sockIo,
|
|
pPort->pReadBuffer,
|
|
PAP_DEFAULT_BUFFER,
|
|
&Flags);
|
|
|
|
if (wsErr == SOCKET_ERROR)
|
|
{
|
|
dwRetCode = GetLastError();
|
|
|
|
DBGPRINT(("recv returns %d\n", dwRetCode));
|
|
|
|
if ((dwRetCode == WSAEDISCON) || (dwRetCode == WSAENOTCONN))
|
|
{
|
|
pPort->fJobFlags |= SFM_JOB_DISCONNECTED;
|
|
|
|
//
|
|
// Try to restart the job
|
|
//
|
|
|
|
SetJob(pPort->hPrinter,
|
|
pPort->dwJobId,
|
|
0,
|
|
NULL,
|
|
JOB_CONTROL_RESTART);
|
|
|
|
SetLastError(ERROR_DEV_NOT_EXIST);
|
|
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wsErr < PAP_DEFAULT_BUFFER)
|
|
pPort->pReadBuffer[wsErr] = '\0';
|
|
else pPort->pReadBuffer[PAP_DEFAULT_BUFFER-1] = '\0';
|
|
|
|
DBGPRINT(("recv returns %s\n", pPort->pReadBuffer));
|
|
|
|
pPort->fJobFlags |= SFM_JOB_ERROR;
|
|
|
|
ParseAndSetPrinterStatus(pPort);
|
|
}
|
|
|
|
WaitForSingleObject(hmutexPortList, INFINITE);
|
|
pPort->fPortFlags |= SFM_PORT_POST_READ;
|
|
ReleaseMutex(hmutexPortList);
|
|
|
|
SetEvent(hevPrimeRead);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: EndDocPort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// This routine is called to mark the end of the
|
|
// print job. The spool file for the job is deleted by
|
|
// this routine.
|
|
//
|
|
// open issues:
|
|
// Do we want to do performance stuff? If so, now's the time
|
|
// to save off any performance counts.
|
|
//
|
|
BOOL
|
|
EndDocPort(
|
|
IN HANDLE hPort
|
|
){
|
|
PATALKPORT pPort;
|
|
fd_set writefds;
|
|
fd_set readfds;
|
|
struct timeval timeout;
|
|
INT wsErr;
|
|
INT Flags = 0;
|
|
|
|
DBGPRINT(("Entering EndDocPort\n")) ;
|
|
|
|
pPort = (PATALKPORT)hPort;
|
|
|
|
if (pPort == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// send the last write
|
|
//
|
|
|
|
FD_ZERO(&writefds);
|
|
FD_SET(pPort->sockIo, &writefds);
|
|
|
|
//
|
|
// If the job was not able to connect to the printer.
|
|
|
|
if ((pPort->fJobFlags & (SFM_JOB_OPEN_PENDING | SFM_JOB_DISCONNECTED)) == 0)
|
|
{
|
|
|
|
timeout.tv_sec = 90;
|
|
timeout.tv_usec = 0;
|
|
|
|
wsErr = select(0, NULL, &writefds, NULL, &timeout);
|
|
|
|
if (wsErr == 1)
|
|
{
|
|
//
|
|
// Send EOF
|
|
//
|
|
send(pPort->sockIo, NULL, 0, 0);
|
|
}
|
|
|
|
//
|
|
// Our socket is non-blocking. If we close down the socket, we could potentially
|
|
// abort the last page. A good thing to do is to wait for a reasonable amount of
|
|
// time out for the printer to send EOF, or request for more data.
|
|
//
|
|
FD_ZERO(&writefds);
|
|
FD_SET(pPort->sockIo, &writefds);
|
|
FD_ZERO(&readfds);
|
|
FD_SET(pPort->sockIo, &readfds);
|
|
|
|
timeout.tv_sec = 30;
|
|
timeout.tv_usec = 0;
|
|
wsErr = select(0, &readfds, &writefds, NULL, &timeout);
|
|
|
|
if (wsErr == 1 && FD_ISSET(pPort->sockIo, &readfds))
|
|
{
|
|
// read printer's EOF. We don't care about an error here
|
|
wsErr = WSARecvEx(pPort->sockIo, pPort->pReadBuffer, PAP_DEFAULT_BUFFER, &Flags);
|
|
}
|
|
}
|
|
|
|
//
|
|
// delete the print job
|
|
//
|
|
|
|
if (pPort->hPrinter != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!SetJob(pPort->hPrinter,
|
|
pPort->dwJobId,
|
|
0,
|
|
NULL,
|
|
JOB_CONTROL_SENT_TO_PRINTER))
|
|
DBGPRINT(("fail to setjob for delete with %d\n", GetLastError())) ;
|
|
|
|
ClosePrinter(pPort->hPrinter);
|
|
|
|
pPort->hPrinter = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// close the PAP connections
|
|
//
|
|
|
|
if (pPort->sockStatus != INVALID_SOCKET)
|
|
{
|
|
closesocket(pPort->sockStatus);
|
|
pPort->sockStatus = INVALID_SOCKET;
|
|
}
|
|
|
|
|
|
if (pPort->sockIo != INVALID_SOCKET)
|
|
{
|
|
closesocket(pPort->sockIo);
|
|
pPort->sockIo = INVALID_SOCKET;
|
|
}
|
|
|
|
pPort->dwJobId = 0;
|
|
pPort->fJobFlags = 0;
|
|
pPort->OnlyOneByteAsCtrlD = 0;
|
|
|
|
WaitForSingleObject(hmutexPortList, INFINITE);
|
|
pPort->fPortFlags &= ~SFM_PORT_IN_USE;
|
|
ReleaseMutex(hmutexPortList);
|
|
|
|
return(TRUE);
|
|
}
|
|
|