//	++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//	INSTDATA.CPP
//
//		HTTP instance data cache implementation.
//
//
//	Copyright 1997 Microsoft Corporation, All Rights Reserved
//

#include "_davprs.h"

#include <buffer.h>
#include "instdata.h"

//	========================================================================
//
//	class CInstData
//

//	------------------------------------------------------------------------
//
//	CInstData::CInstData()
//
//		Constructor.  Init all variables.  Make a copy of the name string
//		for us to keep.
//
//		NOTE: This object must be constructed while we are REVERTED.
//
CInstData::CInstData( LPCWSTR pwszName )
{
	//	Make copy of wide instance name
	//
	m_wszVRoot = WszDupWsz(pwszName);

	//	Parse out and store service instance, otherwise
	//	sometimes referred as the server ID.
	//
	m_lServerID = LInstFromVroot( m_wszVRoot );
	
	//	Create our objects.  Please read the notes about the
	//	relative costs and reasons behind creating these objects
	//	now rather than on demand.  Don't create anything here that
	//	can be created on demand unless at least one of the
	//	following is true:
	//
	//		1. The object is lightweight and cheap to create.
	//		   Example: an empty cache.
	//
	//		2. The object is used in the processing of a vast
	//		   majority of HTTP requests.
	//		   Example: any object used on every GET request.
	//
 
}

//	========================================================================
//
//	class CInstDataCache
//

//	------------------------------------------------------------------------
//
//	CInstDataCache::GetInstData()
//
//		Fetch a row from the cache.
//
CInstData& CInstDataCache::GetInstData( const IEcb& ecb )
{
	auto_ref_ptr<CInstData> pinst;
	CStackBuffer<WCHAR> pwszMetaPath;
	UINT cchMetaPath;
	CStackBuffer<WCHAR> pwszVRoot;
	UINT cchVRoot;
	LPCWSTR pwszRootName;
	UINT cchRootName;

	//	Build up a unique string from the vroot and instance:
	//	lm/w3svc/<site id>/root/<vroot name>
	//

	//	Get the virtual root from the ecb (/<vroot name>).
	//
	cchRootName = ecb.CchGetVirtualRootW( &pwszRootName );

	//	pwszRootName should still be NULL-terminated.  Check it, 'cause
	//	we're going to put the next string after there, and we don't want
	//	to get them mixed...
	//
	Assert( pwszRootName );
	Assert( L'\0' == pwszRootName[cchRootName] );

	//	Ask IIS for the metabase prefix (lm/w3svc/<site id>) for the
	//	virtual server (site) we're on...
	//
	cchMetaPath = pwszMetaPath.celems();
	if (!ecb.FGetServerVariable( "INSTANCE_META_PATH",
								 pwszMetaPath.get(),
								 reinterpret_cast<DWORD *>(&cchMetaPath) ))
	{
		if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
		{
			DebugTrace( "CInstDataCache::GetInstData() - FGetServerVariable() failed"
						" to get INSTANCE_META_PATH\n" );
			throw CLastErrorException();
		}

		if (NULL == pwszMetaPath.resize(cchMetaPath * sizeof(WCHAR)))
		{
			SetLastError(E_OUTOFMEMORY);

			DebugTrace( "CInstDataCache::GetInstData() -  failed to allocate memory\n" );
			throw CLastErrorException();
		}

		if (!ecb.FGetServerVariable( "INSTANCE_META_PATH",
									 pwszMetaPath.get(),
									 reinterpret_cast<DWORD *>(&cchMetaPath) ))
		{
			DebugTrace( "CInstDataCache::GetInstData() - FGetServerVariable() failed"
						" to get INSTANCE_META_PATH\n" );
			throw CLastErrorException();
		}
	}

	//	The function returning server variable returns total number of characters
	//	written to the output buffer, so it will include '\0' termination. Let us make
	//	sure that the return is what is expected.
	//
	Assert(0 == cchMetaPath ||
		   L'\0' == pwszMetaPath[cchMetaPath - 1]);

	//	Adjust the cchMetaPath to reflect the actual character count
	//
	if (0 != cchMetaPath)
	{
		cchMetaPath--;
	}

	//	Check that the root name is either NULL (zero-length string)
	//	or has its own separator.
	//
	//	NOTE: This is conditional because IF we are installed at the ROOT
	//	(on w3svc/1 or w3svc/1/root) and you throw a method against
	//	a file that doesn't live under a registered K2 vroot
	//	(like /default.asp) -- we DO get called, but the mi.cchMatchingURL
	//	comes back as 0, so pwszRootName is a zero-len string.
	//	(In IIS terms, you are not really hitting a virtual root,
	//	so your vroot is "".)
	//	I'm making this assert CONDITIONAL until we figure out more about
	//	how we'll handle this particular install case.
	//$REVIEW: The installed-at-the-root case needs to be examined more
	//$REVIEW: becuase we DON'T always build the same instance string
	//$REVIEW: in that case -- we'll still get vroot strings when the URI
	//$REVIEW: hits a resource under a REGISTERED vroot, and so we'll
	//$REVIEW: build different strings for the different vroots, even though
	//$REVIEW: we are running from a single, global install of DAV.
	//$REVIEW: The name might need to be treated as a starting point for a lookup.
	//	NTBug #168188: On OPTIONS, "*" is a valid URI.  Need to handle this
	//	special case without asserting.
	//
	AssertSz( (L'*' == pwszRootName[0] && 1 == cchRootName) ||
			  (0 == cchRootName) ||
			  (L'/' == pwszRootName[0]),
			  "(Non-zero) VRoot name doesn't have expected slash delimiter.  Instance name string may be malformed!" );

	//	NTBug # 168188: Special case for OPTIONS * -- map us to the root
	//	instdata name of "/w3svc/#/root" (don't want an instdata named
	//	"/w3svc/#/root*" that noone else can ever use!).
	//
	cchVRoot = pwszVRoot.celems();
	if (cchVRoot < cchMetaPath + gc_cch_Root + cchRootName + 1)
	{
		cchVRoot = cchMetaPath + gc_cch_Root + cchRootName;
		if (NULL == pwszVRoot.resize(CbSizeWsz(cchVRoot)))
		{
			SetLastError(E_OUTOFMEMORY);

			DebugTrace( "CInstDataCache::GetInstData() -  failed to allocate memory\n" );
			throw CLastErrorException();
		}
	}

	//	Copy first 2 portions: 'lm/w3svc/<site id>' and '/root'
	//
	memcpy(pwszVRoot.get(), pwszMetaPath.get(), cchMetaPath * sizeof(WCHAR));
	memcpy(pwszVRoot.get() + cchMetaPath, gc_wsz_Root, gc_cch_Root * sizeof(WCHAR));

	//	Copy remaining 3-rd portion: '/<vroot name>' and terminate the string.
	//	NTBug # 168188: Special case for OPTIONS * -- map us to the root
	//	instdata name of "/w3svc/#/root" (don't want an instdata named
	//	"/w3svc/#/root*" that noone else can ever use!).
	//
	if (L'*' == pwszRootName[0] && 1 == cchRootName)
	{
		(pwszVRoot.get())[cchMetaPath + gc_cch_Root] = L'\0';
	}
	else
	{
		memcpy(pwszVRoot.get() + cchMetaPath + gc_cch_Root, pwszRootName, cchRootName * sizeof(WCHAR));
		(pwszVRoot.get())[cchMetaPath + gc_cch_Root + cchRootName] = L'\0';
	}

	//	Slam the string to lower-case so that all variations on the vroot
	//	name will match.  (IIS doesn't allow vroots with the same name --
	//	and "VRoot" and "vroot" count as the same!)
	//
	_wcslwr( pwszVRoot.get() );

	//	Demand load the instance data for this vroot
	//
	{
		CRCWsz crcwszVRoot( pwszVRoot.get() );

		while ( !Instance().m_cache.FFetch( crcwszVRoot, &pinst ) )
		{
			CInitGate ig( L"DAV/CInstDataCache::GetInstData/", pwszVRoot.get() );

			if ( ig.FInit() )
			{
				//	Setup instance data from the system security context,
				//	not the client's security context.
				//
				safe_revert sr(ecb.HitUser());

				pinst = new CInstData(pwszVRoot.get());

				//	Since we are going to use this crcsz as a key in a cache,
				//	need to make sure that it's built on a name string that
				//	WON'T EVER MOVE (go away, get realloc'd).  The stack-based one
				//	above just isn't good enough.  SO, build a new little CRC-dude
				//	on the UNMOVABLE name data in the inst object.
				//	(And check that this new crc matches the old one!)
				//
				CRCWsz crcwszAdd( pinst->GetNameW() );
				AssertSz( crcwszVRoot.isequal(crcwszAdd),
						  "Two CRC's from the same string don't match!" );

				Instance().m_cache.Add( crcwszAdd, pinst );

				//	Log the fact that we've got a new instance pluged in.
				//	Message DAVPRS_VROOT_ATTACH takes two parameters:
				//	the signature of the impl and the vroot.
				//
				//$	RAID:NT:283650: Logging each attach causes a large
				//	number of events to be registered.  We really should
				//	only Log one-time-startup/failure events.
				//
				#undef	LOG_STARTUP_EVENT
				#ifdef	LOG_STARTUP_EVENT
				{
					LPCWSTR	pwszStrings[2];

					pwszStrings[0] = gc_wszSignature;
					pwszStrings[1] = pwszVRoot.get();
					LogEventW(DAVPRS_VROOT_ATTACH,
							  EVENTLOG_INFORMATION_TYPE,
							  2,
							  pwszStrings,
							  0,
							  NULL );
				}
				#endif	// LOG_STARTUP_EVENT
				//
				//$	RAID:X5:283650: end.

				break;
			}
		}
	}

	return *pinst;
}
