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.
2886 lines
70 KiB
2886 lines
70 KiB
/*++
|
|
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
util.cxx
|
|
|
|
Abstract:
|
|
|
|
Contains utility functions
|
|
|
|
Contents:
|
|
new
|
|
delete
|
|
NewString
|
|
CatString
|
|
ResizeBuffer
|
|
_memrchr
|
|
strnistr
|
|
PrivateStrChr
|
|
PlatformType
|
|
PlatformSupport
|
|
GetTimeoutValue
|
|
ProbeReadBuffer
|
|
ProbeWriteBuffer
|
|
ProbeAndSetDword
|
|
ProbeString
|
|
LoadDllEntryPoints
|
|
UnloadDllEntryPoints
|
|
MapInternetError
|
|
CalculateHashValue
|
|
GetCurrentGmtTime
|
|
GetFileExtensionFromUrl
|
|
CheckExpired
|
|
FTtoString
|
|
PrintFileTimeInInternetFormat
|
|
InternetSettingsChanged
|
|
RefreshSslState
|
|
CertHashToStr
|
|
ConvertSecurityInfoIntoCertInfoStruct
|
|
FormatCertInfo
|
|
UnicodeToUtf8
|
|
CountUnicodeToUtf8
|
|
ConvertUnicodeToUtf8
|
|
StringContainsHighAnsi
|
|
IsAddressValidIPString
|
|
IsInGUIModeSetup
|
|
|
|
Author:
|
|
|
|
Richard L Firth (rfirth) 31-Oct-1994
|
|
|
|
Revision History:
|
|
|
|
31-Oct-1994 rfirth
|
|
Created
|
|
|
|
--*/
|
|
|
|
#include <wininetp.h>
|
|
|
|
#if !defined(PAGE_SIZE)
|
|
#define PAGE_SIZE 4096
|
|
#endif
|
|
#define DEFAULT_MAX_EXTENSION_LENGTH 8
|
|
|
|
#pragma warning(disable: 4102)
|
|
|
|
//
|
|
// private prototypes
|
|
//
|
|
|
|
|
|
|
|
//
|
|
// functions
|
|
//
|
|
void * __cdecl operator new(size_t Size) {
|
|
return (void *)ALLOCATE_FIXED_MEMORY((UINT)Size);
|
|
}
|
|
|
|
void __cdecl operator delete(void * Pointer) {
|
|
FREE_MEMORY((HLOCAL)Pointer);
|
|
}
|
|
|
|
LPSTR
|
|
NewString(
|
|
IN LPCSTR lpszIn,
|
|
IN DWORD dwLen
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
kind of version of strdup() but using LocalAlloc to allocate memory
|
|
|
|
Arguments:
|
|
|
|
String - pointer to string to make copy of
|
|
|
|
Return Value:
|
|
|
|
LPSTR
|
|
Success - pointer to duplicated string
|
|
Failure - NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
int len = (dwLen ? dwLen : strlen(lpszIn));
|
|
LPSTR lpszOut;
|
|
|
|
if (lpszOut = (LPSTR)ALLOCATE_FIXED_MEMORY(len+1)) {
|
|
memcpy(lpszOut, lpszIn, len);
|
|
*(lpszOut + len) = '\0';
|
|
}
|
|
return lpszOut;
|
|
}
|
|
|
|
LPWSTR
|
|
NewStringW(
|
|
IN LPCWSTR lpszIn,
|
|
IN DWORD dwLen
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
kind of version of strdup() but using LocalAlloc to allocate memory
|
|
|
|
Arguments:
|
|
|
|
String - pointer to string to make copy of
|
|
|
|
Return Value:
|
|
|
|
LPSTR
|
|
Success - pointer to duplicated string
|
|
Failure - NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
int len = (dwLen ? dwLen : lstrlenW(lpszIn));
|
|
LPWSTR lpszOut;
|
|
|
|
if (lpszOut = (LPWSTR)ALLOCATE_FIXED_MEMORY((sizeof(WCHAR)*(len+1)))) {
|
|
memcpy(lpszOut, lpszIn, len*sizeof(WCHAR));
|
|
*(lpszOut + len) = L'\0';
|
|
}
|
|
return lpszOut;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
kind of version of strcat() but using LocalAlloc to allocate memory
|
|
|
|
Arguments:
|
|
|
|
strings to concatenate
|
|
|
|
Return Value:
|
|
|
|
LPSTR
|
|
Success - pointer to duplicated string
|
|
Failure - NULL
|
|
|
|
--*/
|
|
|
|
LPSTR
|
|
CatString (
|
|
IN LPCSTR lpszLeft,
|
|
IN LPCSTR lpszRight
|
|
)
|
|
{
|
|
int cbLeft = strlen(lpszLeft);
|
|
int cbRight = strlen(lpszRight) + 1; // include null termination
|
|
LPSTR lpszOut;
|
|
|
|
if (lpszOut = (LPSTR) ALLOCATE_FIXED_MEMORY (cbLeft + cbRight)) {
|
|
memcpy (lpszOut, lpszLeft, cbLeft);
|
|
memcpy (lpszOut + cbLeft, lpszRight, cbRight);
|
|
}
|
|
return lpszOut;
|
|
}
|
|
|
|
|
|
|
|
HLOCAL
|
|
ResizeBuffer(
|
|
IN HLOCAL BufferHandle,
|
|
IN DWORD Size,
|
|
IN BOOL Moveable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate, reallocate or free a buffer. If the buffer is moveable memory
|
|
then it must be unlocked. If reallocating, the buffer can be grown or
|
|
shrunk, depending on the current and required sizes
|
|
|
|
Caveat Programmer:
|
|
|
|
Regardless of whether a pre-existing buffer is moveable or fixed memory,
|
|
it will be reallocated with the LMEM_MOVEABLE flag, possibly causing the
|
|
output pointer to be different from the pre-existing pointer
|
|
|
|
Arguments:
|
|
|
|
BufferHandle - current handle of memory buffer. If NULL, a buffer will
|
|
be allocated
|
|
|
|
Size - size of buffer to allocate (or shrink to). If 0, the
|
|
buffer will be freed
|
|
|
|
Moveable - if TRUE and allocating memory then allocates a moveable
|
|
memory buffer, else fixed
|
|
|
|
Return Value:
|
|
|
|
HLOCAL
|
|
Success - handle of moveable memory buffer
|
|
|
|
Failure - NULL;
|
|
|
|
--*/
|
|
|
|
{
|
|
INET_ASSERT(!Moveable);
|
|
|
|
if (BufferHandle == NULL) {
|
|
|
|
//
|
|
// don't allocate anything if no size - LocalAlloc() will return pointer
|
|
// to memory object marked as discarded if we request a zero-length
|
|
// moveable buffer. But I know that if Size is also 0, I don't want a
|
|
// buffer at all, discarded or otherwise
|
|
//
|
|
|
|
if (Size != 0) {
|
|
BufferHandle = ALLOCATE_MEMORY(Moveable ? LMEM_MOVEABLE : LMEM_FIXED, Size);
|
|
}
|
|
} else if (Size == 0) {
|
|
BufferHandle = FREE_MEMORY(BufferHandle);
|
|
|
|
INET_ASSERT(BufferHandle == NULL);
|
|
|
|
} else {
|
|
HLOCAL hNewBuff = REALLOCATE_MEMORY(BufferHandle, Size, LMEM_MOVEABLE);
|
|
if(!hNewBuff)
|
|
{
|
|
FREE_MEMORY(BufferHandle);
|
|
}
|
|
BufferHandle = hNewBuff;
|
|
}
|
|
return BufferHandle;
|
|
}
|
|
|
|
|
|
LPSTR
|
|
_memrchr(
|
|
IN LPSTR lpString,
|
|
IN CHAR cTarget,
|
|
IN INT iLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reverse find character in string
|
|
|
|
Arguments:
|
|
|
|
lpString - pointer to string in which to locate character
|
|
|
|
cTarget - target character to find
|
|
|
|
iLength - length of string
|
|
|
|
Return Value:
|
|
|
|
LPSTR - pointer to located character or NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
for (--iLength; (iLength >= 0) && (lpString[iLength] != cTarget); --iLength) {
|
|
|
|
//
|
|
// empty loop
|
|
//
|
|
|
|
}
|
|
return (iLength < 0) ? NULL : &lpString[iLength];
|
|
}
|
|
|
|
|
|
LPSTR
|
|
strnistr(
|
|
IN LPSTR str1,
|
|
IN LPSTR str2,
|
|
IN DWORD Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Case-insensitive search for substring
|
|
|
|
Arguments:
|
|
|
|
str1 - string to search in
|
|
|
|
str2 - substring to find
|
|
|
|
Length - of str1
|
|
|
|
Return Value:
|
|
|
|
LPSTR - pointer to located str2 in str1 or NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!*str2) {
|
|
return str1;
|
|
}
|
|
|
|
for (LPSTR cp = str1; *cp && Length; ++cp, --Length) {
|
|
|
|
LPSTR s1 = cp;
|
|
LPSTR s2 = str2;
|
|
DWORD l2 = Length;
|
|
|
|
while (*s1 && *s2 && l2 && (toupper(*s1) == toupper(*s2))) {
|
|
++s1;
|
|
++s2;
|
|
--l2;
|
|
}
|
|
|
|
if (!*s2) {
|
|
return cp;
|
|
}
|
|
|
|
if (!l2) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
LPSTR
|
|
FASTCALL
|
|
PrivateStrChr(
|
|
IN LPCSTR lpStart,
|
|
IN WORD wMatch
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find first occurrence of character in string
|
|
|
|
Private implimentation of StrChrA, this code is based on
|
|
a code snipet from ShlWapi, but by placing it here,
|
|
we can remove the extra NLS support that was needed
|
|
in SHLWAPI. This piece of code is over twice as fast
|
|
as the call into SHLWAPI.
|
|
|
|
Arguments:
|
|
|
|
lpStart - points to start of null terminated string
|
|
|
|
wMatch - the character to match
|
|
|
|
Return Value:
|
|
|
|
LPSTR - ptr to the first occurrence of ch in str, NULL if not found.
|
|
|
|
--*/
|
|
{
|
|
for ( ; *lpStart; lpStart++)
|
|
{
|
|
if ((BYTE)*lpStart == LOBYTE(wMatch)) {
|
|
return((LPSTR)lpStart);
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetTickCountWrap()
|
|
{
|
|
#ifdef DEBUG_GETTICKCOUNT
|
|
static BOOL fInit = FALSE;
|
|
static DWORD dwDelta = 0;
|
|
static DWORD dwBasis = 0;
|
|
|
|
if (!fInit)
|
|
{
|
|
HKEY clientKey;
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"),
|
|
0, // reserved
|
|
KEY_QUERY_VALUE,
|
|
&clientKey))
|
|
{
|
|
DWORD dwSize = sizeof(dwDelta);
|
|
RegQueryValueEx(clientKey, "RollOverDelta", NULL, NULL, (LPBYTE)&dwDelta, &dwSize);
|
|
}
|
|
dwBasis = GetTickCount();
|
|
fInit = TRUE;
|
|
}
|
|
DWORD dwResult = GetTickCount() - dwBasis + dwDelta;
|
|
return dwResult;
|
|
#else
|
|
return GetTickCount();
|
|
#endif
|
|
}
|
|
|
|
|
|
DWORD
|
|
PlatformType(
|
|
IN OUT LPDWORD lpdwVersion5os
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the platform type based on the operating system information. We use
|
|
our own platform types
|
|
|
|
Arguments:
|
|
|
|
lpdwVersion5os - optional pointer to value, set to TRUE if we on NT 5
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Failure - PLATFORM_TYPE_UNKNOWN
|
|
either GetVersionEx() failed, or we are running on an
|
|
unrecognized operating system
|
|
|
|
Success - PLATFORM_TYPE_WIN95
|
|
The world's favourite desktop O/S
|
|
|
|
PLATFORM_TYPE_WINNT
|
|
The world's best O/S on top of anything
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifndef UNIX
|
|
OSVERSIONINFO versionInfo;
|
|
|
|
*lpdwVersion5os = FALSE;
|
|
|
|
versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
|
|
if (GetVersionEx(&versionInfo)) {
|
|
switch (versionInfo.dwPlatformId) {
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
if(versionInfo.dwMinorVersion >= 90) {
|
|
GlobalPlatformMillennium = TRUE;
|
|
}
|
|
return PLATFORM_TYPE_WIN95;
|
|
|
|
case VER_PLATFORM_WIN32_NT:
|
|
|
|
if ( lpdwVersion5os &&
|
|
versionInfo.dwMajorVersion >= 5 ) {
|
|
*lpdwVersion5os = TRUE;
|
|
|
|
if (versionInfo.dwMinorVersion >= 1) {
|
|
GlobalPlatformWhistler = TRUE;
|
|
}
|
|
}
|
|
return PLATFORM_TYPE_WINNT;
|
|
|
|
}
|
|
|
|
}
|
|
return PLATFORM_TYPE_UNKNOWN;
|
|
#else
|
|
return PLATFORM_TYPE_UNIX;
|
|
#endif /* UNIX */
|
|
}
|
|
|
|
//
|
|
//DWORD
|
|
//PlatformSupport(
|
|
// VOID
|
|
// )
|
|
//
|
|
///*++
|
|
//
|
|
//Routine Description:
|
|
//
|
|
// Returns a bitmap of capabilities supported by this operating system
|
|
//
|
|
//Arguments:
|
|
//
|
|
// None.
|
|
//
|
|
//Return Value:
|
|
//
|
|
// DWORD
|
|
//
|
|
//--*/
|
|
//
|
|
//{
|
|
// switch (PlatformType()) {
|
|
// case PLATFORM_TYPE_WINNT:
|
|
// return PLATFORM_SUPPORTS_UNICODE;
|
|
// }
|
|
// return 0;
|
|
//}
|
|
|
|
|
|
DWORD
|
|
GetTimeoutValue(
|
|
IN DWORD TimeoutOption
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets a timeout value. The timeout is retrieved from the current handle. If
|
|
it is not available in the current handle then the parent handle is checked
|
|
(actually the current handle is derived from the parent, so this doesn't
|
|
really do anything). If the value is still not available, then the global
|
|
default is used
|
|
|
|
Arguments:
|
|
|
|
TimeoutOption - INTERNET_OPTION_ value used to specify the timeout value
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Requested timeout value
|
|
|
|
--*/
|
|
|
|
{
|
|
HINTERNET hInternet;
|
|
DWORD timeout;
|
|
DWORD error;
|
|
|
|
hInternet = InternetGetMappedObjectHandle();
|
|
if (hInternet != NULL) {
|
|
error = RGetTimeout(hInternet, TimeoutOption, &timeout);
|
|
}
|
|
if ((hInternet == NULL) || (error != ERROR_SUCCESS)) {
|
|
switch (TimeoutOption) {
|
|
case INTERNET_OPTION_SEND_TIMEOUT:
|
|
timeout = GlobalSendTimeout;
|
|
break;
|
|
|
|
case INTERNET_OPTION_RECEIVE_TIMEOUT:
|
|
timeout = GlobalReceiveTimeout;
|
|
break;
|
|
|
|
case INTERNET_OPTION_DATA_SEND_TIMEOUT:
|
|
timeout = GlobalDataSendTimeout;
|
|
break;
|
|
|
|
case INTERNET_OPTION_DATA_RECEIVE_TIMEOUT:
|
|
timeout = GlobalDataReceiveTimeout;
|
|
break;
|
|
|
|
case INTERNET_OPTION_CONNECT_TIMEOUT:
|
|
timeout = GlobalConnectTimeout;
|
|
break;
|
|
|
|
case INTERNET_OPTION_CONNECT_RETRIES:
|
|
timeout = GlobalConnectRetries;
|
|
break;
|
|
|
|
case INTERNET_OPTION_FROM_CACHE_TIMEOUT:
|
|
timeout = GlobalFromCacheTimeout;
|
|
break;
|
|
}
|
|
}
|
|
return timeout;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ProbeReadBuffer(
|
|
IN LPVOID lpBuffer,
|
|
IN DWORD dwBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Probes a buffer for readability. Used as part of API parameter validation,
|
|
this function tests the first and last locations in a buffer. This is not
|
|
as strict as the IsBadXPtr() Windows APIs, but it means we don't have to
|
|
test every location in the buffer
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - pointer to buffer to test
|
|
|
|
dwBufferLength - length of buffer
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_PARAMETER
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
|
|
//
|
|
// the buffer can be NULL if the probe length is 0. Otherwise, its an error
|
|
//
|
|
|
|
if (lpBuffer == NULL) {
|
|
error = (dwBufferLength == 0) ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER;
|
|
} else if (dwBufferLength != 0) {
|
|
__try {
|
|
|
|
LPBYTE p;
|
|
LPBYTE end;
|
|
volatile BYTE b;
|
|
|
|
p = (LPBYTE)lpBuffer;
|
|
end = p + dwBufferLength - 1;
|
|
b = *end;
|
|
|
|
//
|
|
// visit every page in the buffer - it doesn't matter that we may
|
|
// test a character in the middle of a page
|
|
//
|
|
|
|
for (; p < end; p += PAGE_SIZE) {
|
|
b = *p;
|
|
}
|
|
error = ERROR_SUCCESS;
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
error = ERROR_INVALID_PARAMETER;
|
|
}
|
|
ENDEXCEPT
|
|
} else {
|
|
|
|
//
|
|
// zero-length buffer
|
|
//
|
|
|
|
error = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ProbeWriteBuffer(
|
|
IN LPVOID lpBuffer,
|
|
IN DWORD dwBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Probes a buffer for writeability. Used as part of API parameter validation,
|
|
this function tests the first and last locations in a buffer. This is not
|
|
as strict as the IsBadXPtr() Windows APIs, but it means we don't have to
|
|
test every location in the buffer
|
|
|
|
Arguments:
|
|
|
|
lpBuffer - pointer to buffer to test
|
|
|
|
dwBufferLength - length of buffer
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_PARAMETER
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
|
|
//
|
|
// the buffer can be NULL if the probe length is 0. Otherwise, its an error
|
|
//
|
|
|
|
if (lpBuffer == NULL) {
|
|
error = (dwBufferLength == 0) ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER;
|
|
} else if (dwBufferLength != 0) {
|
|
__try {
|
|
|
|
LPBYTE p;
|
|
LPBYTE end;
|
|
volatile BYTE b;
|
|
|
|
p = (LPBYTE)lpBuffer;
|
|
end = p + dwBufferLength - 1;
|
|
b = *end;
|
|
*end = b;
|
|
|
|
//
|
|
// visit every page in the buffer - it doesn't matter that we may
|
|
// test a character in the middle of a page
|
|
//
|
|
|
|
for (; p < end; p += PAGE_SIZE) {
|
|
b = *p;
|
|
*p = b;
|
|
}
|
|
error = ERROR_SUCCESS;
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
error = ERROR_INVALID_PARAMETER;
|
|
}
|
|
ENDEXCEPT
|
|
} else {
|
|
|
|
//
|
|
// zero-length buffer
|
|
//
|
|
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ProbeAndSetDword(
|
|
IN LPDWORD lpDword,
|
|
IN DWORD dwValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Probes a single DWORD buffer for writeability, and as a side-effect sets it
|
|
to a default value. Used as part of API parameter validation
|
|
|
|
Arguments:
|
|
|
|
lpDword - pointer to DWORD buffer to test
|
|
|
|
dwValue - default value to set
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_PARAMETER
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
|
|
__try {
|
|
*lpDword = dwValue;
|
|
error = ERROR_SUCCESS;
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
error = ERROR_INVALID_PARAMETER;
|
|
}
|
|
ENDEXCEPT
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ProbeString(
|
|
IN LPSTR lpString,
|
|
OUT LPDWORD lpdwStringLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Probes a string buffer for readability, and returns the length of the string
|
|
|
|
Arguments:
|
|
|
|
lpString - pointer to string to check
|
|
|
|
lpdwStringLength - returned length of string
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_PARAMETER
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
DWORD length;
|
|
|
|
//
|
|
// initialize string length and return code
|
|
//
|
|
|
|
length = 0;
|
|
error = ERROR_SUCCESS;
|
|
|
|
//
|
|
// the buffer can be NULL
|
|
//
|
|
|
|
if (lpString != NULL) {
|
|
__try {
|
|
|
|
//
|
|
// unfortunately, for a string, we have to visit every location in
|
|
// the buffer to find the terminator
|
|
//
|
|
|
|
while (*lpString != '\0') {
|
|
++length;
|
|
++lpString;
|
|
}
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
error = ERROR_INVALID_PARAMETER;
|
|
}
|
|
ENDEXCEPT
|
|
}
|
|
|
|
*lpdwStringLength = length;
|
|
|
|
return error;
|
|
}
|
|
|
|
DWORD
|
|
ProbeStringW(
|
|
IN LPWSTR lpString,
|
|
OUT LPDWORD lpdwStringLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Probes a wide string buffer for readability, and returns the length of the string
|
|
|
|
Arguments:
|
|
|
|
lpString - pointer to string to check
|
|
|
|
lpdwStringLength - returned length of string
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_PARAMETER
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
DWORD length;
|
|
|
|
//
|
|
// initialize string length and return code
|
|
//
|
|
|
|
length = 0;
|
|
error = ERROR_SUCCESS;
|
|
|
|
//
|
|
// the buffer can be NULL
|
|
//
|
|
|
|
if (lpString != NULL) {
|
|
__try {
|
|
|
|
//
|
|
// unfortunately, for a string, we have to visit every location in
|
|
// the buffer to find the terminator
|
|
//
|
|
|
|
while (*lpString != '\0') {
|
|
++length;
|
|
++lpString;
|
|
}
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
error = ERROR_INVALID_PARAMETER;
|
|
}
|
|
ENDEXCEPT
|
|
}
|
|
|
|
*lpdwStringLength = length;
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
LoadDllEntryPoints(
|
|
IN OUT LPDLL_INFO lpDllInfo,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dynamically loads a DLL and the entry points described in lpDllEntryPoints
|
|
|
|
Assumes: 1. Any thread serialization taken care of by caller
|
|
|
|
2. Module handle, entry point addresses and reference count
|
|
already set to 0 if this is first time the DLL_INFO is
|
|
being used to load the DLL
|
|
|
|
Arguments:
|
|
|
|
lpDllInfo - pointer to DLL_INFO structure containing all info about DLL
|
|
and entry points to load
|
|
|
|
dwFlags - flags controlling how this function operates:
|
|
|
|
LDEP_PARTIAL_LOAD_OK
|
|
- not fatal if we can't load all entry points
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - Win32 error
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_UTIL,
|
|
Dword,
|
|
"LoadDllEntryPoints",
|
|
"%x [%q, %d], %#x",
|
|
lpDllInfo,
|
|
lpDllInfo->lpszDllName,
|
|
lpDllInfo->dwNumberOfEntryPoints,
|
|
dwFlags
|
|
));
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
|
|
if (lpDllInfo->hModule == NULL) {
|
|
|
|
DWORD dwMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
HMODULE hDll = LoadLibrary(lpDllInfo->lpszDllName);
|
|
|
|
if (hDll != NULL) {
|
|
lpDllInfo->hModule = hDll;
|
|
lpDllInfo->LoadCount = 1;
|
|
|
|
for (DWORD i = 0; i < lpDllInfo->dwNumberOfEntryPoints; ++i) {
|
|
|
|
FARPROC proc = GetProcAddress(
|
|
hDll,
|
|
lpDllInfo->lpEntryPoints[i].lpszProcedureName
|
|
);
|
|
|
|
*lpDllInfo->lpEntryPoints[i].lplpfnProcedure = proc;
|
|
if ((proc == NULL) && !(dwFlags & LDEP_PARTIAL_LOAD_OK)) {
|
|
error = GetLastError();
|
|
UnloadDllEntryPoints(lpDllInfo, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
error = GetLastError();
|
|
}
|
|
SetErrorMode(dwMode);
|
|
} else {
|
|
|
|
DEBUG_PRINT(UTIL,
|
|
INFO,
|
|
("info for %q already loaded\n",
|
|
lpDllInfo->lpszDllName
|
|
));
|
|
|
|
InterlockedIncrement(&lpDllInfo->LoadCount);
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
UnloadDllEntryPoints(
|
|
IN OUT LPDLL_INFO lpDllInfo,
|
|
IN BOOL bForce
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Undoes the work of LoadDllEntryPoints()
|
|
|
|
Assumes: 1. Any thread serialization taken care of by caller
|
|
|
|
Arguments:
|
|
|
|
lpDllInfo - pointer to DLL_INFO structure containing all info about DLL
|
|
and (loaded) entry points
|
|
|
|
bForce - TRUE if the DLL will be unloaded irrespective of the usage
|
|
count
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - Win32 error
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_UTIL,
|
|
Dword,
|
|
"UnloadDllEntryPoints",
|
|
"%x [%q, %d], %B",
|
|
lpDllInfo,
|
|
lpDllInfo->lpszDllName,
|
|
lpDllInfo->dwNumberOfEntryPoints,
|
|
bForce
|
|
));
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
|
|
if (bForce) {
|
|
lpDllInfo->LoadCount = 0;
|
|
} else if (InterlockedDecrement(&lpDllInfo->LoadCount) == 0) {
|
|
bForce = TRUE;
|
|
}
|
|
if (bForce && (lpDllInfo->hModule != NULL)) {
|
|
if (!FreeLibrary(lpDllInfo->hModule)) {
|
|
error = GetLastError();
|
|
}
|
|
|
|
//
|
|
// even if FreeLibrary() failed we clear out the load info
|
|
//
|
|
|
|
lpDllInfo->hModule = NULL;
|
|
for (DWORD i = 0; i < lpDllInfo->dwNumberOfEntryPoints; ++i) {
|
|
*lpDllInfo->lpEntryPoints[i].lplpfnProcedure = NULL;
|
|
}
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
#ifndef CERT_E_WRONG_USAGE
|
|
# define CERT_E_WRONG_USAGE _HRESULT_TYPEDEF_(0x800B0110)
|
|
#endif
|
|
|
|
|
|
DWORD
|
|
MapInternetError(
|
|
IN DWORD dwErrorCode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps a winsock/RPC/transport error into a more user-friendly WinInet error,
|
|
and stores the original error in the per-thread context so that the app can
|
|
retrieve it if it really cares
|
|
|
|
N.B. We should no longer be receiving winsock errors directly at the WinInet
|
|
interface. They are available via InternetGetLastResponseInfo()
|
|
|
|
Arguments:
|
|
|
|
dwErrorCode - original (winsock) error code to map
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Mapped error code, or the orignal error if its not one that we handle
|
|
|
|
--*/
|
|
|
|
{
|
|
LPINTERNET_THREAD_INFO lpThreadInfo;
|
|
|
|
DEBUG_ENTER((DBG_UTIL,
|
|
Dword,
|
|
"MapInternetError",
|
|
"%#x [%s]",
|
|
dwErrorCode,
|
|
InternetMapError(dwErrorCode)
|
|
));
|
|
|
|
lpThreadInfo = InternetGetThreadInfo();
|
|
if (lpThreadInfo) {
|
|
lpThreadInfo->dwMappedErrorCode = dwErrorCode;
|
|
}
|
|
|
|
switch (dwErrorCode) {
|
|
|
|
case SEC_E_INSUFFICIENT_MEMORY :
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
|
|
case SEC_E_INVALID_HANDLE :
|
|
case SEC_E_UNSUPPORTED_FUNCTION :
|
|
case SEC_E_TARGET_UNKNOWN :
|
|
case SEC_E_INTERNAL_ERROR :
|
|
case SEC_E_SECPKG_NOT_FOUND :
|
|
case SEC_E_NOT_OWNER :
|
|
case SEC_E_CANNOT_INSTALL :
|
|
case SEC_E_INVALID_TOKEN :
|
|
case SEC_E_CANNOT_PACK :
|
|
case SEC_E_QOP_NOT_SUPPORTED :
|
|
case SEC_E_NO_IMPERSONATION :
|
|
case SEC_E_LOGON_DENIED :
|
|
case SEC_E_UNKNOWN_CREDENTIALS :
|
|
case SEC_E_NO_CREDENTIALS :
|
|
case SEC_E_MESSAGE_ALTERED :
|
|
case SEC_E_OUT_OF_SEQUENCE :
|
|
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
|
|
case SEC_I_CONTINUE_NEEDED :
|
|
case SEC_I_COMPLETE_NEEDED :
|
|
case SEC_I_COMPLETE_AND_CONTINUE :
|
|
case SEC_I_LOCAL_LOGON :
|
|
case SEC_E_BAD_PKGID :
|
|
case SEC_E_CONTEXT_EXPIRED :
|
|
case SEC_E_INCOMPLETE_MESSAGE :
|
|
dwErrorCode = ERROR_INTERNET_SECURITY_CHANNEL_ERROR;
|
|
break;
|
|
|
|
// Cert and Encryption errors
|
|
|
|
case CERT_E_EXPIRED:
|
|
case CERT_E_VALIDITYPERIODNESTING:
|
|
dwErrorCode = ERROR_INTERNET_SEC_CERT_DATE_INVALID;
|
|
break;
|
|
|
|
case CERT_E_UNTRUSTEDROOT:
|
|
dwErrorCode = ERROR_INTERNET_INVALID_CA;
|
|
break;
|
|
|
|
case CERT_E_CN_NO_MATCH:
|
|
dwErrorCode = ERROR_INTERNET_SEC_CERT_CN_INVALID;
|
|
break;
|
|
|
|
case CRYPT_E_REVOKED:
|
|
dwErrorCode = ERROR_INTERNET_SEC_CERT_REVOKED;
|
|
break;
|
|
|
|
// ignore revocation if the certificate does not have a CDP
|
|
case CRYPT_E_NO_REVOCATION_CHECK:
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
break;
|
|
|
|
case CRYPT_E_REVOCATION_OFFLINE:
|
|
dwErrorCode = ERROR_INTERNET_SEC_CERT_REV_FAILED;
|
|
break;
|
|
|
|
case CERT_E_ROLE:
|
|
case CERT_E_PATHLENCONST:
|
|
case CERT_E_CRITICAL:
|
|
case CERT_E_PURPOSE:
|
|
case CERT_E_ISSUERCHAINING:
|
|
case CERT_E_MALFORMED:
|
|
case CERT_E_CHAINING:
|
|
// We can't allow connection if server doesn't have a server auth certificate.
|
|
// To force CERT_E_WRONG_USAGE to error out we map it to the error below.
|
|
// In the future we need to map it to it's own non-recoverable error, so we can
|
|
// give the user a specific error message.
|
|
case CERT_E_WRONG_USAGE:
|
|
dwErrorCode = ERROR_INTERNET_SEC_INVALID_CERT;
|
|
break;
|
|
|
|
case WSAEINTR:
|
|
case WSAEBADF:
|
|
case WSAEACCES:
|
|
case WSAEFAULT:
|
|
case WSAEINVAL:
|
|
case WSAEMFILE:
|
|
case WSAEADDRINUSE:
|
|
case WSAEADDRNOTAVAIL:
|
|
dwErrorCode = ERROR_INTERNET_INTERNAL_ERROR;
|
|
break;
|
|
|
|
case WSAENOTSOCK:
|
|
|
|
//
|
|
// typically, if we see this error its because we tried to use a closed
|
|
// socket handle
|
|
//
|
|
dwErrorCode = ERROR_INTERNET_OPERATION_CANCELLED;
|
|
break;
|
|
|
|
case WSAEWOULDBLOCK:
|
|
case WSAEINPROGRESS:
|
|
case WSAEALREADY:
|
|
case WSAEDESTADDRREQ:
|
|
case WSAEPROTOTYPE:
|
|
case WSAENOPROTOOPT:
|
|
case WSAEPROTONOSUPPORT:
|
|
case WSAESOCKTNOSUPPORT:
|
|
case WSAEOPNOTSUPP:
|
|
case WSAEISCONN:
|
|
case WSAETOOMANYREFS:
|
|
case WSAELOOP:
|
|
case WSAENAMETOOLONG:
|
|
case WSAENOTEMPTY:
|
|
case WSAEPROCLIM:
|
|
case WSAEUSERS:
|
|
case WSAEDQUOT:
|
|
case WSAESTALE:
|
|
case WSAEREMOTE:
|
|
case WSAEDISCON:
|
|
case WSASYSNOTREADY:
|
|
case WSAVERNOTSUPPORTED:
|
|
case WSANOTINITIALISED:
|
|
|
|
//
|
|
// currently unmapped errors
|
|
//
|
|
|
|
break;
|
|
|
|
case WSAEMSGSIZE:
|
|
dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
|
|
break;
|
|
|
|
case WSAEPFNOSUPPORT:
|
|
case WSAEAFNOSUPPORT:
|
|
dwErrorCode = ERROR_INTERNET_TCPIP_NOT_INSTALLED;
|
|
break;
|
|
|
|
case WSAECONNABORTED:
|
|
case WSAESHUTDOWN:
|
|
dwErrorCode = ERROR_INTERNET_CONNECTION_ABORTED;
|
|
break;
|
|
|
|
case WSAECONNRESET:
|
|
case WSAENETRESET:
|
|
dwErrorCode = ERROR_INTERNET_CONNECTION_RESET;
|
|
break;
|
|
|
|
case WSAENOBUFS:
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
|
|
case WSAETIMEDOUT:
|
|
dwErrorCode = ERROR_INTERNET_TIMEOUT;
|
|
break;
|
|
|
|
case WSAENETDOWN:
|
|
case WSAECONNREFUSED:
|
|
case WSAENETUNREACH:
|
|
case WSAENOTCONN:
|
|
dwErrorCode = ERROR_INTERNET_CANNOT_CONNECT;
|
|
break;
|
|
|
|
case WSAEHOSTDOWN:
|
|
case WSAEHOSTUNREACH:
|
|
case WSAHOST_NOT_FOUND:
|
|
case WSATRY_AGAIN:
|
|
case WSANO_RECOVERY:
|
|
case WSANO_DATA:
|
|
dwErrorCode = ERROR_INTERNET_NAME_NOT_RESOLVED;
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUG_PRINT(UTIL,
|
|
WARNING,
|
|
("MapInternetError(): unmapped error code %d [%#x]\n",
|
|
dwErrorCode,
|
|
dwErrorCode
|
|
));
|
|
|
|
break;
|
|
}
|
|
|
|
DEBUG_LEAVE(dwErrorCode);
|
|
|
|
return dwErrorCode;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CalculateHashValue(
|
|
IN LPSTR lpszString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculate a hash number given a string
|
|
|
|
Arguments:
|
|
|
|
lpszString - string to hash
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD hashValue = 0;
|
|
DWORD position = 1;
|
|
|
|
while (*lpszString) {
|
|
hashValue += *lpszString * position;
|
|
++lpszString;
|
|
++position;
|
|
}
|
|
return hashValue;
|
|
}
|
|
|
|
|
|
|
|
VOID GetCurrentGmtTime(
|
|
LPFILETIME lpFt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine returns the current GMT time
|
|
|
|
Arguments:
|
|
|
|
lpFt FILETIME strucutre in which this is returned
|
|
|
|
Returns:
|
|
|
|
Comments:
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME sSysT;
|
|
|
|
GetSystemTime(&sSysT);
|
|
SystemTimeToFileTime(&sSysT, lpFt);
|
|
}
|
|
|
|
///*** DwRemoveDots - Remove any dots from a path name
|
|
//**
|
|
//** Synopsis
|
|
//** DWORD DwRemoveDots (pchPath)
|
|
//** Lifted from win95 kernel.
|
|
//**
|
|
//** Input:
|
|
//** pchPath - A path string
|
|
//**
|
|
//**
|
|
//** Output:
|
|
//** returns the number of double dot levels removed from front
|
|
//**
|
|
//** Errors:
|
|
//** returns dwInvalid if invalid path
|
|
//**
|
|
//** Description:
|
|
//** Removes ..\ and .\ sequences from a path string. The path
|
|
//** string should not include the root drive or net name portion.
|
|
//** The return value of is the number of levels removed from the
|
|
//** start of the string. Levels removed from inside the string
|
|
//** will not be returned. For example:
|
|
//**
|
|
//** String Result Return
|
|
//**
|
|
//** ..\..\dir1 dir1 2
|
|
//** dir1\..\dir2 dir2 0
|
|
//** dir1\..\..\dir2 dir2 1
|
|
//** .\dir1 dir1 0
|
|
//** dir1\.\dir2 dir1\dir2 0
|
|
//**
|
|
//** A backslash at the start of the string will be ignored.
|
|
//*/
|
|
//
|
|
//// File and path definitions
|
|
//
|
|
//#define chExtSep '.'
|
|
//#define szExtSep "."
|
|
//#define chNetIni '\\'
|
|
//#define chDirSep '\\'
|
|
//#define szDirSep "\\"
|
|
//#define chDirSep2 '/'
|
|
//#define chDrvSep ':'
|
|
//#define chRelDir '.'
|
|
//#define chEnvSep ';'
|
|
//#define chWldChr '?'
|
|
//#define chWldSeq '*'
|
|
//#define chMinDrv 'A'
|
|
//#define chMaxDrv 'Z'
|
|
//#define chMinDrvLow 'a'
|
|
//#define chMaxDrvLow 'z'
|
|
//
|
|
//DWORD
|
|
//DwRemoveDots (
|
|
// char * pchPath
|
|
///*++
|
|
//
|
|
//Routine Description:
|
|
// Removes ./ ../ etc from a path to normalize it
|
|
//
|
|
//Arguments:
|
|
//
|
|
// pchPath path string
|
|
//
|
|
//Returns:
|
|
//
|
|
// Count of levels dealt with
|
|
//
|
|
//Comments:
|
|
//
|
|
// Lifted from win95 kernel
|
|
//
|
|
//--*/
|
|
//)
|
|
// {
|
|
// BOOL fInside = FALSE;
|
|
// DWORD cLevel = 0;
|
|
// DWORD cBackup;
|
|
// register char * pchR;
|
|
// register char * pchL;
|
|
//
|
|
//#ifdef MAYBE
|
|
// // Check for invalid characters
|
|
// if (!FFixPathChars(pchPath)) {
|
|
// // No code required.
|
|
// return dwInvalid;
|
|
// }
|
|
//
|
|
//#endif //MAYBE
|
|
// // Skip slashes
|
|
// for (; *pchPath == chDirSep2; pchPath++)
|
|
// ;
|
|
// pchL = pchR = pchPath;
|
|
//
|
|
// // Loop through handling each directory part
|
|
// while (*pchR) {
|
|
// // This part starts with dot. Is it one or more?
|
|
// if (*pchR++ == chRelDir) {
|
|
// for (cBackup = 0; *pchR == chRelDir; cBackup++, pchR++)
|
|
// ;
|
|
// if (cBackup) {
|
|
// // More than one dot. Back up the left pointer.
|
|
// if ((*pchR != chDirSep2) && (*pchR != '\0')) {
|
|
// // we got a [.]+X (X != '\') might be an LFN
|
|
// // process this as a name
|
|
// goto name_processing;
|
|
// }
|
|
// // Doesn't advance for ending ..
|
|
// for (; *pchR == chDirSep2; pchR++)
|
|
// ;
|
|
// if (fInside) {
|
|
// for (; cBackup; cBackup--) {
|
|
// if (pchL <= pchPath) {
|
|
// cLevel += cBackup;
|
|
// fInside = FALSE;
|
|
// break;
|
|
// }
|
|
// // Remove the previous part
|
|
// for (pchL -= 2; *pchL != chDirSep2; pchL--) {
|
|
// if (pchL <= pchPath) {
|
|
// fInside = FALSE;
|
|
// pchL--;
|
|
// break;
|
|
// }
|
|
// }
|
|
// pchL++;
|
|
// }
|
|
// } else {
|
|
// cLevel += cBackup;
|
|
// }
|
|
// // Subtract ending backslash if not root
|
|
// if ((*pchR == '\0') && (pchL != pchPath))
|
|
// pchL--;
|
|
// strcpy(pchL, pchR);
|
|
// pchR = pchL;
|
|
// } else {
|
|
// // This part starts with one dot. Throw it away.
|
|
// if (*pchR != chDirSep2) {
|
|
// // Special case "\." by converting it to ""
|
|
// // unless it is a root, when it becomes "\".
|
|
// if (*pchR == '\0') {
|
|
// if (pchL == pchPath)
|
|
// *(pchR-1) = '\0'; // root
|
|
// else
|
|
// *(pchR-2) = '\0'; // not root
|
|
// return cLevel;
|
|
// }
|
|
// // we started with a '.' and then there was no '\'
|
|
// // might be an LFN name
|
|
// goto name_processing;
|
|
// }
|
|
// pchR++;
|
|
// strcpy(pchL, pchR);
|
|
// pchR = pchL;
|
|
// }
|
|
// } else {
|
|
//name_processing:
|
|
// // This part is a name. Skip it.
|
|
// fInside = TRUE;
|
|
// for (; TRUE; pchR++) {
|
|
// if (*pchR == chDirSep2) {
|
|
// if (*(pchR-1) == chRelDir) {
|
|
// // This name has one or more dots at the end.
|
|
// // Remove the last dot (NT3.5 does this).
|
|
// pchL = pchR-1;
|
|
// strcpy(pchL, pchR);
|
|
// pchR = pchL; // point to chDirSep2 again
|
|
// }
|
|
// for (; *pchR == chDirSep2; pchR++)
|
|
// ;
|
|
// break;
|
|
// } else if (*pchR == '\0') {
|
|
// // Remove trailing dots.
|
|
// // NB Can't fall off the beginning since the first char
|
|
// // of the current path element was not chRelDir.
|
|
// for (; *(pchR-1) == chRelDir; pchR--)
|
|
// ;
|
|
// // Overstore the first trailing dot, if there is one.
|
|
// *pchR = '\0';
|
|
// break;
|
|
// }
|
|
// }
|
|
// pchL = pchR;
|
|
// }
|
|
// }
|
|
// return cLevel;
|
|
//}
|
|
|
|
|
|
//#define OLD
|
|
|
|
#define EXE_EXTENSION TEXT(".exe")
|
|
#define DLL_EXTENSION TEXT(".dll")
|
|
#define CGI_EXTENSION TEXT(".cgi")
|
|
|
|
LPSTR GetFileExtensionFromUrl(
|
|
IN LPSTR lpszUrl,
|
|
IN OUT LPDWORD lpdwLength)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine returns a possible file extension from a URL
|
|
It does this by walking back from the end till the first dot.
|
|
|
|
Arguments:
|
|
|
|
lpszUrl Url to derive the extension from
|
|
|
|
lpdwLength max length of the extension expected
|
|
|
|
Returns:
|
|
|
|
NULL if no dot within the passed in length or a forward slash or a
|
|
backward slash encountered before the dot. Otherwise returns a pointer
|
|
pointing past the dot in the url string
|
|
|
|
Comments:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
INET_ASSERT(lpszUrl && lpdwLength);
|
|
|
|
#ifdef OLD
|
|
if (lpszUrl != NULL) {
|
|
|
|
LPSTR p;
|
|
DWORD len=0 , lenLimit= *lpdwLength;
|
|
|
|
p = lpszUrl + (strlen(lpszUrl) - 1);
|
|
|
|
while ((*p != '.') && (p != lpszUrl)) {
|
|
|
|
// if this contains a character that the filesystems
|
|
// don't like, then return NULL
|
|
if (strchr(vszInvalidFilenameChars, *p)) {
|
|
return(NULL);
|
|
}
|
|
|
|
if ((*p == '/') || (*p == '\\')) {
|
|
break;
|
|
}
|
|
--p;
|
|
++len;
|
|
}
|
|
if ((*p == '.')
|
|
&& (len != 0)
|
|
&& (len < lenLimit)) {
|
|
*lpdwLength = len;
|
|
return p + 1;
|
|
}
|
|
}
|
|
return NULL;
|
|
#else
|
|
if (!lpszUrl)
|
|
{
|
|
*lpdwLength = 0;
|
|
return NULL;
|
|
}
|
|
|
|
LPSTR pszPeriod = NULL;
|
|
BOOL fContinue = TRUE;
|
|
|
|
// Scanning from left to right, note where we last saw a period.
|
|
// If we see a character that cannot be in an extension, and we've seen a period, forget
|
|
// about the period.
|
|
// Repeat this until we've reached the end of the url, a question mark (query) or hash (fragment)
|
|
|
|
// 1.6.98: _However_, if the file extension we've discovered is either .dll or .exe,
|
|
// we'll continue to scan beyond the query mark for a file extension.
|
|
|
|
// 1.20.98: And if we find no extension before the question mark, we'll look after it, then.
|
|
|
|
while (fContinue)
|
|
{
|
|
switch (*lpszUrl)
|
|
{
|
|
case TEXT('.'):
|
|
pszPeriod = lpszUrl;
|
|
break;
|
|
|
|
case TEXT('?'):
|
|
if (pszPeriod)
|
|
{
|
|
if ((!StrCmpNI(pszPeriod, EXE_EXTENSION, ARRAY_ELEMENTS(EXE_EXTENSION)-1))
|
|
|| (!StrCmpNI(pszPeriod, DLL_EXTENSION, ARRAY_ELEMENTS(DLL_EXTENSION)-1))
|
|
|| (!StrCmpNI(pszPeriod, CGI_EXTENSION, ARRAY_ELEMENTS(CGI_EXTENSION)-1)))
|
|
{
|
|
pszPeriod = NULL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
case TEXT('#'):
|
|
case TEXT('\0'):
|
|
fContinue = FALSE;
|
|
break;
|
|
|
|
default:
|
|
if (pszPeriod && strchr(vszInvalidFilenameChars, *lpszUrl))
|
|
{
|
|
pszPeriod = NULL;
|
|
}
|
|
}
|
|
lpszUrl++;
|
|
}
|
|
// This will be off by one
|
|
lpszUrl--;
|
|
if (pszPeriod)
|
|
{
|
|
if (*lpdwLength < (DWORD)(lpszUrl-pszPeriod))
|
|
{
|
|
pszPeriod = NULL;
|
|
}
|
|
else
|
|
{
|
|
pszPeriod++;
|
|
*lpdwLength = (DWORD)(lpszUrl-pszPeriod);
|
|
}
|
|
}
|
|
return pszPeriod;
|
|
#endif
|
|
}
|
|
|
|
|
|
DWORD
|
|
CheckExpired(
|
|
HINTERNET hRequestMapped,
|
|
BOOL* lpfIsExpired,
|
|
CACHE_ENTRY_INFO* pInfo,
|
|
LONGLONG DefaultExpiryDelta
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine checks whether a cache entry has expired for ftp/gopher.
|
|
It uses the globally set synchronization modes to make that decision
|
|
|
|
Arguments:
|
|
|
|
hRequestMapped a mapped request handle
|
|
|
|
lpfIsExpired returns TRUE if expired, FALSE otherwise
|
|
|
|
lpCacheEntryInfo cache entry info containing all the timestamps
|
|
|
|
DefaultExpiryDelta time delta to use for default expiry calculation
|
|
|
|
Returns:
|
|
|
|
Windows error code
|
|
|
|
Comments:
|
|
|
|
--*/
|
|
{
|
|
switch (GlobalUrlCacheSyncMode)
|
|
{
|
|
case WININET_SYNC_MODE_NEVER:
|
|
// Never check, unless the page has expired
|
|
*lpfIsExpired = FALSE;
|
|
break;
|
|
|
|
case WININET_SYNC_MODE_ALWAYS:
|
|
*lpfIsExpired = TRUE;
|
|
break;
|
|
|
|
default:
|
|
if (FT2LL (pInfo->LastSyncTime) < dwdwSessionStartTime)
|
|
*lpfIsExpired = TRUE;
|
|
else
|
|
{
|
|
FILETIME ftCurrent;
|
|
GetCurrentGmtTime (&ftCurrent);
|
|
*lpfIsExpired = (FT2LL(ftCurrent) - FT2LL (pInfo->LastSyncTime)
|
|
> DefaultExpiryDelta);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
LPTSTR
|
|
FTtoString(
|
|
IN FILETIME *pftTime)
|
|
|
|
/*++
|
|
|
|
FTtoString:
|
|
|
|
This routine converts a given FILETIME structure to a string representing
|
|
the given date and time in the local format.
|
|
|
|
Arguments:
|
|
|
|
pftTime supplies the FILETIME structure to convert.
|
|
|
|
Return Value:
|
|
|
|
NULL - Memory allocation failure.
|
|
Otherwise, the address of the string, allocated via LocalAlloc.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 4/12/1996
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG cchTotal, cchNeeded;
|
|
SYSTEMTIME stTime, stLocal;
|
|
LPTSTR szDateTime = NULL;
|
|
|
|
|
|
//
|
|
// Convert the FILETIME to a SYSTEMTIME.
|
|
//
|
|
|
|
if (!FileTimeToSystemTime(pftTime, &stTime))
|
|
goto ErrorExit;
|
|
|
|
//
|
|
// For now, leave it in GMT time, function not implimented in Win'95.
|
|
//
|
|
|
|
//if ( IsPlatformWinNT() )
|
|
//{
|
|
// if (!SystemTimeToTzSpecificLocalTime(NULL, &stTime, &stLocal))
|
|
// goto ErrorExit;
|
|
//}
|
|
//else
|
|
{
|
|
stLocal = stTime;
|
|
}
|
|
|
|
|
|
//
|
|
// Calculate how long the date string will be.
|
|
//
|
|
|
|
cchTotal =
|
|
GetDateFormat(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
DATE_SHORTDATE,
|
|
&stLocal,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
if (0 >= cchTotal)
|
|
goto ErrorExit;
|
|
cchNeeded =
|
|
GetTimeFormat(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
0,
|
|
&stLocal,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
if (0 >= cchNeeded)
|
|
goto ErrorExit;
|
|
cchTotal += cchNeeded;
|
|
cchTotal += 4 * sizeof(TCHAR); // space, trailing NULL, and two extra.
|
|
szDateTime = (LPTSTR)ALLOCATE_MEMORY(LMEM_FIXED, cchTotal);
|
|
if (NULL == szDateTime)
|
|
goto ErrorExit;
|
|
|
|
|
|
//
|
|
// Fill in the time string.
|
|
//
|
|
|
|
cchNeeded =
|
|
GetDateFormat(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
DATE_SHORTDATE,
|
|
&stLocal,
|
|
NULL,
|
|
szDateTime,
|
|
cchTotal);
|
|
if (0 >= cchNeeded)
|
|
goto ErrorExit;
|
|
lstrcat(szDateTime, TEXT(" "));
|
|
cchNeeded = lstrlen(szDateTime);
|
|
cchNeeded =
|
|
GetTimeFormat(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
0,
|
|
&stLocal,
|
|
NULL,
|
|
&szDateTime[cchNeeded],
|
|
cchTotal - cchNeeded);
|
|
if (0 >= cchNeeded)
|
|
goto ErrorExit;
|
|
return szDateTime;
|
|
|
|
|
|
ErrorExit:
|
|
if (NULL != szDateTime)
|
|
FREE_MEMORY(szDateTime);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PrintFileTimeInInternetFormat(
|
|
FILETIME *lpft,
|
|
LPSTR lpszBuff,
|
|
DWORD dwSize
|
|
)
|
|
{
|
|
SYSTEMTIME sSysTime;
|
|
|
|
if (dwSize < INTERNET_RFC1123_BUFSIZE) {
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return (FALSE);
|
|
}
|
|
if (!FileTimeToSystemTime(((CONST FILETIME *)lpft), &sSysTime)) {
|
|
return (FALSE);
|
|
}
|
|
return (InternetTimeFromSystemTime( &sSysTime,
|
|
INTERNET_RFC1123_FORMAT,
|
|
lpszBuff,
|
|
dwSize));
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
InternetSettingsChanged(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the global settings have been changed (inter-process)
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_UTIL,
|
|
Bool,
|
|
"InternetSettingsChanged",
|
|
NULL
|
|
));
|
|
|
|
DWORD dwVer;
|
|
BOOL bChanged = FALSE;
|
|
|
|
if (GetCurrentSettingsVersion(&dwVer)) {
|
|
|
|
DEBUG_PRINT(UTIL,
|
|
INFO,
|
|
("current settings version = %d\n",
|
|
dwVer
|
|
));
|
|
|
|
if (!GlobalSettingsLoaded || (dwVer != GlobalSettingsVersion)) {
|
|
GlobalSettingsLoaded = TRUE;
|
|
GlobalSettingsVersion = dwVer;
|
|
bChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
DEBUG_LEAVE(bChanged);
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RefreshSslState(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starting with Whistler, client auth certificates are cached
|
|
for the logon session (not per process). Inetcpl now contains
|
|
a button that will clear on-demand the SSL state for the session.
|
|
If user cleared the SSL cache, then flush all cached
|
|
client auth certificates in the global cert cache for this process.
|
|
|
|
NOTE: This function leverages a cache header data value.
|
|
Clearing the state affects all processes, and
|
|
using one of these (which was never put into practice)
|
|
prevents the need for another shared registry value.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL If the client auth cert cache was cleared, this function will
|
|
return TRUE. Otherwise, this will return FALSE.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwCount = 0;
|
|
BOOL bCleared = FALSE;
|
|
|
|
DEBUG_ENTER((DBG_UTIL,
|
|
Bool,
|
|
"RefreshSslState",
|
|
NULL
|
|
));
|
|
|
|
if (GlobalPlatformWhistler &&
|
|
GetUrlCacheHeaderData(CACHE_HEADER_DATA_DOWNLOAD_PARTIAL, &dwCount) &&
|
|
dwCount != GlobalSslStateCount)
|
|
{
|
|
GlobalSslStateCount = dwCount;
|
|
PurgeKeepAlives(PKA_NOW);
|
|
GlobalCertCache.ClearClientAuthCertChains();
|
|
bCleared = TRUE;
|
|
}
|
|
|
|
DEBUG_LEAVE(bCleared);
|
|
|
|
return bCleared;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CertHashToStr(
|
|
IN LPSTR lpMD5Hash,
|
|
IN DWORD dwMD5HashSize,
|
|
IN OUT LPSTR *lplpszHashStr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a set of bytes into a neatly formated string of ':' (colon) seperated
|
|
hex digits that can be shown to the user.
|
|
|
|
Arguments:
|
|
|
|
lpMD5Hash - ptr to set of hash bytes
|
|
|
|
dwMD5HashSize - size of lpMD5Hash
|
|
|
|
lplpszHashStr - ptr to ptr where newly allocated return string will be stored.
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD dwStrSize = (2*dwMD5HashSize) + dwMD5HashSize;
|
|
LPSTR lpszHashStr;
|
|
|
|
*lplpszHashStr = new CHAR[dwStrSize];
|
|
|
|
if ( *lplpszHashStr == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lpszHashStr = *lplpszHashStr;
|
|
|
|
for ( DWORD i = 0 ; i < dwMD5HashSize; i++ )
|
|
{
|
|
unsigned char uHashByte;
|
|
|
|
if ( i != 0 )
|
|
{
|
|
*lpszHashStr = ':';
|
|
lpszHashStr++;
|
|
}
|
|
|
|
uHashByte = (unsigned char) * ( ((unsigned char * ) lpMD5Hash) + i);
|
|
|
|
wsprintf( lpszHashStr, "%02X", uHashByte);
|
|
|
|
lpszHashStr += 2;
|
|
|
|
}
|
|
|
|
INET_ASSERT( *lpszHashStr == '\0' );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// private functions
|
|
//
|
|
|
|
DWORD
|
|
ConvertSecurityInfoIntoCertInfoStruct(
|
|
IN LPINTERNET_SECURITY_INFO pSecInfo,
|
|
OUT INTERNET_CERTIFICATE_INFO *pCertificate,
|
|
IN OUT DWORD *pcbCertificate
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts an X509 Certificate Structure into a WININET struct
|
|
used for storing the same info.
|
|
|
|
Arguments:
|
|
|
|
hContext - Context handle of the active SSPI session.
|
|
|
|
pCertInfo - Pointer to Structure where info is returned in.
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
ERROR_SUCCESS - if cert cannot be converted
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
PCERT_INFO pCertInfo = NULL;
|
|
DWORD cbCert = sizeof(INTERNET_CERTIFICATE_INFO),
|
|
cbSubject = 0,
|
|
cbIssuer = 0;
|
|
|
|
BOOL fCanAlloc = FALSE;
|
|
|
|
if(pSecInfo == NULL)
|
|
{
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
if(pCertificate == NULL || *pcbCertificate == 0)
|
|
{
|
|
*pcbCertificate = sizeof(INTERNET_CERTIFICATE_INFO);
|
|
goto quit;
|
|
}
|
|
|
|
if(*pcbCertificate < sizeof(INTERNET_CERTIFICATE_INFO) )
|
|
{
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
ZeroMemory(pCertificate, sizeof(INTERNET_CERTIFICATE_INFO));
|
|
fCanAlloc = TRUE;
|
|
|
|
if(pSecInfo->pCertificate &&
|
|
pSecInfo->pCertificate->pCertInfo )
|
|
{
|
|
pCertInfo = pSecInfo->pCertificate->pCertInfo;
|
|
|
|
//
|
|
// Now Convert Structures from SSPI format to WININET style.
|
|
// While in the process, we'll role them all into one
|
|
// big structure that we'll return to the user.
|
|
//
|
|
|
|
cbSubject = CertNameToStr(pSecInfo->pCertificate->dwCertEncodingType,
|
|
&pCertInfo->Subject,
|
|
CERT_SIMPLE_NAME_STR |
|
|
CERT_NAME_STR_CRLF_FLAG |
|
|
CERT_NAME_STR_NO_PLUS_FLAG,
|
|
NULL,
|
|
0);
|
|
|
|
|
|
if ( cbSubject > 0 )
|
|
{
|
|
// freed by caller outside of wininet
|
|
pCertificate->lpszSubjectInfo = (LPSTR) LocalAlloc(LPTR, cbSubject);
|
|
|
|
if ( pCertificate->lpszSubjectInfo == NULL )
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
CertNameToStr(pSecInfo->pCertificate->dwCertEncodingType,
|
|
&pCertInfo->Subject,
|
|
CERT_SIMPLE_NAME_STR |
|
|
CERT_NAME_STR_CRLF_FLAG |
|
|
CERT_NAME_STR_NO_PLUS_FLAG ,
|
|
pCertificate->lpszSubjectInfo,
|
|
cbSubject);
|
|
|
|
}
|
|
|
|
cbIssuer = CertNameToStr(pSecInfo->pCertificate->dwCertEncodingType,
|
|
&pCertInfo->Issuer,
|
|
CERT_SIMPLE_NAME_STR |
|
|
CERT_NAME_STR_CRLF_FLAG |
|
|
CERT_NAME_STR_NO_PLUS_FLAG,
|
|
NULL,
|
|
0);
|
|
|
|
if ( cbIssuer > 0 )
|
|
{
|
|
// freed by caller outside of wininet
|
|
pCertificate->lpszIssuerInfo = (LPSTR) LocalAlloc(LPTR, cbIssuer);
|
|
|
|
if ( pCertificate->lpszIssuerInfo == NULL )
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
CertNameToStr(pSecInfo->pCertificate->dwCertEncodingType,
|
|
&pCertInfo->Issuer,
|
|
CERT_SIMPLE_NAME_STR |
|
|
CERT_NAME_STR_CRLF_FLAG |
|
|
CERT_NAME_STR_NO_PLUS_FLAG ,
|
|
pCertificate->lpszIssuerInfo,
|
|
cbIssuer);
|
|
|
|
}
|
|
|
|
CopyMemory(
|
|
(PVOID) &pCertificate->ftStart,
|
|
(PVOID) &pCertInfo->NotBefore,
|
|
sizeof(FILETIME)
|
|
);
|
|
|
|
CopyMemory(
|
|
(PVOID) &pCertificate->ftExpiry,
|
|
(PVOID) &pCertInfo->NotAfter,
|
|
sizeof(FILETIME)
|
|
);
|
|
|
|
}
|
|
|
|
/*if(pSecInfo->dwProtocol)
|
|
{
|
|
DWORD dwProtocolID;
|
|
TCHAR lpszProtocol[100];
|
|
|
|
ATTR_MAP ProtocolAttrMap[] =
|
|
{
|
|
{SP_PROT_SSL2_CLIENT, IDS_PROTOCOL_SSL2},
|
|
{SP_PROT_SSL3_CLIENT, IDS_PROTOCOL_SSL3},
|
|
{SP_PROT_PCT1_CLIENT, IDS_PROTOCOL_PCT1},
|
|
{SP_PROT_TLS1_CLIENT, IDS_PROTOCOL_TLS1}
|
|
};
|
|
|
|
|
|
for(j=0; j < sizeof(ProtocolAttrMap)/sizeof(ProtocolAttrMap[0]); j++)
|
|
{
|
|
if(ProtocolAttrMap[j].dwAttr == pSecInfo->dwProtocol)
|
|
{
|
|
dwProtocolID = ProtocolAttrMap[j].dwStringID;
|
|
break;
|
|
}
|
|
}
|
|
if(LoadString(GlobalDllHandle,
|
|
dwProtocolID,
|
|
lpszProtocol,
|
|
sizeof(lpszProtocol)/sizeof(lpszProtocol[0])))
|
|
{
|
|
pCertificate->lpszProtocolName = NewString(lpszProtocol);
|
|
}
|
|
} */
|
|
|
|
pCertificate->dwKeySize = pSecInfo->dwCipherStrength;
|
|
|
|
quit:
|
|
|
|
if ( error != ERROR_SUCCESS &&
|
|
fCanAlloc
|
|
)
|
|
{
|
|
|
|
if ( pCertificate->lpszSubjectInfo )
|
|
{
|
|
LocalFree(pCertificate->lpszSubjectInfo);
|
|
pCertificate->lpszSubjectInfo = NULL;
|
|
}
|
|
|
|
if ( pCertificate->lpszIssuerInfo )
|
|
{
|
|
LocalFree(pCertificate->lpszIssuerInfo);
|
|
pCertificate->lpszIssuerInfo = NULL;
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/*++
|
|
|
|
FormatCertInfo:
|
|
|
|
This routine formats the information within a INTERNET_CERTIFICATE_INFO
|
|
structure suitable for display with localization.
|
|
|
|
Arguments:
|
|
|
|
pCertInfo supplies a pointer to the INTERNET_CERTIFICATE_INFO structure to
|
|
be formatted.
|
|
|
|
Return Value:
|
|
|
|
NULL - An error occurred. Otherwise, a pointer to the formatted string.
|
|
This string must be freed by the caller via LocalFree.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 4/30/1996
|
|
|
|
--*/
|
|
|
|
LPTSTR
|
|
FormatCertInfo(
|
|
IN INTERNET_CERTIFICATE_INFO *pCertInfo
|
|
)
|
|
{
|
|
LPVOID rgpvParams[9]; // Number of insertable elements in the
|
|
// plszStrings->szCertInfo resource
|
|
// string.
|
|
LPTSTR szResult = NULL;
|
|
int i = 0;
|
|
PLOCAL_STRINGSA plszStrings;
|
|
LPTSTR szFrom = NULL;
|
|
LPTSTR szUntil = NULL;
|
|
|
|
|
|
//
|
|
// Get the Certificate Information.
|
|
//
|
|
|
|
plszStrings = FetchLocalStringsA();
|
|
szFrom = FTtoString(&pCertInfo->ftStart);
|
|
szUntil = FTtoString(&pCertInfo->ftExpiry);
|
|
if ((NULL == szUntil) || (NULL == szFrom))
|
|
goto ErrorExit;
|
|
|
|
//ChangeCommaSpaceToCRLF(pCertInfo->lpszSubjectInfo);
|
|
//ChangeCommaSpaceToCRLF(pCertInfo->lpszIssuerInfo);
|
|
|
|
rgpvParams[i++] = (LPVOID)pCertInfo->lpszSubjectInfo;
|
|
rgpvParams[i++] = (LPVOID)pCertInfo->lpszIssuerInfo;
|
|
rgpvParams[i++] = (LPVOID)szFrom;
|
|
rgpvParams[i++] = (LPVOID)szUntil;
|
|
rgpvParams[i++] = (LPVOID)pCertInfo->lpszProtocolName;
|
|
rgpvParams[i++] = (LPVOID)pCertInfo->lpszSignatureAlgName;
|
|
rgpvParams[i++] = (LPVOID)pCertInfo->lpszEncryptionAlgName;
|
|
rgpvParams[i++] = (LPVOID)(DWORD_PTR)pCertInfo->dwKeySize;
|
|
if (96 <= pCertInfo->dwKeySize) // Recommended Key strength
|
|
rgpvParams[i++] = (LPVOID)plszStrings->szStrengthHigh;
|
|
else if (64 <= pCertInfo->dwKeySize) // Passable key strength
|
|
rgpvParams[i++] = (LPVOID)plszStrings->szStrengthMedium;
|
|
else // Ick! Low key strength.
|
|
rgpvParams[i++] = (LPVOID)plszStrings->szStrengthLow;
|
|
INET_ASSERT(i == sizeof(rgpvParams) / sizeof(LPVOID));
|
|
i = FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
|
| FORMAT_MESSAGE_FROM_STRING
|
|
| FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
plszStrings->szCertInfo,
|
|
0, 0,
|
|
(LPTSTR)&szResult,
|
|
0,
|
|
(va_list *)rgpvParams);
|
|
|
|
ErrorExit:
|
|
if (NULL != szFrom)
|
|
FREE_MEMORY(szFrom);
|
|
if (NULL != szUntil)
|
|
FREE_MEMORY(szUntil);
|
|
return szResult;
|
|
}
|
|
|
|
DWORD
|
|
ConvertUnicodeToUTF8(
|
|
IN LPCWSTR pwszIn,
|
|
IN DWORD dwInBufLen,
|
|
OUT LPSTR pszOut,
|
|
OUT DWORD* pdwOutStrLen,
|
|
IN BOOL bEncode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a string of UNICODE characters to UTF-8:
|
|
|
|
0000000000000000..0000000001111111: 0xxxxxxx
|
|
0000000010000000..0000011111111111: 110xxxxx 10xxxxxx
|
|
0000100000000000..1111111111111111: 1110xxxx 10xxxxxx 10xxxxxx
|
|
|
|
Arguments:
|
|
|
|
pwszIn - pointer to input wide-character string
|
|
|
|
dwInBufLen - number of CHARACTERS in pwszIn INCLUDING terminating NULL
|
|
|
|
pszOut - pointer to output narrow-character buffer
|
|
|
|
pdwOutStrLen - STRLEN of pszOut (excl. terminating NULL); IN value discarded.
|
|
|
|
bEncode - TRUE if we are to hex encode characters >= 0x80
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - NO ERROR checking done. Make sure large enuf buffer passed
|
|
in.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"ConvertUnicodeToUtf8",
|
|
"%.100wq, %#x, %#x, %#x, %B",
|
|
pwszIn, dwInBufLen, pszOut, pdwOutStrLen, bEncode
|
|
));
|
|
|
|
INET_ASSERT(pwszIn);
|
|
INET_ASSERT((int)dwInBufLen > 0);
|
|
INET_ASSERT(pszOut);
|
|
|
|
LPSTR pszStart = pszOut;
|
|
static char hexArray[] = "0123456789ABCDEF";
|
|
|
|
while (dwInBufLen--) {
|
|
|
|
WORD wchar = *pwszIn++;
|
|
BYTE bchar;
|
|
|
|
if (wchar <= 0x007F) {
|
|
*pszOut++ = (BYTE)(wchar);
|
|
continue;
|
|
}
|
|
|
|
BYTE lead = ((wchar >= 0x0800) ? 0xE0 : 0xC0);
|
|
int shift = ((wchar >= 0x0800) ? 12 : 6);
|
|
|
|
bchar = lead | (BYTE)(wchar >> shift);
|
|
if (bEncode) {
|
|
*pszOut++ = '%';
|
|
*pszOut++ = hexArray[bchar >> 4];
|
|
bchar = hexArray[bchar & 0x0F];
|
|
}
|
|
*pszOut++ = bchar;
|
|
|
|
if (wchar >= 0x0800) {
|
|
bchar = 0x80 | (BYTE)((wchar >> 6) & 0x003F);
|
|
if (bEncode) {
|
|
*pszOut++ = '%';
|
|
*pszOut++ = hexArray[bchar >> 4];
|
|
bchar = hexArray[bchar & 0x0F];
|
|
}
|
|
*pszOut++ = bchar;
|
|
}
|
|
bchar = 0x80 | (BYTE)(wchar & 0x003F);
|
|
if (bEncode) {
|
|
*pszOut++ = '%';
|
|
*pszOut++ = hexArray[bchar >> 4];
|
|
bchar = hexArray[bchar & 0x0F];
|
|
}
|
|
*pszOut++ = bchar;
|
|
}
|
|
|
|
*pdwOutStrLen = PtrDiff32(pszOut, pszStart)-1;
|
|
DEBUG_LEAVE(ERROR_SUCCESS);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
LPSTR
|
|
ConvertMBCSToUTF8(
|
|
IN LPCSTR lpszMBCSHostName,
|
|
IN DWORD dwMBCSHostNameLength,
|
|
IN DWORD dwCodePage,
|
|
OUT DWORD * pdwUTF8HostNameStrLen,
|
|
IN BOOL bLowerCase
|
|
)
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
String,
|
|
"ConvertMBCSToUTF8",
|
|
"%.100q, %#x, %#x, %#x, %B",
|
|
lpszMBCSHostName, dwMBCSHostNameLength, dwCodePage, (pdwUTF8HostNameStrLen ? *pdwUTF8HostNameStrLen : NULL), bLowerCase
|
|
));
|
|
|
|
DEBUG_DUMP(HTTP,
|
|
"lpszMBCSHostName: strlen+1byte\n",
|
|
lpszMBCSHostName,
|
|
(dwMBCSHostNameLength+1)
|
|
);
|
|
|
|
LPSTR lpszUTF8HostName = NULL;
|
|
LPWSTR lpwszWideHostName = NULL;
|
|
|
|
if (!lpszMBCSHostName || !dwMBCSHostNameLength)
|
|
goto quit;
|
|
|
|
DWORD dwWideLen = MultiByteToWideChar(dwCodePage, 0, lpszMBCSHostName, dwMBCSHostNameLength, NULL, 0);
|
|
//IMPORTANT! NULL terminator not included in dwWideLen
|
|
|
|
if (dwWideLen)
|
|
{
|
|
lpwszWideHostName = new WCHAR[dwWideLen+1];
|
|
|
|
if (lpwszWideHostName
|
|
&& (dwWideLen = MultiByteToWideChar(dwCodePage, 0, lpszMBCSHostName, dwMBCSHostNameLength, lpwszWideHostName, dwWideLen))
|
|
&& (lpszUTF8HostName = new CHAR[dwWideLen*3+1]))
|
|
{
|
|
// *pdwUTF8HostNameLength = dwWideLen*3+1;
|
|
// Fill in the terminating NULL character since dwMBCSHostNameLength doesn't include NULL.
|
|
lpwszWideHostName[dwWideLen] = L'\0';
|
|
|
|
DEBUG_DUMP(HTTP,
|
|
"Before CharLowerW - lpwszWideHostName: strlen*2+2\n",
|
|
lpwszWideHostName,
|
|
(dwWideLen*2+2)
|
|
);
|
|
|
|
if (bLowerCase)
|
|
{
|
|
CharLowerW(lpwszWideHostName);
|
|
|
|
DEBUG_DUMP(HTTP,
|
|
"After CharLowerW - lpwszWideHostName: strlen*2+2\n",
|
|
lpwszWideHostName,
|
|
(dwWideLen*2+2)
|
|
);
|
|
}
|
|
|
|
DWORD error = ConvertUnicodeToUTF8(lpwszWideHostName, dwWideLen+1, lpszUTF8HostName, pdwUTF8HostNameStrLen, FALSE);
|
|
|
|
DEBUG_DUMP(HTTP,
|
|
"ConvertUnicodeToUTF8 - lpszUTF8HostName: strlen+1\n",
|
|
lpszUTF8HostName,
|
|
(*pdwUTF8HostNameStrLen+1)
|
|
);
|
|
|
|
// no other error possible
|
|
INET_ASSERT(error == ERROR_SUCCESS);
|
|
}
|
|
}
|
|
|
|
quit:
|
|
if (lpwszWideHostName)
|
|
{
|
|
delete [] lpwszWideHostName;
|
|
}
|
|
|
|
DEBUG_LEAVE(lpszUTF8HostName);
|
|
return lpszUTF8HostName;
|
|
}
|
|
|
|
|
|
char *FindNamedValue(char *pszHeader, const char *pszFieldName, unsigned long *pdwValSize) {
|
|
|
|
const char ChDblQuote = '\"';
|
|
|
|
char *pszBegin, *pszValue;
|
|
BOOL fFound;
|
|
int cbName;
|
|
|
|
char *pch = pszHeader;
|
|
|
|
ExpectName:
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
ParseName:
|
|
pszBegin = pch;
|
|
|
|
while (*pch && isalnum(*pch))
|
|
pch++;
|
|
|
|
cbName = (int) (pch-pszBegin);
|
|
fFound = (cbName>0) && !strncmp(pszBegin, pszFieldName, cbName);
|
|
|
|
ExpectEqSign:
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
if (*pch=='=')
|
|
pch++;
|
|
|
|
ExpectValue:
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
if (*pch=='\"') {
|
|
pch++;
|
|
goto ParseQuotedValue;
|
|
}
|
|
|
|
ParseValue:
|
|
pszValue = pch;
|
|
while (*pch && *pch!=',')
|
|
pch++;
|
|
|
|
goto ExpectComma;
|
|
|
|
ParseQuotedValue:
|
|
pszValue = pch;
|
|
while (*pch && *pch!=ChDblQuote)
|
|
pch++;
|
|
|
|
ExpectComma:
|
|
if (fFound) {
|
|
int cbValue = (int) (pch-pszValue);
|
|
*pdwValSize = cbValue;
|
|
return pszValue;
|
|
}
|
|
|
|
while (*pch && *pch!=',')
|
|
pch++;
|
|
|
|
if (*pch==',') {
|
|
pch++;
|
|
goto ExpectName;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// Helper to determine if we're currently loaded during GUI mode setup
|
|
BOOL IsInGUIModeSetup()
|
|
{
|
|
// could be called multiple times in wininet
|
|
static DWORD s_dwSystemSetupInProgress = 42;
|
|
|
|
if (42 == s_dwSystemSetupInProgress)
|
|
{
|
|
// Rule is that this value will exist and be equal to 1 if in GUI mode setup.
|
|
// Default to NO, and only do this for upgrades because this is potentially
|
|
// needed for unattended clean installs.
|
|
s_dwSystemSetupInProgress = 0;
|
|
|
|
HKEY hKeySetup = NULL;
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("System\\Setup"),
|
|
0,
|
|
KEY_READ,
|
|
&hKeySetup))
|
|
{
|
|
DWORD dwSize = sizeof(s_dwSystemSetupInProgress);
|
|
|
|
if (ERROR_SUCCESS != RegQueryValueEx (hKeySetup,
|
|
TEXT("SystemSetupInProgress"),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE) &s_dwSystemSetupInProgress,
|
|
&dwSize))
|
|
{
|
|
s_dwSystemSetupInProgress = 0;
|
|
}
|
|
else
|
|
{
|
|
dwSize = sizeof(s_dwSystemSetupInProgress);
|
|
if (s_dwSystemSetupInProgress &&
|
|
ERROR_SUCCESS != RegQueryValueEx (hKeySetup,
|
|
TEXT("UpgradeInProgress"),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE) &s_dwSystemSetupInProgress,
|
|
&dwSize))
|
|
{
|
|
s_dwSystemSetupInProgress = 0;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hKeySetup);
|
|
}
|
|
}
|
|
return s_dwSystemSetupInProgress ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
#ifdef DONT_USE_IERT
|
|
/***
|
|
*char *StrTokEx(pstring, control) - tokenize string with delimiter in control
|
|
*
|
|
*Purpose:
|
|
* StrTokEx considers the string to consist of a sequence of zero or more
|
|
* text tokens separated by spans of one or more control chars. the first
|
|
* call, with string specified, returns a pointer to the first char of the
|
|
* first token, and will write a null char into pstring immediately
|
|
* following the returned token. when no tokens remain
|
|
* in pstring a NULL pointer is returned. remember the control chars with a
|
|
* bit map, one bit per ascii char. the null char is always a control char.
|
|
*
|
|
*Entry:
|
|
* char **pstring - ptr to ptr to string to tokenize
|
|
* char *control - string of characters to use as delimiters
|
|
*
|
|
*Exit:
|
|
* returns pointer to first token in string,
|
|
* returns NULL when no more tokens remain.
|
|
* pstring points to the beginning of the next token.
|
|
*
|
|
*WARNING!!!
|
|
* upon exit, the first delimiter in the input string will be replaced with '\0'
|
|
*
|
|
*******************************************************************************/
|
|
|
|
char * StrTokEx (char ** pstring, const char * control)
|
|
{
|
|
unsigned char *str;
|
|
const unsigned char *ctrl = (const unsigned char *)control;
|
|
unsigned char map[32];
|
|
int count;
|
|
|
|
char *tokenstr;
|
|
|
|
if(*pstring == NULL)
|
|
return NULL;
|
|
|
|
/* Clear control map */
|
|
for (count = 0; count < 32; count++)
|
|
map[count] = 0;
|
|
|
|
/* Set bits in delimiter table */
|
|
do
|
|
{
|
|
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
|
|
} while (*ctrl++);
|
|
|
|
/* Initialize str. */
|
|
str = (unsigned char *)*pstring;
|
|
|
|
/* Find beginning of token (skip over leading delimiters). Note that
|
|
* there is no token if this loop sets str to point to the terminal
|
|
* null (*str == '\0') */
|
|
while ( (map[*str >> 3] & (1 << (*str & 7))) && *str )
|
|
str++;
|
|
|
|
tokenstr = (char *)str;
|
|
|
|
/* Find the end of the token. If it is not the end of the string,
|
|
* put a null there. */
|
|
for ( ; *str ; str++ )
|
|
{
|
|
if ( map[*str >> 3] & (1 << (*str & 7)) )
|
|
{
|
|
*str++ = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* string now points to beginning of next token */
|
|
*pstring = (char *)str;
|
|
|
|
/* Determine if a token has been found. */
|
|
if ( tokenstr == (char *)str )
|
|
return NULL;
|
|
else
|
|
return tokenstr;
|
|
}
|
|
|
|
/***
|
|
* double StrToDbl(const char *str, char **strStop) - convert string to double
|
|
*
|
|
* Purpose:
|
|
* To convert a string into a double. This function supports
|
|
* simple double representations like '1.234', '.5678'. It also support
|
|
* the a killobyte computaion by appending 'k' to the end of the string
|
|
* as in '1.5k' or '.5k'. The results would then become 1536 and 512.5.
|
|
*
|
|
* Return:
|
|
* The double representation of the string.
|
|
* strStop points to the character that caused the scan to stop.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
double StrToDbl(const char *str, char **strStop)
|
|
{
|
|
double dbl = 0;
|
|
char *psz;
|
|
int iMult = 1;
|
|
int iKB = 1;
|
|
int iVal = 0;
|
|
BOOL bHaveDot = FALSE;
|
|
|
|
psz = (char*)str;
|
|
while(*psz)
|
|
{
|
|
if((*psz >= '0') && (*psz <= '9'))
|
|
{
|
|
iVal = (iVal * 10) + (*psz - '0');
|
|
if(bHaveDot)
|
|
iMult *= 10;
|
|
}
|
|
else if((*psz == '.') && !bHaveDot)
|
|
{
|
|
bHaveDot = TRUE;
|
|
}
|
|
else if((*psz == 'k') || (*psz == 'K'))
|
|
{
|
|
iKB = 1024;
|
|
psz++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
psz++;
|
|
}
|
|
*strStop = psz;
|
|
|
|
dbl = (double) (iVal * iKB) / iMult;
|
|
|
|
return(dbl);
|
|
}
|
|
#endif // DONT_USE_IERT
|
|
|
|
|
|
// We need a DBCS-safe version of StrTokEx.
|
|
|
|
char* StrTokEx2(char ** pstring, const char * control)
|
|
{
|
|
/*unsigned*/ char *str;
|
|
const /*unsigned*/ char *ctrl = control;
|
|
unsigned char map[32];
|
|
int count;
|
|
|
|
char *tokenstr;
|
|
|
|
if(*pstring == NULL)
|
|
return NULL;
|
|
|
|
/* Clear control map */
|
|
for (count = 0; count < 32; count++)
|
|
map[count] = 0;
|
|
|
|
/* Set bits in delimiter table */
|
|
do
|
|
{
|
|
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
|
|
ctrl++;
|
|
} while (*ctrl);
|
|
|
|
/* Initialize str. */
|
|
str = *pstring;
|
|
|
|
/* Find beginning of token (skip over leading delimiters). Note that
|
|
* there is no token if this loop sets str to point to the terminal
|
|
* null (*str == '\0') */
|
|
while ((*str>0) && ((CharNext(str)-str)==1) && (map[*str >> 3] & (1 << (*str & 7))))
|
|
str = CharNext(str);
|
|
|
|
tokenstr = str;
|
|
|
|
/* Find the end of the token. If it is not the end of the string,
|
|
* put a null there. */
|
|
for ( ; *str ; str = CharNext(str))
|
|
{
|
|
if (((CharNext(str)-str)==1) && (*str>0) && (map[*str >> 3] & (1 << (*str & 7))))
|
|
{
|
|
*str++ = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* string now points to beginning of next token */
|
|
*pstring = str;
|
|
|
|
/* Determine if a token has been found. */
|
|
if ( tokenstr == str )
|
|
return NULL;
|
|
else
|
|
return tokenstr;
|
|
}
|
|
|
|
|