//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
#include "stdafx.h"
#include "api.h"
#include "depsettings.h"
#include "resource.h"
#include <adserr.h>


#define INITGUID // must be before iadmw.h
#include <iadmw.h>      // Interface header

// for the IID_IISWebService object
#include "iiisext.h"
#include "iisext_i.c"

#pragma warning(disable:4571) //catch(...) blocks compiled with /EHs do NOT catch or re-throw Structured Exceptions

class CIISMutex
{
public:
	HANDLE hMutex;
	CIISMutex()
	{
		hMutex = ::CreateMutex(NULL, FALSE, _T("VC8_VCDEPLOY"));
	}
	bool Lock()
	{
		printf("Trying to lock mutex %lx\n", (DWORD_PTR)hMutex);
		DWORD dw = WaitForSingleObject(hMutex, 10000);
		return (dw == WAIT_OBJECT_0);
	}
	~CIISMutex()
	{
		printf("Releasing mutex %lx\n", (DWORD_PTR)hMutex);
		ReleaseMutex(hMutex);
		CloseHandle(hMutex);
	}
};

HRESULT AddWebSvcExtention(WCHAR* wszRootWeb6, LPWSTR lpwszFileName,VARIANT_BOOL bEnabled,LPWSTR lpwszGroupID,VARIANT_BOOL bDeletableThruUI,LPWSTR lpwszGroupDescription)
{
    HRESULT hrRet = S_FALSE;

    CComPtr<IISWebService> spWeb;
    HRESULT hr = ADsGetObject(wszRootWeb6, IID_IISWebService, (void**)&spWeb);
    if (SUCCEEDED(hr) && spWeb != NULL)
    {
		CComVariant var1, var2;

        var1.vt = VT_BOOL;
        var1.boolVal = bEnabled;

        var2.vt = VT_BOOL;
        var2.boolVal = bDeletableThruUI;

		CComBSTR bstrFileName(lpwszFileName);
		CComBSTR bstrGroupID(lpwszGroupID);
		CComBSTR bstrGroupDescription(lpwszGroupDescription);
        hr = spWeb->AddExtensionFile(bstrFileName,var1,bstrGroupID,var2,bstrGroupDescription);
        if (SUCCEEDED(hr))
        {
            hrRet = S_OK;
        }
        else
        {
            OutputDebugString(_T("failed,probably already exists\r\n"));
        }
        VariantClear(&var1);
        VariantClear(&var2);
    }

    return hrRet;
}

HRESULT RemoveWebSvcExtention(WCHAR* wszRootWeb6, LPWSTR lpwszFileName)
{
    HRESULT hrRet = S_FALSE;

    IISWebService * pWeb = NULL;
    HRESULT hr = ADsGetObject(wszRootWeb6, IID_IISWebService, (void**)&pWeb);
    if (SUCCEEDED(hr) && NULL != pWeb)
    {
		CComBSTR bstrFileName(lpwszFileName);
        hr = pWeb->DeleteExtensionFileRecord(bstrFileName);
        if (SUCCEEDED(hr))
        {
            hrRet = S_OK;
        }
        else
        {
            OutputDebugString(_T("failed,probably already gone\r\n"));
        }
        pWeb->Release();
    }

    return hrRet;
}

HRESULT AddApplicationDependencyUponGroup(WCHAR* wszRootWeb6, LPWSTR lpwszAppName,LPWSTR lpwszGroupID)
{
    HRESULT hrRet = S_FALSE;

    IISWebService * pWeb = NULL;
    HRESULT hr = ADsGetObject(wszRootWeb6, IID_IISWebService, (void**)&pWeb);
    if (SUCCEEDED(hr) && NULL != pWeb)
    {
		CComBSTR bstrAppName(lpwszAppName);
		CComBSTR bstrGroupID(lpwszGroupID);
        hr = pWeb->AddDependency(bstrAppName,bstrGroupID);
        if (SUCCEEDED(hr))
        {
            hrRet = S_OK;
        }
        else
        {
            OutputDebugString(_T("failed,probably already exists\r\n"));
        }
        pWeb->Release();
    }

    return hrRet;
}

HRESULT RemoveApplicationDependencyUponGroup(WCHAR* wszRootWeb6, LPWSTR lpwszAppName,LPWSTR lpwszGroupID)
{
    HRESULT hrRet = S_FALSE;

    IISWebService * pWeb = NULL;
    HRESULT hr = ADsGetObject(wszRootWeb6, IID_IISWebService, (void**)&pWeb);
    if (SUCCEEDED(hr) && NULL != pWeb)
    {
		CComBSTR bstrAppName(lpwszAppName);
		CComBSTR bstrGroupID(lpwszGroupID);
        hr = pWeb->RemoveDependency(bstrAppName,bstrGroupID);
        if (SUCCEEDED(hr))
        {
            hrRet = S_OK;
        }
        else
        {
            OutputDebugString(_T("failed,probably already gone\r\n"));
        }
        pWeb->Release();
    }

    return hrRet;
}

LPSTR _ReverseFind(LPSTR szStr, size_t nLen, char ch)
{
	LPSTR sz = szStr+nLen;
	while (sz >= szStr)
	{
		if (*sz == ch)
		{
			return sz;
		}
		--sz;
	}

	return NULL;
}

BOOL RecursiveCreateDirectoryHelper(LPSTR szPath, size_t nLen)
{
	WIN32_FILE_ATTRIBUTE_DATA fad;
	BOOL bRet;
	LPSTR szCurrent;
	char chTmp;

	memset(&fad, 0x00, sizeof(fad));
	bRet = ::GetFileAttributesExA(szPath, GetFileExInfoStandard, &fad);
	if (!bRet)
	{
		if ((GetLastError() == ERROR_PATH_NOT_FOUND) ||
			(GetLastError() == ERROR_FILE_NOT_FOUND))
		{
			szCurrent = _ReverseFind(szPath, nLen, '\\');
			if (szCurrent != NULL)
			{
				nLen -= (szPath-szCurrent);
				chTmp = *szCurrent;
				*szCurrent = '\0';
				bRet = RecursiveCreateDirectoryHelper(szPath, nLen);
				if (!bRet)
				{
					return bRet;
				}

				*szCurrent = chTmp;
				bRet = ::CreateDirectory(szPath, NULL);
				// VSW#472354 - There is a race condition where if two projects are 
				// building at the same time, the deployment of the other can cause 
				// this directory to get created after we check that it doesn't exist.
				if (!bRet && (GetLastError() != ERROR_ALREADY_EXISTS))
				{
					return bRet;
				}
			}
		}
	}
	return TRUE;
}

BOOL RecursiveCreateDirectory(LPCSTR szDir)
{
	char szPath[MAX_PATH];
	size_t nLen;

	nLen = strlen(szDir);
	if (nLen >= MAX_PATH)
	{
		SetLastError(ERROR_BUFFER_OVERFLOW);
		return FALSE;
	}
	memcpy(szPath, szDir, nLen);
	szPath[nLen] = '\0';

	return RecursiveCreateDirectoryHelper(szPath, nLen);
}

int PrintWarning(unsigned int nMsgID)
{
	return PrintMessage(_T("warning"), nMsgID, NULL);
}

int PrintWarning(unsigned int nMsgID, TCHAR *szExtraInfo)
{
	return PrintMessage(_T("warning"), nMsgID, szExtraInfo);
}

int PrintWarningWithLastWin32(unsigned int nMsgID)
{
	TCHAR szMsg[512];
	if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
					NULL,
					GetLastError(),
					NULL,
					szMsg,
					512,
					NULL))
	{
		return PrintWarning(nMsgID, szMsg);
	}
	return PrintWarning(nMsgID);
}

int PrintWarningFormatted(LPCTSTR szWarning)
{
	return PrintMessage(_T("warning"), szWarning, NULL);
}

int PrintError(unsigned int nMsgID)
{
	return PrintMessage(_T("error"), nMsgID, NULL);
}

int PrintError(unsigned int nMsgID, LPCTSTR szExtraInfo)
{
	return PrintMessage(_T("error"), nMsgID, szExtraInfo);
}

int PrintErrorWithLastWin32(unsigned int nMsgID)
{
	TCHAR szMsg[512];
	if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
					NULL,
					GetLastError(),
					NULL,
					szMsg,
					512,
					NULL))
	{
		return PrintError(nMsgID, szMsg);
	}
	return PrintError(nMsgID);
}

int PrintErrorFormatted(LPCTSTR szError)
{
	return PrintMessage(_T("error"), szError, NULL);
}

int PrintMessage(LPCTSTR szPrefix, unsigned int nMsgID, LPCTSTR szExtraInfo)
{
	CString strMsg;
	if (!LoadStringFromModule(nMsgID, strMsg))
		_tprintf("Failed to load error string %x\n", nMsgID);
	else
	{
		return PrintMessage(szPrefix, (LPCTSTR)strMsg, szExtraInfo);
	}
	return 0;
}

int PrintMessage(LPCTSTR szPrefix, LPCTSTR szMsg, LPCTSTR szExtraInfo)
{
	if (szExtraInfo)
		_tprintf("vcdeploy : %s %s %s.\n", szPrefix, szMsg, szExtraInfo);
	else
		_tprintf("vcdeploy : %s %s\n", szPrefix, szMsg);

	return ATLSDPLY_SUCCESS;
}

int PrintMessage(unsigned int nMsgID)
{
	CString strMsg;
	if (!LoadStringFromModule(nMsgID, strMsg))
		_tprintf("Failed to load error string %x\n", nMsgID);
	else
	{
		_tprintf("%s\n", static_cast<const char *>(strMsg));
	}
	return 0;
}

bool FormatMsg(CString& strDest, UINT uId, ...)
{
    CString strFmt;
    if (!strFmt.LoadString(uId))
        return false;
	va_list argList;
	va_start( argList, uId );
    strDest.FormatV(strFmt, argList);
	va_end( argList );
	return true;
}

int ProcessSettings(CDepSettings *pSettings)
{
	int result = ATLSDPLY_FAIL;
	long nListLen = 0;
	CComPtr<IXMLDOMNodeList> spHostList;
	if (!pSettings)
	{
		PrintError(IDS_UNEXPECTED);
		return ATLSDPLY_FAIL;
	}

	// Get the list of host's from the settings object
	// We should have already failed loading
	// settings if there was no host list.
	result = pSettings->GetHostList(&spHostList);
	RETURN_ON_UNEXPECTED_HRP(result, spHostList); 
												
	result = spHostList->get_length(&nListLen);
	RETURN_ON_UNEXPECTED(result);

	if (nListLen == 0)
		return ATLSDPLY_SUCCESS; // nothing to do

	// start processing stuff for each host
	CComPtr<IXMLDOMNode> spHostNode;
	CComBSTR bstrHost;
    for (long i = 0; i<nListLen; i++)
	{
		CADSIHelper adsHelper;
		result = spHostList->get_item(i, &spHostNode);
		RETURN_ON_UNEXPECTED_HRP(result, spHostNode);

		result = spHostNode->get_text(&bstrHost);
		RETURN_ON_UNEXPECTED(result);

		if (bstrHost[0] == L'\0')
		{
			PrintWarning(IDS_WARNING_EMPTY_HOST_TAG);
			continue; // empty host name, we'll just press on in this case
		}

		if (S_OK == (result = CADSIHelper::FastLocalRootCheck(pSettings->GetVirtDirName())))
		{
			// VRoot exists
			// Unload IIS as required by settings document
			if (pSettings->GetUnloadBeforeCopy())
			{
				result = LocalW3svcReset();
				RETURN_ON_FAIL(result);
			}

			// do filesystem updates, this includes creating directories for
			// new files and copying filesystem files.
			result = UpdateFileSystem(pSettings);
			RETURN_ON_FAIL(result);

		//	result = CheckMinVRootConfigSettings(pSettings);
		//	if (result == ATLSDPLY_SUCCESS)
		//	{
				// connect to ads to configure the root
				result = CheckVRootExistance(bstrHost, &adsHelper, pSettings);
				RETURN_ON_FAIL(result);

				// configure the virtual directory according to the settings file.
				result = ConfigureVRoot(&adsHelper, pSettings, bstrHost);
				RETURN_ON_FAIL(result);
	
				// Commit any changes in the metabase.
				result = adsHelper.SetInfo();
				RETURN_ON_UNEXPECTED(result);

				result = CADSIHelper::SaveMetabaseData();
				if (result != S_OK)
				{
					CString strMsg;
					FormatMsg(strMsg, IDS_WARNING_FAILED_METABSESAVE, result);
					PrintWarningFormatted(strMsg);
				}

		//	}
			result = RegisterExtension(&adsHelper, pSettings);
			RETURN_ON_FAIL2(result,result);

		}
		else
		{
			// copy system files. Has to be done first so we 
			// don't get errors from IIS.
			result = UpdateFileSystem(pSettings);
			RETURN_ON_FAIL(result);
    		
			// create the virtual directory
			result = CreateVRoot(bstrHost, &adsHelper, pSettings);
			RETURN_ON_FAIL(result);

		//	result = CheckMinVRootConfigSettings(pSettings);
		//	if (result == ATLSDPLY_SUCCESS)
		//	{
				// configure the virtual directory according to the settings file.
				result = ConfigureVRoot(&adsHelper, pSettings, bstrHost);
				RETURN_ON_FAIL(result);
		//	}

			// Commit any changes in the metabase.
			result = adsHelper.SetInfo();
			RETURN_ON_UNEXPECTED(result);

			result = RegisterExtension(&adsHelper, pSettings);
			RETURN_ON_FAIL2(result,result);
			
			// finally, we persist the metabase changes. We don't check
			result = CADSIHelper::SaveMetabaseData();
			if (result != S_OK)
			{
				CString strMsg;
				FormatMsg(strMsg, IDS_WARNING_FAILED_METABSESAVE, result);
				PrintWarningFormatted(strMsg);
			}
			result = ATLSDPLY_SUCCESS;

		}
	}
	return ATLSDPLY_SUCCESS;
}

int ConfigureRestrictionList(CADSIHelper * /*pAdsHelper*/,
							 CDepSettings *pSettings,
							 const wchar_t* wszHost)
{
	HRESULT hr = E_FAIL;
	try
	{
		LPCTSTR szvdfspath = pSettings->GetVirtDirFSPath();
		LPCTSTR szextfilename = pSettings->GetExtensionFileName();

		// need all of these if we are going to get anywhere!
		if (szvdfspath == NULL || *szvdfspath == L'\0' ||
			szextfilename == NULL || *szextfilename == L'\0' ||
			wszHost == NULL || *wszHost == L'\0')
		{
			return ATLSDPLY_SUCCESS;
		}

		CStringW strSvc(L"IIS://");
		strSvc += wszHost;
		strSvc += L"/W3SVC";

		if( pSettings->GetIISMajorVer() < 6 )
			return ATLSDPLY_SUCCESS;


		// create the full path to the isapi
		CPathW strIsapiPath;
		CStringW strVirtDirPath(szvdfspath);
		CStringW strExtFileName(szextfilename);
		strIsapiPath.Combine(strVirtDirPath, strExtFileName);

		CStringW strDescription( pSettings->GetVirtDirName() );

		LPWSTR pwszSvc = const_cast<WCHAR*>((LPCWSTR)strSvc);
		LPWSTR pwszIsapiPath = const_cast<WCHAR*>((LPCWSTR)strIsapiPath);
		LPWSTR pwszDescription = const_cast<WCHAR*>((LPCWSTR)strDescription);

		// Add strIsapiPath to the restrictionlist, make sure it's enabled, 
		// and that the user is able to remove the entry thru the UI if they wanted to
		hr = AddWebSvcExtention( pwszSvc, pwszIsapiPath, VARIANT_TRUE, pwszDescription, VARIANT_TRUE, pwszDescription );

		if( hr == S_OK )
		{
			hr = AddApplicationDependencyUponGroup( pwszSvc, pwszDescription, pwszDescription );
		}

		if( hr == S_FALSE ) // the strIsapiPath has already been on the restriction list.
			hr = S_OK;
	}
	catch(...)
	{
		RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);
	}

	
	return hr == S_OK ? ATLSDPLY_SUCCESS : ATLSDPLY_FAIL;
}
/*
int ConfigureRestrictionList(CADSIHelper *pAdsHelper,
							 CDepSettings *pSettings,
							 const wchar_t* wszHost)
{
	HRESULT hr = E_FAIL;
	try
	{
		LPCTSTR szvdfspath = pSettings->GetVirtDirFSPath();
		LPCTSTR szextfilename = pSettings->GetExtensionFileName();

		// need all of these if we are going to get anywhere!
		if (szvdfspath == NULL || *szvdfspath == L'\0' ||
			szextfilename == NULL || *szextfilename == L'\0' ||
			wszHost == NULL || *wszHost == L'\0')
		{
			return ATLSDPLY_SUCCESS;
		}

		ATLASSERT(pAdsHelper != NULL);
		if (!pAdsHelper)
			RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);

		CADSIHelper service;
		CStringW strSvc(L"IIS://");
		strSvc += wszHost;
		strSvc += L"/W3SVC";

		if (S_OK != service.Connect(strSvc))
			RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);

		CComVariant vIRL;
		hr = service.GetProperty(L"IsapiRestrictionList", vIRL);
		if (hr != S_OK)
		{
			if (hr != E_ADS_PROPERTY_NOT_SUPPORTED && hr != DISP_E_UNKNOWNNAME)
			{
				RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);
			}
			else
				return ATLSDPLY_SUCCESS; // Property only exists on IIS > 6
		}

		if ( !(vIRL.vt & (VT_ARRAY | VT_VARIANT)) )
			RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);

		CComSafeArray<VARIANT> saCurrentList;
		saCurrentList.Attach(vIRL.parray);
		vIRL.vt = VT_EMPTY;
		// The first string in the list is either "1" or "0"
		// If it's one, then all ISAPI extensions are allowed to run except
		// the ones in the list. Since this is a new vroot for a new extension
		// we assume our isapi would not be on the list if the first string is
		// "1" so if it is "1", we're done.
		// If it's "0" then no isapis are allowed to run except those on the list
		// so we need to add ourselves to the list. 

		// look at the first string
		CComVariant v = saCurrentList.GetAt(0);
		if (v.vt != VT_BSTR)
			RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);

		if (!wcscmp(L"1", v.bstrVal))
			return ATLSDPLY_SUCCESS; // we're done, everything is allowed to run

		// create the full path to the isapi
		CPathW strIsapiPath;
		CStringW strVirtDirPath(szvdfspath);
		CStringW strExtFileName(szextfilename);
		strIsapiPath.Combine(strVirtDirPath, strExtFileName);

		// make sure this isapi isn't already in the list and skip
		// the first string because it's either "1" or "0"
		int count = saCurrentList.GetCount(); // count of dim 0
		for (int i=1; i<count; i++)
		{
			CComVariant &v = saCurrentList.GetAt(i);
			if (v.vt == VT_BSTR && !strIsapiPath.m_strPath.Compare(v.bstrVal))
				return ATLSDPLY_SUCCESS;
		}

		// otherwise, add the full path to this isapi to the IRL list
		CComSafeArray<VARIANT> saNewList(saCurrentList); // Copies source safearray to our new array
		if (!saNewList.m_psa)
			RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);

		hr = E_FAIL;
		CComVariant vNewItem(strIsapiPath);
		hr = saNewList.Add(vNewItem);
		if (hr == S_OK)
		{
			CComVariant vNewList(saNewList); // Copies the safearray into the variant.
			hr = service.SetProperty(L"IsapiRestrictionList", vNewList);
			if (hr == S_OK)
			{
				hr = service.SetInfo();
				if (hr == S_OK)
				{
					// Always notify the user that we have enabled
					// their ISAPI.
					CString strMsg;
					CString strPath(strIsapiPath);
					FormatMsg(strMsg, IDS_UPDATEIRL, (LPCTSTR)strPath);
					_tprintf((LPCTSTR)strMsg);
				}
			}
		}
	}
	catch(...)
	{
		RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);
	}

	
	return hr == S_OK ? ATLSDPLY_SUCCESS : ATLSDPLY_FAIL;
}
*/

int CheckVRootExistance(const CComBSTR& bstrHostName,
						CADSIHelper *pAdsHelper,
						CDepSettings *pSettings)
{
	ATLASSERT(pAdsHelper);
	ATLASSERT(pSettings);
	if (!pAdsHelper || !pSettings)
		RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);
	HRESULT hr = E_FAIL;

	try
	{
		CStringW strVirtDirName(pSettings->GetVirtDirName());
		CFixedStringT<CStringW, MAX_PATH> strAdsPathRoot(L"IIS://");
		strAdsPathRoot += bstrHostName;

		TCHAR szBuf[513];
		szBuf[0] = _T('\0');
		DWORD dwBufSize = (sizeof(szBuf)/sizeof(TCHAR))-1;
		// get environment variable for web site (just a number)
		dwBufSize = GetEnvironmentVariable(_T("VCDEPLOY_WEBSITE"), szBuf, dwBufSize);
		if ((!dwBufSize) || (dwBufSize >= (sizeof(szBuf)/sizeof(TCHAR))))
		{
			szBuf[0] = _T('1');
			dwBufSize = 1;
		}
		szBuf[dwBufSize] = _T('\0');

//		strAdsPathRoot += L"/W3SVC/1/ROOT";
		strAdsPathRoot.Append(L"/W3SVC/", sizeof("/W3SVC/")-1);
		strAdsPathRoot.Append(CT2CW(szBuf), dwBufSize);
		strAdsPathRoot.Append(L"/ROOT", sizeof("/ROOT")-1);

		CFixedStringT<CStringW, MAX_PATH> strAdsPathFull(strAdsPathRoot);
		strAdsPathFull += _T('/');
		strAdsPathFull += strVirtDirName;


		// first, see if we can bind to the full path, which would mean the virtual
		// directory already exists
		hr = pAdsHelper->Connect(strAdsPathFull);
		RETURN_ON_UNEXPECTED(hr);
	}
	catch(...)
	{
		// catches CString allocation problems
		RETURN_ON_FAIL2(hr, IDS_ERR_OUTOFMEM);
	}
	return hr;
}


int CreateVRoot(const CComBSTR& bstrHostName,
					CADSIHelper *pAdsHelper,
					CDepSettings *pSettings)
{
	HRESULT hr = E_FAIL;
	ATLASSERT(pAdsHelper);
	ATLASSERT(pSettings);
	if (!pAdsHelper || !pSettings)
		RETURN_ON_UNEXPECTED(ATLSDPLY_FAIL);

	try
	{
		CStringW strVirtDirName(pSettings->GetVirtDirName());
		CFixedStringT<CStringW, MAX_PATH> strAdsPathRoot(L"IIS://");
		strAdsPathRoot += bstrHostName;

		TCHAR szBuf[513];
		szBuf[0] = _T('\0');
		DWORD dwBufSize = (sizeof(szBuf)/sizeof(TCHAR))-1;
		// get environment variable for web site (just a number)
		dwBufSize = GetEnvironmentVariable(_T("VCDEPLOY_WEBSITE"), szBuf, dwBufSize);
		if ((!dwBufSize) || (dwBufSize >= (sizeof(szBuf)/sizeof(TCHAR))))
		{
			szBuf[0] = _T('1');
			dwBufSize = 1;
		}
		szBuf[dwBufSize] = _T('\0');

//		strAdsPathRoot += L"/W3SVC/1/ROOT";
		strAdsPathRoot.Append(L"/W3SVC/", sizeof("/W3SVC/")-1);
		strAdsPathRoot.Append(CT2CW(szBuf), dwBufSize);
		strAdsPathRoot.Append(L"/ROOT", sizeof("/ROOT")-1);

		// Root doesn't exist, try to create it
		hr = pAdsHelper->Connect(strAdsPathRoot);
		RETURN_ON_FAIL2(hr, IDS_ERR_CONNECTADSFAILED);

		short nIso = (short)pSettings->GetAppIsolation();
		if( pSettings->SkipVirtDirCreation() )
		{
			hr = pAdsHelper->CreateAppOnly(strVirtDirName, nIso, NULL);
			RETURN_ON_FAIL2(hr, IDS_ERR_CREATEVROOTFAILED);
		}
		else
		{
			//ATLASSERT(nIso >= 0 && nIso <= 2);
			hr = pAdsHelper->CreateVRoot(strVirtDirName, nIso, NULL);
			RETURN_ON_FAIL2(hr, IDS_ERR_CREATEVROOTFAILED);

			// the path to the root must be set at vroot creation time
			// it can't be accessed later.
			CComVariant val;
			val = pSettings->GetVirtDirFSPath();
			hr = pAdsHelper->SetProperty(L"Path", val);
			RETURN_ON_FAIL2(hr, IDS_ERR_SETADSPROPERTY);
		}
	}
	catch(...)
	{
		// catches CString allocation problems
		RETURN_ON_FAIL2(hr, IDS_ERR_OUTOFMEM);
	}
	return ATLSDPLY_SUCCESS;
}

int ConfigureVRoot(	CADSIHelper *pAdsHelper,
					CDepSettings *pSettings,
					const wchar_t* wszHost)
{
	ATLASSERT(pAdsHelper);
	ATLASSERT(pSettings);
	if (!pAdsHelper || !pSettings)
		return IDS_UNEXPECTED;

	HRESULT hr = E_FAIL;
	CComVariant val;

	// Set some general properties about the vroot
	val.vt = VT_BOOL;
	val.boolVal = VARIANT_TRUE;
	hr = pAdsHelper->SetProperty(L"AccessExecute", val);
	RETURN_ON_FAIL2(hr, IDS_ERR_SETADSPROPERTY);

	val = pSettings->GetVirtDirName();
	hr = pAdsHelper->SetProperty(L"AppPackageName", val);
	RETURN_ON_FAIL2(hr, IDS_ERR_SETADSPROPERTY);

    CComVariant varOrigName;
    hr = pAdsHelper->GetPropertySingle(L"AppFriendlyName", varOrigName);
	if (FAILED(hr) || varOrigName.vt != VT_BSTR || ::SysStringLen(varOrigName.bstrVal) == 0)
    {
    	val = pSettings->GetVirtDirName();
    	hr = pAdsHelper->SetProperty(L"AppFriendlyName", val);
    	RETURN_ON_FAIL2(hr, IDS_ERR_SETADSPROPERTY);
    }

	hr = SetRootAppMappings(pAdsHelper, pSettings);
	RETURN_ON_FAIL(hr);
	hr = ConfigureRestrictionList(pAdsHelper, pSettings, wszHost);
	return hr;
}

LONG FindMapping(CComSafeArray<VARIANT> *pArray, BSTR bstrExt)
{
	// if the array is empty, then the entry is not there
	if (!pArray->m_psa)
		return -1;

	// find an item that starts with bstrExt followed by ','
	CComBSTR bstrFullExt(bstrExt);
	bstrFullExt.Append(",");
	size_t nLen = wcslen(bstrFullExt);
	
	ULONG ulCount = pArray->GetCount();
	for (LONG lIndex=0; lIndex < (LONG) ulCount; lIndex++)
	{
		CComVariant var;
		var = pArray->GetAt(lIndex);
		if (var.vt == VT_BSTR && !_wcsnicmp(bstrFullExt, var.bstrVal, nLen))
			return lIndex;
	}
	return -1;
}

int SetRootAppMappings(	CADSIHelper *pAdsHelper,
						CDepSettings *pSettings)
{
	ATLASSERT(pAdsHelper);
	ATLASSERT(pSettings);
	if (!pAdsHelper || !pSettings)
		return IDS_UNEXPECTED;

	LPCTSTR szvdfspath = pSettings->GetVirtDirFSPath();
	LPCTSTR szextfilename = pSettings->GetExtensionFileName();
	if ((!szvdfspath || *szvdfspath == _T('\0') ) ||
		(!szextfilename || *szextfilename == _T('\0') )
		)
		return ATLSDPLY_SUCCESS; // not enough info!

	HRESULT hr = E_FAIL;
	CStringW rgMappings[MAX_VERB_BLOCKS];
	CComPtr<IXMLDOMNodeList> spMappingNodes;
	CPathW strIsapiPath;
	CStringW strVirtDirPath(szvdfspath);
	CStringW strExtFileName(szextfilename);
	strIsapiPath.Combine(strVirtDirPath, strExtFileName);
	hr = pSettings->GetAppMappingList(&spMappingNodes);
	RETURN_ON_UNEXPECTED(hr);

	if (!spMappingNodes)
		return ATLSDPLY_SUCCESS; // nothing to do
	
	long nMappingCount = 0, i=0;
	hr = spMappingNodes->get_length(&nMappingCount);
	RETURN_ON_UNEXPECTED(hr);

	if (nMappingCount <= 0)
		return ATLSDPLY_SUCCESS; // nothing to do

	if (nMappingCount > MAX_VERB_BLOCKS)
		RETURN_ON_FAIL2(ATLSDPLY_FAIL, ATLS_ERR_TOOMANYVERBBLOCKS);

	// get the current script mappings
	CComVariant vCurrentMappings;
	CComSafeArray<VARIANT> saCurrentMappings;

	hr = pAdsHelper->GetProperty(L"ScriptMaps", vCurrentMappings);
	if (SUCCEEDED(hr) && (vCurrentMappings.vt == (VT_ARRAY|VT_VARIANT)))
	{
		VARIANT vVal;
		VariantInit(&vVal);
		vCurrentMappings.Detach(&vVal);
		saCurrentMappings.Attach(vVal.parray);
	}

	CComBSTR strAttrName(L"fileext");
    // loop through the nodes and register the extensions
	for (i = 0; i<nMappingCount; i++)
	{
		CComPtr<IXMLDOMNode> spMappingNode, spExtAttr;
		CComPtr<IXMLDOMNamedNodeMap> spAttributes;
		CComBSTR bstrExt;
		hr = spMappingNodes->get_item(i, &spMappingNode);
		RETURN_ON_UNEXPECTED_HRP(hr, spMappingNode);

		// get the fileext attribute
		hr = spMappingNode->get_attributes(&spAttributes);
		RETURN_ON_FAIL2_HRP(hr, spAttributes, IDS_FILEEXTATTR_NOTFOUND);

		hr = spAttributes->getNamedItem(strAttrName, &spExtAttr);
		RETURN_ON_FAIL2_HRP(hr, spAttributes, IDS_FILEEXTATTR_NOTFOUND);

		hr = spExtAttr->get_text(&bstrExt);
		RETURN_ON_FAIL2(hr, IDS_FILEEXTATTR_INVALID);

		if (bstrExt[0] != L'.')
		{
			CComBSTR bstrTemp(L".");
			bstrTemp += bstrExt;
			bstrExt = bstrTemp;
		}

		// get the list of verbs to map to
		CComPtr<IXMLDOMNodeList> spExtVerbList;
		long nVerbs = 0;
		CComBSTR rgVerbs[MAX_VERB_COUNT];

		hr = spMappingNode->selectNodes(CComBSTR(L"VERB"), &spExtVerbList);
		if (hr != S_OK)
			continue; // no verbs, nothing to do

		RETURN_ON_UNEXPECTED_P(spExtVerbList);

		hr = spExtVerbList->get_length(&nVerbs);
		RETURN_ON_UNEXPECTED(hr);

		if (nVerbs == 0)
			continue; // no verbs, nothing to do

		if (nVerbs > MAX_VERB_COUNT)
		{
			PrintError(IDS_ERR_TOOMANYVERBS);
			return ATLSDPLY_FAIL;
		}

		CStringW strEntry;
		strEntry = bstrExt;
		strEntry += L",";
		strEntry += strIsapiPath;

		
		if (pSettings->GetIISMajorVer() > 4)
		{
			strEntry += L",0,"; // we already know nVerbs>0
			for (long z = 0; z<nVerbs; z++)
			{
				CComPtr<IXMLDOMNode> spVerbNode;
				CComBSTR bstrVerb;
				hr = spExtVerbList->get_item(z, &spVerbNode);
				RETURN_ON_UNEXPECTED(hr);

				hr = spVerbNode->get_text(&bstrVerb);
				RETURN_ON_UNEXPECTED(hr);

				strEntry += bstrVerb;
				if (z < nVerbs-1)
					strEntry +=  L',';
			}
		}
		else
			strEntry += L",0";
		LONG lIndex = FindMapping(&saCurrentMappings, bstrExt);
		if (lIndex >= 0)
		{
			// update the entry in the array
			CComVariant v(strEntry);
			saCurrentMappings.SetAt(lIndex, v, TRUE);
		}
		else
		{
			// add as a new entry
			rgMappings[i] = strEntry;
		}

#ifdef _DEBUG
		CString strOut;
		strOut.Format("Adding mapping %s\n",CW2A(strEntry));
		printf(strOut);
		ATLTRACE(L"Adding script mapping %s\n", strEntry);
#endif
	}

	CComSafeArray<VARIANT> rgsaMappings;
	for ( long y = 0; y<nMappingCount; y++)
	{
		if (rgMappings[y].GetLength() != 0)
		{
			if (S_OK != rgsaMappings.Add(CComVariant(CComBSTR(rgMappings[y]))))
				RETURN_ON_UNEXPECTED(E_FAIL);
		}
	}
	// set the safearray of BSTR 
	if (saCurrentMappings.m_psa)
	{
		if (rgsaMappings.m_psa)
			saCurrentMappings.Add(rgsaMappings);
		CComVariant vSafearray(saCurrentMappings);
		vSafearray.vt=VT_ARRAY|VT_VARIANT;
		hr = pAdsHelper->SetProperty(L"ScriptMaps", vSafearray);
	}
	else
	{
		CComVariant vSafearray(rgsaMappings);
		vSafearray.vt=VT_ARRAY|VT_VARIANT;
		hr = pAdsHelper->SetProperty(L"ScriptMaps", vSafearray);
	}
	RETURN_ON_FAIL2(hr, IDS_ERR_SETADSPROPERTY);
	return hr;
}

int LocalW3svcReset()
{
	// There is a race condition that can happen when deploying two projects
	// at the same time.  We need to stop/restart IIS as a single operation.
	// Use a named mutex that works across processes to serialize this action.
	CIISMutex iisMutex;
	iisMutex.Lock();

	DWORD dwRet = 0;
	// open service control manager
	SC_HANDLE hScm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_ENUMERATE_SERVICE);
	if (hScm==NULL)
	{
		PrintErrorWithLastWin32(IDS_FAILEDOPENSCM);
		return ATLSDPLY_FAIL;
	}

	// open W3SVC
	SC_HANDLE hW3Svc = OpenService(hScm, _T("W3SVC"), SERVICE_QUERY_STATUS|SERVICE_START|SERVICE_STOP);
	if (hW3Svc==NULL)
	{
		PrintErrorWithLastWin32(IDS_FAILEDOPENSVC);
		CloseServiceHandle(hScm);
		return ATLSDPLY_FAIL;
	}

	// stop W3SVC
	SERVICE_STATUS svcStatus;
	ZeroMemory(&svcStatus, sizeof(svcStatus));
	QueryServiceStatus(hW3Svc, &svcStatus);

	if (svcStatus.dwCurrentState == SERVICE_RUNNING)
	{
		ZeroMemory(&svcStatus, sizeof(svcStatus));
		if (!ControlService(hW3Svc, SERVICE_CONTROL_STOP, &svcStatus) &&
			GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
		{
			// couldn't send the control service command plus
			// the service isn't already stopped.
			PrintErrorWithLastWin32(IDS_FAILEDSTOPCOMMAND);
			CloseServiceHandle(hW3Svc);
			CloseServiceHandle(hScm);
			return ATLSDPLY_FAIL;
		}
	}

	
	if (svcStatus.dwCurrentState == SERVICE_STOP_PENDING)
	{
		printf("stopping W3SVC.");
		// Stop is pending, we'll have to wait
		for (int i=0;i<1000; i++)
		{
			// sleep
			//Sleep(svcStatus.dwWaitHint != 0 ? svcStatus.dwWaitHint : 100);
			Sleep(50);
			printf(".");
						
			//interogate
			ZeroMemory(&svcStatus, sizeof(svcStatus));
			if (!QueryServiceStatus(hW3Svc,  &svcStatus))
			{
				PrintErrorWithLastWin32(IDS_FAILEDQUERYSTATUS);
				CloseServiceHandle(hW3Svc);
				CloseServiceHandle(hScm);
				return ATLSDPLY_FAIL;
			}
			if (svcStatus.dwCurrentState == SERVICE_STOPPED)
			{
				// service is stopped
				break;
			}
		}
		printf("\n");
	}
	
	int nRetryCount=0;
	if (svcStatus.dwCurrentState == SERVICE_STOPPED)
	{
		printf("starting W3SVC.");
    	ZeroMemory(&svcStatus, sizeof(svcStatus));
    	if (!StartService(hW3Svc, NULL, NULL))
    	{
    		PrintErrorWithLastWin32(IDS_FAILEDSTARTSVC);
    		CloseServiceHandle(hW3Svc);
    		CloseServiceHandle(hScm);
    		return ATLSDPLY_FAIL;
    	}
    	// check for pending status
    	ZeroMemory(&svcStatus, sizeof(svcStatus));
    	if (!QueryServiceStatus(hW3Svc, &svcStatus))
    	{
    		PrintErrorWithLastWin32(IDS_FAILEDQUERYSTATUS);
    		CloseServiceHandle(hW3Svc);
    		CloseServiceHandle(hScm);
    		return ATLSDPLY_FAIL;
    	}

		if (svcStatus.dwCurrentState == SERVICE_START_PENDING)
		{

			// Start is pending, we'll have to wait.
			for (int i=0;i<1000; i++)
			{
				// sleep
				// Sleep(svcStatus.dwWaitHint != 0 ? svcStatus.dwWaitHint : 100);
				Sleep(50); //polling is faster than using their hint
				//interogate
				printf(".");
				ZeroMemory(&svcStatus, sizeof(svcStatus));
				if (!QueryServiceStatus(hW3Svc,  &svcStatus))
				{
					PrintErrorWithLastWin32(IDS_FAILEDQUERYSTATUS);
					CloseServiceHandle(hW3Svc);
					CloseServiceHandle(hScm);
					return ATLSDPLY_FAIL;
				}
				
				//success?
				if (svcStatus.dwCurrentState == SERVICE_RUNNING)
				{
					// service is running! We're done
					printf("\n");
					break;
				}
				else if (svcStatus.dwCurrentState == SERVICE_STOPPED)
				{
					if (nRetryCount > 50)
						break;
					Sleep(100);
			    	if (!StartService(hW3Svc, NULL, NULL))
    				{
    					PrintErrorWithLastWin32(IDS_FAILEDSTARTSVC);
    					CloseServiceHandle(hW3Svc);
    					CloseServiceHandle(hScm);
    					return ATLSDPLY_FAIL;
    				}
					nRetryCount++;
				}
			}
		}
	}
	else
	{
		// it never stopped for some reason
		PrintError(IDS_ERR_NOSTOPW3SVC);
		dwRet = ATLSDPLY_FAIL;
	}
	
	// final check to see if it's running
	if (svcStatus.dwCurrentState != SERVICE_RUNNING)
	{
		// service should be running by now
		ATLASSERT(FALSE);
		PrintError(IDS_ERR_W3SVCFAILEDTOSTART);
		dwRet = ATLSDPLY_FAIL;
	}
	
	CloseServiceHandle(hW3Svc);
	CloseServiceHandle(hScm);
	return dwRet;
}

int UpdateFileSystem(CDepSettings *pSettings)
{
	CComPtr<IXMLDOMNodeList> spFileGroupsList;
	HRESULT hr = E_FAIL;
	long nAppFileGroups = 0, i=0;

	// Create the vroot root directory
	CPath FSPath(pSettings->GetVirtDirFSPath());

	if (FSPath.m_strPath.GetLength() != 0 && !FSPath.IsDirectory())
	{	
		if (!CreateDirectory(FSPath, NULL))
		{
			PrintErrorWithLastWin32(IDS_ERR_CREATING_DIRECTORY);
			return ATLSDPLY_FAIL;
		}
	}
	else if (FSPath.m_strPath.GetLength() == 0)
	{
		PrintError(IDS_ERR_CREATING_DIRECTORY);
		return ATLSDPLY_FAIL;
	}
	
	hr = pSettings->GetFileGroups(&spFileGroupsList);
	RETURN_ON_UNEXPECTED_HRP(hr, spFileGroupsList);

	//loop through each app file group
	hr = spFileGroupsList->get_length(&nAppFileGroups);
	RETURN_ON_UNEXPECTED(hr);

	if (nAppFileGroups <= 0)
		return ATLSDPLY_SUCCESS; // nothing to do

	for (i=0; i<nAppFileGroups; i++)
	{
		CComPtr<IXMLDOMNode> spAppFileGroupNode;	
		CComPtr<IXMLDOMNodeList> spFileNodeList;
		long nFileNameNodes = 0;

		hr = spFileGroupsList->get_item(i, &spAppFileGroupNode);
		RETURN_ON_UNEXPECTED_HRP(hr, spAppFileGroupNode);

		hr = spAppFileGroupNode->selectNodes(CComBSTR(L"APPFILENAME"), &spFileNodeList);
		RETURN_ON_UNEXPECTED_HRP(hr, spFileNodeList);

		hr = spFileNodeList->get_length(&nFileNameNodes);
		RETURN_ON_UNEXPECTED(hr);

		if (nFileNameNodes <=0)
			continue; // no files in node.

		for (long x=0; x<nFileNameNodes; x++)
		{
			// for each APPFILENAME in the APPFILEGROUP get the 
			// SRC and DEST tag and copy the file
			CComPtr<IXMLDOMNode> spFileNode, spSrc, spDest;
			CComBSTR bstrSrcPath, bstrDestPath;
			hr = spFileNodeList->get_item(x, &spFileNode);
			RETURN_ON_UNEXPECTED_HRP(hr, spFileNode);

			hr = spFileNode->selectSingleNode(CComBSTR(L"SRC"), &spSrc);
			RETURN_ON_UNEXPECTED_HRP(hr, spSrc);

			hr = spFileNode->selectSingleNode(CComBSTR(L"DEST"), &spDest);
			RETURN_ON_UNEXPECTED_HRP(hr, spDest);

			hr = spSrc->get_text(&bstrSrcPath);
			RETURN_ON_UNEXPECTED(hr);

			hr = spDest->get_text(&bstrDestPath);
			RETURN_ON_UNEXPECTED(hr);

			// src is a full path
			// dest is a path relative to the root of the vroot path
			// Calculate the full path of the file, then see if the
			// directory exists. If it doesn't, create it.
			CPath vrDestDir;
			vrDestDir.Combine(pSettings->GetVirtDirFSPath(), CW2T(bstrDestPath));
			CPath vrDestPath(vrDestDir);
			vrDestDir.RemoveFileSpec();
			if (!vrDestDir.IsDirectory())
			{
				if (!RecursiveCreateDirectory(vrDestDir))
				{
					PrintErrorWithLastWin32(IDS_ERR_CREATING_DIRECTORY_RELATIVE);
					return ATLSDPLY_FAIL;
				}
			}

			CW2T szDest(bstrSrcPath);
			// Copy the file
			int result = CheckFileDiff(szDest, vrDestPath);
			if (result == S_FALSE)
			{
				if (!::CopyFile(szDest, vrDestPath, FALSE))
				{
					PrintWarningWithLastWin32(IDS_WARN_COPYING_FILE);
				}
				else
				{
				    CString strMsg;
				    FormatMsg(strMsg, IDS_COPYFILE_MESSAGE, (LPCTSTR)szDest, (LPCTSTR)vrDestPath);
				    _fputts(static_cast<const TCHAR *>(strMsg),stdout);
					ATLTRACE("Copied %s to %s\n", (LPCTSTR)CW2T(bstrSrcPath), (LPCTSTR)vrDestPath);
				}
				
				// Remove the readonly attribute on the destination file
				// if it is set.
				DWORD dwAttrs = GetFileAttributes(vrDestPath);
				if (dwAttrs != 0xFFFFFFFF)
				{
					if (dwAttrs & FILE_ATTRIBUTE_READONLY)
					{
						dwAttrs &= ~FILE_ATTRIBUTE_READONLY;
						if (!SetFileAttributes(vrDestPath, dwAttrs))
						{
							ATLTRACE(_T("Failed to remove readonly attribute of target file %s\n"), vrDestPath);
						}
					}
				}
			}
			else if (result == S_OK)
			{
			    CString strMsg;
			    FormatMsg(strMsg, IDS_FILES_IDENTICAL, (LPCTSTR) szDest);
			    _fputts(static_cast<const TCHAR *>(strMsg), stdout);
			}
		}
	}
	return ATLSDPLY_SUCCESS;
}

int RegisterExtension(CADSIHelper* /*pAdsHelper*/,
					  CDepSettings *pSettings)
{

	if (!pSettings->GetRegIsapi())
		return ATLSDPLY_SUCCESS; // isapi doesn't need to be registered

	LPCTSTR szVirtDirPath = pSettings->GetVirtDirFSPath();
	LPCTSTR szExtFileName = pSettings->GetExtensionFileName();

	if (!szVirtDirPath || *szVirtDirPath == _T('\0'))
	{
        PrintWarning(IDS_ERR_REGISTERING_NOVDIRPATH);
		return ATLSDPLY_SUCCESS;
	}

	if (!szExtFileName || *szExtFileName == _T('\0'))
	{
		PrintWarning(IDS_ERR_REGISTERING_NOEXTFILE);
		return ATLSDPLY_SUCCESS;
	}
	CPathW strIsapiPath;
	int nRet = IDS_ERR_REGISTERING_EXTENSION;

	try
	{

	CStringW strVirtDirPath(szVirtDirPath);
	CStringW strExtFileName(szExtFileName);
	strIsapiPath.Combine(strVirtDirPath, strExtFileName);
	}
	catch(...)
	{
		// probably an allocation problem with the CStrings
		return nRet;
	}

	HINSTANCE hInstExtension = ::LoadLibraryW(strIsapiPath);
	if (!hInstExtension)
		return IDS_ERR_REGISTERING_EXTENSION;

	PFNRegisterServer pfnRegister = (PFNRegisterServer)GetProcAddress(hInstExtension, _T("DllRegisterServer"));
	if (pfnRegister)
	{
		if (S_OK == pfnRegister())
			nRet = ATLSDPLY_SUCCESS;
	}
	else
		return IDS_ERR_REGISTERING_EXTENSION;
	FreeLibrary(hInstExtension);
	return nRet;
}

BOOL LoadStringFromModule(int nID, CString& str)
{		
	if (!str.LoadString(nID))
		return FALSE;
	return TRUE;
}

int CheckMinVRootConfigSettings(CDepSettings *pSettings)
{
	if (!pSettings)
		return IDS_UNEXPECTED;
	
	// For now, if we don't have an ISAPI extension file name
	// or we don't have any script mappings, we can't create an
	// atls vroot
	LPCTSTR szExtensionFile = pSettings->GetExtensionFileName();
	if (!szExtensionFile || *szExtensionFile == _T('\0'))
		return ATLSDPLY_FAIL;

	CComPtr<IXMLDOMNodeList> spList;
	HRESULT hr = pSettings->GetAppMappingList(&spList);
	if (hr != S_OK && spList)
		return ATLSDPLY_FAIL;

	return ATLSDPLY_SUCCESS;
}

int RuntimeCheck()
{
	CComPtr<IMSAdminBase> spAdmBase;
	HRESULT hr = spAdmBase.CoCreateInstance(CLSID_MSAdminBase);
	if (hr != S_OK)
		return ATLSDPLY_FAIL;
	return ATLSDPLY_SUCCESS;
}

// This function checks the length of the files and then does
// a binary check to see if the files are the same. Since we could
// be copying accross different file systems and the timestamp resolution
// even on our own filesystems isn't that great I decided to do a binary
// compare to see if the files are different. It's expensive in terms of
// memory usage but this process should still run pretty fast because there
// shouldn't be that many files to check and the files shouldn't be that big.
// We don't support checking of files of size greater than ULONG_MAX (4 gig).
HRESULT CheckFileDiff(LPCTSTR szSrcPath, LPCTSTR szDestPath)
{
	CAtlFile fSrc, fDest;
	HRESULT hr = E_FAIL;

	if (S_OK == fSrc.Create(szSrcPath, GENERIC_READ, 0, OPEN_EXISTING))
	{
		if (S_OK == fDest.Create(szDestPath, GENERIC_READ, 0, OPEN_EXISTING))
		{
			ULONGLONG lenSrc=0,lenDest=0;
			if (S_OK == fSrc.GetSize(lenSrc) && 
				S_OK == fDest.GetSize(lenDest))
			{
				if (lenSrc != lenDest)
					hr = S_FALSE;
				else
				{
					if (lenSrc > ULONG_MAX ||
						lenDest > ULONG_MAX)
					{
						hr = E_FAIL;
					}
					else
					{
						if (lenSrc > ULONG_MAX || 
							lenDest > ULONG_MAX)
							hr = E_FAIL;
						else
						{
							CHeapPtr<BYTE> pBuffSrc;
							CHeapPtr<BYTE> pBuffDest;
							hr = S_OK;
							if (!pBuffSrc.Allocate((DWORD)lenSrc))
								hr = S_FALSE;
							if (hr == S_OK && !pBuffDest.Allocate((DWORD)lenDest))
								hr = S_FALSE;
							if (hr == S_OK && S_OK != fSrc.Read((LPVOID)(BYTE*)pBuffSrc, (DWORD)lenSrc))
								hr = S_FALSE;
							if (hr == S_OK && S_OK != fDest.Read((LPVOID)(BYTE*)pBuffDest, (DWORD)lenDest))
								hr = S_FALSE;
							if (hr == S_OK)
								hr = memcmp((LPVOID)(BYTE*)pBuffSrc, (LPVOID)(BYTE*)pBuffDest, (DWORD)lenDest) != 0 ? S_FALSE : S_OK;
						}
					}

				}
			}
		}
		else
			hr = S_FALSE; // dest doesn't exist
	}
	else
	{
		CString strFormat;
		DWORD dwLast = GetLastError();
		if (dwLast == ERROR_FILE_NOT_FOUND &&
		    strFormat.LoadString(IDS_WARN_SOURCE_NOT_EXIST))
		{
			CString strMsg;
			strMsg.Format(strFormat, szSrcPath);
			PrintWarningFormatted(strMsg);
		}
		else
		{
			if (strFormat.LoadString(IDS_WARN_SOURCE_ACCESS_ERROR))
			{
				CString strMsg;
				strMsg.Format(strFormat, dwLast);
				PrintWarningFormatted(strMsg);
			}
			
		}
	}
	return hr;
}

HRESULT ProcessAccessCheck()
{
	try
	{
		HANDLE hToken = NULL;
		// Current token will impersonate self
		if (!ImpersonateSelf(SecurityImpersonation))
			return 0;
			
		if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken))
		{
			// per Q118626, we check the process token
			// if there is no thread token
			if (GetLastError() != ERROR_NO_TOKEN)
				return E_FAIL;
			else
			{
				if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
					return E_FAIL;
			}
		}
		
		ATLASSERT(hToken);
		CAccessToken myToken;
		bool bIsMember = false;
		myToken.Attach(hToken);
		if (myToken.CheckTokenMembership(Sids::Admins(), &bIsMember))
			return bIsMember ? S_OK : S_FALSE;
		
	}
	catch(...){	}
	return E_UNEXPECTED; // should have returned something before here.
}

HRESULT GetWWWRootPath(const CStringW strWebHostName,CStringW& strPath)
{           
      CComPtr<IEnumVARIANT> spEnum; 
      CComPtr<IADsContainer> spCont;
      CStringW strIISPath=L"IIS://";
      strIISPath+=strWebHostName;
      strIISPath+=L"/W3SVC";
      HRESULT hr=ADsGetObject(strIISPath.GetString(), IID_IADsContainer, (void**) &spCont);
      CComPtr<IADs> spDeploySite;
      if (SUCCEEDED(hr) && spCont!=NULL)
      {
		CComPtr<IUnknown> spUnk;
        hr=spCont->get__NewEnum(&spUnk);
        if (SUCCEEDED(hr))
        {
			spCont.Release();             
            hr=spUnk.QueryInterface(&spEnum);
			if (SUCCEEDED(hr))
			{
				spUnk.Release();
				CComVariant var;                    
				CComPtr<IDispatch> spDisp;
				ULONG lFetch = 0;                   
				// Enumerate children of IIS service, searching for absolute path to "Default Web Site"
				// or if not found, the first web site.
				hr = spEnum->Next(1, &var, &lFetch);
				while(SUCCEEDED(hr) && lFetch > 0)
				{                             
					spDisp = V_DISPATCH(&var);
					CComPtr<IADs> spADs;                      
					hr=spDisp.QueryInterface(&spADs);
					if (SUCCEEDED(hr) && spADs!=NULL)
					{                       
						spDisp.Release();             
						CComBSTR bstrSchemaClass;
						hr=spADs->get_Class(&bstrSchemaClass);
						if (SUCCEEDED(hr) && bstrSchemaClass == "IIsWebServer")
						{
							if (!spDeploySite)
							{
								spDeploySite=spADs;
							}
							CComVariant varSiteName;
							hr=spADs->Get(L"ServerComment",&varSiteName);
							if (SUCCEEDED(hr))
							{
								CComBSTR bstrSiteName (V_BSTR(&varSiteName));
								if (bstrSiteName == "Default Web Site")
								{
									spDeploySite = spADs;
									break;
								}
							}
						}
					}
					var.Clear();
					hr = spEnum->Next(1, &var, &lFetch);
				} //End While
			}
		}
      }
	  hr=E_FAIL;
      if (spDeploySite!=NULL)
      {
		CComBSTR bstrWebsitePath;
		spDeploySite->get_ADsPath(&bstrWebsitePath);
		bstrWebsitePath+=L"/ROOT";
		CComPtr<IADs> spSite;
		hr=ADsGetObject(bstrWebsitePath, IID_IADs, (void**) &spSite);
		if (SUCCEEDED(hr))
		{
			CComVariant varPath;
			spSite->Get(L"Path",&varPath);
			strPath = V_BSTR(&varPath);
			strPath += L"\\";
		}
      }
      return hr;      
}

