/*
	$Id: CHostEntry.10.1.cpp,v 1.2 2003/08/29 20:35:38 dasenbro Exp $

	File:		CHostEntry.10.1.cpp

	Contains:	C++ implementation of persistent Host Entry objects

	Written by:	Nick Brosnahan

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

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

		$Log: CHostEntry.10.1.cpp,v $
		Revision 1.2  2003/08/29 20:35:38  dasenbro
		Pickup migration of local hosts.
		
		Revision 1.1  2003/08/10 18:09:25  dasenbro
		added to export local host names.
		
		Revision 1.18  2001/07/09 16:03:45  dasenbro
		Syntax changes for better readability.
		
		Revision 1.17  2001/06/21 20:51:06  dasenbro
		Updated file header info.
		
		Revision 1.16  2001/06/21 19:49:42  dasenbro
		Added Change History.
		

	Projector History:

	  <ASM9>	 3/15/99	MED		Removed CSmartYield.
	  <ASM8>	  3/1/99	DOR		Change SetNextTime to do the "right" thing.
	  <ASM7>	 1/24/99	MED		Added a boolean to Create to prevent searching for host prefs.
	  <ASM6>	12/10/98	DOR		We should do actual assignements rather than compares when doing
									our "Set" routines.
		 <5>	10/30/98	DOR		Added support for 2nd ID's to host entries.  We now move host's
									onto and off of a 2nd ID list based on their "nextAttemptTime"
									value.  The outgoing threads will only visit hosts on the
									"active" host list, therefore limiting the number of hosts that
									must be visited to process outgoing mail.
		 <4>	10/29/98	DOR		Merge the expire host changes onto the main code branch.
		 <3>	10/18/98	MED		Set redirect host flag to false on error.
		 <2>	10/13/98	DOR		Lock the cache when deleting an object.
		<69>	10/13/98	DOR		Demo for Mike
		<68>	 9/23/98	DOR		Optimize FindHostByTypeName to check the last host we "saw" to
									see if it's what we're looking for.
		<67>	 9/21/98	MED		Create() first looks for the host and only creates it if it
									isn't found.
		<66>	 8/24/98	MED		Added set/get members to ignore schedule.
		<65>	 6/17/98	DOR		Initialize the  NextAttempt time to be a large value so that
									newly created hosts don't occupy very much of the Mail Server's
									time..
		<64>	  6/4/98	DOR		#2241916 - properly calculate the elasped time for AppleTalk
									connections.
		<63>	  6/1/98	DOR		#2240866 - changed the ETRN code to use the Next deferal
									interval..
		<62>	  6/1/98	DOR		Remove the "kAdd" argument from Create since we no longer use
									it.
		<61>	  6/1/98	MED		Changed ::Create to be a more kinder and gentler ::Create.
		<60>	  6/1/98	DOR		Change finish connection to properly calculate the connection
									time.
		<59>	 5/22/98	DOR		Add code to support ignoring DNS cache settings while doing
									local-host startup checks..
		<58>	 5/19/98	DOR		Fix for bug #2237747
		<57>	 5/19/98	MED		Added future expansion data members.
		<56>	 5/11/98	DOR		Properly use the Server cache setting values as min. rather than
									seconds.  Fill in a "Fake" 4 hour value for RespectTTL..
		<55>	  5/5/98	DOR		Fix another CalcNextETRN bugs.
		<54>	  5/5/98	DOR		Fix CalcNextETRNTime, and add a ETRNConnectionFinish routine.
		<53>	  4/8/98	DOR		Fix warnings.
		<52>	  4/8/98	DOR		Change from using ::strncpy to CUtils::Strncpy
		<51>	  4/8/98	DOR		Fix some ::strcpy's to be ::strncpy's
		<50>	  4/2/98	DOR		Change call to gDB_10_1->removeObject
		<49>	 3/31/98	DOR		Change some calls to "SetConnectionInfo" to set "more"
									information.
		<48>	 3/31/98	DOR		Fix it so that we duplicate suppress when posting to a host
									spool.
		<47>	 3/27/98	DOR		Clear the remote forwarding flag if the domain name is "bad".
		<46>	03/26/98	DOR		Fix CHostEntry so that it can't create a host with an empty
									HostName... Bug #2223086
		<45>	03/23/98	DOR		Grab the ObjectLock in ::Create.
		<44>	03/23/98	DOR		Get the object lock in some key places to prevent race
									conditions.
		<43>	03/18/98	DOR		Change an If condition to do an explicite compare with a value,
									rather than relying on C's implicite zero is false policy.
		<42>	03/17/98	DOR		Use elapsed time utility function to format elapsed time strings
		<41>	 3/14/98	DOR		Close up a race condition in ::Create..
		<40>	 3/12/98	DOR		Print out preference error's when the forwarding DNS info is
									bad.
		<39>	 3/11/98	DOR		Merge changes off of a branch.
		<38>	 3/11/98	MED		Changed FindByID() to do a Use() only if it created the object,
									FindMe() now does an explicit Use().
		<37>	 3/10/98	DOR		Fix a bug where we were resetting the resolved host name _EVERY_
									time we posted a message.
		<36>	  3/9/98	DOR		Fix CalcNextAttemptTime to move the NextAttemptTime forward if a
									custome schedule is currently in a "active" time-window...
		<35>	  3/9/98	DOR		Move Get/Set/Clear OwnerThreadID to CDBObject.
		<34>	  3/7/98	DOR		Minor changes.
		<33>	  3/5/98	DOR		Remove code that was setting a host to "kBounceDestination & add
									some debugging code to prevent it..
		<31>	 2/16/98	DOR		Update some log messages.
		<30>	 2/11/98	DOR		Changes in usage of IsLocalHost.
		<29>	  2/3/98	MED		Cleaned OSUtil.cp into CUtils.cp, COSUtils.cp and
									CSmartYield.cp.
		<28>	 1/12/98	MED		Fixed a conditional check in IsSendETRNOn().
		<27>	 1/12/98	MED		Added support for ETRN.
		<26>	12/16/97	MED		Added a ; after a mail assert, Dave.
		<25>	12/15/97	DOR		Update the object version to "include" the size of the
									dataportion of the object, and add an assert to "FindByID" that
									checks the version of the object.
		<24>	12/12/97	DOR		Update CalcNextAttempt time to use schedule information if
									available.
		<23>	12/11/97	DOR		Deal with calculating the next connection retry frequency based
									on host preferences.
		<22>	12/10/97	DOR		Minor changes.
		<21>	12/10/97	DOR		Minor changes.
		<20>	12/10/97	DOR		Add code to age the DNS entry when the MX/A-Record flags change
									from their previous settings.
		<19>	12/10/97	DOR		Pay attention to "use server general" & "resolve MX & A-Record"
		<18>	12/10/97	DOR		Now pay attention to global mail redirection & global SMTP
									outgoing port #.
		<17>	 12/9/97	DOR		Psuedo code to pay attention to Global server settings.
		<16>	 12/9/97	DOR		New log messages.
		<15>	 12/5/97	MED		fixed SetDeleteThisHost().
		<14>	 12/5/97	MED		Added stop now and delete host flags with getters and setters.
		<13>	 12/4/97	DOR		No longer cache preferences or DNS info, let the Database do
									it!!!  Don't be _CLEVER_!!!!
		<12>	 12/4/97	DOR		Create the prefs object when creating a HostEntry.
		<11>	 12/3/97	DOR		More tweaks.
		<10>	 12/3/97	DOR		Minor syntax errors, file now compiles.
		 <9>	 12/3/97	DOR		Add all the CMXRecord code.  This file may not compile as is.
		 <8>	 12/3/97	MED		Changed CInHostEntryPref to CHostEntryPref_10_1.
		 <7>	 12/3/97	MED		Combined In and Out host entry prefs.
		 <6>	 12/2/97	DOR		Make set next attempt time smart enough to "kick" the outgoing
									threads.
		 <5>	 12/2/97	DOR		Add function to find and cache HostEntry preferences...
		 <4>	11/21/97	DOR		Add code to generate non-delivery warnings.
		 <3>	11/19/97	MED		Integrated 5.0.3 changes.
		 <2>	 10/9/97	DOR		Add two fields to CHostEntry, two ObjID fields to "point" to the
									incoming & outgoing routing preferences.

	To Do:
*/

#include <time.h>

#include "CHostEntry.10.1.h"
#include "CMailSpool.10.1.h"
#include "AppResources.h"
#include "CEnvelopeInfo.10.1.h"
#include "CEnvelope.10.1.h"
#include "Database.10.1.h"
#include "CUtils.h"
#include "CServerPrefs.10.1.h"
#include "CRootObject.10.1.h"
#include "CGlobals.h"


Boolean	CHostEntry_10_1::GetSetFields (	const eFieldDataRequest_10_1 inFieldRequest,
									const void				*inObjDataPtr,
									void					*outFieldData )
{
	Boolean			result			= false;
	CHostEntryData_10_1	*theData		= (CHostEntryData_10_1 *)inObjDataPtr;
	CHostEntry_10_1		*aTempObject	= NULL;
	char			*theDestString	= (char *)outFieldData;
	ObjID			*anObjIDPtr		= (ObjID *)outFieldData;

	switch ( inFieldRequest )
	{
		case kSetObjID_10_1:
			theData->fHostEntryID = *anObjIDPtr;
			break;

		case kStringNameRequest_10_1:
			if (::strlen(theData->fHostName) != 0)
			{
				CUtils::Strncpy(theDestString, theData->fHostName, kStr2IDMaxStringSize_10_1);
				result = true;
			}
			break;
		
		case k2ndIDRequest_10_1:
			result = false;
			if ( (theData->fHostStatistics.fShortFlags & k2ndIDHasBeenSet) != 0 )
			{
				if ( ((theData->fHostStatistics.nextConnectionAttempt != 0) && (theData->fHostStatistics.nextConnectionAttempt < kNeverAttemptToConnectValue_10_1)) ||
					((theData->fHostStatistics.nextETRNTime != 0) && (theData->fHostStatistics.nextETRNTime < kNeverAttemptToConnectValue_10_1)) )
				{
					if ( gRootObj_10_1 != nil )
					{
						*anObjIDPtr = gRootObj_10_1->GetActiveHost2ndID( false );
						result = true;
					}
					else
					{
					}
				}
			}
			break;

		case kObjectPINGRequest_10_1:
			// cause the objects constructor to get called, so we force a check of
			// the struct alignment...
			aTempObject = new CHostEntry_10_1;
			delete( aTempObject );
			break;

		case k2ndIDTargetChk_10_1:
			// fall through...
			
		default:
			result = false;
			break;
	}

	return result;
}

ObjID CHostEntry_10_1::GetObjectID	( void )
{
	return fHostEntryData.fHostEntryID;
}


//--------------------------------------------------------------------------------------------------
//	 Done
//
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::Done ( CHostEntry_10_1* &inPtr )
{
	CDBBaseObject_10_1 *aDBObject = (CDBBaseObject_10_1 *) inPtr;
	aDBObject->Done(aDBObject);
	inPtr = (CHostEntry_10_1 *)aDBObject;

}


CHostEntryPref_10_1* CHostEntry_10_1::FindHostEntryPref ( void )
{
	CHostEntryPref_10_1	*pHostPrefsObj = NULL;

	if ( this->GetHostPrefID() != 0 )
	{
		pHostPrefsObj = CHostEntryPref_10_1::FindByID( this->GetHostPrefID() );
	}

	if ( pHostPrefsObj == NULL )
	{
		pHostPrefsObj = CHostEntryPref_10_1::GetHostEntryPrefsObj( this->GetObjectID() );
	}

	return( pHostPrefsObj );
}


CDBBaseObject_10_1* CHostEntry_10_1::ObjectAllocNew	( void )
{
	return new CHostEntry_10_1;
}

CHostEntry_10_1* CHostEntry_10_1::FindByID ( const ObjID inObjectID )
{
	return (CHostEntry_10_1 *) CDBMailBaseObject_10_1::MailUniversalFindByID(	inObjectID,
														CHostEntry_10_1::GetObjTypeConstant(),
														CHostEntry_10_1::GetObjVersConstant(),
														CHostEntry_10_1::GetObjSizeConstant(),
														CHostEntry_10_1::ObjectAllocNew		);
} // FindByID



CHostEntry_10_1* CHostEntry_10_1::FindByTypeName ( OSType inAddressType, const char* inHostName )
{
	static ObjID	lastHostIDFound = 0;

	Boolean		foundOne = false;
	ObjID		theHostID;
	CHostEntry_10_1	*theHostEntry = NULL;
	OSErr 		result;

	(gDB_10_1->GetBaseObjectCachePtr())->LockTheCache(CHostEntry_10_1::GetObjTypeConstant());

	try
	{
		if (lastHostIDFound != 0)
		{
			theHostEntry = CHostEntry_10_1::FindByID(lastHostIDFound);
			if (theHostEntry != NULL)
			{
				if ((CUtils::Stricmp(inHostName, theHostEntry->GetHostName()) == 0) &&
					(theHostEntry->GetAddressType() == inAddressType))
				{
					foundOne = true;
				}
				else
				{
					theHostEntry->Done(theHostEntry);
				}
			}
		}

		if (foundOne == false)
		{
			result = gDB_10_1->GetObjDataByString(kHostEntrySignature_10_1, inHostName, theHostID);
			if (result == CMailDatabase_10_1::kDBNoErr)
			{
				theHostEntry = CHostEntry_10_1::FindByID(theHostID);
				if (theHostEntry != NULL)
				{
					if (theHostEntry->GetAddressType() == inAddressType)
					{
						lastHostIDFound = theHostID;
						foundOne = true;
					}
					else
					{
						theHostEntry->Done(theHostEntry);
					}
				}
			}
		}		
	}
	catch ( ExceptionCode err )
	{
		(gDB_10_1->GetBaseObjectCachePtr())->UnLockTheCache(CHostEntry_10_1::GetObjTypeConstant());
		Throw_(err);
	}

	(gDB_10_1->GetBaseObjectCachePtr())->UnLockTheCache(CHostEntry_10_1::GetObjTypeConstant());

	return theHostEntry;
}

CHostEntry_10_1* CHostEntry_10_1::Create ( OSType			inAddressType,
								 const char	   *inHostName,
								 Boolean	   &outHostCreated,
								 const Boolean	inSetConnectionTime )
{
	CHostEntry_10_1		*theHostEntry	= nil;
	CHostEntryPref_10_1	*pHostPrefsObj	= NULL;
	OSErr			result = kNoErr;
	ObjID			aNewID = 0;
	Boolean			junk = false;
	uInt32			curTime	= 0;

	try
	{
		outHostCreated = false;

		if ( ::strlen( inHostName ) == 0 )
		{
			return ( NULL );
		}

		(gDB_10_1->GetBaseObjectCachePtr())->LockTheCache(CHostEntry_10_1::GetObjTypeConstant());

		// MUST do a find before a create to see if it already exists...
		theHostEntry = CHostEntry_10_1::FindByTypeName( inAddressType, inHostName );
		if ( theHostEntry == nil )
		{
			theHostEntry = new CHostEntry_10_1;

			ThrowIfMemFail_( theHostEntry );

			// NOTE: this needs to be set _BEFORE_ we create the object in the DB
			// so that at the first entry of the object into the DB we can "find" this
			// host entry from other threads...
			theHostEntry->SetHostName( inHostName );

			result = gDB_10_1->CreateObj( kHostEntrySignature_10_1, aNewID, theHostEntry->GetHostEntryData() );
			if ( result == CMailDatabase_10_1::kDBNoErr )
			{
				if ( theHostEntry->GetObjectLock(false) == kNoErr )
				{
					theHostEntry->Use();
					theHostEntry->SetAddressType (inAddressType);	// kRFC822 or kAppleTalk
					theHostEntry->SetResolveHostName (inHostName, junk);

					if (inSetConnectionTime == true)
					{
						curTime = ::time(NULL);
						curTime = curTime - (curTime % 60);	// round off to last min...this will improve the odds
															// that last connect time & last failed time will _NEVER_ match
															// and for this operation losing upto 59 seconds is not an issue...
						theHostEntry->SetLastSuccessConnectTime(curTime);
						theHostEntry->SetLastFailedConnectTime(curTime);
					}
					
					theHostEntry->SetShortFlags(theHostEntry->GetShortFlags() | k2ndIDHasBeenSet);
					theHostEntry->SetDirty();

					outHostCreated = true;
				}
				else
				{
					delete( theHostEntry );
					theHostEntry = NULL;
				}
			}
			else
			{
				delete( theHostEntry );
				theHostEntry = NULL;
			}

			if (theHostEntry != NULL)
			{
				// Display in the Log
				pHostPrefsObj = CHostEntryPref_10_1::GetHostEntryPrefsObj( theHostEntry->GetObjectID() );

				// the above call has the side effect of linking the host entry to the default host prefs
				if ( pHostPrefsObj != NULL )
				{
					pHostPrefsObj->Done( pHostPrefsObj );
				}

				theHostEntry->ReleaseObjectLock(true);
			}
		}

		(gDB_10_1->GetBaseObjectCachePtr())->UnLockTheCache(CHostEntry_10_1::GetObjTypeConstant());
	}

	catch ( ExceptionCode err )
	{
		(gDB_10_1->GetBaseObjectCachePtr())->UnLockTheCache( CHostEntry_10_1::GetObjTypeConstant() );
	}

	return (theHostEntry);
}


CHostEntryData_10_1* CHostEntry_10_1::GetHostEntryData	( void )
{
	if (this != NULL)
	{
		return &(this->fHostEntryData);
	}
	else
	{
		return NULL;
	}
}

void CHostEntry_10_1::SetDirty	( const Boolean inForceWriteFlag )
{
	if ((this != NULL) && (this->GetHostEntryID() != 0))
	{
		setDirty(inForceWriteFlag);
	}
}



Boolean CHostEntry_10_1::Delete ( CHostEntry_10_1 *inHostEntry )
{
	Boolean deleted = false;
	CMailSpool_10_1	*theMailSpool	= NULL;
	OSErr		result			= kNoErr;

	try
	{
		if ( inHostEntry != NULL )
		{
			( gDB_10_1->GetBaseObjectCachePtr())->LockTheCache(CHostEntry_10_1::GetObjTypeConstant() );

			result = gDB_10_1->RemoveObj(kHostEntrySignature_10_1, inHostEntry->GetHostEntryID());
			deleted = (result == kNoErr);
			if ( deleted )
			{
				inHostEntry->fHostEntryData.fHostEntryID = 0;
			}

			// let's delete the mail spool that is associated with this Host Entry
			theMailSpool = CMailSpool_10_1::FindByID( inHostEntry->GetMailSpoolID() );
			ThrowIfNULL_( theMailSpool );

			CMailSpool_10_1::Delete( theMailSpool );
			theMailSpool->Done( theMailSpool );

			(gDB_10_1->GetBaseObjectCachePtr())->UnLockTheCache(CHostEntry_10_1::GetObjTypeConstant());
		}
	}

	catch ( ExceptionCode err )
	{
		theMailSpool->Done (theMailSpool);
		(gDB_10_1->GetBaseObjectCachePtr())->UnLockTheCache(CHostEntry_10_1::GetObjTypeConstant());
	}

	return( deleted );

} // Delete

uInt32 CHostEntry_10_1::Count()
{
	uInt32	aCount = 0;
	OSErr	result;

	result = gDB_10_1->GetObjectCount( kHostEntrySignature_10_1, aCount );

	if (result != kNoErr)
	{
		aCount = 0;
	}

	return aCount;
}

#pragma mark -

CHostEntry_10_1::CHostEntry_10_1(void) :
	CDBMailBaseObject_10_1(	&fHostEntryData.fDBHeader,
				&fHostEntryData.fDBFooter,
				&fHostEntryData,
				CHostEntry_10_1::GetObjTypeConstant(),
				CHostEntry_10_1::GetObjVersConstant(),
				CHostEntry_10_1::GetObjSizeConstant() )
{
	// NeoAccess IDs
	fHostEntryData.fHostEntryID		= 0;
	fHostEntryData.fMailSpoolID		= 0;

	fGoPostal						= false;
	fDeleteThisHost					= false;

	// HostEntry Information
	fHostEntryData.fAddressType		 = 0;
	fHostEntryData.fHostName[0]		 = '\0';
	fHostEntryData.fDestination		 = 0;
	fHostEntryData.fAccessTime		 = 0;
	fHostEntryData.fDestinationClass = kDestClassUnknown;
	fHostEntryData.fIgnoreSchedule	 = false;		// Ignore schedule flag
	

	// Resolution
	fHostEntryData.fResolveHostName[0]	 = '\0';
	fHostEntryData.fHostPrefsID			 = 0;
	fHostEntryData.fRealHostDNSCacheID	 = 0;
	fHostEntryData.fResolveHostDNSCacheID = 0;

#if USE_APPLETALK
	// AppleTalk
	::memset (&fDDPAddress, 0, sizeof (DDPAddress));
#endif
	::memset (&fHostEntryData.fHostStatistics, 0, sizeof (HostStatistics_10_1));
	fHostEntryData.fHostStatistics.nextConnectionAttempt = kNeverAttemptToConnectValue_10_1;
	fHostEntryData.fHostStatistics.nextETRNTime = kNeverAttemptToConnectValue_10_1;

	// Reserved
	fHostEntryData.fReserved1	= kReservedConst;
	fHostEntryData.fReserved2	= kReservedConst;
	
	this->ChkCompilerStructAlignment();
}



CHostEntry_10_1::CHostEntry_10_1::~CHostEntry_10_1 (void)
{
}

void	CHostEntry_10_1::ChkCompilerStructAlignment	( void )
{
	/*
		this dump was aquired by using the MPW command DumpSYM - TTE from the .xsym file produced
		from a Code Warrior Pro 1 Release Build of the AppleShare IP Mail Server for MacOS version 6.2
		
		this version of the structure is consider the "reference" standard for all future ASIP Mail Servers.
		If the structure does NOT match these offsets the version of the Mail Server that 
		you are compiling will _NOT_ be compatible with AppleShare IP 6.0, 6.1, or 6.2 (this also effects
		the AppleShare IP Mail Tool, and to a more limited extent the AppleShare IP Mail Admin since some
		of these structures are passed across the wire..)
		
		If _ANY_ of these Asserts "fire" you've got a backwards compatibility problem that _MUST_ be fixed!!
		
		this code wasn't put here for my personal amusement, this code was put here from a hard earned lesson
		that simple changes to headers/compilers _WILL_ break this stuff, better to know now than after we
		ship.  Look upon this code as a debugging AIDE, not an impediment.
		
		Type Name:  CHostEntryData_10_1		TINFO offset: 55282		(TINFO SYM file offset (hex): $1257F2)
			RecordOf 
			offset 0 NamedTypeOf fDBHeader TypeDef of "Type ID 248"
			offset 12 NamedTypeOf fHostEntryID 
			offset 16 NamedTypeOf fMailSpoolID unsigned long 
			offset 20 NamedTypeOf fReserved1 
			offset 24 NamedTypeOf fReserved2 unsigned long 
			offset 28 NamedTypeOf fAddressType 
			offset 32 NamedTypeOf fHostName CString 
			offset 288 NamedTypeOf fDestination unsigned long 
			offset 292 
			offset 296 NamedTypeOf fDestinationClass unsigned long 
			offset 300 
			offset 556 NamedTypeOf fHostStatistics TypeDef of "Type ID 255"
			offset 624 NamedTypeOf fHostPrefsID 
			offset 628 NamedTypeOf fRealHostDNSCacheID unsigned long 
			offset 632 NamedTypeOf fResolveHostDNSCacheID 
			offset 636 NamedTypeOf fDBFooter TypeDef of "Type ID 250"
	*/

	static Boolean	aRunOnceFlag = false;	
	if (aRunOnceFlag == false)
	{
		//FileFormatSaftyChk(CHostEntryData_10_1, fDBHeader,				0);
		//FileFormatSaftyChk(CHostEntryData_10_1, fHostEntryID,			12);				// this is us (denormalized)
		//FileFormatSaftyChk(CHostEntryData_10_1, fMailSpoolID,			16);				// this is our mail spool
		//FileFormatSaftyChk(CHostEntryData_10_1, fReserved1,			20);
		//FileFormatSaftyChk(CHostEntryData_10_1, fReserved2,			24);
		//FileFormatSaftyChk(CHostEntryData_10_1, fAddressType,			28);				// kRFC822 or kAppleTalk
		//FileFormatSaftyChk(CHostEntryData_10_1, fHostName,				32);					// Original hostname or NBPAddressString
		//FileFormatSaftyChk(CHostEntryData_10_1, fDestination,			288);				// TCP/IP or ADSP or UUCP or Bounce
		//FileFormatSaftyChk(CHostEntryData_10_1, fAccessTime,				292);				// Last time accessed
		//FileFormatSaftyChk(CHostEntryData_10_1, fDestinationClass,		296);			// keep track of local vs. Remote...
		//FileFormatSaftyChk(CHostEntryData_10_1, fResolveHostName,		300);			// Hostname or NBPAddressString to send to
		//FileFormatSaftyChk(CHostEntryData_10_1, fHostStatistics,			556);
		//FileFormatSaftyChk(CHostEntryData_10_1, fHostPrefsID,			624);				// ObjID of a CHostEntryPref_10_1 record...
		//FileFormatSaftyChk(CHostEntryData_10_1, fRealHostDNSCacheID,		628);
		//FileFormatSaftyChk(CHostEntryData_10_1, fResolveHostDNSCacheID,	632);
		//FileFormatSaftyChk(CHostEntryData_10_1, fDBFooter,				636);
//		this->ReportBackwardCompatibility(sizeof(CHostEntryData_10_1),	640,	"CHostEntryData_10_1", "SIZEOF()");
//		this->ReportBackwardCompatibility(kHostEntryDataSize, 640, "CHostEntryData_10_1", "kHostEntryDataSize");

		/*
			//-----------------------------------------------------------------------------------------
			// now lets check the embedded struct, to make sure it hasn't realigned itself, but kept the
			// same size...
			Type Name:  HostStatistics		TINFO offset: 55426		(TINFO SYM file offset (hex): $125882)
				RecordOf
				offset 0 NamedTypeOf numMessages unsigned long 
				offset 4 NamedTypeOf numMessagesThisHour 
				offset 8 NamedTypeOf numRecipients unsigned long 
				offset 12 NamedTypeOf numBytesTransferred 
				offset 16 NamedTypeOf numSuccessfulConnections unsigned long 
				offset 20 NamedTypeOf numFailedConnections 
				offset 24 NamedTypeOf lastConnectionSuccess unsigned long 
				offset 28 NamedTypeOf lastConnectionFailure 
				offset 32 NamedTypeOf nextConnectionAttempt unsigned long 
				offset 36 NamedTypeOf nextDeferralInterval 
				offset 40 NamedTypeOf nextETRNTime unsigned long 
				offset 44 NamedTypeOf totalConnectionTime 
				offset 48 NamedTypeOf numBytesThisConnection unsigned long 
				offset 52 NamedTypeOf fShortFlags 
				offset 56 NamedTypeOf fLongFlags unsigned long 
				offset 60 NamedTypeOf maxMessageSize 
				offset 64 NamedTypeOf numBadRecipients unsigned long 
		*/

		//FileFormatSaftyChk(HostStatistics, numMessages,					0);	
		//FileFormatSaftyChk(HostStatistics, numMessagesThisHour,			4);
		//FileFormatSaftyChk(HostStatistics, numRecipients,				8);
		//FileFormatSaftyChk(HostStatistics, numBytesTransferred,			12);
		//FileFormatSaftyChk(HostStatistics, numSuccessfulConnections,	16);
		//FileFormatSaftyChk(HostStatistics, numFailedConnections,		20);
		//FileFormatSaftyChk(HostStatistics, lastConnectionSuccess,		24);
		//FileFormatSaftyChk(HostStatistics, lastConnectionFailure,		28);
		//FileFormatSaftyChk(HostStatistics, nextConnectionAttempt,		32);
		//FileFormatSaftyChk(HostStatistics, nextDeferralInterval,		36);
		//FileFormatSaftyChk(HostStatistics, nextETRNTime,				40);
		//FileFormatSaftyChk(HostStatistics, totalConnectionTime,			44);
		//FileFormatSaftyChk(HostStatistics, numBytesThisConnection,		48);
		//FileFormatSaftyChk(HostStatistics, fShortFlags,					52);
		//FileFormatSaftyChk(HostStatistics, fLongFlags,					56);
		//FileFormatSaftyChk(HostStatistics, maxMessageSize,				60);
		//FileFormatSaftyChk(HostStatistics, numBadRecipients,			64);
//		this->ReportBackwardCompatibility(sizeof(HostStatistics),	68,	"HostStatistics", "SIZEOF()");
	}
	
	aRunOnceFlag = true;
}
#pragma mark -

ObjID CHostEntry_10_1::GetHostEntryID (void)
{
	return (fHostEntryData.fHostEntryID);
}

ObjID CHostEntry_10_1::GetMailSpoolID (void)
{
	while ( fHostEntryData.fMailSpoolID == 0 )	// Mailspool does not exist yet
	{
		if (this->GetObjectLock(true) == kNoErr)
		{
			// Create a MailSpool for this HostEntry
			CMailSpool_10_1* theMailSpool =  CMailSpool_10_1::Create(CMailSpool_10_1::kHostSpool, this->GetHostName());

			if (theMailSpool != NULL)
			{
				// Save the MailSpoolID
				this->SetMailSpoolID (theMailSpool->GetMailSpoolID());

				theMailSpool->Done (theMailSpool);
			}

			this->ReleaseObjectLock(true);
		}
	}

	return (fHostEntryData.fMailSpoolID);
}

void CHostEntry_10_1::SetMailSpoolID (ObjID inMailSpoolID)
{
	if (fHostEntryData.fMailSpoolID != inMailSpoolID)
	{
		fHostEntryData.fMailSpoolID = inMailSpoolID;
		this->SetDirty();
	}
}

ObjID CHostEntry_10_1::GetRealHostDNSCacheID (void)
{
	return (fHostEntryData.fRealHostDNSCacheID);
}

void CHostEntry_10_1::SetRealHostDNSCacheID (ObjID inHostDNSCacheID)
{
	if (fHostEntryData.fRealHostDNSCacheID != inHostDNSCacheID)
	{
		fHostEntryData.fRealHostDNSCacheID = inHostDNSCacheID;
		this->SetDirty();
	}
}

ObjID CHostEntry_10_1::GetResolveHostDNSCacheID (void)
{
	return (fHostEntryData.fResolveHostDNSCacheID);
}

void CHostEntry_10_1::SetResolveHostDNSCacheID (ObjID inHostDNSCacheID)
{
	if (fHostEntryData.fResolveHostDNSCacheID != inHostDNSCacheID)
	{
		fHostEntryData.fResolveHostDNSCacheID = inHostDNSCacheID;
		this->SetDirty();
	}
}

#pragma mark -

OSType CHostEntry_10_1::GetAddressType(void)
{
	return (fHostEntryData.fAddressType);
}

void CHostEntry_10_1::SetAddressType(OSType inAddressType)
{
	if (fHostEntryData.fAddressType != inAddressType)
	{
		fHostEntryData.fAddressType = inAddressType;
		this->SetDirty();
	}
}

uInt32 CHostEntry_10_1::GetDestination (void)
{
	return (fHostEntryData.fDestination);
}

void CHostEntry_10_1::SetDestination (uInt32 inDestination)
{
	if (fHostEntryData.fDestination != inDestination)
	{
		fHostEntryData.fDestination = inDestination;
		this->SetDirty();
	}
}

uInt32 CHostEntry_10_1::GetDestClass (void)
{
	return (fHostEntryData.fDestinationClass);
}

void CHostEntry_10_1::SetDestClass (uInt32 inDestClass)
{
	if ((inDestClass != fHostEntryData.fDestinationClass) && (inDestClass != kBounceDestination))
	{
		fHostEntryData.fDestinationClass = inDestClass;
		this->SetDirty();
	}
}

ObjID CHostEntry_10_1::GetHostPrefID (void)
{
	return (fHostEntryData.fHostPrefsID);
}

void CHostEntry_10_1::SetHostPrefID (const ObjID inHostPrefID)
{
	if (inHostPrefID != fHostEntryData.fHostPrefsID)
	{
		fHostEntryData.fHostPrefsID = inHostPrefID;
		this->SetDirty();
	}
}

const char* CHostEntry_10_1::GetHostName(void)
{
	return (fHostEntryData.fHostName);
}

void CHostEntry_10_1::SetHostName(const char* inHostName)
{
	CUtils::Strncpy(fHostEntryData.fHostName, (char *) inHostName, sizeof(InetDomainName));
	this->SetDirty();
}

uTime_t CHostEntry_10_1::GetAccessTime (void)
{
	return (fHostEntryData.fAccessTime);
}

void CHostEntry_10_1::UpdateAccessTime (void)
{
	fHostEntryData.fAccessTime = ::time(NULL);
	this->SetDirty();
}

const char* CHostEntry_10_1::GetResolveHostName(void)
{
	return (fHostEntryData.fResolveHostName);
}

Boolean CHostEntry_10_1::SetResolveHostName(const char* inHostName, Boolean &outBadName )
{
	Boolean changed		= (CUtils::Stricmp(fHostEntryData.fResolveHostName, inHostName) != 0);
	Boolean goodName	= true;
	uInt32	nameLength	= 0;
	uInt32	index		= 0;

	outBadName = false;
	if (changed == true)
	{
		nameLength = ::strlen(inHostName);
		goodName = (goodName == true) && (::strlen(inHostName) != 0);
		for (index = 0; (goodName == true) && (index < nameLength); index++)
		{
			goodName = (goodName == true) && ((inHostName[index] > ' ') && (inHostName[index] <= '~'));
		}

		if (goodName == true)
		{
			CUtils::Strncpy (fHostEntryData.fResolveHostName, (char *) inHostName, sizeof(InetDomainName));
			this->SetDirty();
			outBadName = false;
		}
		else if (CUtils::Stricmp(fHostEntryData.fResolveHostName, fHostEntryData.fHostName) != 0)
		{
			CUtils::Strncpy(fHostEntryData.fResolveHostName, fHostEntryData.fHostName, sizeof(InetDomainName));
			this->SetDirty();
			outBadName = true;
		}
		else
		{
			outBadName = true;
		}
	}

	return changed;
}

void CHostEntry_10_1::SetNextAttemptTime ( const uTime_t inNextAttemptTime )
{
}

#pragma mark -

#if USE_APPLETALK
#pragma mark -

Boolean	CHostEntry_10_1::ResolveADSPHost (void)
{
	DDPAddress theDDPAddress;

	try
	{
		ThrowIfOSErr_ (CNetworkUtilities::ResolveToDDPAddress ((const uInt8*)fHostEntryData.fResolveHostName, &theDDPAddress));

		this->SetDDPAddress (&theDDPAddress);

		return true;
	}
	catch ( ExceptionCode err )
	{
		this->FailedConnect(::time(NULL), (char*)this->GetResolveHostName(), kSLSmtpADSPConnectFail, 0, true);
		return false;
	}
}

void CHostEntry_10_1::GetDDPAddress (DDPAddress* inDDPAddress)
{
	memcpy (inDDPAddress, &fDDPAddress, sizeof (DDPAddress));
}

void CHostEntry_10_1::SetDDPAddress (DDPAddress* inDDPAddress)
{
	memcpy (&fDDPAddress, inDDPAddress, sizeof (DDPAddress));
	this->SetDirty();
}
#endif // USE_APPLETALK
#pragma mark -

void CHostEntry_10_1::Post (ObjID inEnvelopeID)
{
}

void CHostEntry_10_1::DoExpire ( void )
{
}



//--------------------------------------------------------------------------------------------------
//	* GetGoPostal
//
//--------------------------------------------------------------------------------------------------

Boolean CHostEntry_10_1::GetGoPostal ( void )
{
	return( fGoPostal );
} // GetGoPostal



//--------------------------------------------------------------------------------------------------
//	* SetGoPostal
//
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetGoPostal ( Boolean inGoPostal )
{
	fGoPostal = inGoPostal;
} // SetGoPostal



//--------------------------------------------------------------------------------------------------
//	* GetDeleteThisHost
//
//--------------------------------------------------------------------------------------------------

Boolean CHostEntry_10_1::GetDeleteThisHost ( void )
{
	return( fDeleteThisHost );
} // GetDeleteThisHost



//--------------------------------------------------------------------------------------------------
//	* SetDeleteThisHost
//
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetDeleteThisHost ( Boolean inDeleteThisHost )
{
	// we want to stop processing this host
	this->SetGoPostal( true );

	// set the delete flag
	fDeleteThisHost = inDeleteThisHost;

} // SetDeleteThisHost



#pragma mark -

void CHostEntry_10_1::ResetStatistics (void)
{
	memset(&fHostEntryData.fHostStatistics, 0, sizeof (HostStatistics_10_1));
	this->SetDirty();
}

HostStatistics_10_1* CHostEntry_10_1::GetStatistics (void)
{
	return (&fHostEntryData.fHostStatistics);
}

void CHostEntry_10_1::SuccessfulConnect( uTime_t inTime, char* inResolveHostName, uInt32 inTransport, const uInt32 inPort2Use )
{
}

void CHostEntry_10_1::CalcNextAttemptTime ( const uTime_t inTime )
{
} // CalcNextAttemptTime



//--------------------------------------------------------------------------------------------------
//	* GetNextETRNTime
//
//--------------------------------------------------------------------------------------------------

uTime_t CHostEntry_10_1::GetNextETRNTime ( void )
{
    return( 0 );
} // GetNextETRNTime



//--------------------------------------------------------------------------------------------------
//	* SetNextETRNTime
//
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetNextETRNTime ( uTime_t inNextTime )
{
} // SetNextETRNTime



//--------------------------------------------------------------------------------------------------
//	* CalcNextETRNTime
//
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::CalcNextETRNTime ( const uTime_t inTime )
{
} // CalcNextETRNTime



//--------------------------------------------------------------------------------------------------
//	* IsETRNOn
//
//--------------------------------------------------------------------------------------------------

Boolean CHostEntry_10_1::IsETRNOn ( void )
{
	Boolean			result			= false;
	CHostEntryPref_10_1	*hostPrefsObj	= this->FindHostEntryPref();

	if ( hostPrefsObj != nil )
	{
		if ( hostPrefsObj->IsSendETRNOn() == true )
		{
			result = true;
		}
		hostPrefsObj->Done( hostPrefsObj );
	}

	return( result );

} // IsETRNOn



void CHostEntry_10_1::FailedConnect( uTime_t			inTime,
								char		   *inResolveHostName,
								uInt32			inTransport,
								const uInt32	inPortNumber,
								const Boolean	inCalcNextAttempt )
{
}

void CHostEntry_10_1::ConnectionFinished (char* inResolveHostName, uInt32 inMsgs, uInt32 inKBytes,
									uInt32 inRecipientCount, uTime_t inTime, uInt32 inTransport)
{
}

void CHostEntry_10_1::ETRNConnectionFinished ( char* inResolveHostName, const uTime_t inTime )
{
	uInt32		deltaTime		= 0;
	CString		aTimeString( 64 );

	if ( inTime < this->GetLastSuccessConnectTime( true ) )
	{
		deltaTime = 1;
	}
	else
	{
		deltaTime = inTime - this->GetLastSuccessConnectTime( true );
		if ( deltaTime == 0 )
		{
			deltaTime = 1;
		}
	}

	CUtils::FormatElapsedTimeFromSecs( deltaTime, aTimeString );

	if (this->GetLastSuccessConnectTime(true) != 0)
	{
		fHostEntryData.fHostStatistics.totalConnectionTime += deltaTime;

		this->CalcNextETRNTime( inTime );
		this->SetDirty();
	}
}

uTime_t CHostEntry_10_1::NextConnectAt( void )
{
	return ( fHostEntryData.fHostStatistics.nextConnectionAttempt );
}


void CHostEntry_10_1::StatAddMessage(uInt32 inMessageSize)
{
	fHostEntryData.fHostStatistics.numMessages++;
	fHostEntryData.fHostStatistics.numMessagesThisHour++;
	if (inMessageSize > fHostEntryData.fHostStatistics.maxMessageSize)
		fHostEntryData.fHostStatistics.maxMessageSize = inMessageSize;

	this->SetDirty();
}

void CHostEntry_10_1::StatAddRecipients(uInt32 inRecipients)
{
	fHostEntryData.fHostStatistics.numRecipients += inRecipients;
	this->SetDirty();
}

void CHostEntry_10_1::StatAddBadRecipient(void)
{
	fHostEntryData.fHostStatistics.numBadRecipients++;
	this->SetDirty();
}

void CHostEntry_10_1::StatAddBytes (uInt32 inBytes)
{
	fHostEntryData.fHostStatistics.numBytesTransferred += inBytes;
	fHostEntryData.fHostStatistics.numBytesThisConnection += inBytes;
	this->SetDirty();
}


//--------------------------------------------------------------------------------------------------
//	* SetShortFlags
//	
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetShortFlags ( uInt16 inShortFlags )
{
	if ( fHostEntryData.fHostStatistics.fShortFlags != inShortFlags )
	{
		fHostEntryData.fHostStatistics.fShortFlags = inShortFlags;
		this->SetDirty();
	}
} // SetShortFlags


//--------------------------------------------------------------------------------------------------
//	* GetShortFlags
//	
//--------------------------------------------------------------------------------------------------

uInt16 CHostEntry_10_1::GetShortFlags ( void )
{
	return ( fHostEntryData.fHostStatistics.fShortFlags );
} // GetShortFlags


//--------------------------------------------------------------------------------------------------
//	* SetLongFlags
//	
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetLongFlags ( uInt32 inLongFlags )
{
	if ( fHostEntryData.fHostStatistics.fLongFlags != inLongFlags )
	{
		fHostEntryData.fHostStatistics.fLongFlags = inLongFlags;
		this->SetDirty();
	}
} // SetLongFlags


//--------------------------------------------------------------------------------------------------
//	* GetLongFlags
//	
//--------------------------------------------------------------------------------------------------

uInt32 CHostEntry_10_1::GetLongFlags ( void )
{
	return ( fHostEntryData.fHostStatistics.fLongFlags );
} // GetLongFlags


//--------------------------------------------------------------------------------------------------
//	* GetLastSuccessConnectTime
//	
//--------------------------------------------------------------------------------------------------

uTime_t CHostEntry_10_1::GetLastSuccessConnectTime ( const Boolean inAllowFakeNeverConnectTimeFlag )
{
	if ( inAllowFakeNeverConnectTimeFlag == true )
	{
		if ( fHostEntryData.fHostStatistics.lastConnectionSuccess == this->GetLastFailedConnectTime( false ) )
		{
			return( 0 );	// pretend we've never had a connection...
		}
	}
	
	return ( fHostEntryData.fHostStatistics.lastConnectionSuccess );

} // GetLastSuccessConnectTime


//--------------------------------------------------------------------------------------------------
//	* SetLastSuccessConnectTime
//	
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetLastSuccessConnectTime ( const uTime_t inTime )
{
	if (inTime != fHostEntryData.fHostStatistics.lastConnectionSuccess)
	{
		fHostEntryData.fHostStatistics.lastConnectionSuccess = inTime;
		this->SetDirty();
	}
} // SetLastSuccessConnectTime


//--------------------------------------------------------------------------------------------------
//	* GetLastFailedConnectTime
//	
//--------------------------------------------------------------------------------------------------

uTime_t CHostEntry_10_1::GetLastFailedConnectTime ( const Boolean inAllowFakeNeverConnectTimeFlag )
{
	if (inAllowFakeNeverConnectTimeFlag == true)
	{
		if (fHostEntryData.fHostStatistics.lastConnectionFailure == this->GetLastSuccessConnectTime(false))
		{
			return (0);	// pretend we've never had a connection...
		}
	}

	return ( fHostEntryData.fHostStatistics.lastConnectionFailure );
} // GetLastFailedConnectTime


//--------------------------------------------------------------------------------------------------
//	* SetLastFailedConnectTime
//	
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetLastFailedConnectTime ( const uTime_t inTime )
{
	if (inTime != fHostEntryData.fHostStatistics.lastConnectionFailure)
	{
		fHostEntryData.fHostStatistics.lastConnectionFailure = inTime;
		this->SetDirty();
	}
} // SetLastFailedConnectTime


//--------------------------------------------------------------------------------------------------
//	* GetIgnoreSchedule
//	
//--------------------------------------------------------------------------------------------------

Boolean CHostEntry_10_1::GetIgnoreSchedule ( void )
{
	return ( fHostEntryData.fIgnoreSchedule );
} // GetIgnoreSchedule



//--------------------------------------------------------------------------------------------------
//	* SetIgnoreSchedule
//	
//--------------------------------------------------------------------------------------------------

void CHostEntry_10_1::SetIgnoreSchedule ( Boolean inIgnoreIt )
{
	if ( fHostEntryData.fIgnoreSchedule != inIgnoreIt )
	{
		fHostEntryData.fIgnoreSchedule = inIgnoreIt;
		this->SetDirty();
	}
} // SetIgnoreSchedule

