mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
836 lines
19 KiB
836 lines
19 KiB
/**********************************************************************/
|
|
/** Microsoft Windows NT **/
|
|
/** Copyright(c) Microsoft Corp., 1993 **/
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
utility.c
|
|
|
|
This module contains routines of general utility.
|
|
|
|
|
|
FILE HISTORY:
|
|
KeithMo 17-Mar-1993 Created.
|
|
|
|
*/
|
|
|
|
|
|
#include "ftpdp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: ReadRegistryDword
|
|
|
|
SYNOPSIS: Reads a DWORD value from the registry.
|
|
|
|
ENTRY: pszValueName - The name of the value.
|
|
|
|
dwDefaultValue - The default value to use if the
|
|
value cannot be read.
|
|
|
|
RETURNS DWORD - The value from the registry, or dwDefaultValue.
|
|
|
|
NOTES: This function cannot be called until after
|
|
InitializeGlobals().
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
DWORD
|
|
ReadRegistryDword(
|
|
CHAR * pszValueName,
|
|
DWORD dwDefaultValue
|
|
)
|
|
{
|
|
APIERR err;
|
|
DWORD dwBuffer;
|
|
DWORD cbBuffer = sizeof(dwBuffer);
|
|
DWORD dwType;
|
|
|
|
if( hkeyFtpd != NULL )
|
|
{
|
|
err = RegQueryValueEx( hkeyFtpd,
|
|
pszValueName,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&dwBuffer,
|
|
&cbBuffer );
|
|
|
|
if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) )
|
|
{
|
|
dwDefaultValue = dwBuffer;
|
|
}
|
|
}
|
|
|
|
return dwDefaultValue;
|
|
|
|
} // ReadRegistryDword
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: ReadRegistryString
|
|
|
|
SYNOPSIS: Allocates necessary buffer space for a registry
|
|
string, then reads the string into the buffer.
|
|
|
|
ENTRY: pszValueName - The name of the value.
|
|
|
|
pszDefaultValue - The default value to use if the
|
|
value cannot be read.
|
|
|
|
fExpand - Expand environment strings if TRUE.
|
|
|
|
RETURNS: CHAR * - The string, NULL if error.
|
|
|
|
NOTES: I always allocate one more character than actually
|
|
necessary. This will ensure that any code expecting
|
|
to read a REG_MULTI_SZ will not explode if the
|
|
registry actually contains a REG_SZ.
|
|
|
|
This function cannot be called until after
|
|
InitializeGlobals().
|
|
|
|
HISTORY:
|
|
KeithMo 15-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
CHAR *
|
|
ReadRegistryString(
|
|
CHAR * pszValueName,
|
|
CHAR * pszDefaultValue,
|
|
BOOL fExpand
|
|
)
|
|
{
|
|
CHAR * pszBuffer1;
|
|
CHAR * pszBuffer2;
|
|
DWORD cbBuffer;
|
|
DWORD dwType;
|
|
APIERR err;
|
|
|
|
//
|
|
// Determine the buffer size.
|
|
//
|
|
|
|
pszBuffer1 = NULL;
|
|
pszBuffer2 = NULL;
|
|
cbBuffer = 0;
|
|
|
|
if( hkeyFtpd == NULL )
|
|
{
|
|
//
|
|
// Pretend the key wasn't found.
|
|
//
|
|
|
|
err = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
err = RegQueryValueEx( hkeyFtpd,
|
|
pszValueName,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&cbBuffer );
|
|
|
|
if( ( err == NO_ERROR ) || ( err == ERROR_MORE_DATA ) )
|
|
{
|
|
if( ( dwType != REG_SZ ) &&
|
|
( dwType != REG_MULTI_SZ ) &&
|
|
( dwType != REG_EXPAND_SZ ) )
|
|
{
|
|
//
|
|
// Type mismatch, registry data NOT a string.
|
|
// Use default.
|
|
//
|
|
|
|
err = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Item found, allocate a buffer.
|
|
//
|
|
|
|
pszBuffer1 = (CHAR *)FTPD_ALLOC( cbBuffer+1 );
|
|
|
|
if( pszBuffer1 == NULL )
|
|
{
|
|
err = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Now read the value into the buffer.
|
|
//
|
|
|
|
err = RegQueryValueEx( hkeyFtpd,
|
|
pszValueName,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)pszBuffer1,
|
|
&cbBuffer );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( err == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
//
|
|
// Item not found, use default value.
|
|
//
|
|
|
|
err = NO_ERROR;
|
|
|
|
if( pszDefaultValue != NULL )
|
|
{
|
|
pszBuffer1 = (CHAR *)FTPD_ALLOC( strlen(pszDefaultValue)+2 );
|
|
|
|
if( pszBuffer1 == NULL )
|
|
{
|
|
err = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
strcpy( pszBuffer1, pszDefaultValue );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( err != NO_ERROR )
|
|
{
|
|
//
|
|
// Tragic error reading registry, abort now.
|
|
//
|
|
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
//
|
|
// pszBuffer1 holds the registry value. Now expand
|
|
// the environment strings if necessary.
|
|
//
|
|
|
|
if( !fExpand || !pszBuffer1 )
|
|
{
|
|
return pszBuffer1;
|
|
}
|
|
|
|
cbBuffer = ExpandEnvironmentStrings( pszBuffer1,
|
|
NULL,
|
|
0 );
|
|
|
|
pszBuffer2 = (CHAR *)FTPD_ALLOC( cbBuffer+1 );
|
|
|
|
if( pszBuffer2 == NULL )
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
if( ExpandEnvironmentStrings( pszBuffer1,
|
|
pszBuffer2,
|
|
cbBuffer ) > cbBuffer )
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
//
|
|
// pszBuffer2 now contains the registry value with
|
|
// environment strings expanded.
|
|
//
|
|
|
|
FTPD_FREE( pszBuffer1 );
|
|
pszBuffer1 = NULL;
|
|
|
|
return pszBuffer2;
|
|
|
|
ErrorCleanup:
|
|
|
|
//
|
|
// Something tragic happend; free any allocated buffers
|
|
// and return NULL to the caller, indicating failure.
|
|
//
|
|
|
|
if( pszBuffer1 != NULL )
|
|
{
|
|
FTPD_FREE( pszBuffer1 );
|
|
pszBuffer1 = NULL;
|
|
}
|
|
|
|
if( pszBuffer2 != NULL )
|
|
{
|
|
FTPD_FREE( pszBuffer2 );
|
|
pszBuffer2 = NULL;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
} // ReadRegistryString
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: WriteRegistryDword
|
|
|
|
SYNOPSIS: Writes a DWORD value to the registry.
|
|
|
|
ENTRY: pszValueName - The name of the value.
|
|
|
|
dwValue - The value to write.
|
|
|
|
RETURNS APIERR - Win32 error code, NO_ERROR if successful.
|
|
|
|
NOTES: This function cannot be called until after
|
|
InitializeGlobals().
|
|
|
|
HISTORY:
|
|
KeithMo 07-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
APIERR
|
|
WriteRegistryDword(
|
|
CHAR * pszValueName,
|
|
DWORD dwValue
|
|
)
|
|
{
|
|
APIERR err;
|
|
|
|
if( hkeyFtpd == NULL )
|
|
{
|
|
err = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
err = RegSetValueEx( hkeyFtpd,
|
|
pszValueName,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwValue,
|
|
sizeof(dwValue) );
|
|
}
|
|
|
|
return err;
|
|
|
|
} // WriteRegistryDword
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: TransferType
|
|
|
|
SYNOPSIS: Generates printable form of a transfer type.
|
|
|
|
ENTRY: type - From the XFER_TYPE enumerator.
|
|
|
|
RETURNS: CHAR * - "ASCII", "BINARY", etc.
|
|
|
|
HISTORY:
|
|
KeithMo 12-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
CHAR *
|
|
TransferType(
|
|
XFER_TYPE type
|
|
)
|
|
{
|
|
CHAR * pszResult = NULL;
|
|
|
|
switch( type )
|
|
{
|
|
case AsciiType :
|
|
pszResult = "ASCII";
|
|
break;
|
|
|
|
case BinaryType :
|
|
pszResult = "BINARY";
|
|
break;
|
|
|
|
default :
|
|
FTPD_PRINT(( "invalid transfer type %d\n",
|
|
type ));
|
|
FTPD_ASSERT( FALSE );
|
|
pszResult = "ASCII";
|
|
break;
|
|
}
|
|
|
|
FTPD_ASSERT( pszResult != NULL );
|
|
|
|
return pszResult;
|
|
|
|
} // TransferType
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: TransferMode
|
|
|
|
SYNOPSIS: Generates printable form of a transfer mode.
|
|
|
|
ENTRY: mode - From the XFER_MODE enumerator.
|
|
|
|
RETURNS: CHAR * - "STREAM", "BLOCK", etc.
|
|
|
|
NOTES: Currently, only the STREAM mode is suppored.
|
|
|
|
HISTORY:
|
|
KeithMo 12-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
CHAR *
|
|
TransferMode(
|
|
XFER_MODE mode
|
|
)
|
|
{
|
|
CHAR * pszResult = NULL;
|
|
|
|
switch( mode )
|
|
{
|
|
case StreamMode :
|
|
pszResult = "STREAM";
|
|
break;
|
|
|
|
case BlockMode :
|
|
pszResult = "BLOCK";
|
|
break;
|
|
|
|
default :
|
|
FTPD_PRINT(( "invalid transfer mode %d\n",
|
|
mode ));
|
|
FTPD_ASSERT( FALSE );
|
|
pszResult = "STREAM";
|
|
break;
|
|
}
|
|
|
|
FTPD_ASSERT( pszResult != NULL );
|
|
|
|
return pszResult;
|
|
|
|
} // TransferMode
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: DisplayBool
|
|
|
|
SYNOPSIS: Generates printable form of a boolean.
|
|
|
|
ENTRY: fFlag - The BOOL to display.
|
|
|
|
RETURNS: CHAR * - "TRUE" or "FALSE".
|
|
|
|
HISTORY:
|
|
KeithMo 17-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
CHAR *
|
|
DisplayBool(
|
|
BOOL fFlag
|
|
)
|
|
{
|
|
return fFlag ? "TRUE" : "FALSE";
|
|
|
|
} // DisplayBool
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: IsDecimalNumber
|
|
|
|
SYNOPSIS: Determines if a given string represents a decimal
|
|
number.
|
|
|
|
ENTRY: psz - The string to scan.
|
|
|
|
RETURNS: BOOL - TRUE if this is a decimal number, FALSE
|
|
otherwise.
|
|
|
|
HISTORY:
|
|
KeithMo 12-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
BOOL
|
|
IsDecimalNumber(
|
|
CHAR * psz
|
|
)
|
|
{
|
|
BOOL fResult = ( *psz != '\0' );
|
|
CHAR ch;
|
|
|
|
while( ch = *psz++ )
|
|
{
|
|
if( ( ch < '0' ) || ( ch > '9' ) )
|
|
{
|
|
fResult = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fResult;
|
|
|
|
} // IsDecimalNumber
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: GetFtpTime
|
|
|
|
SYNOPSIS: Returns the current system time in an internal
|
|
representation used by the FTP Service.
|
|
|
|
RETURNS: DWORD - The current time (seconds since 1970).
|
|
|
|
HISTORY:
|
|
KeithMo 25-Mar-1993 Created.
|
|
|
|
********************************************************************/
|
|
DWORD
|
|
GetFtpTime(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
LARGE_INTEGER timeSystem;
|
|
DWORD cSecondsSince1970 = 0;
|
|
|
|
ntStatus = NtQuerySystemTime( &timeSystem );
|
|
|
|
if( NT_SUCCESS(ntStatus) )
|
|
{
|
|
RtlTimeToSecondsSince1970( &timeSystem, (PULONG)&cSecondsSince1970 );
|
|
}
|
|
|
|
return cSecondsSince1970;
|
|
|
|
} // GetFtpTime
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: AllocErrorText
|
|
|
|
SYNOPSIS: Maps a specified Win32 error code to a textual
|
|
description. In the interest of multithreaded
|
|
safety, this routine will allocate a block of memory
|
|
to contain the text and return a pointer to that
|
|
block. It is up to the caller to free the block
|
|
with FreeErrorText.
|
|
|
|
ENTRY: err - The error to map.
|
|
|
|
RETURNS: CHAR * - A textual description of err. Will be NULL
|
|
if an error occurred while mapping err to text.
|
|
|
|
HISTORY:
|
|
KeithMo 27-Apr-1993 Created.
|
|
|
|
********************************************************************/
|
|
CHAR *
|
|
AllocErrorText(
|
|
APIERR err
|
|
)
|
|
{
|
|
APIERR fmerr = NO_ERROR;
|
|
CHAR * pszText = NULL;
|
|
|
|
if( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_IGNORE_INSERTS
|
|
| FORMAT_MESSAGE_FROM_SYSTEM
|
|
| FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
|
NULL,
|
|
(DWORD)err,
|
|
0,
|
|
(LPTSTR)&pszText,
|
|
0,
|
|
NULL ) == 0 )
|
|
{
|
|
fmerr = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
|
|
IF_DEBUG( COMMANDS )
|
|
{
|
|
if( fmerr == NO_ERROR )
|
|
{
|
|
FTPD_PRINT(( "mapped error %lu to %s\n",
|
|
err,
|
|
pszText ));
|
|
}
|
|
else
|
|
{
|
|
FTPD_PRINT(( "cannot map error %lu to text, error %lu\n",
|
|
err,
|
|
fmerr ));
|
|
}
|
|
}
|
|
|
|
return pszText;
|
|
|
|
} // AllocErrorText
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: FreeErrorText
|
|
|
|
SYNOPSIS: Frees the pointer returned by AllocErrorText.
|
|
|
|
ENTRY: pszText - The text to free. Must be a pointer
|
|
returned by AllocErrorText.
|
|
|
|
HISTORY:
|
|
KeithMo 27-Apr-1993 Created.
|
|
|
|
********************************************************************/
|
|
VOID
|
|
FreeErrorText(
|
|
CHAR * pszText
|
|
)
|
|
{
|
|
LocalFree( (HLOCAL)pszText );
|
|
|
|
} // FreeErrorText
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: OpenDosPath
|
|
|
|
SYNOPSIS: Opens a handle to a DOS pathname.
|
|
|
|
ENTRY: phFile - Will receive the file handle if successful.
|
|
|
|
pszPath - The path to open. This must be a fully
|
|
canonicalized path of the form D:\dir\dir\...
|
|
|
|
DesiredAccess - The desired file access.
|
|
|
|
ShareAccess - File sharing options.
|
|
|
|
OpenOptions - Other NtOpenFile options.
|
|
|
|
HISTORY:
|
|
KeithMo 01-Jun-1993 Created.
|
|
|
|
********************************************************************/
|
|
APIERR
|
|
OpenDosPath(
|
|
HANDLE * phFile,
|
|
CHAR * pszPath,
|
|
ACCESS_MASK DesiredAccess,
|
|
ULONG ShareAccess,
|
|
ULONG OpenOptions
|
|
)
|
|
{
|
|
UNICODE_STRING UnicodeName;
|
|
UNICODE_STRING TranslatedName;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
NTSTATUS NtStatus;
|
|
|
|
//
|
|
// Map the ANSI pathname to UNICODE.
|
|
//
|
|
|
|
if( !RtlCreateUnicodeStringFromAsciiz( &UnicodeName, pszPath ) )
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY; // best guess...
|
|
}
|
|
|
|
//
|
|
// Convert the DOS path (D:\foo) to an NT path (\DosDevices\D:\foo).
|
|
//
|
|
|
|
NtStatus = RtlDosPathNameToNtPathName_U( UnicodeName.Buffer,
|
|
&TranslatedName,
|
|
NULL,
|
|
NULL );
|
|
|
|
if( NT_SUCCESS(NtStatus) )
|
|
{
|
|
InitializeObjectAttributes( &Obja,
|
|
&TranslatedName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
NtStatus = NtOpenFile( phFile,
|
|
DesiredAccess,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
ShareAccess,
|
|
OpenOptions );
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, TranslatedName.Buffer );
|
|
}
|
|
|
|
RtlFreeUnicodeString( &UnicodeName );
|
|
|
|
return NT_SUCCESS(NtStatus) ? NO_ERROR
|
|
: (APIERR)RtlNtStatusToDosError( NtStatus );
|
|
|
|
} // OpenDosPath
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: FlipSlashes
|
|
|
|
SYNOPSIS: Flips the DOS-ish backslashes ('\') into Unix-ish
|
|
forward slashes ('/').
|
|
|
|
ENTRY: pszPath - The path to munge.
|
|
|
|
RETURNS: CHAR * - pszPath.
|
|
|
|
HISTORY:
|
|
KeithMo 04-Jun-1993 Created.
|
|
|
|
********************************************************************/
|
|
CHAR *
|
|
FlipSlashes(
|
|
CHAR * pszPath
|
|
)
|
|
{
|
|
CHAR ch;
|
|
CHAR * pszScan = pszPath;
|
|
|
|
while( ( ch = *pszScan ) != '\0' )
|
|
{
|
|
if( ch == '\\' )
|
|
{
|
|
*pszScan = '/';
|
|
}
|
|
|
|
pszScan++;
|
|
}
|
|
|
|
return pszPath;
|
|
|
|
} // FlipSlashes
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: OpenLogFile
|
|
|
|
SYNOPSIS: Opens the current file access log file in the
|
|
proper directory based on the current mode.
|
|
|
|
RETURNS: FILE * - The open log file if successful, NULL if not.
|
|
|
|
HISTORY:
|
|
KeithMo 21-Jun-1994 Created.
|
|
|
|
********************************************************************/
|
|
FILE *
|
|
OpenLogFile(
|
|
VOID
|
|
)
|
|
{
|
|
USER_DATA * pUserData;
|
|
FILE * pLogFile;
|
|
HANDLE hToken;
|
|
CHAR ch;
|
|
CHAR szFile[MAX_PATH];
|
|
|
|
//
|
|
// Validate the current log file mode.
|
|
//
|
|
|
|
FTPD_ASSERT( nLogFileAccess <= FTPD_LOG_DAILY );
|
|
|
|
if( nLogFileAccess == FTPD_LOG_DISABLED )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Capture the current impersonation token (if any) for this
|
|
// thread. If there IS an impersonation token, then temporarily
|
|
// remove it so that the log file will get opened/created in the
|
|
// proper user context (LocalSystem).
|
|
//
|
|
|
|
pUserData = UserDataPtr;
|
|
|
|
if( pUserData == NULL )
|
|
{
|
|
hToken = NULL;
|
|
}
|
|
else
|
|
{
|
|
hToken = pUserData->hToken;
|
|
|
|
if( hToken != NULL )
|
|
{
|
|
FTPD_REQUIRE( ImpersonateUser( NULL ) );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Construct the file name.
|
|
//
|
|
|
|
strcpy( szFile, pszLogFileDirectory );
|
|
|
|
ch = szFile[strlen( szFile ) - 1];
|
|
|
|
if( ( ch != '\\' ) && ( ch != '/' ) )
|
|
{
|
|
strcat( szFile, "\\" );
|
|
}
|
|
|
|
if( nLogFileAccess == FTPD_LOG_SINGLE )
|
|
{
|
|
strcat( szFile, FTPD_LOG_FILE );
|
|
}
|
|
else
|
|
{
|
|
GetLocalTime( &stPrevious );
|
|
|
|
wsprintf( szFile + strlen( szFile ),
|
|
"FT%02u%02u%02u.LOG",
|
|
stPrevious.wYear % 100,
|
|
stPrevious.wMonth,
|
|
stPrevious.wDay );
|
|
}
|
|
|
|
//
|
|
// Open it.
|
|
//
|
|
|
|
pLogFile = fopen( szFile, "a+" );
|
|
|
|
//
|
|
// If this thread previously had an impersonation token, then
|
|
// restore it.
|
|
//
|
|
|
|
if( hToken != NULL )
|
|
{
|
|
FTPD_REQUIRE( ImpersonateUser( hToken ) );
|
|
}
|
|
|
|
return pLogFile;
|
|
|
|
} // OpenLogFile
|
|
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|