/*++ Copyright (c) 2001 Microsoft Corporation Module Name: precomp.h Abstract: Main header for BITS server extensions --*/ #define INITGUID #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_WININET #include #else #include "winhttp.h" #include "inethttp.h" #endif #include #include #include "resource.h" #include #if defined(DBG) // check build #define BITS_MUST_SUCCEED( expr ) ASSERT( expr ) #else // free build #define BITS_MUST_SUCCEED( expr ) ( expr ) #endif const UINT32 LOG_INFO = 0x1; const UINT32 LOG_WARNING = 0x2; const UINT32 LOG_ERROR = 0x4; const UINT32 LOG_CALLBEGIN = 0x8; const UINT32 LOG_CALLEND = 0x10; #if defined(DBG) const UINT32 DEFAULT_LOG_FLAGS = LOG_INFO | LOG_WARNING | LOG_ERROR | LOG_CALLBEGIN | LOG_CALLEND; #else const UINT32 DEFAULT_LOG_FLAGS = 0; #endif const UINT32 DEFAULT_LOG_SIZE = 20; // LogSetings path under HKEY_LOCAL_MACHINE const char * const LOG_SETTINGS_PATH = "SOFTWARE\\Microsoft\\BITSServer"; // Values // (REG_EXPAND_SZ). Contains the full path of the log file name const char * const LOG_FILENAME_VALUE = "LogFileName"; // (REG_DWORD) Contains the log flags const char * const LOG_FLAGS_VALUE = "LogFlags"; // (REG_DWORD) Contains the log size in MB const char * const LOG_SIZE_VALUE = "LogSize"; extern UINT32 g_LogFlags; HRESULT LogInit(); void LogClose(); void LogInternal( UINT32 LogFlags, char *Format, va_list arglist ); void inline Log( UINT32 LogFlags, char *Format, ... ) { if ( !( g_LogFlags & LogFlags ) ) return; va_list arglist; va_start( arglist, Format ); LogInternal( LogFlags, Format, arglist ); } const char *LookupHTTPStatusCodeText( DWORD HttpCode ); class ServerException { public: ServerException() : m_Code( 0 ), m_HttpCode( 0 ), m_Context( 0 ) { } ServerException( HRESULT Code, DWORD HttpCode = 0, DWORD Context = 0x5 ) : m_Code( Code ), m_HttpCode( HttpCode ? HttpCode : MapStatus( Code ) ), m_Context( Context ) { } HRESULT GetCode() const { return m_Code; } DWORD GetHttpCode() const { return m_HttpCode; } DWORD GetContext() const { return m_Context; } void SendErrorResponse( EXTENSION_CONTROL_BLOCK * ExtensionControlBlock ) const; DWORD MapStatus( HRESULT Hr ) const; private: HRESULT m_Code; DWORD m_HttpCode; DWORD m_Context; }; class CharStringRoutines { public: static int strcmp( const char *str1, const char *str2 ) { return ::strcmp( str1, str2 ); } static HRESULT StringCchCopy( char *str1, size_t cchDest, const char *str2 ) { return ::StringCchCopyA( str1, cchDest, str2 ); } static size_t strlen( const char *str ) { return ::strlen( str ); } static void* ConvertToInternal( SIZE_T Pad, const char *String, SIZE_T & Size ) { Size = ::strlen( String ); char *Ret = new char[ Pad + Size + 1 ]; ::StringCchCopyA( Ret + Pad, Size + 1, String ); return (void*)Ret; } static void* ConvertToInternal( SIZE_T Pad, const WCHAR *String, SIZE_T & Size ) { int Alloc = WideCharToMultiByte( CP_THREAD_ACP, // code page 0, // performance and mapping flags String, // wide-character string -1, // number of chars in string NULL, // buffer for new string 0, // size of buffer NULL, // default for unmappable chars NULL // set when default char used ); if ( !Alloc ) throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) ); char *Ret = new char[ Pad + Alloc ]; int Actual = WideCharToMultiByte( CP_THREAD_ACP, // code page 0, // performance and mapping flags String, // wide-character string -1, // number of chars in string Ret + Pad, // buffer for new string Alloc, // size of buffer NULL, // default for unmappable chars NULL // set when default char used ); if ( !Actual ) { HRESULT Hr = HRESULT_FROM_WIN32( GetLastError() ); delete[] Ret; throw ServerException( Hr ); } Size = Actual - 1; return Ret; } }; class WCHARStringRoutines { public: static int strcmp( const WCHAR *str1, const WCHAR *str2 ) { return ::wcscmp( str1, str2 ); } static HRESULT StringCchCopy( WCHAR *str1, size_t cchDest, const WCHAR *str2 ) { return ::StringCchCopyW( str1, cchDest, str2 ); } static size_t strlen( const wchar_t *str ) { return ::wcslen( str ); } static void* ConvertToInternal( SIZE_T Pad, const WCHAR *String, SIZE_T & Size ) { Size = ::wcslen( String ); char *Ret = new char[ Pad + ( ( Size + 1 ) * sizeof(WCHAR) ) ]; ::StringCchCopyW( (WCHAR*)(Ret + Pad), Size + 1, String ); return (void*)Ret; } static void* ConvertToInternal( SIZE_T Pad, const char *String, SIZE_T & Size ) { int Alloc = MultiByteToWideChar( CP_THREAD_ACP, // code page 0, // character-type options String, // string to map -1, // number of bytes in string NULL, // wide-character buffer 0 // size of buffer ); if ( !Alloc ) throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) ); char *Ret = new char[ Pad + ( Alloc * sizeof(WCHAR) ) ]; int Actual = MultiByteToWideChar( CP_THREAD_ACP, // code page 0, // character-type options String, // string to map -1, // number of bytes in string (WCHAR*)( Ret + Pad ), // wide-character buffer Alloc // size of buffer ); if ( !Actual ) { HRESULT Hr = HRESULT_FROM_WIN32( GetLastError() ); delete[] Ret; throw ServerException( Hr ); } Size = Actual - 1; return Ret; } }; template class StringHandleTemplate : private CONV { struct StringData { SIZE_T m_Count; long m_Refs; }; struct EmptyStringData { StringData m_StringData; T m_Data; }; static EmptyStringData s_EmptyString; StringData *m_Value; void NewString( const char *String, bool ReplaceExisting = false ); void NewString( const WCHAR *String, bool ReplaceExisting = false ); StringData * RefIt() const { InterlockedIncrement( &m_Value->m_Refs ); return m_Value; } void FreeIt() { if ( InterlockedDecrement( &m_Value->m_Refs ) == 0 ) delete[] (char*)m_Value; } // Create String by concating 2 strings StringHandleTemplate( const StringData *LeftValue, const T *RightValue, SIZE_T RightSize ); public: StringHandleTemplate() { NewString( (T*)NULL ); } StringHandleTemplate( const char *String ) { NewString( String ); } StringHandleTemplate( const WCHAR *String ) { NewString( String ); } StringHandleTemplate( const StringHandleTemplate & Other ) : m_Value( Other.RefIt() ) { } ~StringHandleTemplate() { FreeIt(); } void SetStringSize() { m_Value->m_Count = strlen( (T*)(m_Value + 1) ); } T *AllocBuffer( SIZE_T Size ); StringHandleTemplate & operator=( const StringHandleTemplate & r ) { FreeIt(); m_Value = r.RefIt(); return *this; } StringHandleTemplate & operator=( const T * r ) { NewString( r, true ); return *this; } SIZE_T Size() const { return m_Value->m_Count; } operator const T*() const { return (const T*)(m_Value + 1); } bool operator <( const StringHandleTemplate & r ) const { if ( m_Value == r.m_Value) return false; return (strcmp( (const T*)*this, (const T*)r ) < 0); } StringHandleTemplate operator+( const StringHandleTemplate & r ) const { return StringHandleTemplate( m_Value, (T*)(r.m_Value+1), r.m_Value->m_Count ); } StringHandleTemplate operator+( const T * p ) const { static const T EmptyChar = '\0'; if ( !p ) return StringHandleTemplate( m_Value, &EmptyChar, 0 ); return StringHandleTemplate( m_Value, p, strlen(p) ); } StringHandleTemplate & operator+=( const StringHandleTemplate & r ) { return (*this = (*this + r ) ); } StringHandleTemplate & operator+=( const T * p ) { return (*this = (*this + p ) ); } }; template void StringHandleTemplate::NewString( const char *String, bool ReplaceExisting ) { if ( !String ) { InterlockedIncrement( &s_EmptyString.m_StringData.m_Refs ); StringData* Value = (StringData*)&s_EmptyString; if ( ReplaceExisting ) FreeIt(); m_Value = Value; return; } SIZE_T Size; StringData* Value = (StringData*)ConvertToInternal( sizeof(StringData), String, Size ); Value->m_Count = Size; Value->m_Refs = 1; if ( ReplaceExisting ) FreeIt(); m_Value = Value; } template void StringHandleTemplate::NewString( const WCHAR *String, bool ReplaceExisting ) { if ( !String ) { InterlockedIncrement( &s_EmptyString.m_StringData.m_Refs ); StringData* Value = (StringData*)&s_EmptyString; if ( ReplaceExisting ) FreeIt(); m_Value = Value; return; } SIZE_T Size; StringData* Value = (StringData*)ConvertToInternal( sizeof(StringData), String, Size ); Value->m_Count = Size; Value->m_Refs = 1; if ( ReplaceExisting ) FreeIt(); m_Value = Value; } // Create String by concating 2 strings template StringHandleTemplate::StringHandleTemplate( const StringData *LeftValue, const T *RightValue, SIZE_T RightSize ) { SIZE_T Size = LeftValue->m_Count + RightSize; m_Value = (StringData*)new char[ sizeof(StringData) + (Size*sizeof(T)) + sizeof(T) ]; m_Value->m_Count = Size; m_Value->m_Refs = 1; T *DestData = (T*)( m_Value + 1 ); memcpy( DestData, (T*)(LeftValue + 1), sizeof(T) * LeftValue->m_Count ); memcpy( DestData + LeftValue->m_Count, RightValue, sizeof( T ) * RightSize ); DestData[ Size ] = 0; } template T * StringHandleTemplate::AllocBuffer( SIZE_T Size ) { StringData *Data = (StringData*)new T[sizeof(StringData)+(Size*sizeof(T))+sizeof(T)]; Data->m_Count = 0; Data->m_Refs = 1; T *String = (T*)(Data + 1); String[0] = '\0'; FreeIt(); // Free old string m_Value = Data; // Whoever fills in the string needs to call SetStringSize return String; } template StringHandleTemplate::EmptyStringData StringHandleTemplate::s_EmptyString = { 0, 1, L'\0' // Initialize with 1 ref so it is never deleted }; typedef StringHandleTemplate StringHandleA; typedef StringHandleTemplate StringHandleW; typedef StringHandleA StringHandle; inline UINT64 FILETIMEToUINT64( const FILETIME & FileTime ) { ULARGE_INTEGER LargeInteger; LargeInteger.HighPart = FileTime.dwHighDateTime; LargeInteger.LowPart = FileTime.dwLowDateTime; return LargeInteger.QuadPart; } inline FILETIME UINT64ToFILETIME( UINT64 Int64Value ) { ULARGE_INTEGER LargeInteger; LargeInteger.QuadPart = Int64Value; FILETIME FileTime; FileTime.dwHighDateTime = LargeInteger.HighPart; FileTime.dwLowDateTime = LargeInteger.LowPart; return FileTime; } // API thunks UINT64 BITSGetFileSize( HANDLE Handle ); UINT64 BITSSetFilePointer( HANDLE Handle, INT64 Distance, DWORD MoveMethod ); DWORD BITSWriteFile( HANDLE Handle, LPCVOID Buffer, DWORD NumberOfBytesToWrite); void BITSCreateDirectory( LPCTSTR DirectoryName ); void BITSRenameFile( LPCTSTR ExistingName, LPCTSTR NewName ); void BITSDeleteFile( LPCTSTR FileName ); GUID BITSCreateGuid(); GUID BITSGuidFromString( const char *String ); StringHandle BITSStringFromGuid( GUID Guid ); StringHandle BITSUnicodeToStringHandle( const WCHAR *pStr ); StringHandle BITSUrlCombine( const char *Base, const char *Relative, DWORD dwFlags ); StringHandle BITSUrlCanonicalize( const char *URL, DWORD dwFlags ); void BITSSetCurrentThreadToken( HANDLE hToken ); // Metadata wrappers StringHandle GetMetaDataString( IMSAdminBase *IISAdminBase, METADATA_HANDLE Handle, LPCWSTR Path, DWORD dwIdentifier, LPCSTR DefaultValue ); DWORD GetMetaDataDWORD( IMSAdminBase *IISAdminBase, METADATA_HANDLE Handle, LPCWSTR Path, DWORD dwIdentifier, DWORD DefaultValue ); class WorkStringBufferA { char *Data; public: WorkStringBufferA( SIZE_T Size ) { Data = new char[Size]; } WorkStringBufferA( const char* String ) { size_t BufferSize = strlen(String) + 1; Data = new char[ BufferSize ]; memcpy( Data, String, BufferSize ); } ~WorkStringBufferA() { delete[] Data; } char *GetBuffer() { return Data; } }; class WorkStringBufferW { WCHAR *Data; public: WorkStringBufferW( SIZE_T Size ) { Data = new WCHAR[Size]; } WorkStringBufferW( const WCHAR* String ) { size_t BufferSize = wcslen(String) + 1; Data = new WCHAR[ BufferSize ]; memcpy( Data, String, BufferSize * sizeof( WCHAR ) ); } ~WorkStringBufferW() { delete[] Data; } WCHAR *GetBuffer() { return Data; } }; typedef WorkStringBufferA WorkStringBuffer; const char * const BITS_CONNECTIONS_NAME_WITH_SLASH="BITS-Connections\\"; const char * const BITS_CONNECTIONS_NAME="BITS-Connections"; const UINT64 NanoSec100PerSec = 10000000; //no of 100 nanosecs per sec const DWORD WorkerRunInterval = 1000 * 60 /*secs*/ * 60 /*mins*/ * 12; /* hours */ /* twice a day */ const UINT64 CleanupThreshold = NanoSec100PerSec * 60 /*secs*/ * 60 /*mins*/ * 24 /* hours */ * 3; // 3 days // // Configuration manager // #include "bitssrvcfg.h" class ConfigurationManager; class VDirConfig { friend ConfigurationManager; LONG m_Refs; FILETIME m_LastLookup; public: StringHandle m_Path; StringHandle m_PhysicalPath; StringHandle m_ConnectionsDir; DWORD m_NoProgressTimeout; UINT64 m_MaxFileSize; BITS_SERVER_NOTIFICATION_TYPE m_NotificationType; StringHandle m_NotificationURL; bool m_UploadEnabled; StringHandle m_HostId; DWORD m_HostIdFallbackTimeout; DWORD m_ExecutePermissions; VDirConfig( StringHandle Path, IMSAdminBase *AdminBase ); void AddRef() { InterlockedIncrement( &m_Refs ); } void Release() { if (!InterlockedDecrement( &m_Refs )) delete this; } }; class MapCacheEntry { friend ConfigurationManager; FILETIME m_LastLookup; public: StringHandle m_InstanceMetabasePath; StringHandle m_URL; VDirConfig *m_Config; MapCacheEntry( StringHandle InstanceMetabasePath, StringHandle URL, VDirConfig * Config ) : m_InstanceMetabasePath( InstanceMetabasePath ), m_URL( URL ), m_Config( Config ) { m_Config->AddRef(); GetSystemTimeAsFileTime( &m_LastLookup ); } ~MapCacheEntry() { m_Config->Release(); } }; class ConfigurationManager { public: ConfigurationManager(); ~ConfigurationManager(); VDirConfig* GetConfig( StringHandle InstanceMetabasePath, StringHandle URL ); static const PATH_CACHE_ENTRIES = 10; static const MAP_CACHE_ENTRIES = 10; private: IMSAdminBase *m_IISAdminBase; CRITICAL_SECTION m_CacheCS; DWORD m_ChangeNumber; VDirConfig *m_PathCacheEntries[ PATH_CACHE_ENTRIES ]; MapCacheEntry *m_MapCacheEntries[ MAP_CACHE_ENTRIES ]; void FlushCache(); bool HandleCacheConsistency(); // L2 cache VDirConfig* Lookup( StringHandle Path ); void Insert( VDirConfig *NewConfig ); VDirConfig* GetVDirConfig( StringHandle Path ); // L1 cache VDirConfig* Lookup( StringHandle InstanceMetabasePath, StringHandle URL ); VDirConfig* Insert( StringHandle InstanceMetabasePath, StringHandle URL, StringHandle Path ); StringHandle GetVDirPath( StringHandle InstanceMetabasePath, StringHandle URL ); }; extern ConfigurationManager *g_ConfigMan; extern HMODULE g_hinst; extern PropertyIDManager *g_PropertyMan;