/*
	$Id: DSMgr.cpp,v 1.1 2003/04/20 23:34:11 dasenbro Exp $

	File:		DSMgr.cpp

	Contains:	C++ implementation of Apple Directory Services integration

	Version:	Apple Mail Server - Mac OS X :  $Revision: 1.1 $

	Copyright:	© 1996-2001 by Apple Computer, Inc., all rights reserved.

	Change History:

		$Log: DSMgr.cpp,v $
		Revision 1.1  2003/04/20 23:34:11  dasenbro
		Initial check-in.
		
		Revision 1.55  2002/07/13 01:17:48  dasenbro
		Added new attribute get/set/initialize routines for temp SSL certificates.
		
		Revision 1.54  2002/07/04 18:17:11  dasenbro
		Added HasPasswdServer() and GetPasswdServerInfo().
		
		Revision 1.53  2002/06/10 22:43:47  dasenbro
		Set the correct flag when checking Kerberos state.
		
		Revision 1.52  2002/06/04 18:00:02  dasenbro
		Fixed a memory leak in OpenLocalNode().
		
		Revision 1.51  2002/05/30 17:15:22  dasenbro
		Added Get/SetRequireCRAM_MD5_IMAP_Flag()  init calls.
		
		Revision 1.50  2002/05/20 21:18:35  dasenbro
		Removed GetDeliverToLocalOnlyFlag() status flags.
		
		Revision 1.49  2002/05/09 16:58:56  dasenbro
		Changed all str... calls to CUtils::Str... to be NULL safe.
		
		Revision 1.48  2002/04/18 18:09:10  dasenbro
		Changed bool to Bool for word alignment.
		
		Revision 1.47  2002/04/16 05:52:11  dasenbro
		Removed unneeded error logs.
		
		Revision 1.46  2002/03/21 16:38:52  dasenbro
		Addes network transition support.
		
		Revision 1.45  2002/03/12 00:08:13  dasenbro
		Updated PB WO project files.
		
		Revision 1.44  2002/03/11 23:07:52  dasenbro
		Added support for directory based host prefs.
		
		Revision 1.43  2002/03/05 19:27:40  dasenbro
		Added calls to update rejection list and removed calls
		to MailAdminEvents.h
		
		Revision 1.42  2002/02/20 21:07:11  dasenbro
		Removed #include "MailAdminAEvents.h", split the init calls to
		init the log prefs earlier than the rest and added better log messages
		 for server prefs.
		
		Revision 1.41  2002/01/23 22:16:50  dasenbro
		Added calls to get alternate mailbox location for prefs initialization.

		Revision 1.40  2002/01/14 17:41:36  dasenbro
		Initial S4 updates.

		Revision 1.39  2001/09/15 00:36:44  dasenbro
		Fixed a leak when looking up users in the parent directory
		node.

		Revision 1.38  2001/09/13 23:51:04  dasenbro
		Fixed a leak in GetGroups.

		Revision 1.37  2001/07/09 17:20:55  dasenbro
		Return local unknown error for all unknown DS errors.

		Revision 1.36  2001/06/21 20:50:55  dasenbro
		Updated file header info.

		Revision 1.35  2001/06/21 17:15:11  dasenbro
		Added protection for log user names and passwords.

		Revision 1.34  2001/05/23 22:32:00  dasenbro
		Added GetGroups() for ACL support.


 */
 
#include <stdio.h>		// for sprintf
#include <stdlib.h>		// for calloc
#include <ctype.h>		// for isdigit

#include "DSMgr.h"
#include "CGlobals.h"
#include "DSMailUser.h"
#include "DSMailGroup.h"
#include "DSServerPrefs.h"
#include "DSHostPrefs.h"
#include "UException.h"
#include "AppResources.h"
#include "CUtils.h"


// -- Globals ---------------------------------------------------------------

DSMgr		*gDSMgr				= nil;
DSMgr		*gDSMgrRouter		= nil;

// -- Static Globals --------------------------------------------------------

static const uInt32		kTDataBuffSize	= 8192;

static uInt32			sLookupCount	= 0;
static uInt32			sCacheHitCount	= 0;


// -- Static Methods --------------------------------------------------------

// --------------------------------------------------------------------------------
//	* GetServerPrefsObj ()
//
// --------------------------------------------------------------------------------

DSServerPrefs* DSMgr::GetServerPrefsObj ( void )
{
	DSServerPrefs	*pSrvrPrefs	= nil;

	if ( gDSMgr != nil )
	{
		pSrvrPrefs = gDSMgr->GetServerPrefs();
	}

	return( pSrvrPrefs );

} // GetServerPrefsObj


// --------------------------------------------------------------------------------
//	* GetHostsPrefsObj ()
//
// --------------------------------------------------------------------------------

DSHostPrefs* DSMgr::GetHostPrefsObj ( void )
{
	DSHostPrefs	*pHostPrefs	= nil;

	if ( gDSMgr != nil )
	{
		pHostPrefs = gDSMgr->GetHostPrefs();
	}

	return( pHostPrefs );

} // GetHostsPrefsObj


// --------------------------------------------------------------------------------
//	* DSMgr ()
//
// --------------------------------------------------------------------------------

DSMgr::DSMgr ( void )
{
	fInitialized	= false;
	fHasPasswdSrvr	= false;
	fDSRef			= 0;
	fSearchNodeRef	= 0;
	fTDataBuff		= nil;
	fUserRecType	= nil;
	fGroupRecType	= nil;
	fUserAttrType	= nil;
	fGroupAttrType	= nil;
	fUIDAttrType	= nil;
	fGIDAttrType	= nil;
	fMailGroupID	= nil;
	fSrvrPrefs		= nil;
	fHostPrefs		= nil;
} // DSMgr


// --------------------------------------------------------------------------------
//	* ~DSMgr
//
// --------------------------------------------------------------------------------

DSMgr::~DSMgr ( void )
{
	if ( fSrvrPrefs != nil )
	{
		delete( fSrvrPrefs );
		fSrvrPrefs = nil;
	}

	if ( fHostPrefs != nil )
	{
		delete( fHostPrefs );
		fHostPrefs = nil;
	}
} // ~DSMgr


// --------------------------------------------------------------------------------
//	* Initialize
//
// --------------------------------------------------------------------------------

tDirStatus DSMgr::Initialize ( void )
{
	tDirStatus			dsStatus	= eDSNoErr;
	tDataNodePtr		pRecName	= nil;
	tDataNodePtr		pRecType	= nil;
	tRecordReference	recRef		= 0;

	try
	{
		// Open Directory Services
		dsStatus = this->OpenDirServices();
		if ( dsStatus == eDSNoErr )
		{
			// Open the Search Node
			dsStatus = this->AllocateBuffs();
			if ( dsStatus == eDSNoErr )
			{
				dsStatus = OpenSearchNode();
				if ( dsStatus == eDSNoErr )
				{
					dsStatus = OpenLocalNode();
				}
			}
			else
			{
				(void)::dsCloseDirService( fDSRef );
			}
		}

		// Initialize Server Prefs
		if ( dsStatus == eDSNoErr )
		{
			if ( fSrvrPrefs == nil )
			{
				fSrvrPrefs = new DSServerPrefs();
				ThrowIfNULL_( fSrvrPrefs );
			}

			pRecName = ::dsDataNodeAllocateString( fDSRef, "AppleMailServer" );
			pRecType = ::dsDataNodeAllocateString( fDSRef, kDSStdRecordTypeConfig );

			dsStatus = ::dsOpenRecord( fLocalNodeRef, pRecType, pRecName, &recRef );
			if ( dsStatus != eDSNoErr )
			{
				dsStatus = ::dsCreateRecordAndOpen( fLocalNodeRef, pRecType, pRecName, &recRef );
				if ( dsStatus == eDSNoErr )
				{
					fSrvrPrefs->SetDirRef( fDSRef );
					fSrvrPrefs->SetDirNodeRef( fLocalNodeRef );
					fSrvrPrefs->SetDirRecordRef( recRef );
				}
			}
			else
			{
				fSrvrPrefs->SetDirRef( fDSRef );
				fSrvrPrefs->SetDirNodeRef( fLocalNodeRef );
				fSrvrPrefs->SetDirRecordRef( recRef );
			}

			(void)::dsDataNodeDeAllocate( fDSRef, pRecName );
			(void)::dsDataNodeDeAllocate( fDSRef, pRecType );
		}

		// Initialize Host Prefs
		if ( dsStatus == eDSNoErr )
		{
			if ( fHostPrefs == nil )
			{
				fHostPrefs = new DSHostPrefs();
				ThrowIfNULL_( fHostPrefs );
			}

			pRecName = ::dsDataNodeAllocateString( fDSRef, "Default" );
			pRecType = ::dsDataNodeAllocateString( fDSRef, "dsRecTypeNative:config/AppleMailServer/Hosts" );

			dsStatus = ::dsOpenRecord( fLocalNodeRef, pRecType, pRecName, &recRef );
			if ( dsStatus != eDSNoErr )
			{
				dsStatus = ::dsCreateRecordAndOpen( fLocalNodeRef, pRecType, pRecName, &recRef );
				if ( dsStatus == eDSNoErr )
				{
					fHostPrefs->SetDirRef( fDSRef );
					fHostPrefs->SetDirNodeRef( fLocalNodeRef );
					fHostPrefs->SetDirRecordRef( recRef );
				}
			}
			else
			{
				fHostPrefs->SetDirRef( fDSRef );
				fHostPrefs->SetDirNodeRef( fLocalNodeRef );
				fHostPrefs->SetDirRecordRef( recRef );
			}

			(void)::dsDataNodeDeAllocate( fDSRef, pRecName );
			(void)::dsDataNodeDeAllocate( fDSRef, pRecType );
		}
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}

	return( dsStatus );

} // Initialize


// --------------------------------------------------------------------------------
//	* DeInitialize ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::DeInitialize ( void )
{
	sInt32		status	= kDSErrNoErr;

	try
	{
		if ( fTDataBuff != nil )
		{
			(void)::dsDataBufferDeAllocate( fDSRef, fTDataBuff );
			fTDataBuff = nil;
		}

		if ( fUserRecType != nil )
		{
			(void)::dsDataListDeallocate( fDSRef, fUserRecType );
			fUserRecType = nil;
		}

		if ( fUserAttrType != nil )
		{
			(void)::dsDataListDeallocate( fDSRef, fUserAttrType );
			fUserAttrType = nil;
		}

		if ( fGroupRecType != nil )
		{
			(void)::dsDataListDeallocate( fDSRef, fGroupRecType );
			fGroupRecType = nil;
		}

		if ( fGroupAttrType != nil )
		{
			(void)::dsDataListDeallocate( fDSRef, fGroupAttrType );
			fGroupAttrType = nil;
		}


		if ( fUIDAttrType != nil )
		{
			(void)::dsDataNodeDeAllocate( fDSRef, fUIDAttrType );
			fUIDAttrType = nil;
		}

		if ( fGIDAttrType != nil )
		{
			(void)::dsDataNodeDeAllocate( fDSRef, fGIDAttrType );
			fGIDAttrType = nil;
		}

		if ( fSrvrPrefs != nil )
		{
			fSrvrPrefs->DeInitialize();
		}

		if ( fHostPrefs != nil )
		{
			fHostPrefs->DeInitialize();
		}

		if ( fSearchNodeRef != 0 )
		{
			(void)::dsCloseDirNode( fSearchNodeRef );
			fSearchNodeRef = 0;
		}

		if ( fDSRef != 0 )
		{
			status = ::dsCloseDirService( fDSRef );
			fDSRef = 0;
		}
	}

	catch ( long err )
	{
		status = err;
	}

	catch ( ... )
	{
		status = kUnknownError;
	}

	fDSRef			= 0;
	fSearchNodeRef	= 0;

	return( status );

} // DeInitialize


// --------------------------------------------------------------------------------
//	* ReInitialize ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::ReInitialize ( void )
{
	sInt32		siStatus	= kDSErrNoErr;

	try
	{
		this->DeInitialize();
		siStatus = this->Initialize();
	}

	catch( long err )
	{
		siStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		siStatus = eServerError;
	}

	return( siStatus );

} // ReInitialize


// --------------------------------------------------------------------------------
//	* GetPasswdServerInfo ()
//
// --------------------------------------------------------------------------------

tDirStatus DSMgr::GetPasswdServerInfo ( void )
{
	tDirStatus			dsStatus	= eDSNoErr;
	uInt32				recCount	= 0;
	tContextData		context		= nil;
	tDataList			recName;
	tDataList			recType;
	tDataList			attrType;

	fHasPasswdSrvr = false;

	try
	{
		dsStatus = ::dsBuildListFromStringsAlloc( fDSRef, &recName, "passwordserver", nil );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = ::dsBuildListFromStringsAlloc( fDSRef, &recType, kDSStdRecordTypeConfig, nil );
			if ( dsStatus == eDSNoErr )
			{
				dsStatus = ::dsBuildListFromStringsAlloc( fDSRef, &attrType, kDS1AttrPasswordServerLocation, nil );
				if ( dsStatus == eDSNoErr )
				{
					do
					{
						dsStatus = ::dsGetRecordList( fSearchNodeRef, fTDataBuff, &recName, eDSiExact, &recType,
													  &attrType, false, &recCount, &context );
				
						if ( (dsStatus == eDSNoErr) && (recCount != 0) ) 
						{
							fHasPasswdSrvr = true;
							break;
						}
					} while ( (context != NULL) && (dsStatus == eDSNoErr) );

					if ( context != NULL )
					{
						(void)::dsReleaseContinueData( fDSRef, context );
						context = nil;
					}

					::dsDataListDeAllocate( fDSRef, &attrType, true );
				}
				::dsDataListDeAllocate( fDSRef, &recType, true );
			}
			::dsDataListDeAllocate( fDSRef, &recName, true );
		}
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}

	return( dsStatus );

} // GetPasswdServerInfo


// --------------------------------------------------------------------------------
//	* OpenDirServices
//
// --------------------------------------------------------------------------------

tDirStatus DSMgr::OpenDirServices ( void )
{
	tDirStatus		dsStatus	= eDSNoErr;

	try
	{
		// We have no ref, so let's get one
		if ( fDSRef == 0 )
		{
			dsStatus = ::dsOpenDirService( &fDSRef );
		}
		else
		{
			// Is the one we have still good
			dsStatus = ::dsVerifyDirRefNum( fDSRef );
			if ( dsStatus != eDSNoErr )
			{
				// The ref is bad so let's get a new one
				fDSRef = 0;
				dsStatus = ::dsOpenDirService( &fDSRef );
			}
		}
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}

	return( dsStatus );

} // OpenDirServices


// --------------------------------------------------------------------------------
//	* OpenSearchNode
//
// --------------------------------------------------------------------------------

tDirStatus DSMgr::OpenSearchNode ( void )
{
	tDirStatus		dsStatus	= eDSNoErr;
	uInt32			uiCount		= 0;
	uInt32			uiIndex		= 0;
	tDataList	   *pDataList	= nil;

	try
	{
		if ( fTDataBuff == nil )
		{
			dsStatus = this->AllocateBuffs();
		}

		if ( (fTDataBuff != nil) && (dsStatus == eDSNoErr) )
		{
			dsStatus = ::dsFindDirNodes( fDSRef, fTDataBuff, nil, eDSSearchNodeName, &uiCount, nil );
			if ( dsStatus == eDSNoErr )
			{
				dsStatus = eDSNodeNotFound;
				if ( uiCount == 1 )
				{
					dsStatus = ::dsGetDirNodeName( fDSRef, fTDataBuff, 1, &pDataList );
					if ( dsStatus == eDSNoErr )
					{
						dsStatus = ::dsOpenDirNode( fDSRef, pDataList, &fSearchNodeRef );
					}

					if ( pDataList != nil )
					{
						(void)::dsDataListDeAllocate( fDSRef, pDataList, true );

						::free( pDataList );
						pDataList = nil;
					}
				}
			}
		}
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}

	return( dsStatus );

} // OpenSearchNode


// --------------------------------------------------------------------------------
//	* OpenLocalNode ()
//
// --------------------------------------------------------------------------------

tDirStatus DSMgr::OpenLocalNode ( void )
{
	tDirStatus		dsStatus	= eDSNoErr;
	uInt32			bufferCount	= 1;
	uInt32			uiCount		= 0;
	tDataBuffer	   *pNodeBuf	= nil;
	tDataList	   *pNodeName	= nil;

	pNodeBuf = ::dsDataBufferAllocate( fDSRef, bufferCount * 512 );
	if ( pNodeBuf != nil )
	{
		do {
			dsStatus = dsFindDirNodes( fDSRef, pNodeBuf, NULL, eDSLocalNodeNames, &uiCount, nil );
			if ( dsStatus == eDSBufferTooSmall )
			{
				dsStatus = ::dsDataBufferDeAllocate( fDSRef, pNodeBuf );
				pNodeBuf = ::dsDataBufferAllocate( fDSRef, ++bufferCount * 512 );
			}
		} while ( ((dsStatus == eDSNoErr && uiCount == 0)) || (dsStatus == eDSBufferTooSmall) );

		dsStatus = ::dsGetDirNodeName( fDSRef, pNodeBuf, 1, &pNodeName );
		if ( dsStatus == eDSNoErr )
		{
			dsStatus = ::dsOpenDirNode( fDSRef, pNodeName, &fLocalNodeRef );
		}
		if ( pNodeName != nil )
		{
			(void)::dsDataListDeAllocate( fDSRef, pNodeName, true );

			::free( pNodeName );
			pNodeName = nil;
		}

		(void)::dsDataBufferDeAllocate( fDSRef, pNodeBuf );
	}

	return( dsStatus );

} // OpenLocalNode


// --------------------------------------------------------------------------------
//	* AllocateBuffs ()
//
// --------------------------------------------------------------------------------

tDirStatus DSMgr::AllocateBuffs ( void )
{
	tDirStatus		dsStatus	= eDSNoErr;

	try
	{
		if ( fTDataBuff == NULL )
		{
			fTDataBuff = ::dsDataBufferAllocate( fDSRef, kTDataBuffSize );
			ThrowIfNULL_( fTDataBuff );
		}

		if ( fUserRecType == NULL )
		{
			fUserRecType = ::dsBuildListFromStrings( fDSRef, kDSStdRecordTypeUsers, nil );
			ThrowIfNULL_( fUserRecType );
		}

		if ( fGroupRecType == NULL )
		{
			fGroupRecType = ::dsBuildListFromStrings( fDSRef, kDSStdRecordTypeGroups, nil );
			ThrowIfNULL_( fGroupRecType );
		}

		if ( fUserAttrType == NULL )
		{
			fUserAttrType = ::dsBuildListFromStrings( fDSRef, kDS1AttrMailAttribute, kDS1AttrUniqueID, kDS1AttrPrimaryGroupID,
													kDS1AttrDistinguishedName, kDSNAttrMetaNodeLocation, kDSNAttrEMailAddress, nil );
			ThrowIfNULL_( fUserAttrType );
		}

		if ( fGroupAttrType == NULL )
		{
			fGroupAttrType = ::dsBuildListFromStrings( fDSRef, kDS1AttrPrimaryGroupID, kDSNAttrGroupMembership, nil );
			ThrowIfNULL_( fGroupAttrType );
		}

		if ( fUIDAttrType == NULL )
		{
			fUIDAttrType = ::dsDataNodeAllocateString( fDSRef, kDS1AttrUniqueID );
			ThrowIfNULL_( fUIDAttrType );
		}

		if ( fGIDAttrType == NULL )
		{
			fGIDAttrType = ::dsDataNodeAllocateString( fDSRef, kDS1AttrPrimaryGroupID );
			ThrowIfNULL_( fGIDAttrType );
		}
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}

	return( dsStatus );

} // AllocateBuffs


// --------------------------------------------------------------------------------
//	* CheckPostmaster ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::CheckPostmaster ( void )
{
	sInt32			siResult	= kNoErr;
	const char	   *pFullName	= nil;			// postmaster full name
	DSMailUser	   *pPostmaster	= nil;

	try
	{
		if ( pFullName != nil )
		{
			gDSMgr->GetUserByName( pFullName, &pPostmaster );
			if ( pPostmaster == nil )
			{
			}
			else
			{
				// Check to see if mail is turned on
				delete( pPostmaster );
				pPostmaster = nil;
			}
		}
		else
		{
			siResult = kNullPtr;
		}
	}

	catch( long err )
	{
		siResult = err;
	}

	catch( ... )
	{
		siResult = kUnknownError;
	}

	return( kNoErr );

} // CheckPostmaster



// --------------------------------------------------------------------------------
//	* GetUserByName ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::GetUserByName ( const char *inName, DSMailUser **outMailUser, Bool inUseCache )
{
	sInt32					status			= kDSErrNoErr;
	uInt32					i				= 0;
	uInt32					j				= 0;
	uInt32					recCount		= 0;
	sInt32					usrErr			= kNoErr;
	char				   *pMailUserAttr	= nil;
	char				   *pAcctName		= nil;
	char				   *pMailUserID		= nil;
	char				   *pGroupID		= nil;
	char				   *pRealName		= nil;
	char				   *pUserLocation	= nil;
	char				   *pEmailAddress	= nil;
	tDataList				recName;
	tRecordEntry		   *pRecEntry		= nil;
	tAttributeEntry		   *pAttrEntry		= nil;
	tAttributeValueEntry   *pValueEntry		= nil;
	tContextData			context			= nil;
	tAttributeListRef		attrListRef		= 0;
	tAttributeValueListRef	valueRef		= 0;
	ExceptionCode			exMutex			= kNoErr;

	::memset( &recName,  0, sizeof( tDataList ) );

	try
	{
		if ( inUseCache == true )
		{
			++sLookupCount;

			// First check the cache...
			*outMailUser = fMailCache.GetUserByName( inName );
			if ( *outMailUser != NULL )
			{
				++sCacheHitCount;
							return( kDSErrNoErr );
			}
		}

		// Make sure that this reference is still valid
		status = ::dsVerifyDirRefNum( fDSRef );
		if ( status != kDSErrNoErr )
		{
			status = this->Initialize();
			ThrowIfOSErr_( status );
		}

		status = ::dsBuildListFromStringsAlloc( fDSRef, &recName, inName, nil );
		ThrowIfOSErr_( status );

		do
		{
			// Get the user record(s) that matches the name
			status = ::dsGetRecordList( fSearchNodeRef, fTDataBuff, &recName, eDSiExact, fUserRecType,
										fUserAttrType, false, &recCount, &context );

			if ( (status == eDSNoErr) && (recCount != 0) ) 
			{
				// Get each record that matched the name
				for ( i = 1; ((i <= recCount) && (status == eDSNoErr)); i++ )
				{
					status = ::dsGetRecordEntry( fSearchNodeRef, fTDataBuff, i, &attrListRef, &pRecEntry );
					if ( status == eDSNoErr )
					{
						// Get the record name
						(void)::dsGetRecordNameFromEntry( pRecEntry, &pAcctName );

						// Get the attributes we care about for the record
						for ( j = 1; j <= pRecEntry->fRecordAttributeCount; j++ )
						{
							status = ::dsGetAttributeEntry( fSearchNodeRef, fTDataBuff, attrListRef, j, &valueRef, &pAttrEntry );
							if ( (status == eDSNoErr) && (pAttrEntry != nil) )
							{
								status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
								if ( (status == eDSNoErr) && (pValueEntry != nil) )
								{
									if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrMailAttribute ) == 0) && (pMailUserAttr == nil) )
									{
										// Get the user mail attribute
										pMailUserAttr = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pMailUserAttr, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
									else if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrDistinguishedName ) == 0) && (pRealName == nil) )
									{
										// Get the user's real name
										pRealName = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pRealName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
									else if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation ) == 0) && (pUserLocation == nil) )
									{
										// Get the user location
										pUserLocation = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
									else if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrUniqueID ) == 0) && (pMailUserID == nil) )
									{
										// Get the user ID
										pMailUserID = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pMailUserID, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
									else if ( CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrPrimaryGroupID ) == 0 )
									{
										// Get the user ID
										pGroupID = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pGroupID, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
									else if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrEMailAddress ) == 0) && (pEmailAddress == nil) )
									{
										// Get the user's email address if they have one
										pEmailAddress = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pEmailAddress, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
								}
								if ( pValueEntry != nil )
								{
									(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
									pValueEntry = nil;
								}
							}
							if ( pAttrEntry != nil )
							{
								(void)::dsCloseAttributeValueList( valueRef );
								(void)::dsDeallocAttributeEntry( fSearchNodeRef, pAttrEntry );
								pAttrEntry = nil;
							}
						}

						// Do we have the attributes we are looking for
						// We need all attributes for this to be a complete user (real name optional)
						if ( (pMailUserID != nil) &&
							 (pGroupID != nil) &&
							 (pUserLocation != nil) &&
							 ((pAcctName != nil) || (pRealName != nil)) &&
							 ((pMailUserAttr != nil) || (pEmailAddress != nil)) )
						{
							DSMailUser	*pTmp = new DSMailUser();
							ThrowIfNULL_( pTmp );

							pTmp->SetUserID( ::atoi( pMailUserID ) );
							pTmp->SetGroupID( ::atoi( pGroupID ) );
							(void)pTmp->SetUserName( pAcctName );
							(void)pTmp->SetRealName( pRealName );
							usrErr |= pTmp->SetUserLocation( pUserLocation );
							if ( pMailUserAttr != nil )
							{
								usrErr |= pTmp->SetAttrsFromXML( pMailUserAttr );
							}
							else
							{
								// fail over to email address
								usrErr |= pTmp->SetAutoFwd( pEmailAddress );
							}

							if ( usrErr == kNoErr )
							{
								// Have we found one
								if ( *outMailUser == nil )
								{
									*outMailUser = pTmp;
									pTmp = nil;
								}
								else
								{
									// We have more than one, are they the same
									//	They both have the same ID and we don't care if the name is different
									//	so we need to check the mail account location
									if ( ((*outMailUser)->GetAcctLocation() != nil) || (pTmp->GetAcctLocation() != nil) )
									{
										if ( CUtils::Strcmp( (*outMailUser)->GetAcctLocation(), pTmp->GetAcctLocation() ) != 0 )
										{
											status = kDSErrDuplicateUserName;
										}
									}
									else if ( ((*outMailUser)->GetSMTPForward() != nil) || (pTmp->GetSMTPForward() != nil) )
									{
										if ( CUtils::Strcmp( (*outMailUser)->GetSMTPForward(), pTmp->GetSMTPForward() ) != 0 )
										{
											status = kDSErrDuplicateUserName;
										}
									}
								}
							}

							if ( pTmp != nil )
							{
								// This one is not a player
								delete( pTmp );
								pTmp = nil;
							}

							if ( pAcctName != nil )
							{
								::free( pAcctName );
								pAcctName = nil;
							}

							// Cleanup the locals
							if ( pMailUserID != nil )
							{
								::free( pMailUserID );
								pMailUserID = nil;
							}

							if ( pGroupID != nil )
							{
								::free( pGroupID );
								pGroupID = nil;
							}
						}
						if ( pMailUserAttr != nil )
						{
							::free( pMailUserAttr );
							pMailUserAttr = nil;
						}

						if ( pUserLocation != nil )
						{
							::free( pUserLocation );
							pUserLocation = nil;
						}

						if ( pEmailAddress != nil )
						{
							::free( pEmailAddress );
							pEmailAddress = nil;
						}
					}

					if ( pRecEntry != nil )
					{
						(void)::dsCloseAttributeList( attrListRef );
						(void)::dsDeallocRecordEntry( fSearchNodeRef, pRecEntry );
						pRecEntry = nil;
					}
				}
			}
		} while ( (context != NULL) && (status == eDSNoErr) );
	}

	catch( long err )
	{
		status = err;
	}

	catch( ... )
	{
		status = kUnknownError;
	}

	if ( pMailUserAttr != nil )
	{
		::free( pMailUserAttr );
		pMailUserAttr = nil;
	}

	if ( pAcctName != nil )
	{
		::free( pAcctName );
		pAcctName = nil;
	}

	if ( pMailUserID != nil )
	{
		::free( pMailUserID );
		pMailUserID = nil;
	}

	if ( pGroupID != nil )
	{
		::free( pGroupID );
		pGroupID = nil;
	}

	if ( pRealName != nil )
	{
		::free( pRealName );
		pRealName = nil;
	}

	if ( pUserLocation != nil )
	{
		::free( pUserLocation );
		pUserLocation = nil;
	}

	if ( pEmailAddress != nil )
	{
		::free( pEmailAddress );
		pEmailAddress = nil;
	}

	if ( context != nil )
	{
		(void)::dsReleaseContinueData( fDSRef, context );
		context = nil;
	}

	::dsDataListDeAllocate( fDSRef, &recName, true );

	if ( (status == kNoErr) && (*outMailUser == nil) )
	{
		status = kDSErrUserNotFound;
	}
	else if ( *outMailUser != nil )
	{
		fMailCache.AddUserToCache( *outMailUser, inName );
	}

	return( status );

} // GetUserByName


// --------------------------------------------------------------------------------
//	* GetUserByID ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::GetUserByID ( long inUserID, DSMailUser **outMailUser, Bool inUseCache )
{
	sInt32					status			= kDSErrNoErr;
	uInt32					i				= 0;
	uInt32					j				= 0;
	uInt32					recCount		= 0;
	sInt32					usrErr			= kNoErr;
	char				   *pMailUserAttr	= nil;
	char				   *pGroupID		= nil;
	char				   *pRealName		= nil;
	char				   *pAcctName		= nil;
	char				   *pUserLocation	= nil;
	char				   *pEmailAddress	= nil;
	char					idStr[ 64 ];
	tDataNode			   *pUserID			= nil;
	tRecordEntry		   *pRecEntry		= nil;
	tAttributeEntry		   *pAttrEntry		= nil;
	tAttributeValueEntry   *pValueEntry		= nil;
	tContextData			context			= nil;
	tAttributeListRef		attrListRef		= 0;
	tAttributeValueListRef	valueRef		= 0;

	try
	{
		if ( inUseCache == true )
		{
			++sLookupCount;

			// First check the cache...
			*outMailUser = fMailCache.GetUserByID( inUserID );
			if ( *outMailUser != NULL )
			{
				++sCacheHitCount;
							return( kDSErrNoErr );
			}
		}

		::sprintf( idStr, "%lu", inUserID );
		pUserID = ::dsDataNodeAllocateString( fDSRef, idStr );
		ThrowIfNULL_( pUserID );

		// Make sure that this reference is still valid
		status = ::dsVerifyDirRefNum( fDSRef );
		if ( status != kDSErrNoErr )
		{
			status = this->Initialize();
			ThrowIfOSErr_( status );
		}

		do
		{
			// Get all user records with this ID
			status = ::dsDoAttributeValueSearch( fSearchNodeRef, fTDataBuff, fUserRecType, fUIDAttrType, eDSExact, pUserID, &recCount, &context );
			if ( (status == eDSNoErr) && (recCount != 0) )
			{
				// Get each record
				for ( i = 1; ((i <= recCount) && (status == eDSNoErr)); i++ )
				{
					status = ::dsGetRecordEntry( fSearchNodeRef, fTDataBuff, i, &attrListRef, &pRecEntry );
					if ( status == eDSNoErr )
					{
						// Get the record name
						(void)::dsGetRecordNameFromEntry( pRecEntry, &pAcctName );

						// Get the attributes for this record
						for ( j = 1; j <= pRecEntry->fRecordAttributeCount; j++ )
						{
							status = ::dsGetAttributeEntry( fSearchNodeRef, fTDataBuff, attrListRef, j, &valueRef, &pAttrEntry );
							if ( status == eDSNoErr )
							{
								if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrMailAttribute ) == 0) && (pMailUserAttr == nil) )
								{
									// Only get the first attribute value
									status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
									if ( status == eDSNoErr )
									{
										pMailUserAttr = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pMailUserAttr, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
								}
								else if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrDistinguishedName ) == 0) && (pRealName == nil) )
								{
									// Only get the first attribute value
									status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
									if ( status == eDSNoErr )
									{
										pRealName = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pRealName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
								}
								else if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation ) == 0) && (pUserLocation == nil) )
								{
									// Only get the first attribute value
									status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
									if ( status == eDSNoErr )
									{
										// Get the user location
										pUserLocation = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
								}
								else if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrEMailAddress ) == 0) && (pEmailAddress == nil) )
								{
									// Only get the first attribute value
									status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
									if ( status == eDSNoErr )
									{
										pEmailAddress = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pEmailAddress, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
								}
								else if ( CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrPrimaryGroupID ) == 0 )
								{
									// Only get the first attribute value
									status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
									if ( status == eDSNoErr )
									{
										// Get the user ID
										pGroupID = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
										::memcpy( pGroupID, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
									}
								}

								if ( pValueEntry != nil )
								{
									(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
									pValueEntry = nil;
								}
							}
							if ( pAttrEntry != nil )
							{
								(void)::dsCloseAttributeValueList( valueRef );
								(void)::dsDeallocAttributeEntry( fSearchNodeRef, pAttrEntry );
								pAttrEntry = nil;
							}
						}

						// Do we have the attributes we are looking for
						if ( (pUserLocation != nil) &&
							 (pGroupID != nil) &&
							 ((pAcctName != nil) || (pRealName != nil)) &&
							 ((pMailUserAttr != nil) || (pEmailAddress != nil)) )
						{
							DSMailUser	*pTmp = new DSMailUser();
							ThrowIfNULL_( pTmp );

							pTmp->SetUserID( inUserID );
							pTmp->SetGroupID( ::atoi( pGroupID ) );
							(void)pTmp->SetUserName( pAcctName );
							(void)pTmp->SetRealName( pRealName );
							usrErr |= pTmp->SetUserLocation( pUserLocation );
							if ( pMailUserAttr != nil )
							{
								usrErr |= pTmp->SetAttrsFromXML( pMailUserAttr );
							}
							else
							{
								// fail over to email address
								usrErr |= pTmp->SetAutoFwd( pEmailAddress );
							}

							if ( usrErr == kNoErr )
							{
								// Have we found one
								if ( *outMailUser == nil )
								{
									*outMailUser = pTmp;
									pTmp = nil;
								}
								else
								{
									// We have more than one, are they the same
									//	They both have the same ID and we don't care if the name is different
									//	so we need to check the mail account location
									if ( ((*outMailUser)->GetAcctLocation() != nil) || (pTmp->GetAcctLocation() != nil) )
									{
										if ( CUtils::Strcmp( (*outMailUser)->GetAcctLocation(), pTmp->GetAcctLocation() ) != 0 )
										{
											status = kDSErrDuplicateUserName;
										}
									}
									else if ( ((*outMailUser)->GetSMTPForward() != nil) || (pTmp->GetSMTPForward() != nil) )
									{
										if ( CUtils::Strcmp( (*outMailUser)->GetSMTPForward(), pTmp->GetSMTPForward() ) != 0 )
										{
											status = kDSErrDuplicateUserName;
										}
									}
								}
							}

							if ( pTmp != nil )
							{
								// This one is not a player
								delete( pTmp );
								pTmp = nil;
							}

							// Cleanup the locals
							if ( pAcctName != nil )
							{
								::free( pAcctName );
								pAcctName = nil;
							}
						}

							if ( pMailUserAttr != nil )
							{
								::free( pMailUserAttr );
								pMailUserAttr = nil;
							}

							if ( pGroupID != nil )
							{
								::free( pGroupID );
								pGroupID = nil;
							}

						if ( pRealName != nil )
						{
							::free( pRealName );
							pRealName = nil;
						}

						if ( pUserLocation != nil )
						{
							::free( pUserLocation );
							pUserLocation = nil;
						}

						if ( pEmailAddress != nil )
						{
							::free( pEmailAddress );
							pEmailAddress = nil;
						}
					}

					if ( pRecEntry != nil )
					{
						(void)::dsCloseAttributeList( attrListRef );
						(void)::dsDeallocRecordEntry( fSearchNodeRef, pRecEntry );
						pRecEntry = nil;
					}
				}
			}
		} while ( (context != NULL) && (status == eDSNoErr) );
	}

	catch( long err )
	{
		status = err;
	}

	catch( ... )
	{
		status = kUnknownError;
	}

	if ( pAcctName != nil )
	{
		::free( pAcctName );
		pAcctName = nil;
	}

	if ( pMailUserAttr != nil )
	{
		::free( pMailUserAttr );
		pMailUserAttr = nil;
	}

	if ( pGroupID != nil )
	{
		::free( pGroupID );
		pGroupID = nil;
	}

	if ( pEmailAddress != nil )
	{
		::free( pEmailAddress );
		pEmailAddress = nil;
	}

	if ( pRealName != nil )
	{
		::free( pRealName );
		pRealName = nil;
	}

	if ( pUserLocation != nil )
	{
		::free( pUserLocation );
		pUserLocation = nil;
	}

	if ( context != nil )
	{
		(void)::dsReleaseContinueData( fDSRef, context );
		context = nil;
	}

	if ( pUserID != nil )
	{
		::dsDataNodeDeAllocate( fDSRef, pUserID );
		pUserID = nil;
	}

	if ( (status == kNoErr) && (*outMailUser == nil) )
	{
		status = kDSErrUserNotFound;
	}
	else if ( *outMailUser != nil )
	{
		fMailCache.AddUserToCache( *outMailUser, NULL );
	}

	return( status );

} // GetUserByID


// --------------------------------------------------------------------------------
//	* GetGroupByName ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::GetGroupByName ( const char* inName, DSMailGroup **outGroup, Bool inUseCache )
{
	sInt32					status			= kDSErrNoErr;
	uInt32					i				= 0;
	uInt32					j				= 0;
	uInt32					k				= 0;
	uInt32					recCount		= 0;
	uInt32					groupID			= 0;
	char				   *pGroupID		= nil;
	uInt32					memberCnt		= 0;
	uInt32					dataLen			= 0;
	char				   *pAcctName		= nil;
	GroupMemberList		   *pMemberArray	= nil;
	tDataList				recName;
	tRecordEntry		   *pRecEntry		= nil;
	tAttributeEntry		   *pAttrEntry		= nil;
	tAttributeValueEntry   *pValueEntry		= nil;
	tContextData			context			= nil;
	tAttributeListRef		attrListRef		= 0;
	tAttributeValueListRef	valueRef		= 0;
	DSMailGroup			   *pMailGroup		= nil;
	ExceptionCode			exMutex			= kNoErr;

	::memset( &recName,  0, sizeof( tDataList ) );

	try
	{
		if ( inUseCache == true )
		{
			++sLookupCount;

			// First check the cache...
			*outGroup = fMailCache.GetGroupByName( inName );
			if ( *outGroup != NULL )
			{
				++sCacheHitCount;
							return( kDSErrNoErr );
			}
		}

		// Make sure that this reference is still valid
		status = ::dsVerifyDirRefNum( fDSRef );
		if ( status != kDSErrNoErr )
		{
			status = this->Initialize();
			ThrowIfOSErr_( status );
		}

		status = ::dsBuildListFromStringsAlloc( fDSRef, &recName, inName, nil );
		ThrowIfOSErr_( status );

		do
		{
			status = ::dsGetRecordList( fSearchNodeRef, fTDataBuff, &recName, eDSiExact, fGroupRecType,
										fGroupAttrType, false, &recCount, &context );
			if ( status == eDSNoErr )
			{
				status = ::dsGetRecordEntry( fSearchNodeRef, fTDataBuff, 1, &attrListRef, &pRecEntry );
				if ( status == eDSNoErr )
				{
					// Get the account name for this group
					(void)::dsGetRecordNameFromEntry( pRecEntry, &pAcctName );

					for ( i = 1; i <= pRecEntry->fRecordAttributeCount; i++ )
					{
						status = ::dsGetAttributeEntry( fSearchNodeRef, fTDataBuff, attrListRef, i, &valueRef, &pAttrEntry );
						if ( (status == eDSNoErr) && (pAttrEntry != nil) )
						{
							status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
							if ( (status == eDSNoErr) && (pValueEntry != nil) )
							{
								if ( (CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrPrimaryGroupID ) == 0) && (pGroupID == nil) )
								{
									pGroupID = (char *)::calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof( char ) );
									::memcpy( pGroupID, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );

									groupID = ::atoi( pGroupID );
								}
								else if ( CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrGroupMembership ) == 0 )
								{
									memberCnt = pAttrEntry->fAttributeValueCount;
									pMemberArray = new GroupMemberList[ memberCnt ];
									if ( pMemberArray != nil )
									{
										::memset( pMemberArray, 0, sizeof( GroupMemberList ) * memberCnt );

										// Set the max string length
										dataLen = pValueEntry->fAttributeValueData.fBufferLength;
										if ( dataLen > 63 )
										{
											dataLen = 63;
										}

										::memcpy( pMemberArray[ 0 ], pValueEntry->fAttributeValueData.fBufferData, dataLen );
										(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
										pValueEntry = nil;

										for ( k = 2; k <= pAttrEntry->fAttributeValueCount; k ++ )
										{
											status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, k, valueRef, &pValueEntry );
											if ( status == eDSNoErr )
											{
												dataLen = pValueEntry->fAttributeValueData.fBufferLength;
												if ( dataLen > 63 )
												{
													dataLen = 63;
												}
												::memcpy( pMemberArray[ k - 1 ], pValueEntry->fAttributeValueData.fBufferData, dataLen );

												(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
												pValueEntry = nil;
											}
										}
									}
								}
							}

							if ( pValueEntry != nil )
							{
								(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
								pValueEntry = nil;
							}
						}
						if ( pAttrEntry != nil )
						{
							(void)::dsCloseAttributeValueList( valueRef );
							(void)::dsDeallocAttributeEntry( fSearchNodeRef, pAttrEntry );
							pAttrEntry = nil;
						}
					}

					if ( pGroupID != nil )
					{
						if ( pMailGroup == nil )
						{
							pMailGroup = new DSMailGroup();
							if ( pMailGroup != nil )
							{
								pMailGroup->SetGroupID( groupID );
								pMailGroup->SetGroupName( pAcctName );
								pMailGroup->SetMemberCount( memberCnt );
								pMailGroup->SetGroupMembers( pMemberArray );
								pMemberArray = nil;

								*outGroup = pMailGroup;
							}
						}
					}

					if ( pAcctName != nil )
					{
						free( pAcctName );
						pAcctName = nil;
					}

					if ( pGroupID != nil )
					{
						free( pGroupID );
						pGroupID = nil;
					}
				}

				if ( pRecEntry != nil )
				{
					(void)::dsCloseAttributeList( attrListRef );
					(void)::dsDeallocRecordEntry( fSearchNodeRef, pRecEntry );
					pRecEntry = nil;
				}
			}
		} while ( (context != NULL) && (status == eDSNoErr) );
	}

	catch( long err )
	{
		status = err;
	}

	catch( ... )
	{
		status = kUnknownError;
	}

	if ( context != nil )
	{
		(void)::dsReleaseContinueData( fDSRef, context );
		context = nil;
	}

	if ( pAcctName != nil )
	{
		::free( pAcctName );
		pAcctName = nil;
	}

	if ( pGroupID != nil )
	{
		::free( pGroupID );
		pGroupID = nil;
	}

	if ( pMemberArray != nil )
	{
		delete[] pMemberArray;
		pMemberArray = nil;
	}

	::dsDataListDeAllocate( fDSRef, &recName, true );

	if ( (status == kNoErr) && (*outGroup == nil) )
	{
		status = kDSErrGroupNotFound;
	}
	else if ( *outGroup != nil )
	{
		fMailCache.AddGroupToCache( *outGroup );
	}

	return( status );

} // GetGroupByName


// --------------------------------------------------------------------------------
//	* GetGroupByID ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::GetGroupByID ( long inGroupID, DSMailGroup **outGroup, Bool inUseCache )
{
	sInt32						status			= kDSErrNoErr;
	uInt32						i				= 0;
	uInt32						j				= 0;
	uInt32						k				= 0;
	uInt32						recCount		= 0;
	sInt32						usrErr			= kNoErr;
	uInt32						memberCnt		= 0;
	uInt32						dataLen			= 0;
	char					   *pAcctName		= nil;
	GroupMemberList			   *pMemberArray	= nil;
	char						idStr[ 64 ];
	DSMailGroup				   *pMailGroup		= nil;
	tDataNode				   *pGroupID		= nil;
	tRecordEntry			   *pRecEntry		= nil;
	tAttributeEntry			   *pAttrEntry		= nil;
	tAttributeValueEntry	   *pValueEntry		= nil;
	tContextData				context			= nil;
	tAttributeListRef			attrListRef		= 0;
	tAttributeValueListRef		valueRef		= 0;

	try
	{
		if ( inUseCache == true )
		{
			++sLookupCount;

			// First check the cache...
			*outGroup = fMailCache.GetGroupByID( inGroupID );
			if ( *outGroup != NULL )
			{
				++sCacheHitCount;
							return( kDSErrNoErr );
			}
		}

		::sprintf( idStr, "%lu", inGroupID );
		pGroupID = ::dsDataNodeAllocateString( fDSRef, idStr );
		ThrowIfNULL_( pGroupID );

		// Make sure that this reference is still valid
		status = ::dsVerifyDirRefNum( fDSRef );
		if ( status != kDSErrNoErr )
		{
			status = this->Initialize();
			ThrowIfOSErr_( status );
		}

		do
		{
			status = ::dsDoAttributeValueSearch( fSearchNodeRef, fTDataBuff, fGroupRecType, fGIDAttrType, eDSExact, pGroupID, &recCount, &context );
			if ( (status == eDSNoErr) && (recCount != 0) )
			{
				for ( i = 1; ((i <= recCount) && (status == eDSNoErr)); i++ )
				{
					status = ::dsGetRecordEntry( fSearchNodeRef, fTDataBuff, i, &attrListRef, &pRecEntry );
					if ( status == eDSNoErr )
					{
						// Get the account name
						(void)::dsGetRecordNameFromEntry( pRecEntry, &pAcctName );

						for ( j = 1; j <= pRecEntry->fRecordAttributeCount; j++ )
 						{
							status = ::dsGetAttributeEntry( fSearchNodeRef, fTDataBuff, attrListRef, j, &valueRef, &pAttrEntry );
							if ( status == eDSNoErr )
							{
								status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
								if ( (status == eDSNoErr) && (pValueEntry != nil) )
								{
									if ( CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrGroupMembership ) == 0 )
									{
										memberCnt = pAttrEntry->fAttributeValueCount;
										pMemberArray = new GroupMemberList[ memberCnt ];
										if ( pMemberArray != nil )
										{
											::memset( pMemberArray, 0, sizeof( GroupMemberList ) * memberCnt );

											// Set the max string length
											dataLen = pValueEntry->fAttributeValueData.fBufferLength;
											if ( dataLen > 63 )
											{
												dataLen = 63;
											}

											::memcpy( pMemberArray[ 0 ], pValueEntry->fAttributeValueData.fBufferData, dataLen );
											(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
											pValueEntry = nil;

											for ( k = 2; k <= pAttrEntry->fAttributeValueCount; k ++ )
											{
												status = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, k, valueRef, &pValueEntry );
												if ( status == eDSNoErr )
												{
													// Set the max string length
													dataLen = pValueEntry->fAttributeValueData.fBufferLength;
													if ( dataLen > 63 )
													{
														dataLen = 63;
													}

													::memcpy( pMemberArray[ k - 1 ], pValueEntry->fAttributeValueData.fBufferData, dataLen );

													(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
													pValueEntry = nil;
												}
											}
										}
									}

									if ( pValueEntry != nil )
									{
										(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
										pValueEntry = nil;
									}
								}
							}
							if ( pAttrEntry != nil )
							{
								(void)::dsCloseAttributeValueList( valueRef );
								(void)::dsDeallocAttributeEntry( fSearchNodeRef, pAttrEntry );
								pAttrEntry = nil;
							}
						}

						// Do we have the attributes we are looking for
						if ( pAcctName != nil )
						{
							if ( pMailGroup == nil )
							{
								pMailGroup = new DSMailGroup();
								if ( pMailGroup != nil )
								{
									pMailGroup->SetGroupID( inGroupID );
									pMailGroup->SetGroupName( pAcctName );
									pMailGroup->SetMemberCount( memberCnt );
									pMailGroup->SetGroupMembers( pMemberArray );
									pMemberArray = nil;

									*outGroup = pMailGroup;
								}
							}
						}

						if ( pAcctName != nil )
						{
							free( pAcctName );
							pAcctName = nil;
						}
					}

					if ( pRecEntry != nil )
					{
						(void)::dsCloseAttributeList( attrListRef );
						(void)::dsDeallocRecordEntry( fSearchNodeRef, pRecEntry );
						pRecEntry = nil;
					}
				}
			}
		} while ( (context != NULL) && (status == eDSNoErr) );
	}

	catch( long err )
	{
		status = err;
	}

	catch( ... )
	{
		status = kUnknownError;
	}

	if ( pAcctName != nil )
	{
		::free( pAcctName );
		pAcctName = nil;
	}

	if ( context != nil )
	{
		(void)::dsReleaseContinueData( fDSRef, context );
		context = nil;
	}

	if ( pGroupID != nil )
	{
		::dsDataNodeDeAllocate( fDSRef, pGroupID );
		pGroupID = nil;
	}

	if ( (status == kNoErr) && (*outGroup == nil) )
	{
		status = kDSErrGroupNotFound;
	}
	else if ( *outGroup != nil )
	{
		fMailCache.AddGroupToCache( *outGroup );
	}

	return( status );

} // GetGroupByID


// --------------------------------------------------------------------------------
//	* ValidatePassword ()
//
// --------------------------------------------------------------------------------

sInt32	DSMgr::ValidatePassword ( const char *inPasswd, DSMailUser *inMailUser )
{
	Bool					bIsAdmin		= false;
	sInt32					status			= kDSErrNoErr;
	sInt32					siResult		= -1;
	uInt32					curr			= 0;
	uInt32					len				= 0;
	uInt32					nameLen			= 0;
	uInt32					passwdLen		= 0;
	uInt32					authBuffSzie	= 0;
	tDirStatus				dsStatus		= eDSNoErr;
	tDataList			   *pUserNode		= nil;
	tDirNodeReference		userNodeRef		= 0;
	tDataBuffer			   *pAuthBuff		= nil;
	tDataBuffer			   *pStepBuff		= nil;
	tDataNode			   *pAuthType		= nil;

	// Simple null check
	if ( (inMailUser == nil) || (inPasswd == nil) )
	{
		return( kDSErrSyntaxError );
	}

	// Is the name length too long
	nameLen = CUtils::Strlen( inMailUser->GetUserName() );
	if ( nameLen > 256 )
	{
		return( kDSErrInvalidUserName );
	}

	// Is the password length too long
	passwdLen = CUtils::Strlen( inPasswd );
	if ( passwdLen > 256 )
	{
		return( kDSErrInvalidPassword );
	}

	// Size of the auth buff
	authBuffSzie = nameLen + passwdLen + 32;

	try
	{
		status = ::dsVerifyDirRefNum( fDSRef );
		if ( status != kDSErrNoErr )
		{
			status = this->Initialize();
			ThrowIfOSErr_( status );
		}

		pUserNode = ::dsBuildFromPath( fDSRef, inMailUser->GetUserLocation(), "/" );
		if ( pUserNode != nil )
		{
			dsStatus = ::dsOpenDirNode( fDSRef, pUserNode, &userNodeRef );
			if ( dsStatus == eDSNoErr )
			{
				pAuthBuff = ::dsDataBufferAllocate( fDSRef, authBuffSzie );
				if ( pAuthBuff != nil )
				{
					// We don't use this buffer for clear text auth
					pStepBuff = ::dsDataBufferAllocate( fDSRef, 128 );
					if ( pStepBuff != nil )
					{
						pAuthType = ::dsDataNodeAllocateString( fDSRef, kDSStdAuthNodeNativeClearTextOK );
						if ( pAuthType != nil )
						{
							// User Name
							len = nameLen;
							::memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
							curr += sizeof( unsigned long );
							::memcpy( &(pAuthBuff->fBufferData[ curr ]), inMailUser->GetUserName(), len );
							curr += len;

							// Password
							len = passwdLen;
							::memcpy( &(pAuthBuff->fBufferData[ curr ]), &len, sizeof( unsigned long ) );
							curr += sizeof( unsigned long );
							::memcpy( &(pAuthBuff->fBufferData[ curr ]), inPasswd, len );
							curr += len;

							pAuthBuff->fBufferLength = curr;

							dsStatus = ::dsDoDirNodeAuth( userNodeRef, pAuthType, true, pAuthBuff, pStepBuff, nil );
							if ( dsStatus == eDSNoErr )
							{
								siResult = kNoErr;
							}
							else
							{
								siResult = -7;
							}

							(void)::dsDataNodeDeAllocate( fDSRef, pAuthType );
							pAuthType = nil;
						}
						else
						{
							siResult = -6;
						}
						(void)::dsDataNodeDeAllocate( fDSRef, pStepBuff );
						pStepBuff = nil;
					}
					else
					{
						siResult = -5;
					}
					(void)::dsDataNodeDeAllocate( fDSRef, pAuthBuff );
					pAuthBuff = nil;
				}
				else
				{
					siResult = -4;
				}
				(void)::dsCloseDirNode( userNodeRef );
				userNodeRef = 0;
			}
			else
			{
				siResult = -3;
			}
			(void)::dsDataListDeAllocate( fDSRef, pUserNode, true );
			::free( pUserNode );
			pUserNode = nil;
		}
		else
		{
			siResult = -2;
		}
	}

	catch( long err )
	{
		siResult = err;
	}

	catch( ... )
	{
		siResult = kUnknownError;
	}

	return( siResult );

} // ValidatePassword


// --------------------------------------------------------------------------------
//	* ValidateResponse ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::ValidateResponse ( const char *inUserName,
								 const char *inChallenge,
								 const char *inResponse,
								 eAuthType	 inAuthType )
{
	Bool					bIsAdmin		= false;
	DSMailUser			   *pMailUser		= nil;
	sInt32					status			= kDSErrNoErr;
	sInt32					siResult		= -1;
	uInt32					uiCurr			= 0;
	uInt32					uiLen			= 0;

	uInt32					uiNameLen		= 0;
	uInt32					uiChalLen		= 0;
	uInt32					uiRespLen		= 0;

	uInt32					authBuffSzie	= 0;
	tDirStatus				dsStatus		= eDSNoErr;
	tDataList			   *pUserNode		= nil;
	tDirNodeReference		userNodeRef		= 0;
	tDataBuffer			   *pAuthBuff		= nil;
	tDataBuffer			   *pStepBuff		= nil;
	tDataNode			   *pAuthType		= nil;

	// Simple null check
	if ( (inUserName == nil) || (inChallenge == nil) || (inResponse == nil) )
	{
		return( kDSErrSyntaxError );
	}

	uiNameLen = CUtils::Strlen( inUserName );
	uiChalLen = CUtils::Strlen( inChallenge );
	uiRespLen = CUtils::Strlen( inResponse );

	// Size of the auth buff
	authBuffSzie = uiNameLen + uiChalLen + uiRespLen + 32;

	try
	{
		GetUserByName( inUserName, &pMailUser, false );
		if ( pMailUser != nil )
		{
			status = ::dsVerifyDirRefNum( fDSRef );
			if ( status != kDSErrNoErr )
			{
				status = this->Initialize();
				ThrowIfOSErr_( status );
			}

			pUserNode = ::dsBuildFromPath( fDSRef, pMailUser->GetUserLocation(), "/" );
			if ( pUserNode != nil )
			{
				dsStatus = ::dsOpenDirNode( fDSRef, pUserNode, &userNodeRef );
				if ( dsStatus == eDSNoErr )
				{
					pAuthBuff = ::dsDataBufferAllocate( fDSRef, authBuffSzie );
					if ( pAuthBuff != nil )
					{
						// We don't use this buffer for clear text auth
						pStepBuff = ::dsDataBufferAllocate( fDSRef, 128 );
						if ( pStepBuff != nil )
						{
							if ( inAuthType == kDSAuthAPOP )
							{
								pAuthType = ::dsDataNodeAllocateString( fDSRef, kDSStdAuthAPOP );
							}
							else if ( inAuthType == kDSAuthCRAM_MD5 )
							{
								pAuthType = ::dsDataNodeAllocateString( fDSRef, kDSStdAuthCRAM_MD5 );
							}
							if ( pAuthType != nil )
							{
								// User name
								uiLen = uiNameLen;
								::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
								uiCurr += sizeof( unsigned long );
								::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inUserName, uiLen );
								uiCurr += uiLen;

								// Challenge
								uiLen = uiChalLen;
								::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
								uiCurr += sizeof( unsigned long );
								::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inChallenge, uiLen );
								uiCurr += uiLen;

								// Response
								uiLen = uiRespLen;
								::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), &uiLen, sizeof( unsigned long ) );
								uiCurr += sizeof( unsigned long );
								::memcpy( &(pAuthBuff->fBufferData[ uiCurr ]), inResponse, uiLen );
								uiCurr += uiLen;

								pAuthBuff->fBufferLength = uiCurr;

								dsStatus = ::dsDoDirNodeAuth( userNodeRef, pAuthType, true, pAuthBuff, pStepBuff, nil );
								if ( dsStatus == eDSNoErr )
								{
									siResult = kNoErr;
								}
								else
								{
									siResult = -7;
								}

								(void)::dsDataNodeDeAllocate( fDSRef, pAuthType );
								pAuthType = nil;
							}
							else
							{
								siResult = -6;
							}
							(void)::dsDataNodeDeAllocate( fDSRef, pStepBuff );
							pStepBuff = nil;
						}
						else
						{
							siResult = -5;
						}
						(void)::dsDataNodeDeAllocate( fDSRef, pAuthBuff );
						pAuthBuff = nil;
					}
					else
					{
						siResult = -4;
					}
					(void)::dsCloseDirNode( userNodeRef );
					userNodeRef = 0;
				}
				else
				{
					siResult = -3;
				}
				(void)::dsDataListDeAllocate( fDSRef, pUserNode, true );
				::free( pUserNode );
				pUserNode = nil;
			}
			else
			{
				siResult = -2;
			}

			delete( pMailUser );
			pMailUser = nil;
		}
		else
		{
			siResult = -88;
		}
	}

	catch ( long err )
	{
		siResult = err;
	}

	catch ( ... )
	{
		siResult = kUnknownError;
	}

	return( siResult );

} // ValidateResponse



// --------------------------------------------------------------------------------
//	* VerifyUserOrGroup  ()
//
// --------------------------------------------------------------------------------

sInt32 DSMgr::VerifyUserOrGroup ( UserStruct *inData )
{
	sInt32			siResult		= kNoErr;
	DSMailUser	   *pMailUser		= nil;
	DSMailGroup	   *pMailGroup		= nil;
	char			cName[ 256 ];

	if ( inData == nil )
	{
		return( kNullPtr );
	}

	::memcpy( cName, &inData->fUserName[ 1 ], inData->fUserName[ 0 ] );
	cName[ inData->fUserName[ 0 ] ] = '\0';

	// Is it a user
	gDSMgr->GetUserByName( cName, &pMailUser );
	if ( pMailUser != nil )
	{
		inData->fID		= pMailUser->GetUserID();
		inData->fType	= kUserItem;
// - MED - may need to set what kind of user in fType (admin, guest...)
// - make a pascal string
		inData->fRealName[ 0 ] = CUtils::Strlen( pMailUser->GetRealName() );
		CUtils::Strcpy( &inData->fRealName[ 1 ], pMailUser->GetRealName() );

		inData->fPath[ 0 ] = '\0';

		delete( pMailUser );
		pMailUser = nil;
	}
	else
	{
		gDSMgr->GetGroupByName( cName, &pMailGroup );
		if ( pMailGroup != nil )
		{
			inData->fID		= pMailGroup->GetGroupID();
			inData->fType	= kGrouptItem;

			delete( pMailGroup );
			pMailGroup = nil;
		}
		else
		{
			siResult = kNoUserOrGroup;
		}
	}

	return( siResult );

} // VerifyUserOrGroup


// --------------------------------------------------------------------------------
//	* UserIsMemberOfGroup ()
//
//		- check to see if given group contains given user
// --------------------------------------------------------------------------------

Boolean DSMgr::UserIsMemberOfGroup ( const char *inUserName, const char *inGroupName )
{
	uInt32					i				= 0;
	char				   *recName			= nil;
	Boolean					isInGroup 		= false;
	tDirStatus				dsStatus		= eDSNoErr;
	uInt32					recCount		= 0;
	tDataListPtr			recTypeList		= nil;
	tDataNodePtr			attrType		= nil;
	tDataNodePtr			userName		= nil;
	tAttributeListRef		attrListRef		= 0;
	tRecordEntry		   *pRecEntry		= NULL;

	try
	{
		// Make sure that this reference is still valid
		dsStatus = ::dsVerifyDirRefNum( fDSRef );
		if ( dsStatus != eDSNoErr )
		{
			dsStatus = (tDirStatus)this->Initialize();
			ThrowIfOSErr_( dsStatus );
		}

		recTypeList	= ::dsBuildListFromStrings( fDSRef, kDSStdRecordTypeGroups, nil );
		ThrowIfNULL_( recTypeList );

		attrType = ::dsDataNodeAllocateString( fDSRef, "dsAttrTypeNative:users" );
		ThrowIfNULL_( attrType );

		userName = ::dsDataNodeAllocateString( fDSRef, inUserName );
		ThrowIfNULL_( userName );

		dsStatus = ::dsDoAttributeValueSearch (	fSearchNodeRef,
												fTDataBuff,
												recTypeList,
												attrType,
												eDSExact,
												userName,
												&recCount,
												nil );

		if ( (dsStatus == eDSNoErr) && (recCount != 0) )
		{
			for ( i = 1; i <= recCount; i++ )
			{
				// Get the record entry form the buffer.
				dsStatus = ::dsGetRecordEntry( fSearchNodeRef, fTDataBuff, i, &attrListRef, &pRecEntry );
				if ( dsStatus == eDSNoErr )
				{
					dsStatus = ::dsGetRecordNameFromEntry( pRecEntry, &recName );
					if ( dsStatus == eDSNoErr )
					{
						if ( CUtils::Strcmp( recName, inGroupName ) == 0 )
						{
							isInGroup = true;
						}
						free( recName );
						recName = nil;
					}
				}
				if ( pRecEntry != nil )
				{
					(void)::dsCloseAttributeList( attrListRef );
					(void)::dsDeallocRecordEntry( fSearchNodeRef, pRecEntry );
					pRecEntry = nil;
				}
			}
		}
	}

	catch( long err )
	{
		isInGroup = false;
	}

	catch( ... )
	{
		isInGroup = false;
	}

	if ( recTypeList != nil )
	{
		(void )::dsDataListDeallocate( fDSRef, recTypeList );
		free( recTypeList );
		recTypeList = nil;
	}
	if ( attrType != nil )
	{
		(void )::dsDataNodeDeAllocate( fDSRef, attrType );
		attrType = nil;
	}
	if ( userName != nil )
	{
		(void )::dsDataNodeDeAllocate( fDSRef, userName );
		userName = nil;
	}

	return( isInGroup );

} // UserIsMemberOfGroup


// --------------------------------------------------------------------------------
//	* GetGroups ()
//
//		- Get a list of group ID's that this user is a member of
// --------------------------------------------------------------------------------

sInt32 DSMgr::GetGroups ( const char *inUserName, uInt32 *outGroups )
{
	uInt32					i				= 0;
	uInt32					j				= 0;
	char				   *recName			= nil;
	uInt32					cntr			= 0;
	char					groupID[ 64 ];
	uInt32					len				= 0;
	tDirStatus				dsStatus		= eDSNoErr;
	uInt32					recCount		= 0;
	tDataListPtr			recTypeList		= nil;
	tDataNodePtr			attrType		= nil;
	tDataNodePtr			userName		= nil;
	tAttributeListRef		attrListRef		= 0;
	tRecordEntry		   *pRecEntry		= nil;
	tAttributeEntry		   *pAttrEntry		= nil;
	tAttributeValueEntry   *pValueEntry		= nil;
	tAttributeValueListRef	valueRef		= 0;
	tContextData			context			= nil;

	try
	{
		// Make sure that this reference is still valid
		dsStatus = ::dsVerifyDirRefNum( fDSRef );
		if ( dsStatus != eDSNoErr )
		{
			dsStatus = (tDirStatus)this->Initialize();
			ThrowIfOSErr_( dsStatus );
		}

		recTypeList	= ::dsBuildListFromStrings( fDSRef, kDSStdRecordTypeGroups, nil );
		ThrowIfNULL_( recTypeList );

		attrType = ::dsDataNodeAllocateString( fDSRef, "dsAttrTypeNative:users" );
		ThrowIfNULL_( attrType );

		userName = ::dsDataNodeAllocateString( fDSRef, inUserName );
		ThrowIfNULL_( userName );

		do
		{
			dsStatus = ::dsDoAttributeValueSearch(	fSearchNodeRef, fTDataBuff, recTypeList, attrType,
													eDSExact, userName, &recCount, &context );
			if ( (dsStatus == eDSNoErr) && (recCount != 0) )
			{
				for ( i = 1; i <= recCount; i++ )
				{
					dsStatus = ::dsGetRecordEntry( fSearchNodeRef, fTDataBuff, i, &attrListRef, &pRecEntry );
					if ( dsStatus == eDSNoErr )
					{
						for ( j = 1; j <= pRecEntry->fRecordAttributeCount; j++ )
						{
							dsStatus = ::dsGetAttributeEntry( fSearchNodeRef, fTDataBuff, attrListRef, j, &valueRef, &pAttrEntry );
							if ( (dsStatus == eDSNoErr) && (pAttrEntry != nil) )
							{
								dsStatus = ::dsGetAttributeValue( fSearchNodeRef, fTDataBuff, 1, valueRef, &pValueEntry );
								if ( (dsStatus == eDSNoErr) && (pValueEntry != nil) )
								{
									if ( CUtils::Strcmp( pAttrEntry->fAttributeSignature.fBufferData, kDS1AttrPrimaryGroupID ) == 0 )
									{
										len = pValueEntry->fAttributeValueData.fBufferLength;
										if ( len > 0 )
										{
											if ( len > 63 )
											{
												len = 63;
											}

											::memcpy( groupID, pValueEntry->fAttributeValueData.fBufferData, len );
											groupID[ len ] = '\0';

											outGroups[ cntr++ ] = ::atoi( groupID );
											if ( cntr >= kMaxGroupMembership )
											{
												i = recCount + 1;
											}
										}
									}
								}
							}
							if ( pValueEntry != nil )
							{
								(void)::dsDeallocAttributeValueEntry( fSearchNodeRef, pValueEntry );
								pValueEntry = nil;
							}
							if ( pAttrEntry != nil )
							{
								(void)::dsCloseAttributeValueList( valueRef );
								(void)::dsDeallocAttributeEntry( fSearchNodeRef, pAttrEntry );
								pAttrEntry = nil;
							}
						}
					}
					if ( pRecEntry != nil )
					{
						(void)::dsCloseAttributeList( attrListRef );
						(void)::dsDeallocRecordEntry( fSearchNodeRef, pRecEntry );
						pRecEntry = nil;
					}
				}
			}
		} while ( (context != NULL) && (dsStatus == eDSNoErr) );
	}

	catch( long err )
	{
	}

	catch( ... )
	{
	}

	if ( recTypeList != nil )
	{
		(void )::dsDataListDeallocate( fDSRef, recTypeList );
		free( recTypeList );
		recTypeList = nil;
	}

	if ( context != nil )
	{
		(void)::dsReleaseContinueData( fDSRef, context );
		context = nil;
	}

	if ( attrType != nil )
	{
		(void )::dsDataNodeDeAllocate( fDSRef, attrType );
		attrType = nil;
	}

	if ( userName != nil )
	{
		(void )::dsDataNodeDeAllocate( fDSRef, userName );
		userName = nil;
	}

	return( recCount );

} // UserIsMemberOfGroup


//--------------------------------------------------------------------------------------------------
//	* GetMailGroupID ()
//
//--------------------------------------------------------------------------------------------------

uInt32 DSMgr::GetMailGroupID ( Bool inCheckDirectory )
{
	DSMailGroup	   *pMailGroup	= nil;

	if ( (fMailGroupID == 0) || (inCheckDirectory == true) )
	{
		gDSMgr->GetGroupByName( "mail", &pMailGroup, true );
		if ( pMailGroup != NULL )
		{
			fMailGroupID = pMailGroup->GetGroupID();
			delete( pMailGroup );
		}
	}

	return( fMailGroupID );

} // GetMailGroupID


//--------------------------------------------------------------------------------------------------
//	* InitServerLogPrefs ()
//
//--------------------------------------------------------------------------------------------------

tDirStatus DSMgr::InitServerLogPrefs ( void )
{
	tDirStatus			dsStatus	= eDSNoErr;

	try
	{
		if ( fSrvrPrefs == nil )
		{
			fSrvrPrefs = new DSServerPrefs();
			ThrowIfNULL_( fSrvrPrefs );
		}

		// This would be the place to get any log prefs
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}

	return( dsStatus );


} // InitServerLogPrefs


//--------------------------------------------------------------------------------------------------
//	* InitServerPrefs ()
//
//--------------------------------------------------------------------------------------------------

tDirStatus DSMgr::InitServerPrefs ( void )
{
	tDirStatus			dsStatus		= eDSNoErr;
	Bool				bTemp			= false;
	uInt32				uiTemp			= 0;
	StrListObj		   *pStringList		= nil;
	UserStruct			usrStructTmp;
	CString				csTmp( 128 );

	try
	{
		if ( fSrvrPrefs != nil )
		{
			dsStatus = fSrvrPrefs->GetAltDBLocationFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetAltDBLocationFlag( bTemp, true );
			}

			PrintThis( "Use alternate mail store location", bTemp ? "True" : "False" );

			fSrvrPrefs->GetAltDBLocationValue( csTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetAltDBLocationValue( csTmp.GetData(), true );
			}

			PrintThis( "  Alternate mail store location", csTmp.GetData() );

// Print local names

			// Max message size flag
			dsStatus = fSrvrPrefs->GetMaxMessageSizeFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetMaxMessageSizeFlag( bTemp, true );
			}

			PrintThis( "Use max message size", bTemp ? "True" : "False" );

			// Max message size value
			dsStatus = fSrvrPrefs->GetMaxMessageSize( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetMaxMessageSize( uiTemp, true );
			}

			PrintThis( "  Max message size", uiTemp );

			// BCC flag
			dsStatus = fSrvrPrefs->GetBCC_Flag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetBCC_Flag( bTemp, true );
			}

			PrintThis( "Blind carbon copies (BCC)", bTemp ? "True" : "False" );

			// BCC email address
			dsStatus = fSrvrPrefs->GetBCC_User( csTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetBCC_User( csTmp.GetData(), true );
			}

			// BCC user struct
			::memset( &usrStructTmp, 0, sizeof( UserStruct ) );
			dsStatus = fSrvrPrefs->GetBCC_UserStruct( &usrStructTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetBCC_UserStruct( &usrStructTmp, true );
			}

			PrintThis( "  Blind carbon copies (BCC) to", usrStructTmp.fUserName );

			// Auto delete flag
			dsStatus = fSrvrPrefs->GetAutoMailDeleteFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetAutoMailDeleteFlag( bTemp, true );
			}

			PrintThis( "Automatic Mail Deletion", bTemp ? "True" : "False" );

			// Auto delete unseen value
			dsStatus = fSrvrPrefs->GetDeleteUnseenDays( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetDeleteUnseenDays( uiTemp, true );
			}

			PrintThis( "  Delete unread mail after days", uiTemp );

			// Auto delete seen value
			dsStatus = fSrvrPrefs->GetDeleteSeenDays( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetDeleteSeenDays( uiTemp, true );
			}

			PrintThis( "  Delete read mail after days", uiTemp );

			// Forward unknown local users
			dsStatus = fSrvrPrefs->GetFWDUnknownLoalUserFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetFWDUnknownLoalUserFlag( bTemp, true );
			}

			PrintThis( "Forward mail to unknown local users", bTemp ? "True" : "False" );

			// Forward unknown local users email address
			dsStatus = fSrvrPrefs->GetFWDUnknownLoalUser( csTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetFWDUnknownLoalUser( csTmp.GetData(), true );
			}

			// Forward unknown local users user struct
			::memset( &usrStructTmp, 0, sizeof( UserStruct ) );
			dsStatus = fSrvrPrefs->GetFWDUnknownLoalUserUserStruct ( &usrStructTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetFWDUnknownLoalUserUserStruct ( &usrStructTmp, true );
			}

			PrintThis( "  Forward mail to unknown local users to", usrStructTmp.fUserName );


			// Use blackhole server
			dsStatus = fSrvrPrefs->GetUseBlackholeServerFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetUseBlackholeServerFlag( bTemp, true );
			}

			PrintThis( "Use server for junk mail rejection", bTemp ? "True" : "False" );


			// Default junk mail server
			dsStatus = fSrvrPrefs->GetBlackholeServerName( csTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetBlackholeServerName( csTmp.GetData(), true );
			}

			PrintThis( "  Junk mail server to use", csTmp.GetData() );

//			dsStatus = fSrvrPrefs->GetCustomSpamServerList( StrListObj **inStrList, Bool inReadFromDir = false );
//			fSrvrPrefs->SetCustomSpamServerList( StrListObj *inStrList );

			// Log if SMTP name != IP
			dsStatus = fSrvrPrefs->GetLogIfBadSMTPNameFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetLogIfBadSMTPNameFlag( bTemp, true );
			}

			PrintThis( "Log if SMTP name doesn't match", bTemp ? "True" : "False" );

			// Reject if name doesn't match
			dsStatus = fSrvrPrefs->GetRejectIfNameNoMatchFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetRejectIfNameNoMatchFlag( bTemp, true );
			}

			PrintThis( "  Reject SMTP connection if bad name", bTemp ? "True" : "False" );

			// Require local from
			dsStatus = fSrvrPrefs->GetRequireLocalFromFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetRequireLocalFromFlag( bTemp, true );
			}

			PrintThis( "Require local from to exist", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetSMTP_State( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetSMTP_State( uiTemp, true );
			}

			switch ( uiTemp )
			{
				case kSMTPStateAppleMail:
					PrintThis( "SMTP State", "Enabled" );
					break;

				case kSMTPStateUseOtherMTA:
					PrintThis( "SMTP State", "Other Mail Transfer Agent" );
					break;

				case kSMTPStateDisabled:
					PrintThis( "SMTP State", "Disabled" );
					break;

				default:
					PrintThis( "SMTP State", "Unknown" );
					break;
			}

			dsStatus = fSrvrPrefs->GetSMTPInPort( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetSMTPInPort( uiTemp, true );
			}

			PrintThis( "  SMTP in port", uiTemp );

			dsStatus = fSrvrPrefs->GetSMTPOutPort( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetSMTPOutPort( uiTemp, true );
			}

			PrintThis( "  SMTP out port", uiTemp );

			dsStatus = fSrvrPrefs->GetSMTP_EnabledFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetSMTP_EnabledFlag( bTemp, true );
			}

			dsStatus = fSrvrPrefs->GetIMAP_EnabledFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetIMAP_EnabledFlag( bTemp, true );
			}

			PrintThis( "IMAP State", bTemp ? "Enabled" : "Disabled" );

			dsStatus = fSrvrPrefs->GetIMAPPort( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetIMAPPort( uiTemp, true );
			}

			PrintThis( "  IMAP port", uiTemp );

			dsStatus = fSrvrPrefs->GetPOP3_EnabledFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetPOP3_EnabledFlag( bTemp, true );
			}

			PrintThis( "POP3 State", bTemp ? "Enabled" : "Disabled" );

			dsStatus = fSrvrPrefs->GetPOP3Port( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetPOP3Port( uiTemp, true );
			}

			PrintThis( "  POP3 port", uiTemp );

			dsStatus = fSrvrPrefs->GetNofifyEnabledFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetNofifyEnabledFlag( bTemp, true );
			}

			PrintThis( "Notify Mail State", bTemp ? "Enabled" : "Disabled" );

			dsStatus = fSrvrPrefs->GetNotifyMailPort( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetNotifyMailPort( uiTemp, true );
			}

			PrintThis( "  Notify Mail port", uiTemp );

			dsStatus = fSrvrPrefs->GetSMTPRelayWhenBackupFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetSMTPRelayWhenBackupFlag( bTemp, true );
			}

			PrintThis( "SMTP relay when host is a backup", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetSendUndelivNDRToPMFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetSendUndelivNDRToPMFlag( bTemp, true );
			}

			PrintThis( "Send NDR reports to Postmaster", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetAllowNDRsBulkMailFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetAllowNDRsBulkMailFlag( bTemp, true );
			}

			PrintThis( "Allow NDR reports for bulk mail", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetSMTP_CRAM_MD5_Value( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetSMTP_CRAM_MD5_Value( uiTemp, true );
			}

			PrintThis( "Require CRAM-MD5 for SMTP", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetOtherSMTPAuthFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetOtherSMTPAuthFlag( bTemp, true );
			}

			PrintThis( "  Allow PLAIN and LOGIN on SMTP", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetAllowIMAPAdminAccessFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetAllowIMAPAdminAccessFlag( bTemp, true );
			}

			PrintThis( "ASIA State", bTemp ? "Enabled" : "Disabled" );

			dsStatus = fSrvrPrefs->GetAdminPortNumber( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetAdminPortNumber( uiTemp, true );
			}

			PrintThis( "  ASIA port", uiTemp );

			dsStatus = fSrvrPrefs->GetCaseSensativeFldrNameFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetCaseSensativeFldrNameFlag( bTemp, true );
			}

			PrintThis( "Case-Sensative IMAP folder names", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetRequireCRAM_MD5_IMAP_Flag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetRequireCRAM_MD5_IMAP_Flag( bTemp, true );
			}

			PrintThis( "Require CRAM-MD5 for IMAP Auth", bTemp ? "True" : "False" );

			dsStatus = fSrvrPrefs->GetConnPerIMAPAcctPerIP( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetConnPerIMAPAcctPerIP( uiTemp, true );
			}

			PrintThis( "IMAP connections per IP address", uiTemp );

			dsStatus = fSrvrPrefs->GetIMAPConnIdleTimeout( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetIMAPConnIdleTimeout( uiTemp, true );
			}

			PrintThis( "IMAP connection idle time", uiTemp );

			dsStatus = fSrvrPrefs->GetRequireAPOP_Flag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetRequireAPOP_Flag( bTemp, true );
			}

			PrintThis( "Require APOP", uiTemp );


			dsStatus = fSrvrPrefs->GetKerberosState( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetKerberosState( uiTemp, true );
			}

			switch ( uiTemp )
			{
				case kKerberosStateOff:
					PrintThis( "Kerberos State", "Ignored" );
					break;

				case kKerberosStateRequired:
					PrintThis( "Kerberos State", "Required" );
					break;

				case kKerberosStateAllowed:
					PrintThis( "Kerberos State", "Allowed" );
					break;

				default:
					PrintThis( "Kerberos State", "Unknown" );
					break;
			}

			dsStatus = fSrvrPrefs->GetForwardRemoteMsgFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetForwardRemoteMsgFlag( bTemp, true );
			}

			dsStatus = fSrvrPrefs->GetForwardRemoteMsgName( csTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
			}

			dsStatus = fSrvrPrefs->GetLogMessageBitMap( kAServerLogType );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetLogMessageBitMap( kAServerLogType );
			}

			dsStatus = fSrvrPrefs->GetLogMessageBitMap( kAnErrorLogType );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetLogMessageBitMap( kAnErrorLogType );
			}

			dsStatus = fSrvrPrefs->GetDiskQuotaThreshold( &uiTemp, kLowPercentage, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetDiskQuotaThreshold( uiTemp, kLowPercentage, true );
			}

			dsStatus = fSrvrPrefs->GetDiskQuotaThreshold( &uiTemp, kMediumPercentage, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetDiskQuotaThreshold( uiTemp, kMediumPercentage, true );
			}

			dsStatus = fSrvrPrefs->GetDiskQuotaThreshold( &uiTemp, kHighPercentage, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetDiskQuotaThreshold( uiTemp, kHighPercentage, true );
			}

			dsStatus = fSrvrPrefs->GetDiskQuotaCheckInterval( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetDiskQuotaCheckInterval( uiTemp, true );
			}

			dsStatus = fSrvrPrefs->GetAllowTempSSLCertsFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fSrvrPrefs->SetAllowTempSSLCertsFlag( bTemp, true );
			}

			fSrvrPrefs->GetMsgExpirationHours( &uiTemp, true );
			fSrvrPrefs->GetMsgWarningHours( &uiTemp, true );
			fSrvrPrefs->GetServerDNSCachePopUp( &uiTemp, true );
			fSrvrPrefs->GetServerDNSCacheDeltaType( &uiTemp, true );
			fSrvrPrefs->GetServerDNSCacheDeltaValue( &uiTemp, true );
			fSrvrPrefs->GetServerResolveMXFlag( &bTemp, true );
			fSrvrPrefs->GetServerResolveARecord( &bTemp, true );
			fSrvrPrefs->GetMsgWarningFlag( &bTemp, true );
			fSrvrPrefs->GetWarn_PM_OfNonDelivery( &bTemp, true );
			fSrvrPrefs->GetAltInboxLocationValue( csTmp, true );
			fSrvrPrefs->GetOpenTimeout( &uiTemp, true );
			fSrvrPrefs->GetRdWrTimeout( &uiTemp, true );
		}
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}

	return( dsStatus );

} // InitServerPrefs


//--------------------------------------------------------------------------------------------------
//	* InitHostPrefs ()
//
//--------------------------------------------------------------------------------------------------

tDirStatus DSMgr::InitHostPrefs ( void )
{
	tDirStatus			dsStatus		= eDSNoErr;
	Bool				bTemp			= false;
	uInt32				uiTemp			= 0;
	uInt32				uiTemp1			= 0;
	StrListObj		   *pStringList		= nil;
	UserStruct			usrStructTmp;
	CString				csTmp( 128 );

	try
	{
		if ( fHostPrefs != nil )
		{
			// Log Rejections
			dsStatus = fHostPrefs->GetLogRejectionsFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetLogRejectionsFlag( bTemp, true );
			}

			PrintThis( "Log rejections", bTemp ? "True" : "False" );

			// Send BCC to user
			dsStatus = fHostPrefs->GetSendBCC_CopiesOfAllFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetSendBCC_CopiesOfAllFlag( bTemp, true );
			}

			PrintThis( "Send BCC to user", bTemp ? "True" : "False" );

			// Send BCC to user struct
			::memset( &usrStructTmp, 0, sizeof( UserStruct ) );
			dsStatus = fHostPrefs->GetSendBCC_CopiesOfAllUser( &usrStructTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetSendBCC_CopiesOfAllUser ( &usrStructTmp, true );
			}

			PrintThis( "  Send BCC to ", usrStructTmp.fUserName );

			// Out mail popup value
			dsStatus = fHostPrefs->GetOutMailPopupFlags( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetOutMailPopupFlags( uiTemp, true );
			}

			switch ( uiTemp )
			{
				case 1:
					PrintThis( "Outgoing Mail", "Allow outgoing mail" );
					break;

				case 2:
					PrintThis( "Outgoing Mail", "Limit to local users" );
					break;

				default:
					PrintThis( "Outgoing Mail unknown value", uiTemp );
					break;
			}


			// Notify sender of NDR flag
			dsStatus = fHostPrefs->GetNotifySenderOfNDRFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetNotifySenderOfNDRFlag( bTemp, true );
			}

			PrintThis( "Notify sender of NDR", bTemp ? "True" : "False" );

			// Notify sender of NDR value
			dsStatus = fHostPrefs->GetNotifySenderOfNDRHours( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetNotifySenderOfNDRHours( uiTemp, true );
			}

			PrintThis( "  Notify sender hours", uiTemp );

			// Notify postmaster of NDR flag
			dsStatus = fHostPrefs->GetNotifyPostmasterOfNDR( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetNotifyPostmasterOfNDR( bTemp, true );
			}

			PrintThis( "Notify postmaster of NDR", bTemp ? "True" : "False" );

			// Relay SMTP via flag
			dsStatus = fHostPrefs->GetRelaySMTPFlag( &bTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetRelaySMTPFlag( bTemp, true );
			}

			PrintThis( "Relay all SMTP via", bTemp ? "True" : "False" );

			// Relay SMTP via server
			dsStatus = fHostPrefs->GetRelaySMTPServer( csTmp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetRelaySMTPServer( csTmp.GetData(), true );
			}

			PrintThis( "  Relay all SMTP via", csTmp.GetData() );

			// Expire outbound mail after hours
			dsStatus = fHostPrefs->GetExpireOutMailAfterHours( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetExpireOutMailAfterHours( uiTemp, true );
			}

			PrintThis( "Expire undeliverable mail hours", uiTemp );

			// Retry failed connections every minutes
			dsStatus = fHostPrefs->GetRetryFailedMinutes( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetRetryFailedMinutes( uiTemp, true );
			}

			PrintThis( "Retry failed connections minutes", uiTemp );

			// DNS Request flags
			dsStatus = fHostPrefs->GetDNSRequestButtonFlags( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetDNSRequestButtonFlags( uiTemp, true );
			}

			switch ( uiTemp )
			{
				case kMXandARecord:
					PrintThis( "DNS Request", "MX-LIST & A-Record" );
					break;

				case kMXonly:
					PrintThis( "DNS Request", "MX-LIST Only" );
					break;

				case kARecordOnly:
					PrintThis( "DNS Request", "A-Record Only" );
					break;

				case kNoMXValue:
				default:
					PrintThis( "DNS Request unknown value", uiTemp );
					break;
			}

			// Cache settings
			dsStatus = fHostPrefs->GetCacheSettingsButtonFlags( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetCacheSettingsButtonFlags( uiTemp, true );
			}

			// Cache DNS minutes
			dsStatus = fHostPrefs->GetCacheDNSMinutes( &uiTemp1, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetCacheDNSMinutes( uiTemp1, true );
			}

			switch ( uiTemp )
			{
				case kRespectTTL:
					PrintThis( "Cache Settings", "Respect TTL" );
					break;

				case kCacheNDS:
					PrintThis( "Cache Settings", "Cache DNS" );
					PrintThis( "  Cache DNS minutes", uiTemp1 );
					break;

				case kNoCacheValue:
				default:
					PrintThis( "Cache Settings unknown value", uiTemp );
					break;

			}

			// Open connection timeout
			dsStatus = fHostPrefs->GetOpenConnTimeoutSeconds( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetOpenConnTimeoutSeconds( uiTemp, true );
			}

			PrintThis( "Open connection timeout seconds", uiTemp );

			// Read/Write timeout
			dsStatus = fHostPrefs->GetReadWriteTimeoutSeconds( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetReadWriteTimeoutSeconds( uiTemp, true );
			}

			PrintThis( "Read/Write timeout seconds", uiTemp );

			// SMTP out port
			dsStatus = fHostPrefs->GetOutSMTPPortNumber( &uiTemp, true );
			if ( dsStatus == eDSAttributeNotFound )
			{
				fHostPrefs->SetOutSMTPPortNumber( uiTemp, true );
			}

			PrintThis( "SMTP out port", uiTemp );

		}
	}

	catch( long err )
	{
		dsStatus = (tDirStatus)err;
	}

	catch( ... )
	{
		dsStatus = eServerError;
	}


	return( dsStatus );

} // InitHostPrefs


//--------------------------------------------------------------------------------------------------
//	* InitializeGreetings ()
//
//--------------------------------------------------------------------------------------------------

void DSMgr::InitializeGreetings ( void )
{
	tDirStatus		dsStatus 		= eDSNoErr;
	uInt32			uiLastIP		= 0;
	DSServerPrefs	*pPrefsObj		= nil;

	pPrefsObj = DSMgr::GetServerPrefsObj();
	if ( pPrefsObj != nil )
	{
		pPrefsObj->DSServerPrefs::GetOurLastIPAddr( &uiLastIP, true );

//		if ( (uiLastIP == 0) || (CNetworkUtilities::GetOurIPAddress( kPrimaryIPAddr ) != uiLastIP) )
//		{
//			CString		tmpString( 64 );
//
//			dsStatus = pPrefsObj->GetSMTP_InGreeting( tmpString, true );
//			dsStatus = pPrefsObj->GetSMTP_OutGreeting( tmpString, true );
//			dsStatus = pPrefsObj->GetPOP3_Greeting( tmpString, true );
//			dsStatus = pPrefsObj->GetIMAP_Greeting( tmpString, true );
//			uiLastIP = CNetworkUtilities::GetOurIPAddress( kPrimaryIPAddr );
//			pPrefsObj->SetOurLastIPAddr( uiLastIP );
//		}
	}
} // InitializeGreetings


//--------------------------------------------------------------------------------------------------
//	* HasPasswdServer ()
//
//--------------------------------------------------------------------------------------------------

bool DSMgr::HasPasswdServer ( void )
{
	return( fHasPasswdSrvr );
} // HasPasswdServer


//--------------------------------------------------------------------------------------------------
//	* DoClearTextAuth ()
//
//--------------------------------------------------------------------------------------------------

Bool DSMgr::DoClearTextAuth ( char *inUserName, char *inPasswd )
{
	Bool				bResult		= false;
	DSMailUser		   *pMailUser	= nil;
	eMUGAuthResults		authResult	= kMUGAuthError;

	if ( (inUserName != nil) && (inPasswd != nil) )
	{
		this->GetUserByName( inUserName, &pMailUser );
		if ( pMailUser != nil )
		{
			authResult = (eMUGAuthResults)this->ValidatePassword( inPasswd, pMailUser );

			switch ( authResult )
			{
				// successful authentication
				case kMUGAuthNeedNewPasswd:
				case kMUGAuthPasswdExpired:
				case kMUGAuthOK:
					bResult = true;
					break;

				// failed authentication
				case kMUGAuthLoginDisabled:
				case kMUGAuthError:
				case kMUGAuthUnknownError:
				default:
					bResult = false;
					break;
			}

			free( pMailUser );
			pMailUser = nil;
		}
	}

	return( bResult );

} // DoClearTextAuth


//--------------------------------------------------------------------------------------------------
//	* Validate_MD5_Digest ()
//
//--------------------------------------------------------------------------------------------------

Bool DSMgr::Validate_MD5_Digest ( char *inUserName, char *inChalenge, char *inResponse )
{
	Bool				bResult		= false;
	eMUGAuthResults		authResult	= kMUGAuthError;

	if ( (inUserName != nil) && (inChalenge != nil) && (inResponse != nil) )
	{
		authResult = (eMUGAuthResults)this->ValidateResponse( inUserName, inChalenge, inResponse, kDSAuthCRAM_MD5 );
		switch ( authResult )
		{
			// successful authentication
			case kMUGAuthNeedNewPasswd:
			case kMUGAuthPasswdExpired:
			case kMUGAuthOK:
				bResult = true;
				break;

			// failed authentication
			case kMUGAuthLoginDisabled:
			case kMUGAuthError:
			case kMUGAuthUnknownError:
			default:
				bResult = false;
				break;
		}
	}

	return( bResult );

} // Validate_MD5_Digest


//--------------------------------------------------------------------------------------------------
//	* Validate_APOP_Digest ()
//
//--------------------------------------------------------------------------------------------------

Bool DSMgr::Validate_APOP_Digest ( char *inUserName, char *inChalenge, char *inResponse )
{
	Bool				bResult		= false;
	eMUGAuthResults		authResult	= kMUGAuthError;

	if ( (inUserName != nil) && (inChalenge != nil) && (inResponse != nil) )
	{
		authResult = (eMUGAuthResults)this->ValidateResponse( inUserName, inChalenge, inResponse, kDSAuthAPOP );

		switch ( authResult )
		{
			// successful authentication
			case kMUGAuthNeedNewPasswd:
			case kMUGAuthPasswdExpired:
			case kMUGAuthOK:
				bResult = true;
				break;

			// failed authentication
			case kMUGAuthLoginDisabled:
			case kMUGAuthError:
			case kMUGAuthUnknownError:
			default:
				bResult = false;
				break;
		}
	}

	return( bResult );

} // Validate_APOP_Digest


//--------------------------------------------------------------------------------------------------
//	* PrintThis ()
//
//--------------------------------------------------------------------------------------------------

void DSMgr::PrintThis ( const char *inStr, const uInt32 inValue )
{
	char		pNumStr[ 32 ];

	::sprintf( pNumStr, "%lu", inValue );

	this->PrintThis( inStr, pNumStr );
} // PrintThis


//--------------------------------------------------------------------------------------------------
//	* PrintThis ()
//
//--------------------------------------------------------------------------------------------------

const uInt32	kLineLen	= 45;

void DSMgr::PrintThis ( const char *inStr, const char *inValue )
{
	uInt32		uiLen	= 0;
	const char *pDots	= " .............................................";
	CString		csText( 128 );

	csText.Set( "  " );
	csText.Append( inStr );

	uiLen = csText.GetLength();
	if ( uiLen < kLineLen )
	{
		uiLen = kLineLen - uiLen;
		csText.Append( pDots, uiLen );
	}

	csText.Append( ": " );
	csText.Append( inValue );
} // PrintThis

