/*
	$Id: CTextObject.cpp,v 1.1 2003/04/20 23:31:08 dasenbro Exp $

	File:		CTextObject.cpp

	Contains:	C++ implementation of generic 

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

	Written by:	Michael Dasenbrock

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

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

		$Log: CTextObject.cpp,v $
		Revision 1.1  2003/04/20 23:31:08  dasenbro
		Initial check-in.
		
		Revision 1.11  2002/07/27 02:15:33  dasenbro
		Add 127.0.0.1 to default SMTP relay list.
		
		Revision 1.10  2002/07/19 01:58:21  dasenbro
		Prevent objects from adding data and not releasing it.  Also cleaned up
		a DB object leak.
		
		Revision 1.9  2002/06/04 17:38:14  dasenbro
		Syntax changes.
		
		Revision 1.8  2002/05/09 16:59:04  dasenbro
		Changed all str... calls to CUtils::Str... to be NULL safe.
		
		Revision 1.7  2002/03/21 16:41:42  dasenbro
		Updated file version information.
		
		Revision 1.6  2001/06/21 20:51:09  dasenbro
		Updated file header info.
		
		Revision 1.5  2001/06/21 19:54:42  dasenbro
		Added Change History.
		

	Projector History:
		
	To Do:
*/


// PP
#include <string.h>
#include <stdlib.h>

// App
#include "CTextObject.h"
#include "Database.h"
#include "UException.h"
#include "CGlobals.h"
#include "CUtils.h"


//--------------------------------------------------------------------------------------------------
//	* GetSetFields ()
//
//--------------------------------------------------------------------------------------------------

Boolean	CTextObject::GetSetFields (	const eFieldDataRequest	 inFieldRequest,
									const void				*inObjDataPtr,
									void					*outFieldData )
{
	Boolean				 result			= false;
	CTextObjectData	   *aclDataPtr		= (CTextObjectData *)inObjDataPtr;
	CTextObject		   *aTempObject		= NULL;
	ObjID			   *anObjIDPtr		= (ObjID *)outFieldData;

	if ( aclDataPtr != nil )
	{
		switch ( inFieldRequest )
		{
			case kSetObjID:
				aclDataPtr->fObjectID = *anObjIDPtr;
				break;

			case kObjectPINGRequest:
				// cause the objects constructor to get called, so we force a check of
				// the struct alignment...
				aTempObject = new CTextObject;
				if ( aTempObject != nil )
				{
					delete aTempObject;
				}
				break;

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

	return( result );

} // GetSetFields


//--------------------------------------------------------------------------------------------------
//	* GetObjectID ()
//
//--------------------------------------------------------------------------------------------------

ObjID CTextObject::GetObjectID ( void )
{
	return ( this->GetMyObjectID() );
} // GetObjectID


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

void CTextObject::Done ( CTextObject* &inPtr )
{
	CDBBaseObject *aDBObject = (CDBBaseObject *) inPtr;
	aDBObject->Done(aDBObject);

	inPtr = (CTextObject *)aDBObject;
} // Done


//--------------------------------------------------------------------------------------------------
//	* ObjectAllocNew ()
//
//--------------------------------------------------------------------------------------------------

CDBBaseObject* CTextObject::ObjectAllocNew ( void )
{
	return( new CTextObject );
} // ObjectAllocNew


//--------------------------------------------------------------------------------------------------
//	* FindByID ()
//
//--------------------------------------------------------------------------------------------------

CTextObject* CTextObject::FindByID ( const ObjID inObjectID )
{
	CTextObject	*outObj	= nil;

	outObj = (CTextObject *) CDBMailBaseObject::MailUniversalFindByID ( inObjectID,
															CTextObject::GetObjTypeConstant(),
															CTextObject::GetObjVersConstant(),
															CTextObject::GetObjSizeConstant(),
															CTextObject::ObjectAllocNew );
	return( outObj );

} // FindByID


//--------------------------------------------------------------------------------------------------
//	* Create ()
//
//--------------------------------------------------------------------------------------------------

CTextObject* CTextObject::Create ( void )
{
	CTextObject	*objPtr	= nil;		// creates a reference
	OSErr		result	= kNoErr;
	ObjID		aNewID	= 0;

	try
	{
		objPtr = new CTextObject;
		ThrowIfMemFail_( objPtr );

		(gDB->GetBaseObjectCachePtr())->LockTheCache( CTextObject::GetObjTypeConstant() );

		result = gDB->CreateObj( kTextObjectSignature, aNewID, objPtr->GetMyObjectData() );

		if ( result == CMailDatabase::kDBNoErr )
		{
			objPtr->Use();
			objPtr->SetDirty();
		}
		else
		{
			delete objPtr;
			objPtr = nil;
		}
		
		(gDB->GetBaseObjectCachePtr())->UnLockTheCache( CTextObject::GetObjTypeConstant() );
	}

	catch ( long err )
	{
		(gDB->GetBaseObjectCachePtr())->UnLockTheCache( CTextObject::GetObjTypeConstant() );
	}
	
	return( objPtr );

} // Create


//--------------------------------------------------------------------------------------------------
//	* Delete ()
//
//--------------------------------------------------------------------------------------------------

Boolean CTextObject::Delete ( CTextObject *inObjPtr )
{
	Boolean deleted = false;
	OSErr	result;

	try
	{
		if ( inObjPtr != nil )
		{
			result = gDB->RemoveObj( kTextObjectSignature, inObjPtr->GetMyObjectID());
			deleted = ( result == kNoErr );
			if ( deleted )
			{
				inObjPtr->SetMyObjectID( 0 );
			}
		}
	}
	catch( long err )
	{
	}

	return( deleted );

} // Delete


//--------------------------------------------------------------------------------------------------
//	* Count ()
//
//--------------------------------------------------------------------------------------------------

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

	if ( gDB != nil )
	{
		result = gDB->GetObjectCount( kTextObjectSignature, aCount );
	}

	return( aCount );

} // Count


//--------------------------------------------------------------------------------------------------
//	* VerifyAll ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::VerifyAll ( void )
{
} // VerifyAll


#pragma mark -
#pragma mark * Construct

//--------------------------------------------------------------------------------------------------
//	* CTextObject ()
//
//--------------------------------------------------------------------------------------------------

CTextObject::CTextObject ( void ) :
	CDBMailBaseObject (	&fObjectData.fDBHeader,
				&fObjectData.fDBFooter,
				&fObjectData,
				CTextObject::GetObjTypeConstant(),
				CTextObject::GetObjVersConstant(),
				CTextObject::GetObjSizeConstant() )

{
	fObjectData.fObjectID		= 0;
	fObjectData.fNextObj		= 0;
	fObjectData.fItemCount		= 0;
	fObjectData.fOffset			= 0;

	// Reserved
	fObjectData.fReserved1		= kReservedConst;
	fObjectData.fReserved2		= kReservedConst;

	::memset( fObjectData.fStringData, 0, kTextObjSize );

	this->ChkCompilerStructAlignment();

} // CTextObject


//--------------------------------------------------------------------------------------------------
//	* ~CTextObject ()
//
//--------------------------------------------------------------------------------------------------

CTextObject::~CTextObject ( void )
{
} // ~CTextObject

void	CTextObject::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:  CTextObjectData		TINFO offset: 50884		(TINFO SYM file offset (hex): $1246C4)
//			RecordOf 
//			offset 0 NamedTypeOf fDBHeader TypeDef of "Type ID 248"
//			offset 12 NamedTypeOf fObjectID 
//			offset 16 NamedTypeOf fOwnerID unsigned long 
//			offset 20 NamedTypeOf fPrevObjectID 
//			offset 24 NamedTypeOf fNextObjectID unsigned long 
//			offset 28 NamedTypeOf fItemCount 
//			offset 30 NamedTypeOf fItemSize signed short 
//			offset 32 NamedTypeOf fReserved1 
//			offset 36 NamedTypeOf fReserved2 unsigned long 
//			offset 40 NamedTypeOf fDataBlock 
//			offset 168 NamedTypeOf fDBFooter TypeDef of "Type ID 250"
//	
//
//	static Boolean	aRunOnceFlag = false;	
//	if (aRunOnceFlag == false)
//	{
//		CTextObjectData	*tempData = &(this->fObjectData);
//		register unsigned long baseAddr = (unsigned long) tempData;
//
//		//FileFormatSaftyChk(CTextObjectData, fDBHeader,		0);
//		//FileFormatSaftyChk(CTextObjectData, fObjectID,		12);				// this is us (denormalized)
//		//FileFormatSaftyChk(CTextObjectData, fOwnerID,		16);				// who ownes us
//		//FileFormatSaftyChk(CTextObjectData, fPrevObjectID,	20);			// Link to previous object
//		//FileFormatSaftyChk(CTextObjectData, fNextObjectID,	24);			// Link to next object
//		//FileFormatSaftyChk(CTextObjectData, fItemCount,		28);
//		//FileFormatSaftyChk(CTextObjectData, fItemSize,		30);
//		//FileFormatSaftyChk(CTextObjectData, fReserved1,		32);
//		//FileFormatSaftyChk(CTextObjectData, fReserved2,		36);
//		//FileFormatSaftyChk(CTextObjectData, fDataBlock[0],	40);
//		//FileFormatSaftyChk(CTextObjectData, fDBFooter,		168);
//		this->ReportBackwardCompatibility(sizeof(CTextObjectData),	172,	"CTextObjectData", "SIZEOF()");
//		this->ReportBackwardCompatibility(kExpansionDataSize,	172,	"CTextObjectData", "kExpansionDataSize");
//	}
//	
//	aRunOnceFlag = true;
}

//--------------------------------------------------------------------------------------------------
//	* GetMyObjectData ()
//
//--------------------------------------------------------------------------------------------------

CTextObjectData* CTextObject::GetMyObjectData ( void )
{
	CTextObjectData* result = nil;
	if ( this != nil )
	{
		result = &( this->fObjectData );
	}

	return( result );

} // GetMyObjectData


//--------------------------------------------------------------------------------------------------
//	* SetDirty ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::SetDirty	( void )
{
	if ( (this != nil) && (this->GetMyObjectID() != 0) )
	{
		setDirty();
	}
} // SetDirty


#pragma mark -
#pragma mark * Getters/Setters

//--------------------------------------------------------------------------------------------------
//	* InitObject ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::InitObject ( void )
{
	fObjectData.fItemCount		= 0;
	fObjectData.fOffset			= 0;

	::memset( fObjectData.fStringData, 0, kTextObjSize );

} // InitObject


//--------------------------------------------------------------------------------------------------
//	* GetMyObjectID ()
//
//--------------------------------------------------------------------------------------------------

ObjID CTextObject::GetMyObjectID ( void )
{
	return( fObjectData.fObjectID );
} // GetMyObjectID


//--------------------------------------------------------------------------------------------------
//	* SetMyObjectID ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::SetMyObjectID ( ObjID inObjID )
{
	if ( fObjectData.fObjectID != inObjID )
	{
		fObjectData.fObjectID = inObjID;
		this->SetDirty();
	}
} // SetMyObjectID


//--------------------------------------------------------------------------------------------------
//	* GetNextObjectID ()
//
//--------------------------------------------------------------------------------------------------

ObjID CTextObject::GetNextObjectID ( void )
{
	return( fObjectData.fNextObj );
} // GetNextObjectID


//--------------------------------------------------------------------------------------------------
//	* SetNextObjectID ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::SetNextObjectID ( ObjID inObjID )
{
	if ( fObjectData.fNextObj != inObjID )
	{
		fObjectData.fNextObj = inObjID;
		this->SetDirty();
	}
} // SetNextObjectID


//--------------------------------------------------------------------------------------------------
//	* GetOffset ()
//
//--------------------------------------------------------------------------------------------------

uInt32 CTextObject::GetOffset ( void )
{
	return( fObjectData.fOffset );
} // GetOffset


//--------------------------------------------------------------------------------------------------
//	* SetOffset ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::SetOffset ( uInt32 inOffset )
{
	if ( fObjectData.fOffset != inOffset )
	{
		fObjectData.fOffset = inOffset;
		this->SetDirty();
	}
} // SetOffset


//--------------------------------------------------------------------------------------------------
//	* GetItemCount ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObject::GetItemCount ( void )
{
	return( fObjectData.fItemCount );
} // GetItemCount


//--------------------------------------------------------------------------------------------------
//	* SetItemCount ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::SetItemCount ( sInt32 inCount )
{
	if ( fObjectData.fItemCount != inCount )
	{
		fObjectData.fItemCount = inCount;
		this->SetDirty();
	}
} // SetItemCount


//--------------------------------------------------------------------------------------------------
//	* IncrementItemCount ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::IncrementItemCount ( void )
{
	fObjectData.fItemCount++;
	this->SetDirty();
} // IncrementItemCount


//--------------------------------------------------------------------------------------------------
//	* DecrementItemCount ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::DecrementItemCount ( void )
{
	if ( fObjectData.fItemCount > 0 )
	{
		fObjectData.fItemCount--;
		this->SetDirty();
	}
} // DecrementItemCount


//--------------------------------------------------------------------------------------------------
//	* GetDataBlock ()
//
//--------------------------------------------------------------------------------------------------

char* CTextObject::GetDataBlock ( void )
{
	return( fObjectData.fStringData );
} // GetDataBlock


//--------------------------------------------------------------------------------------------------
//	* SetDataBlock ()
//
//--------------------------------------------------------------------------------------------------

void CTextObject::SetDataBlock ( char *inData )
{
	if ( inData != nil )
	{
		::memcpy( &fObjectData.fStringData, inData, kTextObjSize );
		this->SetDirty();
	}
} // SetDataBlock


//--------------------------------------------------------------------------------------------------
//	* AddString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObject::AddString ( const char *inString, uInt16 *outOffset )
{
	sInt32		siResult	= kNoErr;
	uInt16		usStrLen	= 0;

	if ( inString != nil )
	{
		usStrLen = CUtils::Strlen( inString );

		if ( (fObjectData.fOffset + 8) >= kTextObjSize )
		{
			siResult = kObjectFull;
		}
		else if ( (usStrLen + fObjectData.fOffset + 8) >= kTextObjSize )
		{
			siResult = kStringTooBig;
		}
		else
		{
			*outOffset = fObjectData.fOffset;

			// Copy the marker
			::memcpy( fObjectData.fStringData + fObjectData.fOffset, kStringMarker, 2 );

			// Bump the offset past the marker
			fObjectData.fOffset += 2;

			// Copy the length of the string
			::memcpy( fObjectData.fStringData + fObjectData.fOffset, &usStrLen, 2 );

			// Bump the offset past the length
			fObjectData.fOffset += 2;

			// Copy the string
			::memcpy( fObjectData.fStringData + fObjectData.fOffset, inString, usStrLen );
			
			// Bump the offset past the string
			fObjectData.fOffset += usStrLen;

			// Increment the object count
			fObjectData.fItemCount++;

			// Set the object to dirty
			this->SetDirty();
		}
	}
	else
	{
		siResult = kNullStirng;
	}

	return( siResult );

} // AddString


//--------------------------------------------------------------------------------------------------
//	* RemoveString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObject::RemoveString ( uInt16 inOffset )
{
	sInt32		siResult	= kNoErr;
	uInt16		usOffset	= 0;
	uInt16		usStrLen	= 0;

	usOffset = inOffset;
	if ( inOffset < kTextObjSize )
	{
		if ( ::memcmp( fObjectData.fStringData + usOffset, kStringMarker, 2 ) == 0 )
		{
			// Skip past the marker
			usOffset += 2;

			// Get the length of the string
			::memcpy( &usStrLen, fObjectData.fStringData + usOffset, 2 );

			// Verify that the lenght is not too big.  It can't be bigger than
			//	the text object size minus the offset
			if ( usStrLen <= (kTextObjSize - usOffset) )
			{
				// Everything checks out.  Decrement the string count
				DecrementItemCount();

				siResult = kNoErr;
			}
			else
			{
				siResult = kInvalidString;
			}
		}
		else
		{
			siResult = kInvalidOffset;
		}
	}
	else
	{
		siResult = kOffsetTooBig;
	}

	return( siResult );

} // RemoveString


//--------------------------------------------------------------------------------------------------
//	* GetString ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CTextObject::GetString ( uInt16 inOffset, char **outStr )
{
	sInt32		siResult	= kNoErr;
	uInt16		usOffset	= 0;
	uInt16		usStrLen	= 0;
	char	   *pOutStr		= nil;

	if ( outStr != nil )
	{
		usOffset = inOffset;
		if ( inOffset < kTextObjSize )
		{
			if ( ::memcmp( fObjectData.fStringData + usOffset, kStringMarker, 2 ) == 0 )
			{
				// Skip past the marker
				usOffset += 2;

				// Get the length of the string
				::memcpy( &usStrLen, fObjectData.fStringData + usOffset, 2 );

				// Skip past the length
				usOffset += 2;

				// Verify that the lenght is not too big.  It can't be bigger than
				//	the text object size minus the offset
				if ( usStrLen <= (kTextObjSize - usOffset) )
				{
					if ( CUtils::Strlen( fObjectData.fStringData + usOffset ) == usStrLen )
					{
						pOutStr = (char *)::calloc( usStrLen + 1, sizeof( char ) );
						if ( pOutStr != nil )
						{
							// Copy out the string
							::memcpy( pOutStr, fObjectData.fStringData + usOffset, usStrLen );

							*outStr = pOutStr;

							siResult = kNoErr;
						}
						else
						{
							siResult = kMemFullErr;
						}
					}
					else
					{
						siResult = kCorruptStrObj;
					}
				}
				else
				{
					siResult = kInvalidString;
				}
			}
			else
			{
				siResult = kInvalidOffset;
			}
		}
		else
		{
			siResult = kOffsetTooBig;
		}
	}
	else
	{
		siResult = kEmptyDestString;
	}

	return( siResult );

} // GetString

