2004 lines
49 KiB
C++
2004 lines
49 KiB
C++
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
thrdinfo.cxx
|
||
|
||
Abstract:
|
||
|
||
Functions to manipulate an INTERNET_THREAD_INFO
|
||
|
||
Contents:
|
||
InternetCreateThreadInfo
|
||
InternetDestroyThreadInfo
|
||
InternetTerminateThreadInfo
|
||
InternetGetThreadInfo
|
||
InternetSetThreadInfo
|
||
InternetIndicateStatusAddress
|
||
InternetIndicateStatusString
|
||
InternetIndicateStatusNewHandle
|
||
InternetIndicateStatus
|
||
InternetSetLastError
|
||
_InternetSetLastError
|
||
InternetLockErrorText
|
||
InternetUnlockErrorText
|
||
InternetSetContext
|
||
InternetSetObjectHandle
|
||
InternetGetObjectHandle
|
||
InternetFreeThreadInfo
|
||
|
||
Author:
|
||
|
||
Richard L Firth (rfirth) 16-Feb-1995
|
||
|
||
Environment:
|
||
|
||
Win32 user-level DLL
|
||
|
||
Revision History:
|
||
|
||
16-Feb-1995 rfirth
|
||
Created
|
||
|
||
--*/
|
||
|
||
#include <wininetp.h>
|
||
#include <perfdiag.hxx>
|
||
|
||
//
|
||
// manifests
|
||
//
|
||
|
||
#define BAD_TLS_INDEX 0xffffffff // according to online win32 SDK documentation
|
||
#ifdef SPX_SUPPORT
|
||
#define GENERIC_SPX_NAME "SPX Server"
|
||
#endif //SPX_SUPPORT
|
||
//
|
||
// macros
|
||
//
|
||
|
||
#ifdef ENABLE_DEBUG
|
||
|
||
#define InitializeInternetThreadInfo(lpThreadInfo) \
|
||
InitializeListHead(&lpThreadInfo->List); \
|
||
lpThreadInfo->Signature = INTERNET_THREAD_INFO_SIGNATURE; \
|
||
lpThreadInfo->ThreadId = GetCurrentThreadId();
|
||
|
||
#else
|
||
|
||
#define InitializeInternetThreadInfo(threadInfo) \
|
||
InitializeListHead(&lpThreadInfo->List); \
|
||
lpThreadInfo->ThreadId = GetCurrentThreadId();
|
||
|
||
#endif // ENABLE_DEBUG
|
||
|
||
//
|
||
// private data
|
||
//
|
||
|
||
PRIVATE DWORD InternetTlsIndex = BAD_TLS_INDEX;
|
||
PRIVATE SERIALIZED_LIST ThreadInfoList;
|
||
|
||
|
||
|
||
INTERNETAPI_(BOOL) ResumeSuspendedDownload(
|
||
IN HINTERNET hRequest,
|
||
IN DWORD dwResultCode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attempts to restart a stalled FSM that is blocked on UI interaction,
|
||
|
||
Arguments:
|
||
|
||
hRequest - handle to open HTTP request
|
||
|
||
dwResultCode - the result of InternetErrorDlg, passed back into Wininet via this API
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
ERROR_IO_PENDING
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
HINTERNET hRequestMapped = NULL;
|
||
BOOL fResumed = FALSE;
|
||
DWORD error;
|
||
|
||
//
|
||
// map the handle
|
||
//
|
||
|
||
error = MapHandleToAddress(hRequest, (LPVOID *)&hRequestMapped, FALSE);
|
||
if ((error != ERROR_SUCCESS) && (hRequestMapped == NULL)) {
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// Call internal to do the work
|
||
//
|
||
|
||
error = ResumeAfterUserInput(
|
||
hRequestMapped,
|
||
dwResultCode,
|
||
&fResumed
|
||
);
|
||
|
||
if ( error != ERROR_SUCCESS)
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
if ( error == ERROR_SUCCESS )
|
||
{
|
||
error = ERROR_IO_PENDING; // remap to pending
|
||
}
|
||
|
||
|
||
//
|
||
// If we failed to resume, the handle must have been canceled
|
||
//
|
||
|
||
if (!fResumed)
|
||
{
|
||
error = ERROR_INTERNET_OPERATION_CANCELLED;
|
||
goto quit;
|
||
}
|
||
|
||
quit:
|
||
|
||
if (hRequestMapped != NULL) {
|
||
DereferenceObject((LPVOID)hRequestMapped);
|
||
}
|
||
|
||
SetLastError(error);
|
||
|
||
return (error == ERROR_SUCCESS) ? TRUE : FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
|
||
DWORD
|
||
ResumeAfterUserInput(
|
||
IN HINTERNET hRequestMapped,
|
||
IN DWORD dwResultCode,
|
||
OUT LPBOOL pfItemResumed
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Internal version of Resume API that
|
||
unblocks an FSM after the UI has been shown, user prompted, and the FSM should now continue.
|
||
|
||
Arguments:
|
||
|
||
hRequestMapped -
|
||
dwResultCode -
|
||
pfItemResumed -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
{
|
||
HTTP_REQUEST_HANDLE_OBJECT *pRequest;
|
||
INTERNET_CONNECT_HANDLE_OBJECT * pConnect;
|
||
INTERNET_HANDLE_OBJECT * pInternet;
|
||
DWORD error = ERROR_SUCCESS;
|
||
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
Dword,
|
||
"ResumeAfterUserInput",
|
||
"%x, %u, %x",
|
||
hRequestMapped,
|
||
dwResultCode,
|
||
pfItemResumed
|
||
));
|
||
|
||
*pfItemResumed = FALSE;
|
||
|
||
//
|
||
// Now round up the objects that are involved
|
||
//
|
||
|
||
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hRequestMapped;
|
||
|
||
pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)pRequest->GetParent();
|
||
|
||
INET_ASSERT(pConnect != NULL);
|
||
INET_ASSERT(pConnect->IsValid(TypeHttpConnectHandle) == ERROR_SUCCESS);
|
||
|
||
pInternet = (INTERNET_HANDLE_OBJECT *)pConnect->GetParent();
|
||
|
||
INET_ASSERT(pInternet != NULL);
|
||
INET_ASSERT(pInternet->IsValid(TypeInternetHandle) == ERROR_SUCCESS);
|
||
|
||
//
|
||
// Lock us while we attempt to unblock the FSM
|
||
//
|
||
|
||
pInternet->LockPopupInfo();
|
||
|
||
//
|
||
// Can only resume if we're blocked on both request and internet handles
|
||
//
|
||
|
||
if ( pInternet->IsBlockedOnUserInput() &&
|
||
pRequest->IsBlockedOnUserInput() )
|
||
{
|
||
DWORD dwCntUnBlocked;
|
||
|
||
INET_ASSERT(pInternet->GetBlockId() == pRequest->GetBlockId());
|
||
|
||
dwCntUnBlocked = UnblockWorkItems(
|
||
pInternet->GetBlockedUiCount(),
|
||
pRequest->GetBlockId(), // blocked on FSM
|
||
ERROR_SUCCESS,
|
||
TP_NO_PRIORITY_CHANGE
|
||
);
|
||
|
||
if ( dwCntUnBlocked > 0 )
|
||
{
|
||
*pfItemResumed = TRUE;
|
||
pInternet->SetBlockedResultCode(dwResultCode);
|
||
}
|
||
|
||
}
|
||
|
||
INET_ASSERT( ! (pRequest->IsBlockedOnUserInput() && !pInternet->IsBlockedOnUserInput()) );
|
||
|
||
//quit: -- not used
|
||
|
||
pInternet->UnlockPopupInfo();
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
|
||
|
||
DWORD
|
||
ChangeUIBlockingState(
|
||
IN HINTERNET hRequestMapped,
|
||
IN DWORD dwError,
|
||
OUT LPDWORD lpdwActionTaken,
|
||
OUT LPDWORD lpdwResultCode,
|
||
IN OUT LPVOID * lplpResultData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attempts to determine the best way of putting up UI for a given error. This allows
|
||
us to back out of the Asyncronous FSM thread while popping up UI.
|
||
|
||
How it works:
|
||
We attempt to prevent more than one dialog per InternetOpen handle
|
||
|
||
Arguments:
|
||
|
||
hRequestHandle - mapped handle to open request
|
||
|
||
dwError - error code to pass back to client and ultimately to InternetErrorDlg to generate UI
|
||
|
||
lpdwActionTaken - returns one of several values, used to tell caller what UI action has been taken
|
||
UI_ACTION_CODE_NONE_TAKEN 0
|
||
UI_ACTION_CODE_BLOCKED_FOR_INTERNET_HANDLE 1
|
||
UI_ACTION_CODE_BLOCKED_FOR_USER_INPUT 2
|
||
UI_ACTION_CODE_USER_ACTION_COMPLETED 3
|
||
|
||
lpdwResultCode - returns the result of InternetErrorDlg, passed through ResumeSuspendedDownload
|
||
|
||
lplpResultData - a void pointer allocated and owned by callee until after a thread has been resumed
|
||
used to pass extra data through client to InternetErrorDlg
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
ERROR_IO_PENDING
|
||
|
||
Failure -
|
||
|
||
--*/
|
||
|
||
{
|
||
HTTP_REQUEST_HANDLE_OBJECT *pRequest;
|
||
INTERNET_CONNECT_HANDLE_OBJECT * pConnect;
|
||
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
Dword,
|
||
"ChangeUIBlockingState",
|
||
"%x, %u, %x, %x [%x]",
|
||
hRequestMapped,
|
||
dwError,
|
||
lpdwActionTaken,
|
||
lpdwResultCode,
|
||
lplpResultData,
|
||
(lplpResultData ? *lplpResultData : NULL )
|
||
));
|
||
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
||
DWORD error = ERROR_SUCCESS;
|
||
DWORD_PTR dwBlockId;
|
||
BOOL fLocked = FALSE;
|
||
BOOL fDoAsyncCallback = FALSE; // TRUE if we need to callback to the app
|
||
LPVOID lpResultData = NULL;
|
||
|
||
|
||
*lpdwActionTaken = UI_ACTION_CODE_NONE_TAKEN;
|
||
|
||
if ( lplpResultData ) {
|
||
lpResultData = *lplpResultData; // save off
|
||
}
|
||
|
||
//
|
||
// Gather various sundry elements, objects, thread info, etc,
|
||
// validate, and if proper continue with the process
|
||
//
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
|
||
if ( lpThreadInfo->Fsm == NULL )
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
if ( ! lpThreadInfo->IsAsyncWorkerThread )
|
||
{
|
||
goto quit;
|
||
}
|
||
|
||
INET_ASSERT(lpThreadInfo->hObject != NULL);
|
||
INET_ASSERT(lpThreadInfo->hObjectMapped != NULL);
|
||
|
||
if ( hRequestMapped == NULL )
|
||
{
|
||
hRequestMapped = lpThreadInfo->Fsm->GetMappedHandle();
|
||
}
|
||
|
||
INET_ASSERT(hRequestMapped == lpThreadInfo->Fsm->GetMappedHandle());
|
||
|
||
//
|
||
// if the context value in the thread info block is 0 then we use the
|
||
// context from the handle object
|
||
//
|
||
|
||
DWORD_PTR context;
|
||
|
||
context = _InternetGetContext(lpThreadInfo);
|
||
if (context == INTERNET_NO_CALLBACK) {
|
||
context = ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetContext();
|
||
}
|
||
|
||
INTERNET_STATUS_CALLBACK appCallback;
|
||
|
||
appCallback = ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetStatusCallback();
|
||
|
||
//
|
||
// No callback, no FSM means no special UI callback can be done.
|
||
//
|
||
|
||
if ((appCallback == NULL) || (context == INTERNET_NO_CALLBACK))
|
||
{
|
||
//
|
||
// For the sync return error
|
||
//
|
||
|
||
error = dwError;
|
||
goto quit;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
INET_ASSERT(FALSE);
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// Make sure our handle isn't invalidated before proceeding
|
||
//
|
||
if (((HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->IsInvalidated())
|
||
{
|
||
error = ERROR_INTERNET_OPERATION_CANCELLED;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// Now get the objects that are involved
|
||
//
|
||
|
||
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *) hRequestMapped;
|
||
|
||
pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *)pRequest->GetParent();
|
||
|
||
INET_ASSERT(pConnect != NULL);
|
||
INET_ASSERT(pConnect->IsValid(TypeHttpConnectHandle) == ERROR_SUCCESS);
|
||
|
||
INTERNET_HANDLE_OBJECT * pInternet;
|
||
|
||
pInternet = (INTERNET_HANDLE_OBJECT *)pConnect->GetParent();
|
||
|
||
INET_ASSERT(pInternet != NULL);
|
||
INET_ASSERT(pInternet->IsValid(TypeInternetHandle) == ERROR_SUCCESS);
|
||
|
||
pInternet->LockPopupInfo();
|
||
fLocked = TRUE;
|
||
|
||
//
|
||
// We check whether we're blocked on the HTTP handle and the
|
||
// Internet handle. Basically we have a little matrix here
|
||
// based on which one is blocked (or if neither is blocked)
|
||
//
|
||
// So:
|
||
// InternetHandle(blocked)/RequestHandle(not-blocked) -
|
||
// indicates we are not the request blocking the UI,
|
||
// we will need to block the FSM until the other dialog
|
||
// has completed
|
||
//
|
||
// InternetHandle(blocked)/RequestHandle(blocked) -
|
||
// indicates that we have blocked and have now been
|
||
// woken up, we need return the result of the UI
|
||
// to the caller.
|
||
//
|
||
// InternetHandle(not-blocked)/RequestHandle(blocked) -
|
||
// ASSERT, we shouldn't have this happen
|
||
//
|
||
// InternetHandle(not-blocked)/RequestHandle(not-blocked) -
|
||
// We've just entered with no-blocking handles,
|
||
// so we block both and wait till UI has completed
|
||
//
|
||
|
||
if (!pInternet->IsBlockedOnUserInput())
|
||
{
|
||
//
|
||
// Indicate to the caller via callback
|
||
// that we need to generate UI, then block
|
||
//
|
||
|
||
DEBUG_PRINT(THRDINFO,
|
||
INFO,
|
||
("Blocking on UI, notifing client via callback\n"
|
||
));
|
||
|
||
fDoAsyncCallback = TRUE;
|
||
|
||
INET_ASSERT(!pRequest->IsBlockedOnUserInput());
|
||
|
||
dwBlockId = (DWORD_PTR) lpThreadInfo->Fsm;
|
||
pInternet->BlockOnUserInput(dwError, dwBlockId, lpResultData); // IncrementBlockedUiCount() is implied here.
|
||
pRequest->BlockOnUserInput(dwBlockId);
|
||
*lpdwActionTaken = UI_ACTION_CODE_BLOCKED_FOR_USER_INPUT;
|
||
}
|
||
else if (pRequest->IsBlockedOnUserInput() )
|
||
{
|
||
|
||
DEBUG_PRINT(THRDINFO,
|
||
INFO,
|
||
("UnBlocking on UI, returning UI result\n"
|
||
));
|
||
|
||
INET_ASSERT(pInternet->IsBlockedOnUserInput());
|
||
|
||
//
|
||
// Retrieve the result of the UI and return
|
||
// to caller.
|
||
//
|
||
|
||
// save off block id in case need it later
|
||
DWORD_PTR dwSavedBlockedId = pRequest->GetBlockId();
|
||
|
||
INET_ASSERT(pInternet->GetBlockId() == pRequest->GetBlockId());
|
||
|
||
pInternet->UnBlockOnUserInput(lpdwResultCode, &lpResultData);
|
||
pRequest->UnBlockOnUserInput();
|
||
|
||
if ( lplpResultData ) {
|
||
*lplpResultData = lpResultData;
|
||
}
|
||
|
||
*lpdwActionTaken = UI_ACTION_CODE_USER_ACTION_COMPLETED;
|
||
|
||
//
|
||
// If others are still blocked, then wake them up too
|
||
//
|
||
|
||
if (pInternet->IsBlockedOnUserInput())
|
||
{
|
||
DWORD dwCntUnBlocked;
|
||
|
||
dwCntUnBlocked = UnblockWorkItems(
|
||
pInternet->GetBlockedUiCount(),
|
||
dwSavedBlockedId,
|
||
ERROR_SUCCESS,
|
||
TP_NO_PRIORITY_CHANGE
|
||
);
|
||
|
||
DEBUG_PRINT(THRDINFO,
|
||
INFO,
|
||
("Unblocked %u work items, expected %u\n",
|
||
dwCntUnBlocked,
|
||
pInternet->GetBlockedUiCount()
|
||
));
|
||
|
||
pInternet->ClearBlockedUiCount();
|
||
}
|
||
|
||
goto quit;
|
||
}
|
||
else
|
||
{
|
||
DEBUG_PRINT(THRDINFO,
|
||
INFO,
|
||
("Blocking on another FSM (that is busy doing UI) until their completeion\n"
|
||
));
|
||
|
||
INET_ASSERT(pInternet->IsBlockedOnUserInput());
|
||
INET_ASSERT(!pRequest->IsBlockedOnUserInput());
|
||
|
||
//
|
||
// not blocked on the request handle
|
||
// but blocked on internet handle, so we need
|
||
// to wait until this internet handle is ours
|
||
// so we do nothing
|
||
//
|
||
|
||
pInternet->IncrementBlockedUiCount();
|
||
|
||
dwBlockId = pInternet->GetBlockId();
|
||
*lpdwActionTaken = UI_ACTION_CODE_BLOCKED_FOR_INTERNET_HANDLE;
|
||
}
|
||
|
||
//
|
||
// Now do the actual blocking of the FSM here.
|
||
//
|
||
|
||
lpThreadInfo->Fsm->SetState(FSM_STATE_CONTINUE);
|
||
lpThreadInfo->Fsm->SetNextState(FSM_STATE_CONTINUE);
|
||
|
||
error = BlockWorkItem(
|
||
lpThreadInfo->Fsm,
|
||
dwBlockId, // block the FSM on FSM that created this
|
||
INFINITE // we block foreever
|
||
);
|
||
|
||
if ( error == ERROR_SUCCESS )
|
||
{
|
||
error = ERROR_IO_PENDING;
|
||
//goto quit;
|
||
}
|
||
|
||
quit:
|
||
|
||
if (fLocked)
|
||
{
|
||
INET_ASSERT(hRequestMapped);
|
||
|
||
pInternet->UnlockPopupInfo();
|
||
}
|
||
|
||
if (fDoAsyncCallback)
|
||
{
|
||
INTERNET_ASYNC_RESULT asyncResult;
|
||
|
||
//
|
||
// Pass the result Data pointer back to the caller,
|
||
// this is needed to pass extra info to InternetErrorDlg,
|
||
// once this pointer is passed, the caller must not free,
|
||
// until his/her FSM is restarted.
|
||
//
|
||
|
||
INET_ASSERT(*lpdwActionTaken == UI_ACTION_CODE_BLOCKED_FOR_USER_INPUT);
|
||
|
||
asyncResult.dwResult = (DWORD_PTR)lpResultData;
|
||
asyncResult.dwError = dwError;
|
||
|
||
SetLastError(dwError);
|
||
|
||
error = InternetIndicateStatus(
|
||
INTERNET_STATUS_USER_INPUT_REQUIRED,
|
||
(LPVOID)&asyncResult,
|
||
sizeof(asyncResult)
|
||
);
|
||
|
||
if ( error == ERROR_SUCCESS || error == ERROR_INTERNET_OPERATION_CANCELLED )
|
||
{
|
||
error = ERROR_IO_PENDING;
|
||
}
|
||
else
|
||
{
|
||
INET_ASSERT(FALSE);
|
||
}
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
LPINTERNET_THREAD_INFO
|
||
InternetCreateThreadInfo(
|
||
IN BOOL SetTls
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates, initializes an INTERNET_THREAD_INFO. Optionally (allocates and)
|
||
sets this thread's Internet TLS
|
||
|
||
Assumes: 1. The first time this function is called is in the context of the
|
||
process attach library call, so we allocate the TLS index once
|
||
|
||
Arguments:
|
||
|
||
SetTls - TRUE if we are to set the INTERNET_THREAD_INFO TLS for this thread
|
||
|
||
Return Value:
|
||
|
||
LPINTERNET_THREAD_INFO
|
||
Success - pointer to allocated INTERNET_THREAD_INFO structure which has
|
||
been set as this threads value in its InternetTlsIndex slot
|
||
|
||
Failure - NULL
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = NULL;
|
||
BOOL ok = FALSE;
|
||
|
||
if (InDllCleanup) {
|
||
goto quit;
|
||
}
|
||
if (InternetTlsIndex == BAD_TLS_INDEX) {
|
||
|
||
//
|
||
// first time through, initialize serialized list
|
||
//
|
||
|
||
InitializeSerializedList(&ThreadInfoList);
|
||
|
||
//
|
||
// we assume that if we are allocating the TLS index, then this is the
|
||
// one and only thread in this process that can call into this DLL
|
||
// right now - i.e. this thread is loading the DLL
|
||
//
|
||
|
||
InternetTlsIndex = TlsAlloc();
|
||
}
|
||
if (InternetTlsIndex != BAD_TLS_INDEX) {
|
||
lpThreadInfo = NEW(INTERNET_THREAD_INFO);
|
||
if (lpThreadInfo != NULL) {
|
||
InitializeInternetThreadInfo(lpThreadInfo);
|
||
if (SetTls) {
|
||
ok = TlsSetValue(InternetTlsIndex, (LPVOID)lpThreadInfo);
|
||
if (!ok) {
|
||
|
||
DEBUG_PUT(("InternetCreateThreadInfo(): TlsSetValue(%d, %#x) returns %d\n",
|
||
InternetTlsIndex,
|
||
lpThreadInfo,
|
||
GetLastError()
|
||
));
|
||
|
||
DEBUG_BREAK(THRDINFO);
|
||
|
||
}
|
||
} else {
|
||
ok = TRUE;
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PUT(("InternetCreateThreadInfo(): NEW(INTERNET_THREAD_INFO) returned NULL\n"));
|
||
|
||
DEBUG_BREAK(THRDINFO);
|
||
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PUT(("InternetCreateThreadInfo(): TlsAlloc() returns %#x, error %d\n",
|
||
BAD_TLS_INDEX,
|
||
GetLastError()
|
||
));
|
||
|
||
DEBUG_BREAK(THRDINFO);
|
||
}
|
||
if (ok) {
|
||
InsertAtHeadOfSerializedList(&ThreadInfoList, &lpThreadInfo->List);
|
||
} else {
|
||
if (lpThreadInfo != NULL) {
|
||
DEL(lpThreadInfo);
|
||
lpThreadInfo = NULL;
|
||
}
|
||
if (InternetTlsIndex != BAD_TLS_INDEX) {
|
||
TlsFree(InternetTlsIndex);
|
||
InternetTlsIndex = BAD_TLS_INDEX;
|
||
}
|
||
}
|
||
|
||
quit:
|
||
|
||
return lpThreadInfo;
|
||
}
|
||
|
||
|
||
VOID
|
||
InternetDestroyThreadInfo(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cleans up the INTERNET_THREAD_INFO - deletes any memory it owns and deletes
|
||
it
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
IF_DEBUG(NOTHING) {
|
||
DEBUG_PUT(("InternetDestroyThreadInfo(): Thread %#x: Deleting INTERNET_THREAD_INFO\n",
|
||
GetCurrentThreadId()
|
||
));
|
||
}
|
||
|
||
//
|
||
// don't call InternetGetThreadInfo() - we don't need to create the
|
||
// INTERNET_THREAD_INFO if it doesn't exist in this case
|
||
//
|
||
|
||
lpThreadInfo = (LPINTERNET_THREAD_INFO)TlsGetValue(InternetTlsIndex);
|
||
if (lpThreadInfo != NULL) {
|
||
|
||
#if INET_DEBUG
|
||
|
||
//
|
||
// there shouldn't be anything in the debug record stack. On Win95, we
|
||
// ignore this check if this is the async scheduler (nee worker) thread
|
||
// AND there are entries in the debug record stack. The async thread
|
||
// gets killed off before it has chance to DEBUG_LEAVE, then comes here,
|
||
// causing this assert to be over-active
|
||
//
|
||
|
||
if (IsPlatformWin95() && lpThreadInfo->IsAsyncWorkerThread) {
|
||
if (lpThreadInfo->CallDepth != 0) {
|
||
|
||
DEBUG_PUT(("InternetDestroyThreadInfo(): "
|
||
"Thread %#x: "
|
||
"%d records in debug stack\n",
|
||
lpThreadInfo->CallDepth
|
||
));
|
||
}
|
||
} else {
|
||
|
||
INET_ASSERT(lpThreadInfo->Stack == NULL);
|
||
|
||
}
|
||
|
||
#endif // INET_DEBUG
|
||
|
||
InternetFreeThreadInfo(lpThreadInfo);
|
||
|
||
INET_ASSERT(InternetTlsIndex != BAD_TLS_INDEX);
|
||
|
||
TlsSetValue(InternetTlsIndex, NULL);
|
||
} else {
|
||
|
||
DEBUG_PUT(("InternetDestroyThreadInfo(): Thread %#x: no INTERNET_THREAD_INFO\n",
|
||
GetCurrentThreadId()
|
||
));
|
||
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
InternetFreeThreadInfo(
|
||
IN LPINTERNET_THREAD_INFO lpThreadInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes the INTERNET_THREAD_INFO from the list and frees all allocated
|
||
blocks
|
||
|
||
Arguments:
|
||
|
||
lpThreadInfo - pointer to INTERNET_THREAD_INFO to remove and free
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
RemoveFromSerializedList(&ThreadInfoList, &lpThreadInfo->List);
|
||
|
||
if (lpThreadInfo->hErrorText != NULL) {
|
||
FREE_MEMORY(lpThreadInfo->hErrorText);
|
||
}
|
||
|
||
//if (lpThreadInfo->lpResolverInfo != NULL) {
|
||
// if (lpThreadInfo->lpResolverInfo->DnrSocketHandle != NULL) {
|
||
// lpThreadInfo->lpResolverInfo->DnrSocketHandle->Dereference();
|
||
// }
|
||
// DEL(lpThreadInfo->lpResolverInfo);
|
||
//}
|
||
|
||
DEL(lpThreadInfo);
|
||
}
|
||
|
||
|
||
VOID
|
||
InternetTerminateThreadInfo(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Destroy all INTERNET_THREAD_INFO structures and terminate the serialized
|
||
list. This funciton called at process detach time.
|
||
|
||
At DLL_PROCESS_DETACH time, there may be other threads in the process for
|
||
which we created an INTERNET_THREAD_INFO that aren't going to get the chance
|
||
to delete the structure, so we do it here.
|
||
|
||
Code in this module assumes that it is impossible for a new thread to enter
|
||
this DLL while we are terminating in DLL_PROCESS_DETACH
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// get rid of this thread's info structure. No more debug output after this!
|
||
//
|
||
|
||
InternetDestroyThreadInfo();
|
||
|
||
//
|
||
// get rid of the thread info structures left by other threads
|
||
//
|
||
|
||
LockSerializedList(&ThreadInfoList);
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
while (lpThreadInfo = (LPINTERNET_THREAD_INFO)SlDequeueHead(&ThreadInfoList)) {
|
||
|
||
//
|
||
// already dequeued, no need to call InternetFreeThreadInfo()
|
||
//
|
||
|
||
FREE_MEMORY(lpThreadInfo);
|
||
}
|
||
|
||
UnlockSerializedList(&ThreadInfoList);
|
||
|
||
//
|
||
// no more need for list
|
||
//
|
||
|
||
TerminateSerializedList(&ThreadInfoList);
|
||
|
||
//
|
||
// or TLS index
|
||
//
|
||
|
||
TlsFree(InternetTlsIndex);
|
||
InternetTlsIndex = BAD_TLS_INDEX;
|
||
}
|
||
|
||
|
||
LPINTERNET_THREAD_INFO
|
||
InternetGetThreadInfo(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets the pointer to the INTERNET_THREAD_INFO for this thread and checks
|
||
that it still looks good.
|
||
|
||
If this thread does not have an INTERNET_THREAD_INFO then we create one,
|
||
presuming that this is a new thread
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
LPINTERNET_THREAD_INFO
|
||
Success - pointer to INTERNET_THREAD_INFO block
|
||
|
||
Failure - NULL
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = NULL;
|
||
DWORD lastError;
|
||
|
||
//
|
||
// this is pretty bad - TlsGetValue() can destroy the per-thread last error
|
||
// variable if it returns NULL (to indicate that NULL was actually set, and
|
||
// that NULL does not indicate an error). So we have to read it before it is
|
||
// potentially destroyed, and reset it before we quit.
|
||
//
|
||
// We do this here because typically, other functions will be completely
|
||
// unsuspecting of this behaviour, and it is better to fix it once here,
|
||
// than in several dozen other places, even though it is slightly
|
||
// inefficient
|
||
//
|
||
|
||
lastError = GetLastError();
|
||
if (InternetTlsIndex != BAD_TLS_INDEX) {
|
||
lpThreadInfo = (LPINTERNET_THREAD_INFO)TlsGetValue(InternetTlsIndex);
|
||
}
|
||
|
||
//
|
||
// we may be in the process of creating the INTERNET_THREAD_INFO, in
|
||
// which case its okay for this to be NULL. According to online SDK
|
||
// documentation, a threads TLS value will be initialized to NULL
|
||
//
|
||
|
||
if (lpThreadInfo == NULL) {
|
||
|
||
//
|
||
// we presume this is a new thread. Create an INTERNET_THREAD_INFO
|
||
//
|
||
|
||
IF_DEBUG(NOTHING) {
|
||
DEBUG_PUT(("InternetGetThreadInfo(): Thread %#x: Creating INTERNET_THREAD_INFO\n",
|
||
GetCurrentThreadId()
|
||
));
|
||
}
|
||
|
||
lpThreadInfo = InternetCreateThreadInfo(TRUE);
|
||
}
|
||
if (lpThreadInfo != NULL) {
|
||
|
||
INET_ASSERT(lpThreadInfo->Signature == INTERNET_THREAD_INFO_SIGNATURE);
|
||
INET_ASSERT(lpThreadInfo->ThreadId == GetCurrentThreadId());
|
||
|
||
} else {
|
||
|
||
DEBUG_PUT(("InternetGetThreadInfo(): Failed to get/create INTERNET_THREAD_INFO\n"));
|
||
|
||
}
|
||
|
||
//
|
||
// as above - reset the last error variable in case TlsGetValue() trashed it
|
||
//
|
||
|
||
SetLastError(lastError);
|
||
|
||
//
|
||
// actual success/failure indicated by non-NULL/NULL pointer resp.
|
||
//
|
||
|
||
return lpThreadInfo;
|
||
}
|
||
|
||
|
||
VOID
|
||
InternetSetThreadInfo(
|
||
IN LPINTERNET_THREAD_INFO lpThreadInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets lpThreadInfo as the current thread's INTERNET_THREAD_INFO. Used within
|
||
fibers
|
||
|
||
Arguments:
|
||
|
||
lpThreadInfo - new INTERNET_THREAD_INFO to set
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
if (InternetTlsIndex != BAD_TLS_INDEX) {
|
||
if (!TlsSetValue(InternetTlsIndex, (LPVOID)lpThreadInfo)) {
|
||
|
||
DEBUG_PUT(("InternetSetThreadInfo(): TlsSetValue(%d, %#x) returns %d\n",
|
||
InternetTlsIndex,
|
||
lpThreadInfo,
|
||
GetLastError()
|
||
));
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PUT(("InternetSetThreadInfo(): InternetTlsIndex = %d\n",
|
||
InternetTlsIndex
|
||
));
|
||
|
||
INET_ASSERT(FALSE);
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
InternetIndicateStatusAddress(
|
||
IN DWORD dwInternetStatus,
|
||
IN LPSOCKADDR lpSockAddr,
|
||
IN DWORD dwSockAddrLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Make a status callback to the app. The data is a network address that we
|
||
need to convert to a string
|
||
|
||
Arguments:
|
||
|
||
dwInternetStatus - INTERNET_STATUS_ value
|
||
|
||
lpSockAddr - pointer to full socket address
|
||
|
||
dwSockAddrLength - length of lpSockAddr in bytes
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_INTERNET_OPERATION_CANCELLED
|
||
The app closed the object handle during the callback
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR lpAddress;
|
||
|
||
INET_ASSERT(lpSockAddr != NULL);
|
||
|
||
switch (lpSockAddr->sa_family) {
|
||
case AF_INET:
|
||
lpAddress = _I_inet_ntoa(
|
||
((struct sockaddr_in*)lpSockAddr)->sin_addr
|
||
);
|
||
break;
|
||
|
||
case AF_IPX:
|
||
|
||
//
|
||
// BUGBUG - this should be a call to WSAAddressToString, but that's not implemented yet
|
||
//
|
||
#ifdef SPX_SUPPORT
|
||
lpAddress = GENERIC_SPX_NAME;
|
||
#else
|
||
lpAddress = NULL;
|
||
#endif //SPX_SUPPORT
|
||
break;
|
||
|
||
default:
|
||
lpAddress = NULL;
|
||
break;
|
||
}
|
||
return InternetIndicateStatusString(dwInternetStatus, lpAddress);
|
||
}
|
||
|
||
|
||
DWORD
|
||
InternetIndicateStatusString(
|
||
IN DWORD dwInternetStatus,
|
||
IN LPSTR lpszStatusInfo OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Make a status callback to the app. The data is a string
|
||
|
||
Arguments:
|
||
|
||
dwInternetStatus - INTERNET_STATUS_ value
|
||
|
||
lpszStatusInfo - string status data
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_INTERNET_OPERATION_CANCELLED
|
||
The app closed the object handle during the callback
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
Dword,
|
||
"InternetIndicateStatusString",
|
||
"%d, %q",
|
||
dwInternetStatus,
|
||
lpszStatusInfo
|
||
));
|
||
|
||
DWORD length;
|
||
|
||
if (ARGUMENT_PRESENT(lpszStatusInfo)) {
|
||
length = strlen(lpszStatusInfo) + 1;
|
||
} else {
|
||
length = 0;
|
||
}
|
||
|
||
DWORD error;
|
||
|
||
error = InternetIndicateStatus(dwInternetStatus, lpszStatusInfo, length);
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
InternetIndicateStatusNewHandle(
|
||
IN LPVOID hInternetMapped
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Indicates to the app a new handle
|
||
|
||
Arguments:
|
||
|
||
hInternetMapped - mapped address of new handle being indicated
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_INTERNET_OPERATION_CANCELLED
|
||
The app closed the either the new object handle or the
|
||
parent object handle during the callback
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
Dword,
|
||
"InternetIndicateStatusNewHandle",
|
||
"%#x",
|
||
hInternetMapped
|
||
));
|
||
|
||
HANDLE_OBJECT * hObject = (HANDLE_OBJECT *)hInternetMapped;
|
||
|
||
//
|
||
// reference the new request handle, in case the app closes it in the
|
||
// callback. The new handle now has a reference count of 2
|
||
//
|
||
|
||
hObject->Reference();
|
||
|
||
//INET_ASSERT(hObject->ReferenceCount() == 3);
|
||
|
||
//
|
||
// we indicate the pseudo handle to the app
|
||
//
|
||
|
||
HINTERNET hInternet = hObject->GetPseudoHandle();
|
||
|
||
DWORD error = InternetIndicateStatus(INTERNET_STATUS_HANDLE_CREATED,
|
||
(LPVOID)&hInternet,
|
||
sizeof(hInternet)
|
||
);
|
||
|
||
//
|
||
// dereference the new request handle. If this returns TRUE then the new
|
||
// handle has been deleted (the app called InternetCloseHandle() against
|
||
// it which dereferenced it to 1, and now we've dereferenced it to zero)
|
||
//
|
||
|
||
if (hObject->Dereference()) {
|
||
error = ERROR_INTERNET_OPERATION_CANCELLED;
|
||
} else if (error == ERROR_INTERNET_OPERATION_CANCELLED) {
|
||
|
||
//
|
||
// the parent handle was deleted. Kill off the new handle too
|
||
//
|
||
|
||
BOOL ok;
|
||
|
||
ok = hObject->Dereference();
|
||
|
||
INET_ASSERT(ok);
|
||
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
InternetIndicateStatus(
|
||
IN DWORD dwStatus,
|
||
IN LPVOID lpBuffer,
|
||
IN DWORD dwLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If the app has registered a callback function for the object that this
|
||
thread is operating on, call it with the arguments supplied
|
||
|
||
Arguments:
|
||
|
||
dwStatus - INTERNET_STATUS_ value
|
||
|
||
lpBuffer - pointer to variable data buffer
|
||
|
||
dwLength - length of *lpBuffer in bytes
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_INTERNET_OPERATION_CANCELLED
|
||
The app closed the object handle during the callback
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
Dword,
|
||
"InternetIndicateStatus",
|
||
"%s, %#x, %d",
|
||
InternetMapStatus(dwStatus),
|
||
lpBuffer,
|
||
dwLength
|
||
));
|
||
|
||
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
||
DWORD error = ERROR_SUCCESS;
|
||
|
||
//
|
||
// the app can affect callback operation by specifying a zero context value
|
||
// meaning no callbacks will be generated for this API
|
||
//
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
|
||
INET_ASSERT(lpThreadInfo->hObject != NULL);
|
||
INET_ASSERT(lpThreadInfo->hObjectMapped != NULL);
|
||
|
||
//
|
||
// if the context value in the thread info block is 0 then we use the
|
||
// context from the handle object
|
||
//
|
||
|
||
DWORD_PTR context;
|
||
|
||
context = _InternetGetContext(lpThreadInfo);
|
||
if (context == INTERNET_NO_CALLBACK) {
|
||
context = ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetContext();
|
||
}
|
||
|
||
INTERNET_STATUS_CALLBACK appCallback;
|
||
|
||
appCallback = ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetStatusCallback();
|
||
|
||
IF_DEBUG(THRDINFO) {
|
||
|
||
if (dwStatus == INTERNET_STATUS_REQUEST_COMPLETE) {
|
||
|
||
DEBUG_PRINT(THRDINFO,
|
||
INFO,
|
||
("REQUEST_COMPLETE: dwResult = %#x, dwError = %d [%s]\n",
|
||
((LPINTERNET_ASYNC_RESULT)lpBuffer)->dwResult,
|
||
((LPINTERNET_ASYNC_RESULT)lpBuffer)->dwError,
|
||
InternetMapError(((LPINTERNET_ASYNC_RESULT)lpBuffer)->dwError)
|
||
));
|
||
|
||
}
|
||
}
|
||
|
||
if ((appCallback != NULL) && (context != INTERNET_NO_CALLBACK)) {
|
||
|
||
LPVOID pInfo;
|
||
DWORD infoLength;
|
||
BOOL isAsyncWorkerThread;
|
||
BYTE buffer[256];
|
||
|
||
//
|
||
// we make a copy of the info to remove the app's opportunity to
|
||
// change it. E.g. if we were about to resolve host name "foo" and
|
||
// passed the pointer to our buffer containing "foo", the app could
|
||
// change the name to "bar", changing the intended server
|
||
//
|
||
|
||
if (lpBuffer != NULL) {
|
||
if (dwLength <= sizeof(buffer)) {
|
||
pInfo = buffer;
|
||
} else {
|
||
pInfo = (LPVOID)ALLOCATE_FIXED_MEMORY(dwLength);
|
||
}
|
||
|
||
if (pInfo != NULL) {
|
||
memcpy(pInfo, lpBuffer, dwLength);
|
||
infoLength = dwLength;
|
||
} else {
|
||
infoLength = 0;
|
||
|
||
DEBUG_PRINT(THRDINFO,
|
||
ERROR,
|
||
("Failed to allocate %d bytes for info\n",
|
||
dwLength
|
||
));
|
||
|
||
}
|
||
} else {
|
||
pInfo = NULL;
|
||
infoLength = 0;
|
||
}
|
||
|
||
//
|
||
// we're about to call into the app. We may be in the context of an
|
||
// async worker thread, and if the callback submits an async request
|
||
// then we'll execute it synchronously. To avoid this, we will reset
|
||
// the async worker thread indicator in the INTERNET_THREAD_INFO and
|
||
// restore it when the app returns control to us. This way, if the
|
||
// app makes an API request during the callback, on a handle that
|
||
// has async I/O semantics, then we will simply queue it, and not
|
||
// try to execute it synchronously
|
||
//
|
||
|
||
isAsyncWorkerThread = lpThreadInfo->IsAsyncWorkerThread;
|
||
lpThreadInfo->IsAsyncWorkerThread = FALSE;
|
||
|
||
BOOL bInCallback = lpThreadInfo->InCallback;
|
||
|
||
lpThreadInfo->InCallback = TRUE;
|
||
|
||
INET_ASSERT(!IsBadCodePtr((FARPROC)appCallback));
|
||
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
None,
|
||
"(*callback)",
|
||
"%#x, %#x, %s (%d), %#x [%#x], %d",
|
||
lpThreadInfo->hObject,
|
||
context,
|
||
InternetMapStatus(dwStatus),
|
||
dwStatus,
|
||
pInfo,
|
||
((dwStatus == INTERNET_STATUS_HANDLE_CREATED)
|
||
|| (dwStatus == INTERNET_STATUS_HANDLE_CLOSING))
|
||
? (DWORD_PTR)*(LPHINTERNET)pInfo
|
||
: (((dwStatus == INTERNET_STATUS_REQUEST_SENT)
|
||
|| (dwStatus == INTERNET_STATUS_RESPONSE_RECEIVED)
|
||
|| (dwStatus == INTERNET_STATUS_INTERMEDIATE_RESPONSE)
|
||
|| (dwStatus == INTERNET_STATUS_STATE_CHANGE))
|
||
? *(LPDWORD)pInfo
|
||
: 0),
|
||
infoLength
|
||
));
|
||
|
||
PERF_LOG(PE_APP_CALLBACK_START,
|
||
dwStatus,
|
||
lpThreadInfo->ThreadId,
|
||
lpThreadInfo->hObject
|
||
);
|
||
|
||
HINTERNET hObject = lpThreadInfo->hObject;
|
||
LPVOID hObjectMapped = lpThreadInfo->hObjectMapped;
|
||
|
||
appCallback(lpThreadInfo->hObject,
|
||
context,
|
||
dwStatus,
|
||
pInfo,
|
||
infoLength
|
||
);
|
||
|
||
lpThreadInfo->hObject = hObject;
|
||
lpThreadInfo->hObjectMapped = hObjectMapped;
|
||
|
||
PERF_LOG(PE_APP_CALLBACK_END,
|
||
dwStatus,
|
||
lpThreadInfo->ThreadId,
|
||
lpThreadInfo->hObject
|
||
);
|
||
|
||
DEBUG_LEAVE(0);
|
||
|
||
lpThreadInfo->InCallback = bInCallback;
|
||
lpThreadInfo->IsAsyncWorkerThread = isAsyncWorkerThread;
|
||
|
||
//
|
||
// free the buffer
|
||
//
|
||
|
||
if (pInfo != NULL) {
|
||
if (dwLength > sizeof(buffer))
|
||
FREE_FIXED_MEMORY(pInfo);
|
||
}
|
||
|
||
//
|
||
// if the object is now invalid then the app closed the handle in
|
||
// the callback, and the entire operation is cancelled
|
||
//
|
||
|
||
if (((HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->IsInvalidated()) {
|
||
error = ERROR_INTERNET_OPERATION_CANCELLED;
|
||
}
|
||
} else {
|
||
|
||
DEBUG_PRINT(THRDINFO,
|
||
ERROR,
|
||
("%#x: callback = %#x, context = %#x\n",
|
||
lpThreadInfo->hObject,
|
||
appCallback,
|
||
context
|
||
));
|
||
|
||
//
|
||
// if we're completing a request then we shouldn't be here - it
|
||
// means we lost the context or callback address somewhere along the
|
||
// way
|
||
//
|
||
|
||
INET_ASSERT(dwStatus != INTERNET_STATUS_REQUEST_COMPLETE);
|
||
|
||
#ifdef DEBUG
|
||
if ( dwStatus == INTERNET_STATUS_REQUEST_COMPLETE)
|
||
{
|
||
INET_ASSERT(appCallback != NULL);
|
||
INET_ASSERT(context != INTERNET_NO_CALLBACK);
|
||
INET_ASSERT(_InternetGetContext(lpThreadInfo) != INTERNET_NO_CALLBACK);
|
||
}
|
||
#endif
|
||
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// this is catastrophic if the indication was async request completion
|
||
//
|
||
|
||
DEBUG_PUT(("InternetIndicateStatus(): no INTERNET_THREAD_INFO?\n"));
|
||
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
InternetSetLastError(
|
||
IN DWORD ErrorNumber,
|
||
IN LPSTR ErrorText,
|
||
IN DWORD ErrorTextLength,
|
||
IN DWORD Flags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Copies the error text to the per-thread error buffer (moveable memory)
|
||
|
||
Arguments:
|
||
|
||
ErrorNumber - protocol-specific error code
|
||
|
||
ErrorText - protocol-specific error text (from server). The buffer is
|
||
NOT zero-terminated
|
||
|
||
ErrorTextLength - number of characters in ErrorText
|
||
|
||
Flags - Flags that control how this function operates:
|
||
|
||
SLE_APPEND TRUE if ErrorText is to be appended
|
||
to the text already in the buffer
|
||
|
||
SLE_ZERO_TERMINATE TRUE if ErrorText must have a '\0'
|
||
appended to it
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - Win32 error
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
Dword,
|
||
"InternetSetLastError",
|
||
"%d, %.80q, %d, %#x",
|
||
ErrorNumber,
|
||
ErrorText,
|
||
ErrorTextLength,
|
||
Flags
|
||
));
|
||
|
||
DWORD error;
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo != NULL) {
|
||
error = _InternetSetLastError(lpThreadInfo,
|
||
ErrorNumber,
|
||
ErrorText,
|
||
ErrorTextLength,
|
||
Flags
|
||
);
|
||
} else {
|
||
|
||
DEBUG_PUT(("InternetSetLastError(): no INTERNET_THREAD_INFO\n"));
|
||
|
||
error = ERROR_INTERNET_INTERNAL_ERROR;
|
||
}
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
DWORD
|
||
_InternetSetLastError(
|
||
IN LPINTERNET_THREAD_INFO lpThreadInfo,
|
||
IN DWORD ErrorNumber,
|
||
IN LPSTR ErrorText,
|
||
IN DWORD ErrorTextLength,
|
||
IN DWORD Flags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets or resets the last error text in an INTERNET_THREAD_INFO block
|
||
|
||
Arguments:
|
||
|
||
lpThreadInfo - pointer to INTERNET_THREAD_INFO
|
||
|
||
ErrorNumber - protocol-specific error code
|
||
|
||
ErrorText - protocol-specific error text (from server). The buffer is
|
||
NOT zero-terminated
|
||
|
||
ErrorTextLength - number of characters in ErrorText
|
||
|
||
Flags - Flags that control how this function operates:
|
||
|
||
SLE_APPEND TRUE if ErrorText is to be appended
|
||
to the text already in the buffer
|
||
|
||
SLE_ZERO_TERMINATE TRUE if ErrorText must have a '\0'
|
||
appended to it
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - Win32 error
|
||
|
||
--*/
|
||
|
||
{
|
||
DEBUG_ENTER((DBG_THRDINFO,
|
||
Dword,
|
||
"_InternetSetLastError",
|
||
"%#x, %d, %.80q, %d, %#x",
|
||
lpThreadInfo,
|
||
ErrorNumber,
|
||
ErrorText,
|
||
ErrorTextLength,
|
||
Flags
|
||
));
|
||
|
||
DWORD currentLength;
|
||
DWORD newTextLength;
|
||
DWORD error;
|
||
|
||
newTextLength = ErrorTextLength;
|
||
|
||
//
|
||
// if we are appending text, then account for the '\0' currently at the end
|
||
// of the buffer (if it exists)
|
||
//
|
||
|
||
if (Flags & SLE_APPEND) {
|
||
currentLength = lpThreadInfo->ErrorTextLength;
|
||
if (currentLength != 0) {
|
||
--currentLength;
|
||
}
|
||
newTextLength += currentLength;
|
||
}
|
||
|
||
if (Flags & SLE_ZERO_TERMINATE) {
|
||
++newTextLength;
|
||
}
|
||
|
||
//
|
||
// expect success (and why not?)
|
||
//
|
||
|
||
error = ERROR_SUCCESS;
|
||
|
||
//
|
||
// allocate, grow or shrink the buffer to fit. The buffer is moveable. If
|
||
// the buffer is being shrunk to zero size then NULL will be returned as
|
||
// the buffer handle from ResizeBuffer()
|
||
//
|
||
|
||
lpThreadInfo->hErrorText = ResizeBuffer(lpThreadInfo->hErrorText,
|
||
newTextLength,
|
||
FALSE
|
||
);
|
||
if (lpThreadInfo->hErrorText != NULL) {
|
||
|
||
LPSTR lpErrorText;
|
||
|
||
lpErrorText = (LPSTR)LOCK_MEMORY(lpThreadInfo->hErrorText);
|
||
|
||
INET_ASSERT(lpErrorText != NULL);
|
||
|
||
if (lpErrorText != NULL) {
|
||
if (Flags & SLE_APPEND) {
|
||
lpErrorText += currentLength;
|
||
}
|
||
memcpy(lpErrorText, ErrorText, ErrorTextLength);
|
||
if (Flags & SLE_ZERO_TERMINATE) {
|
||
lpErrorText[ErrorTextLength++] = '\0';
|
||
}
|
||
|
||
//
|
||
// the text should always be zero-terminated. We expect this in
|
||
// InternetGetLastResponseInfo()
|
||
//
|
||
|
||
INET_ASSERT(lpErrorText[ErrorTextLength - 1] == '\0');
|
||
|
||
UNLOCK_MEMORY(lpThreadInfo->hErrorText);
|
||
|
||
} else {
|
||
|
||
//
|
||
// real error occurred - failed to lock memory?
|
||
//
|
||
|
||
error = GetLastError();
|
||
}
|
||
} else {
|
||
|
||
INET_ASSERT(newTextLength == 0);
|
||
|
||
newTextLength = 0;
|
||
}
|
||
|
||
//
|
||
// set the error code and text length
|
||
//
|
||
|
||
lpThreadInfo->ErrorTextLength = newTextLength;
|
||
lpThreadInfo->ErrorNumber = ErrorNumber;
|
||
|
||
DEBUG_LEAVE(error);
|
||
|
||
return error;
|
||
}
|
||
|
||
|
||
LPSTR
|
||
InternetLockErrorText(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns a pointer to the locked per-thread error text buffer
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
LPSTR
|
||
Success - pointer to locked buffer
|
||
|
||
Failure - NULL
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
if (lpThreadInfo != NULL) {
|
||
|
||
HLOCAL lpErrorText;
|
||
|
||
lpErrorText = lpThreadInfo->hErrorText;
|
||
if (lpErrorText != (HLOCAL)NULL) {
|
||
return (LPSTR)LOCK_MEMORY(lpErrorText);
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
//VOID
|
||
//InternetUnlockErrorText(
|
||
// VOID
|
||
// )
|
||
//
|
||
///*++
|
||
//
|
||
//Routine Description:
|
||
//
|
||
// Unlocks the per-thread error text buffer locked by InternetLockErrorText()
|
||
//
|
||
//Arguments:
|
||
//
|
||
// None.
|
||
//
|
||
//Return Value:
|
||
//
|
||
// None.
|
||
//
|
||
//--*/
|
||
//
|
||
//{
|
||
// LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
//
|
||
// lpThreadInfo = InternetGetThreadInfo();
|
||
//
|
||
// //
|
||
// // assume that if we locked the error text, there must be an
|
||
// // INTERNET_THREAD_INFO when we come to unlock it
|
||
// //
|
||
//
|
||
// INET_ASSERT(lpThreadInfo != NULL);
|
||
//
|
||
// if (lpThreadInfo != NULL) {
|
||
//
|
||
// HLOCAL hErrorText;
|
||
//
|
||
// hErrorText = lpThreadInfo->hErrorText;
|
||
//
|
||
// //
|
||
// // similarly, there must be a handle to the error text buffer
|
||
// //
|
||
//
|
||
// INET_ASSERT(hErrorText != NULL);
|
||
//
|
||
// if (hErrorText != (HLOCAL)NULL) {
|
||
// UNLOCK_MEMORY(hErrorText);
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
|
||
VOID
|
||
InternetSetContext(
|
||
IN DWORD_PTR dwContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the context value in the INTERNET_THREAD_INFO for status callbacks
|
||
|
||
Arguments:
|
||
|
||
dwContext - context value to remember
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
_InternetSetContext(lpThreadInfo, dwContext);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
InternetSetObjectHandle(
|
||
IN HINTERNET hInternet,
|
||
IN HINTERNET hInternetMapped
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the hObject field in the INTERNET_THREAD_INFO structure so we can get
|
||
at the handle contents, even when we're in a function that does not take
|
||
the hInternet as a parameter
|
||
|
||
Arguments:
|
||
|
||
hInternet - handle of object we may need info from
|
||
|
||
hInternetMapped - mapped handle of object we may need info from
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
_InternetSetObjectHandle(lpThreadInfo, hInternet, hInternetMapped);
|
||
}
|
||
}
|
||
|
||
|
||
HINTERNET
|
||
InternetGetObjectHandle(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Just returns the hObject value stored in our INTERNET_THREAD_INFO
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
HINTERNET
|
||
Success - non-NULL handle value
|
||
|
||
Failure - NULL object handle (may not have been set)
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
HINTERNET hInternet;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
hInternet = lpThreadInfo->hObject;
|
||
} else {
|
||
hInternet = NULL;
|
||
}
|
||
return hInternet;
|
||
}
|
||
|
||
|
||
HINTERNET
|
||
InternetGetMappedObjectHandle(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Just returns the hObjectMapped value stored in our INTERNET_THREAD_INFO
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
HINTERNET
|
||
Success - non-NULL handle value
|
||
|
||
Failure - NULL object handle (may not have been set)
|
||
|
||
--*/
|
||
|
||
{
|
||
LPINTERNET_THREAD_INFO lpThreadInfo;
|
||
HINTERNET hInternet;
|
||
|
||
lpThreadInfo = InternetGetThreadInfo();
|
||
|
||
if (lpThreadInfo != NULL) {
|
||
hInternet = lpThreadInfo->hObjectMapped;
|
||
} else {
|
||
hInternet = NULL;
|
||
}
|
||
return hInternet;
|
||
}
|