windows-nt/Source/XPSP1/NT/net/tcpip/services/lpd/job.c

717 lines
22 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*************************************************************************
* Microsoft Windows NT *
* *
* Copyright(c) Microsoft Corp., 1994 *
* *
* Revision History: *
* *
* Jan. 24,94 Koti Created *
* Jan. 29,96 JBallard Modified *
* *
* Description: *
* *
* This file contains functions for carrying out LPD printing *
* *
*************************************************************************/
#include "lpd.h"
extern FILE * pErrFile; // Debug output log file.
BOOL GetSpoolFileName(
HANDLE hPrinter,
PSOCKCONN pscConn,
PCHAR *ppwchSpoolPath
);
VOID CleanupConn( PSOCKCONN pscConn);
/*****************************************************************************
* *
* ProcessJob(): *
* This function receives the subcommand from the client to expect the *
* control file, then accepts the control file, then the subcommand to *
* expect the data file, then accepts the data and then hands it over to *
* the spooler to print. *
* If the very first subcommand was to abort the job, we just return. *
* *
* Returns: *
* Nothing *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* History: *
* Jan.24 94 Koti Created *
* *
*****************************************************************************/
VOID ProcessJob( PSOCKCONN pscConn )
{
// the main functionality of LPD implemented in this function!
CHAR chSubCmdCode;
DWORD cbTotalDataLen;
DWORD cbBytesSpooled;
DWORD cbBytesToRead;
DWORD cbDataBufLen;
DWORD cbBytesRemaining;
DWORD dwErrcode;
CHAR chAck;
HANDLE hDFile;
PDFILE_ENTRY pDFile;
PCHAR lpFileName;
PCHAR pchDataBuf;
DWORD ClientCmd;
// initialize the printer that the client wants to use
#ifdef DBG
if( !pscConn || !pscConn->pchPrinterName
|| strstr( pscConn->pchPrinterName, "debug" )
){
print__sockconn( "ProcessJob: entered", pscConn );
}
#endif
if ( InitializePrinter( pscConn ) != NO_ERROR )
{
PCHAR aszStrings[2];
aszStrings[0] = pscConn->pchPrinterName;
aszStrings[1] = pscConn->szIPAddr;
LpdReportEvent( LPDLOG_NONEXISTENT_PRINTER, 2, aszStrings, 0 );
pscConn->fLogGenericEvent = FALSE;
return; // fatal error: exit
}
// thank the client for the command. If we couldn't send, quit
if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
{
LPD_DEBUG( "ProcessJob(): couldn't ACK to \"receive job\"\n" );
return;
}
// 2 subcommands expected: "receive control file" and "receive data file"
// They can come in any order. (One of the two subcommands can also be
// "abort this job" in which case we abort the job and return).
for ( ; ; )
{
// don't need the previous one (in fact, pchCommand field is reused)
if ( pscConn->pchCommand != NULL )
{
LocalFree( pscConn->pchCommand );
pscConn->pchCommand = NULL;
}
// get the first subcommand from the client
// ------------------------------ N = 02, 03, or 01
// | N | Count | SP | Name | LF | Count => control file length
// ------------------------------ Name => controlfile name
ClientCmd = GetCmdFromClient( pscConn );
switch ( ClientCmd )
{
case CONNECTION_CLOSED:
// performance enhancement: close the socket here and then start
// printing: printing could take several seconds,
// so don't tie up client
if ( pscConn->sSock != INVALID_SOCKET )
{
SureCloseSocket( pscConn->sSock );
pscConn->sSock = INVALID_SOCKET;
}
// if we came this far, everything went as planned.
// tell spooler that we are done spooling: go ahead and print!
PrintData( pscConn );
pscConn->wState = LPDS_ALL_WENT_WELL;
return;
case NO_ERROR:
// Not yet done, back to outer loop.
break;
case SOCKET_ERROR:
default:
// If we didn't get a subcommand from client, it's bad news!
// client died or something catastophic like that!
LOGIT(("ProcessJob:GetCmdFromClient %d failed %d.\n",
ClientCmd, GetLastError() ));
return; // the thread exits without doing anything
}
chSubCmdCode = pscConn->pchCommand[0];
switch (chSubCmdCode) {
case LPDCS_RECV_CFILE: // N = 02 ("receive control file")
// client is going to give us a control file: prepare for it
pscConn->wState = LPDSS_RECVD_CFILENAME;
// get the controlfile name, file size out of the command
if ( ParseSubCommand( pscConn, &cbTotalDataLen, &lpFileName ) != NO_ERROR )
{
PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
LpdReportEvent( LPDLOG_BAD_FORMAT, 1, aszStrings, 0 );
pscConn->fLogGenericEvent = FALSE;
return; // fatal error: exit
}
// tell client we got the name of the controlfile ok
if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
{
return; // fatal error: exit
}
// Get the control file (we already know how big it is)
if ( GetControlFileFromClient( pscConn, cbTotalDataLen, lpFileName ) != NO_ERROR )
{
LPD_DEBUG( "GetControlFileFromClient() failed in ProcessJob()\n" );
return;
}
pscConn->wState = LPDSS_RECVD_CFILE;
// tell client we got the controlfile and things look good so far!
if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
{
LOGIT(("ProcessJob:%d: ReplyToClient failed %d\n",
__LINE__, GetLastError() ));
return; // fatal error: exit
}
break;
case LPDCS_RECV_DFILE: // N = 03 ("receive data file")
pscConn->wState = LPDSS_RECVD_DFILENAME;
// tell client we got the name of the datafile ok
if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
{
LOGIT(("ProcessJob:%d: ReplyToClient failed %d\n",
__LINE__, GetLastError() ));
return; // fatal error: exit
}
// get the datafile name, data size out of the command
if ( ParseSubCommand( pscConn, &cbTotalDataLen, &lpFileName ) != NO_ERROR )
{
PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
LpdReportEvent( LPDLOG_BAD_FORMAT, 1, aszStrings, 0 );
pscConn->fLogGenericEvent = FALSE;
LOGIT(("ProcessJob:%d: ParseSubCommand failed %d\n",
__LINE__, GetLastError() ));
return; // fatal error: exit
}
// at this point, we know exactly how much data is coming.
// Allocate buffer to hold the data. If data is more than
// LPD_BIGBUFSIZE, keep reading and spooling several times
// over until data is done
pscConn->wState = LPDSS_SPOOLING;
pDFile = LocalAlloc( LMEM_FIXED, sizeof(DFILE_ENTRY) );
if (pDFile == NULL) {
LocalFree( lpFileName );
LOGIT(("ProcessJob:%d: LocalAlloc failed %d\n",
__LINE__, GetLastError() ));
return; // Fatal Error
}
pDFile->cbDFileLen = cbTotalDataLen;
pDFile->pchDFileName = lpFileName;
if ( !GetSpoolFileName( pscConn->hPrinter, pscConn, &lpFileName ) )
{
LPD_DEBUG( "ERROR: GetSpoolFileName() failed in ProcessJob\n" );
LocalFree( pDFile->pchDFileName );
LocalFree( pDFile );
return;
}
//
// GetTempFileName has already created this file, so use OPEN_ALWAYS.
// Also, use FILE_ATTRIBUTE_TEMPORARY so that it will be faster
// FILE_FLAG_SEQUENTIAL_SCAN, ntbug 79854, MohsinA, 03-Jun-97.
//
pDFile->hDataFile = CreateFile( lpFileName,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL
|FILE_ATTRIBUTE_TEMPORARY
|FILE_FLAG_DELETE_ON_CLOSE
|FILE_FLAG_SEQUENTIAL_SCAN
,
NULL );
LocalFree( lpFileName );
if ( pDFile->hDataFile == INVALID_HANDLE_VALUE )
{
LPD_DEBUG( "ERROR: CreatFile() failed in ProcessJob \n" );
LocalFree( pDFile->pchDFileName );
LocalFree( pDFile );
return;
}
cbBytesToRead = (cbTotalDataLen > LPD_BIGBUFSIZE ) ?
LPD_BIGBUFSIZE : cbTotalDataLen;
pchDataBuf = LocalAlloc( LMEM_FIXED, cbBytesToRead );
if ( pchDataBuf == NULL )
{
LOGIT(("ProcessJob:%d: LocalAlloc failed %d\n",
__LINE__, GetLastError() ));
CloseHandle(pDFile->hDataFile);
pDFile->hDataFile = INVALID_HANDLE_VALUE;
LocalFree( pDFile->pchDFileName );
LocalFree( pDFile );
return; // fatal error: exit
}
cbBytesSpooled = 0;
cbBytesRemaining = cbTotalDataLen;
// keep receiving until we have all the data client said it
// would send
while( cbBytesSpooled < cbTotalDataLen )
{
if ( ReadData( pscConn->sSock, pchDataBuf,
cbBytesToRead ) != NO_ERROR )
{
LPD_DEBUG( "ProcessJob:ReadData failed, job aborted)\n" );
LocalFree( pchDataBuf );
CloseHandle(pDFile->hDataFile);
pDFile->hDataFile = INVALID_HANDLE_VALUE;
LocalFree( pDFile->pchDFileName );
LocalFree( pDFile );
return; // fatal error: exit
}
cbDataBufLen = cbBytesToRead;
if ( SpoolData( pDFile->hDataFile, pchDataBuf, cbDataBufLen ) != NO_ERROR )
{
LPD_DEBUG( "SpoolData() failed in ProcessJob(): job aborted)\n" );
LocalFree( pchDataBuf );
CloseHandle(pDFile->hDataFile);
pDFile->hDataFile = INVALID_HANDLE_VALUE;
LocalFree( pDFile->pchDFileName );
LocalFree( pDFile );
return; // fatal error: exit
}
cbBytesSpooled += cbBytesToRead;
cbBytesRemaining -= cbBytesToRead;
cbBytesToRead = (cbBytesRemaining > LPD_BIGBUFSIZE ) ?
LPD_BIGBUFSIZE : cbBytesRemaining;
}
LocalFree( pchDataBuf );
InsertTailList( &pscConn->DFile_List, &pDFile->Link );
// LPR client sends one byte (of 0 bits) after sending data
dwErrcode = ReadData( pscConn->sSock, &chAck, 1 );
if ( ( dwErrcode != NO_ERROR ) || (chAck != LPD_ACK ) )
{
return;
}
// tell client we got the data and things look good so far!
if ( ReplyToClient( pscConn, LPD_ACK ) != NO_ERROR )
{
LOGIT(("ProcessJob:%d: ReplyToClient failed %d\n",
__LINE__, GetLastError() ));
return; // fatal error: exit
}
break;
case LPDCS_ABORT_JOB: // N = 01 ("abort this job")
// client asked us to abort the job: tell him "ok" and quit!
ReplyToClient( pscConn, LPD_ACK );
pscConn->wState = LPDS_ALL_WENT_WELL; // we did what client wanted
return;
// unknown subcommand: log the event and quit
default:
{
PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
LpdReportEvent( LPDLOG_MISBEHAVED_CLIENT, 1, aszStrings, 0 );
pscConn->fLogGenericEvent = FALSE;
LPD_DEBUG( "ProcessJob(): invalid subcommand, request rejected\n" );
return;
}
}
} // done processing both subcommands
} // end ProcessJob()
/*****************************************************************************
* *
* GetControlFileFromClient(): *
* This function receives the control file from the client. In the *
* previsous subcommand, the client told us how many bytes there are in *
* the control file. *
* Also,after reading all the bytes, we read the 1 byte "ack" from client *
* *
* Returns: *
* NO_ERROR if everything went well *
* ErrorCode if something went wrong somewhere *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* History: *
* Jan.24, 94 Koti Created *
* *
*****************************************************************************/
DWORD
GetControlFileFromClient( PSOCKCONN pscConn, DWORD FileSize, PCHAR FileName )
{
PCHAR pchAllocBuf;
DWORD cbBytesToRead;
PCFILE_ENTRY pCFile;
if (FileSize > LPD_MAX_CONTROL_FILE_LEN)
{
return( (DWORD)LPDERR_NOBUFS );
}
pCFile = LocalAlloc( LMEM_FIXED, sizeof(CFILE_ENTRY) );
if (pCFile == NULL) {
return( (DWORD)LPDERR_NOBUFS );
}
pCFile->cbCFileLen = FileSize;
pCFile->pchCFileName = FileName;
// we know how big the control file is going to be: alloc space for it
// Client sends one byte after sending the control file: read it along
// with the rest of the data
cbBytesToRead = FileSize + 1;
pchAllocBuf = LocalAlloc( LMEM_FIXED, cbBytesToRead );
if (pchAllocBuf == NULL)
{
LocalFree( pCFile );
return( (DWORD)LPDERR_NOBUFS );
}
// now read the data (and the trailing byte) into this allocated buffer
if ( ReadData( pscConn->sSock, pchAllocBuf, cbBytesToRead ) != NO_ERROR )
{
LocalFree( pCFile );
LocalFree( pchAllocBuf );
return( LPDERR_NORESPONSE );
}
// if the trailing byte is not zero, treat it as job aborted (though
// we don't expect this to happen really)
if ( pchAllocBuf[cbBytesToRead-1] != 0 )
{
LocalFree( pchAllocBuf );
LocalFree( pCFile );
LPD_DEBUG( "GetControlFileFromClient: got data followed by a NAK!\n");
return( LPDERR_JOBABORTED );
}
pCFile->pchCFile = pchAllocBuf;
InsertTailList( &pscConn->CFile_List, &pCFile->Link );
return( NO_ERROR );
} // end GetControlFileFromClient()
/*****************************************************************************
* *
* GetSpoolFileName(): *
* This function figures out where to put the spool file. *
* *
* Returns: *
* TRUE if spool location found. *
* FALSE if no spool location available. *
* *
* Parameters: *
* hPrinter (IN): Handle to printer for which we are spooling *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* ppchSpoolPath (IN-OUT): Address of pointer which will receive the *
* spool path. *
* *
* History: *
* Nov.21, 94 JBallard *
* *
*****************************************************************************/
BOOL
GetSpoolFileName
(
HANDLE hPrinter,
PSOCKCONN pscConn,
PCHAR *ppchSpoolPath
)
/*++
Routine Description:
This function comes up with a name for a spool file that we should be
able to write to.
Note: The file name returned has already been created.
Arguments:
hPrinter - handle to the printer that we want a spool file for.
ppchSpoolFileName: pointer that will receive an allocated buffer
containing the file name to spool to. CALLER
MUST FREE. Use LocalFree().
Return Value:
TRUE if everything goes as expected.
FALSE if anything goes wrong.
--*/
{
PBYTE pBuffer = NULL;
DWORD dwAllocSize;
DWORD dwNeeded;
PCHAR pchSpoolPath = NULL;
DWORD dwRetval;
pchSpoolPath = LocalAlloc( LMEM_FIXED, 2 * MAX_PATH + 1 );
if ( pchSpoolPath == NULL )
{
goto Failure;
}
//
// In order to find out where the spooler's directory is, we add
// call GetPrinterData with DefaultSpoolDirectory.
//
dwAllocSize = WCS_LEN( MAX_PATH + 1 );
for (;;)
{
pBuffer = LocalAlloc( LMEM_FIXED, dwAllocSize );
if ( pBuffer == NULL )
{
goto Failure;
}
if ( GetPrinterData( hPrinter,
SPLREG_DEFAULT_SPOOL_DIRECTORY,
NULL,
pBuffer,
dwAllocSize,
&dwNeeded ) == ERROR_SUCCESS )
{
break;
}
if ( ( dwNeeded < dwAllocSize ) ||( GetLastError() != ERROR_MORE_DATA ))
{
goto Failure;
}
//
// Free the current buffer and increase the size that we try to allocate
// next time around.
//
LocalFree( pBuffer );
dwAllocSize = dwNeeded;
}
if( !GetTempFileName( (LPSTR)pBuffer, "LprSpl", 0, pchSpoolPath ))
{
goto Failure;
}
//
// At this point, the spool file name should be done. Free the structure
// we used to get the spooler temp dir and return.
//
LocalFree( pBuffer );
*ppchSpoolPath = pchSpoolPath;
return( TRUE );
Failure:
//
// Clean up and fail.
//
if ( pBuffer != NULL )
{
LocalFree( pBuffer );
}
if ( pchSpoolPath != NULL )
{
LocalFree( pchSpoolPath );
}
return( FALSE );
}
VOID CleanupCFile( PCFILE_ENTRY pCFile )
{
if (pCFile->pchCFileName != NULL) {
LocalFree( pCFile->pchCFileName );
pCFile->pchCFileName = NULL;
}
if (pCFile->pchCFile != NULL) {
LocalFree( pCFile->pchCFile );
pCFile->pchCFile = NULL;
}
LocalFree( pCFile );
}
VOID CleanupDFile( PDFILE_ENTRY pDFile )
{
if (pDFile->pchDFileName != NULL) {
LocalFree( pDFile->pchDFileName );
pDFile->pchDFileName = NULL;
}
if (pDFile->hDataFile != INVALID_HANDLE_VALUE) {
CloseHandle(pDFile->hDataFile);
}
LocalFree( pDFile );
}
VOID CleanupConn( PSOCKCONN pscConn)
{
LIST_ENTRY *pTmpList;
CFILE_ENTRY *pCFile;
DFILE_ENTRY *pDFile;
while ( !IsListEmpty( &pscConn->CFile_List ) ) {
pTmpList = RemoveHeadList( &pscConn->CFile_List );
pCFile = CONTAINING_RECORD( pTmpList,
CFILE_ENTRY,
Link );
CleanupCFile( pCFile );
}
while ( !IsListEmpty( &pscConn->DFile_List ) ) {
pTmpList = RemoveHeadList( &pscConn->DFile_List );
pDFile = CONTAINING_RECORD( pTmpList,
DFILE_ENTRY,
Link );
CleanupDFile( pDFile );
}
return;
}