|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name :
ftpcmd.cxx
Abstract:
This module defines the FTP commands supported by this FTP server and provides a table of functions to be called for processing such command requests. ( Some parts of the code are from old engine.cxx ( KeithMo's FTP server))
Author:
Murali R. Krishnan ( MuraliK ) 28-Mar-1995
Environment:
User Mode -- Win32
Project:
FTP Server DLL
Functions Exported:
ParseCommand ()
Revision History:
--*/
/************************************************************
* Include Headers ************************************************************/
# include <ftpdp.hxx>
# include "ftpcmd.hxx"
# include "lsaux.hxx"
# include "auxctrs.h"
#define MAX_COMMAND_NAME_LEN ( 30)
# define MAX_HELP_LINE_SIZE ( 80)
# define MAX_HELP_AUX_SIZE (100) // fixed sized aux info with HELP
# define HelpMsgSize( nCommands) ((1 + nCommands) * MAX_HELP_LINE_SIZE + \
MAX_HELP_AUX_SIZE)
#define IS_7BIT_ASCII(c) ((UINT)(c) <= 127)
/************************************************************
* Static Data containing command lookups ************************************************************/
# define UsP ( UserStateWaitingForPass)
# define UsUP ( UserStateWaitingForUser | UsP)
# define UsL ( UserStateLoggedOn)
# define UsUPL ( UsL | UsUP)
//
// Store the commands in alphabetical order ( manually stored so!)
// to enable faster search.
//
// Format is:
// Name Help Information FunctionToCall ArgumentType ValidStates
//
FTPD_COMMAND MainCommands[] ={
{ "ABOR", "(abort operation)", MainABOR, ArgTypeNone, UsL}, { "ACCT", "(specify account)", MainACCT, ArgTypeRequired,UsL}, { "ALLO", "(allocate storage vacuously)", MainALLO, ArgTypeRequired,UsL}, { "APPE", "<sp> file-name", MainAPPE, ArgTypeRequired,UsL}, { "CDUP", "change to parent directory", MainCDUP, ArgTypeNone, UsL}, { "CWD", "[ <sp> directory-name ]", MainCWD , ArgTypeOptional,UsL}, { "DELE", "<sp> file-name", MainDELE, ArgTypeRequired,UsL}, { "HELP", "[ <sp> <string>]", MainHELP, ArgTypeOptional,UsUPL}, { "LIST", "[ <sp> path-name ]", MainLIST, ArgTypeOptional,UsL}, { "MDTM", "(sp) file-name", MainMDTM, ArgTypeRequired,UsL }, { "MKD", "<sp> path-name", MainMKD , ArgTypeRequired,UsL}, { "MODE", "(specify transfer mode)", MainMODE, ArgTypeRequired,UsUPL}, { "NLST", "[ <sp> path-name ]", MainNLST, ArgTypeOptional,UsL}, { "NOOP", "", MainNOOP, ArgTypeNone, UsUPL}, { "PASS", "<sp> password", MainPASS, ArgTypeOptional, UsP}, { "PASV", "(set server in passive mode)", MainPASV, ArgTypeNone, UsL}, { "PORT", "<sp> b0,b1,b2,b3,b4,b5", MainPORT, ArgTypeRequired,UsUPL}, { "PWD", "(return current directory)", MainPWD , ArgTypeNone, UsL}, { "QUIT", "(terminate service)", MainQUIT, ArgTypeNone, UsUPL}, { "REIN", "(reinitialize server state)", MainREIN, ArgTypeNone, UsL}, { "REST", "<sp> marker", MainREST, ArgTypeRequired,UsL}, { "RETR", "<sp> file-name", MainRETR, ArgTypeRequired,UsL}, { "RMD", "<sp> path-name", MainRMD , ArgTypeRequired,UsL }, { "RNFR", "<sp> file-name", MainRNFR, ArgTypeRequired,UsL}, { "RNTO", "<sp> file-name", MainRNTO, ArgTypeRequired,UsL }, { "SITE", "(site-specific commands)", MainSITE, ArgTypeOptional,UsL }, { "SIZE", "(sp) file-name", MainSIZE, ArgTypeRequired,UsL }, { "SMNT", "<sp> pathname", MainSMNT, ArgTypeRequired,UsL }, { "STAT", "(get server status)", MainSTAT, ArgTypeOptional,UsL }, { "STOR", "<sp> file-name", MainSTOR, ArgTypeRequired,UsL }, { "STOU", "(store unique file)", MainSTOU, ArgTypeNone, UsL}, { "STRU", "(specify file structure)", MainSTRU, ArgTypeRequired,UsUPL}, { "SYST", "(get operating system type)", MainSYST, ArgTypeNone, UsL }, { "TYPE", "<sp> [ A | E | I | L ]", MainTYPE, ArgTypeRequired,UsL }, { "USER", "<sp> username", MainUSER, ArgTypeRequired,UsUPL}, { "XCUP", "change to parent directory", MainCDUP, ArgTypeNone, UsL}, { "XCWD", "[ <sp> directory-name ]", MainCWD , ArgTypeOptional,UsL }, { "XMKD", "<sp> path-name", MainMKD , ArgTypeRequired,UsL }, { "XPWD", "(return current directory)", MainPWD , ArgTypeNone, UsL }, { "XRMD", "<sp> path-name", MainRMD , ArgTypeRequired,UsL } };
#define NUM_MAIN_COMMANDS ( sizeof(MainCommands) / sizeof(MainCommands[0]) )
FTPD_COMMAND SiteCommands[] = {
{ "CKM", "(toggle directory comments)", SiteCKM , ArgTypeNone,UsL}, { "DIRSTYLE", "(toggle directory format)", SiteDIRSTYLE, ArgTypeNone,UsL}, { "HELP", "[ <sp> <string>]", SiteHELP , ArgTypeOptional, UsL}
#ifdef KEEP_COMMAND_STATS
,{ "STATS", "(display per-command stats)", SiteSTATS , ArgTypeNone, UsL}
#endif // KEEP_COMMAND_STATS
};
#define NUM_SITE_COMMANDS ( sizeof(SiteCommands) / sizeof(SiteCommands[0]) )
#ifdef KEEP_COMMAND_STATS
extern CRITICAL_SECTION g_CommandStatisticsLock; #endif // KEEP_COMMAND_STATS
#ifdef FTP_AUX_COUNTERS
LONG g_AuxCounters[NUM_AUX_COUNTERS];
#endif // FTP_AUX_COUNTERS
char PSZ_COMMAND_NOT_UNDERSTOOD[] = "'%s': command not understood"; char PSZ_INVALID_PARAMS_TO_COMMAND[] = "'%s': Invalid number of parameters"; char PSZ_ILLEGAL_PARAMS[] = "'%s': illegal parameters";
/************************************************************
* Functions ************************************************************/
LPFTPD_COMMAND FindCommandByName( LPSTR pszCommandName, LPFTPD_COMMAND pCommandTable, INT cCommands );
VOID HelpWorker( LPUSER_DATA pUserData, LPSTR pszSource, LPSTR pszCommand, LPFTPD_COMMAND pCommandTable, INT cCommands, INT cchMaxCmd );
/*******************************************************************
NAME: ParseCommand
SYNOPSIS: Parses a command string, dispatching to the appropriate implementation function.
ENTRY: pUserData - The user initiating the request.
pszCommandText - pointer to command text. This array of characters will be munged while parsing.
HISTORY: KeithMo 07-Mar-1993 Created. MuraliK 08-18-1995 Eliminated local copy of the command text
********************************************************************/ VOID ParseCommand( LPUSER_DATA pUserData, LPSTR pszCommandText ) { LPFTPD_COMMAND pcmd; LPFN_COMMAND pfnCmd; LPSTR pszSeparator; LPSTR pszInvalidCommandText = PSZ_INVALID_PARAMS_TO_COMMAND; CHAR chSeparator; BOOL fValidArguments; BOOL fReturn = FALSE;
DBG_ASSERT( pszCommandText != NULL ); DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) ); DBG_ASSERT( IS_VALID_USER_STATE( pUserData->UserState ) );
IF_DEBUG( PARSING) {
DBGPRINTF( ( DBG_CONTEXT, "ParseCommand( %08x, %s)\n", pUserData, pszCommandText)); }
//
// Ensure we didn't get entered in an invalid state.
//
//BOGUS: DBG_ASSERT( ( pUserData->UserState != UserStateEmbryonic ) &&
//BOGUS: ( pUserData->UserState != UserStateDisconnected ) );
pUserData->UpdateOffsets();
//
// The command will be terminated by either a space or a '\0'.
//
pszSeparator = strchr( pszCommandText, ' ' );
if( pszSeparator == NULL ) { pszSeparator = pszCommandText + strlen( pszCommandText ); }
//
// Try to find the command in the command table.
//
chSeparator = *pszSeparator; *pszSeparator = '\0';
pcmd = FindCommandByName( pszCommandText, MainCommands, NUM_MAIN_COMMANDS );
if( chSeparator != '\0' ) { *pszSeparator++ = chSeparator; }
//
// If this is an unknown command, reply accordingly.
//
if( pcmd == NULL ) { FacIncrement( FacUnknownCommands);
ReplyToUser( pUserData, REPLY_UNRECOGNIZED_COMMAND, PSZ_COMMAND_NOT_UNDERSTOOD, pszCommandText ); return; }
//
// Retrieve the implementation routine.
//
pfnCmd = pcmd->Implementation;
//
// If this is an unimplemented command, reply accordingly.
//
if( pfnCmd == NULL ) { ReplyToUser( pUserData, REPLY_COMMAND_NOT_IMPLEMENTED, PSZ_COMMAND_NOT_UNDERSTOOD, pcmd->CommandName );
return; }
//
// Ensure we're in a valid state for the specified command.
//
if ( ( pcmd->dwUserState & pUserData->UserState) == 0) {
if( pfnCmd == MainPASS ) {
ReplyToUser( pUserData, REPLY_BAD_COMMAND_SEQUENCE, "Login with USER first." ); } else {
ReplyToUser( pUserData, REPLY_NOT_LOGGED_IN, "Please login with USER and PASS." ); }
return; }
//
// Do a quick & dirty preliminary check of the argument(s).
//
fValidArguments = FALSE;
while( ( *pszSeparator == ' ' ) && ( *pszSeparator != '\0' ) ) {
pszSeparator++; }
switch( pcmd->ArgumentType ) {
case ArgTypeNone : fValidArguments = ( *pszSeparator == '\0' ); break;
case ArgTypeOptional : fValidArguments = TRUE; break;
case ArgTypeRequired : fValidArguments = ( *pszSeparator != '\0' ); break;
default: DBGPRINTF(( DBG_CONTEXT, "ParseCommand - invalid argtype %d\n", pcmd->ArgumentType )); DBG_ASSERT( FALSE ); break; }
//
// check we did not get extended chars if we are configured not to allow that
//
if( g_fNoExtendedChars /* && !pUserDate->QueryUTF8Option() */) {
LPSTR pszCh = pszSeparator;
while( *pszCh ) {
if( !IS_7BIT_ASCII( *pszCh++ ) ) {
fValidArguments = FALSE; pszInvalidCommandText = PSZ_ILLEGAL_PARAMS; break; } } }
if( fValidArguments ) {
//
// Invoke the implementation routine.
//
if( *pszSeparator == '\0' ) { pszSeparator = NULL; }
IF_DEBUG( PARSING ) { DBGPRINTF(( DBG_CONTEXT, "invoking %s command, args = %s\n", pcmd->CommandName, _strnicmp( pcmd->CommandName, "PASS", 4 ) ? pszSeparator : "{secret...}" )); }
#ifdef KEEP_COMMAND_STATS
EnterCriticalSection( &g_CommandStatisticsLock );
//
// only increment the count if we're not re-processing a command
//
if ( !pUserData->QueryInFakeIOCompletion() ) { pcmd->UsageCount++; } LeaveCriticalSection( &g_CommandStatisticsLock ); #endif // KEEP_COMMAND_STATS
//
// Keep track of what command is being executed, in case command processing doesn't
// complete in this thread and another thread has to finish processing it
// [can happen if we're in PASV mode and doing async accept on the data connection]
// Only need to do this if this thread isn't handling an IO completion we generated
// ourselves because a PASV socket became accept()'able - if it is, we've already
// set the command.
//
if ( !pUserData->QueryInFakeIOCompletion() ) { if ( !pUserData->SetCommand( pszCommandText ) ) { ReplyToUser( pUserData, REPLY_LOCAL_ERROR, "Failed to allocate necessary memory."); } } fReturn = (pfnCmd)( pUserData, pszSeparator );
if ( !fReturn) {
//
// Invalid number of arguments. Inform the client.
//
ReplyToUser(pUserData, REPLY_UNRECOGNIZED_COMMAND, PSZ_COMMAND_NOT_UNDERSTOOD, pszCommandText); }
} else {
// Invalid # of arguments
ReplyToUser(pUserData, REPLY_UNRECOGNIZED_COMMAND, pszInvalidCommandText, pszCommandText); }
return; } // ParseCommand()
/*******************************************************************
NAME: MainSITE
SYNOPSIS: Implementation for the SITE command.
ENTRY: pUserData - The user initiating the request.
pszArg - Command arguments. Will be NULL if no arguments given.
RETURNS: BOOL - TRUE if arguments OK, FALSE if syntax error.
HISTORY: KeithMo 09-Mar-1993 Created.
********************************************************************/ BOOL MainSITE( LPUSER_DATA pUserData, LPSTR pszArg ) { LPFTPD_COMMAND pcmd; LPFN_COMMAND pfnCmd; LPSTR pszSeparator; CHAR chSeparator; BOOL fValidArguments; CHAR szParsedCommand[MAX_COMMAND_LENGTH+1];
DBG_ASSERT( pUserData != NULL );
//
// If no arguments were given, just return the help text.
//
if( pszArg == NULL ) { SiteHELP( pUserData, NULL ); return TRUE; }
//
// Save a copy of the command so we can muck around with it.
//
P_strncpy( szParsedCommand, pszArg, MAX_COMMAND_LENGTH );
//
// The command will be terminated by either a space or a '\0'.
//
pszSeparator = strchr( szParsedCommand, ' ' );
if( pszSeparator == NULL ) { pszSeparator = szParsedCommand + strlen( szParsedCommand ); }
//
// Try to find the command in the command table.
//
chSeparator = *pszSeparator; *pszSeparator = '\0';
pcmd = FindCommandByName( szParsedCommand, SiteCommands, NUM_SITE_COMMANDS );
if( chSeparator != '\0' ) { *pszSeparator++ = chSeparator; }
//
// If this is an unknown command, reply accordingly.
//
if( pcmd == NULL ) {
//
// Syntax error in command.
//
ReplyToUser( pUserData, REPLY_UNRECOGNIZED_COMMAND, "'SITE %s': command not understood", pszArg );
return (TRUE); }
//
// Retrieve the implementation routine.
//
pfnCmd = pcmd->Implementation;
//
// If this is an unimplemented command, reply accordingly.
//
if( pfnCmd == NULL ) { ReplyToUser( pUserData, REPLY_COMMAND_NOT_IMPLEMENTED, "SITE %s command not implemented.", pcmd->CommandName );
return TRUE; }
//
// Ensure we're in a valid state for the specified command.
//
if ( ( pcmd->dwUserState & pUserData->UserState) == 0) {
ReplyToUser( pUserData, REPLY_NOT_LOGGED_IN, "Please login with USER and PASS." ); return (FALSE); }
//
// Do a quick & dirty preliminary check of the argument(s).
//
fValidArguments = FALSE;
while( ( *pszSeparator == ' ' ) && ( *pszSeparator != '\0' ) ) {
pszSeparator++; }
switch( pcmd->ArgumentType ) {
case ArgTypeNone: fValidArguments = ( *pszSeparator == '\0' ); break;
case ArgTypeOptional: fValidArguments = TRUE; break;
case ArgTypeRequired: fValidArguments = ( *pszSeparator != '\0' ); break;
default: DBGPRINTF(( DBG_CONTEXT, "MainSite - invalid argtype %d\n", pcmd->ArgumentType )); DBG_ASSERT( FALSE ); break; }
if( fValidArguments ) {
//
// Invoke the implementation routine.
//
if( *pszSeparator == '\0' ) { pszSeparator = NULL; }
IF_DEBUG( PARSING ) { DBGPRINTF(( DBG_CONTEXT, "invoking SITE %s command, args = %s\n", pcmd->CommandName, pszSeparator )); }
if( (pfnCmd)( pUserData, pszSeparator ) ) { return TRUE; } } else {
// Invalid # of arguments
ReplyToUser(pUserData, REPLY_UNRECOGNIZED_COMMAND, PSZ_INVALID_PARAMS_TO_COMMAND, pszArg); }
return TRUE;
} // MainSITE()
/*******************************************************************
NAME: MainHELP
SYNOPSIS: Implementation for the HELP command.
ENTRY: pUserData - The user initiating the request.
pszArg - Command arguments. Will be NULL if no arguments given.
RETURNS: BOOL - TRUE if arguments OK, FALSE if syntax error.
HISTORY: KeithMo 09-Mar-1993 Created.
********************************************************************/ BOOL MainHELP( LPUSER_DATA pUserData, LPSTR pszArg ) { DBG_ASSERT( pUserData != NULL );
HelpWorker(pUserData, "", pszArg, MainCommands, NUM_MAIN_COMMANDS, 4 );
return TRUE;
} // MainHELP
/*******************************************************************
NAME: SiteHELP
SYNOPSIS: Implementation for the site-specific HELP command.
ENTRY: pUserData - The user initiating the request.
pszArg - Command arguments. Will be NULL if no arguments given.
RETURNS: BOOL - TRUE if arguments OK, FALSE if syntax error.
HISTORY: KeithMo 09-May-1993 Created.
********************************************************************/ BOOL SiteHELP( LPUSER_DATA pUserData, LPSTR pszArg ) { DBG_ASSERT( pUserData != NULL );
HelpWorker(pUserData, "SITE ", pszArg, SiteCommands, NUM_SITE_COMMANDS, 8 );
return TRUE;
} // SiteHELP
#ifdef KEEP_COMMAND_STATS
/*******************************************************************
NAME: SiteSTATS
SYNOPSIS: Implementation for the site-specific STATS command.
ENTRY: pUserData - The user initiating the request.
pszArg - Command arguments. Will be NULL if no arguments given.
RETURNS: BOOL - TRUE if arguments OK, FALSE if syntax error.
HISTORY: KeithMo 26-Sep-1994 Created.
********************************************************************/ BOOL SiteSTATS( LPUSER_DATA pUserData, LPSTR pszArg ) { SOCKET ControlSocket; LPFTPD_COMMAND pCmd; INT i; CHAR rgchUsageStats[NUM_MAIN_COMMANDS * 25]; // 25 chars per command
pCmd = MainCommands;
DBG_ASSERT( NUM_MAIN_COMMANDS > 0); // we know this very well!
// Print the stats for first command.
EnterCriticalSection( &g_CommandStatisticsLock );
// Find first non-zero entry.
for( i = 0; i < NUM_MAIN_COMMANDS && pCmd->UsageCount <= 0; i++, pCmd++) ;
if ( i < NUM_MAIN_COMMANDS) {
// There is some non-zero entry.
CHAR * pszStats = rgchUsageStats; DWORD cch = 0;
// print the stats for first command
cch = wsprintfA( pszStats + cch, "%u-%-4s : %lu\r\n", REPLY_COMMAND_OK, pCmd->CommandName, pCmd->UsageCount);
for( i++, pCmd++ ; i < NUM_MAIN_COMMANDS ; i++, pCmd++) {
if( pCmd->UsageCount > 0 ) {
cch += wsprintfA( pszStats + cch, " %-4s : %lu\r\n", pCmd->CommandName, pCmd->UsageCount ); DBG_ASSERT( cch < NUM_MAIN_COMMANDS * 25); }
} // for
// Ignoring the error code here! probably socket closed
SockSend( pUserData, pUserData->QueryControlSocket(), rgchUsageStats, cch); }
LeaveCriticalSection( &g_CommandStatisticsLock );
#ifdef FTP_AUX_COUNTERS
CHAR * pszStats = rgchUsageStats; DWORD cch = 0;
// print the stats for first counter
cch = wsprintfA( pszStats + cch, "%u-Aux[%d] : %lu\r\n", REPLY_COMMAND_OK, 0, FacCounter(0));
for( i = 1; i < NUM_AUX_COUNTERS; i++) {
cch += wsprintfA( pszStats + cch, " Aux[%d] : %lu\r\n", i, FacCounter(i)); DBG_ASSERT( cch < NUM_MAIN_COMMANDS * 25); }
if ( cch > 0) {
SockSend( pUserData, pUserData->QueryControlSocket(), rgchUsageStats, cch); }
# endif // FTP_AUX_COUNTERS
ReplyToUser( pUserData, REPLY_COMMAND_OK, "End of stats." );
return TRUE;
} // SiteSTATS
#endif KEEP_COMMAND_STATS
/*******************************************************************
NAME: FindCommandByName
SYNOPSIS: Searches the command table for a command with this specified name.
ENTRY: pszCommandName - The name of the command to find.
pCommandTable - An array of FTPD_COMMANDs detailing the available commands.
cCommands - The number of commands in pCommandTable.
RETURNS: LPFTPD_COMMAND - Points to the command entry for the named command. Will be NULL if command not found.
HISTORY: KeithMo 10-Mar-1993 Created. MuraliK 28-Mar-1995 Completely rewrote to support binary search.
********************************************************************/ LPFTPD_COMMAND FindCommandByName( LPSTR pszCommandName, LPFTPD_COMMAND pCommandTable, INT cCommands ) { int iLower = 0; int iHigher = cCommands - 1; // store the indexes
LPFTPD_COMMAND pCommandFound = NULL;
DBG_ASSERT( pszCommandName != NULL ); DBG_ASSERT( pCommandTable != NULL ); DBG_ASSERT( cCommands > 0 );
//
// Search for the command in our table.
//
_strupr( pszCommandName );
while ( iLower <= iHigher) {
int iMid = ( iHigher + iLower) / 2;
int comp = strcmp( pszCommandName, pCommandTable[ iMid].CommandName);
if ( comp == 0) {
pCommandFound = ( pCommandTable + iMid); break;
} else if ( comp < 0) {
// reset the higher bound
iHigher = iMid - 1; } else {
// reset the lower bound
iLower = iMid + 1; } }
return ( pCommandFound);
} // FindCommandByName()
/*******************************************************************
NAME: HelpWorker
SYNOPSIS: Worker function for HELP & site-specific HELP commands.
ENTRY: pUserData - The user initiating the request.
pszSource - The source of these commands.
pszCommand - The command to get help for. If NULL, then send a list of available commands.
pCommandTable - An array of FTPD_COMMANDs, one for each available command.
cCommands - The number of commands in pCommandTable.
cchMaxCmd - Length of the maximum command.
HISTORY: KeithMo 06-May-1993 Created. Muralik 08-May-1995 Added Buffering for performance.
********************************************************************/ VOID HelpWorker( LPUSER_DATA pUserData, LPSTR pszSource, LPSTR pszCommand, LPFTPD_COMMAND pCommandTable, INT cCommands, INT cchMaxCmd ) { LPFTPD_COMMAND pcmd; DWORD dwError;
//
// We should cache the following message and use the cached message for
// sending purposes ==> improves performance.
// MuraliK NYI
//
DBG_ASSERT( pUserData != NULL ); DBG_ASSERT( pCommandTable != NULL ); DBG_ASSERT( cCommands > 0 );
if( pszCommand == NULL ) {
DWORD cch; LS_BUFFER lsb; CHAR szTmp[MAX_HELP_LINE_SIZE];
if ((dwError = lsb.AllocateBuffer(HelpMsgSize(cCommands)))!= NO_ERROR){
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT, "Buffer Allocation ( %d bytes) failed.\n", HelpMsgSize(cCommands))); }
ReplyToUser(pUserData, REPLY_HELP_MESSAGE, "HELP command failed." ); return; }
cch = wsprintfA( lsb.QueryAppendPtr(), "%u-The following %s commands are recognized" "(* ==>'s unimplemented).\r\n", REPLY_HELP_MESSAGE, pszSource); lsb.IncrementCB( cch * sizeof( CHAR));
for( pcmd = pCommandTable; pcmd < pCommandTable + cCommands; pcmd++) {
cch = sprintf( szTmp, " %-*s%c\r\n", cchMaxCmd, pcmd->CommandName, pcmd->Implementation == NULL ? '*' : ' ' );
DBG_ASSERT( cch*sizeof(CHAR) < sizeof(szTmp));
//
// since we calculate and preallocate the buffer, we dont
// need to worry about the overflow of the buffer.
//
DBG_ASSERT( cch*sizeof(CHAR) < lsb.QueryRemainingCB());
if ( cch * sizeof(CHAR) >= lsb.QueryRemainingCB()) {
// This is added for retail code where ASSERT may fail.
dwError = ERROR_NOT_ENOUGH_MEMORY; break; }
strncpy( lsb.QueryAppendPtr(), szTmp, cch); lsb.IncrementCB( cch*sizeof(CHAR));
} // for ( all commands)
if ( dwError == NO_ERROR) {
// Append the ending sequence for success in generating HELP.
cch = sprintf( szTmp, "%u %s\r\n", REPLY_HELP_MESSAGE, "HELP command successful." );
if ( cch*sizeof(CHAR) >= lsb.QueryRemainingCB()) {
dwError = ERROR_NOT_ENOUGH_MEMORY; } else { // copy the completion message
strncpy( lsb.QueryAppendPtr(), szTmp, cch); lsb.IncrementCB( cch*sizeof(CHAR)); } }
if ( dwError == NO_ERROR) {
// Send the chunk of data
dwError = SockSend( pUserData, pUserData->QueryControlSocket(), lsb.QueryBuffer(), lsb.QueryCB()); } else {
IF_DEBUG( ERROR) {
DBGPRINTF(( DBG_CONTEXT, "Error = %u. Should Not happen though...\n", dwError)); }
ReplyToUser( pUserData, REPLY_HELP_MESSAGE, "HELP command failed."); }
lsb.FreeBuffer();
// Ignore the errors if any from propagating outside
} else {
pcmd = FindCommandByName(pszCommand, pCommandTable, cCommands );
if( pcmd == NULL ) {
ReplyToUser( pUserData, REPLY_PARAMETER_SYNTAX_ERROR, "Unknown command %s.", pszCommand ); } else {
ReplyToUser( pUserData, REPLY_HELP_MESSAGE, "Syntax: %s%s %s", pszSource, pcmd->CommandName, pcmd->HelpText ); } }
return;
} // HelpWorker()
/************************ End of File ***********************/
|