// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//


#include "IMCVCODC.H"
#include "IMUTDLL.H"
#include "IMCVSEND.H"

const TInt8 AsciiToBase64[80]=
	{
	 62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
	 57, 58, 59, 60, 61, -1, -1, -1, 64, -1,
	 -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,
	  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
	 18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
	 -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
	 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
	 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
	};

const TInt8 Base64ToAscii[65]=
	{
	 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
	 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
	 85, 86, 87, 88, 89, 90, 97, 98, 99,100,
	101,102,103,104,105,106,107,108,109,110,
	111,112,113,114,115,116,117,118,119,120,
	121,122, 48, 49, 50, 51, 52, 53, 54, 55,
	 56, 57, 43, 47, 61 
	};

const TInt8 KImcvLookUpStartOffset = 43;
const TUint8 KImcvConvEquals = '=';

const TInt KImMaxLengthForEncodedWordAndExtraEncodingTokens = 9;

// Define the maximum length that one encoded character should occupy.
// For certain encoding schemes such as Japanese, a single unicode character
// may need a multi character string to represent it when it is sent as part
// of an email header. This constant defines a maximum length for that string.
// The value is based on the maximum length for a Japanese character
const TInt KImMaxLengthForEncodedChar = 8;


//---------------------------------------------------------------------------------
//              Class TImNoCodec Functions
//---------------------------------------------------------------------------------


//
TInt TImCodecNull::Encode( const TDesC8& aSrcString, TDes8& rDestString)
	{
	rDestString.Copy(aSrcString);
	return 0;
	}

//	
TBool TImCodecNull::Decode( const TDesC8& aInputLine, TDes8& rOutputLine)
	{
	rOutputLine.Copy(aInputLine);
	return ETrue;
	}



//---------------------------------------------------------------------------------
//              Class TImCodecQP Functions
//
//			Utility class, providing encoding/decoding functions for :
//			Quotable-printable, Base64 & UU.
//			Input/output consisting of 8bit strings.
//---------------------------------------------------------------------------------

//
EXPORT_C TImCodecQP::TImCodecQP() : iPlainCharList(TPtrC8()), iEncodeCharList(TPtrC8())
	{
	iQPCharacter=KImcvEquals;
	}

//
TBool TImCodecQP::SmartBreak( TInt written, const TDesC8& pSource )
	{
	TLex8 source( pSource );

	// only check for whether we should break if the current char is breakable
	if ( !IsBreakable(source.Peek()) )
		return EFalse;

 	// scan ahead looking for the next breakable char.

	source.Inc();
	TBool found = EFalse;
	TInt encodingOffset=(IsPlain(source.Peek()) ? 0 : 2);
	while ( !found && !source.Eos() )
		{
		found = IsBreakable(source.Peek());
		if (!found)
			{
			encodingOffset+=(IsPlain(source.Peek()) ? 0 : 2);
			source.Inc();
			}
		}

	// there's another breakable char before the end of the text 
	// - we need to break now if it's too far away
	// but only if the non-breakable text fits on a line itself.

	return ((written+source.Offset()+encodingOffset) > KMaxIMailBodyLineLength-4);
	}

//
EXPORT_C TInt TImCodecQP::Encode( const TDesC8& aInputLine, TDes8& rOutputLine)
	{
	 // number of characters written to the current line.
	TInt written=0; 

	// Used to check we do not 'fold' a line after encountering an ESC character.
	TInt escapeChar=0;

	// number of characters introduced into the line by the encoding mechanism.
	TInt localPaddingCount=0; 
	
	TChar nextChar=0;

	// buffer used for preparing the hex encoding of a non-printable char
	TBuf8<5> formatted; 
	
	TLex8 lexSource(aInputLine);
	TDes8& ptr = rOutputLine;
	TInt ptrEnd = ptr.MaxLength();

	if (!lexSource.Eos())
		{
		lexSource.Get();
		nextChar = lexSource.Peek();
		lexSource.Inc(-1);
		}

	while (written<KImMailMaxBufferSize-2 && !lexSource.Eos())
		{
		if (written>=KMaxIMailBodyLineLength-3 && !escapeChar)
			{
			// force a soft line break.
			__ASSERT_ALWAYS( ptr.Length()+4< ptr.MaxLength(), gPanic(KPanicDescriptorToSmall) );
			ptr.Append(iQPCharacter);
			ptr.Append(KImcvCRLF);
			written+=3; localPaddingCount+=3;
			break;
			}

		TChar peek = lexSource.Peek();

		if (peek==KImcvESC)
			escapeChar=3;

		if ( SmartBreak(written,lexSource.Remainder()) && !escapeChar)
			{
			AddSoftLineBreak( ptr, localPaddingCount, written);
			break;
			}
		else if ( IsPlain(peek) && peek!=KImcvSP && peek!=KImcvEquals )
			// simple case, printable character
			{
			__ASSERT_ALWAYS( ptr.Length()+1<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			ptr.Append(peek);
			written++;
			}
		else if ( peek==KImcvTab || peek==KImcvSP )
			// Tab or space, need to check for immediate EOL so we can keep the space character
			{
			TChar thisChar =lexSource.Get();
			TChar nextChar =(lexSource.Eos() ? (TChar)0 : lexSource.Get());

			if (nextChar==KImcvCR && lexSource.Peek()==KImcvLF)
				// Encode space, as EOL follows.
				{
				__ASSERT_ALWAYS( ptr.Length()+5<ptrEnd, gPanic(KPanicDescriptorToSmall) );
				formatted.Format( KImcvQPFormatString, (TUint) thisChar );
				formatted.UpperCase();
				ptr.Append( formatted );

				// also skip EOL the characters we just allowed for
				written+=5;
				localPaddingCount+=2;
				break;
				}
			else
				// no EOL, just carry on after the whitespace
				{
				__ASSERT_ALWAYS( ptr.Length()+1<ptrEnd, gPanic(KPanicDescriptorToSmall) );
				ptr.Append( thisChar );
				written++;
				lexSource.Inc(-1);
				if (nextChar)
					lexSource.Inc(-1);
				}
			}
		else if (peek==KImcvCR && nextChar==KImcvLF)
			// Do not encode.
			{
			__ASSERT_ALWAYS( ptr.Length()+2<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			ptr.Append(KImcvCRLF);
			written+=2;
			lexSource.Inc();
			break;
			}
		else if ( (TUint8)peek==iQPCharacter )
			// '=' in source text
			{
			__ASSERT_ALWAYS( ptr.Length()+3<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			ptr.Append( KImcvQPEqualsSign );
			written += 3;
			localPaddingCount+=2;
			}
		else if (peek==KImcvTab || peek==KImcvHyphen || peek==KImcvSP)
			{
			__ASSERT_ALWAYS( ptr.Length()+1<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			ptr.Append( peek );
			written++;
			}
		else
			// non-printable char, must encode with Hex value
			{
			__ASSERT_ALWAYS( ptr.Length()+3<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			formatted.Format( KImcvQPEncoded, (TUint) peek );
			formatted.UpperCase();
			ptr.Append(formatted);
			written += 3;
			localPaddingCount += 2;
			}
		
		// advance to next source character	
		lexSource.Inc();
		if (escapeChar)
			escapeChar--;

		// check whether we should give up without creating all the output
		// non-quotable char to come & we're nearly at max length.

		TChar thisChar = lexSource.Get();

		// just in case we're pointing at the last character
		nextChar = (lexSource.Eos() ? (TChar)0 : lexSource.Peek());
		if (thisChar != 0)
			lexSource.Inc(-1);

		} // while

	return written-localPaddingCount;
	}

//
EXPORT_C TInt TImCodecQP::Decode( const TDesC8& aSrcString, TDes8& rDestString )
	{
	TInt error = KErrNone;

	__ASSERT_DEBUG(aSrcString.Length(), gPanic(KPanicInvalidSMTPLine));

	rDestString = KNullDesC8;

	TPtrC8 source( aSrcString.Ptr(), aSrcString.Length() );
	const TUint8* pSource = source.Ptr();
	const TUint8* pEnd = pSource+aSrcString.Length();
	
	// find out if this is a blank line, if so then we'll add a paragraph delimiter instead
	// assume it's blank and then look for non-blank characters
	// avoid the CRLF at the end of the line (we know it's there thanks to the assertion above)

	TBool blankLine = ETrue; 
	while (pSource < pEnd-2) 
		{
		if (*pSource!=KImcvSP && *pSource!=KImcvTab)
			{
			blankLine = EFalse;
			break;
			}
		pSource++;
		}

	if ( blankLine )
		{
		rDestString.Copy( aSrcString );
		return KErrNone;
		}

	TInt outputLength=0;
	TUint8 loBits;
	TUint8 hiBits;
	TUint8 asciiValue;
	pSource = source.Ptr();	// reset to start of source data
	const TUint8 zero = '0';
	const TUint8 alphaAdjust = 55;  // 'A' is ascii 65 so we need to subtract 55 from 
									// alphabetical hex digits to get their numeric value
	while( pSource < pEnd )
		{
		if (*pSource != iQPCharacter )
			{
			//  Quoted character or Attachment bound, just bung it on & move to the next one
			// *ptr++ = *pSource;
			outputLength++;
			rDestString.Append( *pSource );
			}
		else	// check for encoded character
			{
			// start looking at the next two characters, if they are there.

			if ( pSource+2 < pEnd )
				{
				pSource++;

				// check for '=' at EOL => this is a soft break, so remove it
				if (*pSource != KImcvCR) 
					{
					if(*pSource != KImcvLF) 
						{
					 	 // now decode hex value into ASCII code : hi-order bits come first
						 hiBits = (TUint8)(0x0F & (IsDigit( *pSource ) ? (TUint8)(*pSource-zero) : (TUint8)(*pSource-alphaAdjust)));
						 pSource++;
						 loBits = (TUint8)(0x0F & (IsDigit( *pSource ) ? (TUint8)(*pSource-zero) : (TUint8)(*pSource-alphaAdjust)));
						 asciiValue = (TUint8)( (hiBits<<4) + loBits);
						 // bung the character thus formed onto the decoded string
						 rDestString.Append( asciiValue );
						 // *ptr++ = asciiValue;
						 outputLength++;
						}
					}
				else
					{
					pSource++;
					if(*pSource != KImcvLF)
						{
						error=KErrCorrupt;
						pSource-=2;
						rDestString.Append( *pSource );
						pSource++;
						rDestString.Append( *pSource );
						pSource++;
						outputLength+=2;
						}
					}
				}
			else
				{
				// copy the rest of the data & use up the input string in the process.

				while (pSource < pEnd)
					{
					error=KErrCorrupt; // not QP compliant
					//*ptr++ = *pSource++;
					outputLength++;
					rDestString.Append( *pSource );
					pSource++;
					}
				}
			} // check for '=' char
		
		pSource++;  // next source charactery
	} // while

	rDestString.SetLength(outputLength);

	return error;
	}


// Not used. Remove in 6.3
//
EXPORT_C TInt TImCodecQP::EncodeRichText( const TDesC8& aInputLine, TDes8& rOutputLine)
	{
	TInt written=0;  // number of characters written to the current line
	TInt localPaddingCount=0; // number of characters introduced into the line by the encoding mechanism
	
	// buffer used for preparing the hex encoding of a non-printable char
	TBuf8<5> formatted; 
	
	TLex8 lexSource(aInputLine);
	TDes8& ptr = rOutputLine;
	TInt ptrEnd = ptr.MaxLength();

	while (written < KMaxIMailBodyLineLength-3 &&!lexSource.Eos())
		{
		if ( SmartBreak(written,lexSource.Remainder()) )
			{
			AddSoftLineBreak(ptr, localPaddingCount, written);
			break;
			}

		TChar peek = lexSource.Peek();
		if ( IsPlain(peek)  && peek!=KImcvSP )
			// simple case, printable character
			{
			__ASSERT_ALWAYS( ptr.Length()+1<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			ptr.Append(peek);
			written++;
			}
		else if ( peek==KImcvTab || peek==KImcvSP )
			// Tab or space, need to check for immediate EOL so we can keep the space character
			{
			TChar thisChar = (lexSource.Eos() ? (TChar)0 : lexSource.Get());

			if ( lexSource.Peek()==ETextParagraphDelimiter )
				{
				__ASSERT_ALWAYS( ptr.Length()+5<ptrEnd, gPanic(KPanicDescriptorToSmall) );
				formatted.Format( KImcvQPFormatString, (TUint) thisChar );
				formatted.UpperCase();
				ptr.Append( formatted );
				ptr.Append(KImcvCRLF);

				// also skip EOL the characters we just allowed for
				written+=7;
				localPaddingCount+=4;

				// advance to next source character	
				lexSource.Inc();
				break;
				}
			else
				// no EOL, just carry on after the whitespace
				{
				__ASSERT_ALWAYS( ptr.Length()+1<ptrEnd, gPanic(KPanicDescriptorToSmall) );
				ptr.Append( thisChar );
				written++;
				lexSource.Inc(-1);
				}
			}
		else if ( (TUint8)peek==iQPCharacter )
			// '=' in source text
			{
			//is there enough space to encoded a char and put a soft break on the end.
			if(written > KMaxIMailBodyLineLength-6)
				break;

			__ASSERT_ALWAYS( ptr.Length()+3<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			ptr.Append( KImcvQPEqualsSign );
			written += 3;
			localPaddingCount+=2;
			}
		else if (peek<KImcvSP  && peek!=KImcvLF && peek !=KImcvCR )
			// check for CEditableText control character
			{
			TChar replacement = ReplacementChar(peek);
			if (replacement)
				{
				__ASSERT_ALWAYS( ptr.Length()+1<ptrEnd, gPanic(KPanicDescriptorToSmall) );
				ptr.Append( replacement );
				written++;
				}
			else if ( lexSource.Peek()==ETextParagraphDelimiter )
				{
				ptr.Append(KImcvCRLF);
				written+=2;
				localPaddingCount+=1;
				break;
				}
			else
				localPaddingCount-=1;
			}
		else
			// non-printable char, must encode with Hex value
			{
			//is there enough space to encoded a char and put a soft break on the end
			if(written > KMaxIMailBodyLineLength-6)
				{
				if (written+3 < KMaxIMailBodyLineLength)
					{
					AddSoftLineBreak(ptr, localPaddingCount, written);
					}
				break;
				}
			__ASSERT_ALWAYS( ptr.Length()+3<ptrEnd, gPanic(KPanicDescriptorToSmall) );
			formatted.Format( KImcvQPEncoded, (TUint) peek );
			formatted.UpperCase();
			ptr.Append(formatted);
			written += 3;
			localPaddingCount += 2;
			}
		
		// advance to next source character	
		lexSource.Inc();

		// check whether we should give up without creating all the output
		// non-quotable char to come & we're nearly at max length.

		TChar thisChar = lexSource.Get();
		// just in case we're pointing at the last character
		TChar nextChar = (lexSource.Eos() ? (TChar)0 : lexSource.Peek());
		if (thisChar != 0)
			lexSource.Inc(-1);

		if ( !IsPlain( thisChar ) && (written >= KMaxIMailBodyLineLength-2) 
			|| ( thisChar==KImcvSP ||  thisChar==KImcvTab) 
			&& nextChar==CEditableText::EParagraphDelimiter 
			&& (written >= KMaxIMailBodyLineLength-3) ) // whitespace just before linebreak
			break;
		} // while

	rOutputLine.SetLength( written );

	return 	(written-localPaddingCount);
	}


EXPORT_C TInt TImCodecQP::DecodeRichText( const TDesC8& aSrcString, TDes& rDestString )
	{
	TInt error = KErrNone;

	__ASSERT_DEBUG(aSrcString.Length()>=2&&aSrcString[aSrcString.Length()-2]==KImcvCR&&aSrcString[aSrcString.Length()-1]==KImcvLF, gPanic(KPanicInvalidSMTPLine));

	rDestString = KNullDesC;

	TPtrC8 source( aSrcString.Ptr(), aSrcString.Length() );
	const TUint8* pSource = source.Ptr();
	const TUint8* pEnd = pSource+aSrcString.Length();
	
	// find out if this is a blank line, if so then we'll add a paragraph delimiter instead
	TBool blankLine = ETrue; // assume it's blank and then look for non-blank characters
	while (pSource < pEnd-2) // avoid the CRLF at the end of the line (we know it's there thanks to the assertion above)
		{
		if (*pSource!=KImcvSP && *pSource!=KImcvTab)
			{
			blankLine = EFalse;
			break;
			}
		else
			pSource++;
		}

	if ( blankLine )
		rDestString.Copy( aSrcString )
		;	
	else
		{
		TInt outputLength=0;
		TUint8 loBits;
		TUint8 hiBits;
		TUint8 asciiValue;
		pSource = source.Ptr();	// reset to start of source data
		const TUint8 zero = '0';
		const TUint8 alphaAdjust = 55;  // 'A' is ascii 65 so we need to subtract 55 from 
										// alphabetical hex digits to get their numeric value
		while( pSource < pEnd )
			{
			// check for encoded character
			if (*pSource == iQPCharacter )
				{
				// start looking at the next two characters, if they are there
				if ( pSource+2 < pEnd )
					{
					pSource++;
					// check for '=' at EOL => this is a soft break, so remove it
					if (*pSource != KImcvCR) 
						{
						// now decode hex value into ASCII code : hi-order bits come first
						hiBits = (TUint8)(0x0F & (IsDigit( *pSource ) ? (TUint8)(*pSource-zero) : (TUint8)(*pSource-alphaAdjust)));
						pSource++;
						loBits = (TUint8)(0x0F & (IsDigit( *pSource ) ? (TUint8)(*pSource-zero) : (TUint8)(*pSource-alphaAdjust)));
						asciiValue = (TUint8)( (hiBits<<4) + loBits);
						// bung the character thus formed onto the decoded string
						rDestString.Append( asciiValue );
						// *ptr++ = asciiValue;
						outputLength++;
						}
					else
						{
						pSource++;
						if(*pSource != KImcvLF)
							{
							error=KErrCorrupt;
							pSource-=2;
							rDestString.Append( *pSource );
							pSource++;
							rDestString.Append( *pSource );
							pSource++;
							outputLength+=2;
							}
						}
					}
				else
					{
					// copy the rest of the data & use up the input string in the process
					while (pSource < pEnd)
						{
						error=KErrCorrupt; // not QP compliant
						//*ptr++ = *pSource++;
						outputLength++;
						rDestString.Append( *pSource );
						pSource++;
						}
					}
				} // check for '=' char
			else
				{
				if ( *pSource == KImcvCR && (pSource+1 < pEnd && *(pSource+1) == KImcvLF))
					{
					// this is a hard CRLF, so replace it with a Paragraph Delimiter
					
					// *ptr++ = ETextParagraphDelimiter;
					rDestString.Append( (TChar) CEditableText::EParagraphDelimiter );
					outputLength++;
					
					// ... and skip an extra char (the LF)
					pSource++;
					}
				else
					{
					//  Quoted character or Attachment bound, just bung it on & move to the next one
					// *ptr++ = *pSource;
					outputLength++;
					rDestString.Append( *pSource );
					}
				}
			// next source character
			pSource++;
			} // while
		rDestString.SetLength(outputLength);
		} // else
	return error;
	}


//---------------------------------------------------------------------------------
//              Class TImFileCodec Functions
//---------------------------------------------------------------------------------
	

TInt TImFileCodec::PostfixNextLine(TDes8&  rOutputLine, TInt& rPaddingCount)
	{
	return BlankLine(rOutputLine, rPaddingCount);
	}


TInt TImFileCodec::PrefixNextLineL(TDes8& /* rOutputLine*/, const TFileName& /*aName*/, TInt& /*rPaddingCount*/)
	{
	return KImAttFinished;
	}


void TImFileCodec::Initialise()
	{
	iPostfixState=0;
	iPrefixState=0;
	}

//---------------------------------------------------------------------------------
//              Class TImCodecB64 Functions
//---------------------------------------------------------------------------------


EXPORT_C TImCodecB64::TImCodecB64(): iShiftStored(0), iMaskShiftStored(ESix)
	{}


// Returns ETrue if aSrcString is not long enough to decode fully, resulting in the storage of
// the last character and requiring another aSrcString (poss 0 length) to be passed to it to 
// clear this character. 
// Returns EFalse if the line was decoded OK or the end of the encoded file is reached ie "="
//
EXPORT_C TBool TImCodecB64::Decode(const TDesC8& aSrcString, TDes8& rDestString)
	{
	TInt decodedInt=0;
	TInt8 offsetChar=0;
	TUint8 decodedChar=0;
	 
	// Clears the destination string
	rDestString.Zero();

	// Initialise variables
	const TUint8* srcStringPtr=aSrcString.Ptr();
	const TUint8* srcStringEnd=aSrcString.Length()+srcStringPtr;
	TUint8* destStringPtr=(TUint8*)rDestString.Ptr();
	TUint8* destStringPtrBase=destStringPtr;

	TInt maskShift=iMaskShiftStored;
	TInt shiftStorage=iShiftStored;
	
	// Main character process loop
	while(srcStringPtr<srcStringEnd)	
		{
		offsetChar=(TInt8)(*srcStringPtr-KImcvLookUpStartOffset);
		srcStringPtr++;

		// Check for valid B64 character		
		if((offsetChar>=0)&&(offsetChar<80))
			{
			// Read in next character and B64 decode
			decodedInt=AsciiToBase64[offsetChar];

			// Exits when a PAD char is reached
			if(decodedInt==EPadChar)
				{
				rDestString.SetLength((TInt)(destStringPtr-destStringPtrBase));
				return EFalse;
				}

			// Ensures the first 2 chars of 4 are received before processing
			if(maskShift==ESix)
				maskShift=EFour;
			else
				{
				shiftStorage=shiftStorage<<ESix;
				shiftStorage=shiftStorage|decodedInt;
				decodedChar=(TUint8)((shiftStorage>>maskShift)&EEightBitMask);
				
				if((maskShift-=ETwo)<EZero)
					maskShift=ESix; 
				
				*destStringPtr++=decodedChar;
				}
			shiftStorage=decodedInt;
			}
		}
	iShiftStored=shiftStorage;
	iMaskShiftStored=maskShift;
	
	rDestString.SetLength((TInt)(destStringPtr-destStringPtrBase));
	
	return maskShift<ESix;
	}

EXPORT_C TInt TImCodecB64::Encode(const TDesC8& aSrcString, TDes8& rDestString)
	{
	return DoEncode(aSrcString, rDestString, EFalse);
	}

EXPORT_C void TImCodecB64::Initialise()
	{
	iMaskShiftStored=ESix;
	}

TInt TImCodecB64::DoEncode(const TDesC8& aSrcString, TDes8& rDestString, TBool aInsertLineBreaks)
	{
	// Clears the destination string
	rDestString.Zero();
	
	// Initialise variables
	const TUint8* srcStringPtr=aSrcString.Ptr();
	const TUint8* srcStringEnd=aSrcString.Length()+srcStringPtr;
	TUint8* destStringPtr=(TUint8*)rDestString.Ptr();
	TUint8* destStringPtrBase=destStringPtr;

	TInt character=0;
	TUint8 encodedChar=0;
	TInt charStorage=0;
 	TInt maskShift=EZero;
	TInt destStringCharNum = 0;

	while(srcStringPtr<=srcStringEnd)
		{
		// maskShift is used as a char read counter
		if(maskShift==ESix)
			{
			// If the 3rd char read is also the last char then the while loop
			// is broken on the next check.
			if(srcStringPtr==srcStringEnd)
				srcStringPtr++;
			maskShift=EZero;
			character=0;   
			}
		else
			{
			if(srcStringPtr==srcStringEnd)
				character=0;
			else
				character=*srcStringPtr;

			srcStringPtr++;
			// Shifts charStorage ready for the next char
			charStorage=charStorage<<8;
			maskShift+=ETwo;
			}
		charStorage=charStorage|character;
		// Shifts the mask to the correct bit location
		// Masks (AND's) the valid bits from charStorage
		// Shifts the valid bits into the low order 8bits
		// Converts to BASE64 char, Casts the result to an unsigned char (which it should be ?....I hope)
		encodedChar=(TUint8)Base64ToAscii[((charStorage>>maskShift)&ESixBitMask)];

		*destStringPtr++=encodedChar;
		destStringCharNum++;

		// Add a CRLF every KMaxB64EncodedCharsPerLine characters so as not to exceed the line length
		// limitation specified in RFC 2822.
		if (aInsertLineBreaks && destStringCharNum == KMaxB64EncodedCharsPerLine)
			{
			destStringCharNum = 0;
			*destStringPtr++ = '\r';
			*destStringPtr++ = '\n';
			}
		}
	
	// Check for not enough chars and pad if required
	if (maskShift==EFour)
		{
		*destStringPtr++=KImcvConvEquals;
		*destStringPtr++=KImcvConvEquals;
		}
	else
		if(maskShift==ESix)
			*destStringPtr++=KImcvConvEquals;	
			
	rDestString.SetLength((TInt)(destStringPtr-destStringPtrBase));
	return ((TInt)(srcStringPtr-srcStringEnd));
	}

TInt TImCodecB64WithLineBreaks::Encode(const TDesC8& aSrcString, TDes8& rDestString)
   	{
	return DoEncode(aSrcString, rDestString, ETrue);
   	}

//---------------------------------------------------------------------------------
//              Class TImCodecUU Functions
//---------------------------------------------------------------------------------


EXPORT_C TImCodecUU::TImCodecUU()
	{}


// returns ETrue if aSrcString is not long enough, relative to its line length character.
// or if an invalid UU encoded character is found.
// returns EFalse if the line was decoded OK
//
EXPORT_C TBool TImCodecUU::Decode(const TDesC8& aSrcString, TDes8& rDestString)
	{
	// Clears the destination string
	rDestString.Zero();

	// Initialise variables
	const TUint8* srcStringPtr=aSrcString.Ptr();
	TInt numEncChrs=*srcStringPtr++-ESpace;
	TInt maskShift=ESix;
	TInt numDecChrs=0;
	TInt shiftStorage=0;
	TInt character=0;
	TUint8 decodedChar=0;
	
	// Checks if the srcString is atleast long enough to decode.
	if((numEncChrs*4)>((aSrcString.Length()-1)*3)) return ETrue;

	// Main character process loop
	while(numDecChrs<numEncChrs)	
		{
		// Read in next character
		character=*srcStringPtr++;

		// Check for UU character validity
		if((character<ESpace)||(character>EBackQuote)) return ETrue;

		// Protocol thing		
		if(character==EBackQuote)
			character=ESpace;

		character-=ESpace;
		
		// Ensures the first 2 chars of 4 are received before processing
		if(maskShift==ESix)
			maskShift=EFour;
		else
			{
			shiftStorage=shiftStorage<<ESix;
			shiftStorage=shiftStorage|character;

			decodedChar=(TUint8)((shiftStorage>>maskShift)&EEightBitMask);
			maskShift=((maskShift==EZero)? ESix:(maskShift-ETwo)); 
				
			rDestString.Append(decodedChar);
			numDecChrs++;
			}
		shiftStorage=character;
		}
	return EFalse;
	}


EXPORT_C TInt TImCodecUU::Encode(const TDesC8& aSrcString, TDes8& rDestString)
	{
	// Clears the destination string
	rDestString.Zero();
	
	// Initialise variables
	const TUint8* srcStringPtr=aSrcString.Ptr();
	const TInt srcLength=aSrcString.Length();
	const TUint8* srcStringEnd=srcStringPtr+srcLength;
	TInt charStorage=0;
	TInt maskShift=EZero;
	TInt character=0;
	TUint8 encodedChar=0;
	TInt destStringCharNum = 0;
	TInt srcStringBytesRemaining = 0;

   	while(srcStringPtr<=srcStringEnd)
   		{
		// Add unencoded length to start of line as per UU specification.
		if (destStringCharNum == 0)
			{
			srcStringBytesRemaining = srcStringEnd - srcStringPtr;
			if ( srcStringBytesRemaining < KMaxUUUnEncodedCharsPerLine ) 
				rDestString.Append(static_cast<TUint8>(srcStringBytesRemaining) + ESpace);
			else
				rDestString.Append(KMaxUUUnEncodedCharsPerLine + ESpace);
			}

		if(maskShift==ESix)
			{
			// If the 3rd char read is also the last char then the while loop
			// is broken on the next check.
			if(srcStringPtr==srcStringEnd)
				srcStringPtr++;
			character=0;
			maskShift=EZero;
			}
		else
			{
			if(srcStringPtr==srcStringEnd)
				character=0;
			else
				character=*srcStringPtr;

			srcStringPtr++;
			charStorage=charStorage<<8;
			maskShift+=ETwo;
			}
		charStorage=charStorage|character;
		// Shifts the mask to the correct bit location
		// Masks (AND's) the valid bits
		// Shifts the valid bits into the low order 8bits
		// Adds 32 (SPACE), Casts the result to an unsigned char (which it should be ?....I hope)
		encodedChar=(TUint8)(((charStorage>>maskShift)&ESixBitMask)+ESpace);

		// Protocol thing		
		if(encodedChar==ESpace)
			encodedChar=EBackQuote;

		rDestString.Append(encodedChar);
		++destStringCharNum;

		// Add a CRLF every KMaxUUEncodedCharsPerLine characters so as not to exceed the line length
		// limitation recommended for UUE.
		if (destStringCharNum == KMaxUUEncodedCharsPerLine)
			{
			destStringCharNum = 0;
			rDestString.Append(KImcvCRLF);
			}
		}

	return ((TInt)(srcStringPtr-srcStringEnd));
	}


TInt TImCodecUU::PrefixNextLineL( TDes8& rOutputLine, const TFileName& aName, TInt& rPaddingCount )
	{
	rOutputLine.Append( KImcvUueStart );
	rOutputLine.Append( KImcvUue644 );
	AppendFilenameL( rOutputLine, aName );
	rPaddingCount=rOutputLine.Length();
	return 1;
	}

TInt TImCodecUU::PostfixNextLine( TDes8& rOutputLine, TInt& rPaddingCount )
	{
	switch( iPostfixState )
		{
		case EInvertedComma:
			rOutputLine.Append( KImcvInvertedComma );
			break;
		case EEndString:
			rOutputLine = KImcvUueEnd;
			break;
		}
	
	iPostfixState++;
	rPaddingCount=rOutputLine.Length();

	return (iPostfixState==EEndOfPostfix ? KImAttFinished : KErrNone);
	}



void TImCodecUU::AppendFilenameL( TDes8& rOutputLine, const TFileName& aName)
	{
	HBufC8* buf = HBufC8::NewLC( aName.Length() );
	buf->Des().Copy( aName );
	rOutputLine.Append(KImcvSpace);
	rOutputLine.Append( *buf );
	CleanupStack::PopAndDestroy(); // buf
	}




//---------------------------------------------------------------------------------
//              Class CImConvertHeader Functions
//
// Utility class, providing functionality for encoding/decoding header fields.
// Requires use of Charconv "Wrapper" class.
//------------------------------------------------------------------------------

/**
@publishedPartner
@released
*/
EXPORT_C CImConvertHeader* CImConvertHeader::NewL(CImConvertCharconv& aConverter)
	{
	CImConvertHeader* self = new (ELeave) CImConvertHeader (aConverter);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CImConvertHeader::CImConvertHeader(CImConvertCharconv& aConverter) : 
	iCharConv (aConverter)
	{
	}

CImConvertHeader::~CImConvertHeader() 
	{
	iHeader = NULL;
	}

void CImConvertHeader::ConstructL() 
	{
	iQPCodec.AddEncodeChar(KImcvEncodeCharacterList);

	// Default setting.
	iCharConv.PrepareToConvertToFromOurCharsetL(iCharConv.DefaultCharset());
	}

EXPORT_C void CImConvertHeader::SetMessageType(TBool aIsMIME)
	{
	isMIMEMessageHeader=aIsMIME;
	}

EXPORT_C void CImConvertHeader::SetOverrideCharset(TUint aCharset)
/**
Sets the override character set to use when decoding the 8 bit data. Zero
indicates the charset not to be used.

@param aCharset
The new character set to use as an overriding character set.
*/
	{
	iOverrideCharset = aCharset;
	}

EXPORT_C TUint CImConvertHeader::OverrideCharset() const
/**
Retrieves the override character set that will be used when decoding the 8 bit
data. A character set value of zero indicates no overriding character set will
be used.
*/
	{
	return iOverrideCharset;
	}

//------------------------------------------------------------------------------

EXPORT_C void CImConvertHeader::DecodeAllHeaderFieldsL(CImHeader& rHeader)
	{
	iHeader = &rHeader;

	iHeader->SaveEncodedHeadersL();
	
	// If Header field does not have a Mime info, but header has encoded words, then 
	// we need to parse it accordingly. 
	TBool nonMime = EFalse; 
	if (!isMIMEMessageHeader) 
		{ 
		isMIMEMessageHeader = ETrue; 
		nonMime = ETrue; 
		}

	// FROM:
	iEncodingInfo.SetField(TImHeaderEncodingInfo::EFrom);
	DecodeFieldL( rHeader.From());

	// SUBJECT:
	iEncodingInfo.SetField(TImHeaderEncodingInfo::ESubject);
	DecodeFieldL( rHeader.Subject());

	// REPLYTO:
	iEncodingInfo.SetField(TImHeaderEncodingInfo::EReplyTo);
	DecodeFieldL( rHeader.ReplyTo());

	// TO:
	iEncodingInfo.SetField(TImHeaderEncodingInfo::ETo);
	DecodeRecipientListL(rHeader.ToRecipients());

	// CC:
	iEncodingInfo.SetField(TImHeaderEncodingInfo::ECc);
	DecodeRecipientListL(rHeader.CcRecipients());

	// BCC:
	iEncodingInfo.SetField(TImHeaderEncodingInfo::EBcc);
	DecodeRecipientListL(rHeader.BccRecipients());

	if(nonMime) 
		{ 
		isMIMEMessageHeader = EFalse; 
		} 	
		
	iHeader = NULL; // iHeader is not used outside non-exported function DecodeFieldL
	}


void CImConvertHeader::DecodeFieldL(const TDesC& aField)
	{
	HBufC* buf = HBufC::NewLC(aField.Size());
	TPtr ptr(buf->Des());
	DecodeHeaderFieldL(aField, ptr);

	switch (iEncodingInfo.Field())
		{
	case TImHeaderEncodingInfo::EFrom:
		iHeader->SetFromL(*buf);
		break;
	case TImHeaderEncodingInfo::ESubject:
		iHeader->SetSubjectL(*buf);
		break;
	case TImHeaderEncodingInfo::EReplyTo:
		iHeader->SetReplyToL(*buf);
		break;
	default:
		__ASSERT_DEBUG(1, User::Invariant());
		}
	CleanupStack::PopAndDestroy( ); // buf
	}


void CImConvertHeader::DecodeRecipientListL(CDesCArray& aArray)
	{
	HBufC* buf = NULL; 
	TInt i = aArray.Count();
	while (i--)
		{
		iEncodingInfo.SetArrayValue(i);

		if (!(aArray[i]).Length())
			continue; // Should really delete them some how

		buf = HBufC::NewLC(aArray[i].Length());
		TPtr ptr(buf->Des());

		DecodeHeaderFieldL(aArray[i], ptr);
		aArray.Delete(i);
		aArray.InsertL(i, *buf);

		CleanupStack::PopAndDestroy( ); // buf
		}
	}

/**
Search for one or more encoded words in aLine

@publishedPartner
@released
*/

 EXPORT_C void CImConvertHeader::DecodeHeaderFieldL(const TDesC16& aBufIn, TDes& aBufOut)

	{
	// input should be 8bit, possible loss of data otherwise.
	HBufC8* buffer8 = HBufC8::NewLC( aBufIn.Length() );
	buffer8->Des().Copy( aBufIn );

	if (isMIMEMessageHeader)
    	
    	DecodeHeaderFieldL( *buffer8, aBufOut);

	else
		 DecodeNonMIMEHeaderFieldL( *buffer8, aBufOut);



	CleanupStack::PopAndDestroy( ); // buffer8
	}

/**
Search for one or more encoded words in aLine

@publishedPartner
@released
*/
 EXPORT_C void CImConvertHeader::DecodeHeaderFieldL(const TDesC8& aBufIn, TDes& aBufOut)
	{
	__ASSERT_DEBUG(aBufIn.Size() <= aBufOut.MaxSize(), User::Invariant());
	aBufOut.SetLength(0);

	TBool wordExists = ETrue;
	TBool decoded = EFalse;
	TInt rem = 0;
	TPtrC8 encodedWord8(aBufIn);
	TChar QMark = KImcvQuestionMark;
	TInt spaces = 0;
	TLex8 lex(aBufIn);

	while (wordExists)
		{
		wordExists = EFalse;

		lex.SkipSpaceAndMark();
		spaces = lex.TokenLength();

		// Get next Encoded word
		while (lex.Peek() != KImcvEquals && !lex.Eos())
			{
			lex.Inc();
			}

		if (lex.Eos())
			{
			break;	
			}

		lex.Inc();
		if (lex.Peek() == QMark)
			{
			// We have the start of an encoded word..
			// Copy over all data marked prior to word
			lex.Inc(-1);

			if (spaces==lex.TokenLength())
				{
				// contiguous encoded-words, spaces removed in decoded word.
				iEncodingInfo.SetAddSpace(ETrue);
				}
			else
				{
				do
					{
					lex.Inc(-1);
					}	
				while (lex.Peek()==KImcvSpaceChar || lex.Peek()==KImcvTab);
				lex.Inc(); 	
				if (!lex.Eos() && 
					(lex.Peek()==KImcvSpaceChar || lex.Peek()==KImcvTab) ) // Get back to space
					{
					lex.Inc();	
					}
				TPtrC8 marked = lex.MarkedToken();
				Append(aBufOut, marked);

				}

			lex.SkipSpaceAndMark(); // Start of encoded word

			//--Need to check two question marks within encoded word --
			lex.Inc(2);
			while ( lex.Peek() != QMark && !lex.Eos() )
				{
				lex.Inc();
				}
			
			if (lex.Peek() != QMark)
				{
				break; // incorrectly formed encoded-word
				}
			
			if (!lex.Eos())
				{
				lex.Inc();
				}
			else
				{
				break;	
				}
				
			if (!lex.Eos())
				{
				lex.Inc();
				}				
			else
				{
				break;
				}
				
			if (!lex.Eos() && lex.Peek() != QMark)
				{
				break; // incorrectly formed encoded-word
				}
				
			lex.Inc();
			
			while ( lex.Peek() != QMark && !lex.Eos() )
				{
				lex.Inc();
				}
			
			if (lex.Peek() != QMark)
				{
				break; // incorrectly formed encoded-word
				}
			
			if (!lex.Eos())
				{
				lex.Inc();
				}
			else
				{
				break;	
				}
			
			if (lex.Peek() != KImcvEquals)
				{
				break; // incorrectly formed encoded-word
				}
				
			if (!lex.Eos())
				{
				lex.Inc();
				}
			else
				{
				break;	
				}
			//---------------------------------------------------------

			encodedWord8.Set(lex.MarkedToken());

			wordExists = ETrue;
			}

		if (wordExists)
			{
			// If found ...
			decoded=ETrue;
			HBufC* decodedWord = HBufC::NewLC(encodedWord8.Length());

			TPtr decodedPtr(decodedWord->Des());
 			if ( DecodeWordL(encodedWord8, decodedPtr, rem) )
				{
				// replace decoded string with new string
				TUint offset = aBufOut.Length();


				// Store encoding information in Header object.
				iEncodingInfo.SetEncodedLength(lex.TokenLength());
				iEncodingInfo.SetOffset(offset);
				iEncodingInfo.SetLength(decodedPtr.Length());

				if (iHeader)
					{
					iHeader->AddEncodingInfoL(iEncodingInfo);			
					}
					
				}

			aBufOut.Append( decodedPtr );

			CleanupStack::PopAndDestroy(); //  decodedWord;
			}	
		} // while

	lex.UnGetToMark();
	if (decoded && lex.MarkedOffset())
		{
		lex.UnGet();
		if (lex.Peek()==KImcvSpaceChar || lex.Peek()==KImcvTab)
			{
			lex.Mark();
			}			
		}

	if(decoded==EFalse)
		{
		DecodeNonMIMEHeaderFieldL(aBufIn,aBufOut);

		}
 	else
		{
		TPtrC8 marked = lex.RemainderFromMark();
  	    Append(aBufOut, marked);

 		}
}


// For non-MIME case, System default being ASCII normally 
//
 EXPORT_C void CImConvertHeader::DecodeNonMIMEHeaderFieldL(const TDesC8& aBufIn, TDes& aBufOut)

	{
	__ASSERT_DEBUG(aBufIn.Size() <= aBufOut.MaxSize(), User::Invariant());

	TUint charset;

	(iOverrideCharset != 0) ? 
		(charset = iOverrideCharset) : (charset = iCharConv.DefaultCharset());

	if (iCharConv.PrepareToConvertToFromOurCharsetL(charset)== EFalse)
		{
		// Charset not available, don't decode.
		aBufOut.Copy(aBufIn);
		}

	// Character set conversion
	TInt unconvertedChars;
	TInt pos;
	iCharConv.ConvertToOurCharsetL(aBufIn, aBufOut, unconvertedChars, pos);

}



// aBufIn is of the form "=?" "charset" "?" "Encoding" "?" "Encoding Text" "?="
// Decode the 'Encoding Text' based on the information in 'Encoding' and 'charset'
// and pass back in aBufOut

//
  TBool CImConvertHeader::DecodeWordL(const TDesC8& aBufIn, TDes& aBufOut,  TInt rRemainder)
	{
	rRemainder=0;
      HBufC8* dataBuf8 = HBufC8::NewLC(aBufOut.MaxLength());


	const TUint8* ptr = aBufIn.Ptr();	

	__ASSERT_DEBUG( (*ptr == KImcvEquals) && (*(ptr+1) == KImcvQuestionMark),User::Invariant());

	// Extract the charset string and store
	TInt i;
	for(i = 2; *(ptr+i) != KImcvQuestionMark ; i++)
		;

	TUint uid;
	if (iOverrideCharset != 0)
		{
		uid = iOverrideCharset;
		}
	else
		{
		TPtrC8 buf8(ptr+2, i-2);
		uid = iCharConv.GetMimeCharsetUidL(buf8);
		}

	if (!uid || !iCharConv.PrepareToConvertToFromOurCharsetL(uid))
		{
		// Charset not available, dont decode.
        aBufOut.Copy(aBufIn);
 	  	CleanupStack::PopAndDestroy(dataBuf8); 
		return EFalse;
		}

	iEncodingInfo.SetCharsetUid(uid);

	// Extract the Encoding string and store
	for(i++; *(ptr+i) != KImcvQuestionMark ; i++)
		;
	TPtrC8 buf28(ptr+i-1, 1);
	iEncodingInfo.SetEncodingType(buf28); 	// Currently encoding is either Q or B


	// Now at the encoding text
	TInt j;
	for(j = i+1; *(ptr+j) != KImcvQuestionMark ; j++)
		;

	// Intermediate buffer for decoding, prior to converting

      HBufC8* decodeBuf8 = HBufC8::NewLC(aBufOut.MaxLength());

	decodeBuf8->Des().Copy( aBufIn.Mid(i+1, j -i-1));

   	// Depending on type of encoding Q or B, decode.

	TPtr8 dataPtr8(dataBuf8->Des());

	switch (iEncodingInfo.EncodingType())
		{
		case TImHeaderEncodingInfo::EBase64 :
			iB64Codec.Decode( *decodeBuf8, dataPtr8 );
			break;
		case TImHeaderEncodingInfo::EQP :
			// Replace all underscores in text with SPACE.
			iQPCodec.Decode( *decodeBuf8, dataPtr8 );

			i=dataPtr8.Length();
			while (i--)
				{
				if (dataPtr8[i] == KImcvUnderScore)
					dataPtr8[i] = KImcvSpaceChar;	
				}
			break;
		default:
			dataBuf8->Des().Copy(*decodeBuf8); 
			break;
		}

	// Character set conversion
	TInt unconvertedChars, pos;
	rRemainder = iCharConv.ConvertToOurCharsetL(*dataBuf8, aBufOut, unconvertedChars, pos);

	CleanupStack::PopAndDestroy(decodeBuf8);
	CleanupStack::PopAndDestroy(dataBuf8); 
	return (rRemainder < 0) ? EFalse:ETrue;
	}
/**
Convert function used for non-MIME messages.

@publishedPartner
@released
*/
EXPORT_C void CImConvertHeader::ConvertHeaderFieldL(const TDesC16& aBufIn, RBuf8& aBufOut, TBool aIsAddressField)
	{
	// Don't make use of the unconverted char info. 
	//Assume text has been checked for non ascii characters. 

	// Need to identify the email address
	TInt start=0;
	TInt end=0;

	if (ExtractTextToEncode(aBufIn, start, end, aIsAddressField)==EFalse)
	    aBufOut.Copy(aBufIn);
	else
		{
		// clear out buffer
		aBufOut.Zero();
		TInt unconvertedChars;
		TInt pos;
		iCharConv.ConvertFromOurCharsetL( aBufIn.Mid(start, end-start+1), aBufOut, unconvertedChars, pos);
  
		if (start>0)
			{
			// Copy over text prior to bit needing encoding
			aBufOut.ReAllocL(aBufOut.Length() + aBufIn.Length());
			Insert( aBufOut, aBufIn.Left(start));

			}
		
		if (end<aBufIn.Length())
			{
			// Copy over text after bit needing encoding
		 
            aBufOut.ReAllocL(aBufOut.Length() + (aBufIn.Length()-end-1));
			aBufOut.Append(aBufIn.Right(aBufIn.Length()-end-1));

			}
		}
	}


/**
@publishedPartner
@released
*/
EXPORT_C void CImConvertHeader::EncodeHeaderFieldL(const TDesC& aBufIn, RBuf8& aBufOut,				
                CArrayFix<TImHeaderEncodingInfo>* aInfoArray, TInt aState, TInt aArrayVal)
	{
	// In case not requiring encoding, already ascii
   // allocate enough space to copy data
	aBufOut.ReAllocL(aBufIn.Length());
	aBufOut.Copy(aBufIn);

	for (TInt i=0; i < aInfoArray->Count(); i++)
		{
		if ( (aInfoArray->At(i).Field() == (TImHeaderEncodingInfo::TFieldList) aState)
								&& ((*aInfoArray)[i].ArrayValue() == (TInt)aArrayVal) )
			{
			GetCharsetAndEncodeDataL( aBufIn, aBufOut, (*aInfoArray)[i]);
			}
		}
	}

/**
 This function has to encode without any prestored information.
 Encodes from UNICODE, to UTF-7

@publishedPartner
@released
*/
EXPORT_C void CImConvertHeader::EncodeHeaderFieldL(const TDesC& aBufIn, RBuf8&  aBufOut,
				                                   const TUint aCharset, const TImHeaderEncodingInfo::TEncodingType aType, TBool aIsAddressField)
	{
	TImHeaderEncodingInfo infoArray;

	aBufOut.ReAllocL(aBufIn.Length());
	aBufOut.Copy(aBufIn);

	infoArray.SetCharsetUid(aCharset);
	infoArray.SetEncodingType(aType);
	
	TInt startOfEncoding = 0;
	TInt endOfEncoding = 0;
	if(ExtractTextToEncode(aBufIn, startOfEncoding, endOfEncoding, aIsAddressField))
		{
		infoArray.SetOffset(startOfEncoding);
		infoArray.SetLength(endOfEncoding - startOfEncoding + 1);
		GetCharsetAndEncodeDataL(aBufIn, aBufOut, infoArray);	
		}
	}

// Return the offset in the text buffer for the text portion of the header field
//
TBool CImConvertHeader::ExtractTextToEncode(const TDesC& aBufIn, TInt& rStart, TInt& rEnd, TBool aIsAddressField)
	{
	TInt startOfEncoding = 0;
	TInt endOfEncoding = 0;
	TBool mustEncode = EFalse;
	TBool mustEncodeThisWord=EFalse;

	// Check aField for non ascii characters - encode. Return in rEncodedField.

	TInt len=aBufIn.Length();
	TInt i=0;
	for (;i < len;i++)
		{
		TChar inChar = aBufIn[i];
		if (inChar==KImcvSpaceChar || inChar==KImcvQuote || inChar==KImcvTab)
			{
			if (mustEncodeThisWord)
				endOfEncoding=i-1;
			else if (!mustEncode)
				startOfEncoding=i+1; 
			mustEncodeThisWord=EFalse;
			}
		else if (inChar==KImcvLeftChevron && aIsAddressField)
			{
			if (mustEncodeThisWord)
				{
				endOfEncoding=i-1;
				mustEncodeThisWord=EFalse;
				}
			if (mustEncode)
				break; // already have word to encode, now at an address
			}
		else if (inChar==KImcvRightChevron && aIsAddressField)
			{
			startOfEncoding=i+1; 
			}
		else if ( !IsAscii(inChar) )
			{
			mustEncode = ETrue;
			mustEncodeThisWord=ETrue;
			}
		}

	if (mustEncodeThisWord)
		endOfEncoding=i-1;

	rStart=startOfEncoding;
	rEnd=endOfEncoding;
	return mustEncode;
	}
		                                               
void CImConvertHeader::GetCharsetAndEncodeDataL(const TDesC&  aBufIn,
									RBuf8& aBufOut, TImHeaderEncodingInfo& aInfo)
	{
	// Work out which character set to use, and get a string representation
	// of it.
	HBufC8* charsetName=iCharConv.GetMimeCharsetTextStringL(aInfo.CharsetUid());

	TUint charset=aInfo.CharsetUid();
	if (charset==KCharacterSetIdentifierIso88591)
		charset=KCharacterSetIdentifierCodePage1252;
	
	if (!charsetName || !iCharConv.PrepareToConvertToFromOurCharsetL(charset))
		{
		delete charsetName; 
		charsetName=NULL;

		// Problem with charset Uid, use default character set.
		charset=iCharConv.DefaultCharset();
		aInfo.SetCharsetUid(charset);
		charsetName=iCharConv.GetMimeCharsetTextStringL(charset);
		if (charset==KCharacterSetIdentifierIso88591)
			charset=KCharacterSetIdentifierCodePage1252;
		__ASSERT_ALWAYS(iCharConv.PrepareToConvertToFromOurCharsetL(charset), User::Invariant());
		}

	CleanupStack::PushL(charsetName);
	__ASSERT_ALWAYS(charsetName, gPanic(KPanicInvalidDefaultCharacterSet) );

	// Create a buffer to use to store the encoded word
	HBufC8* encodedWordBuffer = HBufC8::NewL(aBufOut.MaxSize());
	RBuf8 encodedWord(encodedWordBuffer);
	encodedWord.CleanupClosePushL();

	EncodeWordL(aBufIn, aBufOut, aInfo, *charsetName, encodedWord);

	CleanupStack::PopAndDestroy(2, charsetName);  // charset name, encoded word
	}


void CImConvertHeader::EncodeWordL(const TDesC& aBufIn, 
								   RBuf8& aBufOut, TImHeaderEncodingInfo& aInfo,
								   const TDesC8& aCharsetName, RBuf8& aEncodedWord)
	{
	TInt max = KEncodedWordMaxLength - KMaxEncodedInformationLength -
		aCharsetName.Length() - KMaxHeaderFieldNameLength;

	TBool splitAndRetry = ETrue;

	// We will convert the word to the required character set, and perform any
	// encoding (base64, quoted-printable) required. At each stage we check whether
	// the buffer has exceeded the maximum length allowed for a line. If so, we
	// cut the buffer into two halves and encode each half separately.

	if (aInfo.Length() <= max)
		{
		// Identify the part of the input buffer that will be encoded
		TPtrC data = aBufIn.Mid(aInfo.Offset(), aInfo.Length());

		// Perform the conversion to the required character set
		DoCharsetConversionL(data, aEncodedWord);

		if (aEncodedWord.Length() <= max)
			{
			// Perform any base64 / quoted printable encoding
			DoEncodingL(aEncodedWord, aInfo);

			if (aEncodedWord.Length() <= max)
				{
				// We have managed to do all the steps without the buffer exceeding
				// the maximum line length
	  			splitAndRetry = EFalse;
				}
			}
		}

	if (splitAndRetry)
		{
		TInt len = aInfo.Length()/2;
		TUint16 offset = (TUint16) aInfo.Offset();
		TUint16 length = (TUint16) aInfo.Length();

		aInfo.SetOffset(offset+len);
		aInfo.SetLength(length-len);
		EncodeWordL(aBufIn, aBufOut, aInfo, aCharsetName, aEncodedWord);
		aBufOut.ReAllocL(aBufOut.MaxLength()+1);
		aBufOut.Insert(offset+len,KImcvSpace);

		aInfo.SetOffset(offset);
		aInfo.SetLength(len);
		EncodeWordL(aBufIn, aBufOut, aInfo, aCharsetName, aEncodedWord);

		aInfo.SetLength(length);
		}
	else
		{
		// Delete unencoded data, prior to putting in encoded data
		aBufOut.Delete(aInfo.Offset(), aInfo.Length());
		aBufOut.ReAllocL(aBufOut.MaxLength()-aInfo.Length());
		AddEncodedWordInfoL(aEncodedWord, aBufOut, aInfo, aCharsetName);
		aEncodedWord.Zero();
		}
	}


TBool CImConvertHeader::DoCharsetConversionL(const TDesC& aDataToConvert, RBuf8& aEncodedWord)
	{
	TBool convertOk = ETrue;

	TInt unconvertedChars, pos;
	TInt excessChars;
	TInt lastExcessChars = 0;
	TBool failNextTimeExcessCharsStaySame = EFalse;
	TBool finishedConvert = EFalse;

	while (!finishedConvert)
		{
		excessChars = iCharConv.ConvertFromOurCharsetL(aDataToConvert, aEncodedWord, unconvertedChars, pos);

		if (excessChars == 0)
			{
			finishedConvert = ETrue;
			}
		else if (excessChars == CCnvCharacterSetConverter::EErrorIllFormedInput)
			{
			User::Leave(KErrCorrupt);
			}
		else
			{
			// We did not convert all the characters in the buffer. This is
			// probably because the output buffer is not large enough.
			// If the number of characters left unconverted this time is the same
			// as last time, then perhaps the conversion is failing for other
			// reasons. To avoid getting stuck like this forever, we will only
			// allow this to happen once.

			if (excessChars == lastExcessChars)
				{
				if (failNextTimeExcessCharsStaySame)
					{
					finishedConvert = ETrue;
					convertOk = EFalse;
					}
				else
					{
					failNextTimeExcessCharsStaySame = ETrue;
					}
				}
			else
				{
					failNextTimeExcessCharsStaySame = EFalse;
				}

			// If we are going around again, increase the size of the output buffer
			if (!finishedConvert)
				{
				aEncodedWord.ReAllocL(aEncodedWord.MaxSize() + KImMaxLengthForEncodedChar);
				lastExcessChars = excessChars;
				}
			}
		}

	return convertOk;
	}

void CImConvertHeader::DoEncodingL(RBuf8& aEncodedWord, TImHeaderEncodingInfo& aInfo)
	{
	// Take a copy of the encoded word so far. This will be used as the
	// input to the encoding routines
	HBufC8* dataToEncode = HBufC8::NewLC(aEncodedWord.Length());
	dataToEncode->Des().Copy(aEncodedWord);
	aEncodedWord.Zero();

	if (aInfo.EncodingType() == TImHeaderEncodingInfo::EBase64)
		{
		// Increase the size of the buffer to allow for the base64 encoding.
		// Base64 takes three 8 bit octets and encodes them into four 6 bit
		// characters.
		aEncodedWord.ReAllocL((((*dataToEncode).Length() + 2) / 3) * 4);

		iB64Codec.Encode(*dataToEncode, aEncodedWord);
		}
	else if (aInfo.EncodingType() == TImHeaderEncodingInfo::EQP)
		{
		// Worst case scenario is that each character in the buffer has to be
		// encoded to its hex equivalent with preceding '=' sign. Also added an
		// extra byte becuase the QP encoder expects the buffer to be one longer
		// than required.
		aEncodedWord.ReAllocL(((*dataToEncode).Length() * 3) + 1);

		// Replace all underscores in text with SPACE.
		for(TInt i=0; i < (*dataToEncode).Length(); i++)
			{
			if ((*dataToEncode)[i] == KImcvSpaceChar)
				dataToEncode->Des()[i] = KImcvUnderScore;	
			}
		 iQPCodec.Encode(*dataToEncode, aEncodedWord);
		}
	else
		aEncodedWord.Copy(*dataToEncode);

	CleanupStack::PopAndDestroy(); // dataToEncode
	}


TInt CImConvertHeader::AddEncodedWordInfoL(const TDesC8& aEncodedWord, RBuf8& aBufOut,
									TImHeaderEncodingInfo& aInfo, const TDesC8& aCharsetName)
	{
	// Encoded data 'in place' in OutBuf.

	// Add BEFORE encode data
	TInt offset = aInfo.Offset();
	TInt charsetSize = aCharsetName.Size();
		
	// Convert based on iCharSet and iEncoding
	
	// allocate enough space for encoded word plus extra encoding tokens
	// KImMaxLengthForEncodedWordAndExtraEncodingTokens = size of '?=' + '?' + ('B' or 'Q') + '?' + '?=' + ' '
	aBufOut.ReAllocL(aBufOut.MaxSize() + KImMaxLengthForEncodedWordAndExtraEncodingTokens + charsetSize + aEncodedWord.Size());
	aBufOut.Insert( offset, KImcvEncodedWordStart);

	// KImcvEncodedWordStart = 2
	// Other numerical values are from the encoded word characters
	// =? .. ?.. ? .. ?=

	aBufOut.Insert( offset + 2, aCharsetName);
	aBufOut.Insert( offset + 2 + charsetSize, KImcvQuestionMarkString);


	if (aInfo.EncodingType() == TImHeaderEncodingInfo::EBase64)
    	{
		aBufOut.Insert( offset + 2 + charsetSize+1, KImcvB);
		}
	else
		{	
		aBufOut.Insert( offset + 2 + charsetSize+1, KImcvQ);
		}

	aBufOut.Insert( offset + 2 + charsetSize+2, KImcvQuestionMarkString);

	offset += 2 + charsetSize + 3;

	aBufOut.Insert( offset, aEncodedWord );

	aBufOut.Insert( offset + aEncodedWord.Length(), KImcvEncodedWordEnd);

	TInt returnOffset = offset + aEncodedWord.Length() + 2;

	if (aInfo.AddSpace())
		{
    	aBufOut.Insert( aInfo.Offset(), KImcvSpace);

		returnOffset++;
		}

	// Return end of encoding offset.
	return returnOffset;
	}



// Check aData to see if it has any partial 'encoded-word's.
// ie a string of the gotm '=? ... ? ... ?=' which is incomplete.
// Return length of new string, up to the start of the partial encoded word.
//
EXPORT_C TBool CImConvertHeader::FindEncodedWord(TPtrC8& aData, TInt& aInit, TInt& rStart, TInt& rEnd)
	{
	TBool found = EFalse;

	TPtrC8 data = aData.Mid(aInit);
	rStart = data.Find(KImcvEncodedWordStart);
	rEnd = data.Length();
	if (rStart != KErrNotFound)
		{
		found = ETrue;

		TInt i;
		TInt j = 0;

		// Check for the three question marks
		for (i = rStart+2; i < data.Length(); i++)
			{
			if (data[i] == KImcvQuestionMark)	
				j++;
			if (j == 3)
				break; 
			}

		if (j != 3)
			found = EFalse;	// Have encountered an incomplete encoded-word

		// Check last ? is followed by =
		rEnd = i+1;
		if ( rEnd > data.Length() || data[rEnd] != KImcvEquals )
			found = EFalse;	// Have encountered an incomplete encoded-word
		} // if

 	return found;
	}


TBool CImConvertHeader::IsAscii( TUint aChar ) const
	{
	return ( ((aChar >= 32) && (aChar <= 126)));
	};

// function for adding 8 bit descriptor onto a 16bit descritpr.
//
void CImConvertHeader::Append(TDes& aBuffer, const TDesC8& aAddition)
//----------------------------------------------------------------------------------------
	{
	TInt addLen = aAddition.Length();
	TInt bufLen = aBuffer.Length();

	aBuffer.SetLength(bufLen+addLen);
	for(TInt i = 0; i < addLen; i++)
		aBuffer[bufLen+i] = aAddition[i];
	}


void CImConvertHeader::Insert(TDes8& aBuffer, const TDesC16& aInsert)
	{
	TInt addLen = aInsert.Length();
	TInt bufLen = aBuffer.Length();
	TInt origAddLen=addLen;

	aBuffer.SetLength(bufLen+addLen);


	while (bufLen > addLen)
		{
		aBuffer[bufLen+addLen-1] = aBuffer[bufLen-1];
		bufLen--;
		}

	while (addLen > bufLen)
		{
		aBuffer[addLen-1] = (TUint8) aInsert[addLen-1];
		addLen--;
		}

	// Assert bufLen==addLen
	while(bufLen)
		{
		aBuffer[bufLen+origAddLen-1] = aBuffer[bufLen-1];
		aBuffer[bufLen-1] = (TUint8) aInsert[bufLen-1];
		bufLen--;
		}


	}



