/*++ Copyright (c) 1998-2002 Microsoft Corporation Module Name: Url.c Abstract: User-mode interface to HTTP.SYS: URL handler for Server APIs. Author: Keith Moore (keithmo) 15-Dec-1998 Revision History: Eric Stenson (ericsten) 01-Jun-2001 Add public "shims" for Transient API Eric Stenson (ericsten) 19-Jul-2001 Split up HTTPAPI/HTTPIIS DLLs. --*/ #include "precomp.h" // // Private prototypes. // extern NTSTATUS HttpApiConfigGroupInformationSanityCheck( IN HTTP_CONFIG_GROUP_INFORMATION_CLASS InformationClass, IN PVOID pConfigGroupInformation, IN ULONG Length ); static CRITICAL_SECTION g_CGListCritSec; static LIST_ENTRY g_ConfigGroupListHead; static DWORD g_ConfigGroupInitialized = 0; typedef struct _tagCONFIG_GROUP_INFO { LIST_ENTRY ListEntry; HTTP_CONFIG_GROUP_ID ConfigGroupId; LPWSTR Url; } CONFIG_GROUP_INFO, *PCONFIG_GROUP_INFO; ULONG AddConfigGroupToTable( HTTP_CONFIG_GROUP_ID CGId, LPCWSTR wszUrl ); VOID DeleteConfigIdFromTable( HTTP_CONFIG_GROUP_ID CGId ); HTTP_CONFIG_GROUP_ID DeleteUrlFromTable( LPCWSTR wszUrl ); // // Public functions. // /***************************************************************************++ Routine Description: Add a URL to the Request Queue (App Pool). The Request Queue will listen for all requests for longest matching URI for the URL. We create a new Config Group object for this URL and associate the Config Group to the App Pool. Arguments: ReqQueueHandle - App Pool Handle pFullyQualifiedUrl - full URL with port descriptor & path pReserved - Must be NULL Return Value: ULONG - Completion status. --***************************************************************************/ HTTPAPI_LINKAGE ULONG WINAPI HttpAddUrl( IN HANDLE ReqQueueHandle, IN PCWSTR pFullyQualifiedUrl, IN PVOID pReserved ) { ULONG status; HTTP_CONFIG_GROUP_ID configId = HTTP_NULL_ID; HTTP_CONFIG_GROUP_APP_POOL configAppPool; HTTP_CONFIG_GROUP_STATE configState; // // Verify we've been init'd. // if ( !HttpIsInitialized(HTTP_INITIALIZE_SERVER) ) { return ERROR_DLL_INIT_FAILED; } // // Validate ReqQueue and URL // if ( (NULL != pReserved) || (NULL == ReqQueueHandle) || (NULL == pFullyQualifiedUrl) || (0 == wcslen(pFullyQualifiedUrl)) ) { return ERROR_INVALID_PARAMETER; } // // Create Config Group (get new Config Group ID) // status = HttpCreateConfigGroup( g_ControlChannel, &configId ); if (status != NO_ERROR) { HttpTrace1( "HttpCreateConfigGroup failed, error %lu\n", status ); goto cleanup; } // // Add a URL to the configuration group. // status = HttpAddUrlToConfigGroup( g_ControlChannel, configId, pFullyQualifiedUrl, 0 ); if (status != NO_ERROR) { HttpTrace1( "HttpAddUrlToConfigGroup failed, error %lu\n", status ); goto cleanup; } // // Associate the configuration group with the application pool. // configAppPool.Flags.Present = 1; configAppPool.AppPoolHandle = ReqQueueHandle; status = HttpSetConfigGroupInformation( g_ControlChannel, configId, HttpConfigGroupAppPoolInformation, &configAppPool, sizeof(configAppPool) ); if (status != NO_ERROR) { HttpTrace1( "Set HttpConfigGroupAppPoolInformation failed, error %lu\n", status ); goto cleanup; } // // Set the config group state. // configState.Flags.Present = 1; configState.State = HttpEnabledStateActive; status = HttpSetConfigGroupInformation( g_ControlChannel, configId, HttpConfigGroupStateInformation, &configState, sizeof(configState) ); if (status != NO_ERROR) { HttpTrace1( "Set HttpConfigGroupStateInformation failed, error %lu\n", status ); goto cleanup; } // Store URL & Config Group ID in hash table, keyed on URL status = AddConfigGroupToTable( configId, pFullyQualifiedUrl ); if (status != NO_ERROR) { HttpTrace1( "AddConfigGroupToTable failed, error %lu\n", status ); goto cleanup; } cleanup: if ( NO_ERROR != status ) { // Failed. Clean up whatever needs to be cleaned up. if ( HTTP_NULL_ID != configId ) { // Delete config group HttpDeleteConfigGroup( g_ControlChannel, configId ); // Remove config group from table DeleteConfigIdFromTable( configId ); } } return status; } // HttpAddUrl /***************************************************************************++ Routine Description: Removes an existing URL from the Request Queue (App Pool). NOTE: The associated Config Group should be cleaned up here. (NYI). Arguments: ReqQueueHandle - App Pool Handle pFullyQualifiedUrl - full URL with port descriptor & path Return Value: ULONG - Completion status. --***************************************************************************/ HTTPAPI_LINKAGE ULONG WINAPI HttpRemoveUrl( IN HANDLE ReqQueueHandle, IN PCWSTR pFullyQualifiedUrl ) { ULONG status; HTTP_CONFIG_GROUP_ID CGId; // // Verify we've been init'd. // if ( !HttpIsInitialized(HTTP_INITIALIZE_SERVER) ) { return ERROR_DLL_INIT_FAILED; } // // Validate ReqQueue and URL // if ( !ReqQueueHandle || !pFullyQualifiedUrl || !wcslen(pFullyQualifiedUrl) ) { return ERROR_INVALID_PARAMETER; } // REVIEW: Do we need to do some sort of access check before we zap // REVIEW: the URL & Config Group? // // Look up Config Group ID from URL // CGId = DeleteUrlFromTable( pFullyQualifiedUrl ); if ( HTTP_NULL_ID != CGId ) { // // Del All URLs from Config Group // HttpRemoveAllUrlsFromConfigGroup( g_ControlChannel, CGId ); // // Del Config Group // status = HttpDeleteConfigGroup( g_ControlChannel, CGId ); } else { status = ERROR_FILE_NOT_FOUND; } return status; } // HttpRemoveUrl /***************************************************************************++ Routine Description: Initializes the config group hash table Return Value: ULONG - Completion status. --***************************************************************************/ ULONG InitializeConfigGroupTable( VOID ) { if (!InitializeCriticalSectionAndSpinCount( &g_CGListCritSec, HTTP_CS_SPIN_COUNT )) { return GetLastError(); } // CODEWORK: actually implement this as a hash table and not just a list InitializeListHead( &g_ConfigGroupListHead ); InterlockedIncrement( (PLONG)&g_ConfigGroupInitialized ); return NO_ERROR; } // InitializeConfigGroupTable /***************************************************************************++ Routine Description: Terminates the config group hash table. Return Value: ULONG - Completion status. --***************************************************************************/ VOID TerminateConfigGroupTable( VOID ) { PCONFIG_GROUP_INFO pCGInfo; PLIST_ENTRY pEntry; ASSERT( g_ControlChannel ); // If not initialized, bail out. if ( g_ConfigGroupInitialized == 0 ) { return; } // CODEWORK: actually implement this as a hash table and not just a list EnterCriticalSection( &g_CGListCritSec ); for ( ;; ) { pEntry = RemoveTailList( &g_ConfigGroupListHead ); if (pEntry == &g_ConfigGroupListHead ) { break; } pCGInfo = CONTAINING_RECORD( pEntry, CONFIG_GROUP_INFO, ListEntry ); HttpTrace1( "TerminateConfigGroupTable: Removing %S\n", pCGInfo->Url ); // // Delete Config Group by ID // ASSERT( HTTP_NULL_ID != pCGInfo->ConfigGroupId ); HttpRemoveAllUrlsFromConfigGroup( g_ControlChannel, pCGInfo->ConfigGroupId ); HttpDeleteConfigGroup( g_ControlChannel, pCGInfo->ConfigGroupId ); FREE_MEM( pCGInfo ); } LeaveCriticalSection( &g_CGListCritSec ); } // TerminateEventCache // // Private functions. // /***************************************************************************++ Routine Description: Adds a Config Group ID/URL pair to a hash table, keyed on URL. CODEWORK: Need to re-implement as hash table Arguments: CGId - Config Group ID to add to hash table. wszUrl - URL associated with config group (always 1:1 mapping) Return Value: ULONG - Completion status. --***************************************************************************/ ULONG AddConfigGroupToTable( HTTP_CONFIG_GROUP_ID CGId, LPCWSTR wszUrl ) { PCONFIG_GROUP_INFO pCGInfo; size_t cbSize; ASSERT( wszUrl ); cbSize = sizeof( CONFIG_GROUP_INFO ); cbSize += sizeof( WCHAR ) * (wcslen( wszUrl ) + 1); pCGInfo = ALLOC_MEM( cbSize ); if ( NULL == pCGInfo ) { return ERROR_NOT_ENOUGH_MEMORY; } pCGInfo->ConfigGroupId = CGId; if ( wcslen(wszUrl) ) { pCGInfo->Url = (LPWSTR) (((PCHAR)pCGInfo) + sizeof( CONFIG_GROUP_INFO )); wcscpy( pCGInfo->Url, wszUrl ); } EnterCriticalSection( &g_CGListCritSec ); InsertTailList( &g_ConfigGroupListHead, &pCGInfo->ListEntry ); LeaveCriticalSection( &g_CGListCritSec ); return NO_ERROR; } // AddConfigGroupToTable /***************************************************************************++ Routine Description: Removes an entry from the hash table (by Config Group ID) Arguments: CGId - Config Group ID. --***************************************************************************/ VOID DeleteConfigIdFromTable( HTTP_CONFIG_GROUP_ID CGId ) { PLIST_ENTRY pEntry; PCONFIG_GROUP_INFO pCGInfo; // Grab crit sec EnterCriticalSection( &g_CGListCritSec ); // Walk List looking for matching entry pEntry = g_ConfigGroupListHead.Flink; while( pEntry != &g_ConfigGroupListHead ) { pCGInfo = CONTAINING_RECORD( pEntry, CONFIG_GROUP_INFO, ListEntry ); if ( pCGInfo->ConfigGroupId == CGId ) { // Remove entry from List RemoveEntryList( pEntry ); // Free structure. FREE_MEM( pCGInfo ); break; } pEntry = pEntry->Flink; } // Release crit sec LeaveCriticalSection( &g_CGListCritSec ); } // DeleteConfigIdFromTable /***************************************************************************++ Routine Description: Removes an entry from the hash table (by URL) Arguments: wszUrl - URL associated with Config Group Id. Returns: HTTP_CONFIG_GROUP_ID - ID of config group associated with wszUrl; HTTP_NULL_ID if no match found. --***************************************************************************/ HTTP_CONFIG_GROUP_ID DeleteUrlFromTable( LPCWSTR wszUrl ) { PLIST_ENTRY pEntry; PCONFIG_GROUP_INFO pCGInfo; HTTP_CONFIG_GROUP_ID CGId = HTTP_NULL_ID; // Grab crit sec EnterCriticalSection( &g_CGListCritSec ); // Walk List looking for matching entry pEntry = g_ConfigGroupListHead.Flink; while( pEntry != &g_ConfigGroupListHead ) { pCGInfo = CONTAINING_RECORD( pEntry, CONFIG_GROUP_INFO, ListEntry ); if ( 0 == wcscmp( pCGInfo->Url, wszUrl ) ) { // Remove entry from List RemoveEntryList( pEntry ); // Free structure. CGId = pCGInfo->ConfigGroupId; FREE_MEM( pCGInfo ); break; } pEntry = pEntry->Flink; } // Release crit sec LeaveCriticalSection( &g_CGListCritSec ); return CGId; } // DeleteUrlFromTable