/*****************************************************************************\
* MODULE: inet.cxx
*
* The module contains routines for the setting up the WWW Printer Service during spooler start up.
*
* The entry point here should be called by localspl\init.c\InitializePrintProvidor() once it is done
* with its work.
*
* Copyright (C) 1996 Microsoft Corporation
*
* History:
*   Dec-1996   BabakJ     Wrote it for IIS 2.0.
*   June-1997  BabakJ     Rewrote to use IIS 4.0's new Metabase interface
*   Feb-1998   Weihaic    Modify the URL in default.htm
*   Feb 1999   BabakJ     Made metabase interface a global to avoi calling too many CoCreateInstance() for perfrmance.
\*****************************************************************************/

//
//
// Note: We cannot use precomp.h here since we requrie ATL which can only be included in C++ source files.
//
//


#define INITGUID     // babakj: (see comments in objbase.h) Needed to do it to get GUID_NULL defined.

#include "precomp.h"
#pragma hdrstop

#include <iadmw.h>      // Interface header
#include <iiscnfg.h>    // MD_ & IIS_MD_ defines

#include "..\spllib\webutil.hxx"

#define MY_META_TIMEOUT 1000

PWCHAR szW3SvcRootPath = L"/LM/W3svc/1/Root";


BOOL   fW3SvcInstalled = FALSE;  // Gobal flag telling if IIS or "Peer eb Server" is installed on the local machine.
PWCHAR szW3Root = NULL;          // The WWWRoot dir, e.g. d:\inetpub\wwwroot


static CRITICAL_SECTION ClientCS;
static CRITICAL_SECTION ServerCS;
static HANDLE hMetaBaseThdReady;
static IMSAdminBase *pIMeta = NULL;  // Metabase interface pointer


class CWebShareData {
public:
    LPWSTR m_pszShareName;
    BOOL   m_bValid;
public:
    CWebShareData (LPWSTR pszShareName);
    ~CWebShareData ();
    int Compare (CWebShareData *pSecond) {return 0;};
};

class CWebShareList :
    public CSingleList<CWebShareData*>
{
public:
    CWebShareList () {};
    ~CWebShareList () {};

    void WebSharePrinterList (void);
};

LPWSTR
mystrstrni(
    LPWSTR pSrc,
    LPWSTR pSearch
);

BOOL
CopyWebPrnFile(
    VOID
);

BOOL
SetupWebPrnSvc(
    IMSAdminBase *pIMSAdminBase,
    BOOL fWebPrnDesired,
    BOOL *pfW3SvcInstalled
);

BOOL
AddWebPrnSvc(
    IMSAdminBase *pIMSAdminBase,
    BOOL *pfW3SvcInstalled
);

BOOL
RemoveWebPrnSvc(
    IMSAdminBase *pIMSAdminBase
);

BOOL
RemoveScript(
    IMSAdminBase *pIMSAdminBase
);

BOOL
RemoveVirtualDir(
    IMSAdminBase *pIMSAdminBase
);

BOOL
InstallWebPrnSvcWorker(
    void
);

void
InstallWebPrnSvcWorkerThread(
    PINISPOOLER pIniSpooler
);

BOOL
AddScriptAtPrinterVDir(
    IMSAdminBase *pIMSAdminBase
);

BOOL
AddVirtualDir(
    IMSAdminBase *pIMSAdminBase
);

void
WebShareWorker(
    LPWSTR pShareName
);

BOOL
CreateVirtualDirForPrinterShare(
    IMSAdminBase *pIMSAdminBase,
    LPWSTR       pShareName
);

void
WebUnShareWorker(
    LPWSTR pShareName
);

BOOL
RemoveVirtualDirForPrinterShare(
    IMSAdminBase *pIMSAdminBase,
    LPWSTR       pShareName
);

void
WebShareAllPrinters(
    PINISPOOLER pIniSpooler
);


//
// This routine is called from init.c at localspl init time to kick start the whole thing.
//
// Make the COM activation on a separate thread in order not to slow down LocalSpl init process
//
void
InstallWebPrnSvc(
    PINISPOOLER pIniSpooler
)
{
    HANDLE ThreadHandle;
    DWORD ThreadId;
    HRESULT hr;                         // com error status


    // Init the sync objects needed for device arrival thread management
    InitializeCriticalSection( &ClientCS );
    InitializeCriticalSection( &ServerCS );
    hMetaBaseThdReady = CreateEvent( NULL, FALSE, FALSE, NULL);   // Auto reset, non-signaled state


    if (ThreadHandle = CreateThread( NULL,
                                     INITIAL_STACK_COMMIT,
                                     (LPTHREAD_START_ROUTINE)InstallWebPrnSvcWorkerThread,
                                     pIniSpooler, 0, &ThreadId )) {
        CloseHandle( ThreadHandle );
    }

}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// We cannot free our interface pointer becasue loading and unloading ADMWPROX.DLL is very slow.
// So we have to keep the interface pointer around. But due to COM Apt limitation, the thread that creates the interface has
// to be alive for other threads to be able to use the pointer. So here we are with this fancy thread management code:
//
// The thread stays alive for a while. Then it goes away. Then future WebShare/Unshare would invoke it again.
// So This thread could be invoked 2 ways: First by spooler init code (only once!), then by WebShare/Unshare code.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define EnterClient()  EnterCriticalSection( &ClientCS );
#define EnterServer()  EnterCriticalSection( &ServerCS );
#define LeaveClient()  LeaveCriticalSection( &ClientCS );
#define LeaveServer()  LeaveCriticalSection( &ServerCS );

static BOOL ThdAlive = FALSE;


///
///   Begin threading work
///


void
InstallWebPrnSvcWorkerThread(
    PINISPOOLER pIniSpooler
)
{
    IMSAdminBase    *pILocalMetaBase = NULL;

    SPLASSERT( (pIniSpooler == NULL) || (pIniSpooler == pLocalIniSpooler) );   // We only expect local pIniSpooler here!

    EnterServer();

    if( ThdAlive ) {
        SPLASSERT( FALSE );
        LeaveServer();
        return;
    }

    if( FAILED (CoInitializeEx( NULL, COINIT_MULTITHREADED )) ||
        FAILED (::CoCreateInstance(CLSID_MSAdminBase,
                          NULL,
                          CLSCTX_ALL,
                          IID_IMSAdminBase,
                          (void **)&pIMeta)))  {

        if( fW3SvcInstalled )
            SetEvent( hMetaBaseThdReady );  // We must have a client thread waiting in WebShare/UnShare, signal it!
        LeaveServer();
        return;
    }


    if( !fW3SvcInstalled ) {          // must be the first time we are being called.
        if (InstallWebPrnSvcWorker()) {
            WebShareAllPrinters( pIniSpooler );
            fW3SvcInstalled = TRUE;   // Once this is set, we never unset it. It is an indication that the WEb init code has succeeded.
        }
    }
    else
        SetEvent( hMetaBaseThdReady );    // event reset after a waiting thread is released.

    ThdAlive = TRUE;

    LeaveServer();

    Sleep( 15 * 60 * 1000 );    // Allow other threads to use the COM pointer for 15 minutes

    //
    // Now tear down the IISADMIN object and self terminate thread. Ensure that
    // we do not release the pointer inside the CS since this could take a long
    // time, this could potentially cause a large number of WorkerThreads queueing
    // up and doing the Release(), but that cannot be helped.
    //
    EnterServer();

    pILocalMetaBase = pIMeta;

    //
    // The client thread expects valid pointers to be non-NULL!
    //
    pIMeta = NULL;
    ThdAlive = FALSE;

    LeaveServer();

    pILocalMetaBase->Release();

    CoUninitialize();

    return;
}

void
WebShareManagement(
    LPWSTR pShareName,
    BOOL bShare          // If TRUE, will share it, else unshare it.
) {
    HANDLE ThreadHandle;
    DWORD ThreadId;

    if( !fW3SvcInstalled ) {
        return;
    }

    if(FAILED (CoInitializeEx( NULL, COINIT_MULTITHREADED )))
        return;

    EnterClient();
    EnterServer();

    if( !ThdAlive ) {
        LeaveServer();

        if (ThreadHandle = CreateThread(NULL,
                                        INITIAL_STACK_COMMIT,
                                        (LPTHREAD_START_ROUTINE)InstallWebPrnSvcWorkerThread,
                                        NULL,
                                        0,
                                        &ThreadId))   // sending NULL pIniSpooler since there is no need for it.
            CloseHandle( ThreadHandle );
        else {
            LeaveClient();
            return;
        }

        WaitForSingleObject( hMetaBaseThdReady, INFINITE );   // automatic reset event, so it is reset after a waiting thd released.
        EnterServer();
    }

    // Now do the real work
    if (pIMeta) {
        if( bShare)
            WebShareWorker( pShareName );
        else
            WebUnShareWorker( pShareName );
    }

    LeaveServer();
    LeaveClient();


    // No need to CoUninitialize();
}


///
///   End threading work
///



void
WebShareAllPrinters(
    PINISPOOLER pIniSpooler
)
{
    CWebShareList *pWebShareList = NULL;
    BOOL   bRet = TRUE;
    PINIPRINTER   pIniPrinter;


    if (pWebShareList = new CWebShareList ()) {

        // Go down the list of printers and share it out

        EnterSplSem();


        //
        // Re-share all shared printers.
        //

        for( pIniPrinter = pIniSpooler->pIniPrinter;
             pIniPrinter;
             pIniPrinter = pIniPrinter->pNext ) {

            if ( pIniPrinter->Attributes & PRINTER_ATTRIBUTE_SHARED ) {

                CWebShareData *pData = new CWebShareData (pIniPrinter->pShareName);

                if (pData && pData->m_bValid &&
                    pWebShareList->Insert (pData)) {
                    continue;
                }
                else {

                    if (pData) {
                        delete pData;
                    }

                    bRet = FALSE;
                    break;
                }
            }
        }

        LeaveSplSem ();

        if (bRet) {
            pWebShareList->WebSharePrinterList ();
        }

        delete pWebShareList;

    }
}

PWSTR
GetPrinterUrl(
    PSPOOL  pSpool
)
{
    PINIPRINTER     pIniPrinter = pSpool->pIniPrinter;
    DWORD           cb;
    PWSTR           pszURL = NULL;
    PWSTR           pszServerName = NULL;
    HRESULT         hr;


    SplInSem();

    // http://machine/share
    if (!pIniPrinter->pShareName)
        goto error;

    // Get FQDN of this machine
    hr = GetDNSMachineName(pIniPrinter->pIniSpooler->pMachineName + 2, &pszServerName);
    if (FAILED(hr)) {
        SetLastError(HRESULT_CODE(hr));
        goto error;
    }

    cb = 7 + wcslen(pszServerName);  // http://machine
    cb += 1 + wcslen(pIniPrinter->pShareName) + 1;  // /share + NULL
    cb *= sizeof(WCHAR);

    if (pszURL = (PWSTR) AllocSplMem(cb)) {
        wsprintf(pszURL, L"http://%ws/%ws", pszServerName, pIniPrinter->pShareName);
    }


error:

    FreeSplStr(pszServerName);

    return pszURL;
}




//
//
// Reads the policy bit. Returns TRUE if Web Printing wanted, FASLE if not.
//
//
BOOL
IsWebPrintingDesired(
    VOID
)
{
    static PWCHAR szRegPolicyBitForDisableWebPrinting = L"Software\\Policies\\Microsoft\\Windows NT\\Printers";


    HKEY   hKey;
    DWORD  dwType;
    DWORD  cbData;
    DWORD  dwDataValue;
    BOOL   fRet = TRUE;

    if( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                      szRegPolicyBitForDisableWebPrinting, 0, KEY_READ, &hKey)) {
        cbData = sizeof(dwDataValue);

        if( RegQueryValueEx(hKey, L"DisableWebPrinting" , NULL, &dwType, (LPBYTE)&dwDataValue, &cbData) == ERROR_SUCCESS )  {
            // As long as the value does not exist, it means that web printing is enabled
            // If the value is a DWORD, then if it is TRUE, Web Printing is Disabled

            if (dwType == REG_DWORD && dwDataValue) fRet = FALSE;
        }
        RegCloseKey( hKey );
    }
    return fRet;
}




BOOL
InstallWebPrnSvcWorker(
    VOID
)
{
    WCHAR  szTmpData[MAX_PATH];
    HRESULT hr;                         // com error status
    METADATA_HANDLE hMeta = NULL;       // handle to metabase
    BOOL    fW3Svc = FALSE;
    DWORD dwMDRequiredDataLen;
    METADATA_RECORD mr;


    // open key to ROOT on website #1 (default)
    hr = pIMeta->OpenKey(METADATA_MASTER_ROOT_HANDLE,
                         L"/LM/W3svc/1",
                         METADATA_PERMISSION_READ,
                         MY_META_TIMEOUT,
                         &hMeta);
    if( SUCCEEDED( hr )) {

        // Get the physical path for the WWWROOT
        mr.dwMDIdentifier = MD_VR_PATH;
        mr.dwMDAttributes = 0;
        mr.dwMDUserType   = IIS_MD_UT_FILE;
        mr.dwMDDataType   = STRING_METADATA;
        mr.dwMDDataLen    = sizeof( szTmpData );
        mr.pbMDData       = reinterpret_cast<unsigned char *>(szTmpData);

        hr = pIMeta->GetData( hMeta, L"/ROOT", &mr, &dwMDRequiredDataLen );
        pIMeta->CloseKey( hMeta );

        if( SUCCEEDED( hr )) {
            szW3Root = AllocSplStr( szTmpData );

            // Pass the inner dumb pointer for the callee to use
            if( SetupWebPrnSvc( pIMeta, IsWebPrintingDesired(), &fW3Svc ))
                DBGMSG(DBG_INFO, ("Setup of WWW Print Service successful.\n"));
            else
                DBGMSG(DBG_INFO, ("Setup of WWW Print Service failed.\n"));

        }
    }

    return fW3Svc;

}



//
// Given that the WWW server is installed on local machine, it installs/removes the Web Printing service.
//
// Won't reinstall if it is already installed.
//
// Installation involves:
//
//   - Add msw3prt as .printer. Just need to get the Win\sys dir.
//   - Add the virtual dis .printer
//
//  Uninstall means the removal of the above two.
//
//
BOOL
SetupWebPrnSvc(
    IMSAdminBase *pIMSAdminBase,
    BOOL fWebPrnDesired,             // whether Web Printing is desired by the caller
    BOOL *pfW3SvcInstalled           // Whether Web Printing actually got installed by this routine.
)
{
    if( fWebPrnDesired ) {

        return AddWebPrnSvc( pIMSAdminBase, pfW3SvcInstalled );
    }
    else {

        *pfW3SvcInstalled = FALSE;
        return RemoveWebPrnSvc( pIMSAdminBase );

    }
}


BOOL
AddWebPrnSvc(
    IMSAdminBase *pIMSAdminBase,
    BOOL *pfW3SvcInstalled           // Whether Web Printing actually got installed by this routine.
)
{
    HRESULT hr;          // com error status


    *pfW3SvcInstalled = FALSE;  // Assume failure

    //
    //  This is to remove the .printer script from the root
    //
    if( !RemoveScript( pIMSAdminBase ))
        return FALSE;


    if( !AddVirtualDir( pIMSAdminBase ))
        return FALSE;

    //
    // Add ".printer" as a script map to the printers virtual directory
    //
    if( !AddScriptAtPrinterVDir( pIMSAdminBase ))
        return FALSE;

    // Flush out the changes and close
    // Call SaveData() after making bulk changes, do not call it on each update
    hr = pIMSAdminBase->SaveData();
    if( FAILED( hr ))
        return FALSE;

    return( *pfW3SvcInstalled = TRUE );   // Web Printing installed.

}

BOOL
RemoveWebPrnSvc(
    IMSAdminBase *pIMSAdminBase
)
{
    HRESULT hr;          // com error status


    // Remove ".printer" as a script map from the website #1 (default)
    if( !RemoveScript( pIMSAdminBase ))
        return FALSE;


    if( !RemoveVirtualDir( pIMSAdminBase ))
        return FALSE;


    // Flush out the changes and close
    // Call SaveData() after making bulk changes, do not call it on each update
    hr = pIMSAdminBase->SaveData();
    if( FAILED( hr ))
        return FALSE;

    return( TRUE );     // Web Printing removed

}


//
//
// Finds the string pSearch in pSrc buffer and returns a ptr to the occurance of pSearch in pSrc.
//
//
LPWSTR mystrstrni( LPWSTR pSrc, LPWSTR pSearch )
{
    UINT uSearchSize = wcslen( pSearch );
    UINT uSrcSize    = wcslen( pSrc );
    LPCTSTR  pEnd;

    if( uSrcSize < uSearchSize )
        return(NULL);

    pEnd = pSrc + uSrcSize - uSearchSize;

    for( ; pSrc <= pEnd; ++pSrc ) {
        if( !_wcsnicmp( pSrc, pSearch, uSearchSize ))
            return((LPWSTR)pSrc);
    }

    return(NULL);
}

//
// Determines if the string pSearch can be found inside of a MULTI_SZ string. If it can, it retunrs a
// pointer to the beginning of the string in multi-sz that contains pSearch.
//
LPWSTR IsStrInMultiSZ( LPWSTR pMultiSzSrc, LPWSTR pSearch )
{
    LPWSTR pTmp = pMultiSzSrc;

    while( TRUE ) {
        if( mystrstrni( pTmp, pSearch ))  // See pSearch (i.e. ".printer" appears anywhere within this string. If it does, it must be ours.
            return pTmp;

        pTmp = pTmp + (wcslen(pTmp) + 1);   // Point to the beginning of the next string in the MULTI_SZ

        if( !*pTmp )
            return FALSE;             // reached the end of the MULTI_SZ string.
    }
}

#define W3SVC          L"w3svc"

/*++

Routine Name:

    AddScriptAtPrinterVDir

Description:

    Add the .printer and .asp script mapping at printers virtual directory

Arguments:

    pIMSAdminBase   - Pointer to the IIS Admin base

Returns:

    An HRESULT

--*/
BOOL
AddScriptAtPrinterVDir(
    IMSAdminBase *pIMSAdminBase
    )
{
    static WCHAR szScritMapFmt[] = L"%ws%c.printer,%ws\\msw3prt.dll,1,GET,POST%c";
    static WCHAR szPrinterVDir[] = L"w3svc/1/root/printers";

    METADATA_HANDLE hMeta           = NULL;       // handle to metabase
    PWCHAR          szFullFormat    = NULL;
    DWORD           dwMDRequiredDataLen;
    METADATA_RECORD mr;
    HRESULT         hr              = S_OK;
    DWORD           nLen;
    WCHAR           szSystemDir[MAX_PATH];
    PWCHAR          pAspMapping     = NULL;
    DWORD           dwMapingLen     = 0;
    PWCHAR          pScriptMap      = NULL;


    //
    // Read any script map set at the top, on LM\w3svc where all other default ones are (e.g. .asp, etc.)
    //
    hr = pIMSAdminBase->OpenKey( METADATA_MASTER_ROOT_HANDLE,
                         L"/LM",
                         METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                         MY_META_TIMEOUT,
                         &hMeta);

    if(SUCCEEDED(hr))
    {
        mr.dwMDIdentifier = MD_SCRIPT_MAPS;
        mr.dwMDAttributes = 0;
        mr.dwMDUserType   = IIS_MD_UT_FILE;
        mr.dwMDDataType   = MULTISZ_METADATA;
        mr.dwMDDataLen    = 0;
        mr.pbMDData       = NULL;

        hr = pIMSAdminBase->GetData( hMeta, W3SVC, &mr, &dwMDRequiredDataLen );

        hr = hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER) ? S_OK : E_FAIL;
    }

    if(SUCCEEDED(hr))
    {
        //
        // allocate for existing stuff plus our new script map.
        //
        szFullFormat = new WCHAR[dwMDRequiredDataLen];
        hr = szFullFormat? S_OK : E_OUTOFMEMORY;
    }

    if(SUCCEEDED(hr))
    {
        mr.dwMDIdentifier = MD_SCRIPT_MAPS;
        mr.dwMDAttributes = 0;
        mr.dwMDUserType   = IIS_MD_UT_FILE;
        mr.dwMDDataType   = MULTISZ_METADATA;
        mr.dwMDDataLen    = dwMDRequiredDataLen * sizeof (WCHAR);
        mr.pbMDData       = reinterpret_cast<unsigned char *>(szFullFormat);

        hr = pIMSAdminBase->GetData( hMeta, W3SVC, &mr, &dwMDRequiredDataLen );
    }

    if(SUCCEEDED(hr))
    {
        pAspMapping = IsStrInMultiSZ( szFullFormat, L".asp" );

        hr = pAspMapping? S_OK: E_FAIL;
    }

    if(SUCCEEDED(hr))
    {
        nLen = COUNTOF (szScritMapFmt) + MAX_PATH + lstrlen (pAspMapping);

        pScriptMap = new WCHAR[nLen];

        hr = pScriptMap ? S_OK : E_OUTOFMEMORY;
    }


    if(SUCCEEDED(hr))
    {
        //
        // Return value is the length in chars w/o null char.
        //
        hr = GetSystemDirectory( szSystemDir, COUNTOF (szSystemDir)) > 0 ? S_OK : E_FAIL;
    }

    if(SUCCEEDED(hr))
    {
        dwMapingLen = wsprintf( pScriptMap, szScritMapFmt, pAspMapping, L'\0', szSystemDir, L'\0');

        hr = dwMapingLen > COUNTOF (szScritMapFmt) ? S_OK : E_FAIL;
    }

    if (SUCCEEDED(hr))
    {
        //
        // Write the new SCRIPT value
        //
        mr.dwMDIdentifier = MD_SCRIPT_MAPS;
        mr.dwMDAttributes = METADATA_INHERIT;
        mr.dwMDUserType   = IIS_MD_UT_FILE;
        mr.dwMDDataType   = MULTISZ_METADATA;
        mr.dwMDDataLen    = sizeof (WCHAR) * (dwMapingLen + 1) ;
        mr.pbMDData       = reinterpret_cast<unsigned char *>(pScriptMap);

        hr = pIMSAdminBase->SetData( hMeta, szPrinterVDir, &mr );
    }

    if (hMeta)
    {
        pIMSAdminBase->CloseKey( hMeta );
        hMeta = NULL;
    }

    delete [] pScriptMap;
    delete [] szFullFormat;

    return SUCCEEDED (hr);
}

//
//
// Finds and removed our script map from the multi_sz, and writes it back to the metabase.
//
//
BOOL
WriteStrippedScriptValue(
    IMSAdminBase *pIMSAdminBase,
    METADATA_HANDLE hMeta,     // Handle to /LM tree
    PWCHAR szFullFormat        // MULTI_SZ string already there
)
{
    LPWSTR  pStrToKill, pNextStr;
    HRESULT hr;


    DBGMSG(DBG_INFO, ("Removing our script if already added.\n"));

    // See if our script map is already there.
    if( !(pStrToKill = IsStrInMultiSZ( szFullFormat, L".printer" )))
        return TRUE;

    // Find the next string (could be the final NULL char)
    pNextStr = pStrToKill + (wcslen(pStrToKill) + 1);

    if( !*pNextStr )
        *pStrToKill = 0;       // Our scipt map was at the end of multi_sz. Write the 2nd NULL char and we are done.
    else
        CopyMemory( pStrToKill,          // Remove our script map by copying the remainder of the multi_sz on top of the string containing the map.
                    pNextStr,
                    GetMultiSZLen(pNextStr) * sizeof(WCHAR));

    // Write the new SCRIPT value
    METADATA_RECORD mr;
    mr.dwMDIdentifier = MD_SCRIPT_MAPS;
    mr.dwMDAttributes = METADATA_INHERIT;
    mr.dwMDUserType   = IIS_MD_UT_FILE;
    mr.dwMDDataType   = MULTISZ_METADATA;
    mr.dwMDDataLen    = GetMultiSZLen(szFullFormat) * sizeof(WCHAR);
    mr.pbMDData       = reinterpret_cast<unsigned char *>(szFullFormat);

    hr = pIMSAdminBase->SetData( hMeta, W3SVC, &mr );

    return( SUCCEEDED( hr ));
}


//
//
// Removes ".printer" as a script map from the website #1 (default) if alreaedy there
//
//
BOOL
RemoveScript(
    IMSAdminBase *pIMSAdminBase
)
{
   METADATA_HANDLE hMeta = NULL;       // handle to metabase
    PWCHAR  szFullFormat;
    DWORD   dwMDRequiredDataLen;
    METADATA_RECORD mr;
    HRESULT hr;
    BOOL    fRet = FALSE;


    // Read any script map set at the top, on LM\w3svc where all other default ones are (e.g. .asp, etc.)
    hr = pIMSAdminBase->OpenKey( METADATA_MASTER_ROOT_HANDLE,
                         L"/LM",
                         METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                         MY_META_TIMEOUT,
                         &hMeta);

    if( SUCCEEDED( hr )) {

        mr.dwMDIdentifier = MD_SCRIPT_MAPS;
        mr.dwMDAttributes = 0;
        mr.dwMDUserType   = IIS_MD_UT_FILE;
        mr.dwMDDataType   = MULTISZ_METADATA;
        mr.dwMDDataLen    = 0;
        mr.pbMDData       = NULL;

        hr = pIMSAdminBase->GetData( hMeta, W3SVC, &mr, &dwMDRequiredDataLen );

        if( FAILED( hr )) {

            if( HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER ) {

                if( szFullFormat = (PWCHAR)AllocSplMem( dwMDRequiredDataLen )) {   // allocate for existing stuff plus our new script map.

                    mr.dwMDIdentifier = MD_SCRIPT_MAPS;
                    mr.dwMDAttributes = 0;
                    mr.dwMDUserType   = IIS_MD_UT_FILE;
                    mr.dwMDDataType   = MULTISZ_METADATA;
                    mr.dwMDDataLen    = dwMDRequiredDataLen;
                    mr.pbMDData       = reinterpret_cast<unsigned char *>(szFullFormat);

                    hr = pIMSAdminBase->GetData( hMeta, W3SVC, &mr, &dwMDRequiredDataLen );

                    if( SUCCEEDED( hr ))
                        fRet = WriteStrippedScriptValue( pIMSAdminBase, hMeta, szFullFormat );    // Remove the .printer map from the multi_sz if there;

                    FreeSplMem( szFullFormat );
                }
            }
        }

        pIMSAdminBase->CloseKey( hMeta );
    }

    return fRet;
}

/////////////////////////////////////////////////////////////////////////////////////////////////


static PWCHAR szPrinters = L"Printers";
#define PRINTERS   szPrinters      // Name of Printers virtual dir.


BOOL
AddVirtualDir(
    IMSAdminBase *pIMSAdminBase
)
{
    METADATA_HANDLE hMeta = NULL;       // handle to metabase
    WCHAR   szVirPath[MAX_PATH];
    WCHAR   szPath[MAX_PATH];
    DWORD   dwMDRequiredDataLen;
    DWORD   dwAccessPerm;
    METADATA_RECORD mr;
    HRESULT hr;
    BOOL    fRet;


    // Attempt to open the virtual dir set on Web server #1 (default server)
    hr = pIMSAdminBase->OpenKey( METADATA_MASTER_ROOT_HANDLE,
                         szW3SvcRootPath,
                         METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                         MY_META_TIMEOUT,
                         &hMeta );

    // Create the key if it does not exist.
    if( FAILED( hr ))
        return FALSE;


    fRet = TRUE;

    mr.dwMDIdentifier = MD_VR_PATH;
    mr.dwMDAttributes = 0;
    mr.dwMDUserType   = IIS_MD_UT_FILE;
    mr.dwMDDataType   = STRING_METADATA;
    mr.dwMDDataLen    = sizeof( szVirPath );
    mr.pbMDData       = reinterpret_cast<unsigned char *>(szVirPath);

    // Read LM/W3Svc/1/Root/Printers see if MD_VR_PATH exists.
    hr = pIMSAdminBase->GetData( hMeta, PRINTERS, &mr, &dwMDRequiredDataLen );

    if( FAILED( hr )) {

        fRet = FALSE;
        if( hr == MD_ERROR_DATA_NOT_FOUND ||
            HRESULT_CODE(hr) == ERROR_PATH_NOT_FOUND ) {

            // Write both the key and the values if GetData() failed with any of the two errors.

            pIMSAdminBase->AddKey( hMeta, PRINTERS );

            if( GetWindowsDirectory( szPath, sizeof(szPath) / sizeof (TCHAR))) {      // Return value is the length in chars w/o null char.

                DBGMSG(DBG_INFO, ("Writing our virtual dir.\n"));

                wsprintf( szVirPath, L"%ws\\web\\printers", szPath );

                mr.dwMDIdentifier = MD_VR_PATH;
                mr.dwMDAttributes = METADATA_INHERIT;
                mr.dwMDUserType   = IIS_MD_UT_FILE;
                mr.dwMDDataType   = STRING_METADATA;
                mr.dwMDDataLen    = (wcslen(szVirPath) + 1) * sizeof(WCHAR);
                mr.pbMDData       = reinterpret_cast<unsigned char *>(szVirPath);

                // Write MD_VR_PATH value
                hr = pIMSAdminBase->SetData( hMeta, PRINTERS, &mr );
                fRet = SUCCEEDED( hr );

                // Set the default authentication method
                if( fRet ) {

                    DWORD dwAuthorization = MD_AUTH_NT;     // NTLM only.

                    mr.dwMDIdentifier = MD_AUTHORIZATION;
                    mr.dwMDAttributes = METADATA_INHERIT;   // need to inherit so that all subdirs are also protected.
                    mr.dwMDUserType   = IIS_MD_UT_FILE;
                    mr.dwMDDataType   = DWORD_METADATA;
                    mr.dwMDDataLen    = sizeof(DWORD);
                    mr.pbMDData       = reinterpret_cast<unsigned char *>(&dwAuthorization);

                    // Write MD_AUTHORIZATION value
                    hr = pIMSAdminBase->SetData( hMeta, PRINTERS, &mr );
                    fRet = SUCCEEDED( hr );
                }
            }
        }
    }

    // In the following, do the stuff that we always want to do to the virtual dir, regardless of Admin's setting.


    if( fRet ) {

        dwAccessPerm = MD_ACCESS_READ | MD_ACCESS_SCRIPT;

        mr.dwMDIdentifier = MD_ACCESS_PERM;
        mr.dwMDAttributes = METADATA_INHERIT;    // Make it inheritable so all subdirectories will have the same rights.
        mr.dwMDUserType   = IIS_MD_UT_FILE;
        mr.dwMDDataType   = DWORD_METADATA;
        mr.dwMDDataLen    = sizeof(DWORD);
        mr.pbMDData       = reinterpret_cast<unsigned char *>(&dwAccessPerm);

        // Write MD_ACCESS_PERM value
        hr = pIMSAdminBase->SetData( hMeta, PRINTERS, &mr );
        fRet = SUCCEEDED( hr );
    }

    if( fRet ) {

        PWCHAR  szDefLoadFile = L"ipp_0001.asp";

        mr.dwMDIdentifier = MD_DEFAULT_LOAD_FILE;
        mr.dwMDAttributes = METADATA_INHERIT;
        mr.dwMDUserType   = IIS_MD_UT_FILE;
        mr.dwMDDataType   = STRING_METADATA;
        mr.dwMDDataLen    = (wcslen(szDefLoadFile) + 1) * sizeof(WCHAR);
        mr.pbMDData       = reinterpret_cast<unsigned char *>(szDefLoadFile);

        // Write MD_DEFAULT_LOAD_FILE value
        hr = pIMSAdminBase->SetData( hMeta, PRINTERS, &mr );
        fRet = SUCCEEDED( hr );
    }

    if( fRet ) {

        PWCHAR  szKeyType = IIS_CLASS_WEB_VDIR_W;

        mr.dwMDIdentifier = MD_KEY_TYPE;
        mr.dwMDAttributes = METADATA_INHERIT;
        mr.dwMDUserType   = IIS_MD_UT_SERVER;
        mr.dwMDDataType   = STRING_METADATA;
        mr.dwMDDataLen    = (wcslen(szKeyType) + 1) * sizeof(WCHAR);
        mr.pbMDData       = reinterpret_cast<unsigned char *>(szKeyType);

        // Write MD_DEFAULT_LOAD_FILE value
        hr = pIMSAdminBase->SetData( hMeta, PRINTERS, &mr );
        fRet = SUCCEEDED( hr );
    }

    pIMSAdminBase->CloseKey( hMeta );

    return fRet;
}




//
//
// Removes "printers" virtual dir from IIS metabase ws3vc\1\root\printers
//
//
BOOL
RemoveVirtualDir(
    IMSAdminBase *pIMSAdminBase
)
{
    METADATA_HANDLE hMeta = NULL;       // handle to metabase
    HRESULT hr;


    // Attempt to open the virtual dir set on Web server #1 (default server)
    hr = pIMSAdminBase->OpenKey( METADATA_MASTER_ROOT_HANDLE,
                         szW3SvcRootPath,
                         METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                         MY_META_TIMEOUT,
                         &hMeta );

    // Create the key if it does not exist.
    if( FAILED( hr ))
        return FALSE;

    pIMSAdminBase->DeleteKey( hMeta, PRINTERS );  // We don't check the retrun value since the key may already not exist and we could get an error for that reason.

    pIMSAdminBase->CloseKey( hMeta );

    return TRUE;
}





//=====================================================================================================
//=====================================================================================================
//=====================================================================================================
//
// Adding printer shares:
//
//  To support http://<server>/<share>, we create a virtual directory with a redirect property.
//
//
//=====================================================================================================
//=====================================================================================================
//=====================================================================================================

//=====================================================================================================
//
//  This function may be called during initialization time after fW3SvcInstalled is set,
//  which means, a printer maybe webshared twice (once in InstallWebPrnSvcWorkerThread,
//  and the other time when WebShare () is calleb by  ShareThisPrinter() in net.c by
//  FinalInitAfterRouterInitCompleteThread () during localspl initialization time.
//
//=====================================================================================================

void
WebShare(
    LPWSTR pShareName
)
{
    WebShareManagement( pShareName, TRUE );
}

void
WebShareWorker(
    LPWSTR pShareName
)
{
    HRESULT hr;                         // com error status

    SPLASSERT( pIMeta != NULL );

    // Pass the inner dumb pointer for the callee to use
    if( CreateVirtualDirForPrinterShare( pIMeta, pShareName ))

        // Flush out the changes and close
        // Call SaveData() after making bulk changes, do not call it on each update
        hr = pIMeta->SaveData();
}


BOOL
CreateVirtualDirForPrinterShare(
    IMSAdminBase *pIMSAdminBase,
    LPWSTR       pShareName
)
{
    METADATA_HANDLE hMeta = NULL;       // handle to metabase
    WCHAR   szOldURL[MAX_PATH];
    WCHAR   szPath[MAX_PATH];
    DWORD   dwMDRequiredDataLen;
    DWORD   dwAccessPerm;
    METADATA_RECORD mr;
    HRESULT hr;
    BOOL    fRet;


    // Attempt to open the virtual dir set on Web server #1 (default server)
    hr = pIMSAdminBase->OpenKey( METADATA_MASTER_ROOT_HANDLE,
                         szW3SvcRootPath,
                         METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                         MY_META_TIMEOUT,
                         &hMeta );

    // Create the key if it does not exist.
    if( FAILED( hr ))
        return FALSE;


    fRet = TRUE;

    mr.dwMDIdentifier = MD_HTTP_REDIRECT;
    mr.dwMDAttributes = 0;
    mr.dwMDUserType   = IIS_MD_UT_FILE;
    mr.dwMDDataType   = STRING_METADATA;
    mr.dwMDDataLen    = sizeof( szOldURL );
    mr.pbMDData       = reinterpret_cast<unsigned char *>(szOldURL);

    // Read LM/W3Svc/1/Root/Printers to see if MD_HTTP_REDIRECT exists.
    // Note that we are only concerned with the presence of the vir dir,
    // not any properties it might have.
    //
    hr = pIMSAdminBase->GetData( hMeta, pShareName, &mr, &dwMDRequiredDataLen );

    if( FAILED( hr )) {

        fRet = FALSE;

        // Notice if the virtual dir exists, we won't touch it. One scenario is
        // if there is a name collision between a printer sharename and an existing,
        // unrelated virtual dir.
        if( HRESULT_CODE(hr) == ERROR_PATH_NOT_FOUND ) {

            // Write both the key and the values if GetData() failed with any of the two errors.

            pIMSAdminBase->AddKey( hMeta, pShareName );


            dwAccessPerm = MD_ACCESS_READ;

            mr.dwMDIdentifier = MD_ACCESS_PERM;
            mr.dwMDAttributes = 0;      // no need for inheritence
            mr.dwMDUserType   = IIS_MD_UT_FILE;
            mr.dwMDDataType   = DWORD_METADATA;
            mr.dwMDDataLen    = sizeof(DWORD);
            mr.pbMDData       = reinterpret_cast<unsigned char *>(&dwAccessPerm);

            // Write MD_ACCESS_PERM value
            hr = pIMSAdminBase->SetData( hMeta, pShareName, &mr );
            fRet = SUCCEEDED( hr );

            if( fRet ) {

                PWCHAR  szKeyType = IIS_CLASS_WEB_VDIR_W;

                mr.dwMDIdentifier = MD_KEY_TYPE;
                mr.dwMDAttributes = 0;   // no need for inheritence
                mr.dwMDUserType   = IIS_MD_UT_FILE;
                mr.dwMDDataType   = STRING_METADATA;
                mr.dwMDDataLen    = (wcslen(szKeyType) + 1) * sizeof(WCHAR);
                mr.pbMDData       = reinterpret_cast<unsigned char *>(szKeyType);

                // Write MD_DEFAULT_LOAD_FILE value
                hr = pIMSAdminBase->SetData( hMeta, pShareName, &mr );
                fRet = SUCCEEDED( hr );
            }

            if( fRet ) {


                WCHAR szURL[MAX_PATH];

                wsprintf( szURL, L"/printers/%ws/.printer", pShareName );

                mr.dwMDIdentifier = MD_HTTP_REDIRECT;
                mr.dwMDAttributes = 0;   // no need for inheritence
                mr.dwMDUserType   = IIS_MD_UT_FILE;
                mr.dwMDDataType   = STRING_METADATA;
                mr.dwMDDataLen    = (wcslen(szURL) + 1) * sizeof(WCHAR);
                mr.pbMDData       = reinterpret_cast<unsigned char *>(szURL);

                // Write MD_DEFAULT_LOAD_FILE value
                hr = pIMSAdminBase->SetData( hMeta, pShareName, &mr );
                fRet = SUCCEEDED( hr );
            }
        }
    }

    pIMSAdminBase->CloseKey( hMeta );

    return fRet;
}


//=====================================================================================================
//=====================================================================================================
//=====================================================================================================
//
// Removing printer shares
//
//
//=====================================================================================================
//=====================================================================================================
//=====================================================================================================

void
WebUnShare(
    LPWSTR pShareName
)
{
    WebShareManagement( pShareName, FALSE );
}


void
WebUnShareWorker(
    LPWSTR pShareName
)
{
    HRESULT hr;                         // com error status

    SPLASSERT( pIMeta != NULL );

    // Pass the inner dumb pointer for the callee to use
    if( RemoveVirtualDirForPrinterShare( pIMeta, pShareName ))

        // Flush out the changes and close
        // Call SaveData() after making bulk changes, do not call it on each update
        hr = pIMeta->SaveData();
}


BOOL
RemoveVirtualDirForPrinterShare(
    IMSAdminBase *pIMSAdminBase,
    LPWSTR       pShareName
)
{
    METADATA_HANDLE hMeta = NULL;       // handle to metabase
    HRESULT hr;


    // Attempt to open the virtual dir set on Web server #1 (default server)
    hr = pIMSAdminBase->OpenKey( METADATA_MASTER_ROOT_HANDLE,
                         szW3SvcRootPath,
                         METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
                         MY_META_TIMEOUT,
                         &hMeta );

    // Create the key if it does not exist.
    if( FAILED( hr ))
        return FALSE;

    pIMSAdminBase->DeleteKey( hMeta, pShareName );  // We don't check the retrun value since the key may already not exist and we could get an error for that reason.
    pIMSAdminBase->CloseKey( hMeta );

    return TRUE;
}

CWebShareData::CWebShareData (LPWSTR pszShareName)
{
    m_bValid = FALSE;
    m_pszShareName = NULL;

    if (m_pszShareName = new WCHAR[lstrlen (pszShareName) +1]) {
        lstrcpy (m_pszShareName, pszShareName);
        m_bValid = TRUE;
    }
}

CWebShareData::~CWebShareData ()
{
    if (m_pszShareName) {
        delete [] m_pszShareName;
    }
}

void CWebShareList::WebSharePrinterList ()
{
    CSingleItem<CWebShareData*> * pItem = m_Dummy.GetNext();

    CWebShareData * pData = NULL;

    while (pItem && (pData = pItem->GetData ()) && pData->m_bValid) {

        WebShareWorker (pData->m_pszShareName);
        pItem = pItem->GetNext ();
    }
}