/*++

Microsoft Windows NT RPC Name Service
Copyright (c) 1995 Microsoft Corporation

Module Name:

    objects.hxx

Abstract:

	This file contains the definitions of all basic classes used in the server, 
	excluding the data structure classes and classes used for network interactions.

Author:

    Satish Thatte (SatishT) 08/21/95  Created all the code below except where
									  otherwise indicated.

--*/

	/*	BUGBUG:  For every operation with a dynamic object, memory
				 management should be a part of the description and  
				 specification for every parameter.  Fix that along 
				 with other documentation.
	*/

#ifndef _OBJECTS_
#define _OBJECTS_

#include <memory.h>
#include <globals.hxx>
#include <locquery.h>

class CGUID;
class CInterface;
struct CBVWrapper;
class CServerEntry;
class CCacheServerEntry;
class CFullServerEntry;
class CInterfaceIndex;
struct CMailSlotReplyItem;
class CServerLookupHandle;
class CServerObjectInqHandle;

typedef TIIterator<CGUID> TGuidIterator;


// The following enumeration is the the type of entry the object is.

enum EntryType {
	
    ServerEntryType,
    GroupEntryType,
    ProfileEntryType,
	CacheServerEntryType,
	FullServerEntryType
};


/*++

Class Definition:

    CStringW

Abstract:

    This is the unicode string wrapper class.

	Note that string comparison is case insensitive.

--*/

class CStringW : public IOrderedItem {

protected:
	
    STRING_T pszVal; 

public:

	int length() { return wcslen(pszVal); }

	int isEmptyString() {
		return (pszVal == NULL)	|| (_wcsicmp(pszVal,TEXT("")) == 0);
	}

	static STRING_T copyString(
					CONST_STRING_T str
					) 
	{ 
		STRING_T result = new WCHAR [(wcslen(str)+1)*sizeof(WCHAR)];
		wcscpy(result,str);
		return result;
	}

	static STRING_T copyMIDLstring(	// for use in out RPC parameters
									// which are deallocated by stubs
					CONST_STRING_T str
					) 
	{ 
		STRING_T result = (STRING_T) midl_user_allocate((wcslen(str)+1)*sizeof(WCHAR));
		wcscpy(result,str);
		return result;
	}

	STRING_T copyAsString() {
		return copyString(pszVal);
	}

	STRING_T copyAsMIDLstring() {	// for use in out RPC parameters
									// which are deallocated by stubs
		return copyMIDLstring(pszVal);
	}

	CStringW() {
		pszVal = NULL;
	}
	
    CStringW( CONST_STRING_T p ) {
		pszVal = copyString(p);
	}


    CStringW& operator=( const CStringW& str ) {
		delete [] pszVal;
		pszVal =  copyString(str.pszVal);
		return *this;
	}

    CStringW( const CStringW& str ) {
		pszVal =  copyString(str.pszVal);
	}


    virtual ~CStringW()		// base class destructor should be virtual
    {
		delete [] pszVal;
    }

	operator STRING_T() { return pszVal; }

    virtual int compare( const IOrderedItem& O ) {
		const CStringW& S = (CStringW&) O;
		return _wcsicmp(pszVal,S.pszVal); 
	}
};


typedef TCGuardedSkipList<CStringW> TGSLString;
typedef TCGuardedSkipListIterator<CStringW> TGSLStringIter;
typedef TCSafeSkipList<CStringW> TSSLString;
typedef TCSafeSkipListIterator<CStringW> TSSLStringIter;
typedef TCSafeLinkList<CStringW> TSLLString;
typedef TCSafeLinkListIterator<CStringW> TSLLStringIter;


/*++

Class Definition:

    CGUID

Abstract:

    This is the GUID wrapper class.  

--*/

class CGUID : public IOrderedItem {

	GUID rep;

public:

	CGUID() {
		memset(&rep,0,sizeof(rep));
	}
	
	CGUID( const GUID& g ) : rep(g) {}
	
    virtual int compare(const IOrderedItem& C ) {
		CGUID& S = (CGUID&) C;
		return memcmp(&rep, &(S.rep),sizeof(rep)); 
	}

	GUID myGUID() { return rep; }

	BOOL IsNil() {

		RPC_STATUS dummyStatus;

		return UuidIsNil(&rep,&dummyStatus);
	}

	CStringW* ConvertToString();
};

typedef TCSafeSkipListIterator<CGUID> TSSLGuidIterator;


/*++

Class Definition:

    CGUIDVersion

Abstract:

    This is the RPC_SYNTAX_IDENTIFIER wrapper class.  

--*/


class CGUIDVersion : public IOrderedItem {

  protected:
	
	struct CidAndVersion {	// this is just a readability aid for
							// the implementation of isCompatibleGV
		CGUID id;
		unsigned short major;
		unsigned short minor;

		CidAndVersion (const RPC_SYNTAX_IDENTIFIER& in) : id(in.SyntaxGUID) {
			major = in.SyntaxVersion.MajorVersion;
			minor = in.SyntaxVersion.MinorVersion;
		}

		CidAndVersion() {
			memset(&id,0,sizeof(GUID));
			major = minor = 0;
		}
	};

    RPC_SYNTAX_IDENTIFIER idAndVersion;

  public:

    CGUIDVersion( const RPC_SYNTAX_IDENTIFIER& g ) { 
		idAndVersion = g;
	}

	CGUIDVersion() {}	// NULL case

	RPC_SYNTAX_IDENTIFIER myIdAndVersion() { return idAndVersion; }

	operator RPC_SYNTAX_IDENTIFIER() { return idAndVersion; }
	
	GUID myGUID() { return idAndVersion.SyntaxGUID; }
		
    virtual int compare(const IOrderedItem& C ) {
		const CGUIDVersion& S = (CGUIDVersion&) C;
		return memcmp(&idAndVersion, &S.idAndVersion, sizeof(RPC_SYNTAX_IDENTIFIER)); 
	}

	int isMatching(const CGUIDVersion& other, UNSIGNED32 vers_option);

	int isCompatibleGV(const CGUIDVersion& other) {
		return isMatching(other,RPC_C_VERS_COMPATIBLE);
	}
};





/*++

Class Definition:

    CMailSlotReplyItem

Abstract:

    This is the class of items to be marshalled into mailslot reply buffers.
	The class, and especially the marshalling code, is dictated largely by
	compatibility requirements for the old locator.

    Everything in a CMailSlotReplyItem object is borrowed from some
	other object and is therefore not freed upon destruction.

	The primary operation is "marshall".
	
--*/

struct CMailSlotReplyItem : public IDataItem {

	/* these are the items needed to marshall a reply packet */

	RPC_SYNTAX_IDENTIFIER Interface;
	RPC_SYNTAX_IDENTIFIER XferSyntax;
	STRING_T binding;
	STRING_T entryName;
	TCSafeSkipList<CGUID> *pObjectList;
	  
	/* the marshall operation returns the number of bytes written to the buffer */

	DWORD Marshall(char * pcBuffer, long lBufferSize);

};


typedef TIIterator<CMailSlotReplyItem> TMSRIIterator;
typedef TCSafeLinkList<CMailSlotReplyItem> TMSRILinkList;
typedef TCSafeLinkListIterator<CMailSlotReplyItem> TMSRILinkListIterator;




/*++

Class Definition:

    CBVWrapper

Abstract:

    This is a very thin wrapper for NSI_BINDING_VECTOR_T to make the
	latter usable with linked lists (by inheriting from IDataItem).

--*/

struct CBVWrapper : public IDataItem {
	NSI_BINDING_VECTOR_T *pBVT;

	CBVWrapper(NSI_BINDING_VECTOR_T *p) {
		pBVT = p;
	}

	void rundown();

	// BUGBUG:  should there be a destructor as a matter of principle?
};



typedef TIIterator<CBVWrapper> TBVIterator;
typedef TCSafeLinkList<CBVWrapper> TBVSafeLinkList;
typedef TCSafeLinkListIterator<CBVWrapper> TBVSafeLinkListIterator;


/*++

Class Definition:

    CNSBinding

Abstract:

    A thin wrapper for NSI_BINDING_T.  Note that the strings in a NSI_BINDING_T used
	to initialize a CNSBinding object are not copied, only the pointers are copied.

--*/

class CNSBinding : public CStringW 
{
	NSI_BINDING_T rep;

  public:

	CNSBinding(					// this is a little crude, but hey..
		NSI_BINDING_T& binding
		)
		:
		rep(binding)
	{
		pszVal = catenate(
					binding.string,
					binding.entry_name
					);
	}

	operator NSI_BINDING_T()
	{
		return rep;
	}

	void 
	copyBinding(NSI_BINDING_T& result)
	{
		result.string = CStringW::copyMIDLstring(rep.string);
		result.entry_name = CStringW::copyMIDLstring(rep.entry_name);
		result.entry_name_syntax = rep.entry_name_syntax;
	}
};


/*++

Class Definition:

    CBindingVector

Abstract:

    Used mainly to keep vectors of binding handles.  Iterators returning
	vectors of handles is what most NS handles are in essence.

--*/

class CBindingVector : private TCSafeSkipList<CStringW>

{
	CServerEntry *pMyEntry;	// the entry this belongs to
	
  public:

	CBindingVector(NSI_SERVER_BINDING_VECTOR_T*, CServerEntry*);

	~CBindingVector() {		// must destroy binding strings stored here
		wipeOut();
	}

	int merge(NSI_SERVER_BINDING_VECTOR_T* pHandles);
	
	TBVSafeLinkList * formObjBVT(
				TSLLString * pLLobjectStrings,
				long ulVS	// max BV size
				);

	SkipStatus insertBindingHandle(STRING_T bindingHandle) {
		CStringW *pTemp	 =  new CStringW(bindingHandle);
		return insert(pTemp);
	}

	TMSRILinkList *msriList(
					CInterface *pIf,
					TCSafeSkipList<CGUID>* psslObjList
					);
};


/*++

Class Definition:

    CInterface

Abstract:

    An interface and its vector of binding handles. 
	The interface can be searched for, using the GUIDVersion 
	comparison operator.

    The major caveat is that we essentially ignore transfer syntax.
	According to DCE, there should be a separate entry for each
	interface/transfer syntax combination.  We assume that NDR is 
	always used.

--*/

class CInterface : public CGUIDVersion {

	friend class CServerEntry;

private:

	CGUIDVersion transferSyntax;
	CBindingVector *pBVhandles;

	int mergeHandles(NSI_SERVER_BINDING_VECTOR_T* pHandles) {
		return pBVhandles->merge(pHandles);
	}
	
public:
	
	CInterface(
		NSI_INTERFACE_ID_T * lpInf,
		NSI_SERVER_BINDING_VECTOR_T *BindingVector,
		CServerEntry *pMyEntry
		);

	RPC_SYNTAX_IDENTIFIER xferSyntaxIdAndVersion() {
		return transferSyntax.myIdAndVersion();
	}

	/*  self is the same or a more recent MINOR version for both
		interface and transfer syntax IDs */

	int isCompatibleWith(
			CGUIDVersion *pInterfaceID,
			CGUIDVersion *pTransferID
			)
	{
		return (!pInterfaceID || this->isCompatibleGV(*pInterfaceID))
			&& (!pTransferID || transferSyntax.isCompatibleGV(*pTransferID));
	}

	~CInterface() {
		delete pBVhandles;
	}
};



/*++

Class Definition:

    CEntryName

Abstract:

    This class encapsulates knowledge about RPC name service entry names.

Data Members:

	CStringW (base) -- the name string in its original form

	DomainName -- the name of the Domain, without any punctuation

    EntryName -- the name of the entry, without any /.: or /... prefix
				 and also without a domain name at the beginning
	
--*/

class CEntryName : public CStringW {

  protected:

	  CStringW *pswDomainName;
	  CStringW *pswEntryName;

  public:

	CEntryName(CONST_STRING_T fullName);	// disassembling constructor

	CEntryName(									// assembling constructor
		CONST_STRING_T domainName, 
		CONST_STRING_T entryName
		);

	virtual ~CEntryName();	//  base class destructor should be virtual

	CStringW* getDomainName() 
	{
		return pswDomainName;
	}

	CStringW* getEntryName() 
	{
		return pswEntryName;
	}

	void changeToLocalName() 
	{
		delete pswDomainName;
		pswDomainName = NULL;
		delete [] pszVal;
		pszVal = catenate(RelativePrefix,*pswEntryName);
	}

	STRING_T copyGlobalName();

	STRING_T copyCurrentName()
	{
		return copyString(getCurrentName());
	}

	STRING_T getCurrentName() 

	/*  this is mainly for use by CEntry objects.  for CEntryName objects, automatic
	    conversion through the inherited STRING_T operator is used instead */
	{
		return pszVal;
	}

	int isLocal();
};

/*++

Class Definition:

    CContextHandle

Abstract:

    This is the base interface for all context handles.  It is needed   
	because, for RPC, there is a single NSI_NS_HANDLE_T context handle type, 
	and there has to be a single entry point for the rundown process.  
	A virtual destructor is needed for the same reason.

    Note that CContextHandle is not an abstract class.
	
--*/


class CContextHandle {

public:

	ULONG ulCacheMax;						// max age of a cached item that is acceptable
											// *at lookup time* (this is important)

	ULONG ulCreationTime;					// when I was created

	virtual void lookupIfNecessary() {}		// redo the lookup which created this handle

	virtual void setExpiryAge(ULONG newMax) {
		ULONG ulOldMax = ulCacheMax;
		ulCacheMax = newMax;
		if (ulCacheMax < ulOldMax) lookupIfNecessary();
	}

	virtual void rundown() {}

	CContextHandle() {
		ulCreationTime = CurrentTime();
	}

	virtual ~CContextHandle() {}	
};



/*++

Template Class Definition:

    CNSHandle

Abstract:

    This template defines the basic nature of an NS handle.
	It is instantiated into abstract base classes such as 
	CObjectInqHandle and CLookupHandle, and is also used in 
	the definition of other templates such as CCompleteHandle
	which abstract general properties of classes of handles.
	
--*/


template <class ItemType>
struct CNSHandle : public CContextHandle
{
  public:

	ULONG StatusCode;

	virtual void lookupIfNecessary() = 0;	// redo the lookup which created this handle

	virtual ItemType * next() = 0;			// primary iterative operation

	virtual int finished() = 0;				// test for end of iterator

	virtual void rundown() = 0;			   // inherited and still pure virtual

	CNSHandle() { StatusCode = NSI_S_OK; }

	virtual ULONG getStatus() { return StatusCode; }

	virtual ~CNSHandle() {}
};



/*++

Class Definition:

    CObjectInqHandle

Abstract:

    This is the class for object lookup handles.

	The primary operation on a handle is "next" which is inherited 
	from the TIIterator template interface (instantiated as TGuidIterator
	which returns CGUID objects).  However, it also needs an additional 
	operation for the locator-to-locator case where an entire vector of
	object UUIDs is returned instead of just one UUID at a time.
	
--*/


typedef CNSHandle<GUID> CObjectInqHandle;


/*++

Class Definition:

    CRemoteObjectInqHandle

Abstract:

    This is the common object inquiry handle class for remote entries.  Handles based 
	on cached entries, master locators and broadcast are derived from it.  Note that 
	the connection between a remote handle and a specific entry is very tenuous.
	We don't even assume that the entry object(s) the handle was derived from will 
	exist as long as the handle does.  In case of a new lookup being forced (because
	handle is too stale -- this happens only with the RpcNsHandleSetExpAge API), we
	expect to start all over again from scratch.


--*/


class CRemoteObjectInqHandle : public CObjectInqHandle {

  protected:

	NSI_UUID_VECTOR_P_T pUuidVector;
	ULONG ulIndex;

	CEntryName * penEntryName;
	ULONG ulEntryNameSyntax;

	/* A flag to delay initialization until the first call on next */

	BOOL fNotInitialized;

  public:

#if DBG
	static ULONG ulHandleCount;
	ULONG ulHandleNo;
#endif

	  CRemoteObjectInqHandle(
					STRING_T szName,
					ULONG ulCacheAge
					) 
	  {
		DBGOUT(MEM2, "CRemoteObjectInqHandle#" << (ulHandleNo = ++ulHandleCount) 
					<< " Created at" << CurrentTime() << "\n\n");

		penEntryName = szName ? new CEntryName(szName) : NULL;
		ulEntryNameSyntax = RPC_C_NS_SYNTAX_DCE;
		ulIndex = 0;
		fNotInitialized = TRUE;
		pUuidVector = NULL;
		ulCacheMax = ulCacheAge;
	  }

	  virtual ~CRemoteObjectInqHandle() 
	  {
		 DBGOUT(MEM2, "CRemoteObjectInqHandle#" << (ulHandleCount--,ulHandleNo)  
					<< " Destroyed at" << CurrentTime() << "\n\n");

		 rundown();

		 delete penEntryName;
		 penEntryName = NULL;
	  }

	  virtual void initialize() = 0;

	  virtual void lookupIfNecessary()
	  /*
			Note that this does not reinitialize the handle 
			-- that is deferred until a "next" or "finished" call
	  */
	  {
		  DBGOUT(MEM1,"lookupIfNecessary called for a remote handle\n\n");
		
		  if (CurrentTime() - ulCreationTime > ulCacheMax) rundown();
	  }

	/* The rundown method should be extended in a subclass if objects of the subclass
	   hold additional resources connected with the current contents of the handle (as
	   opposed to lookup parameters). For additional lookup parameters, a destructor 
	   should be provided instead. */

	  void rundown() 
	  {
		  if (pUuidVector)
		  {
              for (; ulIndex < pUuidVector->count; ulIndex++)
                      midl_user_free(pUuidVector->uuid[ulIndex]);

			  midl_user_free(pUuidVector);
			  pUuidVector = NULL;
		  }
		
		  fNotInitialized = TRUE;
	  }

	  virtual GUID* next() {

		  if (fNotInitialized) initialize();

		  if (finished()) return NULL;

		  GUID* result = pUuidVector->uuid[ulIndex];

		  pUuidVector->uuid[ulIndex] = NULL;

		  ulIndex++;

		  return result;
	  }

	  virtual BOOL finished() {
		  if (fNotInitialized) initialize();
		  return StatusCode || !pUuidVector || (ulIndex >= pUuidVector->count);
	  }
};




/*++

Class Definition:

    CNetObjectInqHandle

Abstract:

    This is the common object inquiry handle class for network-based object inquiry.

    Since net object inquiry is now "lazy" (net is accessed only if needed), we need
	a lazy handle which initializes itself according to the old pattern of
	"use master if you can, broadcast if you must".

--*/

class CNetObjectInqHandle : public CRemoteObjectInqHandle {

	CRemoteObjectInqHandle *pRealHandle;
	
	virtual void initialize();

  public:

	ULONG getStatus() { return pRealHandle->StatusCode; }

	virtual void rundown()
	{
		if (pRealHandle) 
		{
			pRealHandle->rundown();
			delete pRealHandle;
			pRealHandle = NULL;
		}

		fNotInitialized = TRUE;
	}

	CNetObjectInqHandle(
				STRING_T			EntryName,
				unsigned long		ulCacheAge
				) :
				CRemoteObjectInqHandle(
						EntryName,
						ulCacheAge
						)
	{
		pRealHandle = NULL;
		fNotInitialized = TRUE;
	}

	~CNetObjectInqHandle()
	{
		rundown();
	}

	GUID *next() {
		if (fNotInitialized) initialize();
		return pRealHandle->next();
	}

	int finished() {
		if (fNotInitialized) initialize();
		return pRealHandle->finished();
	}
};



/*++

Class Definition:

    CLookupHandle

Abstract:

    This is the base class for BV lookup handles.  Actual handles 
	belong to derived classes corresponding to entry types.

	The primary operation on a handle is "next" which is inherited 
	from the TIIterator template interface (instantiated as TBVIterator).

    There is the possibility that a longlived client will fail to
	release a lookup handle, causing an effective leak of resources,
	but we won't deal with that in the current version.
	
--*/

typedef CNSHandle<NSI_BINDING_VECTOR_T> CLookupHandle;



/*++

Class Definition:

    CMailSlotReplyHandle

Abstract:

    This is the base class for handles which provide items for constructing
	mailslot reply buffers.  Actual handles belong to derived classes 
	corresponding to entry types.

	The primary operation on a handle is "next" which is inherited 
	from the TIIterator template interface (instantiated as TMSRIIterator).
	
--*/

class CMailSlotReplyHandle : public TMSRIIterator {

public:

	virtual ~CMailSlotReplyHandle() {}
};


/*++

Class Definition:

    CEntry

Abstract:

    The base Entry class for the internal cache. Even though this is an abstract
	class due to the unspecified loopup member, the class is not a pure
	interface (it has a constructor), hence the C rather than I prefix in the name.

	Note that CEntry has only constant data members.

--*/

class CEntry : public CEntryName {
	
	const EntryType type;
	
public:
	
	const unsigned long ulEntryNameSyntax;
	
	CEntry(
		CONST_STRING_T pszStr, 
		const EntryType& e,
		const unsigned long ulSyntax = RPC_C_NS_SYNTAX_DCE
		)
		:	CEntryName( pszStr ),
			ulEntryNameSyntax(ulSyntax),
			type(e) 
	{}


	virtual ~CEntry() {}

	EntryType getType() { return type; }

	BOOL isCacheType() {
		switch (type) {
			case CacheServerEntryType: return TRUE;
			default: return FALSE;
		}
	}

	virtual void flush() {}		// useful for cache flushing, for instance

	virtual BOOL isCurrent(ULONG) { return TRUE; }	// QUESTION: again cache oriented
													// need CCacheEntryMixin for these?

	virtual BOOL isEmpty() = 0;

	virtual CLookupHandle * lookup(
			CGUIDVersion	*	pGVInterface,
			CGUIDVersion	*	pGVTransferSyntax,
			CGUID			*	pIDobject,
			unsigned long		ulVectorSize,
			unsigned long		ulCacheAge
			) = 0;

	virtual CObjectInqHandle * objectInquiry(
			unsigned long		ulCacheAge
			) = 0;

	virtual CMailSlotReplyHandle * MailSlotLookup(
					CGUIDVersion	*	pGVInterface,
					CGUID			*	pIDobject
					) = 0;
};


typedef TCGuardedSkipList<CEntry> TGSLEntryList;
typedef TCGuardedSkipListIterator<CEntry> TGSLEntryListIterator;
typedef TCSafeSkipList<CEntry> TSSLEntryList;
typedef TCSafeSkipListIterator<CEntry> TSSLEntryListIterator;
typedef TCSafeLinkList<CEntry> TSLLEntryList;
typedef TCSafeLinkListIterator<CEntry> TSLLEntryListIterator;
typedef TISkipList<CEntry> TEntrySkipList;

typedef TIIterator<CEntry> TEntryIterator;




/*++

Class Definition:

    CRemoteLookupHandle

Abstract:

    This is the common lookup handle class for remote entries.  Handles based on
	cached entries, master locators and broadcast are derived from it.  Note that 
	the connection between a remote handle and a specific entry is very tenuous.
	We don't even assume that the entry object(s) the handle was derived from will 
	exist as long as the handle does.  In case of a renewal lookup (lookupIfNecessary()) 
	being forced, we expect to start all over again from scratch.

	This handle, like all NS handles, relies on the fact that it is accessed
    as a client handle by the client side, and hence the RPC run-time 
    automatically serializes its use.

--*/

class CRemoteLookupHandle : public CLookupHandle {

  protected:
	
	/* caching parameters */

	UNSIGNED32			u32EntryNameSyntax;
	CEntryName		*	penEntryName;
	CGUIDVersion	*	pgvInterface;
	CGUIDVersion	*	pgvTransferSyntax;
	CGUID			*	pidObject;
	ULONG				ulVS;

	/* List of temporary entries created as a cache local to this
	   handle by fetchNext.  Must keep it for proper disposal
	   after its use is finished.  This is only used in net
	   handles -- CMasterLookupHandle and CBroadcastLookupHandle
	*/

	TSSLEntryList *psslNewCache;

	/* a lookup handle based on prefetched info */

	CLookupHandle *plhFetched;

	/* A flag to delay initialization until the first call on next */

	BOOL fNotInitialized;

  public:

#if DBG
	static ULONG ulHandleCount;
	ULONG ulHandleNo;
#endif

	CRemoteLookupHandle(
			UNSIGNED32			EntryNameSyntax,
			STRING_T			EntryName,
			CGUIDVersion	*	pGVInterface,
			CGUIDVersion	*	pGVTransferSyntax,
			CGUID			*	pIDobject,
			unsigned long		ulVectorSize,
			unsigned long		ulCacheAge
		);

	virtual void initialize() = 0;

	/* The rundown method should be extended in a subclass if objects of the subclass
	   hold additional resources connected with the current contents of the handle (as
	   opposed to lookup parameters).  See CMasterLookupHandle::rundown for an example.
	   For additional lookup parameters, a destructor should be provided instead.
	*/

	virtual void rundown() 
	{
		if (plhFetched) {
			plhFetched->rundown();
			delete plhFetched;
			plhFetched = NULL;
		}

		if (psslNewCache) {
			psslNewCache->wipeOut();
			delete psslNewCache;
			psslNewCache = NULL;
		}
	
		fNotInitialized = TRUE;
	}

	virtual ~CRemoteLookupHandle() {

		DBGOUT(MEM2, "RemoteLookupHandle#" << (ulHandleCount--,ulHandleNo) 
					 << " Destroyed at" << CurrentTime() << "\n\n");

		rundown();

		delete pgvInterface;
		delete pgvTransferSyntax;
		delete pidObject;
		delete penEntryName;
	}

	virtual void setExpiryAge(ULONG newMax) {
		ULONG ulOldMax = ulCacheMax;
		ulCacheMax = newMax;
		if (plhFetched) plhFetched->setExpiryAge(newMax);
		if (ulCacheMax < ulOldMax) lookupIfNecessary();
	}

	virtual void lookupIfNecessary() 	// standard behavior is:
	  /*
			Note that this does not reinitialize the handle 
			-- that is deferred until a "next" or "finished" call
	  */
	{
		if (CurrentTime() - ulCreationTime > ulCacheMax) rundown();
	}

	virtual int finished() {				// default behavior
		if (fNotInitialized) initialize();
		return !plhFetched || plhFetched->finished();
	}

	virtual NSI_BINDING_VECTOR_T *next() {	// default behavior
		if (fNotInitialized) initialize();
		return plhFetched ? plhFetched->next() : NULL;
	}
};




/*++

Class Definition:

    CNetLookupHandle

Abstract:

    This is the common lookup handle class for network-based lookup.

    Since net lookup is now "lazy" (net is accessed only if needed), we need
	a lazy handle which initializes itself according to the old pattern of
	"use master if you can, broadcast if you must".

--*/

class CNetLookupHandle : public CRemoteLookupHandle {

	CRemoteLookupHandle *pRealHandle;
	
	virtual void initialize(); 

#if DBG
	static ULONG ulNetLookupHandleCount;
	static ULONG ulNetLookupHandleNo;
	ULONG ulHandleNo;
#endif

  public:

	ULONG getStatus() { return pRealHandle->StatusCode; }

	virtual void rundown()
	{
		DBGOUT(TRACE,"CNetLookupHandle::rundown called for Handle#" << ulHandleNo << "\n\n");
		if (pRealHandle) 
		{
			pRealHandle->rundown();
			delete pRealHandle;
			pRealHandle = NULL;
		}

		fNotInitialized = TRUE;
	}

	CNetLookupHandle(
				UNSIGNED32			EntryNameSyntax,
				STRING_T			EntryName,
				CGUIDVersion	*	pGVInterface,
				CGUIDVersion	*	pGVTransferSyntax,
				CGUID			*	pIDobject,
				unsigned long		ulVectorSize,
				unsigned long		ulCacheAge
				) :
				CRemoteLookupHandle(
						EntryNameSyntax,
						EntryName,
						pGVInterface,
						pGVTransferSyntax,
						pIDobject,
						ulVectorSize,
						ulCacheAge
						)
	{
		pRealHandle = NULL;
		fNotInitialized = TRUE;
#if DBG
		ulNetLookupHandleCount++;
		ulHandleNo = ++ulNetLookupHandleNo;
#endif
	}

	~CNetLookupHandle()
	{
		rundown();
#if DBG
		ulNetLookupHandleCount--;
#endif
	}

	NSI_BINDING_VECTOR_T *next() {
		DBGOUT(TRACE,"CNetLookupHandle::next called for Handle#" << ulHandleNo << "\n\n");
		if (fNotInitialized) initialize();
		return pRealHandle->next();
	}

	int finished() {
		DBGOUT(TRACE,"CNetLookupHandle::finished called for Handle#" << ulHandleNo << "\n\n");
		if (fNotInitialized) initialize();
		return pRealHandle->finished();
	}
};

/*++

Class Definition:

    CServerObjectInqHandle

Abstract:

    This is the object inquiry handle class for local (owned) server entries.

	Since NS handles are used as context handles in RPC calls, the RPC runtime
	guarantees serialization and we do not need to use critical sections explicitly.

--*/

class CServerObjectInqHandle : public CObjectInqHandle {

	TGuidIterator *pcgIterSource;

  public:

    CServerObjectInqHandle(
		TGuidIterator *pHandle,
		ULONG cacheMax = 0
		) : pcgIterSource(pHandle)
	{
		ulCacheMax = cacheMax;
	}

	GUID *next();

	virtual void lookupIfNecessary() {}		// never redo lookup for local info

	int finished() {
		return pcgIterSource->finished();
	}

	virtual ~CServerObjectInqHandle() {
		rundown();
	}

	virtual void rundown();
};





/*++

Class Definition:

    CServerEntry

Abstract:

    The specific Entry class for entries with binding and object attributes.

--*/

class CServerEntry : public CEntry {
	
protected:
	
	TCSafeSkipList<CGUID> ObjectList;			// object attribute
	TCSafeSkipList<CInterface> InterfaceList;	// binding attribute

	TSLLString*
	CServerEntry::formObjectStrings(
							CGUID*	pIDobject
							);

public:
	
	CServerEntry(CONST_STRING_T pszStr) 
		: CEntry(pszStr, ServerEntryType) {}
	
	CServerEntry(CONST_STRING_T pszStr, const EntryType type) 
		: CEntry(pszStr, type) {}
	
	virtual void flush();		// inherited from CEntry

	virtual BOOL isEmpty() {
		CriticalReader me(rwEntryGuard);
		return (ObjectList.size() == 0) && (InterfaceList.size() == 0);
	}

	virtual ~CServerEntry() {	// cache version could be derived from it
		flush();
	}

	virtual int addObjects(NSI_UUID_VECTOR_P_T ObjectVector);

	virtual int removeObjects(
				NSI_UUID_VECTOR_P_T ObjectVector,
				int& fRemovedAll
				);

	virtual int addToInterface(
			NSI_INTERFACE_ID_T *,
			NSI_SERVER_BINDING_VECTOR_T *,
			CInterfaceIndex *
			);

	virtual int removeInterfaces(
					NSI_IF_ID_P_T Interface,
					UNSIGNED32 VersOption,
					CInterfaceIndex *
					);
	
	int memberObject(CGUID *obj) {
		CriticalReader me(rwEntryGuard);
		return ObjectList.find(obj) != NULL;
	}

	virtual CLookupHandle * lookup(
			CGUIDVersion	*	pGVInterface,
			CGUIDVersion	*	pGVTransferSyntax,
			CGUID			*	pIDobject,
			unsigned long		ulVectorSize,
			unsigned long		ulCacheAge		// ignored in this case
		);

	virtual CObjectInqHandle * objectInquiry(
			unsigned long		ulCacheAge
		);

	virtual CMailSlotReplyHandle * MailSlotLookup(
			CGUIDVersion	*	pGVInterface,
			CGUID			*	pIDobject
		);
};


/*++

Class Definition:

    CCacheServerEntry

Abstract:

    A variation on CServerEntry with modifications to reflect the fact that the
	info is cached rather than owned by the locator, including a notion of being
	current based on the earliest caching time of any info in the entry.  Note that
	info may be added incrementally at various times.


--*/

class CCacheServerEntry : public CServerEntry {

	ULONG ulCacheTime;
	int fHasCachedInfo;

public:

	virtual BOOL isCurrent(ULONG ulTolerance);

	CCacheServerEntry(
				CONST_STRING_T pszStr
				) 
		: CServerEntry(pszStr,CacheServerEntryType)
	{
		fHasCachedInfo = FALSE;
		ulCacheTime = 0;
	}
	
	virtual void flush() 
	{		
		CServerEntry::flush();

		CriticalWriter me(rwCacheEntryGuard);

		DBGOUT(OBJECT, "\nFlushing CCacheServerEntry\n");
		DBGOUT(OBJECT, "EntryName = " << getCurrentName() << WNL);
		DBGOUT(OBJECT, "This entry has a ulCacheTime = " << ulCacheTime << "\n\n");

		fHasCachedInfo = FALSE;
	}

	

	int addObjects(NSI_UUID_VECTOR_P_T ObjectVector) {

		{
			CriticalWriter me(rwCacheEntryGuard);

			if (!fHasCachedInfo) {
				ulCacheTime = CurrentTime();
				fHasCachedInfo = TRUE;
			}

			else {
				DBGOUT(OBJECT, "\nPerforming addObjects on a nonempty entry\n");
				DBGOUT(OBJECT, "EntryName = " << getCurrentName() << WNL);
				DBGOUT(OBJECT, "This entry has a ulCacheTime = " << ulCacheTime << WNL);
				DBGOUT(OBJECT, "Current Time = " << CurrentTime() << "\n\n");
				DBGOUT(OBJECT, "The Objects:\n" << ObjectVector);
			}
		}

		return CServerEntry::addObjects(ObjectVector);
	}

	int addToInterface(
			NSI_INTERFACE_ID_T *pInf,
			NSI_SERVER_BINDING_VECTOR_T *pBVT,
			CInterfaceIndex *pIndex
			)
	{
		{
			CriticalWriter me(rwCacheEntryGuard);

			if (!fHasCachedInfo) {
				ulCacheTime = CurrentTime();
				fHasCachedInfo = TRUE;
			}

			else {
				DBGOUT(OBJECT, "\nPerforming addToInterface on a nonempty entry\n");
				DBGOUT(OBJECT, "EntryName = " << getCurrentName() << WNL);
				DBGOUT(OBJECT, "This entry has a ulCacheTime = " << ulCacheTime << WNL);
				DBGOUT(OBJECT, "Current Time = " << CurrentTime() << WNL << WNL);
				DBGOUT(OBJECT, "The Bindings:\n" << pBVT);
			}
		}

		return CServerEntry::addToInterface(pInf,pBVT,pIndex);
	}

	int removeObjects(	// shouldn't happen to a cached entry
				NSI_UUID_VECTOR_P_T ObjectVector,
				int& fRemovedAll
				)
	{
		Raise(NSI_S_ENTRY_NOT_FOUND);

		/* the following just keeps the compiler happy */

		return FALSE;
	}

	virtual CObjectInqHandle * objectInquiry(
			unsigned long		ulCacheAge
		);

	int removeInterfaces(	// shouldn't happen to a cached entry
				NSI_IF_ID_P_T Interface,
				UNSIGNED32 VersOption,
				CInterfaceIndex &
				)
	{
		Raise(NSI_S_ENTRY_NOT_FOUND);

		/* the following just keeps the compiler happy */

		return FALSE;

	}
	
	virtual CLookupHandle * lookup(
			CGUIDVersion	*	pGVInterface,
			CGUIDVersion	*	pGVTransferSyntax,
			CGUID			*	pIDobject,
			unsigned long		ulVectorSize,
			unsigned long		ulCacheAge
		);

	virtual CMailSlotReplyHandle * MailSlotLookup(
			CGUIDVersion	*	pGVInterface,
			CGUID			*	pIDobject
		)

	// Cached info is not returned in response to a broadcast, hence

	{ 
		return NULL;
	}
};



/*++

Class Definition:

    CFullServerEntry

Abstract:

    This class is used to account for the fact that direct exports to
	the same server entry may be made on two different machines.
	As a result, the information in a server entry is partly cached
	remote handles and partly locally exported handles.  This is a 
	temporary situation until we have a persistent global database.
	Therefore this struct does the minimal necessary to tide us over 
	until then.	In particular, it is not a real server entry, but
	exposes two kinds of server entries within itself.

--*/

class CFullServerEntry : public CEntry {

	BOOL fNetLookupDone;

	void flushCacheIfNecessary(ULONG ulTolerance);

	CServerEntry *pLocalEntry;
	CCacheServerEntry *pCachedEntry;
		
  public:

	CServerEntry *getLocal() { 
		CriticalReader me(rwFullEntryGuard);
		return pLocalEntry; 
	}

	CCacheServerEntry *getCache() { 
		CriticalReader me(rwFullEntryGuard);
		return pCachedEntry; 
	}
	
	CFullServerEntry(
		CONST_STRING_T pszName
		)
		:CEntry(
			pszName, 
			FullServerEntryType
			)
	{
		pLocalEntry = new CServerEntry(pszName);
		pCachedEntry = new CCacheServerEntry(pszName);

		fNetLookupDone = FALSE;
	}

	virtual ~CFullServerEntry() {
		delete pLocalEntry;
		delete pCachedEntry;
	}

	virtual BOOL isEmpty() {
		CriticalReader me(rwFullEntryGuard);
		return pLocalEntry->isEmpty() && pCachedEntry->isEmpty();
	}

	virtual CLookupHandle * lookup(
			CGUIDVersion	*	pGVInterface,
			CGUIDVersion	*	pGVTransferSyntax,
			CGUID			*	pIDobject,
			unsigned long		ulVectorSize,
			unsigned long		ulCacheAge
			);

	virtual CObjectInqHandle * objectInquiry(
			unsigned long		ulCacheAge
			);

	virtual CMailSlotReplyHandle * MailSlotLookup(
					CGUIDVersion	*	pGVInterface,
					CGUID			*	pIDobject
					);
};




/*++

Class Definition:

    CCacheServerObjectInqHandle

Abstract:

    This is the object inquiry handle class for cached server entries.

	The only difference from a local CServerObjectInqHandle is
	the functionality added by CRemoteObjectInqHandle.

--*/

class CCacheServerObjectInqHandle : public CRemoteObjectInqHandle {

	void initialize();

public:

	CCacheServerObjectInqHandle(
					STRING_T pszName,
					ULONG ulCacheAge
					);
};




/*++

Class Definition:

    CGroupEntry

Abstract:

    A stub for future extension.

--*/

class CGroupEntry : public CEntry {
	
protected:
	
	TCSafeSkipList<CStringW> GroupList;

public:
	
	CGroupEntry(const STRING_T pszStr) 
		: CEntry(pszStr, GroupEntryType) {}
};




/*++

Class Definition:

    CProfileEntry

Abstract:

    A stub for future extension.

--*/

class CProfileEntry : public CEntry {
	
public:
	
	CProfileEntry(const STRING_T pszStr) 
		: CEntry(pszStr, ProfileEntryType) {}
};



/*++

Class Definition:

    CServerLookupHandle

Abstract:

    This is the binding lookup handle class for local (owned) server entries.

	Since NS handles are used as context handles in RPC calls, the RPC runtime
	guarantees serialization and we do not need to use critical sections explicitly.

--*/

class CServerLookupHandle : public CLookupHandle {

	unsigned long ulVectorSize;

	TBVIterator *pBVIterator;

public:

	CServerLookupHandle(
			TBVSafeLinkList			*	pBVLL
		);

	virtual ~CServerLookupHandle() {
		rundown();
	}

	virtual void lookupIfNecessary() {}		// never redo lookup for local info

	NSI_BINDING_VECTOR_T *next() {
		CBVWrapper *pBVW = pBVIterator->next();
		NSI_BINDING_VECTOR_T *pResult = pBVW->pBVT;	// unwrap
		delete pBVW;								// throw away wrapper
		return pResult;
	}

	int finished() {
		return pBVIterator->finished();
	}

	virtual void rundown();
};



/*++

Class Definition:

    CCacheServerLookupHandle

Abstract:

    This is the lookup handle class for cached server entries.

	The only difference from a local CServerLookupHandle is
	the functionality added by CRemoteLookupHandle.

--*/

class CCacheServerLookupHandle : public CRemoteLookupHandle {
	
	void initialize();

public:

	CCacheServerLookupHandle(
					STRING_T pszName,
					CGUIDVersion *pGVInterface,
					CGUIDVersion *pGVTransferSyntax,
					CGUID *pIDobject,
					ULONG ulVectorSize,
					ULONG ulCacheAge
					);
};




/*++

Class Definition:

    CGroupLookupHandle

Abstract:

    This is the lookup handle class for group entries and other groups.
	Its primary use currently is to produce handles for null-entry
	lookups where information from multiple entries needs to be collected.

--*/

class CGroupLookupHandle : public CLookupHandle {

  protected:

	/* search parameters */

	CGUIDVersion	*	pGVInterface;
	CGUIDVersion	*	pGVTransferSyntax;
	CGUID			*	pIDobject;
	unsigned long		ulVectorSize;

	/* Iterator for entries in the group  --  please use a guarded kind! */

	TEntryIterator *pEIterator;

	/* handle for the currently active entry */

	CLookupHandle * pCurrentHandle;

	void advanceCurrentHandle();	// look for next nonempty entry handle

public:

	CGroupLookupHandle(
			TEntryIterator	*	pEI,
			CGUIDVersion	*	pGVInterface,
			CGUIDVersion	*	pGVTransferSyntax,
			CGUID			*	pIDobject,
			unsigned long		ulVectorSize,
			unsigned long		ulCacheAge
		);

	virtual ~CGroupLookupHandle() {
		rundown();

		delete pEIterator;
		delete pGVInterface;
		delete pGVTransferSyntax;
		delete pIDobject;
	}

	virtual void lookupIfNecessary() {} 

	NSI_BINDING_VECTOR_T *next(
		);

	int finished();

	virtual void rundown() 
	{
		if (pCurrentHandle) 
		{
			pCurrentHandle->rundown();
			delete pCurrentHandle;
			pCurrentHandle = NULL;
		}
	}

};




/*++

Class Definition:

    CIndexLookupHandle

Abstract:

    This is a specialization of the group handle class for Index lookup.
	The only difference is an implementation of lookupIfNecessary().

--*/

class CIndexLookupHandle : public CGroupLookupHandle {

public:

	CIndexLookupHandle(
			CGUIDVersion	*	pGVInterface,
			CGUIDVersion	*	pGVTransferSyntax,
			CGUID			*	pIDobject,
			unsigned long		ulVectorSize,
			unsigned long		ulCacheAge
		);

	virtual void lookupIfNecessary(); 

};


/*++

Template Class Definition:

    CCompleteHandle

Abstract:

	The complete handle template implements the idea that a top level NS handle
	typically is heterogeneous -- it contains info that is a) owned b) cached
	and c) just captured from the net.  The behavior of such a handle is 
	independent of the nature of the data being looked up, hence the template.

--*/

template <class ItemType>
class CCompleteHandle : public CNSHandle<ItemType> {

	/* these three handles are owned by this object and therefore  
	   destroyed when it is deleted */

	CNSHandle<ItemType> * pLocalHandle;
	CNSHandle<ItemType> * pCacheHandle;
	CNSHandle<ItemType> * pNetHandle;

  public:

#if DBG
	static ULONG ulHandleCount;
	static ULONG ulHandleNo;
	ULONG ulMyHandleNo;
#endif

	ULONG netStatus() { return pNetHandle->getStatus(); }

	CCompleteHandle(
			CNSHandle<ItemType> * pLocal,
			CNSHandle<ItemType> * pCache,
			CNSHandle<ItemType> * pNet,
			ULONG ulCacheAge
			)
	{

#if DBG
		ulHandleCount++;
		ulHandleNo++;
		ulMyHandleNo = ulHandleNo;
#endif
		DBGOUT(MEM1, "CompleteHandle#" << ulMyHandleNo << " Created at" 
									  << CurrentTime() << "\n\n");

		pLocalHandle = pLocal;
		pCacheHandle = pCache;
		pNetHandle = pNet;
		setExpiryAge(ulCacheAge);

		//  By checking if there is any info at all, we force
		//  initialization of remote handles if necessary
		finished(); 

	}

	~CCompleteHandle() {

		DBGOUT(MEM1, "CompleteHandle#" << (ulHandleCount--,ulMyHandleNo) << " Destroyed at" 
									  << CurrentTime() << "\n\n");


		rundown();
	}

	/* setExpiryAge on component handles should accomplish the needful */

	virtual void lookupIfNecessary() {}

	virtual void rundown();

	virtual void setExpiryAge(
						ULONG ulCacheAge
						)
	{
		ulCacheMax = ulCacheAge;
		if (pLocalHandle) pLocalHandle->setExpiryAge(ulCacheAge);
		if (pCacheHandle) pCacheHandle->setExpiryAge(ulCacheAge);
		if (pNetHandle) pNetHandle->setExpiryAge(ulCacheAge);
	}

	ItemType *next();

	int finished();
};


template <class ItemType>
ULONG 
CCompleteHandle<ItemType>::ulHandleCount = 0;


template <class ItemType>
ULONG 
CCompleteHandle<ItemType>::ulHandleNo = 0;


template <class ItemType>
void 
CCompleteHandle<ItemType>::rundown() 
{
	if (pLocalHandle) pLocalHandle->rundown();
	if (pCacheHandle) pCacheHandle->rundown();
	if (pNetHandle) pNetHandle->rundown();
		
	delete pLocalHandle;
	delete pCacheHandle;
	delete pNetHandle;

	pLocalHandle = NULL;
	pCacheHandle = NULL;
	pNetHandle = NULL;
}


template <class ItemType>
ItemType * 
CCompleteHandle<ItemType>::next()
{
	if (pLocalHandle)
		if (!pLocalHandle->finished())
			return pLocalHandle->next();
		else {
			delete pLocalHandle;
			pLocalHandle = NULL;
		}

	if (pCacheHandle)
		if (!pCacheHandle->finished())
			return pCacheHandle->next();
		else {
			delete pCacheHandle;
			pCacheHandle = NULL;
		}

	if (pNetHandle)
		if (!pNetHandle->finished())
			return pNetHandle->next();
		else {
			delete pNetHandle;
			pNetHandle = NULL;
		}

	return NULL;
}



template <class ItemType>
int  
CCompleteHandle<ItemType>::finished() 
{
	return (pLocalHandle ? pLocalHandle->finished() : TRUE) &&
		   (pCacheHandle ? pCacheHandle->finished() : TRUE) &&
		   (pNetHandle ? pNetHandle->finished() : TRUE);
}





/*++

Class Definition:

    CInterfaceIndex

Abstract:

    This class defines an entire interface index in the locator.
	It is like a pseudo entry, but hasn't been formatted that way.
	The form of the lookup method suggests it.

--*/

class Locator;

class CInterfaceIndex {

	struct CInterfaceIndexEntry : public CGUID {	// private class

		TCSafeSkipList<CStringW> PossibleEntries;

		CInterfaceIndexEntry(CGUID& guid) : CGUID(guid)
		{}

		~CInterfaceIndexEntry() {
			PossibleEntries.wipeOut();
		}

		void insert(
			CServerEntry * pSElocation) {

			CStringW *psw = new CStringW(*pSElocation);

			if (Duplicate == PossibleEntries.insert(psw))
				delete psw;
		}

		void remove(CServerEntry * pSElocation) {

			CStringW * deletedItem =
				PossibleEntries.remove(pSElocation);

			// the item had better be there!

			ASSERT(deletedItem, "Interface Index Corrupted\n");
		}
	};

	CReadWriteSection rwLock;

	TCSafeSkipList<CInterfaceIndexEntry> InterfaceEntries;

	/* the null index contains all entries in EntryList */

	CInterfaceIndexEntry *pNullIndex;

	Locator *pMyLocator;

public:

	CInterfaceIndex(
		Locator *myL
		) 
	{
		CGUID nullGUID;	// inits as null UUID thru default constructor
		pNullIndex = new CInterfaceIndexEntry(nullGUID);
		pMyLocator = myL;
	}

	~CInterfaceIndex() {
		InterfaceEntries.wipeOut();
		delete pNullIndex;
	}

	void insert(
			CServerEntry * pSElocation,
			CInterface * pInf);

	void remove(
			CServerEntry * pSElocation,
			CInterface * pInf);

	TSLLEntryList * lookup(
			CGUIDVersion	*	pGVInterface
		);

};


#endif // _OBJECTS_