Windows NT 4.0 source code leak
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

/**********************************************************************/
/** 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.
//