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

865 lines
27 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 *
* *
* Description: *
* *
* This file contains functions for parsing the commands/control file *
* sent by the LPR client. *
* *
*************************************************************************/
#include "lpd.h"
/*****************************************************************************
* *
* LicensingApproval(): *
* This function passes the username or hostname to the licensing dll. *
* The dll does whatever it needs to do and returns either a success in *
* which case we continue with printing, or a failure in which case we *
* refuse to print. *
* *
* Returns: *
* TRUE if licensing approves and we should continue with printing *
* FALSE if licensing disapproves and we should refuse printing *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* History: *
* Nov.21, 94 Koti Created *
* *
*****************************************************************************/
BOOL LicensingApproval( PSOCKCONN pscConn )
{
NT_LS_DATA LsData;
LS_STATUS_CODE err;
LS_HANDLE LicenseHandle;
BOOL fRetval;
fRetval = FALSE;
LsData.DataType = NT_LS_USER_NAME;
LsData.Data = (VOID *) pscConn->pchUserName;
LsData.IsAdmin = FALSE;
err = NtLicenseRequest(LPD_SERVICE_USRFRIENDLY_NAME,
szNTVersion,
&LicenseHandle,
&LsData);
switch (err)
{
case LS_SUCCESS:
pscConn->LicenseHandle = LicenseHandle;
pscConn->fMustFreeLicense = TRUE;
fRetval = TRUE;
break;
case LS_INSUFFICIENT_UNITS:
LPD_DEBUG( "LicensingApproval(): request rejected\n" );
break;
case LS_RESOURCES_UNAVAILABLE:
LPD_DEBUG( "LicensingApproval(): no resources\n" );
break;
default:
LPD_DEBUG( "LicensingApproval(): got back an unknown error code\n" );
}
return( fRetval );
} // end LicensingApproval()
/*****************************************************************************
* *
* ParseQueueName(): *
* This function parses the first comand from the client to retrieve the *
* name of the queue (printer). *
* *
* Returns: *
* TRUE if we successfully got the queue name *
* FALSE if something went wrong somewhere *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* Notes: *
* We are parsing a string (command) that's of the following form: *
* *
* ------------------ *
* | N | Queue | LF | where N=02 or 03 *
* ------------------ Queue = name of the queue *
* 1byte ..... 1byte *
* *
* This may not work in the case of GetQueue commands since the format *
* may include space and list, too. ParseQueueRequest takes care of it. *
* *
* History: *
* Jan.25, 94 Koti Created *
* Mar 04, 97, MohsinA Albert Ting Cluster prefix with ip address. *
*****************************************************************************/
BOOL ParseQueueName( PSOCKCONN pscConn )
{
PCHAR pchPrinterName;
DWORD cbPrinterNameLen;
DWORD cbServerPrefixLen;
// make sure Queue length is at least 1 byte
// (i.e. command is at least 3 bytes long)
if ( pscConn->cbCommandLen < 3 ){
LPD_DEBUG( "Bad command in GetQueueName(): len < 3 bytes\n" );
return( FALSE );
}
if( pscConn->szServerIPAddr == NULL ){
LPD_DEBUG( "ParseQueueName_: pscConn->szServerIPAddr NULL.\n" );
return FALSE ;
}
// What they call Queue in rfc1179, we call it Printer!
//
// We need to fully qualify the printer name with \\x.x.x.x\printer
// since there may be multiple addresses with print clustering.
// Prepend "\\x.x.x.x\" to regular name.
cbPrinterNameLen = pscConn->cbCommandLen - 2 +
2 + strlen( pscConn->szServerIPAddr ) + 1;
pchPrinterName = LocalAlloc( LMEM_FIXED, cbPrinterNameLen+1 );
if ( pchPrinterName == NULL ){
LPD_DEBUG( "LocalAlloc failed in GetQueueName()\n" );
return( FALSE );
}
// Format the prefix of the printer name \\x.x.x.x\.
sprintf( pchPrinterName, "\\\\%s\\", pscConn->szServerIPAddr );
cbServerPrefixLen = strlen( pchPrinterName );
// Append the printer name.
strncpy( &pchPrinterName[cbServerPrefixLen],
&(pscConn->pchCommand[1]),
cbPrinterNameLen - cbServerPrefixLen );
pchPrinterName[cbPrinterNameLen] = '\0';
pscConn->pchPrinterName = pchPrinterName;
return( TRUE );
} // end ParseQueueName()
/*****************************************************************************
* *
* ParseSubCommand(): *
* This function parses the subcommand to get the count and of how many *
* bytes are to come (as control file or data) and name of the control *
* file or data file, as the case may be. pscConn->wState decides which *
* subcommand is being parsed. *
* *
* Returns: *
* NO_ERROR if everything went well *
* ErrorCode if something went wrong somewhere *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* Notes: *
* We are parsing a string (subcommand) that's of the following form: *
* *
* ------------------------------ *
* | N | Count | SP | Name | LF | where N = 02 for Control file *
* ------------------------------ 03 for Data file *
* 1byte ..... 1byte .... 1byte *
* *
* History: *
* Jan.25, 94 Koti Created *
* *
*****************************************************************************/
DWORD ParseSubCommand( PSOCKCONN pscConn, DWORD *FileLen, PCHAR *FileName )
{
PCHAR pchFileName=NULL;
PCHAR pchPtr;
DWORD dwFileLen=0;
DWORD dwFileNameLen=0;
DWORD dwParseLen;
DWORD dwParsedSoFar;
WORD i;
pchPtr = &pscConn->pchCommand[1];
dwParseLen = pscConn->cbCommandLen;
dwParsedSoFar = 1; // since we're starting from 2nd byte
// pchPtr now points at the "Count" field of the subcommand
// find out how long the file is
dwFileLen = atol( pchPtr );
if ( dwFileLen <= 0 )
{
return( LPDERR_BADFORMAT );
}
// go to the next field
while ( !IS_WHITE_SPACE( *pchPtr ) )
{
pchPtr++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
// skip any trailing white space
while ( IS_WHITE_SPACE( *pchPtr ) )
{
pchPtr++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
// pchPtr now points at the "Name" field of the subcommand
// find out how long the filename is (the subcommand is terminated
// by LF character)
while( pchPtr[dwFileNameLen] != LF )
{
dwFileNameLen++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
pchFileName = (PCHAR)LocalAlloc( LMEM_FIXED, (dwFileNameLen + 1) );
if ( pchFileName == NULL )
{
return( LPDERR_NOBUFS );
}
for ( i=0; i<dwFileNameLen; i++ )
{
pchFileName[i] = pchPtr[i];
}
pchFileName[dwFileNameLen] = '\0';
// is it a control file name or data file name that we parsed?
*FileName = pchFileName;
*FileLen = dwFileLen;
return( NO_ERROR );
} // end ParseSubCommand()
/*****************************************************************************
* *
* ParseQueueRequest(): *
* This function parses the subcommand sent by the client to request the *
* status of the queue or to request removing of job(s). *
* *
* Returns: *
* NO_ERROR if everything went well *
* ErrorCode if something went wrong somewhere *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* fAgent (IN): whether to look for the Agent field. *
* *
* Notes: *
* We are parsing a string that's like one of the following: *
* *
* ------------------------------ N=03 (Short Q), 04 (Long Q) *
* | N | Queue | SP | List | LF | Queue = name of the Q (printer) *
* ------------------------------ List = user name and/or job-ids *
* 1byte ..... 1byte ..... 1byte *
* OR *
* -------------------------------------------- *
* | 05 | Queue | SP | Agent | SP | List | LF | *
* -------------------------------------------- *
* 1byte ..... 1byte ..... 1byte 1byte *
* *
* History: *
* Jan.25, 94 Koti Created *
* *
*****************************************************************************/
DWORD ParseQueueRequest( PSOCKCONN pscConn, BOOL fAgent )
{
PCHAR pchPrinterName;
PCHAR pchPrinterNameFQN;
PCHAR pchPtr;
DWORD cbPrinterNameLen;
DWORD cbPrefixLen;
DWORD dwParseLen;
DWORD dwParsedSoFar;
PQSTATUS pqStatus;
//
// ParseQueueName had allocated it: free it and reparse because
// it was parsed for the most common case, not for the Queue case
//
if (pscConn->pchPrinterName)
{
LocalFree( pscConn->pchPrinterName );
pscConn->pchPrinterName = NULL;
}
// get the printer (queue) name from the command request
// make sure Queue length is at least 1 byte
// (i.e. command is at least 4 bytes long)
if ( pscConn->cbCommandLen < 4 )
{
LPD_DEBUG( "ParseQueueRequest(): error: len shorter than 4 bytes\n" );
return( LPDERR_BADFORMAT );
}
cbPrefixLen = 2 + strlen( pscConn->szServerIPAddr ) + 1;
// alloc buffer to store printer name (yes, allocating more than needed)
pchPrinterName = LocalAlloc( LMEM_FIXED, (pscConn->cbCommandLen+cbPrefixLen) );
if ( pchPrinterName == NULL )
{
LPD_DEBUG( "LocalAlloc failed in GetQueueName()\n" );
return( LPDERR_NOBUFS );
}
pchPrinterNameFQN = pchPrinterName;
// Format the prefix of the printer name \\x.x.x.x\.
sprintf( pchPrinterName, "\\\\%s\\", pscConn->szServerIPAddr );
// advance the pointer to point to start of the printer name
pchPrinterName += strlen( pchPrinterName );
dwParseLen = pscConn->cbCommandLen;
cbPrinterNameLen = 0;
pchPtr = &(pscConn->pchCommand[1]);
while ( !IS_WHITE_SPACE( *pchPtr ) && ( *pchPtr != LF ) )
{
pchPrinterName[cbPrinterNameLen] = *pchPtr;
pchPtr++;
cbPrinterNameLen++;
if (cbPrinterNameLen >= dwParseLen )
{
LPD_DEBUG( "ParseQueueRequest(): bad request (no SP found!)\n" );
LocalFree( pchPrinterNameFQN );
return( LPDERR_BADFORMAT );
}
}
pchPrinterName[cbPrinterNameLen] = '\0';
pscConn->pchPrinterName = pchPrinterNameFQN;
dwParsedSoFar = cbPrinterNameLen + 1; // we started parsing from 2nd byte
// skip any trailing white space
while ( IS_WHITE_SPACE( *pchPtr ) )
{
pchPtr++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
// quite often, lpq won't specify any users or job-ids (i.e., the "List"
// field in the command is skipped). If so, we are done!
if ( *pchPtr == LF )
{
return( NO_ERROR );
}
// first, create a QSTATUS structure
pscConn->pqStatus = (PQSTATUS)LocalAlloc( LMEM_FIXED, sizeof(QSTATUS) );
if ( pscConn->pqStatus == NULL )
{
return( LPDERR_NOBUFS );
}
pqStatus = pscConn->pqStatus;
pqStatus->cbActualJobIds = 0;
pqStatus->cbActualUsers = 0;
pqStatus->pchUserName = NULL;
// if we have been called to parse command code 05 ("Remove Jobs")
// then get the username out of the string
if ( fAgent )
{
pqStatus->pchUserName = pchPtr;
// skip this field and go to the "List" field
while ( !IS_WHITE_SPACE( *pchPtr ) )
{
pchPtr++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
*pchPtr++ = '\0';
// skip any trailing white space
while ( IS_WHITE_SPACE( *pchPtr ) )
{
pchPtr++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
}
while ( *pchPtr != LF )
{
// if we reached the limit, stop parsing!
if ( ( pqStatus->cbActualJobIds == LPD_SP_STATUSQ_LIMIT ) ||
( pqStatus->cbActualUsers == LPD_SP_STATUSQ_LIMIT ) )
{
break;
}
// skip any trailing white space
while ( IS_WHITE_SPACE( *pchPtr ) )
{
pchPtr++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
if ( *pchPtr == LF )
{
*pchPtr = '\0';
return( NO_ERROR );
}
// is this a job id?
if ( isdigit( *pchPtr ) )
{
pqStatus->adwJobIds[pqStatus->cbActualJobIds++] = atol( pchPtr );
}
else // nope, it's user name
{
pqStatus->ppchUsers[pqStatus->cbActualUsers++] = pchPtr;
}
while ( !IS_WHITE_SPACE( *pchPtr ) && ( *pchPtr != LF ) )
{
pchPtr++;
if ( ++dwParsedSoFar >= dwParseLen )
{
return( LPDERR_BADFORMAT );
}
}
// if we reached LF, we are done
if ( *pchPtr == LF )
{
*pchPtr = '\0';
return( NO_ERROR );
}
// go to the next username or jobid, or end
*pchPtr++ = '\0';
dwParsedSoFar++;
if (dwParsedSoFar >= dwParseLen)
{
return( LPDERR_BADFORMAT );
}
}
return( NO_ERROR );
} // end ParseQueueRequest()
/*****************************************************************************
* *
* ParseControlFile(): *
* This function parses contrl file and assigns values to the appropriate *
* fields of the CFILE_INFO structure. *
* *
* Returns: *
* NO_ERROR if parsing went well *
* LPDERR_BADFORMAT if the control file didn't conform to rfc1179 *
* *
* Parameters: *
* pscConn (IN-OUT): pointer to SOCKCONN structure for this connection *
* *
* History: *
* Jan.29, 94 Koti Created *
* *
*****************************************************************************/
DWORD ParseControlFile( PSOCKCONN pscConn, PCFILE_ENTRY pCFile )
{
CFILE_INFO CFileInfo;
PCHAR pchCFile;
DWORD dwBytesParsedSoFar;
BOOL DocReady;
PCHAR DocName;
BOOL fUnsupportedCommand;
#ifdef DBG
if( !pscConn || !pscConn->pchPrinterName
|| strstr( pscConn->pchPrinterName, "debug" )
){
print__sockconn( "ParseControlFile: entered", pscConn );
}
#endif
memset( (PCHAR)&CFileInfo, 0, sizeof( CFILE_INFO ) );
if ( pCFile->pchCFile == NULL )
{
LPD_DEBUG( "ParseControlFile(): pchCFile NULL on entry\n" );
return( LPDERR_BADFORMAT );
}
if (pscConn==NULL ){
LPD_DEBUG( "ParseControlFile(): pscConn NULL on entry\n" );
return( LPDERR_BADFORMAT );
}
pchCFile = pCFile->pchCFile;
dwBytesParsedSoFar = 0;
// default: most likely, only one copy is needed
CFileInfo.usNumCopies = 1;
// default: most likely, it's "raw" data
CFileInfo.szPrintFormat = LPD_RAW_STRING;
// loop through and parse the entire file, as per rfc 1179, sec.7.
DocReady = FALSE;
CFileInfo.pchSrcFile = NULL;
CFileInfo.pchTitle = NULL;
CFileInfo.pchUnlink = NULL;
DocName = NULL;
fUnsupportedCommand = FALSE;
while( dwBytesParsedSoFar < pCFile->cbCFileLen )
{
switch( *pchCFile++ )
{
case 'C' : CFileInfo.pchClass = pchCFile;
break;
case 'H' : CFileInfo.pchHost = pchCFile;
break;
case 'I' : CFileInfo.dwCount = atol( pchCFile );
break;
case 'J' : CFileInfo.pchJobName = pchCFile;
break;
case 'L' : CFileInfo.pchBannerName = pchCFile;
break;
case 'M' : CFileInfo.pchMailName = pchCFile;
break;
case 'N' : if (CFileInfo.pchSrcFile != NULL) {
DocReady = TRUE;
break;
}
CFileInfo.pchSrcFile = pchCFile;
break;
case 'P' : CFileInfo.pchUserName = pchCFile;
pscConn->pchUserName = pchCFile;
break;
case 'S' : CFileInfo.pchSymLink = pchCFile;
break;
case 'T' : if (CFileInfo.pchTitle != NULL) {
DocReady = TRUE;
break;
}
CFileInfo.pchTitle = pchCFile;
break;
case 'U' : if (CFileInfo.pchUnlink != NULL) {
DocReady = TRUE;
break;
}
CFileInfo.pchUnlink = pchCFile;
break;
case 'W' : CFileInfo.dwWidth = atol( pchCFile );
break;
case '1' : CFileInfo.pchTrfRFile = pchCFile;
break;
case '2' : CFileInfo.pchTrfIFile = pchCFile;
break;
case '3' : CFileInfo.pchTrfBFile = pchCFile;
break;
case '4' : CFileInfo.pchTrfSFile = pchCFile;
break;
case 'K' :
case '#' : CFileInfo.usNumCopies = (WORD)atoi(pchCFile);
break;
case 'f' : if (DocName != NULL) {
DocReady = TRUE;
break;
}
CFileInfo.pchFrmtdFile = pchCFile;
CFileInfo.szPrintFormat = LPD_TEXT_STRING;
if ( fAlwaysRawGLB ) {
CFileInfo.szPrintFormat = LPD_RAW_STRING;
}
DocName = pchCFile;
break;
case 'g' : CFileInfo.pchPlotFile = pchCFile;
// fall through
case 'n' : CFileInfo.pchDitroffFile = pchCFile;
case 'o' : CFileInfo.pchPscrptFile = pchCFile;
case 't' : CFileInfo.pchTroffFile = pchCFile;
case 'v' : CFileInfo.pchRasterFile = pchCFile;
fUnsupportedCommand = TRUE;
case 'l' : if (DocName != NULL) {
DocReady = TRUE;
break;
}
CFileInfo.pchUnfrmtdFile = pchCFile;
if ( fAlwaysRawGLB ) {
CFileInfo.szPrintFormat = LPD_RAW_STRING;
}
DocName = pchCFile;
break;
case 'p' : if (DocName != NULL) {
DocReady = TRUE;
break;
}
CFileInfo.pchPRFrmtFile = pchCFile;
CFileInfo.szPrintFormat = LPD_TEXT_STRING;
if ( fAlwaysRawGLB ) {
CFileInfo.szPrintFormat = LPD_RAW_STRING;
}
DocName = pchCFile;
break;
case 'r' : if (DocName != NULL) {
DocReady = TRUE;
break;
}
CFileInfo.pchFortranFile = pchCFile;
// if someone really sends 'r', treat it like text file
CFileInfo.szPrintFormat = LPD_TEXT_STRING;
if ( fAlwaysRawGLB ) {
CFileInfo.szPrintFormat = LPD_RAW_STRING;
}
DocName = pchCFile;
break;
// unknown command! Ignore it
default:
fUnsupportedCommand = TRUE;
break;
} // end of switch( *pchCFile )
if (DocReady) {
pchCFile--;
if ( ( CFileInfo.pchHost == NULL ) ||
( CFileInfo.pchUserName == NULL ) )
{
return( LPDERR_BADFORMAT );
}
if (!LicensingApproval( pscConn ))
{
return( LPDERR_BADFORMAT );
}
if (DocName != NULL) {
PrintIt(pscConn, pCFile, &CFileInfo, DocName);
}
// Look for more work, first initialize correctly.
// - MohsinA, 23-Jan-97.
DocReady = FALSE;
CFileInfo.usNumCopies = 1;
CFileInfo.szPrintFormat = LPD_RAW_STRING;
CFileInfo.pchSrcFile = NULL;
CFileInfo.pchTitle = NULL;
CFileInfo.pchUnlink = NULL;
DocName = NULL;
fUnsupportedCommand = FALSE;
continue;
}
// we finished looking at the first char of the line
dwBytesParsedSoFar++;
// move to the end of the line
while( !IS_LINEFEED_CHAR( *pchCFile ) )
{
pchCFile++;
dwBytesParsedSoFar++;
}
// convert LF into 0 so each of our substrings above is now
// a properly null-terminated string
*pchCFile = '\0';
pchCFile++;
dwBytesParsedSoFar++;
} // end of while( dwBytesParsedSoFar < pCFile->cbCFileLen )
if( fUnsupportedCommand )
{
char *pszSource;
if ( CFileInfo.pchUserName )
pszSource = CFileInfo.pchUserName;
else if ( CFileInfo.pchHost )
pszSource = CFileInfo.pchHost;
else
pszSource = "Unknown";
LpdReportEvent( LPDLOG_UNSUPPORTED_PRINT, 1, &pszSource, 0 );
}
if(DocName != NULL ){
PrintIt(pscConn, pCFile, &CFileInfo, DocName);
}
#ifdef DBG
if( !CFileInfo.pchSrcFile
|| strstr( CFileInfo.pchSrcFile, "debug" )
){
print__controlfile_info( "ParseControlFile: all ok", &CFileInfo );
print__sockconn( "ParseControlFile: entered", pscConn );
print__cfile_entry( "ParseControlFile: ", pCFile );
}
#endif
return( NO_ERROR );
} // end ParseControlFile()