|
|
/*
Microsoft Windows NT Copyright(c) Microsoft Corp., 1994-1997
File: //KERNEL/RAZZLE3/src/sockets/tcpsvcs/lpd/print.c
Revision History:
Jan. 24,94 Koti Created 05-May-97 MohsinA Thread Pooling and Perf.
Description: This file contains all the functions that make calls to the Spooler to print or manipulate a print job.
*/
#include "lpd.h"
BOOL IsPrinterDataSet( IN HANDLE hPrinter, IN LPTSTR pszParameterName ); BOOL IsDataTypeRaw( PCHAR pchDataBuf, int cchBufferLen );
extern FILE * pErrFile; // Debug output log file.
#define MAX_PCL_SEARCH_DEPTH 4096
/*****************************************************************************
* * * ResumePrinting(): * * This function issues the PRINTER_CONTROL_RESUME to the spooler. * * * * 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.25, 94 Koti Created * * * *****************************************************************************/
DWORD ResumePrinting( PSOCKCONN pscConn ) {
HANDLE hPrinter; PCHAR aszStrings[2];
if( pscConn->pchPrinterName == NULL ){ LPD_DEBUG( "ResumePrinting(): pscConn->pchPrinterName NULL.\n" ); return( LPDERR_NOPRINTER ); }
if( !OpenPrinter( pscConn->pchPrinterName, &hPrinter, NULL ) ){ LPD_DEBUG( "OpenPrinter() failed in ResumePrinting()\n" ); return( LPDERR_NOPRINTER ); }
pscConn->hPrinter = hPrinter;
if ( !SetPrinter( hPrinter, 0, NULL, PRINTER_CONTROL_RESUME ) ) { LPD_DEBUG( "SetPrinter() failed in ResumePrinting()\n" );
return( LPDERR_NOPRINTER ); }
aszStrings[0] = pscConn->szIPAddr; aszStrings[1] = pscConn->pchPrinterName;
LpdReportEvent( LPDLOG_PRINTER_RESUMED, 2, aszStrings, 0 );
pscConn->wState = LPDS_ALL_WENT_WELL;
return( NO_ERROR );
}
/*****************************************************************************
* * * InitializePrinter(): * * This function lays the ground work with the spooler so that we can * * print the job after receiving data from the client. * * * * Returns: * * NO_ERROR if initialization went well * * ErrorCode if something went wrong somewhere * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
DWORD InitializePrinter( PSOCKCONN pscConn ) {
HANDLE hPrinter; DWORD dwActualSize; DWORD dwErrcode; BOOL fBagIt = FALSE; PPRINTER_INFO_2 p2Info; PRINTER_DEFAULTS prtDefaults; LONG lcbDevModeSize; LONG lRetval; PDEVMODE pLocalDevMode;
// Make sure a printer by this name exists!
if ( ( pscConn->pchPrinterName == NULL ) || ( !OpenPrinter( pscConn->pchPrinterName, &hPrinter, NULL ) ) ) { LPD_DEBUG( "OpenPrinter() failed in InitializePrinter()\n" );
return( LPDERR_NOPRINTER ); }
pscConn->hPrinter = hPrinter;
// allocate a 4k buffer..
p2Info = (PPRINTER_INFO_2)LocalAlloc( LMEM_FIXED, 4096 );
if ( p2Info == NULL ) { LPD_DEBUG( "4K LocalAlloc() failed in InitializePrinter\n" );
return( LPDERR_NOBUFS ); }
// do a GetPrinter so that we know what the default pDevMode is. Then
// we will modify the fields of our interest and do OpenPrinter again
if ( !GetPrinter(hPrinter, 2, (LPBYTE)p2Info, 4096, &dwActualSize) ) { if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { LocalFree( p2Info );
// 4k buffer wasn't enough: allocate however much that's needed
p2Info = (PPRINTER_INFO_2)LocalAlloc( LMEM_FIXED, dwActualSize );
if ( p2Info == NULL ) { LPD_DEBUG( "LocalAlloc() failed in InitializePrinter\n" );
return( LPDERR_NOBUFS ); }
if ( !GetPrinter(hPrinter, 2, (LPBYTE)p2Info, dwActualSize, &dwActualSize) ) { LPD_DEBUG( "InitializePrinter(): GetPrinter failed again\n" );
fBagIt = TRUE; } } else { fBagIt = TRUE; } }
if ( fBagIt ) { LPD_DEBUG( "InitializePrinter(): GetPrinter failed\n" );
LocalFree( p2Info );
return( LPDERR_NOPRINTER ); }
//
// QFE: t-heathh - 25 Aug 1994 - Fixes 24342
//
// In the case where a printer has not yet been configured, the
// pDevMode may come back NULL. In this case, we need to call
// DocumentProperties to fill in the DevMode for us.
//
if ( p2Info->pDevMode == NULL ) { // Get the size of the needed buffer.
lcbDevModeSize = DocumentProperties( NULL, // No Dialog box, please
hPrinter, pscConn->pchPrinterName, NULL, // No output buf
NULL, // No input buf
0 ); // ( flags = 0 ) => return size of out buf
if ( lcbDevModeSize < 0L ) { LPD_DEBUG( "DocumentProperties not able to return needed BufSize\n" );
LocalFree( p2Info );
return( LPDERR_NOBUFS ); }
pLocalDevMode = LocalAlloc( LMEM_FIXED, lcbDevModeSize );
if ( pLocalDevMode == NULL ) { LPD_DEBUG( "Cannot allocate local DevMode\n" );
LocalFree( p2Info );
return( LPDERR_NOBUFS ); }
lRetval = DocumentProperties( NULL, hPrinter, pscConn->pchPrinterName, pLocalDevMode, NULL, DM_OUT_BUFFER );
if ( lRetval < 0 ) { LPD_DEBUG( "Document properties won't fill-in DevMode buffer.\n" );
LocalFree( pLocalDevMode ); LocalFree( p2Info );
return( LPDERR_NOBUFS ); }
p2Info->pDevMode = pLocalDevMode;
} else { pLocalDevMode = NULL; } p2Info->pDevMode->dmCopies = 1;
//
// Since we haven't even read the Data file yet, we can't have an override
//
pscConn->bDataTypeOverride = FALSE;
// If not, set to default and UpdateJobInfo will correct it later
prtDefaults.pDatatype = LPD_RAW_STRING;
// Close it: we will open it again after modifying the pDevMode struct
ShutdownPrinter( pscConn );
prtDefaults.pDevMode = p2Info->pDevMode;
prtDefaults.DesiredAccess = PRINTER_ACCESS_USE | PRINTER_ACCESS_ADMINISTER;
if ( !OpenPrinter( pscConn->pchPrinterName, &hPrinter, &prtDefaults) )
{ LPD_DEBUG( "InitializePrinter(): second OpenPrinter() failed\n" );
LocalFree( p2Info );
if ( pLocalDevMode != NULL ) { LocalFree( pLocalDevMode ); pLocalDevMode = NULL; }
return( LPDERR_NOPRINTER ); }
if ( pLocalDevMode != NULL ) { LocalFree( pLocalDevMode ); pLocalDevMode = NULL; }
LocalFree( p2Info );
pscConn->hPrinter = hPrinter; return( NO_ERROR );
} // end InitializePrinter()
/*****************************************************************************
* * * UpdateJobInfo(): * * This function does a SetJob so that the spooler has more info about * * the job/client. * * * * Returns: * * NO_ERROR if initialization went well * * ErrorCode if something went wrong somewhere * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
DWORD UpdateJobInfo( PSOCKCONN pscConn, PCFILE_INFO pCFileInfo ) {
PJOB_INFO_2 pji2GetJob; DWORD dwNeeded; PCHAR pchTmpBuf; int ErrCode; int len; PCHAR pchModHostName=NULL;
// first do a GetJob (that way we know all fields are valid to begin
// with, and we only change the ones we want)
// Mr.Spooler, how big a buffer should I allocate?
if ( !GetJob( pscConn->hPrinter, pscConn->dwJobId, 2, NULL, 0, &dwNeeded ) ) { if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { return( LPDERR_GODKNOWS ); } } pji2GetJob = LocalAlloc( LMEM_FIXED, dwNeeded );
if ( pji2GetJob == NULL ) { return( LPDERR_NOBUFS ); }
if ( !GetJob( pscConn->hPrinter, pscConn->dwJobId, 2, (LPBYTE)pji2GetJob, dwNeeded, &dwNeeded ) ) { LocalFree( pji2GetJob );
return( LPDERR_GODKNOWS ); }
// store ip address, so we can match ip addr if the client later
// sends request to delete this job (yes, that's our security!)
pchTmpBuf = StoreIpAddr( pscConn );
if (pchTmpBuf) { pji2GetJob->pUserName = pchTmpBuf; } else { pji2GetJob->pUserName = pCFileInfo->pchUserName; }
pji2GetJob->pNotifyName = pCFileInfo->pchUserName;
// Fill in the Job title.
if ( pCFileInfo->pchTitle != NULL ) { pji2GetJob->pDocument = pCFileInfo->pchTitle; } else if ( pCFileInfo->pchSrcFile != NULL ) { pji2GetJob->pDocument = pCFileInfo->pchSrcFile; } else if ( pCFileInfo->pchJobName != NULL ) { pji2GetJob->pDocument = pCFileInfo->pchJobName; } else { pji2GetJob->pDocument = GETSTRING( LPD_DEFAULT_DOC_NAME ); }
if ( pCFileInfo->pchHost != NULL ) { len = strlen(pCFileInfo->pchHost) + strlen(LPD_JOB_PREFIX) + 2;
pchModHostName = LocalAlloc( LMEM_FIXED, len);
if (pchModHostName) { // convert HostName to job=lpdHostName so lpr knows we set the name
strcpy(pchModHostName, LPD_JOB_PREFIX); strcat(pchModHostName, pCFileInfo->pchHost);
pji2GetJob->pParameters = pchModHostName; } }
//
// Set the datatype to what the control files reflects, unless the
// auto-sense code has already overriden the control file.
//
// Illogical? Printit() might have overridden it or it is NULL.
// - MohsinA, 23-Jan-97.
//
// if( !pscConn->bDataTypeOverride )?
// if( pji2GetJob->pDatatype == NULL ){
// pji2GetJob->pDatatype = pCFileInfo->szPrintFormat;
// }
pji2GetJob->Position = JOB_POSITION_UNSPECIFIED;
ErrCode = SetJob( pscConn->hPrinter, pscConn->dwJobId, 2, (LPBYTE)pji2GetJob, 0 );
if( ErrCode ){ LPD_DEBUG("lpd:UpdateJobInfo: SetJob failed\n"); }
LocalFree( pji2GetJob );
if (pchTmpBuf) { LocalFree( pchTmpBuf ); }
if (pchModHostName) { LocalFree( pchModHostName ); }
return( NO_ERROR );
} // end UpdateJobInfo()
/* ========================================================================
Routine Description:
This function looks at the data file contents and the registry settings to see if the control file specified data type shall be overridden.
Arguments:
pscConn - Pointer to a SOCKCONN that has already received the first part of the data file.
Returns:
NO_ERROR in the usual case, various error codes otherwise
*/
DWORD UpdateJobType( PSOCKCONN pscConn, PCHAR pchDataBuf, DWORD cbDataLen ) {
PJOB_INFO_2 pji2GetJob; DWORD dwNeeded; PCHAR pchTmpBuf; BOOL override = FALSE; int ErrCode;
// first do a GetJob (that way we know all fields are valid to begin
// with, and we only change the ones we want)
// Mr.Spooler, how big a buffer should I allocate?
if ( !GetJob( pscConn->hPrinter, pscConn->dwJobId, 2, NULL, 0, &dwNeeded ) ) { if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { LPD_DEBUG("lpd:UpdateJobType: GetJob failed/1.\n"); return( LPDERR_GODKNOWS ); } } pji2GetJob = LocalAlloc( LMEM_FIXED, dwNeeded );
if ( pji2GetJob == NULL ) { LPD_DEBUG("lpd:UpdateJobType: no mem.\n"); return( LPDERR_NOBUFS ); }
if ( !GetJob( pscConn->hPrinter, pscConn->dwJobId, 2, (LPBYTE)pji2GetJob, dwNeeded, &dwNeeded ) ) { LPD_DEBUG("lpd:UpdateJobType: GetJob failed/2.\n"); LocalFree( pji2GetJob ); return( LPDERR_GODKNOWS ); }
//
// Set the datatype to NULL so that we will know if it isn't explicitly
// set by the registry.
//
//
// See if this printer has a registry setting that tells us to always
// print the job as RAW data. This registry value is accessed through
// the {Get|Set}PrinterData API that will always know where to look
// for printer settings.
//
// MohsinA, 23-Jan-97.
if ( IsPrinterDataSet( pscConn->hPrinter, TEXT( LPD_PARMNAME_PRINTER_PASS_THROUGH )) ){ #if DBG
LpdPrintf( "Printer %s has registry setting for PASS_THROUGH mode.\n", pscConn->pchPrinterName ); #endif
override = TRUE;
// else not PASS_THROUGH and AUTO-DETECT for raw job type.
}else if( !IsPrinterDataSet( pscConn->hPrinter, TEXT( LPD_PARMNAME_DONT_DETECT )) && IsDataTypeRaw( pchDataBuf, cbDataLen ) ){ // We detect PS or PCL, so make it raw.
override = TRUE; }
if ( override ){ #if DBG
LpdPrintf( "Printer %s override to raw mode.\n", pscConn->pchPrinterName ); #endif
pscConn->bDataTypeOverride = TRUE; pji2GetJob->pDatatype = LPD_RAW_STRING;
ErrCode = SetJob( pscConn->hPrinter, pscConn->dwJobId, 2, (LPBYTE)pji2GetJob, 0 );
if( ErrCode ){ LPD_DEBUG("lpd:UpdateJobType: SetJob failed.\n"); LocalFree( pji2GetJob ); return LPDERR_GODKNOWS; } }
LocalFree( pji2GetJob );
return( NO_ERROR ); } // end UpdateJobInfo()
/*****************************************************************************
* * * StoreIpAddr(): * * This function returns a buffer that contains the user name with the * * ip address appended to it, so that if a request comes in later to * * delete the job, we match the ipaddrs (some security at least!) * * * * Returns: * * Pointer to a buffer that contains the modified user name * * NULL If the name is not modified (unlikely, but possible if lpd and * * lprmon point to each other!), or if malloc fails. * * * * Parameters: * * pscConn (IN) : pointer to SOCKCONN structure for this connection * * * * Notes: Caller must free the buffer that's allocated here * * * * History: * * Jan.17, 95 Koti Created * * * *****************************************************************************/
PCHAR StoreIpAddr( PSOCKCONN pscConn ) {
DWORD dwBufLen; PCHAR pchBuf; PCHAR pchUserName; DWORD i; DWORD dwDots=0; BOOL fOpenPara=FALSE, fClosePara=FALSE;
pchUserName = pscConn->pchUserName;
//
// if the user name was "Koti" then it will be "Koti (11.101.4.25)" after
// this function.
// In some configurations (e.g. point lpd to lprmon and lprmon back to lpd)
// we could keep appending ipaddrs for each instance of the job. First
// find out if ipaddr is already appended
//
dwBufLen = strlen( pchUserName );
for (i=0; i<dwBufLen; i++) { switch( *(pchUserName+i) ) { case '(': fOpenPara = TRUE; break; case ')': fClosePara = TRUE; break; case '.': dwDots++; break; } }
//
// if name already contains the ipaddr, don't append again
//
if (fOpenPara && fClosePara && dwDots >= 3) { return( NULL ); }
// buffer to store a string in the form "Koti (11.101.4.25)"
dwBufLen += 25;
pchBuf = LocalAlloc( LMEM_FIXED, dwBufLen );
if ( pchBuf == NULL ) { LPD_DEBUG( "StoreIpAddr(): LocalAlloc failed\n" ); return( NULL ); }
sprintf( pchBuf, "%s (%s)", pchUserName, pscConn->szIPAddr );
return( pchBuf );
} // end StoreIpAddr()
// ========================================================================
//
// Sends profiling information back on socket qsock.
// Scans the wait queue and reports clients/peers also.
//
// Created: MohsinA, 05-May-97.
//
#ifdef PROFILING
void SendProfileStatus( SOCKET qsock ) { char buff[4000], buff2[NI_MAXHOST]; int buflen; COMMON_LPD local_common = Common; // struct copy, for readonly.
PSOCKCONN pscConn = NULL; // S0186, B#101002, MohsinA, 18/8/97.
SOCKCONN local_sockconn;
int again = 1; int ok; int count = 0;
SOCKET csocket; SOCKADDR_STORAGE csock_addr; int csock_len;
// ====================================================================
// First send the Common information.
buflen = sprintf(buff, "--- PROFILING NT 5.0 LPD Server of %s %s ---\n" "AliveThreads=%d, MaxThreads=%d, TotalAccepts=%d,\n" "TotalErrors=%d, QueueLength=%d\n" , __DATE__, __TIME__, local_common.AliveThreads, local_common.MaxThreads, local_common.TotalAccepts, local_common.TotalErrors, local_common.QueueLength );
if( buflen > 0 ){ assert( buflen < sizeof( buff ) ); SendData( qsock, buff, buflen ); }
// ====================================================================
// Now scan the wait queue.
EnterCriticalSection( &csConnSemGLB );
while( again && !fShuttingDownGLB ){
{ if( pscConn == NULL ){ // First time?
pscConn = scConnHeadGLB.pNext; }else{ pscConn = pscConn->pNext; }
if( pscConn == NULL ){ again = 0; }else{ local_sockconn = *pscConn; // struct copy.
csocket = local_sockconn.sSock; csock_len = sizeof(csock_addr); ok = getpeername( csocket, (SOCKADDR *) &csock_addr, &csock_len ); } }
if( fShuttingDownGLB || !again ) break;
count++; assert( count <= local_common.QueueLength );
if( ok == SOCKET_ERROR ){ buflen = sprintf( buff, "(%d) Bad peer, err=%d, queued at %s", count, GetLastError(), ctime(&local_sockconn.time_queued ) ); }else{ buflen = NI_MAXHOST; WSAAddressToString((LPSOCKADDR)&csock_addr, csock_len, NULL, buff2, &buflen);
buflen = sprintf( buff, "(%d) Client %s queued since %s", count, buff2, ctime(&local_sockconn.time_queued ) ); }
if( buflen > 0 ){ assert( buflen < sizeof( buff ) ); SendData( qsock, buff, buflen ); }
} // end while pscConn.
LeaveCriticalSection( &csConnSemGLB );
return; } #endif
/*****************************************************************************
* * * SendQueueStatus(): * * This function retrieves the status of all the jobs on the printer of * * our interest and sends over to the client. If the client specified * * users and/or job-ids in the status request then we send status of jobs * * of only those users and/or those job-ids. * * * * Returns: * * Nothing * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * wMode (IN): whether short or long status info is requested * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
VOID SendQueueStatus( PSOCKCONN pscConn, WORD wMode ) { BOOL fResult; HANDLE hPrinter; DWORD dwBufSize; DWORD dwHdrsize; DWORD dwNumJobs; PCHAR pchSndBuf=NULL; PCHAR pchSpoolerBuf=NULL; BOOL fNoPrinter=FALSE; BOOL fAtLeastOneJob=TRUE; CHAR szPrinterNameAndStatus[300]; int nResult;
// for now, we return the same status info regardless of whether
// the client asked for Short or Long queue Status. This might
// be enough since we are giving adequate info anyway
#ifdef PROFILING
SendProfileStatus( pscConn->sSock ); #endif
if ( ( pscConn->pchPrinterName == NULL ) || ( !OpenPrinter( pscConn->pchPrinterName, &hPrinter, NULL ) ) ) { LPD_DEBUG( "OpenPrinter() failed in SendQueueStatus()\n" );
fNoPrinter = TRUE;
goto SendQueueStatus_BAIL; }
pscConn->hPrinter = hPrinter;
pchSpoolerBuf = LocalAlloc( LMEM_FIXED, 4096 ); if ( pchSpoolerBuf == NULL ) { goto SendQueueStatus_BAIL; }
// store the printer name (we might append status to it)
strcpy( szPrinterNameAndStatus, pscConn->pchPrinterName );
// do a get printer to see how the printer is doing
if ( GetPrinter(pscConn->hPrinter, 2, pchSpoolerBuf, 4096, &dwBufSize) ) { if ( ((PPRINTER_INFO_2)pchSpoolerBuf)->Status == PRINTER_STATUS_PAUSED ) { strcat( szPrinterNameAndStatus, GETSTRING( LPD_PRINTER_PAUSED ) ); } else if ( ((PPRINTER_INFO_2)pchSpoolerBuf)->Status == PRINTER_STATUS_PENDING_DELETION ) { strcat( szPrinterNameAndStatus, GETSTRING( LPD_PRINTER_PENDING_DEL ) ); } } else { LPD_DEBUG( "GetPrinter() failed in SendQueueStatus()\n" ); }
// Since OpenPrinter succeeded, we will be sending to the client
// at least dwHdrsize bytes that includes the printername
dwHdrsize = strlen( GETSTRING( LPD_LOGO ) ) + strlen( GETSTRING( LPD_PRINTER_TITLE) ) + strlen( szPrinterNameAndStatus ) + strlen( GETSTRING( LPD_QUEUE_HEADER )) + strlen( GETSTRING( LPD_QUEUE_HEADER2)) + strlen( LPD_NEWLINE );
//
// query spooler for all the jobs currently pending
// (we have already allocate 4K buffer)
//
dwBufSize = 4096;
while(1) { fResult = EnumJobs( pscConn->hPrinter, 0, LPD_MAXJOBS_ENUM, 2, pchSpoolerBuf, dwBufSize, &dwBufSize, &dwNumJobs );
// most common case: will work the first time
if (fResult == TRUE) { break; }
// it's possible spooler got a new job, and our buffer is now small
// so it returns ERROR_INSUFFICIENT_BUFFER. Other than that, quit!
if ( (fResult == FALSE) && ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) ) { goto SendQueueStatus_BAIL; }
LocalFree( pchSpoolerBuf );
// spooler wants more memory: allocate it
pchSpoolerBuf = LocalAlloc( LMEM_FIXED, dwBufSize );
if ( pchSpoolerBuf == NULL ) { goto SendQueueStatus_BAIL; } }
if (dwNumJobs == 0) { fAtLeastOneJob = FALSE; }
// header, and one line per job that's queued (potentially, dwNumJobs=0)
dwBufSize = dwHdrsize + ( dwNumJobs * LPD_LINE_SIZE );
// to put a newline at the end of display!
dwBufSize += sizeof( LPD_NEWLINE );
ShutdownPrinter( pscConn );
// this is the buffer we use to send the data out
pchSndBuf = LocalAlloc( LMEM_FIXED, dwBufSize );
if ( pchSndBuf == NULL ) { goto SendQueueStatus_BAIL; }
// copy the dwHdrsize bytes of header
nResult = sprintf( pchSndBuf, "%s%s%s%s%s%s", GETSTRING( LPD_LOGO ), GETSTRING( LPD_PRINTER_TITLE ), szPrinterNameAndStatus, GETSTRING( LPD_QUEUE_HEADER ), GETSTRING( LPD_QUEUE_HEADER2 ), LPD_NEWLINE );
//
// watch for buffer overwrites
//
LPD_ASSERT( nResult == (int) dwHdrsize );
// if there are any jobs, fill the buffer with status
// of each of the jobs
if ( fAtLeastOneJob ) { nResult += FillJobStatus( pscConn, (pchSndBuf + dwHdrsize ), (PJOB_INFO_2)pchSpoolerBuf, dwNumJobs ); LPD_ASSERT ((int) dwBufSize >= nResult); if (nResult > (int) dwBufSize) { nResult = (int) dwBufSize; } }
// not much can be done if SendData fails!
SendData( pscConn->sSock, pchSndBuf, nResult);
if ( pchSpoolerBuf != NULL ) { LocalFree( pchSpoolerBuf ); }
LocalFree( pchSndBuf );
pscConn->wState = LPDS_ALL_WENT_WELL;
return;
// if we reached here, some error occured while getting job status.
// Tell the client that we had a problem!
SendQueueStatus_BAIL:
ShutdownPrinter( pscConn );
if ( pchSndBuf != NULL ) { LocalFree( pchSndBuf ); }
if ( pchSpoolerBuf != NULL ) { LocalFree( pchSpoolerBuf ); }
// just add size of all possible error messages, so we have room for
// the largest message!
dwBufSize = strlen( GETSTRING( LPD_LOGO ) ) + strlen( GETSTRING( LPD_QUEUE_ERRMSG ) ) + strlen( GETSTRING( LPD_QUEUE_NOPRINTER ) );
pchSndBuf = LocalAlloc( LMEM_FIXED, dwBufSize );
if ( pchSndBuf == NULL ) { return; }
if ( fNoPrinter ) { LPD_DEBUG( "Rejected status request for non-existent printer\n" );
sprintf( pchSndBuf, "%s%s", GETSTRING( LPD_LOGO ), GETSTRING( LPD_QUEUE_NOPRINTER ) ); } else { LPD_DEBUG( "Something went wrong in SendQueueStatus()\n" );
sprintf( pchSndBuf, "%s%s", GETSTRING( LPD_LOGO ), GETSTRING( LPD_QUEUE_ERRMSG ) ); }
// Not much can be done about an error here: don't bother checking!
SendData( pscConn->sSock, pchSndBuf, dwBufSize );
LocalFree( pchSndBuf );
return;
} // end SendQueueStatus()
/*****************************************************************************
* * * ShutDownPrinter(): * * This function closes the printer in our context. * * * * Returns: * * Nothing * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
VOID ShutdownPrinter( PSOCKCONN pscConn ) {
if ( pscConn->hPrinter == (HANDLE)INVALID_HANDLE_VALUE ) { return; }
if ( ClosePrinter( pscConn->hPrinter ) ) { pscConn->hPrinter = (HANDLE)INVALID_HANDLE_VALUE; } else { LPD_DEBUG( "ShutDownPrinter: ClosePrinter failed\n" ); }
return;
} // end ShutdownPrinter()
/*****************************************************************************
* * * SpoolData(): * * This function writes the data that we got from client into spool file. * * * * Returns: * * NO_ERROR if things went well * * ErrorCode if something didn't go right * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
DWORD SpoolData( HANDLE hSpoolFile, PCHAR pchDataBuf, DWORD cbDataBufLen ) {
DWORD dwBytesWritten; BOOL fRetval;
fRetval = WriteFile( hSpoolFile, pchDataBuf, cbDataBufLen, &dwBytesWritten, NULL );
// if WriteFile failed, or if fewer bytes got written, quit!
if ( (fRetval == FALSE) || (dwBytesWritten != cbDataBufLen) ) { LPD_DEBUG( "WriteFile() failed in SpoolData\n" );
return( LPDERR_NOPRINTER ); }
return( NO_ERROR );
} // end SpoolData()
/*****************************************************************************
* * * PrintData(): * * This function tells the spooler that we are done writing to the spool * * file and that it should go ahead and dispatch it. * * * * Returns: * * NO_ERROR if everything went ok * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
DWORD PrintData( PSOCKCONN pscConn ) { CFILE_ENTRY *pCFile; DWORD dwRetval; LIST_ENTRY *pTmpList;
while ( !IsListEmpty( &pscConn->CFile_List ) ) {
pTmpList = RemoveHeadList( &pscConn->CFile_List ); pCFile = CONTAINING_RECORD( pTmpList, CFILE_ENTRY, Link );
if ( (dwRetval = ParseControlFile( pscConn, pCFile )) != NO_ERROR ) { PCHAR aszStrings[2]={ pscConn->szIPAddr, NULL };
LpdReportEvent( LPDLOG_BAD_FORMAT, 1, aszStrings, 0 ); pscConn->fLogGenericEvent = FALSE; LPD_DEBUG( "ParseControlFile() failed in ProcessJob()\n" ); CleanupCFile( pCFile ); return( dwRetval ); // the thread exits
}
CleanupCFile( pCFile ); }
return( NO_ERROR );
} // end PrintData()
/*****************************************************************************
* * * PrintIt(): * * This function tells the spooler that we are done writing to the spool * * file and that it should go ahead and dispatch it. * * * * Returns: * * Nothing * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * * *****************************************************************************/
DWORD PrintIt( PSOCKCONN pscConn, PCFILE_ENTRY pCFileEntry, PCFILE_INFO pCFileInfo, PCHAR pFileName ) { DOC_INFO_1 dc1Info; PDFILE_ENTRY pDFile; DWORD cbTotalDataLen; DWORD cbBytesSpooled; DWORD cbBytesToSpool; DWORD cbDataBufLen; DWORD cbBytesRemaining; DWORD cbBytesActuallySpooled; PCHAR pchDataBuf; DWORD dwRetval; BOOL fRetval; USHORT cbCount;
#ifdef DBG
if( !pCFileInfo || !pCFileInfo->pchSrcFile || strstr( pCFileInfo->pchSrcFile, "debug" ) ){ print__controlfile_info( "PrintIt: ", pCFileInfo ); print__cfile_entry( "Printit: ", pCFileEntry ); } #endif
memset( &dc1Info, 0, sizeof( dc1Info ) );
// Defaults.
dc1Info.pDatatype = LPD_RAW_STRING;
// Get the job title if any.
if ( pCFileInfo->pchTitle != NULL ) { dc1Info.pDocName = pCFileInfo->pchTitle; } else if ( pCFileInfo->pchSrcFile != NULL ) { dc1Info.pDocName = pCFileInfo->pchSrcFile; } else if ( pCFileInfo->pchJobName != NULL ) { dc1Info.pDocName = pCFileInfo->pchJobName; } else { dc1Info.pDocName = pCFileInfo->pchTitle = pCFileInfo->pchJobName = pCFileInfo->pchSrcFile = pFileName; }
dc1Info.pOutputFile = NULL; // we aren't writing to file
//
// If datatype is known, set it.
// Doesn't it default to raw? - MohsinA, 23-Jan-97.
//
if( pCFileInfo->szPrintFormat != NULL ) { dc1Info.pDatatype = pCFileInfo->szPrintFormat; }
for (cbCount = 0; cbCount < pCFileInfo->usNumCopies; cbCount++) {
pscConn->dwJobId = StartDocPrinter( pscConn->hPrinter, 1, (LPBYTE)&dc1Info ) ;
if ( pscConn->dwJobId == 0 ) { LPD_DEBUG( "InitializePrinter(): StartDocPrinter() failed\n" );
return( LPDERR_NOPRINTER ); }
UpdateJobInfo( pscConn, pCFileInfo );
#ifdef DBG
if( !pCFileInfo || !pCFileInfo->pchSrcFile || strstr( pCFileInfo->pchSrcFile, "debug" ) ){ print__controlfile_info( "PrintIt: after UpdateJobInfo ", pCFileInfo ); print__cfile_entry( "Printit: after UpdateJobInfo", pCFileEntry ); } #endif
if (!IsListEmpty( &pscConn->DFile_List ) ) {
pDFile = (PDFILE_ENTRY)pscConn->DFile_List.Flink; while (strncmp( pDFile->pchDFileName, pFileName, strlen(pFileName) ) != 0 ) { pDFile = (PDFILE_ENTRY)pDFile->Link.Flink; if (pDFile == (PDFILE_ENTRY)&pscConn->DFile_List) { return( LPDERR_BADFORMAT ); } }
dwRetval = SetFilePointer( pDFile->hDataFile, 0, NULL, FILE_BEGIN );
if (dwRetval == 0xFFFFFFFF) { LPD_DEBUG( "ERROR: SetFilePointer() failed in PrintIt\n" ); return( LPDERR_GODKNOWS ); }
cbTotalDataLen = pDFile->cbDFileLen;
cbBytesToSpool = (cbTotalDataLen > LPD_BIGBUFSIZE ) ? LPD_BIGBUFSIZE : cbTotalDataLen;
pchDataBuf = LocalAlloc( LMEM_FIXED, cbBytesToSpool );
if ( pchDataBuf == NULL ) { CloseHandle(pDFile->hDataFile); pDFile->hDataFile = INVALID_HANDLE_VALUE; return( (DWORD)LPDERR_NOBUFS ); }
cbBytesSpooled = 0;
cbBytesRemaining = cbTotalDataLen;
// keep receiving until we have all the data client said it
// would send
while( cbBytesSpooled < cbTotalDataLen ) { fRetval = ReadFile( pDFile->hDataFile, pchDataBuf, cbBytesToSpool, &cbBytesActuallySpooled, NULL ); if ( (!fRetval) || (cbBytesActuallySpooled != cbBytesToSpool) ) { LPD_DEBUG( "ReadFile() failed in PrintIt(): job aborted)\n" );
LocalFree( pchDataBuf ); CloseHandle(pDFile->hDataFile); pDFile->hDataFile = INVALID_HANDLE_VALUE; return( LPDERR_NOPRINTER ); }
// MohsinA, 23-Jan-97?
if ( cbBytesSpooled == 0 ) { UpdateJobType( pscConn, pchDataBuf, cbBytesToSpool ); }
cbDataBufLen = cbBytesToSpool;
fRetval = WritePrinter( pscConn->hPrinter, pchDataBuf, cbBytesToSpool, &cbBytesActuallySpooled);
if ( (fRetval==FALSE) || (cbBytesToSpool != cbBytesActuallySpooled) ) { LPD_DEBUG( "WritePrinter() failed in PrintIt():job aborted)\n" );
LocalFree( pchDataBuf ); CloseHandle(pDFile->hDataFile); pDFile->hDataFile = INVALID_HANDLE_VALUE; return( LPDERR_NOPRINTER ); }
cbBytesSpooled += cbBytesToSpool;
cbBytesRemaining -= cbBytesToSpool;
cbBytesToSpool = (cbBytesRemaining > LPD_BIGBUFSIZE ) ? LPD_BIGBUFSIZE : cbBytesRemaining;
}
LocalFree( pchDataBuf );
if ( !EndDocPrinter( pscConn->hPrinter ) ) { LPD_DEBUG( "EndDocPrinter() failed in PrintData\n" ); return( LPDERR_NOPRINTER ); } } }
return(NO_ERROR);
} // end PrintIt()
/*****************************************************************************
* * * AbortThisJob(): * * This function tells the spooler to abort the specified job. * * * * Returns: * * Nothing * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
VOID AbortThisJob( PSOCKCONN pscConn ) { assert( pscConn );
if( pscConn->hPrinter == INVALID_HANDLE_VALUE ) { LOGIT(("lpd:AbortThisJob: invalid hPrinter %d.\n", pscConn->hPrinter )); return; }
// not much can be done if there is an error: don't bother checking
if (!SetJob( pscConn->hPrinter, pscConn->dwJobId, 0, NULL, JOB_CONTROL_CANCEL ) ) { LPD_DEBUG( "AbortThisJob: SetJob failed\n"); }
if ( !EndDocPrinter( pscConn->hPrinter ) ) { LPD_DEBUG( "EndDocPrinter() failed in AbortThisJob\n" ); }
LPD_DEBUG( "AbortThisJob(): job aborted\n" );
return;
} // end AbortThisJob()
/*****************************************************************************
* * * RemoveJobs(): * * This function removes all the jobs the user has asked us to remove, * * after verifying that the job was indeed sent originally by the same * * user (ip addresses of machine sending the original job and the request * * to remove it should match). * * * * Returns: * * NO_ERROR if everything went ok * * Errorcode if job couldn't be deleted * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
DWORD RemoveJobs( PSOCKCONN pscConn ) { PQSTATUS pqStatus; PJOB_INFO_1 pji1GetJob; BOOL fSuccess=TRUE; HANDLE hPrinter; DWORD dwNeeded; DWORD dwBufLen; PCHAR pchUserName; PCHAR pchIPAddr; DWORD i, j;
if ( (pqStatus = pscConn->pqStatus) == NULL ) { return( LPDERR_BADFORMAT ); }
if ( ( pscConn->pchPrinterName == NULL ) || ( !OpenPrinter( pscConn->pchPrinterName, &hPrinter, NULL ) ) ) { LPD_DEBUG( "OpenPrinter() failed in RemoveJobs()\n" );
return( LPDERR_NOPRINTER ); }
pscConn->hPrinter = hPrinter;
// the "List" field can contain UserNames or JobId's of the jobs to be
// removed. Even though we parse UserNames into the ppchUsers[] array
// (in pqStatus struct), we only use the JobId's, and not use the UserNames
// at all. Reason is we only want to remove jobs that the user submitted
// and not allow a user to specify other usernames.
// try to remove every job the user has asked us to remove
for ( i=0; i<pqStatus->cbActualJobIds; i++ ) {
// ask GetJob how big a buffer we must pass. If the errorcode is
// anything other than ERROR_INSUFFICIENT_BUFFER, the job must be
// done (so JobId is invalid), and we won't do anything
if ( !GetJob( pscConn->hPrinter, pqStatus->adwJobIds[i], 1, NULL, 0, &dwNeeded ) ) { if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { LPD_DEBUG( "lpd:RemoveJobs: GetJob failed.\n" ); fSuccess = FALSE;
continue; } }
pji1GetJob = LocalAlloc( LMEM_FIXED, dwNeeded );
if ( pji1GetJob == NULL ) { LPD_DEBUG("lpd:RemoveJobs: no mem\n"); return( LPDERR_NOBUFS ); }
if ( !GetJob( pscConn->hPrinter, pqStatus->adwJobIds[i], 1, (LPBYTE)pji1GetJob, dwNeeded, &dwNeeded ) ) { fSuccess = FALSE;
LocalFree( pji1GetJob );
continue; }
// pUserName is in the form "Koti (11.101.4.25)"
// (we store the string in this exact format (in UpdateJobInfo()))
// Also, jobs coming from unix can't use space in user name, so if
// we do find a space, it must be the one we introduced (before '(' )
pchUserName = pji1GetJob->pUserName;
dwBufLen = strlen( pchUserName );
pchIPAddr = pchUserName;
j = 0; while( *pchIPAddr != ')' ) // first convert the last ')' to '\0'
{ pchIPAddr++;
j++;
//
// if we traversed the entire name and didn't find ')', something
// isn't right (e.g. not one of our jobs): just skip this one
//
if (j >= dwBufLen) { LocalFree( pji1GetJob ); break; } }
if (j >= dwBufLen) { continue; }
*pchIPAddr = '\0'; // convert the ')' to '\0'
pchIPAddr = pchUserName;
while ( !IS_WHITE_SPACE( *pchIPAddr ) ) { pchIPAddr++; }
*pchIPAddr = '\0'; // convert the space to \0
//
// just make sure that it's what we had set earlier
//
if ( *(pchIPAddr+1) != '(' ) { LocalFree( pji1GetJob ); continue; }
pchIPAddr += 2; // skip over the new \0 and the '('
// make sure the job was indeed submitted by the same user from
// the same machine (that's the extent of our security!)
if ( ( strcmp( pchUserName, pqStatus->pchUserName ) != 0 ) || ( strcmp( pchIPAddr, pscConn->szIPAddr ) != 0 ) ) { PCHAR aszStrings[4];
aszStrings[0] = pscConn->szIPAddr; aszStrings[1] = pqStatus->pchUserName; aszStrings[2] = pchUserName; aszStrings[3] = pchIPAddr;
LpdReportEvent( LPDLOG_UNAUTHORIZED_REQUEST, 4, aszStrings, 0 );
LPD_DEBUG( "Unauthorized request in RemoveJobs(): refused\n" );
fSuccess = FALSE;
LocalFree( pji1GetJob );
continue; }
// now that we've crossed all hurdles, delete the job!
SetJob( pscConn->hPrinter, pqStatus->adwJobIds[i], 0, NULL, JOB_CONTROL_CANCEL );
LocalFree( pji1GetJob );
}
if ( !fSuccess ) { return( LPDERR_BADFORMAT ); }
pscConn->wState = LPDS_ALL_WENT_WELL;
return( NO_ERROR );
} // end RemoveJobs()
/*****************************************************************************
* * * FillJobStatus(): * * This function takes output from the EnumJobs() call and puts into a * * buffer info about the job that's of interest to us. * * * * Returns: * * Nothing * * * * Parameters: * * pscConn (IN-OUT): pointer to SOCKCONN structure for this connection * * pchDest (OUT): buffer into which we put info about the jobs * * pji2QStatus (IN): buffer we got as output from the EnumJobs() call * * dwNumJobs (IN): how many jobs does data in pji2QStatus pertain to. * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
INT FillJobStatus( PSOCKCONN pscConn, PCHAR pchDest, PJOB_INFO_2 pji2QStatus, DWORD dwNumJobs ) {
DWORD i, j; BOOL fUsersSpecified=FALSE; BOOL fJobIdsSpecified=FALSE; BOOL fMatchFound; PQSTATUS pqStatus; CHAR szFormat[8]; PCHAR pchStart = pchDest;
// if users/job-ids not specified, we return status on all jobs
if ( (pqStatus = pscConn->pqStatus) == NULL ) { fMatchFound = TRUE; }
// looks like users and/or job-ids is specified
else { if ( pqStatus->cbActualUsers > 0 ) { fUsersSpecified = TRUE; }
if ( pqStatus->cbActualJobIds > 0 ) { fJobIdsSpecified = TRUE; }
fMatchFound = FALSE; // flip the default
}
// if user or job-ids or both are specified, then we fill in data only
// if we find a match. if neither is specified (most common case)
// then we report all jobs (default for fMatchFound does the trick)
for ( i=0; i<dwNumJobs; i++, pji2QStatus++ ) { if ( fUsersSpecified ) { for ( j=0; j<pqStatus->cbActualUsers; j++ ) { if (_stricmp( pji2QStatus->pUserName, pqStatus->ppchUsers[j] ) == 0) { fMatchFound = TRUE; break; } } }
if ( (!fMatchFound) && (fJobIdsSpecified) ) { for ( j=0; j<pqStatus->cbActualJobIds; j++ ) { if ( pji2QStatus->JobId == pqStatus->adwJobIds[j] ) { fMatchFound = TRUE; break; } } }
if ( !fMatchFound ) { continue; }
// put in the desired fields for each (selected) of the jobs
LpdFormat( pchDest, pji2QStatus->pUserName, LPD_FLD_OWNER ); pchDest += LPD_FLD_OWNER;
//
// Since we can have multiple bits set, but print only 1 status, so
// first handle the error bits
//
if (pji2QStatus->Status & JOB_STATUS_ERROR) { LpdFormat( pchDest, GETSTRING( LPD_STR_ERROR ), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_OFFLINE) { LpdFormat( pchDest, GETSTRING( LPD_STR_OFFLINE), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_PAPEROUT) { LpdFormat( pchDest, GETSTRING( LPD_STR_PAPEROUT), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_USER_INTERVENTION) { LpdFormat( pchDest, GETSTRING( LPD_STR_USER_INTERVENTION ), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_BLOCKED_DEVQ) { LpdFormat( pchDest, GETSTRING( LPD_STR_BLOCKED_DEVQ ), LPD_FLD_STATUS ); } //
// Now, handle the processing states
//
else if (pji2QStatus->Status & JOB_STATUS_PRINTING) { LpdFormat( pchDest, GETSTRING( LPD_STR_PRINTING), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_SPOOLING) { LpdFormat( pchDest, GETSTRING( LPD_STR_SPOOLING), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_DELETING) { LpdFormat( pchDest, GETSTRING( LPD_STR_DELETING), LPD_FLD_STATUS ); } //
// Now, handle the processed states
//
else if (pji2QStatus->Status & JOB_STATUS_DELETED) { LpdFormat( pchDest, GETSTRING( LPD_STR_DELETED ), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_PAUSED) { LpdFormat( pchDest, GETSTRING( LPD_STR_PAUSED ), LPD_FLD_STATUS ); } else if (pji2QStatus->Status & JOB_STATUS_PRINTED) { LpdFormat( pchDest, GETSTRING( LPD_STR_PRINTED), LPD_FLD_STATUS ); } //
// Remaining cases
//
else if (pji2QStatus->Status & JOB_STATUS_RESTART) { LpdFormat( pchDest, GETSTRING( LPD_STR_RESTART ), LPD_FLD_STATUS ); } else { LpdFormat( pchDest, GETSTRING( LPD_STR_PENDING), LPD_FLD_STATUS ); }
pchDest += LPD_FLD_STATUS;
LpdFormat( pchDest, pji2QStatus->pDocument, LPD_FLD_JOBNAME ); pchDest += LPD_FLD_JOBNAME;
sprintf( szFormat, "%s%d%s", "%", LPD_FLD_JOBID, "d" ); sprintf( pchDest, szFormat, pji2QStatus->JobId ); pchDest += LPD_FLD_JOBID;
sprintf( szFormat, "%s%d%s", "%", LPD_FLD_SIZE, "d" ); sprintf( pchDest, szFormat, pji2QStatus->Size ); pchDest += LPD_FLD_SIZE;
sprintf( szFormat, "%s%d%s", "%", LPD_FLD_PAGES, "d" ); sprintf( pchDest, szFormat, pji2QStatus->TotalPages ); pchDest += LPD_FLD_PAGES;
sprintf( szFormat, "%s%d%s", "%", LPD_FLD_PRIORITY, "d" ); sprintf( pchDest, szFormat, pji2QStatus->Priority ); pchDest += LPD_FLD_PRIORITY;
sprintf( pchDest, "%s", LPD_NEWLINE ); pchDest += sizeof( LPD_NEWLINE ) -1;
if (pqStatus) { //
// If a specific job was requested, then we should
// re-determine the criteria!
//
fMatchFound = FALSE; } } // for ( i=0; i<dwNumJobs; i++, pji2QStatus++ )
sprintf( pchDest, "%s", LPD_NEWLINE ); pchDest += sizeof( LPD_NEWLINE );
return (INT)(pchDest - pchStart); } // end FillJobStatus()
/*****************************************************************************
* * * LpdFormat(): * * This function copies exactly the given number of bytes from source * * to dest buffer, by truncating or padding with spaces if need be. The * * byte copied into the dest buffer is always a space. * * * * Returns: * * Nothing * * * * Parameters: * * pchDest (OUT): destination buffer * * pchSource (IN): source buffer * * dwLimit (IN): number of bytes to copy * * * * History: * * Jan.25, 94 Koti Created * * * *****************************************************************************/
VOID LpdFormat( PCHAR pchDest, PCHAR pchSource, DWORD dwLimit ) {
DWORD dwCharsToCopy; BOOL fPaddingNeeded; DWORD i;
if( pchSource ){ dwCharsToCopy = strlen( pchSource ); }else{ DEBUG_PRINT(("LpdFormat NULL pchSource\n")); dwCharsToCopy = 0; }
if ( dwCharsToCopy < (dwLimit-1) ) { fPaddingNeeded = TRUE; } else { fPaddingNeeded = FALSE; dwCharsToCopy = dwLimit-1; }
for ( i=0; i<dwCharsToCopy; i++ ) { pchDest[i] = pchSource[i]; }
if ( fPaddingNeeded ) { for ( i=dwCharsToCopy; i<dwLimit-1; i++ ) { pchDest[i] = ' '; } }
// make sure last byte is a space
pchDest[dwLimit-1] = ' ';
} // end LpdFormat()
/* ========================================================================
Routine Description:
Uses spooler-provided APIs to determine if the named registry DWORD is a non-zero value. If the registry key does not exist, it is created, with a value of zero.
Arguments:
hPrinter - A handle to the printer whose configuration is queried. In order for writing the default value to work, this handle must have been opened with PRINTER_ACCESS_ADMINISTER
pszParameterName - The name of the registry key to retrieve and test.
Return Value:
TRUE if the registry key exists for this printer and contains a non-zero value. FALSE is returned in all other cases.
*/
BOOL IsPrinterDataSet( IN HANDLE hPrinter, IN LPTSTR pszParameterName ) { DWORD dwRegValue; DWORD dwRegType; DWORD cbValueSize; DWORD dwErrcode;
if ( ( GetPrinterData( hPrinter, pszParameterName, &dwRegType, ( LPBYTE )&dwRegValue, sizeof( dwRegValue ), &cbValueSize ) == ERROR_SUCCESS ) && ( dwRegType == REG_DWORD ) && ( cbValueSize == sizeof( DWORD ) ) ) { if ( dwRegValue ) { #if DBG
LpdPrintf( "Printer ox%08X has registry setting for %S.\n", hPrinter, pszParameterName ); #endif
return( TRUE ); } } else { #if DBG
LpdPrintf( "lpd:IsPrinterDataSet: GetPrinterData() failed.\n"); #endif
//
// Either the registry value in question is not in the registry, or it is
// not a REG_DWORD that we can read. The following code adds the setting
// and defaults it to zero (0). While this will not change the operation
// of the print queue, it will make it easier for a user wishing to do so
// to find the correct registry parameter. Notice that this paragraph is
// fully justified.
//
// If this fails we don't really care, so we don't check the return value
//
dwRegValue = 0;
dwErrcode = SetPrinterData( hPrinter, pszParameterName, REG_DWORD, ( LPBYTE )&dwRegValue, sizeof( dwRegValue ) );
#if DBG
LpdPrintf( "lpd: wrote %S == %d for printer 0x%08X, dwErrcode is %s.\n", pszParameterName, dwRegValue, hPrinter, ( dwErrcode == ERROR_SUCCESS ) ? "Succesful" : "ERROR" ); #endif
}
return( FALSE ); }
/*
Routine Description:
This function looks at the data file contents and attempts to determine if they are 'RAW' PostScript or PCL
Arguments:
pchDataBuf - Pointer to the _beginning_ of the data file.
cchBufferLen - Number of characters pointed to.
Returns:
TRUE if the job type is detected as raw, FALSE if it is not.
*/
BOOL IsDataTypeRaw( PCHAR pchDataBuf, int cchBufferLen ) { PCHAR pchData; int cchAmountToSearch; int cchAmountSearched;
ASSERT( STRING_LENGTH_POSTSCRIPT_HEADER == strlen( STRING_POSTSCRIPT_HEADER ) );
//
// Because some PS print drivers may send blank lines, end-of-file marks, and
// other control characters at the beginning of the print data, the following
// loop scans through these and skips them. The Windows 3.1 PostScript driver
// was notorious for doing this, as an example.
//
for ( pchData = pchDataBuf; ( pchData - pchDataBuf ) < cchBufferLen; pchData ++ ) { if ( *pchData >= 0x20 ) { break; } }
if ( (( cchBufferLen - ( pchData - pchDataBuf )) >= STRING_LENGTH_POSTSCRIPT_HEADER ) && ( memcmp( pchData, STRING_POSTSCRIPT_HEADER, STRING_LENGTH_POSTSCRIPT_HEADER ) == 0 )) { LPD_DEBUG( "Printed data was detected as PostScript\n" ); return( TRUE ); }
//
// The job was not determined to be PostScript, so check to see if it is PCL.
//
pchData = pchDataBuf;
cchAmountToSearch = min( cchBufferLen, MAX_PCL_SEARCH_DEPTH ); cchAmountSearched = 0;
while ( cchAmountSearched < cchAmountToSearch ) { pchData = memchr( pchData, 0x1B, cchAmountToSearch - cchAmountSearched );
if ( pchData == NULL ) { break; }
cchAmountSearched = (int)(pchData - pchDataBuf);
if ( ( cchAmountSearched + 3 ) < cchAmountToSearch ) { pchData++; cchAmountSearched++;
if ( *pchData != '&' ) { continue; }
pchData++; cchAmountSearched++;
if ( *pchData != 'l' ) { continue; }
pchData++; cchAmountSearched++;
while (( cchAmountSearched < cchAmountToSearch ) && ( isdigit( *pchData ))) { pchData++; cchAmountSearched++; }
if (( cchAmountSearched < cchAmountToSearch ) && ( isalpha( *pchData ))) { LPD_DEBUG( "Printed data was detected as PCL\n" );
return( TRUE ); } }
// reached end of buffer
else { break; } }
LPD_DEBUG( "Printed data was not detected as anything special (like PS or PCL)\n" );
return( FALSE ); }
|