/*
	File:		UHeaders_10_1.10.1.cpp

	Contains:	C++ implementation of 822 Header manipulation class

	Copyright:	© 2003 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

	To Do:
*/


#include <stdio.h>
#include <ctype.h>

#include "UHeaders.10.1.h"
#include "CMessagePart.10.1.h"
#include "CSmallMsgPart.10.1.h"
#include "CEnvelope.10.1.h"
#include "CServerPrefs.10.1.h"

#include "AppResources.h"
#include "CString.h"
#include "CUtils.h"
#include "COSUtils.h"

//#include "ProtocolStrings.h"

//-----------------------------------------------------------------------------------
//	* UHeaders_10_1
//
//-----------------------------------------------------------------------------------

UHeaders_10_1::UHeaders_10_1 ( ObjID inEnvelopeID )
{
	fEnvelopeID		= inEnvelopeID;
	fEnvelope		= nil;
	fSmallMsgPart	= nil;
	fLargeMsgPart	= nil;
	fDataPtr		= nil;
} // UHeaders_10_1



//-----------------------------------------------------------------------------------
//	* ~UHeaders_10_1
//
//-----------------------------------------------------------------------------------

UHeaders_10_1::~UHeaders_10_1 (void)
{
	if ( fEnvelope != nil )
	{
		fEnvelope->Done( fEnvelope );
		fEnvelope = nil;
	}

	if ( fSmallMsgPart != nil )
	{
		fSmallMsgPart->Done( fSmallMsgPart );
		fSmallMsgPart = nil;
	}

	if ( fLargeMsgPart != nil )
	{
		fLargeMsgPart->Done( fLargeMsgPart );
		fLargeMsgPart = nil;
	}

} // ~UHeaders_10_1



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

Boolean UHeaders_10_1::Initialize ( void )
{
	Boolean		success		= false;
	ObjID		msgPartID	= 0;
	uInt32		partLen		= 0;

	if ( fEnvelopeID != 0 )
	{
		fEnvelope = CEnvelope_10_1::FindByID( fEnvelopeID );
		if ( fEnvelope != nil )
		{
			msgPartID = fEnvelope->GetMsgPartListHeadID();
			if ( msgPartID == 0 )
			{
				msgPartID = fEnvelope->GetSmallMsgPartID();
				if ( msgPartID != 0 )
				{
					fSmallMsgPart = CSmallMsgPart_10_1::FindByID( msgPartID );
					if ( fSmallMsgPart != nil )
					{
						fDataPtr	= fSmallMsgPart->GetPartData();
						partLen		= fSmallMsgPart->GetDataLength();
						success = true;
					}
				}
			}
			else
			{
				fLargeMsgPart = CMessagePart_10_1::FindByID( msgPartID );
				if ( fLargeMsgPart != nil )
				{
					fDataPtr	= fLargeMsgPart->GetPartData();
					partLen		= fLargeMsgPart->GetDataLength();
					success = true;
				}
			}

			if ( success == true )
			{
				fEndPtr = ::strstr( fDataPtr, "\r\n\r\n" );
				if ( fEndPtr != nil )
				{
					fHdrLen = fEndPtr - fDataPtr;
					if ( fHdrLen > partLen )
					{
						fEndPtr = fDataPtr + partLen;
						fHdrLen = partLen;
					}
				}
			}
		}
	}
		// find the header body separator

	return( success );

} // Initialize



//-----------------------------------------------------------------------------------
//	* FindHeaderTerm
//
//-----------------------------------------------------------------------------------

char* UHeaders_10_1::FindHeaderTerm ( char *inBuffer, uInt32 inBufferLength )
{
	char marker[] = "\r\n\r\n";

	// Assume the worst
	Boolean markerSeen = false;

	// Initialize the "expecting pointer"
	char *ep = marker;

	// True if we're in the middle of a possible marker
	Boolean checking = false;

	// Initialize the working pointer
	char *ptr = inBuffer;

	// index into buffers
	uInt32 i = 0;

	while (i < inBufferLength)
	{
		i++;
		if ( *ptr++ == *ep)
		{
			// as long as we see the next char in the signature, we are "checking"
			checking = true;

			// If we're at the end of what we were expecting, then we're done, and
			// we should note that we saw the marker
			if (*(++ep) == '\0')
			{
				markerSeen = true;
				break;
			}
		}
		else
		{
			// if we were checking for more signature chars, we must reset
			if (checking)
			{
				checking = false;
				ep = marker;
			}
		}
	}

	if (markerSeen)
		return ptr;
	else
		return NULL;
} // FindHeaderTerm



//-----------------------------------------------------------------------------------
//	* GetMaxReceivedHeaders
//
//-----------------------------------------------------------------------------------

long UHeaders_10_1::GetMaxReceivedHeaders ( void )
{
#if RHAPSODY_MISSING_FEATURES
	static Handle hMaxReceived = NULL;
	MaxReceivedHeaders* theMaxReceivedHeaders = NULL;

	if (hMaxReceived == NULL)
	{
		hMaxReceived = ::Get1Resource( kMaxReceivedHeadersResType, kMaxReceivedHeadersResID );
		if ( hMaxReceived != NULL )
		{
			::DetachResource (hMaxReceived);
			::HLock( hMaxReceived );
		}
	}

	if ( hMaxReceived != NULL )
	{
		theMaxReceivedHeaders = *((MaxReceivedHeaders**)hMaxReceived);

		return (theMaxReceivedHeaders->maxReceivedHeaders);
	}
#endif
	return 20;		// default to 20
} // GetMaxReceivedHeaders



//-----------------------------------------------------------------------------------
//	* SanitizeHeaders
//
//		- Make sure the headers are RFC822 compliant
//
//-----------------------------------------------------------------------------------
const uInt32 gKMaxReceivedFieldLength = 1024;
const uInt32 gKMaxMessagePrefixLength = 1024;

void UHeaders_10_1::Sanitize822Headers ( uInt32 inServerIPAddr )
{
} // SanitizeHeaders


const char *UHeaders_10_1::GetMsgviaString ( CEnvelope_10_1 *inEnvelope )
{
	if (inEnvelope != NULL)
	{
		switch (inEnvelope->GetViaCode())
		{
			case CEnvelope_10_1::kViaSMTP:
				return("via TCP with SMTP");
				break;

			case CEnvelope_10_1::kViaATalk:
				return("via ADSP with SMTP");
				break;

			case CEnvelope_10_1::kViaUUCP:
				return("with UUCP");
				break;

			case CEnvelope_10_1::kViaNDR:
				return("via NDR");
				break;

			case CEnvelope_10_1::kViaIMAP:
				return("via IMAP");
				break;

			case CEnvelope_10_1::kViaPOPAccountNo822Parse:
				return("via POP3 post to AppleShare IP account");
				break;

			case CEnvelope_10_1::kViaPOPAccountWith822Parse:
				return("via POP3 822 header based routing");
				break;
		}
	}
	
	return("via UNKNOWN");
}

// --------------------------------------------------------------------------------
//	* HeaderValue
//
//		- Get the value of a header
//
// --------------------------------------------------------------------------------

Boolean UHeaders_10_1::HeaderValue ( char		*inBuffer,
								long		 inBufLen,
								const char	*inHeader,
								char		*outBuffer )
{
	char* val = NULL;
	Boolean found = false;

	// The _very_ end of the buffer may not be null terminated, and is probably
	// beyond the headers, so we null terminate it temporarily;
	char lastchar = inBuffer[inBufLen -1];
	inBuffer[inBufLen - 1] = '\0';

	// find the header body separator
	char* headerEnd = strstr( inBuffer, "\r\n\r\n" );
	if ( headerEnd == NULL )
	{
		headerEnd = inBuffer + inBufLen;
	}

	if ( headerEnd != NULL )
	{
		// null terminate the end of the header
		char lastHdrChar = *headerEnd;
		*headerEnd = '\0';

		// find the header tag (if any)
		char* headerLoc = FindHdrTag( inBuffer, inHeader );

		if ( headerLoc )
		{
			if ( headerLoc < headerEnd )
			{
				found = true;
				val = headerLoc;
			}
		}

		// reset the last header character
		*headerEnd = lastHdrChar;
	}

	// restore the last character
	inBuffer[inBufLen-1] = lastchar;

	if ( found && outBuffer )
	{
		// skip past the header field...
		val += ::strlen( inHeader );

		// skip past ws, if any...
		while ( (*val == ' ') || (*val == '\t') )
		{
			val++;
		}

		Boolean		done = false;

		while ( !done )
		{
			done = true;
			// copy value to outbuffer and terminate
			while ( (*val != '\r') && (*val != '\n') )
			{
				*outBuffer++ = *val++;
			}
			*outBuffer = '\0';

			// Check for line continuation
			if ( *val == '\r' )
			{
				val++;
				if ( *val == '\n' )
				{
					val++;
					if ( (*val == ' ') || (*val == '\t') )
					{
						done = false;
					}
				}
			}
		}
	}

	return found;
} // HeaderValue



//--------------------------------------------------------------------------------------------------
//	* GetHeaderFromBuff
//
//--------------------------------------------------------------------------------------------------

Boolean UHeaders_10_1::GetHeaderFromBuff (	char		 *inBuffer,
										long		  inBufLen,
										const char	 *inHeader,
										CString		 &outBuffer,
										Boolean		  inIncludeName )
{
	char	   *val		= nil;
	Boolean		found	= false;
	Boolean		done	= false;

	// The _very_ end of the buffer may not be null terminated, and is probably
	// beyond the headers, so we null terminate it temporarily;
	char lastchar = inBuffer[inBufLen -1];
	inBuffer[inBufLen -1] = '\0';

	// find the header body separator
	char* headerEnd = strstr( inBuffer, "\r\n\r\n" );
	if ( headerEnd == nil )
	{
		headerEnd = inBuffer + inBufLen;
	}


	if ( headerEnd != nil )
	{
		// null terminate the end of the header
		char lastHdrChar = *headerEnd;
		*headerEnd = '\0';

		// find the header tag (if any)
		char* headerLoc = FindHdrTag( inBuffer, inHeader );

		if ( headerLoc )
		{
			if ( headerLoc < headerEnd )
			{
				found = true;
				val = headerLoc;
			}
		}

		// reset the last header character
		*headerEnd = lastHdrChar;
	}

	// restore the last character
	inBuffer[inBufLen-1] = lastchar;

	if ( found )
	{

		if ( inIncludeName == false )
		{
			// skip past the header field...
			val += ::strlen( inHeader );

			// skip past ws, if any...
			while ( (*val == ' ') || (*val == '\t') )
			{
				val++;
			}
		}

		while ( !done )
		{
			char	lastChar	= '\0';

			done = true;

			// copy value to outbuffer and terminate
			while ( (*val != '\r') && (*val != '\n') && (*val != nil) )
			{
				if ( (*val != ' ') && (*val != '\t') )
				{
					lastChar = *val;
				}
				outBuffer.Append( *val++ );
			}
//			outBuffer.Append( '\0' );


			// Check for line continuation
			if ( *val == '\r' )
			{
				val++;
				if ( *val == '\n' )
				{
					val++;
					if ( lastChar == ';' )
					{
						outBuffer.Append( "\r\n" );
						done = false;
					}
					else if ( (*val == ' ') || (*val == '\t') )
					{
						if ( lastChar == ',' )
						{
							outBuffer.Append( "\r\n" );
						}
						done = false;
					}
				}
			}
			if ( *val == nil )
			{
				done = true;
			}
			if ( done == true )
			{
				outBuffer.Append( '\0' );
			}
		}
	}

	return( found );

} // GetHeaderFromBuff



//--------------------------------------------------------------------------------------------------
//	* RemoveHeader
//
//		- Remove a header from the message. Returns the # of bytes removed.
//--------------------------------------------------------------------------------------------------

long UHeaders_10_1::RemoveHeader (char *inBuffer, long inBufLen, char* inHeader)
{
	char* val = NULL;
	Boolean found = false;

	// The _very_ end of the buffer may not be null terminated, and is probably
	// beyond the headers, so we null terminate it temporarily;
	char lastchar = inBuffer[inBufLen -1];
	inBuffer[inBufLen -1] = '\0';

	// find the header body separator
	char* headerEnd = strstr(inBuffer, "\r\n\r\n");

	// find the header tag (if any)
	char* headerLoc = CUtils::Stristr(inBuffer, inHeader);

	if (headerLoc)
	{
		if (headerEnd)
		{
			if (headerLoc < headerEnd)
			{
				found = true;
				val = headerLoc;
			}
		}
		else
		{
			// couldn't find the header separator. blow it off.
			val = headerLoc;
			found = true;
		}
	}

	// restore the last character
	inBuffer[inBufLen-1] = lastchar;

	if (found)
	{
		char *ptr = val;

		// look for EOL
		while ( *ptr != '\r' )
			ptr++;

		// double check to make sure we're pointing to the end of line...
		if ((*ptr == '\r') && (*(ptr+1) == '\n'))
		{
			// remove line...
			ptr++; // ptr now points to last char or header line.

			// remlen is how much of the buffer is remaining after the header...
			long remlen = inBufLen - (ptr - inBuffer) - 1;

			// Copy the ending stuff over the removed header
			::memcpy( headerLoc, (ptr+1), remlen );

			// Return the amount removed...
			return ptr - headerLoc + 1;
		}
	}

	return 0;
} // RemoveHeader



//--------------------------------------------------------------------------------------------------
//	* CountReceived
//
//		- This method searches through the headers for "Received:" lines and returns a count.
//
//--------------------------------------------------------------------------------------------------

const	sInt16		kTempLen	= 64;

long UHeaders_10_1::CountReceived ( void )
{
	Boolean				done				= false;
	Boolean				noEndFound			= false;
	char				*buffer				= nil;
	char				lastchar			= '\0';
	char				*headerEnd			= nil;
	char				*startPoint			= nil;
	char				*headerLoc			= nil;
	char				tmpBuff[ kTempLen ];
	long				receivedCount		= 0;
	long				buflen				= 0;
	ObjID				nextID				= 0;
	CMessagePart_10_1		*mp					= nil;
	CSmallMsgPart_10_1		*smallMP			= nil;
	CEnvelope_10_1			*envObj				= nil;

	try
	{
		// Check the message prefix first
		envObj = CEnvelope_10_1::FindByID( fEnvelopeID );
		ThrowIfNULL_( envObj );

		if ( envObj->GetMessagePrefixLength() )		// if non-zero, we know there is one in there
		{
			receivedCount++;
		}

		// Get the header part ( message part zero )
		if ( envObj->GetMsgPartListHeadID() != 0 )
		{
			mp = CMessagePart_10_1::FindByID( envObj->GetMsgPartListHeadID() );
			ThrowIfNULL_( mp );

			buffer = mp->GetPartData();
			buflen = mp->GetDataLength();
		}
		else if ( envObj->GetSmallMsgPartID() != 0 )
		{
			smallMP = CSmallMsgPart_10_1::FindByID( envObj->GetSmallMsgPartID() );
			ThrowIfNULL_( smallMP );

			buffer = smallMP->GetPartData();
			buflen = smallMP->GetDataLength();
		}
		envObj->Done ( envObj );

		// The _very_ end of the buffer may not be null terminated, and is probably
		// beyond the headers, so we null terminate it temporarily;
		lastchar = buffer[ buflen - 1 ];
		buffer[ buflen - 1 ] = '\0';

		// find the header body separator
		headerEnd = ::strstr( buffer, "\r\n\r\n" );

		if ( headerEnd == nil )
		{
			noEndFound = true;
			headerEnd = buffer + buflen - 1;
		}

		startPoint = buffer;
		while ( (startPoint < headerEnd) && (!done) )
		{
			// find a "received:" tag ( if any )
			headerLoc = CUtils::Stristr( startPoint, "received:" );
			if ( headerLoc )
			{
				receivedCount++;

				// Advance the startPoint beyond the header tag
				startPoint = headerLoc + ::strlen( "received:" );
			}
			else
			{
				if ( (noEndFound == true) && (mp != nil) && (mp->GetNextMessagePartID() != 0) )
				{
					buffer[ buflen - 1 ] = lastchar;

					// null out the temp buffer
					::memset( tmpBuff, 0, kTempLen );

					// get the last 8 chars from the first message part
					::memcpy( tmpBuff, buffer + (buflen - 8), 8 );

					nextID = mp->GetNextMessagePartID();
					mp->Done( mp );

					mp = CMessagePart_10_1::FindByID( nextID );
					if ( mp != nil )
					{
						buffer = mp->GetPartData();
						buflen = mp->GetDataLength();

						lastchar = buffer[ buflen - 1 ];
						buffer[ buflen - 1 ] = '\0';

						::memcpy( tmpBuff + 8, buffer, 8 );
						if ( ::strstr( tmpBuff, "\r\n\r\n" ) != nil )
						{
							done = true;
						}
						else if ( CUtils::Stristr( tmpBuff, "received:" ) != nil )
						{
							receivedCount++;
						}

						// find the header body separator
						headerEnd = ::strstr( buffer, "\r\n\r\n" );
						if ( headerEnd == nil )
						{
							noEndFound = true;
							headerEnd = buffer + buflen - 1;
						}
						else
						{
							noEndFound = false;
						}

						startPoint = buffer;
					}
				}
				else
				{
					// we didn't find any more
					break;
				}
			}
		}

		// restore the last character
		buffer[ buflen - 1 ] = lastchar;

		smallMP->Done( smallMP );
		mp->Done( mp );

		return( receivedCount );
	}

	catch ( ExceptionCode err )
	{
		mp->Done( mp );
		smallMP->Done( smallMP );
		envObj->Done( envObj );
	}

	return( 0 );

} // CountReceived



//--------------------------------------------------------------------------------------------------
//	* GetHeaderFromMsg
//
//		- Find the hearder
//
//--------------------------------------------------------------------------------------------------

Boolean UHeaders_10_1::GetHeaderFromMsg ( const char	*inHeader,
									 CString	&outBuffer,
									 Boolean	 inIncludeTag,
									 Boolean	 inCheckPrefix )
{
	char		   *bufPtr		= nil;
	sInt32			bufLen		= 0;
	Boolean			found		= false;
	CEnvelope_10_1	   *envObj		= nil;
	CMessagePart_10_1   *mp			= nil;
	CSmallMsgPart_10_1  *smallMP		= nil;
	CString			aMsgPrefix;

	try
	{
		if ( fEnvelopeID != 0 )
		{
			envObj = CEnvelope_10_1::FindByID ( fEnvelopeID );
			if ( envObj != nil)
			{
				// First check the prefix in the envelope object
				if ( (envObj->GetMessagePrefixLength() != 0) && (inCheckPrefix == true) )
				{
					envObj->GetFullMessagePrefix(aMsgPrefix);
					bufPtr	= aMsgPrefix.GetData();
					bufLen	= ::strlen(bufPtr);

					found = GetHeaderFromBuff( bufPtr, bufLen, inHeader, outBuffer, inIncludeTag );
				}

				if ( found == false )
				{
					// Get the header part (message part zero)
					if ( envObj->GetMsgPartListHeadID() != 0 )
					{
						mp = CMessagePart_10_1::FindByID(envObj->GetMsgPartListHeadID());
						ThrowIfNULL_(mp);

						bufPtr = mp->GetPartData();
						bufLen = mp->GetDataLength();
					}
					else if (envObj->GetSmallMsgPartID() != 0)
					{
						smallMP = CSmallMsgPart_10_1::FindByID(envObj->GetSmallMsgPartID());
						ThrowIfNULL_(smallMP);

						bufPtr = smallMP->GetPartData();
						bufLen = smallMP->GetDataLength();
					}

					found = GetHeaderFromBuff( bufPtr, bufLen, inHeader, outBuffer, inIncludeTag );
				}

				envObj->Done( envObj );
			}
		}

		smallMP->Done( smallMP );
		mp->Done( mp );
	}

	catch ( ExceptionCode err )
	{
		envObj->Done( envObj );
		smallMP->Done( smallMP );
		mp->Done( mp );
	}

	return( found );

} // GetHeaderFromMsg



//--------------------------------------------------------------------------------------------------
//	* FindHdrTag
//
//		- Find the hearder.  Start on newline only
//
//--------------------------------------------------------------------------------------------------

char* UHeaders_10_1::FindHdrTag ( const char *inString, const char *inHdrTag )
{
	Boolean		done	= false;
	const char   *p		= inString;
	const char   *ep	= inHdrTag;

	while ( !done )
	{
		char c1 = ( ::isupper(*p) ) ? ::tolower(*p) : *p;
		char c2 = ( ::isupper(*ep) ) ? ::tolower(*ep) : *ep;
		p++;

		if ( c1 == c2 )
		{
			ep++;
			if ( !*ep )
			{
				return( (char *)(p - ::strlen( inHdrTag )) );
			}
		}
		else
		{
			// get the next line
			p = ::strstr( p, "\r\n" );
			if ( p == nil )
			{
				done = true;
			}
			else
			{
				p += 2;
				ep = inHdrTag;
			}
		}

	}
	return( NULL );
} // FindHdrTag



//--------------------------------------------------------------------------------------------------
//	* Get822Header
//
//--------------------------------------------------------------------------------------------------

void UHeaders_10_1::Get822Header ( CString &outHdr )
{
	char		   *bufPtr		= nil;
	char		   *hdrEnd		= nil;
	ObjID			mpID		= 0;
	sInt32			bufLen		= 0;
	Boolean			found		= false;
	CEnvelope_10_1	   *envObj		= nil;
	CMessagePart_10_1   *mp			= nil;
	CSmallMsgPart_10_1  *smallMP		= nil;
	CString			aMsgPrefix;

	try
	{
		if ( fEnvelopeID != 0 )
		{
			envObj = CEnvelope_10_1::FindByID ( fEnvelopeID );
			if ( envObj != nil)
			{
				// First check the prefix in the envelope object
				if ( envObj->GetMessagePrefixLength() != 0 )
				{
					envObj->GetFullMessagePrefix(aMsgPrefix);
					bufPtr	= aMsgPrefix.GetData();
					bufLen	= ::strlen(bufPtr);

					outHdr.Append( bufPtr, bufLen );
				}

				bufPtr = nil;

				// Get the header part (message part zero)
				if ( envObj->GetMsgPartListHeadID() != 0 )
				{
					mp = CMessagePart_10_1::FindByID( envObj->GetMsgPartListHeadID() );
					ThrowIfNULL_(mp);

					bufPtr = mp->GetPartData();
					bufLen = mp->GetDataLength();
				}
				else if (envObj->GetSmallMsgPartID() != 0)
				{
					smallMP = CSmallMsgPart_10_1::FindByID( envObj->GetSmallMsgPartID() );
					ThrowIfNULL_(smallMP);

					bufPtr = smallMP->GetPartData();
					bufLen = smallMP->GetDataLength();
				}

				if ( bufPtr != nil )
				{
					hdrEnd = ::strstr( bufPtr, "\r\n\r\n" );
					if ( hdrEnd != nil )
					{
						bufLen = hdrEnd - bufPtr;
						outHdr.Append( bufPtr, bufLen );
					}
					else
					{
						outHdr.Append( bufPtr, bufLen );
						if ( mp != nil )
						{
							mpID = mp->GetNextMessagePartID();
							mp->Done( mp );

							mp = CMessagePart_10_1::FindByID( envObj->GetMsgPartListHeadID() );
							ThrowIfNULL_( mp );

							bufPtr = mp->GetPartData();
							bufLen = mp->GetDataLength();

							hdrEnd = ::strstr( bufPtr, "\r\n\r\n" );
							if ( hdrEnd != nil )
							{
								bufLen = hdrEnd - bufPtr;
							}

							outHdr.Append( bufPtr, bufLen );
						}
					}
				}

				mp->Done( mp );
				envObj->Done( envObj );
				smallMP->Done( smallMP );
			}
		}
	}

	catch ( ExceptionCode err )
	{
		mp->Done( mp );
		envObj->Done( envObj );
		smallMP->Done( smallMP );
	}

} // Get822Header
