/************************************************************************

Copyright (c) 2002 Microsoft Corporation

Module Name :

    config.cpp

Abstract :

    Configuration APIs

Author :

Revision History :

 ***********************************************************************/

#include "precomp.h"

#pragma warning( disable : 4355 )

WCHAR *
ConvertObjectPathToADSI( 
    const WCHAR *ObjectPath )
{

    WCHAR * ReturnPath      = NULL;
    SIZE_T ReturnPathSize   = 0;

    if ( _wcsnicmp( L"IIS://", (WCHAR*)ObjectPath, wcslen( L"IIS://") ) == 0 )
        {
        // already have an adsi path
        ReturnPathSize  = wcslen( ObjectPath ) + 1;
        ReturnPath      = new WCHAR[ ReturnPathSize ];

        if ( !ReturnPath )
            throw ComError( E_OUTOFMEMORY );

        memcpy( ReturnPath, ObjectPath, ReturnPathSize * sizeof( WCHAR ) );
        }
    else if ( _wcsnicmp( L"/LM/", (WCHAR*)ObjectPath, wcslen( L"/LM/" ) ) == 0 )
        {
        //metabase path to local machine
        ReturnPathSize  = wcslen( (WCHAR*)ObjectPath ) + wcslen( L"IIS://LocalHost/" ) + 1;
        ReturnPath      = new WCHAR[ ReturnPathSize  ];
        
        if ( !ReturnPath )
            throw ComError( E_OUTOFMEMORY );

        StringCchCopyW( ReturnPath, ReturnPathSize, L"IIS://LocalHost/" );
        StringCchCatW( ReturnPath, ReturnPathSize, ObjectPath + wcslen( L"/LM/" ) );
        }
    else if ( _wcsnicmp( L"LM/", (WCHAR*)ObjectPath, wcslen( L"LM/" ) ) == 0 )
        {
        //metabase path to local machine
        ReturnPathSize  = wcslen( (WCHAR*)ObjectPath ) + wcslen( L"IIS://LocalHost/" ) + 1;
        ReturnPath      = new WCHAR[ ReturnPathSize ];
        
        if ( !ReturnPath )
            throw ComError( E_OUTOFMEMORY );

        StringCchCopyW( ReturnPath, ReturnPathSize, L"IIS://LocalHost/" );
        StringCchCatW( ReturnPath, ReturnPathSize, ObjectPath + wcslen( L"LM/" ) );
        }
    else 
        {
        //metabase path to another server
        ReturnPathSize  = wcslen( (WCHAR*)ObjectPath ) + wcslen( L"IIS://" ) + 1;
        ReturnPath      = new WCHAR[ ReturnPathSize ];

        if ( !ReturnPath )
            throw ComError( E_OUTOFMEMORY );

        if ( '/' == *(WCHAR*)ObjectPath )
            StringCchCopyW( ReturnPath, ReturnPathSize, L"IIS:/" );
        else
            StringCchCopyW( ReturnPath, ReturnPathSize, L"IIS://" );

        StringCchCatW( ReturnPath, ReturnPathSize, (WCHAR*)ObjectPath );

        }

    return ReturnPath;
}

HRESULT 
GetTypeInfo( 
    const GUID & guid, 
    ITypeInfo **TypeInfo )
{

    DWORD Result;
    HRESULT hr;
    WCHAR DllName[ MAX_PATH ];

    

    Result = 
        GetModuleFileName(
            g_hinst,
            DllName,
            MAX_PATH - 1 );

    if ( !Result )
        return HRESULT_FROM_WIN32( GetLastError() );

    ITypeLib *TypeLib;

    hr = LoadTypeLibEx( 
        DllName, 
        REGKIND_NONE,  
        &TypeLib );

    if ( FAILED( hr ) )
        return hr;

    hr = TypeLib->GetTypeInfoOfGuid(
            guid,
            TypeInfo );

    TypeLib->Release();

    return hr;
}

void 
FreeReturnedWorkItems(
    ULONG NamesReturned,
    LPWSTR **ItemNamesPtr )
{

    LPWSTR *ItemNames = *ItemNamesPtr;

    if ( ItemNames )
        {

        for( ULONG i = 0; i < NamesReturned; i++ )
            {
            CoTaskMemFree( ItemNames[i] );
            }

        CoTaskMemFree( ItemNames );

        *ItemNamesPtr = NULL;

        }

}

HRESULT
FindWorkItemForVDIR( 
    ITaskScheduler *TaskScheduler,
    LPCWSTR Key,
    ITask   **ReturnedTask,
    LPWSTR  *ReturnedTaskName )
{

    HRESULT Hr;
    SIZE_T KeyLength = sizeof(WCHAR) * ( wcslen( Key ) + 1 );
    WORD DataLength;
    
    if ( ReturnedTask )
        *ReturnedTask = NULL;

    if ( ReturnedTaskName )
        *ReturnedTaskName = NULL;

    ITask *Task = NULL;
    IEnumWorkItems *EnumWorkItems = NULL;
    LPWSTR *ItemNames = NULL;
    BYTE *ItemData = NULL;
    ULONG NamesReturned = 0;

    try
    {
        THROW_COMERROR( TaskScheduler->Enum( &EnumWorkItems ) );
        
        while( 1 )
            {

            THROW_COMERROR( EnumWorkItems->Next( 255, &ItemNames, &NamesReturned ) );

            if ( !NamesReturned )
                throw ComError( HRESULT_FROM_WIN32( ERROR_NOT_FOUND ) );

            for ( ULONG i = 0; i < NamesReturned; i++ )
                {

                THROW_COMERROR( TaskScheduler->Activate( ItemNames[i], __uuidof( *Task ), (IUnknown**)&Task ) );
                THROW_COMERROR( Task->GetWorkItemData( &DataLength, &ItemData ) );

                if ( KeyLength == DataLength && 
                     ( wcscmp( Key, (WCHAR*)ItemData ) == 0 ) )
                    {

                    // Found the item, cleanup and return

                    if ( ReturnedTask )
                        *ReturnedTask = Task;
                    else
                        Task->Release();

                    if ( ReturnedTaskName )
                        {
                        *ReturnedTaskName = ItemNames[i];
                        ItemNames[i] = NULL;
                        }

                    FreeReturnedWorkItems(
                        NamesReturned,
                        &ItemNames );

                    EnumWorkItems->Release();
                    CoTaskMemFree( ItemData );
                    return S_OK;
                    }

                Task->Release();
                Task = NULL;

                CoTaskMemFree( ItemData );
                ItemData = NULL;

                }

            FreeReturnedWorkItems(
                NamesReturned,
                &ItemNames );
            NamesReturned = 0;


            }
        

    }
    catch( ComError Error )
    {
        if ( EnumWorkItems )
            EnumWorkItems->Release();

        if ( Task )
            Task->Release();

        FreeReturnedWorkItems(
           NamesReturned,
           &ItemNames );
        
        CoTaskMemFree( ItemData );

        return Error.m_Hr;
    }

}

HRESULT
CreateWorkItemForVDIR(
    ITaskScheduler *TaskScheduler,
    LPCWSTR Path, 
    LPCWSTR Key )
{
    
    WORD KeySize = sizeof(WCHAR) * ( wcslen( Key ) + 1 );

    WCHAR ItemNameFormat[ MAX_PATH ];
    WCHAR ItemName[ MAX_PATH * 4 ];
    WCHAR Parameters[ MAX_PATH * 4];

    LoadString( g_hinst, IDS_WORK_ITEM_NAME, ItemNameFormat, MAX_PATH - 1 );

    // Use the last part of the path for the description
    const WCHAR *ShortPath = Path + wcslen( Path );

    while( *ShortPath != L'/' &&
           *ShortPath != L'\\' &&
           ShortPath != Path )
        ShortPath--;

    if ( *ShortPath == L'/' ||
         *ShortPath == L'\\' )
        ShortPath++;

    DWORD Result;
    void* InsertArray[2] = { (void*)ShortPath, (void*)Key };
    
    Result = FormatMessage(
        FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
        (LPCVOID)ItemNameFormat,
        0,
        0,
        ItemName,
        MAX_PATH * 4,
        (va_list*)InsertArray );


    if ( !Result )
        return HRESULT_FROM_WIN32( GetLastError() );

    StringCbPrintfW( 
        Parameters, 
        sizeof( Parameters ), 
        L"bitsmgr.dll,Cleanup_RunDLL %s \"%s\" %s", Path, ItemName, Key );

    WORD TriggerNumber;
    ITask *Task = NULL;
    ITaskTrigger *TaskTrigger = NULL;
    IPersistFile *PersistFile = NULL;

    try
    {
        HRESULT Hr;

        Hr = FindWorkItemForVDIR( TaskScheduler, Key, &Task, NULL );

        if ( SUCCEEDED( Hr ) )
            {
            // The work item already exists
            Task->Release();
            return S_OK;
            }

        if ( FAILED(Hr) && HRESULT_FROM_WIN32( ERROR_NOT_FOUND ) != Hr )
            THROW_COMERROR( Hr );

        THROW_COMERROR( TaskScheduler->NewWorkItem( ItemName, CLSID_CTask, __uuidof( *Task ), (IUnknown**)&Task ) );

        // Set basic task data
        THROW_COMERROR( Task->SetApplicationName( L"%SystemRoot%\\system32\\rundll32.exe" ) );
        THROW_COMERROR( Task->SetMaxRunTime( INFINITE ) );
        THROW_COMERROR( Task->SetParameters( Parameters ) );
        THROW_COMERROR( Task->SetPriority( IDLE_PRIORITY_CLASS  ) );
        THROW_COMERROR( Task->SetAccountInformation( L"", NULL ) ); //Run as localsystem
        THROW_COMERROR( Task->SetFlags( TASK_FLAG_RUN_ONLY_IF_LOGGED_ON | TASK_FLAG_HIDDEN  ) );
        THROW_COMERROR( Task->SetWorkItemData( KeySize, (BYTE*)Key ) );

        // set trigger information.  Set start time to now, with a default
        // interval of once a day.
        THROW_COMERROR( Task->CreateTrigger( &TriggerNumber, &TaskTrigger ) );

        SYSTEMTIME LocalTime;
        GetLocalTime( &LocalTime );
        
        TASK_TRIGGER Trigger;
        memset( &Trigger, 0, sizeof( Trigger ) );
        Trigger.cbTriggerSize               = sizeof(Trigger);
        Trigger.wBeginYear                  = LocalTime.wYear;
        Trigger.wBeginMonth                 = LocalTime.wMonth;
        Trigger.wBeginDay                   = LocalTime.wDay;
        Trigger.wStartHour                  = LocalTime.wHour;
        Trigger.wStartMinute                = LocalTime.wMinute;
        Trigger.TriggerType                 = TASK_TIME_TRIGGER_DAILY;
        Trigger.MinutesDuration             = 24 * 60; // 24 hours per day 
        Trigger.MinutesInterval             = 12 * 60; // twice per day
        Trigger.Type.Daily.DaysInterval     = 1;
        
        THROW_COMERROR( TaskTrigger->SetTrigger( &Trigger ) );

        // Commit the changes to disk.
        THROW_COMERROR( Task->QueryInterface( __uuidof( IPersistFile ), (void**)&PersistFile ) );
        THROW_COMERROR( PersistFile->Save( NULL, TRUE ) );

        PersistFile->Release();
        TaskTrigger->Release();
        Task->Release();

        return S_OK;
    }
    catch( ComError Error )
    {
        if ( TaskTrigger )
            TaskTrigger->Release();

        if ( Task )
            Task->Release();

        if ( PersistFile )
            PersistFile->Release();

        TaskScheduler->Delete( ItemName );

        return Error.m_Hr;
    }
    
}

HRESULT
DeleteWorkItemForVDIR(
    ITaskScheduler *TaskScheduler,
    LPWSTR Key )
{
    
    HRESULT Hr;
    LPWSTR TaskName = NULL;



    Hr = FindWorkItemForVDIR( TaskScheduler, Key, NULL, &TaskName );

    if ( HRESULT_FROM_WIN32( ERROR_NOT_FOUND ) == Hr )
        return S_OK;

    if ( FAILED( Hr ) )
        return Hr;

    Hr = TaskScheduler->Delete( TaskName );

    CoTaskMemFree( TaskName );
    return Hr;

}

HRESULT
ConnectToTaskScheduler(
    LPWSTR ComputerName, 
    ITaskScheduler ** TaskScheduler )
{

     HRESULT Hr;

     Hr = CoCreateInstance(
         CLSID_CTaskScheduler,
         NULL,
         CLSCTX_INPROC_SERVER,
         __uuidof( **TaskScheduler ),
         (void **) TaskScheduler );

     if ( FAILED( Hr ) )
         return Hr;

     Hr = (*TaskScheduler)->SetTargetComputer( ComputerName );

     if ( FAILED( Hr ) )
         {
         (*TaskScheduler)->Release();
         (*TaskScheduler) = NULL;
         return Hr;
         }

     return S_OK;

}

HRESULT 
IsBITSEnabledOnVDir(
    PropertyIDManager *PropertyManager,
    IMSAdminBase *IISAdminBase,
    LPWSTR VirtualDirectory,
    BOOL *IsEnabled )
{

    HRESULT Hr;
    DWORD BufferRequired;

    *IsEnabled = false;

    DWORD IsEnabledVal;
    METADATA_RECORD MdRecord;
    memset( &MdRecord, 0, sizeof( MdRecord ) );
    
    MdRecord.dwMDDataType   = DWORD_METADATA;
    MdRecord.dwMDIdentifier = PropertyManager->GetPropertyMetabaseID( MD_BITS_UPLOAD_ENABLED );
    MdRecord.dwMDDataLen    = sizeof(IsEnabled);
    MdRecord.pbMDData       = (PBYTE)&IsEnabledVal;

    Hr =
        IISAdminBase->GetData(
            METADATA_MASTER_ROOT_HANDLE,
            VirtualDirectory,
            &MdRecord,
            &BufferRequired );

    if ( MD_ERROR_DATA_NOT_FOUND == Hr ||
         HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) == Hr )
        return S_OK;

    if ( FAILED( Hr ) )
        return Hr;
    
    *IsEnabled = IsEnabledVal ? true : false;
    return S_OK;
}

HRESULT
EnableBITSForVDIR(
    PropertyIDManager   *PropertyManager,
    IMSAdminBase        *IISAdminBase,
    LPCWSTR             Path )
{

    HRESULT Hr;
    METADATA_HANDLE MdVDirKey       = NULL;
    LPWSTR NewScriptMapBuffer       = NULL;
    ITaskScheduler* TaskScheduler   = NULL;

    try
    {

        Hr = DisableBITSForVDIR(
                PropertyManager,
                IISAdminBase,
                Path,
                true );

        if ( FAILED( Hr ) )
            throw ComError( Hr );

        // build the string to add to the scriptmap
        WCHAR SystemDir[ MAX_PATH + 1 ];

        if (!GetSystemDirectoryW( SystemDir, MAX_PATH ) )
            throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );

        WCHAR ScriptMapString[ MAX_PATH * 2 + 1 ];

        StringCbPrintfW( 
            ScriptMapString,
            sizeof( ScriptMapString ),
            L"*,%s\\bitssrv.dll,1," BITS_COMMAND_VERBW,
            SystemDir );

        int RetChars = wcslen( ScriptMapString );
        ScriptMapString[ RetChars ] = L'\0';
        ScriptMapString[ RetChars + 1] = L'\0';

        RetChars += 2;  // ScriptMapScript is now double NULL terminated

        Hr =
        IISAdminBase->OpenKey(
            METADATA_MASTER_ROOT_HANDLE,
            Path,
            METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
            30000,
            &MdVDirKey );

        if (FAILED(Hr))
            throw ComError(Hr);

        DWORD IsEnabled;

        DWORD BufferRequired;
        METADATA_RECORD MdRecord;

        DWORD AccessFlags;
        MdRecord.dwMDIdentifier = MD_ACCESS_PERM;
        MdRecord.dwMDAttributes = METADATA_INHERIT;
        MdRecord.dwMDUserType   = IIS_MD_UT_FILE;
        MdRecord.dwMDDataType   = DWORD_METADATA;
        MdRecord.dwMDDataLen    = sizeof( AccessFlags );
        MdRecord.pbMDData       = (unsigned char*)&AccessFlags;
        MdRecord.dwMDDataTag    = 0;

        Hr =
            IISAdminBase->GetData(
                MdVDirKey,
                NULL,
                &MdRecord,
                &BufferRequired );

        if ( MD_ERROR_DATA_NOT_FOUND == Hr ||
             HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) == Hr )
            {
            AccessFlags = 0;
            }
        else if ( FAILED( Hr ) )
            {
            throw ComError( Hr );
            }

        if ( AccessFlags & ( MD_ACCESS_SCRIPT | MD_ACCESS_EXECUTE ) )
            {
            
            AccessFlags &= ~( MD_ACCESS_SCRIPT | MD_ACCESS_EXECUTE );
            MdRecord.dwMDAttributes &= ~METADATA_ISINHERITED;

            Hr = 
                IISAdminBase->SetData(
                    MdVDirKey,
                    NULL,
                    &MdRecord );


            if ( FAILED( Hr ) )
                throw ComError( Hr );

            }

        MdRecord.dwMDIdentifier = PropertyManager->GetPropertyMetabaseID( MD_BITS_UPLOAD_ENABLED );
        MdRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
        MdRecord.dwMDUserType   = ALL_METADATA;
        MdRecord.dwMDDataType   = DWORD_METADATA;
        MdRecord.dwMDDataLen    = sizeof(IsEnabled);
        MdRecord.pbMDData       = (PBYTE)&IsEnabled;
        MdRecord.dwMDDataTag    = 0;

        Hr =
            IISAdminBase->GetData(
                MdVDirKey,
                NULL,
                &MdRecord,
                &BufferRequired );
    
        if ( FAILED( Hr ) )
            {
            if ( !( MD_ERROR_DATA_NOT_FOUND == Hr ||
                    HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) == Hr ) )
                {
                IISAdminBase->CloseKey( MdVDirKey );
                return S_OK;
                }
            }
        else if ( IsEnabled )
            {
            IISAdminBase->CloseKey( MdVDirKey );
            return S_OK;
            }


        // 
        //  retrieve the current scriptmap adding room to the allocated memory
        //

        memset( &MdRecord, 0, sizeof( MdRecord ) );

        MdRecord.dwMDDataType   = MULTISZ_METADATA;
        MdRecord.dwMDAttributes = METADATA_INHERIT;
        MdRecord.dwMDUserType   = IIS_MD_UT_FILE;
        MdRecord.dwMDIdentifier = MD_SCRIPT_MAPS;
        MdRecord.dwMDDataLen    = 0;
        MdRecord.pbMDData       = (PBYTE)NULL;

        Hr = 
            IISAdminBase->GetData(
                MdVDirKey,
                NULL,
                &MdRecord,
                &BufferRequired );

        if ( MD_ERROR_DATA_NOT_FOUND == Hr ||
             HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) == Hr )
            {
            // The Current key doesn't exist.
            MdRecord.pbMDData       = (PBYTE)ScriptMapString;
            MdRecord.dwMDDataLen    = RetChars * sizeof(WCHAR); 
            }
        else if ( HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) == Hr )
            {

            NewScriptMapBuffer      = new WCHAR[ ( BufferRequired / sizeof(WCHAR) ) + RetChars ];
            MdRecord.pbMDData       = (PBYTE)NewScriptMapBuffer;
            MdRecord.dwMDDataLen    = BufferRequired + ( RetChars * sizeof(WCHAR) );

            Hr =
                IISAdminBase->GetData(
                    MdVDirKey,
                    NULL,
                    &MdRecord,
                    &BufferRequired );

            if ( FAILED(Hr) )
                throw ComError( Hr );

            // append script entry at the end

            for( WCHAR *p = NewScriptMapBuffer; *p != 0; p += ( wcslen( p ) + 1 ) );
            memcpy( p, ScriptMapString, RetChars * sizeof(WCHAR) );

            MdRecord.pbMDData        = (PBYTE)NewScriptMapBuffer;
            MdRecord.dwMDDataLen     = (DWORD)( ( (char*)p - (char*)NewScriptMapBuffer ) +
                                                ( RetChars * sizeof(WCHAR) ) );
            }
        else
            throw ComError( Hr );

        // Write out the new values starting with ScriptMap

        Hr = 
            IISAdminBase->SetData(
                MdVDirKey,
                NULL,
                &MdRecord );

        if ( FAILED( Hr ) )
            throw ComError( Hr );
         
        // Set the enabled property

        DWORD EnableData = 1;
        memset( &MdRecord, 0, sizeof( MdRecord ) );
        MdRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
        MdRecord.dwMDDataType   = DWORD_METADATA;
        MdRecord.dwMDUserType   = PropertyManager->GetPropertyUserType( MD_BITS_UPLOAD_ENABLED );
        MdRecord.dwMDIdentifier = PropertyManager->GetPropertyMetabaseID( MD_BITS_UPLOAD_ENABLED );
        MdRecord.dwMDDataLen    = sizeof(EnableData);
        MdRecord.pbMDData       = (PBYTE)&EnableData;        

        Hr =
            IISAdminBase->SetData(
                MdVDirKey,
                NULL,
                &MdRecord );

        if ( FAILED( Hr ) )
            throw ComError( Hr );

        delete[] NewScriptMapBuffer;

        // Create the task scheduler cleanup work item

        WCHAR GuidString[ 255 ];

        // first try looking up the guid

        MdRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
        MdRecord.dwMDDataType   = STRING_METADATA;
        MdRecord.dwMDUserType   = PropertyManager->GetPropertyUserType( MD_BITS_CLEANUP_WORKITEM_KEY );
        MdRecord.dwMDIdentifier = PropertyManager->GetPropertyMetabaseID( MD_BITS_CLEANUP_WORKITEM_KEY );
        MdRecord.dwMDDataLen    = sizeof( GuidString );
        MdRecord.pbMDData       = (PBYTE)GuidString;
        MdRecord.dwMDDataTag    = 0;
       
        Hr = IISAdminBase->GetData(
                MdVDirKey,
                NULL,
                &MdRecord,
                &BufferRequired );

        if ( MD_ERROR_DATA_NOT_FOUND == Hr ||
             HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) == Hr )
            {

            // create a new guid and save it away

            GUID guid;
        
            THROW_COMERROR( CoCreateGuid( &guid ) );
            StringFromGUID2( guid, GuidString, 254 );
            
            MdRecord.dwMDDataLen    = sizeof(WCHAR) * ( wcslen(GuidString) + 1 );
            MdRecord.pbMDData       = (PBYTE)GuidString;

            THROW_COMERROR( 
                IISAdminBase->SetData(
                    MdVDirKey,
                    NULL,
                    &MdRecord ) );

            }

        else if ( FAILED( Hr ) )
            throw ComError( Hr );


        THROW_COMERROR( ConnectToTaskScheduler( NULL, &TaskScheduler ) );
        THROW_COMERROR( CreateWorkItemForVDIR( TaskScheduler, Path, GuidString ) );

        TaskScheduler->Release();
        IISAdminBase->CloseKey( MdVDirKey );
        return S_OK;
    }
    
    catch( ComError Exception )
    {
        if ( TaskScheduler )
            TaskScheduler->Release();

        DisableBITSForVDIR(
            PropertyManager,
            IISAdminBase,
            Path,
            false );

        delete[] NewScriptMapBuffer;
        if ( MdVDirKey )
            IISAdminBase->CloseKey( MdVDirKey );
        return Exception.m_Hr;
    }

}

HRESULT
DisableBITSForVDIR(
    PropertyIDManager   *PropertyManager,
    IMSAdminBase        *IISAdminBase,
    LPCWSTR             Path,
    bool                DisableForEnable )
{

    HRESULT Hr;
    METADATA_HANDLE MdVDirKey           = NULL;
    LPWSTR          OriginalScriptMap   = NULL;
    LPWSTR          NewScriptMap        = NULL;
    ITaskScheduler* TaskScheduler       = NULL;

    try
    {

        if ( !DisableForEnable )
            CleanupForRemoval( Path );

        // build the string to add to the scriptmap
        WCHAR SystemDir[ MAX_PATH + 1 ];

        if (!GetSystemDirectoryW( SystemDir, MAX_PATH ) )
            throw ComError( HRESULT_FROM_WIN32( GetLastError() ) );

        WCHAR ScriptMapString[ MAX_PATH * 2 + 1 ];

        StringCchPrintfW( 
            ScriptMapString,
            MAX_PATH * 2 + 1,
            L"*,%s\\bitssrv.dll,1,BITS_COMMAND",
            SystemDir );

        int RetChars = wcslen( ScriptMapString );
        ScriptMapString[ RetChars ] = L'\0';
        ScriptMapString[ RetChars + 1] = L'\0';

        // ScriptMapScript is now double NULL terminated

        WCHAR ScriptMapString2[ MAX_PATH * 2 + 1];

        StringCbPrintfW( 
            ScriptMapString2,
            sizeof( ScriptMapString2 ),
            L"*,%\\bitsserver.dll,1,BITS_COMMAND",
            SystemDir );

        RetChars = wcslen( ScriptMapString2 );
        ScriptMapString2[ RetChars ] = L'\0';
        ScriptMapString2[ RetChars + 1 ] = L'\0';

        // ScriptMapScript2 is not double NULL terminated


        WCHAR ScriptMapString3[ MAX_PATH * 2 + 1 ];

        StringCbPrintfW( 
            ScriptMapString3,
            sizeof( ScriptMapString3 ),
            L"*,%s\\bitssrv.dll,1," BITS_COMMAND_VERBW,
            SystemDir );
        
        RetChars = wcslen( ScriptMapString3 );
        ScriptMapString3[ RetChars ] = L'\0';
        ScriptMapString3[ RetChars + 1] = L'\0';

        // ScriptMapScript3 is now double NULL terminated

        THROW_COMERROR( 
            IISAdminBase->OpenKey(
                METADATA_MASTER_ROOT_HANDLE,
                Path,
                METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                30000,
                &MdVDirKey ) );

        // 
        //  retrieve the current scriptmap adding room to the allocated memory
        //

        DWORD BufferRequired;

        METADATA_RECORD MdRecord;
        memset( &MdRecord, 0, sizeof( MdRecord ) );

        MdRecord.dwMDDataType   = MULTISZ_METADATA;
        MdRecord.dwMDAttributes = METADATA_INHERIT;
        MdRecord.dwMDUserType   = IIS_MD_UT_FILE;
        MdRecord.dwMDIdentifier = MD_SCRIPT_MAPS;
        MdRecord.dwMDDataLen    = 0;
        MdRecord.pbMDData       = (PBYTE)NULL;

        Hr = 
            IISAdminBase->GetData(
                MdVDirKey,
                NULL,
                &MdRecord,
                &BufferRequired );

        if ( HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) != Hr )
            throw ComError( Hr );

        OriginalScriptMap = new WCHAR[ BufferRequired / 2 + 2 ];
        NewScriptMap      = new WCHAR[ BufferRequired / 2 + 2 ];

        OriginalScriptMap[0] = OriginalScriptMap[1] = L'\0';

        MdRecord.dwMDDataLen    = BufferRequired;
        MdRecord.pbMDData       = (PBYTE)OriginalScriptMap;

        Hr = 
            IISAdminBase->GetData(
                MdVDirKey,
                NULL,
                &MdRecord,
                &BufferRequired );


        if ( FAILED(Hr) )
            throw ComError( Hr );

        // Copy the orignal Scriptmap to the new scriptmap
        // removing bits goo in the process.

        LPWSTR CurrentOriginalItem = OriginalScriptMap;
        LPWSTR CurrentNewItem      = NewScriptMap;

        for( ;L'\0' != *CurrentOriginalItem;
             CurrentOriginalItem += ( wcslen( CurrentOriginalItem ) + 1 )  )
            {

            if ( _wcsicmp( CurrentOriginalItem, ScriptMapString ) == 0 )
                continue; //remove this item

            if ( _wcsicmp( CurrentOriginalItem, ScriptMapString2 ) == 0 )
                continue;

            if ( _wcsicmp( CurrentOriginalItem, ScriptMapString3 ) == 0 )
                continue;

            SIZE_T CurrentOriginalItemSize = wcslen( CurrentOriginalItem ) + 1;
            memcpy( CurrentNewItem, CurrentOriginalItem, CurrentOriginalItemSize * sizeof( WCHAR ) );
            CurrentNewItem += CurrentOriginalItemSize;

            }

        // Add the extra 0
        *CurrentNewItem++ = L'\0';

        MdRecord.dwMDDataLen    = (DWORD)( (char*)CurrentNewItem - (char*)NewScriptMap );
        MdRecord.pbMDData       = (PBYTE)NewScriptMap;

        // Set the is enabled property first
        DWORD EnableData = 0;
        METADATA_RECORD MdEnabledRecord;
        memset( &MdEnabledRecord, 0, sizeof( MdEnabledRecord ) );

        MdEnabledRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
        MdEnabledRecord.dwMDDataType   = DWORD_METADATA;
        MdEnabledRecord.dwMDUserType   = PropertyManager->GetPropertyUserType( MD_BITS_UPLOAD_ENABLED );
        MdEnabledRecord.dwMDIdentifier = PropertyManager->GetPropertyMetabaseID( MD_BITS_UPLOAD_ENABLED );
        MdEnabledRecord.dwMDDataLen    = sizeof(EnableData);
        MdEnabledRecord.pbMDData       = (PBYTE)&EnableData;

        Hr =
            IISAdminBase->SetData(
                MdVDirKey,
                NULL,
                &MdEnabledRecord );

        if ( FAILED( Hr ) )
            throw ComError( Hr );

        // set the new scriptmap

        Hr = 
            IISAdminBase->SetData(
                MdVDirKey,
                NULL,
                &MdRecord );

        if ( FAILED( Hr ) )
            throw ComError( Hr );

        WCHAR GuidString[ 255 ];
        memset( &MdRecord, 0, sizeof( MdRecord ) );

        MdRecord.dwMDDataType   = STRING_METADATA;
        MdRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
        MdRecord.dwMDUserType   = PropertyManager->GetPropertyUserType( MD_BITS_CLEANUP_WORKITEM_KEY );
        MdRecord.dwMDIdentifier = PropertyManager->GetPropertyMetabaseID( MD_BITS_CLEANUP_WORKITEM_KEY );
        MdRecord.dwMDDataLen    = sizeof( GuidString );
        MdRecord.pbMDData       = (PBYTE)GuidString;

        Hr =
            IISAdminBase->GetData(
                MdVDirKey,
                NULL,
                &MdRecord,
                &BufferRequired );

        if ( FAILED( Hr ) && Hr != MD_ERROR_DATA_NOT_FOUND )
            throw ComError( Hr );

        if ( SUCCEEDED( Hr ) && !DisableForEnable )
            {
            
            THROW_COMERROR( ConnectToTaskScheduler( NULL, &TaskScheduler ) );
            THROW_COMERROR( DeleteWorkItemForVDIR( TaskScheduler, GuidString ) );
            TaskScheduler->Release();

            THROW_COMERROR( 
                IISAdminBase->DeleteData(
                    MdVDirKey,
                    NULL,
                    PropertyManager->GetPropertyMetabaseID( MD_BITS_CLEANUP_WORKITEM_KEY ),
                    ALL_METADATA ) );

            }

        delete[] OriginalScriptMap;
        delete[] NewScriptMap;

        IISAdminBase->CloseKey( MdVDirKey );
        MdVDirKey = NULL;
        return S_OK;

    }
    catch( ComError Exception )
    {
        if ( TaskScheduler )
            TaskScheduler->Release();

        delete[] OriginalScriptMap;
        delete[] NewScriptMap;
        if ( MdVDirKey )
            IISAdminBase->CloseKey( MdVDirKey );
        return Exception.m_Hr;
    }

}

HRESULT
FindWorkItemForVDIR( 
    PropertyIDManager   *PropertyManager,
    IMSAdminBase        *AdminBase,
    LPCWSTR             Path,
    LPWSTR              *ReturnedTaskName )
{

    if ( ReturnedTaskName )
        *ReturnedTaskName = NULL;

    WCHAR GuidString[ 255 ];
    DWORD BufferRequired;
    METADATA_RECORD MdRecord;
    HRESULT Hr;

    MdRecord.dwMDDataType   = STRING_METADATA;
    MdRecord.dwMDAttributes = METADATA_NO_ATTRIBUTES;
    MdRecord.dwMDUserType   = PropertyManager->GetPropertyUserType( MD_BITS_CLEANUP_WORKITEM_KEY );
    MdRecord.dwMDIdentifier = PropertyManager->GetPropertyMetabaseID( MD_BITS_CLEANUP_WORKITEM_KEY );
    MdRecord.dwMDDataLen    = sizeof( GuidString );
    MdRecord.pbMDData       = (PBYTE)GuidString;
    MdRecord.dwMDDataTag    = 0;

    Hr =
        AdminBase->GetData(
            METADATA_MASTER_ROOT_HANDLE,
            Path,
            &MdRecord,
            &BufferRequired );

    if ( MD_ERROR_DATA_NOT_FOUND == Hr )
        return S_FALSE;

    ITaskScheduler* TaskScheduler = NULL;
    Hr = ConnectToTaskScheduler( NULL, &TaskScheduler );

    if ( FAILED( Hr ) )
        return Hr;

    Hr = FindWorkItemForVDIR( TaskScheduler, GuidString, NULL, ReturnedTaskName );

    // simply return NULL if the task item isn't found.
    if ( HRESULT_FROM_WIN32( ERROR_NOT_FOUND ) == Hr )
        Hr = S_FALSE;

    TaskScheduler->Release();

    return Hr;
}

CBITSExtensionSetupFactory::CBITSExtensionSetupFactory() :
m_cref(1),
m_TypeInfo(NULL)
{
    OBJECT_CREATED
}
    
CBITSExtensionSetupFactory::~CBITSExtensionSetupFactory()
{
    if ( m_TypeInfo )
        m_TypeInfo->Release();

    OBJECT_DESTROYED
}

STDMETHODIMP CBITSExtensionSetupFactory::QueryInterface(REFIID riid, LPVOID *ppv)
{
    if (!ppv)
        return E_FAIL;
    
    *ppv = NULL;
    
    if (IsEqualIID(riid, IID_IUnknown))
        *ppv = static_cast<IUnknown *>(this);
    else if (IsEqualIID(riid, __uuidof(IBITSExtensionSetupFactory)))
        *ppv = static_cast<IBITSExtensionSetupFactory *>(this);
    
    if (*ppv) 
    {
        reinterpret_cast<IUnknown *>(*ppv)->AddRef();
        return S_OK;
    }
    
    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CBITSExtensionSetupFactory::AddRef()
{
    return InterlockedIncrement((LONG *)&m_cref);
}

STDMETHODIMP_(ULONG) CBITSExtensionSetupFactory::Release()
{
    if (InterlockedDecrement((LONG *)&m_cref) == 0)
    {
        // we need to decrement our object count in the DLL
        delete this;
        return 0;
    }
    
    return m_cref;
}

HRESULT
CBITSExtensionSetupFactory::LoadTypeInfo()
{

   if ( m_TypeInfo )
       return S_OK;

   return ::GetTypeInfo( __uuidof( IBITSExtensionSetupFactory ), &m_TypeInfo ); 

}

STDMETHODIMP 
CBITSExtensionSetupFactory::GetIDsOfNames( 
    REFIID,  
    OLECHAR FAR* FAR* rgszNames, 
    unsigned int cNames, 
    LCID, 
    DISPID FAR* rgDispId )
{

    HRESULT Hr;
    Hr = LoadTypeInfo();

    if ( FAILED( Hr ) )
        return Hr;

    return DispGetIDsOfNames( m_TypeInfo, rgszNames, cNames, rgDispId);


}

STDMETHODIMP 
CBITSExtensionSetupFactory::GetTypeInfo( 
    unsigned int iTInfo, 
    LCID lcid, 
    ITypeInfo FAR* FAR* ppTInfo )
{


   *ppTInfo = NULL;

   if(iTInfo != 0)
      return ResultFromScode(DISP_E_BADINDEX);

   HRESULT Hr;
   Hr = LoadTypeInfo();

   if ( FAILED( Hr ) )
       return Hr;

   m_TypeInfo->AddRef();      
   *ppTInfo = m_TypeInfo;

   return NOERROR;
}

STDMETHODIMP 
CBITSExtensionSetupFactory::GetTypeInfoCount( 
    unsigned int FAR* pctinfo )
{
    *pctinfo = 1;
    return NOERROR;
}

STDMETHODIMP 
CBITSExtensionSetupFactory::Invoke( 
    DISPID dispIdMember, 
    REFIID, 
    LCID, 
    WORD wFlags, 
    DISPPARAMS FAR* pDispParams, 
    VARIANT FAR* pVarResult, 
    EXCEPINFO FAR* pExcepInfo, 
    unsigned int FAR* puArgErr )
{

    HRESULT Hr;
    Hr = LoadTypeInfo();

    if ( FAILED( Hr ) )
        return Hr;


   return
       DispInvoke(
           this, 
           m_TypeInfo,
           dispIdMember, 
           wFlags, 
           pDispParams,
           pVarResult, 
           pExcepInfo, 
           puArgErr); 

}


STDMETHODIMP CBITSExtensionSetupFactory::GetObject( 
    BSTR Path, 
    IBITSExtensionSetup **ppExtensionSetup )
{

    WCHAR *ObjectPath = NULL;
    IUnknown *Object = NULL;

    try
    {
        if ( !Path || !ppExtensionSetup )
            throw ComError( E_INVALIDARG );

        *ppExtensionSetup = NULL;
        ObjectPath = ConvertObjectPathToADSI( (WCHAR*)Path );

        THROW_COMERROR( ADsGetObject( BSTR( ObjectPath ), __uuidof( IUnknown ), (void**)&Object ) );

        delete ObjectPath;
        ObjectPath = NULL;

        CBITSExtensionSetup *SetupObj = new CBITSExtensionSetup( NULL, Object );

        if ( !SetupObj )
            throw ComError( E_OUTOFMEMORY );

        Object = NULL;
        *ppExtensionSetup = static_cast<IBITSExtensionSetup*>( SetupObj );
        return S_OK;
    }
    catch( ComError Error )
    {
        delete ObjectPath;
        if ( Object )
            Object->Release();
        return Error.m_Hr;
    }

}


STDMETHODIMP CNonDelegatingIUnknown::QueryInterface(REFIID riid, LPVOID *ppv)
{
    if (!ppv)
        return E_FAIL;
    
    *ppv = NULL;

    if ( riid == __uuidof(IUnknown) )
        *ppv = static_cast<IUnknown *>(this);
    else if ( riid == __uuidof(IDispatch) )
        *ppv = static_cast<IDispatch*>(m_DelegatingIUnknown);
    else if ( riid == __uuidof(IBITSExtensionSetup) )
        *ppv = static_cast<IBITSExtensionSetup *>(m_DelegatingIUnknown);
    else if ( riid == __uuidof(IADsExtension) )
        *ppv = static_cast<IADsExtension *>(m_DelegatingIUnknown);

    if (*ppv) 
    {
        reinterpret_cast<IUnknown *>(*ppv)->AddRef();
        return S_OK;
    }
    
    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CNonDelegatingIUnknown::AddRef()
{
    return InterlockedIncrement((LONG *)&m_cref);
}

STDMETHODIMP_(ULONG) CNonDelegatingIUnknown::Release()
{
    if (InterlockedDecrement((LONG *)&m_cref) == 0)
    {
        // we need to decrement our object count in the DLL
        delete m_DelegatingIUnknown;
        return 0;
    }
    
    return m_cref;
}

CNonDelegatingIUnknown::CNonDelegatingIUnknown( CBITSExtensionSetup * DelegatingIUnknown ) :
m_DelegatingIUnknown( DelegatingIUnknown ),
m_cref(1)
{
}

CBITSExtensionSetup::CBITSExtensionSetup( IUnknown *Outer, IUnknown *Object ) :
m_pOuter( Outer ),
m_pObject( Object ),
m_OuterDispatch( NULL ),
m_TypeInfo( NULL ),
m_ADSIPath( NULL ),
m_Path( NULL ),
m_PropertyMan( NULL ),
m_DelegationIUnknown( this ),
m_RemoteInterface( NULL ),
m_InitComplete( false ),
m_Lock( 0 )
{

    if ( m_pOuter )
        {

        HRESULT Hr = m_pOuter->QueryInterface( __uuidof( IDispatch ), (void**)&m_OuterDispatch );

        if ( FAILED( Hr ) )
            m_OuterDispatch = NULL;

        }

    OBJECT_CREATED
}

CBITSExtensionSetup::~CBITSExtensionSetup()
{
    if ( m_pObject )
        {
        m_pObject->Release();
        m_pObject = NULL;
        }

    if ( m_OuterDispatch )
        m_OuterDispatch->Release();

    if ( m_TypeInfo )
        m_TypeInfo->Release();

    delete[] m_Path; // Noop on NULL
    m_Path = NULL;

    if ( m_RemoteInterface )
        m_RemoteInterface->Release();

    delete m_PropertyMan;

    SysFreeString( m_ADSIPath );

    OBJECT_DESTROYED
}

STDMETHODIMP CBITSExtensionSetup::QueryInterface(REFIID riid, LPVOID *ppv)
{
    if ( m_pOuter )
        return m_pOuter->QueryInterface( riid, ppv );
    else
        return m_DelegationIUnknown.QueryInterface( riid, ppv );
}

STDMETHODIMP_(ULONG) CBITSExtensionSetup::AddRef()
{
    
    if ( m_pOuter )
        return m_pOuter->AddRef();
    else
        return m_DelegationIUnknown.AddRef();
}

STDMETHODIMP_(ULONG) CBITSExtensionSetup::Release()
{
    if ( m_pOuter )
        return m_pOuter->AddRef();
    else
        return m_DelegationIUnknown.AddRef();
}

HRESULT
CBITSExtensionSetup::LoadTypeInfo()
{

   if ( m_TypeInfo )
       return S_OK;

   // Lock object
   while( InterlockedExchange( &m_Lock, 1 ) )
       Sleep( 0 );

   HRESULT Hr = ::GetTypeInfo( __uuidof( IBITSExtensionSetup ), &m_TypeInfo ); 

   // Unlock the object
   InterlockedExchange( &m_Lock, 0 );
   return Hr;

}

STDMETHODIMP 
CBITSExtensionSetup::Operate(
    ULONG dwCode, 
    VARIANT varData1, 
    VARIANT varData2, 
    VARIANT varData3)
{

   return E_NOTIMPL;         

}

STDMETHODIMP 
CBITSExtensionSetup::PrivateGetIDsOfNames( 
    REFIID,  
    OLECHAR FAR* FAR* rgszNames, 
    unsigned int cNames, 
    LCID, 
    DISPID FAR* rgDispId )
{

    HRESULT Hr;
    Hr = LoadTypeInfo();

    if ( FAILED( Hr ) )
        return Hr;

    return DispGetIDsOfNames( m_TypeInfo, rgszNames, cNames, rgDispId);


}

STDMETHODIMP 
CBITSExtensionSetup::PrivateGetTypeInfo( 
    unsigned int iTInfo, 
    LCID lcid, 
    ITypeInfo FAR* FAR* ppTInfo )
{


   *ppTInfo = NULL;

   if(iTInfo != 0)
      return ResultFromScode(DISP_E_BADINDEX);

   HRESULT Hr;
   Hr = LoadTypeInfo();

   if ( FAILED( Hr ) )
       return Hr;

   m_TypeInfo->AddRef();      
   *ppTInfo = m_TypeInfo;

   return NOERROR;
}

STDMETHODIMP 
CBITSExtensionSetup::PrivateGetTypeInfoCount( 
    unsigned int FAR* pctinfo )
{
    *pctinfo = 1;
    return NOERROR;
}

STDMETHODIMP 
CBITSExtensionSetup::PrivateInvoke( 
    DISPID dispIdMember, 
    REFIID, 
    LCID, 
    WORD wFlags, 
    DISPPARAMS FAR* pDispParams, 
    VARIANT FAR* pVarResult, 
    EXCEPINFO FAR* pExcepInfo, 
    unsigned int FAR* puArgErr )
{

    HRESULT Hr;
    Hr = LoadTypeInfo();

    if ( FAILED( Hr ) )
        return Hr;


   return
       DispInvoke(
           static_cast<IBITSExtensionSetup*>(this), 
           m_TypeInfo,
           dispIdMember, 
           wFlags, 
           pDispParams,
           pVarResult, 
           pExcepInfo, 
           puArgErr); 

}

STDMETHODIMP 
CBITSExtensionSetup::GetIDsOfNames( 
    REFIID riid,  
    OLECHAR FAR* FAR* rgszNames, 
    unsigned int cNames, 
    LCID lcid, 
    DISPID FAR* rgDispId )
{

    if ( m_OuterDispatch )
        return m_OuterDispatch->GetIDsOfNames( 
            riid,
            rgszNames,
            cNames,
            lcid,
            rgDispId );
    
    return PrivateGetIDsOfNames( 
        riid,
        rgszNames,
        cNames,
        lcid,
        rgDispId );


}

STDMETHODIMP 
CBITSExtensionSetup::GetTypeInfo( 
    unsigned int iTInfo, 
    LCID lcid, 
    ITypeInfo FAR* FAR* ppTInfo )
{


   if ( m_OuterDispatch )
       return m_OuterDispatch->GetTypeInfo(
           iTInfo,
           lcid,
           ppTInfo );

   return 
       PrivateGetTypeInfo(
           iTInfo,
           lcid,
           ppTInfo );

}

STDMETHODIMP 
CBITSExtensionSetup::GetTypeInfoCount( 
    unsigned int FAR* pctinfo )
{

    if ( m_OuterDispatch )
        return m_OuterDispatch->GetTypeInfoCount( pctinfo );

    return PrivateGetTypeInfoCount( pctinfo );

}

STDMETHODIMP 
CBITSExtensionSetup::Invoke( 
    DISPID dispIdMember, 
    REFIID riid, 
    LCID lcid, 
    WORD wFlags, 
    DISPPARAMS FAR* pDispParams, 
    VARIANT FAR* pVarResult, 
    EXCEPINFO FAR* pExcepInfo, 
    unsigned int FAR* puArgErr )
{

    if ( m_OuterDispatch )
        return m_OuterDispatch->Invoke( 
            dispIdMember,
            riid,
            lcid,
            wFlags,
            pDispParams,
            pVarResult,
            pExcepInfo,
            puArgErr );


    return 
        PrivateInvoke( 
            dispIdMember,
            riid,
            lcid,
            wFlags,
            pDispParams,
            pVarResult,
            pExcepInfo,
            puArgErr );

}

HRESULT
CBITSExtensionSetup::ConnectToRemoteExtension()
{
    WCHAR *HostName                     = NULL;
    WCHAR *NewPath                      = NULL;
    BSTR NewPathBSTR                    = NULL;
    IBITSExtensionSetupFactory* Factory = NULL;

    try
    {

        // Extract out the host part of the path

        const SIZE_T PrefixSize = sizeof(L"IIS://")/sizeof(WCHAR) - 1;
        if ( _wcsnicmp( (WCHAR*)m_ADSIPath, L"IIS://", PrefixSize ) != 0 ) 
            throw ComError( E_INVALIDARG );

        WCHAR *HostNameStart = ((WCHAR*)m_ADSIPath) + PrefixSize;

        WCHAR *p = HostNameStart;

        while( L'/' != *p )
            {
            if ( L'\0' == *p )
                throw ComError( E_INVALIDARG );

			p++;
            }

        SIZE_T HostNameSize = (char*)p - (char*)HostNameStart + sizeof(L'\0');
        HostName = new WCHAR[ HostNameSize / sizeof(WCHAR) ];
        if ( !HostName )
            throw ComError( E_OUTOFMEMORY );

        
        memcpy( HostName, HostNameStart, HostNameSize - sizeof(WCHAR) );
        HostName[ ( HostNameSize - sizeof(WCHAR) ) / sizeof(WCHAR) ] = L'\0';

        if ( L'\0' == *++p )
            throw ComError( E_INVALIDARG );

        SIZE_T NewPathSize = wcslen( L"IIS://LocalHost/" ) + wcslen( p ) + 1;
        NewPath = new WCHAR[ NewPathSize ];

        if ( !NewPath )
            throw ComError( E_OUTOFMEMORY );

        StringCchCopyW( NewPath, NewPathSize, L"IIS://LocalHost/" );
        StringCchCatW( NewPath, NewPathSize, p );

        NewPathBSTR = SysAllocString( NewPath );

        if ( !NewPathBSTR )
            throw ComError( E_OUTOFMEMORY );

        COSERVERINFO coinfo;
        coinfo.dwReserved1  = 0;
        coinfo.dwReserved2  = 0;
        coinfo.pAuthInfo    = NULL;
        coinfo.pwszName     = HostName;

        GUID guid = __uuidof( IBITSExtensionSetupFactory );
        MULTI_QI mqi;
        mqi.hr              = S_OK;
        mqi.pIID            = &guid;
        mqi.pItf            = NULL;

        THROW_COMERROR( 
            CoCreateInstanceEx(
                __uuidof(BITSExtensionSetupFactory),
                NULL,
                CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
                &coinfo,
                1,
                &mqi ) );
        THROW_COMERROR( mqi.hr );

        Factory     = (IBITSExtensionSetupFactory*)mqi.pItf;
        mqi.pItf    = NULL;

        THROW_COMERROR( Factory->GetObject( NewPathBSTR, &m_RemoteInterface ) );

        Factory->Release();

        SysFreeString( NewPathBSTR );
        delete[] NewPath;
        delete[] HostName;

        return S_OK;
    }
    catch( ComError Error )
    {
        SysFreeString( NewPathBSTR ); 
        delete[] HostName;
        delete[] NewPath;

        if ( Factory )
            Factory->Release();

        return Error.m_Hr;
    }


}

HRESULT CBITSExtensionSetup::LoadPath()
{

    HRESULT Hr;

    if ( m_InitComplete )
        return S_OK;

    // Lock object
    while( InterlockedExchange( &m_Lock, 1 ) )
        Sleep( 0 );

    if ( !m_PropertyMan )
        {

        m_PropertyMan = new PropertyIDManager();

        if ( !m_PropertyMan )
            {
            Hr = E_OUTOFMEMORY;
            goto error;
            }

        Hr = m_PropertyMan->LoadPropertyInfo();

        if ( FAILED(Hr) )
            {
            delete m_PropertyMan;
            m_PropertyMan = NULL;
            goto error;
            }
        
        }

    if ( !m_ADSIPath )
        {
        
        IADs *ObjectADS = NULL;

        if ( m_pObject )
            Hr = m_pObject->QueryInterface( __uuidof(*ObjectADS), (void**) &ObjectADS );
        else
            Hr = m_pOuter->QueryInterface( __uuidof(*ObjectADS), (void**) &ObjectADS );

        if ( FAILED( Hr ) )
            goto error;

        Hr = ObjectADS->get_ADsPath( &m_ADSIPath );

        ObjectADS->Release();
        ObjectADS = NULL;

        if ( FAILED( Hr ) )
            goto error;

        }


    if ( !m_Path && !m_RemoteInterface )
        {

        if ( _wcsnicmp( (WCHAR*)m_ADSIPath, L"IIS://LocalHost/", wcslen( L"IIS://LocalHost/" ) ) == 0 )
            {
            SIZE_T PathSize = wcslen( (WCHAR*)m_ADSIPath ) + 1; 
            m_Path = new WCHAR[ PathSize ]; 
        
            if ( !m_Path )
                {
                Hr = E_OUTOFMEMORY;
                goto error;
                }

            StringCchCopyW( m_Path, PathSize, L"/LM/" );
            StringCchCatW( m_Path, PathSize, reinterpret_cast<WCHAR*>( m_ADSIPath ) + wcslen( L"IIS://LocalHost/" ) );
            }
        
        else
            {
            
            Hr = ConnectToRemoteExtension( );


            if ( FAILED( Hr ) )
                goto error;

            }

        }

    m_InitComplete = true;
    // unlock 
    InterlockedExchange( &m_Lock, 0 );
    return S_OK;

error:
    // unlock 
    InterlockedExchange( &m_Lock, 0 );
    return Hr;

}

STDMETHODIMP CBITSExtensionSetup::EnableBITSUploads()
{

    HRESULT Hr = LoadPath();

    if ( FAILED(Hr) )
        return Hr;

    if ( m_RemoteInterface )
        return m_RemoteInterface->EnableBITSUploads();

    IMSAdminBase *AdminBase = NULL;

    Hr =
        CoCreateInstance(
            GETAdminBaseCLSID(TRUE),
            NULL,
            CLSCTX_SERVER,
            __uuidof( IMSAdminBase ),
            (LPVOID*)&AdminBase );

    if ( FAILED( Hr ) )
        return Hr;

    Hr = EnableBITSForVDIR( m_PropertyMan, AdminBase, m_Path );

    AdminBase->Release();
    
    return Hr;
}

STDMETHODIMP CBITSExtensionSetup::DisableBITSUploads()
{

    HRESULT Hr = LoadPath();

    if ( FAILED(Hr) )
        return Hr;

    if ( m_RemoteInterface )
        return m_RemoteInterface->DisableBITSUploads();

    IMSAdminBase *AdminBase = NULL;

    Hr =
        CoCreateInstance(
            GETAdminBaseCLSID(TRUE),
            NULL,
            CLSCTX_SERVER,
            __uuidof( IMSAdminBase ),
            (LPVOID*)&AdminBase );

    if ( FAILED( Hr ) )
        return Hr;

    Hr = DisableBITSForVDIR( m_PropertyMan, AdminBase, m_Path, false );

    AdminBase->Release();
    
    return Hr;


}

STDMETHODIMP 
CBITSExtensionSetup::GetCleanupTaskName( BSTR *pTaskName )
{

    *pTaskName = NULL;

    HRESULT Hr = LoadPath();

    if ( FAILED(Hr) )
        return Hr;

    if ( m_RemoteInterface )
        return m_RemoteInterface->GetCleanupTaskName( pTaskName );

    IMSAdminBase *AdminBase = NULL;

    Hr =
        CoCreateInstance(
            GETAdminBaseCLSID(TRUE),
            NULL,
            CLSCTX_SERVER,
            __uuidof( IMSAdminBase ),
            (LPVOID*)&AdminBase );

    if ( FAILED( Hr ) )
        return Hr;

    LPWSTR TaskName = NULL;
    Hr = FindWorkItemForVDIR( m_PropertyMan, AdminBase, m_Path, &TaskName );

    if ( SUCCEEDED( Hr ) && TaskName )
        {

        *pTaskName = SysAllocString( TaskName );
        if ( !*pTaskName )
            Hr = E_OUTOFMEMORY;

        CoTaskMemFree( TaskName );
        TaskName = NULL;

        }

    AdminBase->Release();
    
    return Hr;

}


STDMETHODIMP 
CBITSExtensionSetup::GetCleanupTask( 
    [in] REFIID riid, 
    [out,retval] IUnknown **ppUnk )
{

    HRESULT Hr = S_OK;
    ITaskScheduler *TaskScheduler = NULL;
    BSTR ItemName                 = NULL;
    WCHAR *HostName               = NULL;
    
    if ( ppUnk )
        *ppUnk = NULL;

    try
    {

        THROW_COMERROR( LoadPath() );

        //
        // Build the taskscheduler form of the host name
        //
        
        const SIZE_T PrefixSize = sizeof(L"IIS://")/sizeof(WCHAR) - 1;
        if ( _wcsnicmp( (WCHAR*)m_ADSIPath, L"IIS://", PrefixSize ) != 0 ) 
            throw ComError( E_INVALIDARG );

        WCHAR *HostNameStart = ((WCHAR*)m_ADSIPath) + PrefixSize;
        WCHAR *p = HostNameStart;

        while( L'/' != *p )
            {
            if ( L'\0' == *p )
                throw ComError( E_INVALIDARG );

			p++;
            }

        SIZE_T HostNameSize = (char*)p - (char*)HostNameStart + sizeof(L'\0');
        HostName = new WCHAR[ ( HostNameSize / sizeof(WCHAR) ) + 2 ];
        if ( !HostName )
            throw ComError( E_OUTOFMEMORY );

        HostName[0] = HostName[1] = L'\\';
        memcpy( HostName + 2, HostNameStart, HostNameSize - sizeof(WCHAR) );
        HostName[ ( ( HostNameSize - sizeof(WCHAR) ) / sizeof(WCHAR) ) + 2 ] = L'\0';

        if ( _wcsicmp( HostName, L"\\\\LocalHost" ) == 0 )
            {
            delete[] HostName;
            HostName = NULL;
            }

        THROW_COMERROR( ConnectToTaskScheduler( HostName, &TaskScheduler ) );
        THROW_COMERROR( GetCleanupTaskName( &ItemName ) );

        if ( ItemName )
            THROW_COMERROR( TaskScheduler->Activate( (LPCWSTR)ItemName, riid, ppUnk ) );
        else
            Hr = S_FALSE;

    }
    catch( ComError Error )
    {
        Hr = Error.m_Hr;
    }

    if ( TaskScheduler )
        TaskScheduler->Release();

    SysFreeString( ItemName ); 
    delete[] HostName;

    return Hr;

}


#include "bitssrvcfgimp.h"