811 lines
18 KiB
C
811 lines
18 KiB
C
/*****************************************************************/
|
||
/** 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);
|
||
}
|
||
|