You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4167 lines
127 KiB
4167 lines
127 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
davutil.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the user mode DAV miniredir routines pertaining to
|
|
initialization, callbacks etc.
|
|
|
|
Author:
|
|
|
|
Rohan Kumar [RohanK] 07-July-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#include "ntumrefl.h"
|
|
#include "usrmddav.h"
|
|
#include "global.h"
|
|
#include <time.h>
|
|
#include <objbase.h>
|
|
#include "UniUtf.h"
|
|
#include <netevent.h>
|
|
#include <wincrypt.h>
|
|
|
|
//
|
|
// Global definitions used in the DAV user mode process. These are explained
|
|
// in the header file "global.h".
|
|
//
|
|
|
|
HINTERNET IHandle = INVALID_HANDLE_VALUE;
|
|
|
|
HINTERNET ISyncHandle = INVALID_HANDLE_VALUE;
|
|
|
|
LIST_ENTRY ServerHashTable[SERVER_TABLE_SIZE];
|
|
|
|
CRITICAL_SECTION HashServerEntryTableLock = {0};
|
|
CRITICAL_SECTION DavPassportLock = {0};
|
|
|
|
//
|
|
// The BOOL is used in DavClose() to check if the critical section (see above)
|
|
// "HashServerEntryTableLock" was initialized. Since this is only used in
|
|
// DavInit() and DavClose() functions, both os which are implemented in this
|
|
// file, this global is not exported in any header file.
|
|
//
|
|
BOOL ServerTableLockSet = FALSE;
|
|
|
|
ULONG ServerIDCount;
|
|
|
|
LIST_ENTRY ToBeFinalizedServerEntries;
|
|
|
|
BOOL didDavUseObjectInitialize = FALSE;
|
|
|
|
BOOL DavUsingWinInetSynchronously = FALSE;
|
|
|
|
//
|
|
// Mentioned below are the prototypes of functions that are used only within
|
|
// this module (file). These functions should not be exposed outside.
|
|
//
|
|
|
|
BOOL
|
|
DavFinalizeServerEntry (
|
|
PHASH_SERVER_ENTRY ServerHashEntry
|
|
);
|
|
|
|
//
|
|
// Implementation of functions begins here.
|
|
//
|
|
|
|
ULONG
|
|
DavInit(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the DAV environment.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
ULONG count = 0;
|
|
DWORD NumOfConnections = 0, ConnBuffSize = 0;
|
|
INTERNET_STATUS_CALLBACK DavCallBack;
|
|
BOOL ReturnVal;
|
|
ULONG_PTR CallbackStatus;
|
|
LPWSTR DAVUserAgent = NULL;
|
|
OSVERSIONINFO osVersionInfo;
|
|
WCHAR DAVUserAgentNameStr[] = L"Microsoft-WebDAV-MiniRedir";
|
|
LONG DisableHKCUCaching = 0;
|
|
|
|
//
|
|
// Get the OS version. This will be used to form WebDAV User Agent string.
|
|
// This String is used in HttpPackects xchange.
|
|
//
|
|
ZeroMemory(&osVersionInfo, sizeof(OSVERSIONINFO));
|
|
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&osVersionInfo)) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/GetVersionEx. Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DAVUserAgent = (LPWSTR) LocalAlloc ( LMEM_FIXED | LMEM_ZEROINIT,
|
|
( wcslen(DAVUserAgentNameStr) + // for: Microsoft-WebDAV-MiniRedir
|
|
1 + // for L"/"
|
|
5 + // for Major-Version
|
|
1 + // for '.'
|
|
5 + // for Minor-Version
|
|
1 + // for '.'
|
|
10 // for Build-No
|
|
) * sizeof (WCHAR));
|
|
if (DAVUserAgent == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS, "DavInit/LocalAlloc. Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
swprintf(DAVUserAgent, L"%s/%d.%d.%d",
|
|
DAVUserAgentNameStr,
|
|
osVersionInfo.dwMajorVersion,
|
|
osVersionInfo.dwMinorVersion,
|
|
osVersionInfo.dwBuildNumber);
|
|
|
|
//
|
|
// Set the ConnectionsPerServer limit to infinity.
|
|
//
|
|
|
|
NumOfConnections = 0xffffffff;
|
|
ConnBuffSize = sizeof(DWORD);
|
|
|
|
ReturnVal = InternetSetOptionW(NULL,
|
|
INTERNET_OPTION_MAX_CONNS_PER_SERVER,
|
|
&(NumOfConnections),
|
|
ConnBuffSize);
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetSetOptionW(1). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
ReturnVal = InternetSetOptionW(NULL,
|
|
INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER,
|
|
&(NumOfConnections),
|
|
ConnBuffSize);
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetSetOptionW(2). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC, "DavInit: Using WinInet Synchronously\n"));
|
|
|
|
DavUsingWinInetSynchronously = TRUE;
|
|
|
|
//
|
|
// Initialize an Internet handle for synchronous use.
|
|
//
|
|
IHandle = InternetOpenW((LPCWSTR)DAVUserAgent,
|
|
INTERNET_OPEN_TYPE_PRECONFIG,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
if (IHandle == NULL) {
|
|
IHandle = INVALID_HANDLE_VALUE;
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetOpenW(2). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
{
|
|
DWORD dwDisable = 0;
|
|
if ( !InternetSetOptionW(IHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &dwDisable, sizeof(DWORD)) ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetSetOption(3). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize a synchronous Internet handle for synchronous use.
|
|
//
|
|
ISyncHandle = InternetOpenW((LPCWSTR)DAVUserAgent,
|
|
INTERNET_OPEN_TYPE_PRECONFIG,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
if (ISyncHandle == NULL) {
|
|
ISyncHandle = INVALID_HANDLE_VALUE;
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetOpenW(3). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
{
|
|
DWORD dwDisable = 1;
|
|
if ( !InternetSetOptionW(ISyncHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &dwDisable, sizeof(DWORD)) ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetSetOption(3). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the Global server hash table lock. Yes,
|
|
// InitializeCriticalSection can throw a STATUS_NO_MEMORY exception.
|
|
//
|
|
try {
|
|
InitializeCriticalSection( &(HashServerEntryTableLock) );
|
|
InitializeCriticalSection( &(ServerShareTableLock) );
|
|
InitializeCriticalSection( &(DavLoggedOnUsersLock) );
|
|
InitializeCriticalSection( &(DavPassportLock) );
|
|
InitializeCriticalSection( &(NonDAVServerListLock) );
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
WStatus = GetExceptionCode();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InitializeCriticalSection: Exception Code ="
|
|
" = %08lx.\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
ServerTableLockSet = TRUE;
|
|
|
|
//
|
|
// Initialize the hash table entries.
|
|
//
|
|
for (count = 0; count < SERVER_TABLE_SIZE; count++) {
|
|
InitializeListHead( &(ServerHashTable[count]) );
|
|
}
|
|
|
|
//
|
|
// Initialize the ServerShare table entries.
|
|
//
|
|
for (count = 0; count < SERVER_SHARE_TABLE_SIZE; count++) {
|
|
InitializeListHead( &(ServerShareTable[count]) );
|
|
}
|
|
|
|
//
|
|
// Set the ServerIDCount to zero;
|
|
//
|
|
ServerIDCount = 0;
|
|
|
|
//
|
|
// Set the number of logged on users to 0.
|
|
//
|
|
DavNumberOfLoggedOnUsers = 0;
|
|
|
|
//
|
|
// Initialize the "To Be Finalized Server Entries" list.
|
|
//
|
|
InitializeListHead( &(ToBeFinalizedServerEntries) );
|
|
|
|
InitializeListHead( &(NonDAVServerList) );
|
|
|
|
//
|
|
// Initialize the Dav "net use" table.
|
|
//
|
|
DavUseObject.TableSize = 0;
|
|
DavUseObject.Table = NULL;
|
|
RtlInitializeResource( &(DavUseObject.TableResource) );
|
|
didDavUseObjectInitialize = TRUE;
|
|
|
|
//
|
|
// WinInet needs to store the secondary DA cache in the HKCU. Even though
|
|
// the thread that is doing this write is impersonating a different user this
|
|
// write happens in wind up in HKEY_USERS\S-1-5-19 (LocalSystem). This is
|
|
// because of a bug in the registry APIs. First open of the predefined handle
|
|
// will initialize the HKCU cache and any open after that doesn't take the
|
|
// impersonation into account. It’s not quite right, but it’s legacy by now
|
|
// (been there since NT4) and cannot be changed. By calling the registry API
|
|
// RegDisablePredefinedCache, we can disable this caching process wide. The
|
|
// DA cache will now be stored in the right HKCU. Tweener spec states that
|
|
// the secondary DA cache be stored in the HKCU hive so that all Tweener
|
|
// apps (IE, WPW) can benefit from this single location.
|
|
//
|
|
DisableHKCUCaching = RegDisablePredefinedCache();
|
|
if (DisableHKCUCaching != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/RegDisablePredefinedCache: DisableHKCUCaching = %d\n",
|
|
DisableHKCUCaching));
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
|
|
if (IHandle != INVALID_HANDLE_VALUE) {
|
|
BOOL ReturnVal;
|
|
ReturnVal = InternetCloseHandle(IHandle);
|
|
if (!ReturnVal) {
|
|
ULONG CloseStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetCloseHandle(1): Error Val = %d.\n",
|
|
CloseStatus));
|
|
}
|
|
IHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (ISyncHandle != INVALID_HANDLE_VALUE) {
|
|
BOOL ReturnVal;
|
|
ReturnVal = InternetCloseHandle(ISyncHandle);
|
|
if (!ReturnVal) {
|
|
ULONG CloseStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInit/InternetCloseHandle(2): Error Val = %d.\n",
|
|
CloseStatus));
|
|
}
|
|
ISyncHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
}
|
|
|
|
if (DAVUserAgent != NULL) {
|
|
LocalFree((HLOCAL)DAVUserAgent);
|
|
DAVUserAgent = NULL;
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavClose(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees up the resources acquired during the initialization of
|
|
the DAV environment.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Close IHandle if needed.
|
|
//
|
|
if (IHandle != INVALID_HANDLE_VALUE) {
|
|
BOOL ReturnVal;
|
|
ReturnVal = InternetCloseHandle(IHandle);
|
|
if (!ReturnVal) {
|
|
ULONG CloseStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavClose/InternetCloseHandle(1): Error Val = %d.\n",
|
|
CloseStatus));
|
|
}
|
|
IHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (ISyncHandle != INVALID_HANDLE_VALUE) {
|
|
BOOL ReturnVal;
|
|
ReturnVal = InternetCloseHandle(ISyncHandle);
|
|
if (!ReturnVal) {
|
|
ULONG CloseStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavClose/InternetCloseHandle(2): Error Val = %d.\n",
|
|
CloseStatus));
|
|
}
|
|
ISyncHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Delete the critical section used for synchronizing the server hash table.
|
|
//
|
|
if (ServerTableLockSet) {
|
|
DeleteCriticalSection( &(HashServerEntryTableLock) );
|
|
DeleteCriticalSection( &(ServerShareTableLock) );
|
|
DeleteCriticalSection( &(DavLoggedOnUsersLock) );
|
|
DeleteCriticalSection( &(DavPassportLock) );
|
|
DeleteCriticalSection( &(NonDAVServerListLock) );
|
|
ServerTableLockSet = FALSE;
|
|
}
|
|
|
|
if (didDavUseObjectInitialize) {
|
|
RtlDeleteResource( &(DavUseObject.TableResource) );
|
|
didDavUseObjectInitialize = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DavHashTheServerName(
|
|
PWCHAR ServerName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The hash function that takes in a string, hashes it to produce a ULONG
|
|
which is returned to the caller.
|
|
|
|
Arguments:
|
|
|
|
ServerName - Name to be hashed.
|
|
|
|
Return Value:
|
|
|
|
The hashed value.
|
|
|
|
--*/
|
|
{
|
|
ULONG HashedValue = 0, Val = 0, TotalVal = 0, shiftCount = 0;
|
|
PWCHAR cPtr;
|
|
|
|
if (ServerName == NULL) {
|
|
DavPrint((DEBUG_ERRORS, "DavHashTheServerName. The ServerName is NULL.\n"));
|
|
HashedValue = SERVER_TABLE_SIZE;
|
|
return (HashedValue);
|
|
}
|
|
|
|
//
|
|
// The for loop below forms the hashing logic. We take each character of the
|
|
// server name, cast it to a ULONG, lshift it by shiftCount (0, 4, 8,...,28)
|
|
// and add it to HashedValue. Once the shiftCount reaches 28, we reset it to
|
|
// zero.
|
|
//
|
|
for (cPtr = ServerName; *cPtr != L'\0'; cPtr++) {
|
|
Val = (ULONG)(*cPtr);
|
|
Val = Val << shiftCount;
|
|
shiftCount += 4;
|
|
if (shiftCount == 28) {
|
|
shiftCount = 0;
|
|
}
|
|
TotalVal += Val;
|
|
}
|
|
|
|
//
|
|
// Finally we take the value % SERVER_TABLE_SIZE.
|
|
//
|
|
HashedValue = TotalVal % SERVER_TABLE_SIZE;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavHashTheServerName. ServerName = %ws, HashValue = %d\n",
|
|
ServerName, HashedValue));
|
|
|
|
return (HashedValue);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DavIsThisServerInTheTable(
|
|
IN PWCHAR ServerName,
|
|
OUT PHASH_SERVER_ENTRY *ServerHashEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if an entry for the ServerName supplied by the
|
|
caller exists in the hash table. If it does, the address of the entry is
|
|
returned in the caller supplied buffer. Note that the caller should take a
|
|
lock on the ServerHashTable before calling this routine.
|
|
|
|
Arguments:
|
|
|
|
ServerName - Name of the server.
|
|
|
|
ServerHashEntry - Pointer to the Hash entry structure.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Server entry exists in the hash table
|
|
|
|
FALSE - It does not. Duh.
|
|
|
|
--*/
|
|
{
|
|
BOOL isPresent = FALSE;
|
|
ULONG ServerHashID;
|
|
PLIST_ENTRY listEntry;
|
|
PHASH_SERVER_ENTRY HashEntry;
|
|
|
|
//
|
|
// IMPORTANT!!!! The caller should take a lock on the global ServerHashTable
|
|
// before calling this routine.
|
|
//
|
|
|
|
ASSERT(ServerName != NULL);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavIsThisServerInTheTable: Checking if ServerName: %ws exists "
|
|
"in the table.\n", ServerName));
|
|
|
|
//
|
|
// Get the hash index of the server.
|
|
//
|
|
ServerHashID = DavHashTheServerName(ServerName);
|
|
ASSERT(ServerHashID != SERVER_TABLE_SIZE);
|
|
|
|
//
|
|
// Search the hash table at this index to see if an entry for this server
|
|
// exists.
|
|
//
|
|
listEntry = ServerHashTable[ServerHashID].Flink;
|
|
while ( listEntry != &ServerHashTable[ServerHashID] ) {
|
|
//
|
|
// Get the pointer to the HASH_SERVER_ENTRY structure.
|
|
//
|
|
HashEntry = CONTAINING_RECORD(listEntry,
|
|
HASH_SERVER_ENTRY,
|
|
ServerListEntry);
|
|
//
|
|
// Check to see if this entry is for the server in question.
|
|
//
|
|
if ( wcscmp(ServerName, HashEntry->ServerName) == 0 ) {
|
|
isPresent = TRUE;
|
|
break;
|
|
}
|
|
listEntry = listEntry->Flink;
|
|
}
|
|
|
|
if (isPresent) {
|
|
//
|
|
// Yes, we found the entry for this server. Return its address to the
|
|
// caller in the supplied buffer.
|
|
//
|
|
*ServerHashEntry = HashEntry;
|
|
return isPresent;
|
|
}
|
|
|
|
//
|
|
// We did not find an entry for this server. Duh.
|
|
//
|
|
*ServerHashEntry = NULL;
|
|
|
|
return isPresent;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DavIsServerInFinalizeList(
|
|
IN PWCHAR ServerName,
|
|
OUT PHASH_SERVER_ENTRY *ServerHashEntry,
|
|
IN BOOL ReactivateIfExists
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if an entry for the ServerName supplied by the
|
|
caller exists in the "to be finalized" list. If it does, the address of the
|
|
entry is returned in the caller supplied buffer. It also moves the server
|
|
entry from the "to be finalized list" to the hash table. Note that the
|
|
caller should take a lock on the "ToBeFinalizedServerEntries" before calling
|
|
this routine.
|
|
|
|
Arguments:
|
|
|
|
ServerName - Name of the server.
|
|
|
|
ServerHashEntry - Pointer to the Hash entry structure.
|
|
|
|
ReactivateIfExists - If this is TRUE, then if the ServerHashEntry exists, it
|
|
is reactivated. If this is FALSE, it means that the
|
|
caller just wanted to know if the ServerHashEntry exists
|
|
or not in the ServerHashTable and we shouldn't reactivate
|
|
it.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Server entry exists in the list.
|
|
|
|
FALSE - It does not. Duh.
|
|
|
|
--*/
|
|
{
|
|
BOOL isPresent = FALSE;
|
|
ULONG ServerHashID;
|
|
PLIST_ENTRY listEntry;
|
|
PHASH_SERVER_ENTRY ServerEntry;
|
|
PPER_USER_ENTRY PerUserEntry;
|
|
|
|
//
|
|
// IMPORTANT!!!! The caller should take a lock on the global ServerHashTable
|
|
// before calling this routine.
|
|
//
|
|
|
|
//
|
|
// Before we search the ToBeFinalizedList for an entry for this Server, we
|
|
// finalize the list to remove any stale entires. Once we are done with the
|
|
// finalization, we can proceed.
|
|
//
|
|
DavFinalizeToBeFinalizedList();
|
|
|
|
listEntry = ToBeFinalizedServerEntries.Flink;
|
|
|
|
while ( listEntry != &ToBeFinalizedServerEntries ) {
|
|
|
|
//
|
|
// Get the pointer to the HASH_SERVER_ENTRY structure.
|
|
//
|
|
ServerEntry = CONTAINING_RECORD(listEntry,
|
|
HASH_SERVER_ENTRY,
|
|
ServerListEntry);
|
|
//
|
|
// Check to see if this entry is for the server in question.
|
|
//
|
|
if ( wcscmp(ServerName, ServerEntry->ServerName) == 0 ) {
|
|
isPresent = TRUE;
|
|
break;
|
|
}
|
|
|
|
listEntry = listEntry->Flink;
|
|
|
|
}
|
|
|
|
if (isPresent) {
|
|
|
|
//
|
|
// If this entry is not for a valid DAV server, then we return TRUE, but
|
|
// set *ServerHashEntry to NULL. This gives an indication to the caller
|
|
// that the entry exists, but is not a valid DAV server.
|
|
//
|
|
if (!ServerEntry->isDavServer) {
|
|
*ServerHashEntry = NULL;
|
|
return isPresent;
|
|
}
|
|
|
|
if (ReactivateIfExists) {
|
|
|
|
//
|
|
// OK, its a valid DAV server. Remove it from the "to be finalized"
|
|
// list.
|
|
//
|
|
RemoveEntryList( &(ServerEntry->ServerListEntry) );
|
|
|
|
//
|
|
// Check to see if the worker (scavenger) thread tried finalizing it.
|
|
// If it did, we need to unfinalize it. By that we mean, go through all
|
|
// the user entries (they should be marked closing), add a reference
|
|
// count (the thread would have decremented it while finalizing) and set
|
|
// the state to initialized.
|
|
//
|
|
if (ServerEntry->HasItBeenScavenged) {
|
|
|
|
listEntry = ServerEntry->PerUserEntry.Flink;
|
|
|
|
while ( listEntry != &(ServerEntry->PerUserEntry) ) {
|
|
//
|
|
// Get the pointer to the PER_USER_ENTRY structure.
|
|
//
|
|
PerUserEntry = CONTAINING_RECORD(listEntry,
|
|
PER_USER_ENTRY,
|
|
UserEntry);
|
|
//
|
|
// The current state should be closing.
|
|
//
|
|
ASSERT(PerUserEntry->UserEntryState == UserEntryClosing);
|
|
|
|
//
|
|
// Set the state to initialized.
|
|
//
|
|
PerUserEntry->UserEntryState = UserEntryInitialized;
|
|
|
|
//
|
|
// Increment the reference count.
|
|
//
|
|
PerUserEntry->UserEntryRefCount++;
|
|
|
|
listEntry = listEntry->Flink;
|
|
}
|
|
|
|
ServerEntry->HasItBeenScavenged = FALSE;
|
|
}
|
|
|
|
//
|
|
// Set its RefCount to 1.
|
|
//
|
|
ServerEntry->ServerEntryRefCount = 1;
|
|
|
|
//
|
|
// Add it to the hash table.
|
|
//
|
|
ServerHashID = DavHashTheServerName(ServerName);
|
|
ASSERT(ServerHashID != SERVER_TABLE_SIZE);
|
|
InsertHeadList( &(ServerHashTable[ServerHashID]),
|
|
&(ServerEntry->ServerListEntry) );
|
|
|
|
ServerEntry->TimeValueInSec = DONT_EXPIRE;
|
|
|
|
}
|
|
|
|
//
|
|
// Yes, we found the entry for this server. We need to move this entry
|
|
// to the hash table.
|
|
//
|
|
*ServerHashEntry = ServerEntry;
|
|
|
|
return isPresent;
|
|
|
|
}
|
|
|
|
//
|
|
// We did not find an entry for this server. Duh.
|
|
//
|
|
*ServerHashEntry = NULL;
|
|
|
|
return isPresent;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavInitializeAndInsertTheServerEntry(
|
|
IN OUT PHASH_SERVER_ENTRY ServerHashEntry,
|
|
IN PWCHAR ServerName,
|
|
IN ULONG EntrySize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes a newly created server entry strucutre and inserts
|
|
it into the global server hash table. Note that the caller should take a
|
|
lock on the ServerHashTable before calling this routine.
|
|
|
|
Arguments:
|
|
|
|
ServerHashEntry - Pointer to the Hash entry structure to be initialized and
|
|
inserted.
|
|
|
|
ServerName - Name of the server.
|
|
|
|
EntrySize - Size of the server entry including the server name.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
ULONG ServerHashID;
|
|
|
|
//
|
|
// IMPORTANT!!!! The caller should take a lock on the global ServerHashTable
|
|
// before calling this routine.
|
|
//
|
|
|
|
ASSERT(ServerName != NULL);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavInitializeAndInsertTheServerEntry: ServerName: %ws.\n",
|
|
ServerName));
|
|
//
|
|
// Copy the server name to the end of the structure.
|
|
//
|
|
ASSERT( (EntrySize - sizeof(HASH_SERVER_ENTRY)) >=
|
|
((wcslen(ServerName) + 1) * sizeof(WCHAR)) );
|
|
ServerHashEntry->ServerName = &ServerHashEntry->StrBuffer[0];
|
|
wcscpy(ServerHashEntry->ServerName, ServerName);
|
|
|
|
ServerHashEntry->EntrySize = EntrySize;
|
|
|
|
ServerHashEntry->TimeValueInSec = DONT_EXPIRE;
|
|
|
|
ServerHashEntry->HasItBeenScavenged = FALSE;
|
|
|
|
//
|
|
// Increment the ID and assign it to the entry.
|
|
//
|
|
ServerIDCount++;
|
|
ServerHashEntry->ServerID = ServerIDCount;
|
|
|
|
//
|
|
// Initialize the Per User list that hangs off the server entry.
|
|
//
|
|
InitializeListHead( &(ServerHashEntry->PerUserEntry) );
|
|
|
|
//
|
|
// Finally set the reference count of this entry to 1.
|
|
//
|
|
ServerHashEntry->ServerEntryRefCount = 1;
|
|
|
|
//
|
|
// Finally, get the hash ID and insert this new entry into the global server
|
|
// entry hash table.
|
|
//
|
|
ServerHashID = DavHashTheServerName(ServerName);
|
|
ASSERT(ServerHashID != SERVER_TABLE_SIZE);
|
|
InsertHeadList( &(ServerHashTable[ServerHashID]), &(ServerHashEntry->ServerListEntry) );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavFinalizeToBeFinalizedList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks through the list of ToBeFinalizedServerEntries and
|
|
finalizes those whose "to live" time has expired. When server entries
|
|
are added to this list, the time is saved. Periodically a worker thread
|
|
calls this function and finalizes all the entries for whom,
|
|
(CurrentTime - TimeSaved >= ThresholdValue). Note that the caller should
|
|
take a lock on the "ToBeFinalizedServerEntries" before calling this routine.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
time_t CurrentTimeInSec;
|
|
ULONGLONG TimeDiff;
|
|
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
|
|
BOOL shouldFree = TRUE;
|
|
|
|
//
|
|
// IMPORTANT!!!! The caller should take a lock on the global ServerHashTable
|
|
// before calling this routine.
|
|
//
|
|
|
|
listEntry = ToBeFinalizedServerEntries.Flink;
|
|
|
|
while ( listEntry != &ToBeFinalizedServerEntries) {
|
|
|
|
//
|
|
// Get the pointer to the HASH_SERVER_ENTRY structure.
|
|
//
|
|
ServerHashEntry = CONTAINING_RECORD(listEntry,
|
|
HASH_SERVER_ENTRY,
|
|
ServerListEntry);
|
|
|
|
//
|
|
// Get the next entry on the list.
|
|
//
|
|
listEntry = listEntry->Flink;
|
|
|
|
//
|
|
// If the ServerEntryRefCount is > 0 then we don't finalize this
|
|
// ServerHashEntry since some thread is still accessing it.
|
|
//
|
|
if (ServerHashEntry->ServerEntryRefCount > 0) {
|
|
continue;
|
|
}
|
|
|
|
CurrentTimeInSec = time(NULL);
|
|
|
|
TimeDiff = ( CurrentTimeInSec - (ServerHashEntry->TimeValueInSec) );
|
|
|
|
if ( TimeDiff >= ServerNotFoundCacheLifeTimeInSec ) {
|
|
|
|
//
|
|
// Finalize this server entry. If the return value is TRUE it means
|
|
// that all the user entries that were hanging off this server
|
|
// entry have been finalized and so we can go ahead and free this
|
|
// entry. If its FALSE, it means that the we have marked as closing
|
|
// all the user entries, but not all of them were finalized. This
|
|
// is because some thread still holds a reference to the user entry.
|
|
// Finally, set the bool value that says it was scavenged to TRUE.
|
|
//
|
|
ServerHashEntry->HasItBeenScavenged = TRUE;
|
|
|
|
shouldFree = DavFinalizeServerEntry(ServerHashEntry);
|
|
|
|
if (shouldFree) {
|
|
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
|
|
//
|
|
// Remove this entry from the ToBeFinalizedList of Server
|
|
// entries.
|
|
//
|
|
RemoveEntryList( &(ServerHashEntry->ServerListEntry) );
|
|
|
|
//
|
|
// If the ServerEventHandle is not NULL then we close it
|
|
// before freeing the ServerHashEntry structure.
|
|
//
|
|
if (ServerHashEntry->ServerEventHandle != NULL) {
|
|
CloseHandle(ServerHashEntry->ServerEventHandle);
|
|
}
|
|
|
|
FreeHandle = LocalFree((HLOCAL)ServerHashEntry);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFinalizeToBeFinalizedList/LocalFree. "
|
|
"Error Val = %d.\n", FreeStatus));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DavFinalizeServerEntry (
|
|
PHASH_SERVER_ENTRY ServerHashEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finalizes the server entry that is passed to the routine. Note
|
|
that the caller should take a lock on the ServerHashTable before calling
|
|
this routine.
|
|
|
|
Arguments:
|
|
|
|
ServerHashEntry - The server entry being finalized.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY listEntry;
|
|
PPER_USER_ENTRY UserEntry;
|
|
BOOL didFree = TRUE, didFinalize;
|
|
|
|
//
|
|
// IMPORTANT!!!! The caller should take a lock on the global ServerHashTable
|
|
// before calling this routine.
|
|
//
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFinalizeServerEntry: ServerEntry: %08lx.\n", ServerHashEntry));
|
|
|
|
listEntry = ServerHashEntry->PerUserEntry.Flink;
|
|
|
|
//
|
|
// Go through all the User entries, mark them closing and finalize them.
|
|
// If we have already marked them closing then we don't need to finalize
|
|
// them again.
|
|
//
|
|
while ( listEntry != &(ServerHashEntry->PerUserEntry) ) {
|
|
|
|
//
|
|
// Get the pointer to the PER_USER_ENTRY structure.
|
|
//
|
|
UserEntry = CONTAINING_RECORD(listEntry, PER_USER_ENTRY, UserEntry);
|
|
|
|
//
|
|
// Get the next entry on the list.
|
|
//
|
|
listEntry = listEntry->Flink;
|
|
|
|
//
|
|
// This is the only routine that marks the state of a user entry to be
|
|
// closing. If the first one is marked closing, then we have already
|
|
// through this list before and hence we just return. Some other thread(s)
|
|
// has(ve) a reference to this and will finalizeit when they are done.
|
|
//
|
|
if (UserEntry->UserEntryState == UserEntryClosing) {
|
|
ASSERT(ServerHashEntry->HasItBeenScavenged == TRUE);
|
|
didFree = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Mark this entry closing and then call the finalization routine. If
|
|
// we did not finalize, then set didFree to FALSE. Since we do not wish
|
|
// to free the server entry even if one user entry is not finalized.
|
|
//
|
|
UserEntry->UserEntryState = UserEntryClosing;
|
|
|
|
didFree = FALSE;
|
|
|
|
}
|
|
|
|
return didFree;
|
|
}
|
|
|
|
|
|
VOID
|
|
_stdcall
|
|
DavHandleAsyncResponse(
|
|
HINTERNET IHandle,
|
|
DWORD_PTR CallBackContext,
|
|
DWORD InternetStatus,
|
|
LPVOID StatusInformation,
|
|
DWORD StatusInformationLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback routine that gets called at various times during the
|
|
processing of an asynchronous request.
|
|
|
|
Arguments:
|
|
|
|
pDavCallBackContext - The context structure to be set.
|
|
|
|
DavOperation - The Dav operation that will be called with this context.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
ASSERT(!"WinInet Callback should not be called");
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
DavCommonDispatch(
|
|
LPVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback routine that gets called at various times during the
|
|
processing of an asynchronous request.
|
|
|
|
Arguments:
|
|
|
|
Context - The DAV_USERMODE_WORKITEM value.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
DWORD WStatus = ERROR_SUCCESS;
|
|
PDAV_USERMODE_WORKITEM DavWorkItem = (PDAV_USERMODE_WORKITEM)Context;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavCommonDispatch: DavWorkItem = %08lx, DavOperation = %d,"
|
|
" WorkItemType = %d\n", DavWorkItem, DavWorkItem->DavOperation,
|
|
DavWorkItem->WorkItemType));
|
|
|
|
if (DavWorkItem->DavOperation <= DAV_CALLBACK_HTTP_SEND) {
|
|
|
|
WStatus = DavAsyncCommonStates(DavWorkItem, TRUE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch/DavAsyncCommonStates. WStatus = "
|
|
"%08lx\n", WStatus));
|
|
}
|
|
|
|
} else {
|
|
|
|
switch(DavWorkItem->WorkItemType) {
|
|
|
|
case UserModeCreateSrvCall: {
|
|
WStatus = DavAsyncCreateSrvCall(DavWorkItem, TRUE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch/DavAsyncCreateSrvCall. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UserModeCreateVNetRoot: {
|
|
WStatus = DavAsyncCreateVNetRoot(DavWorkItem, TRUE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch/DavAsyncCreateVNetRoot. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UserModeCreate: {
|
|
WStatus = DavAsyncCreate(DavWorkItem, TRUE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch/DavAsyncCreate. WStatus = %08lx.\n",
|
|
WStatus));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UserModeQueryDirectory: {
|
|
WStatus = DavAsyncQueryDirectory(DavWorkItem, TRUE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch/DavAsyncQueryDirectory. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UserModeReName: {
|
|
WStatus = DavAsyncReName(DavWorkItem, TRUE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch/DavAsyncReName. WStatus = %08lx.\n",
|
|
WStatus));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case UserModeSetFileInformation: {
|
|
ASSERT(FALSE);
|
|
}
|
|
break;
|
|
|
|
case UserModeClose: {
|
|
WStatus = DavAsyncClose(DavWorkItem, TRUE);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch/DavAsyncClose. WStatus = %08lx.\n",
|
|
WStatus));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: {
|
|
ASSERT(!"Invalid DavWorkItem->WorkItemType");
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavCommonDispatch: Invalid DavWorkItem->WorkItemType = %d.\n",
|
|
DavWorkItem->WorkItemType));
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavAsyncCommonStates(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
BOOLEAN CalledByCallBackThread
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to handle the common operations during the Async
|
|
server calls. To avoid duplicating the code in every Async operation like
|
|
CreateSrvCall, Create etc., the code handling the common states has been
|
|
consolidated into this routine.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
CalledByCallbackThread - TRUE, if this function was called by the thread
|
|
which picks of the DavWorkItem from the Callback
|
|
function. This happens when an Async WinInet call
|
|
returns ERROR_IO_PENDING and completes later.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
PUMRX_USERMODE_WORKITEM_HEADER UserWorkItem;
|
|
HINTERNET DavConnHandle = NULL, DavOpenHandle = NULL;
|
|
ULONG_PTR CallBackContext = (ULONG_PTR)0;
|
|
BOOL ReturnVal, didImpersonate = FALSE;
|
|
PWCHAR HTTPVerb = NULL;
|
|
PWCHAR ObjectName = NULL;
|
|
LPINTERNET_BUFFERS InternetBuffers = NULL;
|
|
DWORD SendEndRequestFlags = 0;
|
|
BOOL BStatus = FALSE;
|
|
PWCHAR PassportCookie = NULL;
|
|
|
|
UserWorkItem = (PUMRX_USERMODE_WORKITEM_HEADER)DavWorkItem;
|
|
|
|
//
|
|
// If we are using WinInet synchronously, the the flag value that is passed
|
|
// to HttpSendRequestExW and HttpEndRequestW is HSR_SYNC.
|
|
//
|
|
SendEndRequestFlags = HSR_SYNC;
|
|
|
|
switch (DavWorkItem->DavOperation) {
|
|
|
|
case DAV_CALLBACK_INTERNET_CONNECT: {
|
|
|
|
BOOL setEvt;
|
|
PPER_USER_ENTRY PerUserEntry = NULL;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncCommonStates: Entering DAV_CALLBACK_INTERNET_CONNECT.\n"));
|
|
|
|
//
|
|
// We need to now do somethings depending on the WorkItemType.
|
|
//
|
|
switch(DavWorkItem->WorkItemType) {
|
|
|
|
case UserModeCreateSrvCall: {
|
|
|
|
//
|
|
// Select the verb to be used.
|
|
//
|
|
HTTPVerb = L"OPTIONS";
|
|
|
|
ObjectName = L"/";
|
|
}
|
|
break;
|
|
|
|
case UserModeCreate: {
|
|
|
|
PerUserEntry = (PPER_USER_ENTRY)DavWorkItem->AsyncCreate.PerUserEntry;
|
|
|
|
//
|
|
// Select the verb to be used.
|
|
//
|
|
HTTPVerb = L"PROPFIND";
|
|
|
|
DavWorkItem->AsyncCreate.AsyncCreateState = AsyncCreatePropFind;
|
|
DavWorkItem->DavMinorOperation = DavMinorReadData;
|
|
DavWorkItem->AsyncCreate.DataBuff = NULL;
|
|
DavWorkItem->AsyncCreate.didRead = NULL;
|
|
DavWorkItem->AsyncCreate.Context1 = NULL;
|
|
DavWorkItem->AsyncCreate.Context2 = NULL;
|
|
|
|
ObjectName = DavWorkItem->AsyncCreate.RemPathName;
|
|
}
|
|
break;
|
|
|
|
case UserModeCreateVNetRoot: {
|
|
|
|
PDAV_USERMODE_CREATE_V_NET_ROOT_REQUEST CreateVNetRootRequest = NULL;
|
|
|
|
PerUserEntry = (PPER_USER_ENTRY)DavWorkItem->AsyncCreateVNetRoot.PerUserEntry;
|
|
|
|
//
|
|
// Get the request buffer from the DavWorkItem.
|
|
//
|
|
CreateVNetRootRequest = &(DavWorkItem->CreateVNetRootRequest);
|
|
|
|
//
|
|
// Select the verb to be used.
|
|
//
|
|
HTTPVerb = L"PROPFIND";
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
ObjectName = &(CreateVNetRootRequest->ShareName[1]);
|
|
DavPrint((DEBUG_MISC, "DavAsyncCommonStates: ObjectName = %ws\n", ObjectName));
|
|
}
|
|
break;
|
|
|
|
case UserModeQueryVolumeInformation: {
|
|
|
|
PerUserEntry = (PPER_USER_ENTRY)DavWorkItem->AsyncCreate.PerUserEntry;
|
|
//
|
|
// Select the verb to be used.
|
|
//
|
|
HTTPVerb = L"PROPFIND";
|
|
|
|
//
|
|
// The first character is a '\' which has to be stripped.
|
|
//
|
|
ObjectName = &(DavWorkItem->QueryVolumeInformationRequest.ShareName[1]);
|
|
DavPrint((DEBUG_MISC, "DavAsyncCommonStates: ObjectName = %ws\n", ObjectName));
|
|
}
|
|
break;
|
|
|
|
default: {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates: Invalid DavWorkItem->WorkItemType "
|
|
"= %d.\n", DavWorkItem->WorkItemType));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// If the WorkItem type is UserModeCreateSrvCall, then we don't have
|
|
// a PerUserEntry. In this case, the DavConnHandle is stored in the
|
|
// DavWorkItem structure.
|
|
//
|
|
if ( (DavWorkItem->WorkItemType == UserModeCreate) ||
|
|
(DavWorkItem->WorkItemType == UserModeCreateVNetRoot)||
|
|
(DavWorkItem->WorkItemType == UserModeQueryVolumeInformation)) {
|
|
DavConnHandle = PerUserEntry->DavConnHandle;
|
|
} else {
|
|
ASSERT(DavWorkItem->WorkItemType == UserModeCreateSrvCall);
|
|
DavConnHandle = DavWorkItem->AsyncCreateSrvCall.DavConnHandle;
|
|
}
|
|
|
|
|
|
if ( (DavWorkItem->WorkItemType == UserModeCreate) ||
|
|
(DavWorkItem->WorkItemType == UserModeCreateVNetRoot) ) {
|
|
|
|
//
|
|
// We are in InternetConnect callback state. We need to cache this Conn
|
|
// handle away in the PerUserEntry of the user which hangs off the
|
|
// server hash entry. We need to take a lock on the table before doing
|
|
// this.
|
|
//
|
|
EnterCriticalSection( &(HashServerEntryTableLock) );
|
|
|
|
//
|
|
// Since the handle was created successfully, we store ERROR_SUCCESS
|
|
// in the status field of the PerUserEntry.
|
|
//
|
|
PerUserEntry->ErrorStatus = ERROR_SUCCESS;
|
|
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncCommonStates: PerUserEntry->DavConnHandle = "
|
|
"%08lx.\n", PerUserEntry->DavConnHandle));
|
|
|
|
//
|
|
// Set the state of the user entry to initialized.
|
|
//
|
|
PerUserEntry->UserEntryState = UserEntryInitialized;
|
|
|
|
//
|
|
// Signal the event of the user entry to wake up the threads which
|
|
// might be waiting for this to happen.
|
|
//
|
|
setEvt = SetEvent(PerUserEntry->UserEventHandle);
|
|
if (!setEvt) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/SetEvent. Error Val = %d.\n",
|
|
WStatus));
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// This was acquired above.
|
|
//
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
|
|
}
|
|
|
|
//
|
|
// The next async operation is http open.
|
|
//
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_OPEN;
|
|
|
|
//
|
|
// Convert the unicode object name to a UTF-8 URL format.
|
|
// Space and other white characters will remain untouched - these should
|
|
// be taken care of by wininet calls.
|
|
//
|
|
BStatus = DavHttpOpenRequestW(DavConnHandle,
|
|
(LPWSTR)HTTPVerb,
|
|
(LPWSTR)ObjectName,
|
|
L"HTTP/1.1",
|
|
NULL,
|
|
NULL,
|
|
INTERNET_FLAG_KEEP_CONNECTION |
|
|
INTERNET_FLAG_NO_COOKIES |
|
|
INTERNET_FLAG_NO_CACHE_WRITE |
|
|
INTERNET_FLAG_RESYNCHRONIZE,
|
|
CallBackContext,
|
|
L"DavAsyncCommonStates",
|
|
&DavOpenHandle);
|
|
if(BStatus == FALSE) {
|
|
WStatus = GetLastError();
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (DavOpenHandle == NULL) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpOpenRequest. Error Val = %d\n",
|
|
WStatus));
|
|
}
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
//
|
|
// Lack of break is intentional.
|
|
//
|
|
|
|
case DAV_CALLBACK_HTTP_OPEN: {
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncCommonStates: Entering DAV_CALLBACK_HTTP_OPEN.\n"));
|
|
|
|
//
|
|
// Get the handle from http open. If DavOpenHandle is NULL, it means
|
|
// that either the async request HttpOpenRequestW returned ERROR_IO_PENDING
|
|
// and that the handle will be stored in DavWorkItem->pAsyncResult->
|
|
// dwResult (implies CalledByCallBackThread == TRUE) or that the function
|
|
// that called this function cached it in the DavWorkItm structure.
|
|
//
|
|
|
|
switch(DavWorkItem->WorkItemType) {
|
|
|
|
case UserModeCreateSrvCall: {
|
|
|
|
if (DavOpenHandle == NULL) {
|
|
|
|
//
|
|
// HttpOpen handle was cached away in the DavWorkItem by the
|
|
// function that called this function.
|
|
//
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncCreateSrvCall.DavOpenHandle;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
|
|
DavWorkItem->AsyncCreateSrvCall.DavOpenHandle = DavOpenHandle;
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case UserModeCreateVNetRoot: {
|
|
|
|
if (DavOpenHandle == NULL) {
|
|
|
|
//
|
|
// HttpOpen handle was cached away in the DavWorkItem by the
|
|
// function that called this function.
|
|
//
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncCreateVNetRoot.DavOpenHandle;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
|
|
DavWorkItem->AsyncCreateVNetRoot.DavOpenHandle = DavOpenHandle;
|
|
|
|
}
|
|
|
|
//
|
|
// Since all that we need is information about this share, set the
|
|
// depth header to 0. This way the PROPFIND that we send will get
|
|
// back the properties of just this share.
|
|
//
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Depth: 0\n",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case UserModeCreate: {
|
|
|
|
if (DavOpenHandle == NULL) {
|
|
|
|
//
|
|
// HttpOpen handle was cached away in the DavWorkItem by the
|
|
// function that called this function.
|
|
//
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncCreate.DavOpenHandle;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
|
|
DavWorkItem->AsyncCreate.DavOpenHandle = DavOpenHandle;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a PROPFIND, set the depth header to 0. This matters
|
|
// when the open is being done for a directory. We only need the
|
|
// properties of the directory and not the files it contains.
|
|
//
|
|
if (DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreatePropFind ||
|
|
DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreateQueryParentDirectory) {
|
|
|
|
PDAV_USERMODE_CREATE_REQUEST CreateRequest = &(DavWorkItem->CreateRequest);
|
|
|
|
if (DavWorkItem->AsyncCreate.AsyncCreateState == AsyncCreatePropFind &&
|
|
CreateRequest->CreateOptions & FILE_DIRECTORY_FILE &&
|
|
(CreateRequest->CreateOptions & FILE_DELETE_ON_CLOSE ||
|
|
CreateRequest->DesiredAccess & DELETE)) {
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Depth: 1\n",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
} else {
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Depth: 0\n",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case UserModeQueryDirectory: {
|
|
|
|
if (DavOpenHandle == NULL) {
|
|
|
|
//
|
|
// HttpOpen handle was cached away in the DavWorkItem by the
|
|
// function that called this function.
|
|
//
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
|
|
DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle = DavOpenHandle;
|
|
|
|
}
|
|
|
|
if (DavWorkItem->AsyncQueryDirectoryCall.NoWildCards) {
|
|
|
|
//
|
|
// If there are no wild cards, we have a filename and we set
|
|
// the depth to 0.
|
|
//
|
|
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Depth: 0\n",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// On a QueryDirectory, we do a PROPFIND on the directory. Since we
|
|
// only need to get the properties of files within the first level
|
|
// of the directory, we set the depth header of the request to 1.
|
|
//
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Depth: 1\n",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case UserModeQueryVolumeInformation: {
|
|
|
|
//
|
|
// Since all that we need is information about this share, set the
|
|
// depth header to 0. This way the PROPFIND that we send will get
|
|
// back the properties of just this share.
|
|
//
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Depth: 0\n",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle = DavOpenHandle;
|
|
|
|
}
|
|
break;
|
|
|
|
case UserModeReName: {
|
|
|
|
PDAV_USERMODE_RENAME_REQUEST DavReNameRequest = NULL;
|
|
|
|
if (DavOpenHandle == NULL) {
|
|
|
|
//
|
|
// HttpOpen handle was cached away in the DavWorkItem by the
|
|
// function that called this function.
|
|
//
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncReName.DavOpenHandle;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
|
|
DavWorkItem->AsyncReName.DavOpenHandle = DavOpenHandle;
|
|
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncCommonStates: Rename!! HeaderBuff: %ws\n",
|
|
DavWorkItem->AsyncReName.HeaderBuff));
|
|
|
|
//
|
|
// We are doing a "MOVE" and hence we need to set the DAV header
|
|
// "Destination:". This has to be the URI of the new file.
|
|
//
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
DavWorkItem->AsyncReName.HeaderBuff,
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Get the request buffer pointer from the DavWorkItem.
|
|
//
|
|
DavReNameRequest = &(DavWorkItem->ReNameRequest);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncCommonStates: Rename!! ReplaceIfExists: %d\n",
|
|
DavReNameRequest->ReplaceIfExists));
|
|
|
|
//
|
|
// We need to set the Overwrite header in this MOVE request. This
|
|
// determines what is done if the destination file already exists.
|
|
// If the ReplaceIfExists is set to TRUE, then we set the Overwrite
|
|
// header to T (TRUE) else F (FALSE).
|
|
//
|
|
if (DavReNameRequest->ReplaceIfExists) {
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Overwrite: T",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
} else {
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"Overwrite: F",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If OpaqueLockToken is non-NULL, then we need to add this header
|
|
// to the request being sent out.
|
|
//
|
|
if (DavReNameRequest->OpaqueLockToken != NULL) {
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
DavReNameRequest->OpaqueLockToken,
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case UserModeClose: {
|
|
|
|
PDAV_USERMODE_CLOSE_REQUEST CloseRequest = &(DavWorkItem->CloseRequest);
|
|
|
|
if (DavOpenHandle == NULL) {
|
|
|
|
//
|
|
// HttpOpen handle was cached away in the DavWorkItem by the
|
|
// function that called this function.
|
|
//
|
|
|
|
DavOpenHandle = DavWorkItem->AsyncClose.DavOpenHandle;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We need to cache the DavOpenHandle in the DavWorkItem.
|
|
//
|
|
|
|
DavWorkItem->AsyncClose.DavOpenHandle = DavOpenHandle;
|
|
|
|
}
|
|
|
|
if (DavWorkItem->AsyncClose.DataBuff != NULL) {
|
|
|
|
ASSERT(DavWorkItem->DavMinorOperation == DavMinorPutFile);
|
|
|
|
if (DavWorkItem->AsyncClose.InternetBuffers == NULL) {
|
|
|
|
InternetBuffers = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
|
sizeof(INTERNET_BUFFERS) );
|
|
if (InternetBuffers == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavWorkItem->AsyncClose.InternetBuffers = InternetBuffers;
|
|
|
|
InternetBuffers->dwStructSize = sizeof(INTERNET_BUFFERS);
|
|
InternetBuffers->Next = NULL;
|
|
InternetBuffers->lpcszHeader = NULL;
|
|
InternetBuffers->dwHeadersLength = 0;
|
|
InternetBuffers->dwBufferTotal = 0;
|
|
InternetBuffers->lpvBuffer = DavWorkItem->AsyncClose.DataBuff;
|
|
InternetBuffers->dwBufferLength = (DWORD)DavWorkItem->AsyncClose.DataBuffSizeInBytes;
|
|
InternetBuffers->dwBufferTotal = 0;
|
|
InternetBuffers->dwOffsetLow = 0;
|
|
InternetBuffers->dwOffsetHigh = 0;
|
|
|
|
} else {
|
|
|
|
InternetBuffers = DavWorkItem->AsyncClose.InternetBuffers;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DavWorkItem->AsyncClose.InternetBuffers = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// If OpaqueLockToken is non-NULL, then we need to add this header
|
|
// to the request being sent out.
|
|
//
|
|
if (CloseRequest->OpaqueLockToken != NULL) {
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
CloseRequest->OpaqueLockToken,
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
default: {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates: Invalid DavWorkItem->WorkItemType "
|
|
"= %d.\n", DavWorkItem->WorkItemType));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncCommonStates: DavOpenHandle = %08lx.\n", DavOpenHandle));
|
|
|
|
//
|
|
// In case of UserModeCreateSrvCall, we don't have a passport cookie yet.
|
|
//
|
|
if (DavWorkItem->WorkItemType != UserModeCreateSrvCall) {
|
|
WStatus = DavAttachPassportCookie(DavWorkItem, DavOpenHandle, &PassportCookie);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to add the header "translate:f" to tell IIS that it should
|
|
// allow the user to excecute this VERB on the specified path which it
|
|
// would not allow (in some cases) otherwise. Finally, there is a special
|
|
// flag in the metabase to allow for uploading of "dangerous" content
|
|
// (anything that can be run on the server). This is the ScriptSourceAccess
|
|
// flag in the UI or the AccessSource flag in the metabase. You will need
|
|
// to set this bit to true as well as correct NT ACLs in order to be able
|
|
// to upload .exes or anything executable.
|
|
//
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
L"translate: f\n",
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpAddRequestHeadersW. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
WStatus = DavInternetSetOption(DavWorkItem, DavOpenHandle);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Need to change the DavOperation field before submitting another
|
|
// asynchronous request. The next async operation is http send.
|
|
//
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_SEND;
|
|
|
|
//
|
|
// We need the following symbol if we are using WinInet synchronously.
|
|
//
|
|
|
|
RESEND_THE_REQUEST:
|
|
|
|
//
|
|
// Send the request to the server.
|
|
//
|
|
ReturnVal = HttpSendRequestExW(DavOpenHandle,
|
|
InternetBuffers,
|
|
NULL,
|
|
SendEndRequestFlags,
|
|
CallBackContext);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpSendRequest. Error Val = %d\n",
|
|
WStatus));
|
|
}
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
}
|
|
//
|
|
// Lack of break is intentional.
|
|
//
|
|
|
|
case DAV_CALLBACK_HTTP_SEND: {
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAsyncCommonStates: Entering DAV_CALLBACK_HTTP_SEND.\n"));
|
|
|
|
switch(DavWorkItem->WorkItemType) {
|
|
|
|
case UserModeCreateSrvCall: {
|
|
DavOpenHandle = DavWorkItem->AsyncCreateSrvCall.DavOpenHandle;
|
|
}
|
|
break;
|
|
|
|
case UserModeCreateVNetRoot: {
|
|
DavOpenHandle = DavWorkItem->AsyncCreateVNetRoot.DavOpenHandle;
|
|
}
|
|
break;
|
|
|
|
case UserModeCreate: {
|
|
DavOpenHandle = DavWorkItem->AsyncCreate.DavOpenHandle;
|
|
}
|
|
break;
|
|
|
|
case UserModeQueryDirectory: {
|
|
DavOpenHandle = DavWorkItem->AsyncQueryDirectoryCall.DavOpenHandle;
|
|
}
|
|
break;
|
|
case UserModeQueryVolumeInformation: {
|
|
DavOpenHandle = DavWorkItem->AsyncQueryVolumeInformation.DavOpenHandle;
|
|
}
|
|
break;
|
|
|
|
case UserModeReName: {
|
|
DavOpenHandle = DavWorkItem->AsyncReName.DavOpenHandle;
|
|
}
|
|
break;
|
|
|
|
case UserModeClose: {
|
|
DavOpenHandle = DavWorkItem->AsyncClose.DavOpenHandle;
|
|
}
|
|
break;
|
|
|
|
default: {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates: Invalid DavWorkItem->WorkItemType "
|
|
"= %d.\n", DavWorkItem->WorkItemType));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Need to change the DavOperation field before submitting another
|
|
// asynchronous request. The next operation is http end.
|
|
//
|
|
DavWorkItem->DavOperation = DAV_CALLBACK_HTTP_END;
|
|
|
|
|
|
//
|
|
// Issue the End request once send request completes.
|
|
//
|
|
ReturnVal = HttpEndRequestW(DavOpenHandle,
|
|
NULL,
|
|
SendEndRequestFlags,
|
|
CallBackContext);
|
|
if (!ReturnVal) {
|
|
|
|
WStatus = GetLastError();
|
|
|
|
//
|
|
// If the error we got back is ERROR_INTERNET_FORCE_RETRY, then WinInet
|
|
// is trying to authenticate itself with the server. If we get back
|
|
// ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION, WinInet is expecting us to
|
|
// confirm that the redirect needs to be followed. In these scenarios,
|
|
// we need to repeat the HttpSend and HttpEnd request calls.
|
|
//
|
|
if (WStatus == ERROR_INTERNET_FORCE_RETRY || WStatus == ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION) {
|
|
goto RESEND_THE_REQUEST;
|
|
}
|
|
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/HttpEndRequestW. Error Val = %d\n",
|
|
WStatus));
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
} else {
|
|
|
|
PWCHAR Cookie = NULL;
|
|
|
|
DavQueryPassportCookie(DavOpenHandle,&Cookie);
|
|
|
|
if (Cookie) {
|
|
DavPrint((DEBUG_MISC,
|
|
"Passport Cookie saved for PUE %x\n",DavWorkItem->ServerUserEntry.PerUserEntry));
|
|
//
|
|
// Set or renew passport cookie
|
|
//
|
|
EnterCriticalSection(&DavPassportLock);
|
|
|
|
if (DavWorkItem->ServerUserEntry.PerUserEntry) {
|
|
if (DavWorkItem->ServerUserEntry.PerUserEntry->Cookie) {
|
|
LocalFree(DavWorkItem->ServerUserEntry.PerUserEntry->Cookie);
|
|
}
|
|
|
|
DavWorkItem->ServerUserEntry.PerUserEntry->Cookie = Cookie;
|
|
}
|
|
|
|
LeaveCriticalSection(&DavPassportLock);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Now we need to call the Async routines that handle WorkItemType
|
|
// specific things.
|
|
//
|
|
switch(DavWorkItem->WorkItemType) {
|
|
|
|
case UserModeCreateSrvCall: {
|
|
WStatus = DavAsyncCreateSrvCall(DavWorkItem, CalledByCallBackThread);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/DavAsyncCreateSrvCall. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
return WStatus;
|
|
}
|
|
break;
|
|
|
|
case UserModeCreateVNetRoot: {
|
|
WStatus = DavAsyncCreateVNetRoot(DavWorkItem, CalledByCallBackThread);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/DavAsyncCreateVNetRoot. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
return WStatus;
|
|
}
|
|
break;
|
|
|
|
case UserModeCreate: {
|
|
WStatus = DavAsyncCreate(DavWorkItem, CalledByCallBackThread);
|
|
if (WStatus != ERROR_SUCCESS &&
|
|
WStatus != ERROR_IO_PENDING &&
|
|
WStatus != ERROR_FILE_NOT_FOUND) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/DavAsyncCreate. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
return WStatus;
|
|
}
|
|
break;
|
|
|
|
case UserModeQueryDirectory: {
|
|
WStatus = DavAsyncQueryDirectory(DavWorkItem, CalledByCallBackThread);
|
|
if (WStatus != ERROR_SUCCESS &&
|
|
WStatus != ERROR_IO_PENDING &&
|
|
WStatus != ERROR_FILE_NOT_FOUND) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/DavAsyncQueryDirectory. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
return WStatus;
|
|
}
|
|
break;
|
|
case UserModeQueryVolumeInformation: {
|
|
WStatus = DavAsyncQueryVolumeInformation(DavWorkItem, CalledByCallBackThread);
|
|
if (WStatus != ERROR_SUCCESS &&
|
|
WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/DavAsyncQueryVolumeInformation. WStatus = "
|
|
"%08lx.\n", WStatus));
|
|
}
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
return WStatus;
|
|
}
|
|
break;
|
|
|
|
case UserModeReName: {
|
|
WStatus = DavAsyncReName(DavWorkItem, CalledByCallBackThread);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/DavAsyncReName. WStatus = %08lx.\n",
|
|
WStatus));
|
|
}
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
return WStatus;
|
|
}
|
|
break;
|
|
|
|
case UserModeClose: {
|
|
WStatus = DavAsyncClose(DavWorkItem, CalledByCallBackThread);
|
|
if (WStatus != ERROR_SUCCESS && WStatus != ERROR_IO_PENDING) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/DavAsyncClose. WStatus = %08lx.\n",
|
|
WStatus));
|
|
}
|
|
if (didImpersonate) {
|
|
RevertToSelf();
|
|
}
|
|
return WStatus;
|
|
}
|
|
break;
|
|
|
|
default: {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates: Invalid DavWorkItem->WorkItemType "
|
|
"= %d.\n", DavWorkItem->WorkItemType));
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
default: {
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates: Invalid DavWorkItem->DavOperation = %d.\n",
|
|
DavWorkItem->DavOperation));
|
|
}
|
|
break;
|
|
|
|
} // End of switch.
|
|
|
|
EXIT_THE_FUNCTION:
|
|
//
|
|
// If we did impersonate, we need to revert back.
|
|
//
|
|
if (didImpersonate) {
|
|
ULONG RStatus;
|
|
RStatus = UMReflectorRevert(UserWorkItem);
|
|
if (RStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAsyncCommonStates/UMReflectorRevert. Error Val = %d\n",
|
|
RStatus));
|
|
}
|
|
}
|
|
|
|
if (PassportCookie) {
|
|
LocalFree(PassportCookie);
|
|
}
|
|
|
|
|
|
//
|
|
// If we are using WinInet synchronously, then we should never get back
|
|
// ERROR_IO_PENDING from WinInet.
|
|
//
|
|
ASSERT(WStatus != ERROR_IO_PENDING);
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DavFsSetTheDavCallBackContext(
|
|
IN OUT PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the callback context to be sent in subsequent asynchronous
|
|
request.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The work item that came down from the kernel. This is also
|
|
used as the callbackcontext.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
BOOL ReturnVal;
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Make the handles invalid to begin with.
|
|
//
|
|
DavWorkItem->ImpersonationHandle = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Get the handle used to impersonate this thread.
|
|
//
|
|
ReturnVal = OpenThreadToken(GetCurrentThread(),
|
|
TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE,
|
|
FALSE,
|
|
&(DavWorkItem->ImpersonationHandle));
|
|
if (!ReturnVal) {
|
|
DavWorkItem->ImpersonationHandle = INVALID_HANDLE_VALUE;
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFsSetTheDavCallBackContext/OpenThreadToken. Operation = %d"
|
|
", Error Val = %d\n", DavWorkItem->WorkItemType, WStatus));
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavFsFinalizeTheDavCallBackContext(
|
|
IN PDAV_USERMODE_WORKITEM DavWorkItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finalizes the callback context which was used for some
|
|
asynchronous request. This basically amounts to freeing up any resources
|
|
that were acquired by the context. Its called when the request associated
|
|
with this context completes.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The context structure to be set.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
BOOL ReturnVal;
|
|
ULONG WStatus;
|
|
|
|
//
|
|
// If the Impersonation handle was initialized, close it.
|
|
//
|
|
if (DavWorkItem->ImpersonationHandle != INVALID_HANDLE_VALUE) {
|
|
ReturnVal = CloseHandle(DavWorkItem->ImpersonationHandle);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"ERROR: DavFsFinalizeTheDavCallBackContext/CloseHandle."
|
|
"(Impersonation) Error Val = %d.\n", WStatus));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DavDoesUserEntryExist(
|
|
IN PWCHAR ServerName,
|
|
IN ULONG ServerID,
|
|
IN PLUID LogonID,
|
|
OUT PPER_USER_ENTRY *PerUserEntry,
|
|
OUT PHASH_SERVER_ENTRY *ServerHashEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine searches for a per user entry in the list of per user entries
|
|
of a server entry in the hash table. Note that the caller should take a
|
|
lock on the ServerHashTable before calling this routine.
|
|
|
|
Arguments:
|
|
|
|
ServerName - The server name whose per user entries should be searched.
|
|
|
|
ServerID - The unique ID associated with this server. This ID is generated
|
|
during the CreateSrvCall stage.
|
|
|
|
LogonID - The LogonID of the user/session to be searched.
|
|
|
|
PerUserEntry - The PerUserEntry of this user which hangs of the server.
|
|
|
|
ServerHashEntry - The ServerHashEntry for this server. This is used to add
|
|
the new user entry to its list if an entry for this user
|
|
does not exist.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The entry was found and FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOL ReturnVal = FALSE;
|
|
BOOL isPresent = FALSE;
|
|
ULONG ServerHashID;
|
|
PLIST_ENTRY listServerEntry, listUserEntry;
|
|
PHASH_SERVER_ENTRY HashEntry = NULL;
|
|
PPER_USER_ENTRY UsrEntry = NULL;
|
|
|
|
//
|
|
// IMPORTANT!!!! The caller should take a lock on the global ServerHashTable
|
|
// before calling this routine.
|
|
//
|
|
|
|
ASSERT(ServerName != NULL);
|
|
|
|
DavPrint((DEBUG_MISC, "DavDoesUserEntryExist: ServerName: %ws, ServerID: "
|
|
"%d.\n", ServerName, ServerID));
|
|
|
|
//
|
|
// Finally, get the hash ID and insert this new entry into the global server
|
|
// entry hash table.
|
|
//
|
|
ServerHashID = DavHashTheServerName(ServerName);
|
|
ASSERT(ServerHashID != SERVER_TABLE_SIZE);
|
|
|
|
//
|
|
// Search the hash table at this index to see if an entry for this server
|
|
// exists.
|
|
//
|
|
listServerEntry = ServerHashTable[ServerHashID].Flink;
|
|
while ( listServerEntry != &(ServerHashTable[ServerHashID]) ) {
|
|
//
|
|
// Get the pointer to the HASH_SERVER_ENTRY structure.
|
|
//
|
|
HashEntry = CONTAINING_RECORD(listServerEntry,
|
|
HASH_SERVER_ENTRY,
|
|
ServerListEntry);
|
|
//
|
|
// Check to see if this entry is for the server in question.
|
|
//
|
|
if ( ServerID == HashEntry->ServerID ) {
|
|
//
|
|
// If the ID's match, the server names should match.
|
|
//
|
|
ASSERT( wcscmp(ServerName, HashEntry->ServerName) == 0 );
|
|
isPresent = TRUE;
|
|
DavPrint((DEBUG_MISC, "DavDoesUserEntryExist: ServerName: %ws found"
|
|
".\n", ServerName));
|
|
break;
|
|
}
|
|
listServerEntry = listServerEntry->Flink;
|
|
}
|
|
|
|
//
|
|
// If the ServerHashEntry does not exist, then return FALSE;
|
|
//
|
|
if (!isPresent) {
|
|
DavPrint((DEBUG_MISC,
|
|
"DavDoesUserEntryExist: ServerHashEntry not found. %ws\n",
|
|
ServerName));
|
|
*ServerHashEntry = NULL;
|
|
*PerUserEntry = NULL;
|
|
return (isPresent);
|
|
}
|
|
|
|
//
|
|
// Return the ServerHashEntry. This will be used to add the new user
|
|
// entry to the user list of this server.
|
|
//
|
|
*ServerHashEntry = HashEntry;
|
|
DavPrint((DEBUG_MISC, "DavDoesUserEntryExist: ServerHashEntry = %08lx\n",
|
|
HashEntry));
|
|
|
|
//
|
|
// Now, search the "per user entries" that hang off this server entry to
|
|
// see if an entry for this user exists.
|
|
//
|
|
listUserEntry = HashEntry->PerUserEntry.Flink;
|
|
while ( listUserEntry != &(HashEntry->PerUserEntry) ) {
|
|
//
|
|
// Get the pointer to the HASH_SERVER_ENTRY structure.
|
|
//
|
|
UsrEntry = CONTAINING_RECORD(listUserEntry,
|
|
PER_USER_ENTRY,
|
|
UserEntry);
|
|
//
|
|
// Check to see if this entry is for the user in question. We do this
|
|
// by comparing the LogonID values.
|
|
//
|
|
if ( (UsrEntry->LogonID.LowPart == LogonID->LowPart) &&
|
|
(UsrEntry->LogonID.HighPart == LogonID->HighPart) ) {
|
|
DavPrint((DEBUG_MISC, "DavDoesUserEntryExist: User found.\n"));
|
|
ReturnVal = TRUE;
|
|
break;
|
|
}
|
|
listUserEntry = listUserEntry->Flink;
|
|
}
|
|
|
|
if (!ReturnVal) {
|
|
DavPrint((DEBUG_MISC, "DavDoesUserEntryExist: User not found.\n"));
|
|
*PerUserEntry = NULL;
|
|
return (ReturnVal);
|
|
}
|
|
|
|
//
|
|
// Since the server has been found, return the PerUserEntry.
|
|
//
|
|
*PerUserEntry = UsrEntry;
|
|
DavPrint((DEBUG_MISC, "DavDoesUserEntryExist: UsrEntry = %08lx\n", UsrEntry));
|
|
|
|
return (ReturnVal);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DavFinalizePerUserEntry(
|
|
PPER_USER_ENTRY *PUE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrements the reference count of the user entry by one. If
|
|
the count reduces to zero, the entry is freed.
|
|
|
|
Arguments:
|
|
|
|
PUE - The per user entry to be finalized.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The user entry was finalized (freed).
|
|
|
|
FALSE - Was not since the ref count was > 0.
|
|
|
|
--*/
|
|
{
|
|
PPER_USER_ENTRY PerUserEntry = *PUE;
|
|
BOOL retVal = TRUE;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFinalizePerUserEntry: Finalizing PerUserEntry: %08lx.\n",
|
|
PerUserEntry));
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFinalizePerUserEntry: UserEntryRefCount = %d, LogonId.LowPart = %d,"
|
|
" LogonId.HighPart = %d\n", PerUserEntry->UserEntryRefCount,
|
|
PerUserEntry->LogonID.LowPart, PerUserEntry->LogonID.HighPart));
|
|
|
|
//
|
|
// Before we modify the reference count, we need to take a lock.
|
|
//
|
|
EnterCriticalSection( &(HashServerEntryTableLock) );
|
|
|
|
PerUserEntry->UserEntryRefCount--;
|
|
|
|
//
|
|
// If we had the last reference, we need to do the following :
|
|
// 1. Remove the entry from the servers list.
|
|
// 2. Close any open handles stored or cached in the entry.
|
|
// 3. Free the cookie, if we allocated one for Passport Auth and,
|
|
// 4. Free the entry.
|
|
//
|
|
if (PerUserEntry->UserEntryRefCount == 0) {
|
|
|
|
HLOCAL FreeHandle;
|
|
ULONG FreeStatus;
|
|
BOOL CloseStatus;
|
|
PHASH_SERVER_ENTRY ServerHashEntry = NULL;
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFinalizePerUserEntry: Finalized!!! LogonId.LowPart = %d, LogonId.HighPart = %d\n",
|
|
PerUserEntry->LogonID.LowPart, PerUserEntry->LogonID.HighPart));
|
|
|
|
ServerHashEntry = PerUserEntry->ServerHashEntry;
|
|
|
|
//
|
|
// Remove the entry from the servers list.
|
|
//
|
|
RemoveEntryList( &(PerUserEntry->UserEntry) );
|
|
|
|
//
|
|
// When this PerUserEntry was created, we took a reference on the
|
|
// ServerHashEntry. We need to remove it now. Also, if the reference
|
|
// on the ServerHashEntry goes to 0, we need to put it in the list of
|
|
// "ToBeFinalized" ServerHashEntries.
|
|
//
|
|
ServerHashEntry->ServerEntryRefCount -= 1;
|
|
|
|
if (ServerHashEntry->ServerEntryRefCount == 0) {
|
|
|
|
ServerHashEntry->TimeValueInSec = time(NULL);
|
|
|
|
//
|
|
// Now move this server entry from the hash table to the
|
|
// "to be finalized" list.
|
|
//
|
|
RemoveEntryList( &(ServerHashEntry->ServerListEntry) );
|
|
InsertHeadList( &(ToBeFinalizedServerEntries),
|
|
&(ServerHashEntry->ServerListEntry) );
|
|
|
|
}
|
|
|
|
//
|
|
// If we created the event handle, we need to close it now.
|
|
//
|
|
if (PerUserEntry->UserEventHandle != NULL) {
|
|
CloseStatus = CloseHandle(PerUserEntry->UserEventHandle);
|
|
if (!CloseStatus) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFinalizePerUserEntry/CloseHandle. Error Val ="
|
|
" %d.\n", FreeStatus));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we created the DavConnHandle, we need to close it now.
|
|
//
|
|
if (PerUserEntry->DavConnHandle != NULL) {
|
|
CloseStatus = InternetCloseHandle(PerUserEntry->DavConnHandle);
|
|
if (!CloseStatus) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFinalizePerUserEntry/InternetCloseHandle. "
|
|
"Error Val = %d.\n", FreeStatus));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we allocated memory for storing the cookies, we need to free it.
|
|
//
|
|
if (PerUserEntry->Cookie) {
|
|
SecureZeroMemory(PerUserEntry->Cookie, ((wcslen(PerUserEntry->Cookie) + 1) * sizeof(WCHAR)));
|
|
LocalFree(PerUserEntry->Cookie);
|
|
PerUserEntry->Cookie = NULL;
|
|
}
|
|
|
|
if (PerUserEntry->UserName) {
|
|
LocalFree(PerUserEntry->UserName);
|
|
PerUserEntry->UserName = NULL;
|
|
}
|
|
|
|
if (PerUserEntry->Password) {
|
|
SecureZeroMemory(PerUserEntry->Password, PerUserEntry->BlockSizeInBytes);
|
|
LocalFree(PerUserEntry->Password);
|
|
PerUserEntry->Password = NULL;
|
|
}
|
|
|
|
//
|
|
// Finally, free the entry.
|
|
//
|
|
FreeHandle = LocalFree((HLOCAL)PerUserEntry);
|
|
if (FreeHandle != NULL) {
|
|
FreeStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFinalizePerUserEntry/LocalFree. Error Val = %d.\n",
|
|
FreeStatus));
|
|
}
|
|
|
|
//
|
|
// Set the entry to NULL. Just in case !!!
|
|
//
|
|
*PUE = NULL;
|
|
|
|
} else {
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFinalizePerUserEntry: Did not finalize %08lx. RefCount "
|
|
"= %d\n", PerUserEntry, PerUserEntry->UserEntryRefCount));
|
|
|
|
retVal = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Free the lock before leaving.
|
|
//
|
|
LeaveCriticalSection( &(HashServerEntryTableLock) );
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SetupRpcServer(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets up the RPC server of the WebClient service.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS rpcErr;
|
|
RPC_BINDING_VECTOR *BindingVector = NULL;
|
|
|
|
rpcErr = RpcServerRegisterIf(davclntrpc_ServerIfHandle, NULL, NULL);
|
|
if (rpcErr != RPC_S_OK) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"SetupRpcServer/RpcServerRegisterIf: rpcErr = %08lx\n",
|
|
rpcErr));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
rpcErr = RpcServerUseProtseqW(L"ncalrpc",
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
NULL);
|
|
if (rpcErr != RPC_S_OK) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"SetupRpcServer/RpcServerUseProtseqEp: rpcErr = %08lx\n",
|
|
rpcErr));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
rpcErr = RpcServerInqBindings( &(BindingVector) );
|
|
if (rpcErr != RPC_S_OK) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"SetupRpcServer/RpcServerInqBindings: rpcErr = %08lx\n",
|
|
rpcErr));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
rpcErr = RpcEpRegister(davclntrpc_ServerIfHandle,
|
|
BindingVector,
|
|
NULL,
|
|
L"DAV RPC SERVICE");
|
|
if (rpcErr != RPC_S_OK) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"SetupRpcServer/RpcEpRegister: rpcErr = %08lx\n",
|
|
rpcErr));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
rpcErr = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
|
|
if (rpcErr != RPC_S_OK) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"SetupRpcServer/RpcServerListen: rpcErr = %08lx\n", rpcErr));
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (BindingVector) {
|
|
RpcBindingVectorFree( &(BindingVector) );
|
|
}
|
|
|
|
//
|
|
// Luckily for us, RPC errors simply map into the Win32 error space.
|
|
// If that ever changes, we need to make the mapping a bit more complex.
|
|
//
|
|
return (DWORD) rpcErr;
|
|
}
|
|
|
|
|
|
DWORD
|
|
StopRpcServer(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine stops the RPC server of the WebClient service.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS rpcErr;
|
|
|
|
rpcErr = RpcMgmtStopServerListening(NULL);
|
|
|
|
if (rpcErr != RPC_S_OK) {
|
|
DavPrint((DEBUG_RPC,
|
|
"WebClient received err 0x%x during "
|
|
"RpcMgmtStopServerListening.\n", rpcErr));
|
|
}
|
|
|
|
rpcErr = RpcServerUnregisterIf(davclntrpc_ServerIfHandle, 0, TRUE);
|
|
|
|
if (rpcErr != RPC_S_OK) {
|
|
DavPrint((DEBUG_RPC,
|
|
"WebClient received err 0x%x during RpcServerUnregisterIf.\n",
|
|
rpcErr));
|
|
}
|
|
|
|
return (DWORD) rpcErr;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DavQueryAndParseResponse(
|
|
HINTERNET DavOpenHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calls DavQueryAndParseResponseEx to map the Http/Dav response
|
|
to the Win32 error code.
|
|
|
|
Arguments:
|
|
|
|
DavOpenHandle - The handle created by HttpOpenRequest on which the request
|
|
was sent.
|
|
|
|
Return Value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
WStatus = DavQueryAndParseResponseEx(DavOpenHandle, NULL);
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DavQueryAndParseResponseEx(
|
|
IN HINTERNET DavOpenHandle,
|
|
OUT PULONG HttpResponseStatus OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function queries the response header for the status value returned
|
|
from the server. It then maps the status to a Win32 error code and returns
|
|
it to the caller. We added this function becuase some callers may be
|
|
interested in special casing some of the Http/Dav responses. Before this we
|
|
just had the DavQueryAndParseResponse function.
|
|
|
|
Arguments:
|
|
|
|
DavOpenHandle - The handle created by HttpOpenRequest on which the request
|
|
was sent.
|
|
|
|
HttpResponseStatus - If this is non NULL, then the response status returned
|
|
by the server is filled in it. Some callers of this
|
|
function might need it to special case some of the
|
|
Http/Dav responses.
|
|
|
|
Return Value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
DWORD ResponseStatus = 0;
|
|
DWORD ResponseSize = 0;
|
|
BOOL ReturnVal = FALSE;
|
|
|
|
//
|
|
// Query the header for the servers response status.
|
|
//
|
|
ResponseSize = sizeof(ResponseStatus);
|
|
ReturnVal = HttpQueryInfoW(DavOpenHandle,
|
|
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
|
|
&(ResponseStatus),
|
|
&(ResponseSize),
|
|
NULL);
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavQueryAndParseResponseEx/HttpQueryInfoW: Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// If the caller is interested in the Http/Dav response status, we return
|
|
// it.
|
|
//
|
|
if (HttpResponseStatus) {
|
|
*HttpResponseStatus = ResponseStatus;
|
|
}
|
|
|
|
//
|
|
// Map the Http response status code to the appropriate Http error.
|
|
//
|
|
WStatus = DavMapHttpErrorToDosError(ResponseStatus);
|
|
if (WStatus != ERROR_SUCCESS &&
|
|
WStatus != ERROR_FILE_NOT_FOUND) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavQueryAndParseResponseEx/DavMapHttpErrorToDosError: WStatus = %d"
|
|
", ResponseStatus = %d\n", WStatus, ResponseStatus));
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DavMapHttpErrorToDosError(
|
|
ULONG HttpResponseStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function maps the response status returned by the Http/Dav server to
|
|
the corresponding Win32 error code.
|
|
|
|
Arguments:
|
|
|
|
HttpResponseStatus - The http status that has to be mapped to the Win32
|
|
error code.
|
|
|
|
Return Value:
|
|
|
|
A Win32 error code.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Map the HTTP response to the corresponding Win32 error. These will
|
|
// finally get mapped to an NTSTATUS value before the request is sent down
|
|
// to the kernel.
|
|
//
|
|
switch (HttpResponseStatus) {
|
|
|
|
//
|
|
// 100 OK to continue with request.
|
|
//
|
|
case HTTP_STATUS_CONTINUE:
|
|
return ERROR_SUCCESS; // STATUS_SUCCESS;
|
|
|
|
//
|
|
// 101 server has switched protocols in upgrade header.
|
|
//
|
|
case HTTP_STATUS_SWITCH_PROTOCOLS:
|
|
return ERROR_IO_DEVICE; // STATUS_DEVICE_PROTOCOL_ERROR;
|
|
|
|
//
|
|
// 200 Request completed.
|
|
// 201 Object created, reason = new URI.
|
|
// 202 Async completion (TBS).
|
|
// 203 Partial completion.
|
|
// 204 No info to return.
|
|
// 205 Request completed, but clear form.
|
|
// 206 Partial GET furfilled.
|
|
// 207 Multi status response.
|
|
//
|
|
case HTTP_STATUS_OK:
|
|
case HTTP_STATUS_CREATED:
|
|
case HTTP_STATUS_ACCEPTED:
|
|
case HTTP_STATUS_PARTIAL:
|
|
case HTTP_STATUS_NO_CONTENT:
|
|
case HTTP_STATUS_RESET_CONTENT:
|
|
case HTTP_STATUS_PARTIAL_CONTENT:
|
|
case DAV_MULTI_STATUS:
|
|
return ERROR_SUCCESS; // STATUS_SUCCESS;
|
|
|
|
//
|
|
// 300 Server couldn't decide what to return.
|
|
//
|
|
case HTTP_STATUS_AMBIGUOUS:
|
|
return ERROR_GEN_FAILURE; // STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// 301 Object permanently moved.
|
|
//
|
|
case HTTP_STATUS_MOVED:
|
|
return ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
//
|
|
// 302 Object temporarily moved.
|
|
//
|
|
case HTTP_STATUS_REDIRECT:
|
|
return ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
//
|
|
// 303 Redirection w/new access method.
|
|
//
|
|
case HTTP_STATUS_REDIRECT_METHOD:
|
|
return ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
//
|
|
// 304 If-modified-since was not modified.
|
|
//
|
|
case HTTP_STATUS_NOT_MODIFIED:
|
|
return ERROR_SUCCESS; // STATUS_SUCCESS;
|
|
|
|
//
|
|
// 305 Redirection to proxy, location header specifies proxy to use.
|
|
//
|
|
case HTTP_STATUS_USE_PROXY:
|
|
return ERROR_HOST_UNREACHABLE; // STATUS_HOST_UNREACHABLE;
|
|
|
|
//
|
|
// 307 HTTP/1.1: keep same verb.
|
|
//
|
|
case HTTP_STATUS_REDIRECT_KEEP_VERB:
|
|
return ERROR_SUCCESS; // STATUS_SUCCESS;
|
|
|
|
//
|
|
// 400 Invalid syntax.
|
|
//
|
|
case HTTP_STATUS_BAD_REQUEST:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 401 Access denied.
|
|
//
|
|
case HTTP_STATUS_DENIED:
|
|
return ERROR_ACCESS_DENIED; // STATUS_ACCESS_DENIED;
|
|
|
|
//
|
|
// 402 Payment required.
|
|
//
|
|
case HTTP_STATUS_PAYMENT_REQ:
|
|
return ERROR_ACCESS_DENIED; // STATUS_ACCESS_DENIED;
|
|
|
|
//
|
|
// 403 Request forbidden.
|
|
//
|
|
case HTTP_STATUS_FORBIDDEN:
|
|
return ERROR_ACCESS_DENIED; // STATUS_ACCESS_DENIED;
|
|
|
|
//
|
|
// 404 Object not found.
|
|
//
|
|
case HTTP_STATUS_NOT_FOUND:
|
|
return ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
//
|
|
// 405 Method is not allowed.
|
|
//
|
|
case HTTP_STATUS_BAD_METHOD:
|
|
return ERROR_ACCESS_DENIED; // STATUS_ACCESS_DENIED;
|
|
|
|
//
|
|
// 406 No response acceptable to client found.
|
|
//
|
|
case HTTP_STATUS_NONE_ACCEPTABLE:
|
|
return ERROR_ACCESS_DENIED; // STATUS_ACCESS_DENIED;
|
|
|
|
//
|
|
// 407 Proxy authentication required.
|
|
//
|
|
case HTTP_STATUS_PROXY_AUTH_REQ:
|
|
return ERROR_ACCESS_DENIED; // STATUS_ACCESS_DENIED;
|
|
|
|
//
|
|
// 408 Server timed out waiting for request.
|
|
//
|
|
case HTTP_STATUS_REQUEST_TIMEOUT:
|
|
return ERROR_SEM_TIMEOUT; // STATUS_IO_TIMEOUT;
|
|
|
|
//
|
|
// 409 User should resubmit with more info.
|
|
//
|
|
case HTTP_STATUS_CONFLICT:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 410 The resource is no longer available.
|
|
//
|
|
case HTTP_STATUS_GONE:
|
|
return ERROR_FILE_NOT_FOUND; // STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
//
|
|
// 411 The server refused to accept request w/o a length.
|
|
//
|
|
case HTTP_STATUS_LENGTH_REQUIRED:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 412 Precondition given in request failed.
|
|
//
|
|
case HTTP_STATUS_PRECOND_FAILED:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 413 Request entity was too large.
|
|
//
|
|
case HTTP_STATUS_REQUEST_TOO_LARGE:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 414 Request URI too long.
|
|
//
|
|
case HTTP_STATUS_URI_TOO_LONG:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 415 Unsupported media type.
|
|
//
|
|
case HTTP_STATUS_UNSUPPORTED_MEDIA:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 449 Retry after doing the appropriate action.
|
|
//
|
|
case HTTP_STATUS_RETRY_WITH:
|
|
return ERROR_RETRY; // STATUS_RETRY;
|
|
|
|
//
|
|
// 500 Internal server error.
|
|
//
|
|
case HTTP_STATUS_SERVER_ERROR:
|
|
return ERROR_GEN_FAILURE; // STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// 501 Required not supported.
|
|
//
|
|
case HTTP_STATUS_NOT_SUPPORTED:
|
|
return ERROR_NOT_SUPPORTED; // STATUS_NOT_SUPPORTED;
|
|
|
|
//
|
|
// 502 Error response received from gateway.
|
|
//
|
|
case HTTP_STATUS_BAD_GATEWAY:
|
|
return ERROR_HOST_UNREACHABLE; // STATUS_HOST_UNREACHABLE;
|
|
|
|
//
|
|
// 503 Temporarily overloaded.
|
|
//
|
|
case HTTP_STATUS_SERVICE_UNAVAIL:
|
|
return ERROR_GEN_FAILURE; // STATUS_UNSUCCESSFUL;
|
|
|
|
//
|
|
// 504 Timed out waiting for gateway.
|
|
//
|
|
case HTTP_STATUS_GATEWAY_TIMEOUT:
|
|
return ERROR_HOST_UNREACHABLE; // STATUS_HOST_UNREACHABLE;
|
|
|
|
//
|
|
// 505 HTTP version not supported.
|
|
//
|
|
case HTTP_STATUS_VERSION_NOT_SUP:
|
|
return ERROR_NOT_SUPPORTED; // STATUS_NOT_SUPPORTED;
|
|
|
|
//
|
|
// WebDav specific status codes.
|
|
//
|
|
|
|
//
|
|
// 507.
|
|
//
|
|
case DAV_STATUS_INSUFFICIENT_STORAGE:
|
|
return ERROR_NOT_ENOUGH_QUOTA; // STATUS_QUOTA_EXCEEDED;
|
|
|
|
//
|
|
// 422.
|
|
//
|
|
case DAV_STATUS_UNPROCESSABLE_ENTITY:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// 423.
|
|
//
|
|
case DAV_STATUS_LOCKED:
|
|
return ERROR_ACCESS_DENIED; //STATUS_ACCESS_DENIED;
|
|
|
|
//
|
|
// 424.
|
|
//
|
|
case DAV_STATUS_FAILED_DEPENDENCY:
|
|
return ERROR_INVALID_PARAMETER; // STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// This is not a valid Http error code. We return this back to the caller.
|
|
//
|
|
default:
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavMapHttpErrorToDosError: Invalid!!! HttpResponseStatus = %d\n",
|
|
HttpResponseStatus));
|
|
return HttpResponseStatus;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DavDumpHttpResponseHeader(
|
|
HINTERNET OpenHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function dumps the response header that came back from the server.
|
|
|
|
Arguments:
|
|
|
|
OpenHandle - The HttpOpenRequest handle on which the request was sent.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
BOOL IntRes;
|
|
PWCHAR DataBuff = NULL;
|
|
DWORD intLen = 0;
|
|
|
|
IntRes = HttpQueryInfoW(OpenHandle,
|
|
HTTP_QUERY_RAW_HEADERS_CRLF,
|
|
DataBuff,
|
|
&intLen,
|
|
NULL);
|
|
if ( !IntRes ) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_INSUFFICIENT_BUFFER) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavDumpHttpResponseHeader/HttpQueryInfoW: Error Val = "
|
|
"%d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
DataBuff = (PWCHAR) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, intLen);
|
|
if (DataBuff == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavDumpHttpResponseHeader/LocalAlloc: Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
IntRes = HttpQueryInfoW(OpenHandle,
|
|
HTTP_QUERY_RAW_HEADERS_CRLF,
|
|
DataBuff,
|
|
&intLen,
|
|
NULL);
|
|
if ( !IntRes ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavDumpHttpResponseHeader/HttpQueryInfoW: Error Val = "
|
|
"%d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavPrint((DEBUG_DEBUG, "DavDumpHttpResponseHeader:\n%ws\n", DataBuff));
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (DataBuff) {
|
|
LocalFree(DataBuff);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavDumpHttpResponseData(
|
|
HINTERNET OpenHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function dumps the response data that came back from the server.
|
|
|
|
Arguments:
|
|
|
|
OpenHandle - The HttpOpenRequest handle on which the request was sent.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
BOOL ReadRes;
|
|
CHAR DataBuff[4096];
|
|
DWORD didRead = 0, TotalDataBytesRead = 0;
|
|
|
|
DavPrint((DEBUG_DEBUG, "DavDumpHttpResponseData:\n"));
|
|
|
|
//
|
|
// Read the Data in a loop and dump it.
|
|
//
|
|
do {
|
|
|
|
RtlZeroMemory(DataBuff, 4096);
|
|
|
|
ReadRes = InternetReadFile(OpenHandle, (LPVOID)DataBuff, 4096, &didRead);
|
|
if ( !ReadRes ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavDumpHttpResponseData/InternetReadFile: Error Val = "
|
|
"%d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// We reject files whose attributes are greater than a certain size
|
|
// (DavFileAttributesLimitInBytes). This is a parameter that can be
|
|
// set in the registry. This is done to avoid attacks by rogue servers.
|
|
//
|
|
TotalDataBytesRead += didRead;
|
|
if (TotalDataBytesRead > DavFileAttributesLimitInBytes) {
|
|
WStatus = ERROR_BAD_NET_RESP;
|
|
DavPrint((DEBUG_ERRORS, "DavDumpHttpResponseData. FileAttributesSize > %d\n", DavFileAttributesLimitInBytes));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (didRead == 0) {
|
|
break;
|
|
}
|
|
|
|
DavPrint((DEBUG_DEBUG, "%s", DataBuff));
|
|
|
|
} while (TRUE);
|
|
|
|
DavPrint((DEBUG_DEBUG, "\n"));
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavRemoveDummyShareFromFileName(
|
|
PWCHAR FileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function removes the DAV_DUMMY_SHARE from the FileName. This dummy
|
|
share is added when a user tries to map a drive against http://server. This
|
|
is allowed in DAV but doesn't fit well with the File system semantics. Hence
|
|
a dummy share is added in WNetAddConnection3.
|
|
|
|
Arguments:
|
|
|
|
FileName - The name of the file which has to be checked and modifed if
|
|
necessary.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR TempName1, TempName2 = NULL;
|
|
ULONG i;
|
|
|
|
TempName1 = wcsstr(FileName, DAV_DUMMY_SHARE);
|
|
|
|
if (TempName1) {
|
|
TempName2 = wcschr(TempName1, L'/');
|
|
if (TempName2 != NULL) {
|
|
TempName2++;
|
|
for (i = 0; TempName2[i] != L'\0'; i++) {
|
|
TempName1[i] = TempName2[i];
|
|
}
|
|
TempName1[i] = L'\0';
|
|
} else {
|
|
TempName1[0] = L'\0';
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
DavObtainServerProperties(
|
|
PWCHAR DataBuff,
|
|
BOOL *lpfIsHttpServer,
|
|
BOOL *lpfIsIIS,
|
|
BOOL *lpfIsDavServer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to parse the response (buffer) to the OPTIONS request
|
|
sent to server. This info helps to figure out if the HTTP server supports
|
|
DAV extensions and whether it is an IIS (Microsoft's) server. The response
|
|
buffer is split into lines and each line is sent to this routine.
|
|
|
|
Arguments:
|
|
|
|
DataBuff - The Buffer containing the raw http response headers to be parsed.
|
|
|
|
lpfIsHttpServer - Set to TRUE if this is a http server.
|
|
|
|
lpfIsIIS - Set to TRUE if this is an IIS server.
|
|
|
|
lpfIsDavServer - Set to TRUE if this is a DAV server.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR p, ParseData;
|
|
|
|
if (lpfIsHttpServer)
|
|
{
|
|
*lpfIsHttpServer = FALSE;
|
|
}
|
|
|
|
if (lpfIsIIS)
|
|
{
|
|
*lpfIsIIS = FALSE;
|
|
}
|
|
|
|
if (lpfIsDavServer)
|
|
{
|
|
*lpfIsDavServer = FALSE;
|
|
}
|
|
|
|
//
|
|
// Parse the DataBuff here.
|
|
//
|
|
ParseData = wcstok(DataBuff, L"\n");
|
|
|
|
while (ParseData != NULL) {
|
|
|
|
if ( ( p = wcsstr(ParseData, L"HTTP/1.1") ) != NULL ) {
|
|
//
|
|
// This is a HTTP server.
|
|
//
|
|
if (lpfIsHttpServer)
|
|
{
|
|
*lpfIsHttpServer = TRUE;
|
|
}
|
|
} else if ( ( p = wcsstr(ParseData, L"Microsoft-IIS") ) != NULL ) {
|
|
//
|
|
// This is a Microsoft IIS server.
|
|
//
|
|
if (lpfIsIIS)
|
|
{
|
|
*lpfIsIIS = TRUE;
|
|
}
|
|
} else if ( ( p = wcsstr(ParseData, L"DAV") ) != NULL ) {
|
|
//
|
|
// This HTTP server supports DAV extensions.
|
|
//
|
|
if (lpfIsDavServer)
|
|
{
|
|
*lpfIsDavServer = TRUE;
|
|
}
|
|
}
|
|
|
|
ParseData = wcstok(NULL, L"\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavReportEventInEventLog(
|
|
DWORD EventType,
|
|
DWORD EventId,
|
|
DWORD NumberOfStrings,
|
|
PWCHAR *EventStrings
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine logs a message in the EventLog under System section.
|
|
|
|
Arguments:
|
|
|
|
EventType - Specifies the type of event being logged.
|
|
|
|
EventId - Specifies the event. The event identifier specifies the message
|
|
that goes with this event as an entry in the message file
|
|
associated with the event source.
|
|
|
|
NumberOfStrings - Specifies the number of strings in the array pointed to by\
|
|
the EventStrings parameter. A value of zero indicates that
|
|
no strings are present.
|
|
|
|
EventStrings - Pointer to a buffer containing an array of null-terminated
|
|
strings which get logged in this message.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate Win32 Error.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
HANDLE WebClientHandle = NULL;
|
|
BOOL reportEvent = FALSE;
|
|
|
|
WebClientHandle = RegisterEventSourceW(NULL, SERVICE_DAVCLIENT);
|
|
if (WebClientHandle == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavReportEventInEventLog/RegisterEventSourceW: Error Val = "
|
|
"%d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
reportEvent = ReportEventW(WebClientHandle,
|
|
(WORD)EventType,
|
|
0,
|
|
EventId,
|
|
NULL,
|
|
(WORD)NumberOfStrings,
|
|
0,
|
|
EventStrings,
|
|
NULL);
|
|
if (!reportEvent) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavReportEventInEventLog/ReportEventW: Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (WebClientHandle != NULL) {
|
|
BOOL deRegister;
|
|
deRegister = DeregisterEventSource(WebClientHandle);
|
|
if (!deRegister) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavReportEventInEventLog/DeregisterEventSource: Error Val = "
|
|
"%d\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavFormatAndLogError(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
DWORD Win32Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine formats the Error Message and calls DavReportEventInEventLog
|
|
to log it in the EventLog.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The workitem for the failed request.
|
|
|
|
Win32Status - The Win32 failure status.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate Win32 Error.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
PWCHAR *EventStrings = NULL, TempName = NULL;
|
|
PWCHAR ServerName = NULL, PathName = NULL, CompleteFileName = NULL;
|
|
ULONG StringCount = 0, EventId = 0, SizeInBytes = 0;
|
|
UNICODE_STRING StatusString;
|
|
|
|
StatusString.Buffer = NULL;
|
|
StatusString.Length = 0;
|
|
StatusString.MaximumLength = 0;
|
|
|
|
switch (DavWorkItem->WorkItemType) {
|
|
|
|
case UserModeClose: {
|
|
|
|
ServerName = DavWorkItem->CloseRequest.ServerName;
|
|
PathName = DavWorkItem->CloseRequest.PathName;
|
|
|
|
switch (DavWorkItem->DavMinorOperation) {
|
|
|
|
case DavMinorPutFile:
|
|
EventId = EVENT_WEBCLIENT_CLOSE_PUT_FAILED;
|
|
break;
|
|
|
|
case DavMinorDeleteFile:
|
|
EventId = EVENT_WEBCLIENT_CLOSE_DELETE_FAILED;
|
|
break;
|
|
|
|
case DavMinorProppatchFile:
|
|
EventId = EVENT_WEBCLIENT_CLOSE_PROPPATCH_FAILED;
|
|
break;
|
|
|
|
default:
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case UserModeSetFileInformation: {
|
|
|
|
ServerName = DavWorkItem->SetFileInformationRequest.ServerName;
|
|
PathName = DavWorkItem->SetFileInformationRequest.PathName;
|
|
|
|
switch (DavWorkItem->DavMinorOperation) {
|
|
|
|
case DavMinorProppatchFile:
|
|
EventId = EVENT_WEBCLIENT_SETINFO_PROPPATCH_FAILED;
|
|
break;
|
|
|
|
default:
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
WStatus = ERROR_INVALID_PARAMETER;
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
}
|
|
|
|
//
|
|
// We always log 2 string in this function. One is the status value and the
|
|
// other is the filename.
|
|
//
|
|
StringCount = 2;
|
|
|
|
EventStrings = LocalAlloc(LPTR, StringCount * sizeof(PWCHAR));
|
|
if (EventStrings == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFormatAndLogError/LocalAlloc(1): WStatus = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Build the complete path name from the server name and the path name.
|
|
//
|
|
|
|
//
|
|
// The extra 1 is for the \0 character.
|
|
//
|
|
SizeInBytes = ( (wcslen(ServerName) + wcslen(PathName) + 1) * sizeof(WCHAR) );
|
|
|
|
CompleteFileName = LocalAlloc(LPTR, SizeInBytes);
|
|
if (CompleteFileName == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFormatAndLogError/LocalAlloc(2): WStatus = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
wcsncpy( CompleteFileName, ServerName, wcslen(ServerName) );
|
|
|
|
TempName = ( CompleteFileName + wcslen(ServerName) );
|
|
|
|
wcsncpy( TempName, PathName, wcslen(PathName) );
|
|
|
|
CompleteFileName[ ( (SizeInBytes / sizeof(WCHAR)) - 1 ) ] = L'\0';
|
|
|
|
//
|
|
// Replace all '/'s with '\'s.
|
|
//
|
|
for (TempName = CompleteFileName; *TempName != L'\0'; TempName++) {
|
|
if (*TempName == L'/') {
|
|
*TempName = L'\\';
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build a string out of the WStatus. We assume that the ErrorCode will not
|
|
// be more than 8 digits.
|
|
//
|
|
StatusString.Length = ( 10 * sizeof(WCHAR) );
|
|
StatusString.MaximumLength = ( 10 * sizeof(WCHAR) );
|
|
StatusString.Buffer = LocalAlloc(LPTR, StatusString.Length);
|
|
if (StatusString.Buffer == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFormatAndLogError/LocalAlloc(3): WStatus = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
WStatus = RtlIntegerToUnicodeString(Win32Status, 0, &(StatusString));
|
|
if (WStatus != STATUS_SUCCESS) {
|
|
WStatus = RtlNtStatusToDosError(WStatus);
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFormatAndLogError/RtlIntegerToUnicodeString: WStatus = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavFormatAndLogError: CompleteFileName = %ws, ErrorString = %ws\n",
|
|
CompleteFileName, StatusString.Buffer));
|
|
|
|
EventStrings[0] = CompleteFileName;
|
|
EventStrings[1] = StatusString.Buffer;
|
|
|
|
WStatus = DavReportEventInEventLog(EVENTLOG_WARNING_TYPE,
|
|
EventId,
|
|
StringCount,
|
|
EventStrings);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavFormatAndLogError/DavReportEventInEventLog: WStatus = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if (CompleteFileName) {
|
|
LocalFree(CompleteFileName);
|
|
CompleteFileName = NULL;
|
|
}
|
|
|
|
if (StatusString.Buffer) {
|
|
LocalFree(StatusString.Buffer);
|
|
StatusString.Buffer = NULL;
|
|
StatusString.Length = 0;
|
|
StatusString.MaximumLength = 0;
|
|
}
|
|
|
|
if (EventStrings) {
|
|
LocalFree(EventStrings);
|
|
EventStrings = NULL;
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavAttachPassportCookie(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
HINTERNET DavOpenHandle,
|
|
PWCHAR *PassportCookie
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attaches the passport cookie to the Http request header if it
|
|
exists.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
DavOpenHandle - The Wininet request handle.
|
|
|
|
PassportCookie - The buffer contains the cookie sent to Wininet
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
Note:
|
|
|
|
The caller of this routine should free the PassportCookie at the end of the
|
|
request.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
BOOL ReturnVal;
|
|
|
|
EnterCriticalSection(&DavPassportLock);
|
|
|
|
if (DavWorkItem->ServerUserEntry.PerUserEntry->Cookie != NULL) {
|
|
|
|
*PassportCookie = LocalAlloc((LMEM_FIXED | LMEM_ZEROINIT),
|
|
(wcslen(DavWorkItem->ServerUserEntry.PerUserEntry->Cookie) + 1) * sizeof(WCHAR));
|
|
if (*PassportCookie == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAttachPassportCookie/LocalAlloc: Error Val = %d\n",
|
|
WStatus));
|
|
LeaveCriticalSection(&DavPassportLock);
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
wcscpy(*PassportCookie, DavWorkItem->ServerUserEntry.PerUserEntry->Cookie);
|
|
|
|
LeaveCriticalSection(&DavPassportLock);
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavAttachPassportCookie: %x %d %ws\n",
|
|
DavWorkItem->ServerUserEntry.PerUserEntry,
|
|
wcslen(*PassportCookie)*sizeof(WCHAR),
|
|
*PassportCookie));
|
|
|
|
ReturnVal = HttpAddRequestHeadersW(DavOpenHandle,
|
|
*PassportCookie,
|
|
-1L,
|
|
HTTP_ADDREQ_FLAG_ADD |
|
|
HTTP_ADDREQ_FLAG_REPLACE );
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavAttachPassportCookie/Add Cookie. "
|
|
"Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
} else {
|
|
|
|
LeaveCriticalSection(&DavPassportLock);
|
|
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DavInternetSetOption(
|
|
PDAV_USERMODE_WORKITEM DavWorkItem,
|
|
HINTERNET DavOpenHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine set the user name and password to the internet handle.
|
|
|
|
Arguments:
|
|
|
|
DavWorkItem - The DAV_USERMODE_WORKITEM value.
|
|
|
|
DavOpenHandle - The Wininet request handle.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS or the appropriate error value.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
BOOL ReturnVal;
|
|
|
|
//
|
|
// In case of UserModeCreateSrvCall, we don't have a PerUserEntry yet.
|
|
//
|
|
if (DavWorkItem->WorkItemType == UserModeCreateSrvCall) {
|
|
|
|
if (lstrlenW(DavWorkItem->UserName)) {
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle,
|
|
INTERNET_OPTION_USERNAME,
|
|
DavWorkItem->UserName,
|
|
lstrlenW(DavWorkItem->UserName));
|
|
DavPrint((DEBUG_MISC,
|
|
"DavInternetSetOption: UserName = %ws\n",
|
|
DavWorkItem->UserName));
|
|
} else {
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle,INTERNET_OPTION_USERNAME,L"",1);
|
|
}
|
|
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInternetSetOption(1). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (lstrlenW(DavWorkItem->Password)) {
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle,
|
|
INTERNET_OPTION_PASSWORD,
|
|
DavWorkItem->Password,
|
|
lstrlenW(DavWorkItem->Password));
|
|
DavPrint((DEBUG_MISC,
|
|
"DavInternetSetOption: Password = %ws\n",
|
|
DavWorkItem,DavWorkItem->Password));
|
|
} else {
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle,INTERNET_OPTION_PASSWORD,L"",1);
|
|
}
|
|
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInternetSetOption(2). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (lstrlenW(DavWorkItem->ServerUserEntry.PerUserEntry->UserName)) {
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle,
|
|
INTERNET_OPTION_USERNAME,
|
|
DavWorkItem->ServerUserEntry.PerUserEntry->UserName,
|
|
lstrlenW(DavWorkItem->ServerUserEntry.PerUserEntry->UserName));
|
|
DavPrint((DEBUG_MISC,
|
|
"DavInternetSetOption: UserName = %ws\n",
|
|
DavWorkItem->ServerUserEntry.PerUserEntry->UserName));
|
|
} else {
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle,INTERNET_OPTION_USERNAME,L"",1);
|
|
}
|
|
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInternetSetOption(3). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (DavWorkItem->ServerUserEntry.PerUserEntry->BlockSizeInBytes) {
|
|
|
|
PWCHAR Password = NULL;
|
|
DWORD BlockSizeInBytes = 0;
|
|
|
|
BlockSizeInBytes = DavWorkItem->ServerUserEntry.PerUserEntry->BlockSizeInBytes;
|
|
|
|
DavPrint((DEBUG_MISC, "DavInternetSetOption: BlockSizeInBytes = %d\n", BlockSizeInBytes));
|
|
|
|
Password = LocalAlloc((LMEM_FIXED | LMEM_ZEROINIT), BlockSizeInBytes);
|
|
if (Password == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInternetSetOption/LocalAlloc. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
RtlCopyMemory(Password, DavWorkItem->ServerUserEntry.PerUserEntry->Password, BlockSizeInBytes);
|
|
|
|
ReturnVal = CryptUnprotectMemory(Password, BlockSizeInBytes, CRYPTPROTECTMEMORY_SAME_PROCESS);
|
|
if (!ReturnVal) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavInternetSetOption/CryptUnprotectMemory. Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavInternetSetOption: EncryptedPassword = %ws, DecryptedPassword = %ws\n",
|
|
DavWorkItem->ServerUserEntry.PerUserEntry->Password, Password));
|
|
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle,
|
|
INTERNET_OPTION_PASSWORD,
|
|
Password,
|
|
lstrlenW(Password));
|
|
|
|
SecureZeroMemory(Password, BlockSizeInBytes);
|
|
|
|
LocalFree(Password);
|
|
|
|
} else {
|
|
|
|
ReturnVal = InternetSetOptionW(DavOpenHandle, INTERNET_OPTION_PASSWORD, L"", 1);
|
|
|
|
}
|
|
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS, "DavInternetSetOption(4). Error Val = %d\n", WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
return WStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
DavQueryPassportCookie(
|
|
IN HINTERNET RequestHandle,
|
|
IN OUT PWCHAR *Cookie
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function get the Set-Cookie strings from the HTTP response.
|
|
|
|
Arguments:
|
|
|
|
RequestHandle - The handle from HttpOpenRequestW.
|
|
|
|
Cookie - The pointer of the buffer that stores the pointer of the cookies
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - Success or the appropriate Win32 error code.
|
|
|
|
Notes:
|
|
|
|
Here are the exsample of the Set-Cookies on the PROPFIND response from a Tweener server:
|
|
|
|
MSPProf=1AAAAAARAHWeNZdbsWxdhaoUAQ0TfwgHdg7f%2A4ShKm5kK%2AhXHJOsOdPyG27%2A8sh7cirwMRoJoIu764HkLE9lZeKQHOxHw5ZaU2Be0I4BNcxKksiv1vgKvc0Dzy7rlZrOGt6W6efmkr8f8%24; domain=.pp.test.microsoft.com; path=/
|
|
MSPAuth=1AAAAAASAHimsAU2%2AhA9F60NUehefWQp%2AqMNG6%2AWP3f4H25EBsGW8Zo1dZGwVG5txt; domain=.pp.test.microsoft.com; path=/
|
|
MSPProfC=; path=/; expires=Tue 1-Jan-1980 12:00:00 GMT;
|
|
|
|
We are only interested in part of them:
|
|
|
|
MSPProf=1AAAAAARAHWeNZdbsWxdhaoUAQ0TfwgHdg7f%2A4ShKm5kK%2AhXHJOsOdPyG27%2A8sh7cirwMRoJoIu764HkLE9lZeKQHOxHw5ZaU2Be0I4BNcxKksiv1vgKvc0Dzy7rlZrOGt6W6efmkr8f8%24;
|
|
MSPAuth=1AAAAAASAHimsAU2%2AhA9F60NUehefWQp%2AqMNG6%2AWP3f4H25EBsGW8Zo1dZGwVG5txt;
|
|
MSPProfC=;
|
|
|
|
This routine allocates a buffer to save the cookie, which should be freed at
|
|
the end of the connection.
|
|
|
|
--*/
|
|
{
|
|
ULONG WStatus = ERROR_SUCCESS;
|
|
BOOL ReturnVal = FALSE;
|
|
PWCHAR SetCookies = NULL;
|
|
DWORD TotalLength = 0;
|
|
DWORD Current = 0;
|
|
WCHAR CustomBuffer[30];
|
|
ULONG CustomBufferLength = 0;
|
|
DWORD Index = 0;
|
|
|
|
ASSERT(*Cookie == NULL);
|
|
|
|
RtlZeroMemory(CustomBuffer, sizeof(CustomBuffer));
|
|
|
|
wcscpy(CustomBuffer, L"Authentication-Info:");
|
|
|
|
CustomBufferLength = sizeof(CustomBuffer);
|
|
|
|
ReturnVal = HttpQueryInfoW(RequestHandle,
|
|
HTTP_QUERY_CUSTOM,
|
|
(PVOID)CustomBuffer,
|
|
&(CustomBufferLength),
|
|
&Index);
|
|
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_INSUFFICIENT_BUFFER) {
|
|
//
|
|
// The reponse with the valid passport cookie should always have the
|
|
// "Authentication-Info:" included on the header.
|
|
//
|
|
DavPrint((DEBUG_MISC,
|
|
"DavQuerySetCookie/HttpQueryInfoW(0): WStatus = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
|
|
RtlZeroMemory(CustomBuffer, sizeof(CustomBuffer));
|
|
|
|
wcscpy(CustomBuffer, L"Set-Cookie:");
|
|
|
|
Index = 0;
|
|
|
|
for ( ; ; ) {
|
|
|
|
//
|
|
// Query the size of the each Set-Cookie string.
|
|
//
|
|
|
|
CustomBufferLength = sizeof(CustomBuffer);
|
|
|
|
ReturnVal = HttpQueryInfoW(RequestHandle,
|
|
HTTP_QUERY_CUSTOM,
|
|
(PVOID)CustomBuffer,
|
|
&(CustomBufferLength),
|
|
&Index);
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
if (WStatus != ERROR_INSUFFICIENT_BUFFER) {
|
|
DavPrint((DEBUG_MISC,
|
|
"DavQuerySetCookie/HttpQueryInfoW(1): WStatus = %d, "
|
|
"TotalLength = %d, Index = %d\n",
|
|
WStatus, TotalLength, Index));
|
|
if (WStatus == ERROR_HTTP_HEADER_NOT_FOUND) {
|
|
//
|
|
// No more Set-Cookie strings exist.
|
|
//
|
|
break;
|
|
} else {
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
}
|
|
}
|
|
|
|
TotalLength += CustomBufferLength;
|
|
|
|
Index++;
|
|
|
|
if (Index > 20) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
TotalLength += ( (1 + wcslen(L"Cookie: ")) * sizeof(WCHAR) );
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavQuerySetCookie: TotalLength = %d, Index = %d\n",
|
|
TotalLength, Index));
|
|
|
|
if (TotalLength > 0) {
|
|
|
|
SetCookies = LocalAlloc((LMEM_FIXED | LMEM_ZEROINIT), TotalLength);
|
|
if (SetCookies == NULL) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavQuerySetCookie/LocalAlloc: Error Val = %d\n",
|
|
WStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
RtlZeroMemory(SetCookies, TotalLength);
|
|
|
|
wcscpy(SetCookies, L"Cookie: ");
|
|
|
|
Current = wcslen(L"Cookie: ") * sizeof(WCHAR);
|
|
|
|
Index = 0;
|
|
|
|
for ( ; ; ) {
|
|
|
|
ULONG i = 0;
|
|
|
|
//
|
|
// Save the Set-Cookie strings to a single buffer.
|
|
//
|
|
|
|
wcscpy(&SetCookies[Current/sizeof(WCHAR)], L"Set-Cookie:");
|
|
CustomBufferLength = TotalLength - Current;
|
|
|
|
//
|
|
// A successful call to HttpQueryInfoW will increment the Index.
|
|
//
|
|
ReturnVal = HttpQueryInfoW(RequestHandle,
|
|
HTTP_QUERY_CUSTOM,
|
|
(PVOID)(&SetCookies[Current/sizeof(WCHAR)]),
|
|
&(CustomBufferLength),
|
|
&(Index));
|
|
if ( !ReturnVal ) {
|
|
WStatus = GetLastError();
|
|
DavPrint((DEBUG_ERRORS,
|
|
"DavQuerySetCookie/HttpQueryInfoW(2): Error Val = %d\n",
|
|
WStatus));
|
|
if (WStatus != ERROR_HTTP_HEADER_NOT_FOUND) {
|
|
goto EXIT_THE_FUNCTION;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = Current; i < (Current + CustomBufferLength); i += sizeof(WCHAR)) {
|
|
if (SetCookies[ i / sizeof(WCHAR) ] == L' ') {
|
|
i += sizeof(WCHAR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Only interested in the first string of the set-cookie.
|
|
//
|
|
Current = i;
|
|
RtlZeroMemory( &SetCookies[ i / sizeof(WCHAR) ], (TotalLength - i) );
|
|
|
|
DavPrint((DEBUG_MISC,
|
|
"DavQuerySetCookie: Current = %d, CustomBufferLength = %d, "
|
|
"Index = %d, SetCookies = %ws\n",
|
|
Current, CustomBufferLength, Index, SetCookies));
|
|
|
|
if (Index > 20) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get rid of the last "Set-Cookie:" used for HttpQueryInfoW.
|
|
//
|
|
RtlZeroMemory( &SetCookies[ Current / sizeof(WCHAR) ], (TotalLength-Current) );
|
|
*Cookie = SetCookies;
|
|
|
|
WStatus = ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
if ((WStatus != ERROR_SUCCESS) && (SetCookies != NULL)) {
|
|
LocalFree(SetCookies);
|
|
}
|
|
|
|
return WStatus;
|
|
}
|
|
|