email/imap4mtm/imapmailstore/src/cstoreutilities.cpp
changeset 31 ebfee66fde93
parent 0 72b543305e3a
child 33 94cccd85bd25
child 40 224522e33db9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imapmailstore/src/cstoreutilities.cpp	Fri Jun 04 10:25:39 2010 +0100
@@ -0,0 +1,382 @@
+// Copyright (c) 2006-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 "cstoreutilities.h"
+#include <miuthdr.h>
+#include <barsread.h>
+
+// The maximum number of octets to be uuencoded on each line is 45
+const TInt KUuDecodedLineLength = 45;	
+// Initial size of buffer in which the "Real name <user@host>" strings
+// are built before they're put into the CImHeader and the size by
+// which to increment the buffer when necessary
+const TInt KImapAddressSizeInc=256;
+const TInt KKiloByteSize = 1024;
+const TInt KBufferExtension = 4;
+/**
+Constructor.
+*/
+CStoreUtilities::CStoreUtilities(TImEncodingType aEncodingType,TUint aCharsetId,RFs& aFs)
+	:iEncodingType(aEncodingType),iCharsetId(aCharsetId),iFs(aFs)
+	{
+	}
+	
+/**
+Factory constructors.
+Ownership aHeader is taken.
+*/	
+CStoreUtilities* CStoreUtilities::NewL(TImEncodingType aEncodingType,TUint aCharsetId,RFs& aFs)
+	{
+	CStoreUtilities* self=new(ELeave) CStoreUtilities(aEncodingType,aCharsetId,aFs);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop();
+	return self;
+	}
+	
+void CStoreUtilities::ConstructL()
+	{
+	User::LeaveIfError(iFs.Connect());
+	// Create converter objects
+	iCharacterConverter=CCnvCharacterSetConverter::NewL();
+	iCharConv=CImConvertCharconv::NewL(*iCharacterConverter, iFs);	
+	}
+
+/**
+Destructor.
+*/	
+CStoreUtilities::~CStoreUtilities()
+	{
+	delete iCharConv;
+	delete iCharacterConverter;
+	delete iFooterString;
+	delete iPartialLine;
+	}
+
+/**
+Checks if the descriptor contains the UUE begin header
+Extracts the file name if it is
+*/	
+TBool CStoreUtilities::CheckUUEStartL(const TDesC8& aSourceLine)
+	{
+	
+	TInt sourceLength = aSourceLine.Length();
+	if(sourceLength < KImcvUueStart().Length()+3) // can't be "begin ###", it's not long enough; 3=length of ###
+		return EFalse;
+
+	if(!aSourceLine.Left(KImcvUueStart().Length()).CompareF(KImcvUueStart)) // start of line might be UUE boundary
+		{
+		// we also need to check that the next three chars are numbers - Unix file access code
+		const TUint8* sourceLinePtr = aSourceLine.Ptr();
+		TInt length=KImcvUueStart().Length();// this defines length as 6 ie. "b e g i n  " 
+		if( TChar(sourceLinePtr[length]).IsDigit() && 
+			TChar(sourceLinePtr[length+1]).IsDigit() && 
+			TChar(sourceLinePtr[length+2]).IsDigit() )
+			{
+			// Found 'begin ###' at the start of a line - assume this is a UUencode header
+			// The attachment name in this header is ignored. We use the value from the MIME header
+			return ETrue;
+			}
+		}
+		
+	return EFalse;
+	}
+	
+/**
+Appends data to partial line object extening the destination buffer as appropirate 
+@param aBufferPtr the destination bufer
+@param aText the data to be appended
+*/	
+	
+void CStoreUtilities::AppendExtendL(HBufC8** aBufferPtr, const TDesC8& aText)
+	{
+	HBufC8 *buffer = *aBufferPtr;
+	TInt32 space = buffer->Des().MaxLength() - (buffer->Des().Length() + aText.Length());
+	if (space < 0)
+		{
+		TInt32 inc = (-space) + KImapAddressSizeInc - ((-space) % KImapAddressSizeInc);
+		TInt32 newSize = buffer->Des().MaxLength() + inc;
+		HBufC8 *newBuf = buffer->ReAllocL(newSize);
+		*aBufferPtr = buffer = newBuf;
+		}
+	buffer->Des().Append(aText);
+	}
+	
+	
+void CStoreUtilities::AttachFooterInfoL(TInt32 aBodyPartRemainingSize,CImapSettings& aImapSettings,HBufC8*& aDecodedData)
+	{
+	const HBufC8* buf= aImapSettings.DefaultPartialMessageFooter();
+
+	_LIT(KIntegerDirective,"%d");
+	const TInt KNoOfDigitsInMailSize = 10; // maximum length for no of digits for remaining body parts size
+	TResourceReader reader;
+	reader.SetBuffer(buf);
+	// Check if %d is not found in the resource string (To avoid problems due to localisation)
+	if(buf->Find((TDesC8&)KIntegerDirective) == KErrNotFound)
+		{
+		iFooterString = (reader.ReadTPtrC()).AllocL();
+		}
+	else
+		{
+		HBufC* resourceBuf = (reader.ReadTPtrC()).AllocL();
+		CleanupStack::PushL(resourceBuf);
+		iFooterString = HBufC::NewL(resourceBuf->Length()+KNoOfDigitsInMailSize);
+		iFooterString->Des().Format(*resourceBuf,(aBodyPartRemainingSize / KKiloByteSize));
+		CleanupStack::PopAndDestroy(resourceBuf);
+		}	
+	//caller will have left aDecodedData on the cleanupstack
+	CleanupStack::Pop(aDecodedData); 
+	TInt newSize = aDecodedData->Size() + iFooterString->Size();
+	aDecodedData = aDecodedData->ReAlloc(newSize);
+	CleanupStack::PushL(aDecodedData);
+	aDecodedData->Des().Append(*iFooterString);
+	delete iFooterString;
+	iFooterString = NULL;
+			
+	}
+	
+
+// convert text from its charset and write to richtext store. aText
+// can span multiple and partial lines
+void CStoreUtilities::WriteToBodyL(const TDesC8& aText,CRichText* aMessageBody)
+	{
+	TInt pos = aMessageBody->DocumentLength();
+
+	// Add bits of body text, converting along the way, till no characters left
+	// .. to convert.
+
+	// Convert text before writing to body.
+	TInt rem = 0;
+
+	// there will be a max of one output char per input byte
+	HBufC16* text16=HBufC16::NewLC(aText.Length());
+	TPtr16 ptr16=text16->Des();
+	
+	TBool preparedToConvert = iCharConv->PrepareToConvertToFromOurCharsetL(iCharsetId);
+	
+	if (!preparedToConvert)
+		{
+		ptr16.Copy(aText);
+		aMessageBody->InsertL(pos, ptr16);
+		}
+	else
+		{
+		TInt unconvertedChars, firstPos; // not used 
+		rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, 
+											  unconvertedChars, firstPos);
+		if (rem < 0) // error
+			{
+			// Copy unconverted characters.
+			ptr16.Copy(aText);
+			aMessageBody->InsertL(pos, ptr16);
+			}
+		else if (rem && rem < iLeftOver.MaxLength())
+			{
+			iLeftOver.Copy(aText.Right(rem));	
+			}
+
+		// convert CRLF to ELineBreak
+		TInt start = 0;
+		TInt length = ptr16.Length();
+		
+		if (length>0)
+			{
+			TInt i;
+			for (i=1; i<length; i++)
+				{
+				if (ptr16[i-1] == KImcvCR && ptr16[i] == KImcvLF)
+					{
+					ptr16[i-1] = CEditableText::ELineBreak;
+
+					// write this line to body
+					TPtrC ptr = ptr16.Mid(start, i-start);
+					aMessageBody->InsertL(pos, ptr);
+					pos += ptr.Length();
+					start = i+1;
+					}
+				}
+
+			if (start != i)
+				{
+				TPtrC ptr = ptr16.Mid(start, i-start);
+				aMessageBody->InsertL(pos, ptr);
+				}
+			}
+		}
+
+	CleanupStack::PopAndDestroy(text16);
+	}
+	
+// Decode and store received data
+HBufC8* CStoreUtilities::DecodeL(const TPtrC8& aBodyData, const TBool aEndOfStream)
+	{
+	// Somewhere to store decoded data, at least as long as source (plus anything we have left
+	// in the partial line buffer which may now get consumed)
+	TInt outputbuffersize=aBodyData.Length()+KBufferExtension;
+	if (iPartialLine)
+		outputbuffersize+=iPartialLine->Des().Length();
+
+	HBufC8* decoded=HBufC8::NewL(outputbuffersize);
+	TPtr8 decoded_ptr=decoded->Des();
+
+	// Which decoder are we using?
+	switch(iEncodingType)
+		{
+		case EEncodingTypeNone:
+		case EEncodingType7Bit:
+		case EEncodingType8Bit:
+		case EEncodingTypeBinary:
+		case EEncodingTypeUnknown:
+			// Nothing to do, just copy data
+			decoded->Des().Append(aBodyData);
+			break;
+
+		case EEncodingTypeBASE64:
+			// Decode Base64 data: just filter it through decoder, it
+			// ignores line breaks anyway.
+			iB64Decoder.Decode(aBodyData,decoded_ptr);
+			break;
+
+		case EEncodingTypeUU:
+			{
+			TPtrC8 bodydata=aBodyData;
+
+			// Got a partial buffer?
+			if (!iPartialLine)
+				{
+				// Allocate buffer
+				iPartialLine=HBufC8::NewL(KUuDecodedLineLength);
+				iUUDecoding = EFalse;
+				}
+			
+			// Decode UUEncoded data: line by line
+			TBool decodeEnded = EFalse;
+			TInt position=0;
+			while ( bodydata.Length() && !decodeEnded )
+				{
+				// Find() returns the start of "\r\n". The decoding algorithm
+				// requires that the encoded line contains the "\r\n".
+				TInt lineEnd = bodydata.Find( _L8("\r\n") );
+				if (lineEnd != KErrNotFound)
+					{
+					lineEnd = lineEnd + 2;
+					AppendExtendL( &iPartialLine, bodydata.Left( lineEnd ));
+				
+					bodydata.Set( bodydata.Mid( lineEnd ) );
+				
+					// Check for a well-formated  begin-tag
+					if ( CheckUUEStartL( iPartialLine->Des() ) )
+						{
+						iUUDecoding = ETrue;
+						}
+					else if ( iPartialLine->Compare( KImcvUueEnd ) != 0 && iUUDecoding )
+						{
+						// Every malformatted string is decoded as an empty string 
+						// with length 0. Appending such a string is harmless.
+						TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position);
+						iUUDecoder.Decode(*iPartialLine,destination);
+						position+=destination.Length();			
+						}
+					else if ( iUUDecoding )
+						{
+						decodeEnded = ETrue;
+						iUUDecoding = EFalse;	
+						}
+					
+					iPartialLine->Des().Zero();
+					}
+				else
+					{
+					AppendExtendL( &iPartialLine, bodydata);
+					
+					// advance to end of bodydata
+  					bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0);
+					}
+				}
+			decoded->Des().SetLength(position);	
+			break;
+			}
+
+		case EEncodingTypeQP:
+			{
+			TPtrC8 bodydata=aBodyData;
+
+			// Got a partial buffer?
+			if (!iPartialLine)
+				{
+				// Allocate buffer
+				iPartialLine=HBufC8::NewL(256);
+				}
+
+			// Build buffer to decode: basically, QP decoder wants CRLF terminated
+			// lines, so we build them in the iPartialLine buffer. There may be
+			// stuff already there from previous data packet - so we just append.
+			TInt position=0;
+			while(bodydata.Length())
+				{
+				// Find a line break
+				TInt lineend=bodydata.Find(_L8("\r\n"));
+
+				// No break?
+				if (lineend==KErrNotFound && !aEndOfStream)
+					{
+					// Stick it all in the partialline buffer, we should get a CRLF
+					// soon...
+					AppendExtendL( &iPartialLine,bodydata);
+					break;
+					}
+				else
+					{
+					if (lineend==KErrNotFound)
+						{
+						// Append whole thing left to buffer
+						AppendExtendL( &iPartialLine,bodydata);
+
+						// advance to end of bodydata
+						bodydata.Set(bodydata.Ptr()+bodydata.Length(), 0);
+						}
+					else
+						{
+						// Append to buffer up to that point (including the \r\n)
+						AppendExtendL( &iPartialLine,bodydata.Left(lineend+2));
+
+						// Remove from the buffer we're working on (including the \r\n)
+						bodydata.Set(bodydata.Ptr()+lineend+2,bodydata.Length()-lineend-2);
+						}
+
+					// Decode & skip on in buffer
+					TPtr8 destination((unsigned char*)decoded_ptr.Ptr()+position,0,outputbuffersize-position);
+					iQPDecoder.Decode(*iPartialLine,destination);
+					position+=destination.Length();
+					iPartialLine->Des().Zero();
+					}
+				}
+
+			// Update decoded
+			decoded->Des().SetLength(position);
+			break;
+			}
+		}
+
+	// put back any partially converted data
+	if (iLeftOver.Length())
+		{
+		decoded->Des().Insert(0, iLeftOver);
+		iLeftOver.SetLength(0);
+		}
+		
+	return decoded;
+	}
+