3312 lines
85 KiB
C++
3312 lines
85 KiB
C++
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
inetapia.cxx
|
||
|
||
Abstract:
|
||
|
||
Contains the ANSI and character-mode-independent Internet APIs
|
||
|
||
Contents:
|
||
WinHttpCloseHandle
|
||
WinHttpReadData
|
||
WinHttpWriteData
|
||
WinHttpQueryDataAvailable
|
||
|
||
|
||
WinHttpCrackUrlA
|
||
WinHttpCreateUrlA
|
||
InternetCanonicalizeUrlA
|
||
InternetCombineUrlA
|
||
InternetOpenA
|
||
_InternetCloseHandle
|
||
_InternetCloseHandleNoContext
|
||
InternetConnectA
|
||
InternetOpenUrlA
|
||
ReadFile_End
|
||
InternetQueryOptionA
|
||
InternetSetOptionA
|
||
InternetGetLastResponseInfoA
|
||
(wInternetCloseConnectA)
|
||
(CreateDeleteSocket)
|
||
|
||
Author:
|
||
|
||
Richard L Firth (rfirth) 02-Mar-1995
|
||
|
||
Environment:
|
||
|
||
Win32 user-mode DLL
|
||
|
||
Revision History:
|
||
|
||
02-Mar-1995 rfirth
|
||
Created
|
||
|
||
07-Mar-1995 madana
|
||
|
||
|
||
--*/
|
||
|
||
|
||
#include <wininetp.h>
|
||
#include <perfdiag.hxx>
|
||
|
||
// because wininet doesnt know IStream
|
||
#define NO_SHLWAPI_STREAM
|
||
#include <shlwapi.h>
|
||
#include <shlwapip.h>
|
||
|
||
|
||
//
|
||
// private manifests
|
||
//
|
||
|
||
//
|
||
// private prototypes
|
||
//
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ReadFile_Fsm(
|
||
IN CFsm_ReadFile * Fsm
|
||
);
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ReadFileEx_Fsm(
|
||
IN CFsm_ReadFileEx * Fsm
|
||
);
|
||
|
||
PRIVATE
|
||
VOID
|
||
ReadFile_End(
|
||
IN BOOL bDeref,
|
||
IN BOOL bSuccess,
|
||
IN HINTERNET hFileMapped,
|
||
IN DWORD dwBytesRead,
|
||
IN LPVOID lpBuffer OPTIONAL,
|
||
IN DWORD dwNumberOfBytesToRead,
|
||
OUT LPDWORD lpdwNumberOfBytesRead OPTIONAL
|
||
);
|
||
|
||
PRIVATE
|
||
DWORD
|
||
QueryAvailable_Fsm(
|
||
IN CFsm_QueryAvailable * Fsm
|
||
);
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
wInternetCloseConnectA(
|
||
IN HINTERNET lpConnectHandle,
|
||
IN DWORD ServiceType
|
||
);
|
||
|
||
PRIVATE
|
||
BOOL
|
||
InternetParseCommon(
|
||
IN LPCTSTR lpszBaseUrl,
|
||
IN LPCTSTR lpszRelativeUrl,
|
||
OUT LPTSTR lpszBuffer,
|
||
IN OUT LPDWORD lpdwBufferLength,
|
||
IN DWORD dwFlags
|
||
);
|
||
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
WinHttpCrackUrlA(
|
||
IN LPCSTR lpszUrl,
|
||
IN DWORD dwUrlLength,
|
||
IN DWORD dwFlags,
|
||
IN LPURL_COMPONENTSA lpUrlComponents
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cracks an URL into its constituent parts. Optionally escapes the url-path.
|
||
We assume that the user has supplied large enough buffers for the various
|
||
URL parts
|
||
|
||
Arguments:
|
||
|
||
lpszUrl - pointer to URL to crack
|
||
|
||
dwUrlLength - 0 if lpszUrl is ASCIIZ string, else length of lpszUrl
|
||
|
||
dwFlags - flags controlling operation
|
||
|
||
lpUrlComponents - pointer to URL_COMPONENTS
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE
|
||
|
||
Failure - FALSE. Call GetLastError() for more info
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"WinHttpCrackUrlA",
|
||
"%q, %#x, %#x, %#x",
|
||
lpszUrl,
|
||
dwUrlLength,
|
||
dwFlags,
|
||
lpUrlComponents
|
||
));
|
||
|
||
DWORD error;
|
||
|
||
//
|
||
// validate parameters
|
||
//
|
||
if (!dwUrlLength)
|
||
dwUrlLength = lstrlen(lpszUrl);
|
||
|
||
//
|
||
// get the individual components to return. If they reference a buffer then
|
||
// check it for writeability
|
||
//
|
||
|
||
LPSTR lpUrl;
|
||
LPSTR urlCopy;
|
||
INTERNET_SCHEME schemeType;
|
||
LPSTR schemeName;
|
||
DWORD schemeNameLength;
|
||
LPSTR hostName;
|
||
DWORD hostNameLength;
|
||
INTERNET_PORT nPort;
|
||
LPSTR userName;
|
||
DWORD userNameLength;
|
||
LPSTR password;
|
||
DWORD passwordLength;
|
||
LPSTR urlPath;
|
||
DWORD urlPathLength;
|
||
LPSTR extraInfo;
|
||
DWORD extraInfoLength;
|
||
BOOL copyComponent;
|
||
BOOL havePort;
|
||
|
||
copyComponent = FALSE;
|
||
|
||
schemeName = lpUrlComponents->lpszScheme;
|
||
schemeNameLength = lpUrlComponents->dwSchemeLength;
|
||
if ((schemeName != NULL) && (schemeNameLength != 0))
|
||
{
|
||
*schemeName = '\0';
|
||
copyComponent = TRUE;
|
||
}
|
||
|
||
hostName = lpUrlComponents->lpszHostName;
|
||
hostNameLength = lpUrlComponents->dwHostNameLength;
|
||
if ((hostName != NULL) && (hostNameLength != 0))
|
||
{
|
||
*hostName = '\0';
|
||
copyComponent = TRUE;
|
||
}
|
||
|
||
userName = lpUrlComponents->lpszUserName;
|
||
userNameLength = lpUrlComponents->dwUserNameLength;
|
||
if ((userName != NULL) && (userNameLength != 0))
|
||
{
|
||
*userName = '\0';
|
||
copyComponent = TRUE;
|
||
}
|
||
|
||
password = lpUrlComponents->lpszPassword;
|
||
passwordLength = lpUrlComponents->dwPasswordLength;
|
||
if ((password != NULL) && (passwordLength != 0))
|
||
{
|
||
*password = '\0';
|
||
copyComponent = TRUE;
|
||
}
|
||
|
||
urlPath = lpUrlComponents->lpszUrlPath;
|
||
urlPathLength = lpUrlComponents->dwUrlPathLength;
|
||
if ((urlPath != NULL) && (urlPathLength != 0))
|
||
{
|
||
*urlPath = '\0';
|
||
copyComponent = TRUE;
|
||
}
|
||
|
||
extraInfo = lpUrlComponents->lpszExtraInfo;
|
||
extraInfoLength = lpUrlComponents->dwExtraInfoLength;
|
||
if ((extraInfo != NULL) && (extraInfoLength != 0))
|
||
{
|
||
*extraInfo = '\0';
|
||
copyComponent = TRUE;
|
||
}
|
||
|
||
//
|
||
// we can only escape or decode the URL if the caller has provided us with
|
||
// buffers to write the escaped strings into
|
||
//
|
||
|
||
if (dwFlags & (ICU_ESCAPE | ICU_DECODE)) {
|
||
if (!copyComponent) {
|
||
error = ERROR_INVALID_PARAMETER;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// create a copy of the URL. CrackUrl() will modify this in situ. We
|
||
// need to copy the results back to the user's buffer(s)
|
||
//
|
||
|
||
urlCopy = NewString((LPSTR)lpszUrl, dwUrlLength);
|
||
if (urlCopy == NULL) {
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
lpUrl = urlCopy;
|
||
} else {
|
||
lpUrl = (LPSTR)lpszUrl;
|
||
urlCopy = NULL;
|
||
}
|
||
|
||
//
|
||
// crack the URL into its constituent parts
|
||
//
|
||
|
||
error = CrackUrl(lpUrl,
|
||
dwUrlLength,
|
||
(dwFlags & ICU_ESCAPE) ? TRUE : FALSE,
|
||
&schemeType,
|
||
&schemeName,
|
||
&schemeNameLength,
|
||
&hostName,
|
||
&hostNameLength,
|
||
&nPort,
|
||
&userName,
|
||
&userNameLength,
|
||
&password,
|
||
&passwordLength,
|
||
&urlPath,
|
||
&urlPathLength,
|
||
extraInfoLength ? &extraInfo : NULL,
|
||
extraInfoLength ? &extraInfoLength : 0,
|
||
&havePort
|
||
);
|
||
if (error != ERROR_SUCCESS) {
|
||
goto crack_error;
|
||
}
|
||
|
||
BOOL copyFailure;
|
||
|
||
copyFailure = FALSE;
|
||
|
||
//
|
||
// update the URL_COMPONENTS structure based on the results, and what was
|
||
// asked for
|
||
//
|
||
|
||
if (lpUrlComponents->lpszScheme != NULL) {
|
||
if (lpUrlComponents->dwSchemeLength > schemeNameLength) {
|
||
memcpy((LPVOID)lpUrlComponents->lpszScheme,
|
||
(LPVOID)schemeName,
|
||
schemeNameLength
|
||
);
|
||
lpUrlComponents->lpszScheme[schemeNameLength] = '\0';
|
||
if (dwFlags & ICU_DECODE) {
|
||
UrlUnescapeInPlace(lpUrlComponents->lpszScheme, 0);
|
||
}
|
||
} else {
|
||
++schemeNameLength;
|
||
copyFailure = TRUE;
|
||
}
|
||
lpUrlComponents->dwSchemeLength = schemeNameLength;
|
||
} else if (lpUrlComponents->dwSchemeLength != 0) {
|
||
lpUrlComponents->lpszScheme = schemeName;
|
||
lpUrlComponents->dwSchemeLength = schemeNameLength;
|
||
}
|
||
|
||
if (lpUrlComponents->lpszHostName != NULL) {
|
||
if (lpUrlComponents->dwHostNameLength > hostNameLength) {
|
||
memcpy((LPVOID)lpUrlComponents->lpszHostName,
|
||
(LPVOID)hostName,
|
||
hostNameLength
|
||
);
|
||
lpUrlComponents->lpszHostName[hostNameLength] = '\0';
|
||
if (dwFlags & ICU_DECODE) {
|
||
UrlUnescapeInPlace(lpUrlComponents->lpszHostName, 0);
|
||
}
|
||
} else {
|
||
++hostNameLength;
|
||
copyFailure = TRUE;
|
||
}
|
||
lpUrlComponents->dwHostNameLength = hostNameLength;
|
||
} else if (lpUrlComponents->dwHostNameLength != 0) {
|
||
lpUrlComponents->lpszHostName = hostName;
|
||
lpUrlComponents->dwHostNameLength = hostNameLength;
|
||
}
|
||
|
||
if (lpUrlComponents->lpszUserName != NULL) {
|
||
if (lpUrlComponents->dwUserNameLength > userNameLength) {
|
||
memcpy((LPVOID)lpUrlComponents->lpszUserName,
|
||
(LPVOID)userName,
|
||
userNameLength
|
||
);
|
||
lpUrlComponents->lpszUserName[userNameLength] = '\0';
|
||
if (dwFlags & ICU_DECODE) {
|
||
UrlUnescapeInPlace(lpUrlComponents->lpszUserName, 0);
|
||
}
|
||
} else {
|
||
++userNameLength;
|
||
copyFailure = TRUE;
|
||
}
|
||
lpUrlComponents->dwUserNameLength = userNameLength;
|
||
} else if (lpUrlComponents->dwUserNameLength != 0) {
|
||
lpUrlComponents->lpszUserName = userName;
|
||
lpUrlComponents->dwUserNameLength = userNameLength;
|
||
}
|
||
|
||
if (lpUrlComponents->lpszPassword != NULL) {
|
||
if (lpUrlComponents->dwPasswordLength > passwordLength) {
|
||
memcpy((LPVOID)lpUrlComponents->lpszPassword,
|
||
(LPVOID)password,
|
||
passwordLength
|
||
);
|
||
lpUrlComponents->lpszPassword[passwordLength] = '\0';
|
||
if (dwFlags & ICU_DECODE) {
|
||
UrlUnescapeInPlace(lpUrlComponents->lpszPassword, 0);
|
||
}
|
||
} else {
|
||
++passwordLength;
|
||
copyFailure = TRUE;
|
||
}
|
||
lpUrlComponents->dwPasswordLength = passwordLength;
|
||
} else if (lpUrlComponents->dwPasswordLength != 0) {
|
||
lpUrlComponents->lpszPassword = password;
|
||
lpUrlComponents->dwPasswordLength = passwordLength;
|
||
}
|
||
|
||
if (lpUrlComponents->lpszUrlPath != NULL) {
|
||
if (lpUrlComponents->dwUrlPathLength > urlPathLength) {
|
||
memcpy((LPVOID)lpUrlComponents->lpszUrlPath,
|
||
(LPVOID)urlPath,
|
||
urlPathLength
|
||
);
|
||
lpUrlComponents->lpszUrlPath[urlPathLength] = '\0';
|
||
if (dwFlags & ICU_DECODE) {
|
||
UrlUnescapeInPlace(lpUrlComponents->lpszUrlPath, 0);
|
||
}
|
||
lpUrlComponents->dwUrlPathLength = urlPathLength;
|
||
} else {
|
||
++urlPathLength;
|
||
copyFailure = TRUE;
|
||
lpUrlComponents->dwUrlPathLength = urlPathLength;
|
||
}
|
||
} else if (lpUrlComponents->dwUrlPathLength != 0) {
|
||
lpUrlComponents->lpszUrlPath = urlPath;
|
||
lpUrlComponents->dwUrlPathLength = urlPathLength;
|
||
}
|
||
|
||
if (lpUrlComponents->lpszExtraInfo != NULL) {
|
||
if (lpUrlComponents->dwExtraInfoLength > extraInfoLength) {
|
||
memcpy((LPVOID)lpUrlComponents->lpszExtraInfo,
|
||
(LPVOID)extraInfo,
|
||
extraInfoLength
|
||
);
|
||
lpUrlComponents->lpszExtraInfo[extraInfoLength] = '\0';
|
||
if (dwFlags & ICU_DECODE) {
|
||
UrlUnescapeInPlace(lpUrlComponents->lpszExtraInfo, 0);
|
||
}
|
||
} else {
|
||
++extraInfoLength;
|
||
copyFailure = TRUE;
|
||
}
|
||
lpUrlComponents->dwExtraInfoLength = extraInfoLength;
|
||
} else if (lpUrlComponents->dwExtraInfoLength != 0) {
|
||
lpUrlComponents->lpszExtraInfo = extraInfo;
|
||
lpUrlComponents->dwExtraInfoLength = extraInfoLength;
|
||
}
|
||
|
||
//
|
||
// we may have failed to copy one or more components because we didn't have
|
||
// enough buffer space.
|
||
//
|
||
// N.B. Don't change error below here. If need be, move this test lower
|
||
//
|
||
|
||
if (copyFailure) {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
|
||
//
|
||
// copy the scheme type
|
||
//
|
||
|
||
lpUrlComponents->nScheme = schemeType;
|
||
|
||
//
|
||
// convert 0 port (not in URL) to default value for scheme
|
||
//
|
||
|
||
if (nPort == INTERNET_INVALID_PORT_NUMBER && !havePort) {
|
||
switch (schemeType) {
|
||
case INTERNET_SCHEME_HTTP:
|
||
nPort = INTERNET_DEFAULT_HTTP_PORT;
|
||
break;
|
||
|
||
case INTERNET_SCHEME_HTTPS:
|
||
nPort = INTERNET_DEFAULT_HTTPS_PORT;
|
||
break;
|
||
}
|
||
}
|
||
lpUrlComponents->nPort = nPort;
|
||
|
||
crack_error:
|
||
|
||
if (urlCopy != NULL) {
|
||
DEL_STRING(urlCopy);
|
||
}
|
||
|
||
quit:
|
||
BOOL success = (error==ERROR_SUCCESS);
|
||
|
||
if (!success) {
|
||
DEBUG_ERROR(API, error);
|
||
SetLastError(error);
|
||
}
|
||
|
||
DEBUG_LEAVE_API(success);
|
||
return success;
|
||
}
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
WinHttpCreateUrlA(
|
||
IN LPURL_COMPONENTSA lpUrlComponents,
|
||
IN DWORD dwFlags,
|
||
OUT LPSTR lpszUrl OPTIONAL,
|
||
IN OUT LPDWORD lpdwUrlLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates an URL from its constituent parts
|
||
|
||
Arguments:
|
||
|
||
lpUrlComponents - pointer to URL_COMPONENTS structure containing pointers
|
||
and lengths of components of interest
|
||
|
||
dwFlags - flags controlling function:
|
||
|
||
ICU_ESCAPE - the components contain characters that
|
||
must be escaped in the output URL
|
||
|
||
lpszUrl - pointer to buffer where output URL will be written
|
||
|
||
lpdwUrlLength - IN: number of bytes in lpszUrl buffer
|
||
OUT: if success, number of characters in lpszUrl, else
|
||
number of bytes required for buffer
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - URL written to lpszUrl
|
||
|
||
Failure - call GetLastError() for more info
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"WinHttpCreateUrlA",
|
||
"%#x, %#x, %#x, %#x",
|
||
lpUrlComponents,
|
||
dwFlags,
|
||
lpszUrl,
|
||
lpdwUrlLength
|
||
));
|
||
|
||
#if INET_DEBUG
|
||
|
||
LPSTR lpszUrlOriginal = lpszUrl;
|
||
|
||
#endif
|
||
|
||
DWORD error = ERROR_SUCCESS;
|
||
LPSTR encodedUrlPath = NULL;
|
||
LPSTR encodedExtraInfo = NULL;
|
||
|
||
//
|
||
// validate parameters
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(lpszUrl)) {
|
||
*lpdwUrlLength = 0;
|
||
}
|
||
|
||
//
|
||
// allocate large buffers from heap
|
||
//
|
||
|
||
encodedUrlPath = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, INTERNET_MAX_URL_LENGTH + 1);
|
||
encodedExtraInfo = (LPSTR)ALLOCATE_MEMORY(LMEM_FIXED, INTERNET_MAX_URL_LENGTH + 1);
|
||
if ((encodedUrlPath == NULL) || (encodedExtraInfo == NULL)) {
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// if we get an exception, we return ERROR_INVALID_PARAMETER
|
||
//
|
||
|
||
__try {
|
||
|
||
//
|
||
// get the individual components to copy
|
||
//
|
||
|
||
LPSTR schemeName;
|
||
DWORD schemeNameLength;
|
||
DWORD schemeFlags;
|
||
LPSTR hostName;
|
||
DWORD hostNameLength;
|
||
INTERNET_PORT nPort;
|
||
DWORD portLength;
|
||
LPSTR userName;
|
||
DWORD userNameLength;
|
||
LPSTR password;
|
||
DWORD passwordLength;
|
||
LPSTR urlPath;
|
||
DWORD urlPathLength;
|
||
DWORD extraLength;
|
||
DWORD encodedUrlPathLength;
|
||
LPSTR extraInfo;
|
||
DWORD extraInfoLength;
|
||
DWORD encodedExtraInfoLength;
|
||
LPSTR schemeSep;
|
||
DWORD schemeSepLength;
|
||
INTERNET_SCHEME schemeType;
|
||
INTERNET_PORT defaultPort;
|
||
|
||
//
|
||
// if the scheme name is absent then we use the default
|
||
//
|
||
|
||
schemeName = lpUrlComponents->lpszScheme;
|
||
schemeType = lpUrlComponents->nScheme;
|
||
|
||
if (schemeName == NULL) {
|
||
if (schemeType == INTERNET_SCHEME_DEFAULT){
|
||
schemeName = DEFAULT_URL_SCHEME_NAME;
|
||
schemeNameLength = sizeof(DEFAULT_URL_SCHEME_NAME) - 1;
|
||
}
|
||
else {
|
||
schemeName = MapUrlScheme(schemeType, &schemeNameLength);
|
||
}
|
||
} else {
|
||
schemeNameLength = lpUrlComponents->dwSchemeLength;
|
||
if (schemeNameLength == 0) {
|
||
schemeNameLength = lstrlen(schemeName);
|
||
}
|
||
}
|
||
|
||
if (schemeNameLength == 0)
|
||
{
|
||
error = ERROR_INVALID_PARAMETER;
|
||
goto quit;
|
||
}
|
||
|
||
|
||
//
|
||
// doesn't have to be a host name
|
||
//
|
||
|
||
hostName = lpUrlComponents->lpszHostName;
|
||
portLength = 0;
|
||
if (hostName != NULL) {
|
||
hostNameLength = lpUrlComponents->dwHostNameLength;
|
||
if (hostNameLength == 0) {
|
||
hostNameLength = lstrlen(hostName);
|
||
}
|
||
|
||
//
|
||
// if the port is default then we don't add it to the URL, else we need to
|
||
// copy it as a string
|
||
//
|
||
// there won't be a port unless there's host.
|
||
|
||
schemeType = MapUrlSchemeName(schemeName, schemeNameLength ? schemeNameLength : -1);
|
||
switch (schemeType) {
|
||
case INTERNET_SCHEME_HTTP:
|
||
defaultPort = INTERNET_DEFAULT_HTTP_PORT;
|
||
break;
|
||
|
||
case INTERNET_SCHEME_HTTPS:
|
||
defaultPort = INTERNET_DEFAULT_HTTPS_PORT;
|
||
break;
|
||
|
||
default:
|
||
defaultPort = INTERNET_INVALID_PORT_NUMBER;
|
||
break;
|
||
}
|
||
|
||
if (lpUrlComponents->nPort != defaultPort) {
|
||
|
||
INTERNET_PORT divisor;
|
||
|
||
nPort = lpUrlComponents->nPort;
|
||
if (nPort) {
|
||
divisor = 10000;
|
||
portLength = 6; // max is 5 characters, plus 1 for ':'
|
||
while ((nPort / divisor) == 0) {
|
||
--portLength;
|
||
divisor /= 10;
|
||
}
|
||
} else {
|
||
portLength = 2; // port is ":0"
|
||
}
|
||
}
|
||
} else {
|
||
hostNameLength = 0;
|
||
}
|
||
|
||
|
||
//
|
||
// doesn't have to be a user name
|
||
//
|
||
|
||
userName = lpUrlComponents->lpszUserName;
|
||
if (userName != NULL) {
|
||
userNameLength = lpUrlComponents->dwUserNameLength;
|
||
if (userNameLength == 0) {
|
||
userNameLength = lstrlen(userName);
|
||
}
|
||
} else {
|
||
|
||
userNameLength = 0;
|
||
}
|
||
|
||
//
|
||
// doesn't have to be a password
|
||
//
|
||
|
||
password = lpUrlComponents->lpszPassword;
|
||
if (password != NULL) {
|
||
passwordLength = lpUrlComponents->dwPasswordLength;
|
||
if (passwordLength == 0) {
|
||
passwordLength = lstrlen(password);
|
||
}
|
||
} else {
|
||
|
||
passwordLength = 0;
|
||
}
|
||
|
||
//
|
||
// but if there's a password without a user name, then its an error
|
||
//
|
||
|
||
if (password && !userName) {
|
||
error = ERROR_INVALID_PARAMETER;
|
||
} else {
|
||
|
||
//
|
||
// determine the scheme type for possible uses below
|
||
//
|
||
|
||
schemeFlags = 0;
|
||
if (strnicmp(schemeName, "http", schemeNameLength) == 0) {
|
||
schemeFlags = SCHEME_HTTP;
|
||
} else if (strnicmp(schemeName, "ftp", schemeNameLength) == 0) {
|
||
schemeFlags = SCHEME_FTP;
|
||
} else if (strnicmp(schemeName, "gopher", schemeNameLength) == 0) {
|
||
schemeFlags = SCHEME_GOPHER;
|
||
}
|
||
|
||
//
|
||
// doesn't have to be an URL-path. Empty string is default
|
||
//
|
||
|
||
urlPath = lpUrlComponents->lpszUrlPath;
|
||
if (urlPath != NULL) {
|
||
urlPathLength = lpUrlComponents->dwUrlPathLength;
|
||
if (urlPathLength == 0) {
|
||
urlPathLength = lstrlen(urlPath);
|
||
}
|
||
if ((*urlPath != '/') && (*urlPath != '\\')) {
|
||
extraLength = 1;
|
||
} else {
|
||
extraLength = 0;
|
||
}
|
||
|
||
//
|
||
// if requested, we will encode the URL-path
|
||
//
|
||
|
||
if (dwFlags & ICU_ESCAPE) {
|
||
|
||
//
|
||
// only encode the URL-path if it's a recognized scheme
|
||
//
|
||
|
||
if (schemeFlags != 0) {
|
||
encodedUrlPathLength = INTERNET_MAX_URL_LENGTH + 1;
|
||
error = EncodeUrlPath(NO_ENCODE_PATH_SEP,
|
||
schemeFlags,
|
||
urlPath,
|
||
urlPathLength,
|
||
&encodedUrlPath,
|
||
&encodedUrlPathLength
|
||
);
|
||
if (error == ERROR_SUCCESS) {
|
||
urlPath = encodedUrlPath;
|
||
urlPathLength = encodedUrlPathLength;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
urlPathLength = 0;
|
||
extraLength = 0;
|
||
}
|
||
|
||
//
|
||
// handle extra info if present
|
||
//
|
||
|
||
if (error == ERROR_SUCCESS) {
|
||
extraInfo = lpUrlComponents->lpszExtraInfo;
|
||
if (extraInfo != NULL) {
|
||
extraInfoLength = lpUrlComponents->dwExtraInfoLength;
|
||
if (extraInfoLength == 0) {
|
||
extraInfoLength = lstrlen(extraInfo);
|
||
}
|
||
|
||
//
|
||
// if requested, we will encode the extra info
|
||
//
|
||
|
||
if (dwFlags & ICU_ESCAPE) {
|
||
|
||
//
|
||
// only encode the extra info if it's a recognized scheme
|
||
//
|
||
|
||
if (schemeFlags != 0) {
|
||
encodedExtraInfoLength = INTERNET_MAX_URL_LENGTH + 1;
|
||
error = EncodeUrlPath(0,
|
||
schemeFlags,
|
||
extraInfo,
|
||
extraInfoLength,
|
||
&encodedExtraInfo,
|
||
&encodedExtraInfoLength
|
||
);
|
||
if (error == ERROR_SUCCESS) {
|
||
extraInfo = encodedExtraInfo;
|
||
extraInfoLength = encodedExtraInfoLength;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
extraInfoLength = 0;
|
||
}
|
||
}
|
||
|
||
DWORD requiredSize;
|
||
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Determine if we have a protocol scheme that requires slashes
|
||
//
|
||
|
||
if (DoesSchemeRequireSlashes(schemeName, schemeNameLength, (hostName != NULL))) {
|
||
schemeSep = "://";
|
||
schemeSepLength = sizeof("://") - 1;
|
||
} else {
|
||
schemeSep = ":";
|
||
schemeSepLength = sizeof(":") - 1;
|
||
}
|
||
|
||
//
|
||
// ensure we have enough buffer space
|
||
//
|
||
|
||
requiredSize = schemeNameLength
|
||
+ schemeSepLength
|
||
+ hostNameLength
|
||
+ portLength
|
||
+ (userName ? userNameLength + 1 : 0) // +1 for '@'
|
||
+ (password ? passwordLength + 1 : 0) // +1 for ':'
|
||
+ urlPathLength
|
||
+ extraLength
|
||
+ extraInfoLength
|
||
+ 1 // +1 for '\0'
|
||
;
|
||
|
||
//
|
||
// if there is enough buffer, copy the URL
|
||
//
|
||
|
||
if (*lpdwUrlLength >= requiredSize) {
|
||
memcpy((LPVOID)lpszUrl, (LPVOID)schemeName, schemeNameLength);
|
||
lpszUrl += schemeNameLength;
|
||
memcpy((LPVOID)lpszUrl, (LPVOID)schemeSep, schemeSepLength);
|
||
lpszUrl += schemeSepLength;
|
||
if (userName) {
|
||
memcpy((LPVOID)lpszUrl, (LPVOID)userName, userNameLength);
|
||
lpszUrl += userNameLength;
|
||
if (password) {
|
||
*lpszUrl++ = ':';
|
||
memcpy((LPVOID)lpszUrl, (LPVOID)password, passwordLength);
|
||
lpszUrl += passwordLength;
|
||
}
|
||
*lpszUrl++ = '@';
|
||
}
|
||
if (hostName) {
|
||
memcpy((LPVOID)lpszUrl, (LPVOID)hostName, hostNameLength);
|
||
lpszUrl += hostNameLength;
|
||
|
||
// We won't attach a port unless there's a host to go with it.
|
||
if (portLength) {
|
||
lpszUrl += wsprintf(lpszUrl, ":%d", nPort & 0xffff);
|
||
}
|
||
|
||
}
|
||
if (urlPath) {
|
||
|
||
//
|
||
// Only do extraLength if we've actually copied something
|
||
// after the scheme.
|
||
//
|
||
|
||
if (extraLength != 0 && (userName || hostName || portLength)) {
|
||
*lpszUrl++ = '/';
|
||
} else if (extraLength != 0) {
|
||
--requiredSize;
|
||
}
|
||
memcpy((LPVOID)lpszUrl, (LPVOID)urlPath, urlPathLength);
|
||
lpszUrl += urlPathLength;
|
||
} else if (extraLength != 0) {
|
||
--requiredSize;
|
||
}
|
||
if (extraInfo) {
|
||
memcpy((LPVOID)lpszUrl, (LPVOID)extraInfo, extraInfoLength);
|
||
lpszUrl += extraInfoLength;
|
||
}
|
||
|
||
//
|
||
// terminate string
|
||
//
|
||
|
||
*lpszUrl = '\0';
|
||
|
||
//
|
||
// -1 for terminating '\0'
|
||
//
|
||
|
||
--requiredSize;
|
||
} else {
|
||
|
||
//
|
||
// not enough buffer space - just return the required buffer length
|
||
//
|
||
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
}
|
||
|
||
//
|
||
// update returned parameters
|
||
//
|
||
|
||
*lpdwUrlLength = requiredSize;
|
||
}
|
||
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||
error = ERROR_INVALID_PARAMETER;
|
||
}
|
||
ENDEXCEPT
|
||
quit:
|
||
|
||
//
|
||
// clear up the buffers we allocated
|
||
//
|
||
|
||
|
||
if (encodedUrlPath != NULL) {
|
||
FREE_MEMORY(encodedUrlPath);
|
||
}
|
||
if (encodedExtraInfo != NULL) {
|
||
FREE_MEMORY(encodedExtraInfo);
|
||
}
|
||
|
||
BOOL success = (error==ERROR_SUCCESS);
|
||
|
||
if (success) {
|
||
|
||
DEBUG_PRINT_API(API,
|
||
INFO,
|
||
("URL = %q\n",
|
||
lpszUrlOriginal
|
||
));
|
||
} else {
|
||
|
||
DEBUG_ERROR(API, error);
|
||
SetLastError(error);
|
||
}
|
||
|
||
DEBUG_LEAVE_API(success);
|
||
return success;
|
||
}
|
||
|
||
//
|
||
// ICUHrToWin32Error() is specifically for converting the return codes for
|
||
// Url* APIs in shlwapi into win32 errors.
|
||
// WARNING: it should not be used for any other purpose.
|
||
//
|
||
DWORD
|
||
ICUHrToWin32Error(HRESULT hr)
|
||
{
|
||
DWORD err = ERROR_INVALID_PARAMETER;
|
||
switch(hr)
|
||
{
|
||
case E_OUTOFMEMORY:
|
||
err = ERROR_NOT_ENOUGH_MEMORY;
|
||
break;
|
||
|
||
case E_POINTER:
|
||
err = ERROR_INSUFFICIENT_BUFFER;
|
||
break;
|
||
|
||
case S_OK:
|
||
err = ERROR_SUCCESS;
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
InternetCanonicalizeUrlA(
|
||
IN LPCSTR lpszUrl,
|
||
OUT LPSTR lpszBuffer,
|
||
IN OUT LPDWORD lpdwBufferLength,
|
||
IN DWORD dwFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Combines a relative URL with a base URL to form a new full URL.
|
||
|
||
Arguments:
|
||
|
||
lpszUrl - pointer to URL to be canonicalize
|
||
lpszBuffer - pointer to buffer where new URL is written
|
||
lpdwBufferLength - size of buffer on entry, length of new URL on exit
|
||
dwFlags - flags controlling operation
|
||
|
||
Return Value:
|
||
|
||
BOOL - TRUE if successful, FALSE if not
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"InternetCanonicalizeUrlA",
|
||
"%q, %#x, %#x [%d], %#x",
|
||
lpszUrl,
|
||
lpszBuffer,
|
||
lpdwBufferLength,
|
||
lpdwBufferLength ? *lpdwBufferLength : 0,
|
||
dwFlags
|
||
));
|
||
|
||
HRESULT hr ;
|
||
BOOL bRet = TRUE;;
|
||
|
||
INET_ASSERT(lpszUrl);
|
||
INET_ASSERT(lpszBuffer);
|
||
INET_ASSERT(lpdwBufferLength && (*lpdwBufferLength > 0));
|
||
|
||
//
|
||
// the flags for the Url* APIs in shlwapi should be the same
|
||
// except that NO_ENCODE is on by default. so we need to flip it
|
||
//
|
||
dwFlags ^= ICU_NO_ENCODE;
|
||
|
||
// Check for invalid parameters
|
||
|
||
if (!lpszUrl || !lpszBuffer || !lpdwBufferLength || *lpdwBufferLength == 0 || IsBadWritePtr(lpszBuffer, *lpdwBufferLength*sizeof(CHAR)))
|
||
{
|
||
hr = E_INVALIDARG;
|
||
}
|
||
else
|
||
{
|
||
hr = UrlCanonicalizeA(lpszUrl, lpszBuffer,
|
||
lpdwBufferLength, dwFlags | URL_WININET_COMPATIBILITY);
|
||
}
|
||
|
||
if(FAILED(hr))
|
||
{
|
||
DWORD dw = ICUHrToWin32Error(hr);
|
||
|
||
bRet = FALSE;
|
||
|
||
DEBUG_ERROR(API, dw);
|
||
|
||
SetLastError(dw);
|
||
}
|
||
|
||
DEBUG_LEAVE_API(bRet);
|
||
|
||
return bRet;
|
||
}
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
InternetCombineUrlA(
|
||
IN LPCSTR lpszBaseUrl,
|
||
IN LPCSTR lpszRelativeUrl,
|
||
OUT LPSTR lpszBuffer,
|
||
IN OUT LPDWORD lpdwBufferLength,
|
||
IN DWORD dwFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Combines a relative URL with a base URL to form a new full URL.
|
||
|
||
Arguments:
|
||
|
||
lpszBaseUrl - pointer to base URL
|
||
lpszRelativeUrl - pointer to relative URL
|
||
lpszBuffer - pointer to buffer where new URL is written
|
||
lpdwBufferLength - size of buffer on entry, length of new URL on exit
|
||
dwFlags - flags controlling operation
|
||
|
||
Return Value:
|
||
|
||
BOOL - TRUE if successful, FALSE if not
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"InternetCombineUrlA",
|
||
"%q, %q, %#x, %#x [%d], %#x",
|
||
lpszBaseUrl,
|
||
lpszRelativeUrl,
|
||
lpszBuffer,
|
||
lpdwBufferLength,
|
||
lpdwBufferLength ? *lpdwBufferLength : 0,
|
||
dwFlags
|
||
));
|
||
|
||
HRESULT hr ;
|
||
BOOL bRet;
|
||
|
||
INET_ASSERT(lpszBaseUrl);
|
||
INET_ASSERT(lpszRelativeUrl);
|
||
INET_ASSERT(lpdwBufferLength);
|
||
|
||
//
|
||
// the flags for the Url* APIs in shlwapi should be the same
|
||
// except that NO_ENCODE is on by default. so we need to flip it
|
||
//
|
||
dwFlags ^= ICU_NO_ENCODE;
|
||
|
||
// Check for invalid parameters
|
||
|
||
if (!lpszBaseUrl || !lpszRelativeUrl || !lpdwBufferLength || (lpszBuffer && IsBadWritePtr(lpszBuffer, *lpdwBufferLength*sizeof(CHAR))))
|
||
{
|
||
hr = E_INVALIDARG;
|
||
}
|
||
else
|
||
{
|
||
hr = UrlCombineA(lpszBaseUrl, lpszRelativeUrl, lpszBuffer,
|
||
lpdwBufferLength, dwFlags | URL_WININET_COMPATIBILITY);
|
||
}
|
||
|
||
if(FAILED(hr))
|
||
{
|
||
DWORD dw = ICUHrToWin32Error(hr);
|
||
|
||
bRet = FALSE;
|
||
|
||
DEBUG_ERROR(API, dw);
|
||
|
||
SetLastError(dw);
|
||
}
|
||
else
|
||
bRet = TRUE;
|
||
|
||
IF_DEBUG_CODE() {
|
||
if (bRet) {
|
||
DEBUG_PRINT_API(API,
|
||
INFO,
|
||
("URL = %q\n",
|
||
lpszBuffer
|
||
));
|
||
}
|
||
}
|
||
|
||
DEBUG_LEAVE_API(bRet);
|
||
|
||
return bRet;
|
||
}
|
||
|
||
|
||
INTERNETAPI
|
||
HINTERNET
|
||
WINAPI
|
||
InternetOpenA(
|
||
IN LPCSTR lpszAgent,
|
||
IN DWORD dwAccessType,
|
||
IN LPCSTR lpszProxy OPTIONAL,
|
||
IN LPCSTR lpszProxyBypass OPTIONAL,
|
||
IN DWORD dwFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens a root Internet handle from which all HINTERNET objects are derived
|
||
|
||
Arguments:
|
||
|
||
lpszAgent - name of the application making the request (arbitrary
|
||
identifying string). Used in "User-Agent" header when
|
||
communicating with HTTP servers, if the application does
|
||
not add a User-Agent header of its own
|
||
|
||
dwAccessType - type of access required. Can be
|
||
|
||
INTERNET_OPEN_TYPE_PRECONFIG
|
||
- Gets the configuration from the registry
|
||
|
||
INTERNET_OPEN_TYPE_DIRECT
|
||
- Requests are made directly to the nominated server
|
||
|
||
INTERNET_OPEN_TYPE_PROXY
|
||
- Requests are made via the nominated proxy
|
||
|
||
INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY
|
||
- Like Pre-Config, but prevents JavaScript, INS
|
||
and other auto-proxy types from being used.
|
||
|
||
lpszProxy - if INTERNET_OPEN_TYPE_PROXY, a list of proxy servers to
|
||
use
|
||
|
||
lpszProxyBypass - if INTERNET_OPEN_TYPE_PROXY, a list of servers which we
|
||
will communicate with directly
|
||
|
||
dwFlags - flags to control the operation of this API or potentially
|
||
all APIs called on the handle generated by this API.
|
||
Currently supported are:
|
||
|
||
WINHTTP_FLAG_ASYNC - Not supported in WinHttpX v6.
|
||
|
||
|
||
Return Value:
|
||
|
||
HINTERNET
|
||
Success - handle of Internet object
|
||
|
||
Failure - NULL. For more information, call GetLastError()
|
||
|
||
--*/
|
||
|
||
{
|
||
PERF_INIT();
|
||
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Handle,
|
||
"InternetOpenA",
|
||
"%q, %s (%d), %q, %q, %#x",
|
||
lpszAgent,
|
||
InternetMapOpenType(dwAccessType),
|
||
dwAccessType,
|
||
lpszProxy,
|
||
lpszProxyBypass,
|
||
dwFlags
|
||
));
|
||
|
||
DWORD error;
|
||
HINTERNET hInternet = NULL;
|
||
|
||
if (!GlobalDataInitialized) {
|
||
error = GlobalDataInitialize();
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// validate parameters
|
||
//
|
||
|
||
if (!
|
||
(
|
||
(dwAccessType == INTERNET_OPEN_TYPE_DIRECT)
|
||
|| (dwAccessType == INTERNET_OPEN_TYPE_PROXY)
|
||
|| (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
|
||
|| (dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY)
|
||
|| (
|
||
(dwAccessType == INTERNET_OPEN_TYPE_PROXY)
|
||
&&
|
||
(
|
||
!ARGUMENT_PRESENT(lpszProxy)
|
||
|| (*lpszProxy == '\0')
|
||
|
||
)
|
||
)
|
||
|| (dwFlags & ~WINHTTP_OPEN_FLAGS_MASK)
|
||
)
|
||
)
|
||
{
|
||
error = ERROR_INVALID_PARAMETER;
|
||
goto quit;
|
||
}
|
||
|
||
|
||
INTERNET_HANDLE_OBJECT * lpInternet;
|
||
|
||
lpInternet = New INTERNET_HANDLE_OBJECT(lpszAgent,
|
||
dwAccessType,
|
||
(LPSTR)lpszProxy,
|
||
(LPSTR)lpszProxyBypass,
|
||
dwFlags
|
||
);
|
||
if (lpInternet == NULL) {
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto quit;
|
||
}
|
||
error = lpInternet->GetStatus();
|
||
if (error == ERROR_SUCCESS) {
|
||
hInternet = (HINTERNET)lpInternet;
|
||
|
||
//
|
||
// success - don't return the object address, return the pseudo-handle
|
||
// value we generated
|
||
//
|
||
|
||
hInternet = ((HANDLE_OBJECT *)hInternet)->GetPseudoHandle();
|
||
|
||
} else {
|
||
|
||
//
|
||
// hack fix to stop InternetIndicateStatus (called from the handle
|
||
// object destructor) blowing up if there is no handle object in the
|
||
// thread info block. We can't call back anyway
|
||
//
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (lpThreadInfo) {
|
||
|
||
//
|
||
// BUGBUG - incorrect handle value
|
||
//
|
||
|
||
_InternetSetObjectHandle(lpThreadInfo, lpInternet, lpInternet);
|
||
}
|
||
|
||
//
|
||
// we failed during initialization. Kill the handle using Dereference()
|
||
// (in order to stop the debug version complaining about the reference
|
||
// count not being 0. Invalidate for same reason)
|
||
//
|
||
|
||
lpInternet->Invalidate();
|
||
lpInternet->Dereference();
|
||
|
||
INET_ASSERT(hInternet == NULL);
|
||
|
||
}
|
||
|
||
quit:
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
|
||
DEBUG_ERROR(API, error);
|
||
|
||
SetLastError(error);
|
||
}
|
||
|
||
DEBUG_LEAVE_API(hInternet);
|
||
|
||
return hInternet;
|
||
}
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
WinHttpCloseHandle(
|
||
IN HINTERNET hInternet
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Closes any open internet handle object
|
||
|
||
Arguments:
|
||
|
||
hInternet - handle of internet object to close
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE
|
||
|
||
Failure - FALSE. For more information call GetLastError()
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"WinHttpCloseHandle",
|
||
"%#x",
|
||
hInternet
|
||
));
|
||
|
||
PERF_ENTER(InternetCloseHandle);
|
||
|
||
DWORD error;
|
||
BOOL success = FALSE;
|
||
HINTERNET hInternetMapped = NULL;
|
||
|
||
if (!GlobalDataInitialized) {
|
||
error = GlobalDataInitialize();
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// map the handle. Don't invalidate it (_InternetCloseHandle() does this)
|
||
//
|
||
|
||
error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, FALSE);
|
||
if (error != ERROR_SUCCESS) {
|
||
if (hInternetMapped == NULL) {
|
||
|
||
//
|
||
// the handle never existed or has been completely destroyed
|
||
//
|
||
|
||
DEBUG_PRINT(API,
|
||
ERROR,
|
||
("Handle %#x is invalid\n",
|
||
hInternet
|
||
));
|
||
|
||
//
|
||
// catch invalid handles - may help caller
|
||
//
|
||
|
||
DEBUG_BREAK(INVALID_HANDLES);
|
||
|
||
} else {
|
||
|
||
//
|
||
// this handle is already being closed (it's invalidated). We only
|
||
// need one InternetCloseHandle() operation to invalidate the handle.
|
||
// All other threads will simply dereference the handle, and
|
||
// eventually it will be destroyed
|
||
//
|
||
|
||
DereferenceObject((LPVOID)hInternetMapped);
|
||
}
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// the handle is not invalidated
|
||
//
|
||
|
||
HANDLE_OBJECT * pHandle;
|
||
|
||
pHandle = (HANDLE_OBJECT *)hInternetMapped;
|
||
|
||
DEBUG_PRINT(INET,
|
||
INFO,
|
||
("handle %#x == %#x == %s\n",
|
||
hInternet,
|
||
hInternetMapped,
|
||
InternetMapHandleType(pHandle->GetHandleType())
|
||
));
|
||
|
||
//
|
||
// clear the handle object last error variables
|
||
//
|
||
|
||
InternetClearLastError();
|
||
|
||
//
|
||
// decrement session count here rather than in destructor, since
|
||
// the session is ref-counted and there may still be outstanding
|
||
// references from request/connect handles on async fsms.
|
||
//
|
||
if (pHandle->GetHandleType() == TypeInternetHandle)
|
||
{
|
||
InterlockedDecrement(&g_cSessionCount);
|
||
}
|
||
|
||
//
|
||
// remove the reference added by MapHandleToAddress(), or the handle won't
|
||
// be destroyed by _InternetCloseHandle()
|
||
//
|
||
|
||
DereferenceObject((LPVOID)hInternetMapped);
|
||
|
||
//
|
||
// use _InternetCloseHandle() to do the work
|
||
//
|
||
|
||
success = _InternetCloseHandle(hInternet);
|
||
|
||
quit:
|
||
|
||
// SetLastError must be called after PERF_LEAVE !!!
|
||
PERF_LEAVE(InternetCloseHandle);
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
DEBUG_ERROR(API, error);
|
||
SetLastError(error);
|
||
}
|
||
|
||
DEBUG_LEAVE_API(success);
|
||
|
||
return success;
|
||
}
|
||
|
||
|
||
BOOL
|
||
_InternetCloseHandle(
|
||
IN HINTERNET hInternet
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Same as InternetCloseHandle() except does not clear out the last error text.
|
||
Mainly for FTP
|
||
|
||
Arguments:
|
||
|
||
hInternet - handle of internet object to close
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE
|
||
|
||
Failure - FALSE. For more information call GetLastError()
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_INET,
|
||
Bool,
|
||
"_InternetCloseHandle",
|
||
"%#x",
|
||
hInternet
|
||
));
|
||
|
||
DWORD error;
|
||
BOOL success;
|
||
HINTERNET hInternetMapped = NULL;
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (lpThreadInfo == NULL) {
|
||
if (InDllCleanup) {
|
||
error = ERROR_WINHTTP_SHUTDOWN;
|
||
} else {
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
}
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// map the handle and invalidate it. This will cause any new requests with
|
||
// the handle as a parameter to fail
|
||
//
|
||
|
||
error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, TRUE);
|
||
if (error != ERROR_SUCCESS) {
|
||
if (hInternetMapped != NULL) {
|
||
|
||
//
|
||
// the handle is already being closed, or is already deleted
|
||
//
|
||
|
||
DereferenceObject((LPVOID)hInternetMapped);
|
||
}
|
||
|
||
//
|
||
// since this is the only function that can invalidate a handle, if we
|
||
// are here then the handle is just waiting for its refcount to go to
|
||
// zero. We already removed the refcount we added above, so we're in
|
||
// the clear
|
||
//
|
||
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// there may be an active socket operation. We close the socket to abort the
|
||
// operation
|
||
//
|
||
|
||
((INTERNET_HANDLE_OBJECT *)hInternetMapped)->AbortSocket();
|
||
|
||
//
|
||
// we need the parent handle - we will set this as the handle object being
|
||
// processed by this thread. This is required for async worker threads (see
|
||
// below)
|
||
//
|
||
|
||
HINTERNET hParent;
|
||
HINTERNET hParentMapped;
|
||
DWORD_PTR dwParentContext;
|
||
|
||
hParentMapped = ((HANDLE_OBJECT *)hInternetMapped)->GetParent();
|
||
if (hParentMapped != NULL) {
|
||
hParent = ((HANDLE_OBJECT *)hParentMapped)->GetPseudoHandle();
|
||
dwParentContext = ((HANDLE_OBJECT *)hParentMapped)->GetContext();
|
||
}
|
||
|
||
//
|
||
// set the object handle in the per-thread data structure
|
||
//
|
||
|
||
_InternetSetObjectHandle(lpThreadInfo, hInternet, hInternetMapped);
|
||
|
||
//
|
||
// at this point, there should *always* be at least 2 references on the
|
||
// handle - one added when the object was created, and one added by
|
||
// MapHandleToAddress() above. If the object is still alive after the 2
|
||
// dereferences, then it will be destroyed when the current owning thread
|
||
// dereferences it
|
||
//
|
||
|
||
(void)DereferenceObject((LPVOID)hInternetMapped);
|
||
error = DereferenceObject((LPVOID)hInternetMapped);
|
||
|
||
//
|
||
// now set the object to be the parent. This is necessary for e.g.
|
||
// FtpGetFile() and async requests (where the async worker thread will make
|
||
// an extra callback to deliver the results of the async request)
|
||
//
|
||
|
||
if (hParentMapped != NULL) {
|
||
_InternetSetObjectHandle(lpThreadInfo, hParent, hParentMapped);
|
||
}
|
||
|
||
//
|
||
// if the handle was still alive after dereferencing it then we will inform
|
||
// the app that the close is pending
|
||
//
|
||
|
||
quit:
|
||
|
||
success = (error==ERROR_SUCCESS);
|
||
if (!success) {
|
||
SetLastError(error);
|
||
DEBUG_ERROR(INET, error);
|
||
}
|
||
DEBUG_LEAVE(success);
|
||
return success;
|
||
}
|
||
|
||
|
||
DWORD
|
||
_InternetCloseHandleNoContext(
|
||
IN HINTERNET hInternet
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Same as _InternetCloseHandle() except does not change the per-thread info
|
||
structure handle/context values
|
||
|
||
BUGBUG - This should be handled via a parameter to _InternetCloseHandle(),
|
||
but its close to shipping...
|
||
|
||
Arguments:
|
||
|
||
hInternet - handle of internet object to close
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_INVALID_HANDLE
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_INET,
|
||
Bool,
|
||
"_InternetCloseHandleNoContext",
|
||
"%#x",
|
||
hInternet
|
||
));
|
||
|
||
DWORD error;
|
||
HINTERNET hInternetMapped = NULL;
|
||
|
||
//
|
||
// map the handle and invalidate it. This will cause any new requests with
|
||
// the handle as a parameter to fail
|
||
//
|
||
|
||
error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, TRUE);
|
||
if (error != ERROR_SUCCESS) {
|
||
if (hInternetMapped != NULL) {
|
||
|
||
//
|
||
// the handle is already being closed, or is already deleted
|
||
//
|
||
|
||
DereferenceObject((LPVOID)hInternetMapped);
|
||
}
|
||
|
||
//
|
||
// since this is the only function that can invalidate a handle, if we
|
||
// are here then the handle is just waiting for its refcount to go to
|
||
// zero. We already removed the refcount we added above, so we're in
|
||
// the clear
|
||
//
|
||
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// there may be an active socket operation. We close the socket to abort the
|
||
// operation
|
||
//
|
||
|
||
((INTERNET_HANDLE_OBJECT *)hInternetMapped)->AbortSocket();
|
||
|
||
//
|
||
// at this point, there should *always* be at least 2 references on the
|
||
// handle - one added when the object was created, and one added by
|
||
// MapHandleToAddress() above. If the object is still alive after the 2
|
||
// dereferences, then it will be destroyed when the current owning thread
|
||
// dereferences it
|
||
//
|
||
|
||
(void)DereferenceObject((LPVOID)hInternetMapped);
|
||
error = DereferenceObject((LPVOID)hInternetMapped);
|
||
|
||
quit:
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
|
||
INTERNETAPI
|
||
HINTERNET
|
||
WINAPI
|
||
InternetConnectA(
|
||
IN HINTERNET hInternet,
|
||
IN LPCSTR lpszServerName,
|
||
IN INTERNET_PORT nServerPort,
|
||
IN DWORD dwFlags,
|
||
IN DWORD_PTR dwContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opens a connection with a server, logging-on the user in the process.
|
||
|
||
Arguments:
|
||
|
||
hInternet - Internet handle, returned by InternetOpen()
|
||
|
||
lpszServerName - name of server with which to connect
|
||
|
||
nServerPort - port at which server listens
|
||
|
||
dwFlags - protocol-specific flags. The following are defined:
|
||
- INTERNET_FLAG_KEEP_CONNECTION (HTTP)
|
||
- WINHTTP_FLAG_SECURE (HTTP)
|
||
|
||
dwContext - application-supplied value used to identify this
|
||
request in callbacks
|
||
|
||
Return Value:
|
||
|
||
HINTERNET
|
||
Success - address of a new handle object
|
||
|
||
Failure - NULL. Call GetLastError() for more info
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Handle,
|
||
"InternetConnectA",
|
||
"%#x, %q, %d, %#08x, %#x",
|
||
hInternet,
|
||
lpszServerName,
|
||
nServerPort,
|
||
dwFlags,
|
||
dwContext
|
||
));
|
||
|
||
HINTERNET connectHandle = NULL;
|
||
HINTERNET hInternetMapped = NULL;
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
INTERNET_CONNECT_HANDLE_OBJECT * pConnect = NULL;
|
||
|
||
BOOL bIsWorker = FALSE;
|
||
BOOL bNonNestedAsync = FALSE;
|
||
BOOL isAsync;
|
||
|
||
DWORD error = ERROR_SUCCESS;
|
||
|
||
if (!GlobalDataInitialized) {
|
||
error = ERROR_WINHTTP_NOT_INITIALIZED;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// get the per-thread info block
|
||
//
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo == NULL) {
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
_InternetIncNestingCount();
|
||
bIsWorker = lpThreadInfo->IsAsyncWorkerThread;
|
||
bNonNestedAsync = bIsWorker && (lpThreadInfo->NestedRequests == 1);
|
||
|
||
//
|
||
// handle/refcount munging:
|
||
//
|
||
// sync:
|
||
// map hInternet on input (+1 ref)
|
||
// generate connect handle (1 ref)
|
||
// if failure && !connect handle
|
||
// close connect handle (0 refs: delete)
|
||
// if success
|
||
// deref hInternet (-1 ref)
|
||
// else if going async
|
||
// ref connect handle (2 refs)
|
||
//
|
||
// async:
|
||
// hInternet is mapped connect handle (2 refs)
|
||
// get real hInternet from connect handle parent (2 refs (e.g.))
|
||
// deref connect handle (1 ref)
|
||
// if failure
|
||
// close connect handle (0 refs: delete)
|
||
// deref open handle (-1 ref)
|
||
//
|
||
// N.B. the final deref of the *indicated* handle on async callback will
|
||
// happen in the async code
|
||
//
|
||
|
||
if (bNonNestedAsync) {
|
||
connectHandle = hInternet;
|
||
hInternetMapped = ((HANDLE_OBJECT *)connectHandle)->GetParent();
|
||
hInternet = ((HANDLE_OBJECT *)hInternetMapped)->GetPseudoHandle();
|
||
} else {
|
||
error = MapHandleToAddress(hInternet, (LPVOID *)&hInternetMapped, FALSE);
|
||
if ((error != ERROR_SUCCESS) && (hInternetMapped == NULL)) {
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// set the info and clear the last error info
|
||
//
|
||
|
||
_InternetSetObjectHandle(lpThreadInfo, hInternet, hInternetMapped);
|
||
_InternetClearLastError(lpThreadInfo);
|
||
|
||
//
|
||
// quit now if the handle object is invalidated
|
||
//
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// validate the handle & discover sync/async
|
||
//
|
||
|
||
error = RIsHandleLocal(hInternetMapped,
|
||
NULL,
|
||
&isAsync,
|
||
TypeInternetHandle
|
||
);
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// we allow all valid flags to be passed in
|
||
//
|
||
|
||
if ((dwFlags & ~WINHTTP_CONNECT_FLAGS_MASK)
|
||
|| (lpszServerName == NULL)
|
||
|| (*lpszServerName == '\0'))
|
||
{
|
||
error = ERROR_INVALID_PARAMETER;
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// validate arguments if we're not in the async thread context, in which
|
||
// case we did this when the original request was made
|
||
//
|
||
|
||
if (bNonNestedAsync)
|
||
{
|
||
pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)connectHandle;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// app thread or in async worker thread but being called from another
|
||
// async API, such as InternetOpenUrl()
|
||
//
|
||
|
||
INET_ASSERT(connectHandle == NULL);
|
||
INET_ASSERT(error == ERROR_SUCCESS);
|
||
|
||
error = RMakeInternetConnectObjectHandle(
|
||
hInternetMapped,
|
||
&connectHandle,
|
||
(LPSTR) lpszServerName,
|
||
nServerPort,
|
||
dwFlags,
|
||
dwContext
|
||
);
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// this new handle will be used in callbacks
|
||
//
|
||
|
||
_InternetSetObjectHandle(lpThreadInfo,
|
||
((HANDLE_OBJECT *)connectHandle)->GetPseudoHandle(),
|
||
connectHandle
|
||
);
|
||
|
||
//
|
||
// based on whether we have been asked to perform async I/O AND we are not
|
||
// in an async worker thread context AND the request is to connect with an
|
||
// FTP service (currently only FTP because this request performs network
|
||
// I/O - gopher and HTTP just allocate & fill in memory) AND there is a
|
||
// valid context value, we will queue the async request, or execute the
|
||
// request synchronously
|
||
//
|
||
|
||
pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)connectHandle;
|
||
}
|
||
|
||
|
||
INET_ASSERT(error == ERROR_SUCCESS);
|
||
|
||
quit:
|
||
|
||
_InternetDecNestingCount(1);
|
||
|
||
|
||
done:
|
||
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// success - return generated pseudo-handle
|
||
//
|
||
|
||
connectHandle = ((HANDLE_OBJECT *)connectHandle)->GetPseudoHandle();
|
||
|
||
} else {
|
||
if (bNonNestedAsync
|
||
&& (/*((HANDLE_OBJECT *)connectHandle)->Dereference()
|
||
||*/ ((HANDLE_OBJECT *)connectHandle)->IsInvalidated())) {
|
||
error = ERROR_WINHTTP_OPERATION_CANCELLED;
|
||
}
|
||
|
||
//
|
||
// if we are not pending an async request but we created a handle object
|
||
// then close it
|
||
//
|
||
|
||
if ((error != ERROR_IO_PENDING) && (connectHandle != NULL)) {
|
||
|
||
//
|
||
// use _InternetCloseHandle() to close the handle: it doesn't clear
|
||
// out the last error text, so that an app can find out what the
|
||
// server sent us in the event of an FTP login failure
|
||
//
|
||
|
||
|
||
if (bNonNestedAsync) {
|
||
|
||
//
|
||
// this handle deref'd at async completion
|
||
//
|
||
|
||
hInternetMapped = NULL;
|
||
}
|
||
else
|
||
{
|
||
_InternetCloseHandle(((HANDLE_OBJECT *)connectHandle)->GetPseudoHandle());
|
||
}
|
||
}
|
||
connectHandle = NULL;
|
||
}
|
||
if (hInternetMapped != NULL) {
|
||
DereferenceObject((LPVOID)hInternetMapped);
|
||
}
|
||
if (error != ERROR_SUCCESS) {
|
||
DEBUG_ERROR(API, error);
|
||
SetLastError(error);
|
||
}
|
||
DEBUG_LEAVE_API(connectHandle);
|
||
return connectHandle;
|
||
}
|
||
|
||
|
||
|
||
INTERNETAPI
|
||
HINTERNET
|
||
WINAPI
|
||
InternetOpenUrlA(
|
||
IN HINTERNET hInternet,
|
||
IN LPCSTR lpszUrl,
|
||
IN LPCSTR lpszHeaders OPTIONAL,
|
||
IN DWORD dwHeadersLength,
|
||
IN DWORD dwFlags,
|
||
IN DWORD_PTR dwContext
|
||
)
|
||
{
|
||
// this is dead code
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
WinHttpReadData(
|
||
IN HINTERNET hFile,
|
||
IN LPVOID lpBuffer,
|
||
IN DWORD dwNumberOfBytesToRead,
|
||
OUT LPDWORD lpdwNumberOfBytesRead
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This functions reads the next block of data from the file object.
|
||
|
||
Arguments:
|
||
|
||
hFile - handle returned from Open function
|
||
|
||
lpBuffer - pointer to caller's buffer
|
||
|
||
dwNumberOfBytesToRead - size of lpBuffer in BYTEs
|
||
|
||
lpdwNumberOfBytesRead - returned number of bytes read into lpBuffer
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE
|
||
|
||
Failure - FALSE. Call GetLastError() for more info
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"WinHttpReadData",
|
||
"%#x, %#x, %d, %#x",
|
||
hFile,
|
||
lpBuffer,
|
||
dwNumberOfBytesToRead,
|
||
lpdwNumberOfBytesRead
|
||
));
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
DWORD nestingLevel = 0;
|
||
DWORD error;
|
||
BOOL success = FALSE;
|
||
HINTERNET hFileMapped = NULL;
|
||
DWORD bytesRead = 0;
|
||
BOOL bEndRead = TRUE;
|
||
|
||
if (!GlobalDataInitialized)
|
||
{
|
||
error = ERROR_WINHTTP_NOT_INITIALIZED;
|
||
goto done;
|
||
}
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo == NULL)
|
||
{
|
||
INET_ASSERT(FALSE);
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
//INET_ASSERT(lpThreadInfo->Fsm == NULL);
|
||
|
||
_InternetIncNestingCount();
|
||
nestingLevel = 1;
|
||
|
||
error = MapHandleToAddress(hFile, (LPVOID *)&hFileMapped, FALSE);
|
||
if ((error != ERROR_SUCCESS) && (hFileMapped == NULL))
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
// set the handle, and last-error info in the per-thread data block
|
||
// before we go any further. This allows us to return a status in the async
|
||
// case, even if the handle has been closed
|
||
|
||
if (!lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
PERF_LOG(PE_CLIENT_REQUEST_START,
|
||
AR_INTERNET_READ_FILE,
|
||
lpThreadInfo->ThreadId,
|
||
hFile
|
||
);
|
||
}
|
||
|
||
_InternetSetObjectHandle(lpThreadInfo, hFile, hFileMapped);
|
||
_InternetClearLastError(lpThreadInfo);
|
||
|
||
// if MapHandleToAddress() returned a non-NULL object address, but also an
|
||
// error status, then the handle is being closed - quit
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
BOOL isAsync;
|
||
error = RIsHandleLocal(hFileMapped, NULL, &isAsync, TypeHttpRequestHandle);
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
INET_ASSERT(FALSE);
|
||
goto quit;
|
||
}
|
||
|
||
// validate parameters
|
||
if (!lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
error = ProbeAndSetDword(lpdwNumberOfBytesRead, 0);
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
error = ProbeWriteBuffer(lpBuffer, dwNumberOfBytesToRead);
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
*lpdwNumberOfBytesRead = 0;
|
||
|
||
} // end if (!lpThreadInfo->IsAsyncWorkerThread)
|
||
|
||
|
||
INET_ASSERT(error == ERROR_SUCCESS);
|
||
|
||
// just call the underlying API: return whatever it returns, and let it
|
||
// handle setting the last error
|
||
|
||
CFsm_ReadFile *pFsm;
|
||
|
||
pFsm = New CFsm_ReadFile(lpBuffer,
|
||
dwNumberOfBytesToRead,
|
||
lpdwNumberOfBytesRead
|
||
);
|
||
|
||
|
||
if (pFsm != NULL)
|
||
{
|
||
HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)hFileMapped;
|
||
if (isAsync && !lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
error = DoAsyncFsm(pFsm, pRequest);
|
||
}
|
||
else
|
||
{
|
||
pFsm->SetPushPop(TRUE);
|
||
pFsm->Push();
|
||
error = DoFsm(pFsm);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
success = (error == ERROR_SUCCESS) ? TRUE : FALSE;
|
||
bEndRead = FALSE;
|
||
|
||
|
||
quit:
|
||
|
||
_InternetDecNestingCount(nestingLevel);;
|
||
|
||
if (bEndRead)
|
||
{
|
||
//
|
||
// if handleType is not HttpRequest or File then we are making this
|
||
// request in the context of an uninterruptable async worker thread.
|
||
// HTTP and file requests use the normal mechanism. In the case of non-
|
||
// HTTP and file requests, we need to treat the request as if it were
|
||
// sync and deref the handle
|
||
//
|
||
|
||
ReadFile_End(!lpThreadInfo->IsAsyncWorkerThread,
|
||
success,
|
||
hFileMapped,
|
||
bytesRead,
|
||
lpBuffer,
|
||
dwNumberOfBytesToRead,
|
||
lpdwNumberOfBytesRead
|
||
);
|
||
}
|
||
|
||
if (lpThreadInfo && !lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
|
||
PERF_LOG(PE_CLIENT_REQUEST_END,
|
||
AR_INTERNET_READ_FILE,
|
||
bytesRead,
|
||
lpThreadInfo->ThreadId,
|
||
hFile
|
||
);
|
||
|
||
}
|
||
|
||
done:
|
||
|
||
// if error is not ERROR_SUCCESS then this function returning the error,
|
||
// otherwise the error has already been set by the API we called,
|
||
// irrespective of the value of success
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
DEBUG_ERROR(API, error);
|
||
SetLastError(error);
|
||
success = FALSE;
|
||
}
|
||
|
||
DEBUG_LEAVE_API(success);
|
||
return success;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
VOID
|
||
ReadFile_End(
|
||
IN BOOL bDeref,
|
||
IN BOOL bSuccess,
|
||
IN HINTERNET hFileMapped,
|
||
IN DWORD dwBytesRead,
|
||
IN LPVOID lpBuffer OPTIONAL,
|
||
IN DWORD dwNumberOfBytesToRead,
|
||
OUT LPDWORD lpdwNumberOfBytesRead OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Common end-of-read processing:
|
||
|
||
- update bytes read parameter
|
||
- dump data if logging & API data requested
|
||
- dereference handle if not async request
|
||
|
||
Arguments:
|
||
|
||
bDeref - TRUE if handle should be dereferenced (should be
|
||
FALSE for async request)
|
||
|
||
bSuccess - TRUE if Read completed successfully
|
||
|
||
hFileMapped - mapped file handle
|
||
|
||
dwBytesRead - number of bytes read
|
||
|
||
lpBuffer - into this buffer
|
||
|
||
dwNumberOfBytesToRead - originally requested bytes to read
|
||
|
||
lpdwNumberOfBytesRead - where bytes read is stored
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_INET,
|
||
None,
|
||
"ReadFile_End",
|
||
"%B, %B, %#x, %d, %#x, %d, %#x",
|
||
bDeref,
|
||
bSuccess,
|
||
hFileMapped,
|
||
dwBytesRead,
|
||
lpBuffer,
|
||
dwNumberOfBytesToRead,
|
||
lpdwNumberOfBytesRead
|
||
));
|
||
|
||
if (bSuccess) {
|
||
|
||
//
|
||
// update the amount of immediate data available only if we succeeded
|
||
//
|
||
|
||
((INTERNET_HANDLE_OBJECT *)hFileMapped)->ReduceAvailableDataLength(dwBytesRead);
|
||
|
||
if (lpdwNumberOfBytesRead != NULL) {
|
||
*lpdwNumberOfBytesRead = dwBytesRead;
|
||
|
||
DEBUG_PRINT(API,
|
||
INFO,
|
||
("*lpdwNumberOfBytesRead = %d\n",
|
||
*lpdwNumberOfBytesRead
|
||
));
|
||
|
||
//
|
||
// dump API data only if requested
|
||
//
|
||
|
||
IF_DEBUG_CONTROL(DUMP_API_DATA) {
|
||
DEBUG_DUMP_API(API,
|
||
"Received data:\n",
|
||
lpBuffer,
|
||
*lpdwNumberOfBytesRead
|
||
);
|
||
}
|
||
|
||
}
|
||
if (dwBytesRead < dwNumberOfBytesToRead) {
|
||
|
||
DEBUG_PRINT(API,
|
||
INFO,
|
||
("(!) bytes read (%d) < bytes requested (%d)\n",
|
||
dwBytesRead,
|
||
dwNumberOfBytesToRead
|
||
));
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// if async request, handle will be deref'd after REQUEST_COMPLETE callback
|
||
// is delivered
|
||
//
|
||
|
||
if (bDeref && (hFileMapped != NULL)) {
|
||
DereferenceObject((LPVOID)hFileMapped);
|
||
}
|
||
|
||
PERF_LOG(PE_CLIENT_REQUEST_END,
|
||
AR_INTERNET_READ_FILE,
|
||
dwBytesRead,
|
||
0,
|
||
(!bDeref && hFileMapped) ? ((INTERNET_HANDLE_OBJECT *)hFileMapped)->GetPseudoHandle() : NULL
|
||
);
|
||
|
||
DEBUG_LEAVE(0);
|
||
}
|
||
|
||
|
||
|
||
|
||
DWORD
|
||
CFsm_ReadFile::RunSM(
|
||
IN CFsm * Fsm
|
||
)
|
||
{
|
||
DEBUG_ENTER((DBG_HTTP,
|
||
Dword,
|
||
"CFsm_ReadFile::RunSM",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
DWORD error;
|
||
CFsm_ReadFile * stateMachine = (CFsm_ReadFile *)Fsm;
|
||
|
||
switch (Fsm->GetState()) {
|
||
case FSM_STATE_INIT:
|
||
case FSM_STATE_CONTINUE:
|
||
error = ReadFile_Fsm(stateMachine);
|
||
break;
|
||
|
||
default:
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
break;
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ReadFile_Fsm(
|
||
IN CFsm_ReadFile * Fsm
|
||
)
|
||
{
|
||
DEBUG_ENTER((DBG_INET,
|
||
Dword,
|
||
"ReadFile_Fsm",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
CFsm_ReadFile & fsm = *Fsm;
|
||
DWORD error = fsm.GetError();
|
||
|
||
if ((error == ERROR_SUCCESS) && (fsm.GetState() == FSM_STATE_INIT)) {
|
||
error = HttpReadData(fsm.GetMappedHandle(),
|
||
fsm.m_lpBuffer,
|
||
fsm.m_dwNumberOfBytesToRead,
|
||
&fsm.m_dwBytesRead,
|
||
0
|
||
);
|
||
if (error == ERROR_IO_PENDING) {
|
||
goto quit;
|
||
}
|
||
}
|
||
ReadFile_End(!fsm.GetThreadInfo()->IsAsyncWorkerThread,
|
||
(error == ERROR_SUCCESS) ? TRUE : FALSE,
|
||
fsm.GetMappedHandle(),
|
||
fsm.m_dwBytesRead,
|
||
fsm.m_lpBuffer,
|
||
fsm.m_dwNumberOfBytesToRead,
|
||
fsm.m_lpdwNumberOfBytesRead
|
||
);
|
||
fsm.SetDone();
|
||
|
||
quit:
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
CFsm_ReadFileEx::RunSM(
|
||
IN CFsm * Fsm
|
||
)
|
||
{
|
||
DEBUG_ENTER((DBG_HTTP,
|
||
Dword,
|
||
"CFsm_ReadFileEx::RunSM",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
DWORD error;
|
||
CFsm_ReadFileEx * stateMachine = (CFsm_ReadFileEx *)Fsm;
|
||
|
||
switch (Fsm->GetState()) {
|
||
case FSM_STATE_INIT:
|
||
case FSM_STATE_CONTINUE:
|
||
error = ReadFileEx_Fsm(stateMachine);
|
||
break;
|
||
|
||
default:
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
break;
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ReadFileEx_Fsm(
|
||
IN CFsm_ReadFileEx * Fsm
|
||
)
|
||
{
|
||
DEBUG_ENTER((DBG_INET,
|
||
Dword,
|
||
"ReadFileEx_Fsm",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
CFsm_ReadFileEx & fsm = *Fsm;
|
||
DWORD error = fsm.GetError();
|
||
|
||
if ((error == ERROR_SUCCESS) && (fsm.GetState() == FSM_STATE_INIT)) {
|
||
fsm.m_dwNumberOfBytesToRead = fsm.m_lpBuffersOut->dwBufferLength;
|
||
error = HttpReadData(fsm.GetMappedHandle(),
|
||
fsm.m_lpBuffersOut->lpvBuffer,
|
||
fsm.m_dwNumberOfBytesToRead,
|
||
&fsm.m_dwBytesRead,
|
||
(fsm.m_dwFlags & IRF_NO_WAIT)
|
||
? SF_NO_WAIT
|
||
: 0
|
||
);
|
||
if (error == ERROR_IO_PENDING) {
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// if we are asynchronously completing a no-wait read then we don't update
|
||
// any app parameters - we simply return the indication that we completed.
|
||
// The app will then make another no-wait read to get the data
|
||
//
|
||
|
||
BOOL bNoOutput;
|
||
|
||
bNoOutput = ((fsm.m_dwFlags & IRF_NO_WAIT)
|
||
&& fsm.GetThreadInfo()->IsAsyncWorkerThread)
|
||
? TRUE
|
||
: FALSE;
|
||
|
||
ReadFile_End(!fsm.GetThreadInfo()->IsAsyncWorkerThread,
|
||
(error == ERROR_SUCCESS) ? TRUE : FALSE,
|
||
fsm.GetMappedHandle(),
|
||
bNoOutput ? 0 : fsm.m_dwBytesRead,
|
||
bNoOutput ? NULL : fsm.m_lpBuffersOut->lpvBuffer,
|
||
bNoOutput ? 0 : fsm.m_dwNumberOfBytesToRead,
|
||
bNoOutput ? NULL : &fsm.m_lpBuffersOut->dwBufferLength
|
||
);
|
||
fsm.SetDone();
|
||
|
||
quit:
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
WinHttpWriteData(
|
||
IN HINTERNET hFile,
|
||
IN LPCVOID lpBuffer,
|
||
IN DWORD dwNumberOfBytesToWrite,
|
||
OUT LPDWORD lpdwNumberOfBytesWritten
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function write next block of data to the internet file. Currently it
|
||
supports the following protocol data:
|
||
|
||
HttpWriteFile
|
||
|
||
Arguments:
|
||
|
||
hFile - handle that was obtained by OpenFile Call
|
||
|
||
lpBuffer - pointer to the data buffer
|
||
|
||
dwNumberOfBytesToWrite - number of bytes in the above buffer
|
||
|
||
lpdwNumberOfBytesWritten - pointer to a DWORD where the number of bytes
|
||
of data actually written is returned
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE
|
||
|
||
Failure - FALSE. Call GetLastError() for more info
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"WinHttpWriteData",
|
||
"%#x, %#x, %d, %#x",
|
||
hFile,
|
||
lpBuffer,
|
||
dwNumberOfBytesToWrite,
|
||
lpdwNumberOfBytesWritten
|
||
));
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
DWORD nestingLevel = 0;
|
||
DWORD error;
|
||
BOOL success = FALSE;
|
||
BOOL fNeedDeref = TRUE;
|
||
HINTERNET hFileMapped = NULL;
|
||
|
||
if (!GlobalDataInitialized)
|
||
{
|
||
error = ERROR_WINHTTP_NOT_INITIALIZED;
|
||
goto done;
|
||
}
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo == NULL)
|
||
{
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
goto done;
|
||
}
|
||
|
||
_InternetIncNestingCount();
|
||
nestingLevel = 1;
|
||
|
||
error = MapHandleToAddress(hFile, (LPVOID *)&hFileMapped, FALSE);
|
||
if ((error != ERROR_SUCCESS) && (hFileMapped == NULL))
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// set the handle, and last-error info in the per-thread data block
|
||
// before we go any further. This allows us to return a status in the async
|
||
// case, even if the handle has been closed
|
||
//
|
||
|
||
_InternetSetObjectHandle(lpThreadInfo, hFile, hFileMapped);
|
||
_InternetClearLastError(lpThreadInfo);
|
||
|
||
//
|
||
// if MapHandleToAddress() returned a non-NULL object address, but also an
|
||
// error status, then the handle is being closed - quit
|
||
//
|
||
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
// validate handle and its type
|
||
BOOL isAsync;
|
||
error = RIsHandleLocal(hFileMapped, NULL, &isAsync, TypeHttpRequestHandle);
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
INET_ASSERT(FALSE);
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// validate parameters - write length cannot be 0
|
||
//
|
||
|
||
if (!lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
if (dwNumberOfBytesToWrite != 0)
|
||
{
|
||
error = ProbeReadBuffer((LPVOID)lpBuffer, dwNumberOfBytesToWrite);
|
||
if (error == ERROR_SUCCESS)
|
||
{
|
||
error = ProbeAndSetDword(lpdwNumberOfBytesWritten, 0);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
error = ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
}
|
||
|
||
|
||
// # 62953
|
||
// If the authentication state of the handle is Negotiate,
|
||
// don't submit data to the server but return success.
|
||
// ** Added test for NTLM or Negotiate - Adriaanc.
|
||
//
|
||
|
||
HTTP_REQUEST_HANDLE_OBJECT *pRequest;
|
||
pRequest = (HTTP_REQUEST_HANDLE_OBJECT*) hFileMapped;
|
||
|
||
if (pRequest->GetAuthState() == AUTHSTATE_NEGOTIATE)
|
||
{
|
||
*lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite;
|
||
error = ERROR_SUCCESS;
|
||
success = TRUE;
|
||
goto quit;
|
||
}
|
||
|
||
|
||
INET_ASSERT(error == ERROR_SUCCESS);
|
||
|
||
CFsm_HttpWriteData *pFsm = New CFsm_HttpWriteData((LPVOID)lpBuffer,
|
||
dwNumberOfBytesToWrite,
|
||
lpdwNumberOfBytesWritten,
|
||
0,
|
||
pRequest
|
||
);
|
||
|
||
if (pFsm != NULL)
|
||
{
|
||
HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)hFileMapped;
|
||
if (isAsync && !lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
error = DoAsyncFsm(pFsm, pRequest);
|
||
}
|
||
else
|
||
{
|
||
pFsm->SetPushPop(TRUE);
|
||
pFsm->Push();
|
||
error = DoFsm(pFsm);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Don't Derefrence if we're going pending cause the FSM will do
|
||
// it for us.
|
||
//
|
||
|
||
if ( error == ERROR_IO_PENDING )
|
||
{
|
||
fNeedDeref = FALSE;
|
||
}
|
||
success = (error == ERROR_SUCCESS) ? TRUE : FALSE;
|
||
|
||
quit:
|
||
|
||
if (hFileMapped != NULL && fNeedDeref)
|
||
{
|
||
DereferenceObject((LPVOID)hFileMapped);
|
||
}
|
||
|
||
_InternetDecNestingCount(nestingLevel);;
|
||
|
||
done:
|
||
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
DEBUG_ERROR(API, error);
|
||
SetLastError(error);
|
||
}
|
||
|
||
DEBUG_LEAVE_API(success);
|
||
|
||
return success;
|
||
}
|
||
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
WinHttpQueryDataAvailable(
|
||
IN HINTERNET hFile,
|
||
OUT LPDWORD lpdwNumberOfBytesAvailable
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines the amount of data currently available to be read on the handle
|
||
|
||
Arguments:
|
||
|
||
hFile - handle of internet object
|
||
|
||
lpdwNumberOfBytesAvailable - pointer to returned bytes available
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE
|
||
|
||
Failure - FALSE. Call GetLastError() for more info
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"WinHttpQueryDataAvailable",
|
||
"%#x, %#x, %#x",
|
||
hFile,
|
||
lpdwNumberOfBytesAvailable
|
||
));
|
||
|
||
BOOL success;
|
||
DWORD error;
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = NULL;
|
||
HINTERNET hFileMapped = NULL;
|
||
BOOL bDeref = TRUE;
|
||
|
||
if (!GlobalDataInitialized)
|
||
{
|
||
error = ERROR_WINHTTP_NOT_INITIALIZED;
|
||
bDeref = FALSE;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// get the per-thread info block
|
||
//
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo == NULL)
|
||
{
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
goto quit;
|
||
}
|
||
|
||
//INET_ASSERT(lpThreadInfo->Fsm == NULL);
|
||
|
||
PERF_LOG(PE_CLIENT_REQUEST_START,
|
||
AR_INTERNET_QUERY_DATA_AVAILABLE,
|
||
lpThreadInfo->ThreadId,
|
||
hFile
|
||
);
|
||
|
||
//
|
||
// validate parameters
|
||
//
|
||
|
||
error = MapHandleToAddress(hFile, &hFileMapped, FALSE);
|
||
if ((error != ERROR_SUCCESS) && (hFileMapped == NULL))
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
INET_ASSERT(hFileMapped);
|
||
|
||
//
|
||
// set the handle values in the per-thread info block (this API
|
||
// can't return extended error info, so we don't care about it)
|
||
//
|
||
|
||
_InternetSetObjectHandle(lpThreadInfo, hFile, hFileMapped);
|
||
|
||
//
|
||
// if the handle is invalid, quit now
|
||
//
|
||
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// validate rest of parameters
|
||
//
|
||
|
||
error = ProbeAndSetDword(lpdwNumberOfBytesAvailable, 0);
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
BOOL isAsync;
|
||
error = RIsHandleLocal(hFileMapped, NULL, &isAsync, TypeHttpRequestHandle);
|
||
if (error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// since the async worker thread doesn't come back through this API, the
|
||
// following test is sufficient. Note that we only go async if there is
|
||
// no data currently available on the handle
|
||
//
|
||
|
||
BOOL dataAvailable;
|
||
dataAvailable = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->IsDataAvailable();
|
||
|
||
BOOL eof;
|
||
eof = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->IsEndOfFile();
|
||
|
||
if (dataAvailable || eof)
|
||
{
|
||
|
||
DWORD available;
|
||
|
||
available = ((INTERNET_HANDLE_OBJECT *)hFileMapped)->AvailableDataLength();
|
||
|
||
DEBUG_PRINT(API,
|
||
INFO,
|
||
("%d bytes are immediately available\n",
|
||
available
|
||
));
|
||
|
||
*lpdwNumberOfBytesAvailable = available;
|
||
success = TRUE;
|
||
goto finish;
|
||
}
|
||
|
||
INET_ASSERT(hFileMapped);
|
||
|
||
//
|
||
// sync path. wInternetQueryDataAvailable will set the last error code
|
||
// if it fails
|
||
//
|
||
|
||
CFsm_QueryAvailable *pFsm;
|
||
|
||
pFsm = New CFsm_QueryAvailable(lpdwNumberOfBytesAvailable,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if (pFsm != NULL)
|
||
{
|
||
HTTP_REQUEST_HANDLE_OBJECT *pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)hFileMapped;
|
||
if (isAsync && !lpThreadInfo->IsAsyncWorkerThread)
|
||
{
|
||
error = DoAsyncFsm(pFsm, pRequest);
|
||
}
|
||
else
|
||
{
|
||
pFsm->SetPushPop(TRUE);
|
||
pFsm->Push();
|
||
error = DoFsm(pFsm);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
error = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
if (error == ERROR_SUCCESS)
|
||
{
|
||
success = TRUE;
|
||
}
|
||
else
|
||
{
|
||
if (error == ERROR_IO_PENDING)
|
||
{
|
||
bDeref = FALSE;
|
||
}
|
||
goto quit;
|
||
}
|
||
|
||
finish:
|
||
|
||
DEBUG_PRINT_API(API,
|
||
INFO,
|
||
("*lpdwNumberOfBytesAvailable (%#x) = %d\n",
|
||
lpdwNumberOfBytesAvailable,
|
||
*lpdwNumberOfBytesAvailable
|
||
));
|
||
|
||
if (bDeref && (hFileMapped != NULL))
|
||
{
|
||
DereferenceObject((LPVOID)hFileMapped);
|
||
}
|
||
|
||
if (lpThreadInfo)
|
||
{
|
||
|
||
PERF_LOG(PE_CLIENT_REQUEST_END,
|
||
AR_INTERNET_QUERY_DATA_AVAILABLE,
|
||
*lpdwNumberOfBytesAvailable,
|
||
lpThreadInfo->ThreadId,
|
||
hFile
|
||
);
|
||
|
||
}
|
||
|
||
DEBUG_LEAVE_API(success);
|
||
return success;
|
||
|
||
quit:
|
||
|
||
DEBUG_ERROR(API, error);
|
||
|
||
SetLastError(error);
|
||
success = FALSE;
|
||
|
||
goto finish;
|
||
}
|
||
|
||
|
||
DWORD
|
||
CFsm_QueryAvailable::RunSM(
|
||
IN CFsm * Fsm
|
||
)
|
||
{
|
||
DEBUG_ENTER((DBG_HTTP,
|
||
Dword,
|
||
"CFsm_QueryAvailable::RunSM",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
DWORD error;
|
||
CFsm_QueryAvailable * stateMachine = (CFsm_QueryAvailable *)Fsm;
|
||
|
||
switch (Fsm->GetState()) {
|
||
case FSM_STATE_INIT:
|
||
case FSM_STATE_CONTINUE:
|
||
error = QueryAvailable_Fsm(stateMachine);
|
||
break;
|
||
|
||
default:
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
break;
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
QueryAvailable_Fsm(
|
||
IN CFsm_QueryAvailable * Fsm
|
||
)
|
||
{
|
||
DEBUG_ENTER((DBG_INET,
|
||
Dword,
|
||
"QueryAvailable_Fsm",
|
||
"%#x",
|
||
Fsm
|
||
));
|
||
|
||
CFsm_QueryAvailable & fsm = *Fsm;
|
||
DWORD error = fsm.GetError();
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
goto quit;
|
||
}
|
||
|
||
HTTP_REQUEST_HANDLE_OBJECT * pRequest;
|
||
|
||
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)fsm.GetMappedHandle();
|
||
|
||
if (fsm.GetState() == FSM_STATE_INIT) {
|
||
error = pRequest->QueryDataAvailable(fsm.m_lpdwNumberOfBytesAvailable);
|
||
}
|
||
if (error == ERROR_SUCCESS) {
|
||
pRequest->SetAvailableDataLength(*fsm.m_lpdwNumberOfBytesAvailable);
|
||
|
||
DEBUG_PRINT(INET,
|
||
INFO,
|
||
("%d bytes available\n",
|
||
*fsm.m_lpdwNumberOfBytesAvailable
|
||
));
|
||
|
||
fsm.SetApiData(*fsm.m_lpdwNumberOfBytesAvailable);
|
||
}
|
||
|
||
quit:
|
||
|
||
if (error != ERROR_IO_PENDING) {
|
||
fsm.SetDone();
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
|
||
INTERNETAPI
|
||
BOOL
|
||
WINAPI
|
||
InternetGetLastResponseInfoA(
|
||
OUT LPDWORD lpdwErrorCategory,
|
||
IN LPSTR lpszBuffer OPTIONAL,
|
||
IN OUT LPDWORD lpdwBufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the per-thread last internet error description text
|
||
or server response.
|
||
|
||
If this function is successful, *lpdwBufferLength contains the string length
|
||
of lpszBuffer.
|
||
|
||
If this function returns a failure indication, *lpdwBufferLength contains
|
||
the number of BYTEs required to hold the response text
|
||
|
||
Arguments:
|
||
|
||
lpdwErrorCategory - pointer to DWORD location where the error catagory is
|
||
returned
|
||
|
||
lpszBuffer - pointer to buffer where the error text is returned
|
||
|
||
lpdwBufferLength - IN: length of lpszBuffer
|
||
OUT: number of characters in lpszBuffer if successful
|
||
else size of buffer required to hold response text
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE
|
||
lpszBuffer contains the error text. The caller must check
|
||
*lpdwBufferLength: if 0 then there was no text to return
|
||
|
||
Failure - FALSE
|
||
Call GetLastError() for more information
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER_API((DBG_API,
|
||
Bool,
|
||
"InternetGetLastResponseInfoA",
|
||
"%#x, %#x, %#x [%d]",
|
||
lpdwErrorCategory,
|
||
lpszBuffer,
|
||
lpdwBufferLength,
|
||
lpdwBufferLength ? *lpdwBufferLength : 0
|
||
));
|
||
|
||
DWORD error;
|
||
BOOL success;
|
||
DWORD textLength;
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
//
|
||
// validate parameters
|
||
//
|
||
|
||
if (IsBadWritePtr(lpdwErrorCategory, sizeof(*lpdwErrorCategory))
|
||
|| IsBadWritePtr(lpdwBufferLength, sizeof(*lpdwBufferLength))
|
||
|| (ARGUMENT_PRESENT(lpszBuffer)
|
||
? IsBadWritePtr(lpszBuffer, *lpdwBufferLength)
|
||
: FALSE)) {
|
||
error = ERROR_INVALID_PARAMETER;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// if the buffer pointer is NULL then its the same as a zero-length buffer
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(lpszBuffer)) {
|
||
*lpdwBufferLength = 0;
|
||
} else if (*lpdwBufferLength != 0) {
|
||
*lpszBuffer = '\0';
|
||
}
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo == NULL) {
|
||
|
||
DEBUG_PRINT(INET,
|
||
ERROR,
|
||
("failed to get INTERNET_THREAD_INFO\n"
|
||
));
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// there may not be any error text for this thread - either no server
|
||
// error/response has been received, or the error text has been cleared by
|
||
// an intervening API
|
||
//
|
||
|
||
if (lpThreadInfo->hErrorText != NULL) {
|
||
|
||
//
|
||
// copy as much as we can fit in the user supplied buffer
|
||
//
|
||
|
||
textLength = lpThreadInfo->ErrorTextLength;
|
||
if (*lpdwBufferLength) {
|
||
|
||
LPBYTE errorText;
|
||
|
||
errorText = (LPBYTE)LOCK_MEMORY(lpThreadInfo->hErrorText);
|
||
if (errorText != NULL) {
|
||
textLength = min(textLength, *lpdwBufferLength) - 1;
|
||
memcpy(lpszBuffer, errorText, textLength);
|
||
|
||
//
|
||
// the error text should always be zero terminated, so the
|
||
// calling app can treat it as a string
|
||
//
|
||
|
||
lpszBuffer[textLength] = '\0';
|
||
|
||
UNLOCK_MEMORY(lpThreadInfo->hErrorText);
|
||
|
||
if (textLength == lpThreadInfo->ErrorTextLength - 1) {
|
||
error = ERROR_SUCCESS;
|
||
} else {
|
||
|
||
//
|
||
// returned length is amount of buffer required
|
||
//
|
||
|
||
textLength = lpThreadInfo->ErrorTextLength;
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PRINT(INET,
|
||
ERROR,
|
||
("failed to lock hErrorText (%#x): %d\n",
|
||
lpThreadInfo->hErrorText,
|
||
GetLastError()
|
||
));
|
||
|
||
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// user's buffer is not large enough to hold the info. We'll
|
||
// let them know the required length
|
||
//
|
||
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
} else {
|
||
|
||
INET_ASSERT(lpThreadInfo->ErrorTextLength == 0);
|
||
|
||
textLength = 0;
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
|
||
*lpdwErrorCategory = lpThreadInfo->ErrorNumber;
|
||
*lpdwBufferLength = textLength;
|
||
|
||
IF_DEBUG(ANY) {
|
||
if ((error == ERROR_SUCCESS)
|
||
|| ((textLength != 0) && (lpszBuffer != NULL))) {
|
||
|
||
DEBUG_DUMP_API(API,
|
||
"Last Response Info:\n",
|
||
lpszBuffer,
|
||
textLength
|
||
);
|
||
|
||
}
|
||
}
|
||
|
||
quit:
|
||
success = (error == ERROR_SUCCESS);
|
||
if (!success) {
|
||
DEBUG_ERROR(API, error);
|
||
SetLastError(error);
|
||
}
|
||
|
||
DEBUG_LEAVE_API(success);
|
||
|
||
return success;
|
||
}
|
||
|
||
|
||
|
||
|