--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/servermtmutils/src/imcvrecv.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,4360 @@
+// Copyright (c) 1998-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 <e32base.h>
+#include "IMUTDLL.H"
+#include "IMCVTEXT.H"
+#include "IMCVRECV.H"
+#include "IMCVCODC.H" // TImConvert
+#include "IMCVUTIL.H"
+#include "CMsvPlainBodyTextEntry.H"
+#include <cmsvplainbodytext.h>
+
+#include <miuthdr.h> // CImHeader
+#include <miutatch.h> // TImAttachmentFile
+#include <msvids.h>
+#include <txtrich.h>
+#include <imcm.rsg> // resource definition for IMCM
+
+#include <pop3set.h> // For including KUidMsgTypePOP3
+#include <barsread.h> // TResourceReader
+
+#include <cmsvbodytext.h>
+
+#ifdef __WINS__
+#include <e32wins.h> // for maxfilename lengths
+#include <msvapi.h>
+#endif
+
+#ifndef __WINS__
+#include <msvapi.h>
+#endif
+#include <cmsvattachment.h>
+#include <mmsvattachmentmanager.h>
+#include <mmsvattachmentmanagersync.h>
+
+#ifndef _MSG_NO_LOGGING
+#define __IMUT_LOGGING
+_LIT(KLogFilePath, "c:\\logs\\mailtext\\in.txt");
+_LIT8(KNewLogHeader, "\r\n------ New Receive Session ------\r\n");
+_LIT8(KFound, "found: ");
+_LIT8(KFoundIncomplete, "found but incomplete: ");
+_LIT8(KPartLine, "unknown or part line");
+_LIT8(KEndOFHeader, "End of header");
+_LIT8(KSkippingData, "Skipping data");
+_LIT8(KCollectingData7, "Collecting 7/8-bit data");
+_LIT8(KCollectingDataQP, "Collecting QP data");
+_LIT8(KCollectingBase64, "Collecting BASE64 data");
+_LIT8(KCollectingUU, "Collecting UU data");
+_LIT8(KDecodingBase64, "Decoding BASE64 data");
+_LIT8(KDecodedBase64, "Decoded BASE64 data");
+_LIT8(KDecodingUU, "Decoding UU data");
+_LIT8(KDecodedUU, "Decoded UU data");
+_LIT8(KDecodingQP, "Decoding QP data");
+_LIT8(KDecodedQP, "Decoded QP data");
+_LIT8(KUUEDataCorrupt, "UU Data is corrupt");
+_LIT8(KFoundUUEStartBoundary, "UUE start boundary");
+_LIT8(KFoundUUEEndBoundary, "UUE end boundary");
+_LIT8(KClosingAttachFile, "Closing attachment file");
+_LIT8(KDeletingAttachFile, "Deleting attachment file");
+_LIT8(KDeletedAttachFile, "Deleted attachment file");
+_LIT8(KFailedToWriteToFile, "Failed to write to attachment file");
+_LIT8(KSectionHeader, "Section header");
+_LIT8(KFoundMIMEBoundary, "MIME boundary");
+_LIT8(KWritingToBody, "writing body");
+_LIT8(KWroteToBody, "wrote body");
+_LIT8(KWritingToFile, "writing attachment");
+_LIT8(KWroteToFile, "wrote attachment");
+_LIT8(KReset, "Resetting CImRecvConvert");
+_LIT8(KReseted, "Resetting CImRecvConvert");
+_LIT8(KDelete, "Deleting CImRecvConvert");
+_LIT8(KDeleted, "Deleting CImRecvConvert");
+_LIT8(KLineHasLineBreak, "Linebreak");
+_LIT8(KLastToken, "That was the last token");
+_LIT8(KBlankLine, "Blank line");
+_LIT8(KRemoveBoundary, "Remove boundary");
+_LIT8(KRemovedBoundary, "Removed boundary");
+_LIT8(KResetForNewEntry, "Reset for a New Entry");
+_LIT8(KResetedForNewEntry, "Reseted for a New Entry");
+_LIT8(KResetForNonMimeEntry, "Reset for a New Non-Mime Entry");
+_LIT8(KResetedForNonMimeEntry, "Reseted for a New Non-Mime Entry");
+_LIT8(KCreatingEntry, "Creating Entry");
+_LIT8(KCreatedEntry, "Created Entry");
+_LIT8(KUpdatingEntry, "Updating Entry");
+_LIT8(KUpdatedEntry, "Updated Entry");
+_LIT8(KMoveToParentEntry, "Moved to parent entry");
+_LIT8(KMovedToParentEntry, "End of Moved to parent entry");
+_LIT8(KStoringHeader, "Storing CImHeader");
+_LIT8(KStoredHeader, "Stored CImHeader");
+_LIT8(KStoringBody, "Storing CRichText");
+_LIT8(KStoring8BitBody, "Storing CMsvBodyText");
+_LIT8(KStoredBody, "Stored CRichText");
+_LIT8(KStored8BitBody, "Stored CMsvBodyText");
+_LIT8(KStoringMIMEHeader, "Storing CImMimeHeader");
+_LIT8(KStoredMIMEHeader, "Stored CImMimeHeader");
+_LIT8(KIgnoringStreams, "Discarding streams");
+_LIT8(KStartMessageComplete, "Start Message Complete");
+_LIT8(KMessageComplete, "Message Complete");
+_LIT8(KHeaderComplete, "Message Header complete");
+_LIT8(KReturnReceiptTo, "return-receipt-To is set");
+_LIT8(KStartStoringEntryStream, "Starting Storing Entry Stream");
+_LIT8(KStoringEntryStream, "Storing Entry Stream");
+_LIT8(KDoneStoringEntryStream, "Done Storing Entry Stream");
+#endif // _MSG_NO_LOGGING
+
+// uncomment the line below if logging is to be enabled
+
+#if defined(__IMUT_LOGGING)
+#define RECVLOG(text) (iImcvLog?iImcvLog->AppendComment(text):void(0));
+#define RECVLOG_OUT(text) (iImcvLog?iImcvLog->AppendOut(text):void(0));
+#define LOGGING(string1, string2) (Logging(string1, string2));
+#else
+#define RECVLOG(text) (void(0));
+#define RECVLOG_OUT(text) (void(0));
+#define LOGGING(string1, string2) (void(0));
+#endif
+
+
+const TInt KBodyTextChunkSizeBytes = 512;
+
+_LIT(KIntegerKey,"%d");
+const TInt KSpaceToAddNumber=20;
+
+
+//----------------------------------------------------------------------------------------
+LOCAL_C TBool IsIllegalChar(const TUint aChar)
+//----------------------------------------------------------------------------------------
+ {
+ // EPOC32 filenames must not contain any of the following chars
+ return (aChar == '*' || aChar == '\\' || aChar == '<' || aChar == '>' ||
+ aChar == ':' || aChar == '"' || aChar == '/' || aChar == '|' ||
+ aChar < ' ' || aChar =='?');
+ }
+
+//----------------------------------------------------------------------------------------
+LOCAL_C void RemoveSurroundingCharacters( const TUint8& aLeft, const TUint8& aRight, HBufC8& aString)
+//----------------------------------------------------------------------------------------
+ {
+ TPtr8 des = aString.Des();
+ TInt len = des.Length();
+
+ if( len>2 && des[0]==aLeft && des[len-1]==aRight )
+ {
+ TPtrC8 mid = des.Mid(1, len-1);
+ des.Copy(mid);
+ des.SetLength(len-2);
+ }
+ }
+
+
+//****************************************************************************************
+// Class CRfc822Token Functions
+//****************************************************************************************
+
+//----------------------------------------------------------------------------------------
+EXPORT_C CRfc822Token* CRfc822Token::NewLC()
+//----------------------------------------------------------------------------------------
+ {
+ CRfc822Token* self = new (ELeave) CRfc822Token;
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+//----------------------------------------------------------------------------------------
+void CRfc822Token::ConstructL()
+//----------------------------------------------------------------------------------------
+ {
+ iOutputLine = HBufC8::NewL(KMaxIMailHeaderReadLineLength+1);
+ Reset();
+ }
+
+//----------------------------------------------------------------------------------------
+CRfc822Token::CRfc822Token()
+//----------------------------------------------------------------------------------------
+ {
+ __DECLARE_NAME(_S("CRfc822Token"));
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C CRfc822Token::~CRfc822Token()
+//----------------------------------------------------------------------------------------
+ {
+ delete iOutputLine;
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C void CRfc822Token::Reset()
+//----------------------------------------------------------------------------------------
+ {
+ iOutputLine->Des().FillZ();
+ iBufferedInput.SetLength(0);
+ iBufferedInput.FillZ();
+
+ iLastToken = EFalse;
+ iFirstLine = ETrue;
+ i822FieldsExist=EFalse;
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C void CRfc822Token::ParseNextLineL(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ TBool hitEof = EFalse;
+ TBool endOfField = EFalse;
+ // Always read one more packet than we need so we know we've read the entire field.
+
+ // Consequently, we need to check whether there is any buffered input from the
+ // previous rfc822 field:*******
+
+ if(iBufferedInput.Length())
+ {
+ // the buffered input is never more than a line of text...
+ *iOutputLine = iBufferedInput;
+
+ iBufferedInput.FillZ();
+ iBufferedInput.SetLength(0);
+ }
+ iInputLine = aSourceLine;
+
+ // discard any LF character from the begining of the line (this is abit strange?)
+ while(iInputLine.Length() > 0 && (iInputLine[0] == KImcvLF || iInputLine[0] == KImcvCR))
+ iInputLine.Delete(0, 1);
+
+ // remove the CR at the end but only if there's stuff before it
+ while(iInputLine.Length() > 0 && (iInputLine[iInputLine.Length()-1] == KImcvLF || iInputLine[iInputLine.Length()-1] == KImcvCR))
+ iInputLine.Delete(iInputLine.Length()-1, 1);
+
+ // if the newly read line is now empty, then don't read any more through this tokeniser
+ iLastToken = !iInputLine.Length();
+
+ // only bother with the rest of the parsing if there's any data left
+ if(iInputLine.Length() > 0 || iOutputLine->Length() > 0)
+ {
+ // check whether we're on the next field or still on the current one
+ if(!iFirstLine && iInputLine.Length() > 0 && iInputLine[0] != KImcvSP && iInputLine[0] != KImcvTab)
+ {
+ // we've read the first line of the next field
+ iBufferedInput = iInputLine;
+ endOfField = ETrue; // flag the fact that the current field is ready for parsing
+ }
+ else // the line belongs in the current field, we need to keep the leading whitespace
+ // Folding headers, RFC 2822, section 2.2.3
+ {
+ // make sure we're not about to exceed the buffer
+ if((iOutputLine->Length() + iInputLine.Length()) > iOutputLine->Des().MaxLength())
+ iOutputLine = iOutputLine->ReAllocL(iOutputLine->Length() + iInputLine.Length());
+
+ // now copy the remaining data into the buffer
+ iOutputLine->Des().Append(iInputLine);
+ }
+ }
+ else
+ hitEof = ETrue; //this means that EEndOfHeader is set if a line of zero length is parsed - ie. (clean CRLF - CRLF) = 0.
+ iFirstLine = EFalse;
+
+ if(endOfField || iLastToken)
+ {
+ // only parse the input when we have a whole field,
+ // or we just found the blank line that
+ // separates the body from the header
+ if(MatchAndRemoveL(KImcvFromPrompt))
+ iHeaderPart = EFrom;
+ else if(MatchAndRemoveL(KImcvReplyToPrompt))
+ iHeaderPart = EReplyTo;
+ else if(MatchAndRemoveL(KImcvToPrompt))
+ iHeaderPart = ETo;
+ else if(MatchAndRemoveL(KImcvCcPrompt))
+ iHeaderPart = ECc;
+ else if(MatchAndRemoveL(KImcvBccPrompt))
+ iHeaderPart = EBcc;
+ else if(MatchAndRemoveL(KImcvSubjectPrompt))
+ iHeaderPart = ESubject;
+ else if(MatchAndRemoveL(KImcvDatePrompt))
+ iHeaderPart = EDate;
+ else if(MatchAndRemoveL(KImcvReceivedPrompt))
+ iHeaderPart = EReceived;
+ else if(MatchAndRemoveL(KImcvMessageIdPrompt))
+ {
+ iHeaderPart = EMessageId;
+ RemoveSurroundingCharacters(KImcvLeftChevron, KImcvRightChevron, *iOutputLine);
+ }
+ else if (PriorityAndReceiptsMatchAndRemoveL(EPriority))
+ iHeaderPart=EPriority;
+ else if (PriorityAndReceiptsMatchAndRemoveL(EReturnReceiptTo))
+ iHeaderPart=EReturnReceiptTo;
+ else if(hitEof)
+ iHeaderPart = EEndOfHeader;
+ else
+ {
+ iHeaderPart = EUnknown;
+ iImRecvConvert->iMimeParser->ParseLineL(*iOutputLine);
+ }
+ }
+ else
+ iHeaderPart = ENotFinished;
+ }
+
+TBool CRfc822Token::PriorityAndReceiptsMatchAndRemoveL(THeaderPart aPriority)
+ {
+ CDesC8Array* fields=new (ELeave)CDesC8ArrayFlat(6);
+ CleanupStack::PushL(fields);
+ if (aPriority==EPriority)
+ CImcvUtils::PriorityFieldsL(*fields);
+ else if (aPriority==EReturnReceiptTo)
+ CImcvUtils::ReceiptFieldsL(*fields);
+ else
+ {
+ CleanupStack::PopAndDestroy(); //fields
+ return EFalse;
+ }
+ TInt count=fields->Count();
+ TBool matched=EFalse;
+ for (TInt i=0;i<count;i++)
+ {
+ if (MatchAndRemoveL((*fields)[i]))
+ {
+ matched=ETrue;
+ break;
+ }
+ }
+ CleanupStack::PopAndDestroy(); //fields
+ return matched;
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CRfc822Token::MatchAndRemoveL(const TDesC8& aString)
+//----------------------------------------------------------------------------------------
+ {
+ TInt comparison;
+ TInt stringLength = aString.Length();
+ TInt desLength = (*iOutputLine).Length();
+ TInt compareLength = stringLength > desLength ? desLength : stringLength;
+ TPtrC8 left((*iOutputLine).Left(compareLength));
+
+ // now see whether the current line contains the search string
+ comparison = left.CompareF(aString);
+ if(!comparison)
+ {
+ // found the match string at the start of the output line, so remove it
+ // get rid of any whitespace betweebn the tag and the data while we have a chance
+ if (!i822FieldsExist)
+ i822FieldsExist=ETrue;
+ TInt whitespaceLength=0;
+ TInt maxLength=desLength-stringLength;
+ const TUint8* ptr = (*iOutputLine).Ptr();
+ while(whitespaceLength <= maxLength && (ptr[stringLength+whitespaceLength] == KImcvSP || ptr[stringLength+whitespaceLength] == KImcvTab))
+ whitespaceLength++;
+ (iOutputLine->Des()).Delete(0, stringLength+whitespaceLength);
+ }
+ return (comparison==0);
+ }
+
+
+//****************************************************************************************
+// Class CImRecvConvert
+//****************************************************************************************
+//----------------------------------------------------------------------------------------
+EXPORT_C CImRecvConvert* CImRecvConvert::NewLC(RFs& anFs, CMsvServerEntry* aServerEntry,
+ TUid aMsgType, TMsvId aEmailServiceId)
+//----------------------------------------------------------------------------------------
+ {
+ CImRecvConvert* self = new (ELeave) CImRecvConvert(anFs, aServerEntry, aMsgType,
+ aEmailServiceId);
+ CleanupStack::PushL(self);
+ self->ConstructL(anFs);
+ return self;
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C CImRecvConvert* CImRecvConvert::NewL(RFs& anFs, CMsvServerEntry* aServerEntry,
+ TUid aMsgType, TMsvId aEmailServiceId)
+//----------------------------------------------------------------------------------------
+ {
+ CImRecvConvert* self = CImRecvConvert::NewLC(anFs, aServerEntry, aMsgType,
+ aEmailServiceId);
+ CleanupStack::Pop();
+ return self;
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ConstructL(RFs& anFs)
+//----------------------------------------------------------------------------------------
+ {
+ iFsSession = &anFs;
+
+ RResourceFile resFile;
+ OpenResourceFileL(resFile, anFs); // NB leaves if file not found
+
+ // make sure the resource file will be closed if anything goes wrong
+ // CloseResourceFile is declared in IMUTDLL.H and defined in IMUTDLL.CPP
+ TCleanupItem close( CloseResourceFile, &resFile );
+ CleanupStack::PushL( close );
+
+ // Read iStore8BitData flag.
+ HBufC8* buf = resFile.AllocReadLC( STORE_8BIT_BODY_TEXT );
+ TResourceReader reader;
+ reader.SetBuffer(buf);
+ iStore8BitData = reader.ReadInt8();
+ CleanupStack::PopAndDestroy(buf);
+
+ //read iStorePlainBodyText flag for writing bodytext chunk bu chunk.
+ buf = resFile.AllocReadLC( STORE_PLAIN_BODY_TEXT );
+ reader.SetBuffer(buf);
+ iStorePlainBodyText = reader.ReadInt8();
+ CleanupStack::PopAndDestroy(buf);
+
+
+ buf = resFile.AllocReadLC( REMOVED_ATTACHMENT_TAG );
+ reader.SetBuffer(buf);
+ iRemovedAttachmentTag = reader.ReadTPtrC().AllocL();
+ CleanupStack::PopAndDestroy(buf);
+
+ buf = resFile.AllocReadLC( DEFAULT_ATTACHMENT_NAME );
+ reader.SetBuffer(buf);
+ iDefaultAttachmentName = reader.ReadTPtrC().AllocL();
+ CleanupStack::PopAndDestroy(buf);
+
+ buf = resFile.AllocReadLC(PARTIAL_DOWNLOAD_FOOTER_MESSAGE);
+ reader.SetBuffer(buf);
+ if(iStore8BitData)
+ {
+ iPartialEmailFooter8 = buf;
+ CleanupStack::Pop(buf);
+ }
+ else
+ {
+ iPartialEmailFooter = (reader.ReadTPtrC()).AllocL();
+ CleanupStack::PopAndDestroy(buf);
+ }
+ buf = NULL;
+ CleanupStack::PopAndDestroy(&resFile); // resFile (Close resfile)
+
+ // create CImHeader object to store Rfc822 header info...
+ iOutputHeader = CImHeader::NewLC(); // PushL(iHeader)
+ CleanupStack::Pop(); // iHeader
+ if (iStore8BitData)
+ {
+ //Create body text storage helper.
+ iBodyText = CMsvBodyText::NewL();
+ }
+ else
+ {
+ // create CRichText object to store body...
+ iParaLayer = CParaFormatLayer::NewL();
+ iCharLayer = CCharFormatLayer::NewL();
+ iOutputBody = CRichText::NewL(iParaLayer, iCharLayer);
+ }
+
+ // Create Rfc822 filter object...
+ iRfc822Token = CRfc822Token::NewLC(); // PushL(iRfc822Token)
+ CleanupStack::Pop(); // iRfc822Token
+ iRfc822Token->SetImRecvConvert(this);
+
+ // Create converter objects...
+ iCharacterConverter = CCnvCharacterSetConverter::NewL();
+ iCharConv = CImConvertCharconv::NewL(*iCharacterConverter, anFs);
+ iHeaderConverter = CImConvertHeader::NewL(*iCharConv);
+
+ // Create MIME filter object...
+ iMimeParser = CMimeParser::NewL(*this);
+
+
+ // logfile stuff
+ iImcvLog=NULL;
+
+#ifdef __IMUT_LOGGING
+ TRAPD(err,iImcvLog=CImLog::NewL(KLogFilePath, EAppend));
+#endif
+
+ iRootEntryId = EntryId();
+
+ iNotFinishedRfc822Header = ETrue;
+
+ iEmailEntry = new (ELeave) TMsvEmailEntry;
+ iReceivingHeadersOnly=EFalse;
+
+ iParent = new (ELeave) CArrayFixFlat<TParentDetails>(3);
+
+ iFirstBoundaryReached=EFalse;
+
+ ResetL();
+
+ RECVLOG( KNewLogHeader )
+ }
+
+//----------------------------------------------------------------------------------------
+CImRecvConvert::CImRecvConvert(RFs& anFs, CMsvServerEntry* aServerEntry,
+ TUid aMsgType, TMsvId aEmailServiceId)
+ : iServerEntry(aServerEntry), iNewMsgType(aMsgType), iDefaultEntryType(ETextEntry),
+ iEmailServiceId(aEmailServiceId), iAttachmentFile(anFs)
+//----------------------------------------------------------------------------------------
+ {
+ __DECLARE_NAME(_S("CImRecvConvert"));
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C CImRecvConvert::~CImRecvConvert()
+//----------------------------------------------------------------------------------------
+ {
+ delete iEmailEntry;
+ delete iOutputHeader;
+ delete iBodyText;
+ delete iBodyBuf;
+ delete iParaLayer;
+ delete iCharLayer;
+ delete iOutputBody;
+ delete iRemovedAttachmentTag;
+ delete iDefaultAttachmentName;
+ delete iRfc822Token;
+ delete iMimeParser;
+ delete iHeaderConverter;
+ delete iCharConv;
+ delete iCharacterConverter;
+ delete iAttachmentFullPath;
+ delete iImcvUtils;
+ delete iParent;
+ RECVLOG(KDeleted)
+ delete iImcvLog;
+ delete iPartialEmailFooter8;
+ delete iPartialEmailFooter;
+ delete iPlainBodyTextEntry;
+ delete iPartialRetrievalBody;
+ iPartialRetrievalBody=NULL;
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C void CImRecvConvert::Cancel()
+//----------------------------------------------------------------------------------------
+ {
+ if(iAttachmentFileState == EFileIsOpen)
+ {
+ iAttachmentFileState = EFileIsIncomplete; //because of cancel during download
+ }
+ TRAPD(ignore, CloseAttachmentFileL()); //ensure the file is closed, and deleted
+ }
+
+
+
+//----------------------------------------------------------------------------------------
+EXPORT_C void CImRecvConvert::ResetForHeadersL()
+//----------------------------------------------------------------------------------------
+ {
+ // This function is used in preference to ResetL() when the owner
+ // only wishes to create email entries in the remote mailbox -
+ // they do not wish to create any stores associated with the message
+ // entry
+ ResetL();
+ iReceivingHeadersOnly=ETrue;
+ }
+//----------------------------------------------------------------------------------------
+EXPORT_C void CImRecvConvert::ResetL()
+//----------------------------------------------------------------------------------------
+ {
+ RECVLOG(KReset)
+
+ iBodyId = KMsvNullIndexEntryId;
+ iSizeOfAttachmentsRemoved = 0;
+
+ User::LeaveIfError(iServerEntry->SetEntry(iRootEntryId));
+
+ iReceivingHeadersOnly = EFalse;
+ iMessageEntryCalled = EFalse;
+ iLeaveError = KErrNone;
+ iCurrentMultipartFolderEntryId = 0;
+
+ TParentDetails parent;
+ parent.iAttachment = EFalse;
+ parent.iMHTML = EFalse;
+ parent.iICal = EFalse;
+ parent.iVCal = EFalse;
+ parent.iSize = 0;
+ iParent->InsertL(0,parent);
+
+ if(iEmailEntry)
+ {
+ delete iEmailEntry;
+ iEmailEntry = NULL;
+ }
+ iEmailEntry = new (ELeave) TMsvEmailEntry;
+
+ // "iSavingAttachments" commands this code to store attachments in files.
+ // Currently this is always set to ETrue as we intend to save all attachments
+ // However, if the user wishes to discard (ie do not save) any
+ // attachments in incoming email messages - then this member variable
+ // may be set to EFalse by the inline accessor function "SaveAllAttachments(TBool)"
+ iSavingAttachments = ETrue;
+ iAttachmentFileState = EFileIsClosed;
+
+ // iLeaveError contains the error value of any functions that leaves (ie CRichText::InsertL)
+ // or any returned error value that generates a leave from within CImRecvConvert (ie RFile::Create)
+
+ iGlobalIndent = 0;
+ iLongestLine = 50;
+
+ iImPriority = EMsvMediumPriority;
+ delete iImcvUtils;
+ iImcvUtils = NULL;
+ iImcvUtils = CImcvUtils::NewL();
+
+ // reset internal date
+ iTimeDate.UniversalTime();
+ iParsedTime=EFalse;
+
+ iMimeParser->Reset();
+
+ iNewEntry = EFalse; //EFalse if the entry was moved to. ETrue if the entry was just created
+ iTopMessagePart = KMsvNullIndexEntryId; //A value of NULL indicates the next entry created is a main message entry
+ ResetForNewEntryL(iDefaultEntryType);
+ iMIMEPart_822Header = EFalse;
+ iPartialEmail = EFalse;
+
+ RECVLOG(KReseted)
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ResetForNewEntryL(TValidEntryType entryType)
+//----------------------------------------------------------------------------------------
+ {
+ RECVLOG(KResetForNewEntry)
+
+ iFinishedHeader = EFalse;
+
+ iPreviousLineLength = 0;
+ iPreviousTrailingWhitespace = 0;
+ iLastChar = 0;
+
+ // Reset the new entry state
+ ResetForNonMimeEntryL();
+
+ // Clear the storage classes
+ iMimeParser->ResetForNewEntry();
+ iRfc822Token->Reset();
+
+ iEmailPart = KNoPart;
+ iEntryType=entryType;
+
+ if (iEntryType==EMessageEntry)
+ {
+ iEmailPart = KParentPart;
+ iMimeParser->SetMessageFolderType(EFolderTypeRFC822);
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ResetForNonMimeEntryL()
+//----------------------------------------------------------------------------------------
+ {
+ iEntryType = ETextEntry;
+ RECVLOG(KResetForNonMimeEntry)
+
+ iSkipData = EFalse;
+ iPrepared = EFalse;
+ iNewNonMIMEBodyPart=ETrue;
+ iEncounteredLineEndingInCarriageReturn=EFalse;
+
+ iAlgorithm = ENoAlgorithm;
+ iCurrentPartIsRichText = ETrue;
+ CloseAttachmentFileL();
+ iEntryDataSize = 0;
+
+ if (iStore8BitData)
+ {
+ //Create a buffer to hold the body text as it is down loaded.
+ delete iBodyBuf;
+ iBodyBuf = NULL;
+ iBodyBuf = CBufSeg::NewL(KBodyTextChunkSizeBytes);
+ }
+ else
+ {
+ iOutputBody->Reset();
+ }
+ iAttachmentName.Zero();
+ iFinalLine = EFalse;
+
+ iOutputHeader->Reset();
+ iEmptyHeaderSize=iOutputHeader->DataSize();
+ RECVLOG(KResetedForNonMimeEntry)
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C TInt CImRecvConvert::ParseNextField(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ __ASSERT_DEBUG(iRootEntryId!=KMsvNullIndexEntryId, gPanic(KPanicServiceIdNotValid));
+ RECVLOG_OUT(aSourceLine);
+
+ // If we are temporarily on the null entry then move back to the entry we are working on
+ if ((iLeaveError == KErrNone) && (iServerEntry->Entry().Id() == KMsvNullIndexEntryId))
+ iLeaveError = iServerEntry->SetEntry(iSavedEntryId);
+
+ if(iLeaveError==KErrNone)
+ TRAP(iLeaveError, ParseNextLineL(aSourceLine));
+
+ // Save the current entry id for later.
+ if (iLeaveError==KErrNone)
+ iSavedEntryId = iServerEntry->Entry().Id();
+
+ TUid type = iServerEntry->Entry().iType;
+ if( type != KUidMsvMessageEntry &&
+ type != KUidMsvEmailTextEntry &&
+ type != KUidMsvEmailHtmlEntry &&
+ type != KUidMsvAttachmentEntry)
+ {
+ // Set the current id to null so that we aren't locking any folders
+ iServerEntry->SetEntry(KMsvNullIndexEntryId);
+ }
+
+ return iLeaveError;
+ }
+
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ParseNextLineL(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ iParsedMimeBoundaryLast=0;
+ if(!iFinishedHeader)
+ {
+ // start by getting the next token from the header
+ iRfc822Token->ParseNextLineL(aSourceLine);
+
+ switch(iRfc822Token->iHeaderPart)
+ {
+ case CRfc822Token::EUnknown:
+ case CRfc822Token::ENotFinished:
+ RECVLOG(KPartLine)
+ break;
+ case CRfc822Token::EFrom:
+ iOutputHeader->SetFromL(*iRfc822Token->OutputLine());
+ LOGGING(KFound,KImcvFromPrompt);
+ break;
+ case CRfc822Token::EReplyTo:
+ iOutputHeader->SetReplyToL(*iRfc822Token->OutputLine());
+ LOGGING(KFound,KImcvReplyToPrompt);
+ break;
+ case CRfc822Token::ETo:
+ ParseRecipientListL(iOutputHeader->ToRecipients());
+ LOGGING(KFound,KImcvToPrompt);
+ break;
+ case CRfc822Token::ECc:
+ ParseRecipientListL(iOutputHeader->CcRecipients());
+ LOGGING(KFound,KImcvCcPrompt);
+ break;
+ case CRfc822Token::EBcc:
+ ParseRecipientListL(iOutputHeader->BccRecipients());
+ LOGGING(KFound,KImcvBccPrompt);
+ break;
+ case CRfc822Token::ESubject:
+ iOutputHeader->SetSubjectL(*iRfc822Token->OutputLine());
+ LOGGING(KFound,KImcvSubjectPrompt);
+ break;
+ case CRfc822Token::EDate:
+ // if we have not already parsed the date from the received header.
+ if(!iParsedTime)
+ {
+ iRfc822Date.ParseDateField(*iRfc822Token->OutputLine() , iTimeDate);
+
+ //if this is an embedded entry
+ if (iEmailEntry->Id() != iTopMessagePart)
+ {
+ iEmailEntry->iDate = iTimeDate;
+ }
+
+ LOGGING(KFound,KImcvDatePrompt);
+ }
+ break;
+ case CRfc822Token::EReceived:
+ if(!iParsedTime)
+ {
+ // remove the data before the comma, to just leave the date
+ TPtr8 ptr(iRfc822Token->OutputLine()->Des());
+ TInt lPos=ptr.Locate(';');
+
+ // No point trying to process a date if we did not find the ';' or
+ // if there is no data after the ';'
+ if ((lPos != KErrNotFound) && (lPos < ptr.Length() - 2))
+ {
+ ptr = ptr.Right(ptr.Length()-lPos-2);
+ iRfc822Date.ParseDateField(ptr,iTimeDate);
+
+ //if this is an embedded entry
+ if (iEmailEntry->Id() != iTopMessagePart)
+ {
+ iEmailEntry->iDate=iTimeDate;
+ }
+ //indicate that we have the time stamp for the entry
+ iParsedTime=ETrue;
+
+ LOGGING(KFound,KImcvReceivedPrompt);
+ }
+ else
+ {
+ LOGGING(KFoundIncomplete, KImcvReceivedPrompt);
+ }
+ }
+ break;
+
+ case CRfc822Token::EMessageId:
+ iOutputHeader->SetImMsgIdL(*iRfc822Token->OutputLine());
+ LOGGING(KFound,KImcvMessageIdPrompt);
+ break;
+ case CRfc822Token::EPriority:
+ iImPriority=iImcvUtils->EvaluatePriorityText(*iRfc822Token->OutputLine());
+ LOGGING(KFound,KImcvPriorityPrompt);
+ break;
+ case CRfc822Token::EReturnReceiptTo:
+ iOutputHeader->SetReceiptAddressL(*iRfc822Token->OutputLine());
+ LOGGING(KFound,KReturnReceiptTo);
+ break;
+ case CRfc822Token::EEndOfHeader:
+ // the next line goes in the body part
+ iFinishedHeader = ETrue;
+ RECVLOG(KEndOFHeader)
+ break;
+ default:
+ RECVLOG(KEndOFHeader)
+ break;
+ }
+
+
+ if(iRfc822Token->iHeaderPart != CRfc822Token::ENotFinished)
+ {
+ // Now that we've used the data, we also need to clear the output line from the tokeniser....
+ iRfc822Token->OutputLine()->Des() = KNullDesC8;
+
+ // whatever part we just read, we may also have read the empty line that separates
+ // the header from the body
+ if((iFinishedHeader = iRfc822Token->LastToken()) != EFalse) //iFinishedHeader set to 1 on CRLF-
+ {
+ RECVLOG(KLastToken);
+ iNotFinishedRfc822Header = EFalse;
+
+ iHeaderConverter->SetMessageType(iMimeParser->MessageIsMime());
+ iHeaderConverter->DecodeAllHeaderFieldsL(*iOutputHeader);
+
+ if (iMimeParser->MessageIsMime())
+ EndOfHeaderMIMEProcessingL();
+ else
+ EndOfHeaderProcessingL();
+
+ // CMsvPlainBodyText will be using for storing bodytext in chunks.
+ if(iStorePlainBodyText && iEmailEntry->iType==KUidMsvEmailTextEntry)
+ {
+ iFirstLinePlainText = ETrue;
+ }
+ else
+ {
+ if (iStore8BitData)
+ {
+ iBodyText->SetDefaultCharacterSet(iCharConv->SystemDefaultCharset());
+ if ( iMimeParser->MessageIsMime() && iMimeParser->ContentType()==EMimeText )
+ iBodyText->SetCharacterSet(iMimeParser->CurrentCharsetL());
+ else
+ iBodyText->SetCharacterSet(0);
+ }
+ else
+ {
+ // Get charset for decoding.
+ if ( iMimeParser->MessageIsMime() && iMimeParser->ContentType()==EMimeText )
+ iCharConv->PrepareToConvertToFromOurCharsetL(iMimeParser->CurrentCharsetL());
+ else
+ iCharConv->PrepareToConvertToFromOurCharsetL(iCharConv->SystemDefaultCharset());
+ }
+ }
+ }
+ }
+ }
+ else if (iReceivingHeadersOnly==EFalse)
+ {
+ //Set to EFalse when a single line is to be skipped (ie boundary line)
+ iCommitLine=ETrue;
+
+ // read one line of the message body if I am processing a whole email message
+ if(iMimeParser->MessageIsMime())
+ ParseMimeLineL(aSourceLine);
+ else
+ ParseBodyLineL(aSourceLine);
+
+ if((iCommitLine)&&(!iSkipData))
+ DecodeAndStoreLineL(aSourceLine);
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C TMsvEmailEntry CImRecvConvert::MessageEntryDetailsL()
+//----------------------------------------------------------------------------------------
+ {
+
+ iMessageEntryCalled=ETrue;
+ CloseAttachmentFileL();
+
+ // A message requiring manual termination and not part way through a MIME part header
+ if( iTopMessagePart==EntryId() && iFinishedHeader)
+ StoreEntryStreamsL(KStore822Header|KStoreMIMEHeader);
+ else
+ {
+ User::LeaveIfError(iServerEntry->SetEntry(iTopMessagePart));
+ if(iEmailEntry)
+ {
+ delete iEmailEntry;
+ iEmailEntry=NULL;
+ }
+
+ iEmailEntry = new (ELeave) TMsvEmailEntry(iServerEntry->Entry());
+ }
+
+ return *iEmailEntry;
+ }
+
+
+//----------------------------------------------------------------------------------------
+EXPORT_C void CImRecvConvert::MessageCompleteL(TMsvEmailEntry aEmailEntry)
+//----------------------------------------------------------------------------------------
+ {
+ // Restore the entry context
+ if (iServerEntry->Entry().Id() == KMsvNullIndexEntryId)
+ User::LeaveIfError(iServerEntry->SetEntry(iSavedEntryId));
+
+ __ASSERT_DEBUG(iMessageEntryCalled, gPanic(KPanicMessageEntryNotCalled));
+ __ASSERT_DEBUG(aEmailEntry.Id()==iTopMessagePart, gPanic(KPanicMessageEntryIdHasChanged)); // Id should be set to iTopMessagePart
+
+ // Called ResetL() to reset object instead of ResetForHeadersL()
+ __ASSERT_DEBUG(iReceivingHeadersOnly, gPanic(KPanicIncorrectResetState));
+
+ if(iEmailEntry)
+ {
+ if (iEmailEntry->Id())
+ {
+ if(iLeaveError==KErrNone)
+ {
+ // a remote email header cannot have any attachments or be marked as complete...
+ aEmailEntry.SetAttachment(EFalse);
+ aEmailEntry.SetMHTMLEmail(EFalse);
+ aEmailEntry.SetComplete(EFalse);
+ User::LeaveIfError(iServerEntry->ChangeEntryBulk(aEmailEntry));
+ RECVLOG(KHeaderComplete)
+ }
+ else
+ {
+ if (!iPopulateMessage)
+ {
+ TMsvId currentId = EntryId();
+ User::LeaveIfError(iServerEntry->SetEntry(iServerEntry->Entry().Parent()));
+ User::LeaveIfError(iServerEntry->DeleteEntry(currentId));
+ }
+ User::Leave(iLeaveError);
+ }
+ }
+ }
+
+ // Save the entry context and move it to null so that we're not locking any folders
+ iSavedEntryId = iServerEntry->Entry().Id();
+ User::LeaveIfError(iServerEntry->SetEntry(KMsvNullIndexEntryId));
+ }
+
+
+EXPORT_C void CImRecvConvert::MessageCompleteL(TBool aPartialDownload)
+ {
+ iPartialEmail=aPartialDownload;
+ MessageCompleteL();
+ }
+
+
+//----------------------------------------------------------------------------------------
+EXPORT_C void CImRecvConvert::MessageCompleteL()
+//----------------------------------------------------------------------------------------
+ {
+ // Restore the entry context
+ RECVLOG(KStartMessageComplete)
+ if (iServerEntry->Entry().Id() == KMsvNullIndexEntryId)
+ User::LeaveIfError(iServerEntry->SetEntry(iSavedEntryId));
+
+ if (iParsedMimeBoundaryLast==EFalse && iAttachmentFileState==EFileIsOpen && iPartialEmail!=EFalse)
+ {
+ iAttachmentFileState=EFileTopIncomplete;
+ }
+
+ CloseAttachmentFileL();
+
+ if(!iEmailEntry->Id())
+ return;
+
+ if(iLeaveError!=KErrNone)
+ {
+ User::Leave(iLeaveError);
+ }
+
+ if (EntryId()!=iTopMessagePart)
+ {
+ iEmailEntry->SetComplete(ETrue);
+ StoreEntryStreamsL(KStoreMIMEHeader | KStoreBodyText);
+ MoveToParentEntryL();
+
+ if( iTopMessagePart==EntryId() && iFinishedHeader)
+ {
+ iEmailEntry->SetVisible(ETrue);
+ iEmailEntry->SetInPreparation(EFalse);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ iEmailEntry->SetComplete(ETrue);
+ if(iEmailEntry->PartialDownloaded())
+ {
+ iEmailEntry->SetPartialDownloaded( EFalse );
+ }
+ // Main message.
+ StoreMessageEntryDetailsL();
+ return;
+ }
+ else if(iEmailEntry->iType == KUidMsvMessageEntry)
+ {
+ StoreMessageEntryDetailsL();
+ MoveToParentEntryL();
+ StoreMessageEntryDetailsL();
+ }
+ }
+
+ // A message requiring manual termination and not part way through
+ // a MIME part header.
+
+ User::LeaveIfError(iServerEntry->SetEntry(iTopMessagePart));
+ if(iEmailEntry)
+ {
+ delete iEmailEntry;
+ iEmailEntry=NULL;
+ }
+ iEmailEntry = new (ELeave) TMsvEmailEntry(iServerEntry->Entry());
+ iEmailEntry->SetVisible(ETrue);
+ iEmailEntry->SetInPreparation(EFalse);
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ if(iEmailEntry->PartialDownloaded())
+ {
+ iEmailEntry->SetPartialDownloaded( EFalse );
+ }
+
+ iEmailEntry->SetAttachment(iParent->At(0).iAttachment);
+ iEmailEntry->SetMHTMLEmail(iParent->At(0).iMHTML);
+ iEmailEntry->SetVCalendar(iParent->At(0).iVCal);
+ iEmailEntry->SetICalendar(iParent->At(0).iICal);
+
+ if(iEmailEntry->MHTMLEmail() == EFalse && iEmailEntry->Attachment() == EFalse && iRelatedAttachments !=EFalse)
+ {
+ iEmailEntry->SetAttachment(ETrue);
+ }
+ iRelatedAttachments=EFalse;
+
+ iEmailEntry->iSize=iParent->At(0).iSize;
+ iEmailEntry->SetMessageFolderType(iParent->At(0).iFolder);
+
+ User::LeaveIfError(iServerEntry->ChangeEntry(*iEmailEntry));
+
+ // Save the entry context and move it to null so that we're not locking any folders
+ iSavedEntryId = iServerEntry->Entry().Id();
+ User::LeaveIfError(iServerEntry->SetEntry(KMsvNullIndexEntryId));
+ RECVLOG(KMessageComplete)
+ }
+
+EXPORT_C void CImRecvConvert::SetCaf(CImCaf& aCaf)
+/**
+Initialise class CimCaf reference
+@param aCaf - Reference to a CAF instance
+*/
+ {
+ iCaf = &aCaf;
+ }
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::PrepareDecoder()
+//----------------------------------------------------------------------------------------
+ {
+ switch(iMimeParser->ContentEncoding())
+ {
+ case EEncodingTypeNone:
+ case EEncodingType7Bit:
+ case EEncodingType8Bit:
+ iAlgorithm = ENoAlgorithm;
+ RECVLOG(KCollectingData7)
+ break;
+ case EEncodingTypeQP:
+ iAlgorithm = EQPDecode;
+ RECVLOG(KCollectingDataQP)
+ break;
+ case EEncodingTypeBASE64:
+ iAlgorithm = EBase64Decode;
+ RECVLOG(KCollectingBase64)
+ break;
+ case EEncodingTypeUU:
+ iAlgorithm = EUUDecode;
+ RECVLOG(KCollectingUU)
+ break;
+ default: // EEncodingTypeUnknown, EEncodingTypeNone, EEncodingTypeBinary
+ iAlgorithm = ENoAlgorithm;
+ break;
+ } // switch
+ iPrepared = ETrue;
+ }
+
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::DecodeAndStoreLineL(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ //If bodytext has been stored using chunk storage mechanism.
+ if(iFirstLinePlainText && iEmailEntry->iType==KUidMsvEmailTextEntry)
+ {
+ iBodyId = iEmailEntry->Id();
+
+ delete iPlainBodyTextEntry;
+ iPlainBodyTextEntry = NULL;
+
+ iPlainBodyTextEntry = CMsvPlainBodyTextEntry::NewL(iStore8BitData, *iServerEntry, iMimeParser->CurrentCharsetL(), iCharConv->SystemDefaultCharset());
+ iFirstLinePlainText = EFalse;
+ }
+
+ TInt sourceLineLength=aSourceLine.Length();
+ TBool blankLine = EFalse;
+ TBool decodeError = EFalse;
+
+ // create a temporary buffer to write the decoded data into.
+ // This will always be as long as or shorter than the original.
+
+ HBufC8* convertedLine = HBufC8::NewLC(sourceLineLength+KConversionRemainderLength);
+ TPtr8 des(convertedLine->Des());
+
+ if(iFinalLine)
+ {
+ // We've got to the end of encoded section, so set in order to skip all
+ // trailing empty lines & postamble until we reach the next MIME/UUE boundary
+
+ RECVLOG(KSkippingData)
+ iSkipData = ETrue;
+ }
+ else
+ {
+ switch(iAlgorithm)
+ {
+ case EBase64Decode:
+ RECVLOG(KDecodingBase64);
+ // Keep track of the error so we don't append junk to the body-text
+ decodeError = iB64Codec.Decode(aSourceLine, des);
+ RECVLOG(KDecodedBase64);
+ break;
+ case EUUDecode:
+ RECVLOG(KDecodingUU);
+ //used to end a Mime encoded UU section else no purpose
+ iFinalLine=(aSourceLine.CompareF(KImcvUueLastLine)==0);
+ if(iFinalLine)
+ iCommitLine=EFalse;
+ else
+ {
+ //returns True on invalid data
+ if((!iFinalLine)&&(iUUCodec.Decode(aSourceLine, des)))
+ {
+ RECVLOG(KUUEDataCorrupt)
+ if(iAttachmentFileState==EFileIsOpen)
+ {
+ iAttachmentFileState=EFileIsCorrupt;
+ iLeaveError=KErrCorrupt;
+ CloseAttachmentFileL();
+ }
+ else
+ User::Leave(KErrCorrupt);
+ }
+ }
+ RECVLOG(KDecodedUU);
+ break;
+ case EQPDecode:
+ RECVLOG(KDecodingQP);
+ iQPCodec.Decode(aSourceLine, des);
+ RECVLOG(KDecodedQP);
+ break;
+ case ENoAlgorithm:
+ {
+ // If the data is to be stored in CRichText clean it up before copying it to the buffer
+ TLex8 lex(aSourceLine);
+ blankLine=ETrue;
+ while(blankLine && !lex.Eos())
+ {
+ blankLine = (lex.Peek()==KImcvSP || lex.Peek()==KImcvTab
+ || lex.Peek()==KImcvCR || lex.Peek()==KImcvLF);
+ lex.Inc();
+ }
+ des.Copy(aSourceLine);
+ }
+ default:
+ break;
+ } // end switch
+ // Commits the decoded data to the appropriate store or trashes the line
+ if(iCommitLine && !decodeError)
+ {
+ if (iLeftOver.Length())
+ {
+ des.Insert(0, iLeftOver);
+ iLeftOver.SetLength(0);
+ }
+
+ if(iCurrentPartIsRichText)
+ {
+ if (iBodyId==KMsvNullIndexEntryId)
+ {
+ iBodyId = iEmailEntry->Id();
+ }
+ //Store bodytext to message store as chunks.
+ if(iStorePlainBodyText && iEmailEntry->iType==KUidMsvEmailTextEntry)
+ {
+ iPlainBodyTextEntry->AddChunkL(des);
+ }
+ else
+ {
+ if (iStore8BitData)
+ iBodyBuf->InsertL(iBodyBuf->Size(), des);
+ else
+ WriteToBodyL(des, blankLine);
+
+ }
+ }
+ else
+ WriteToAttachmentL(des);
+ }
+ } // end else
+
+ CleanupStack::PopAndDestroy(); // convertedLine
+ }
+
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ParseMimeLineL(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ iTopPartialDownloadCounter+=aSourceLine.Length(); // increment the number of bytes of the attachment downloaded so far
+ if(!iMimeParser->IsBoundary(aSourceLine))
+ {
+ if(!iPrepared) // first line of the body
+ {
+ if (iMIMEPart_822Header)
+ {
+ // missing 822 part header. Revert to default.
+ EndOfHeaderMIMEProcessingL();
+ iMIMEPart_822Header=EFalse;
+ }
+ PrepareDecoder();
+ }
+
+ if (CheckUUEStartL(aSourceLine))
+ {
+ iAlgorithm=EUUDecode;
+ iCommitLine=EFalse;
+ }
+
+ /* since aSourceLine is a body part here, Set iSkipData to false, */
+ iSkipData=EFalse;
+
+ }
+ else
+ {
+ iParsedMimeBoundaryLast=ETrue;
+ // found a MIME boundary so store the current parts data and update its entry.
+
+ RECVLOG(KFoundMIMEBoundary)
+ iCommitLine=EFalse; //Dont store this line as its a boundary.
+
+ if(!iFirstBoundaryReached && iEntryType==EFolderEntry)
+ {
+ iFirstBoundaryReached=ETrue;
+ if (!iCurrentMultipartFolderEntryId)
+ iCurrentMultipartFolderEntryId = EntryId();
+ MoveToParentEntryL();
+ ResetForNewEntryL(iDefaultEntryType);
+ return; // First boundary encountered.
+ }
+
+ CloseAttachmentFileL();
+ if(iNewEntry)
+ {
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ StoreEntryStreamsL(KStoreMIMEHeader | KStoreBodyText);
+
+ if (iBodyId==KMsvNullIndexEntryId)
+ {
+ iBodyId=iEmailEntry->Id();
+ }
+ }
+
+ iSkipData = EFalse;
+
+ // check whether we just found the terminating boundary...
+ if(iMimeParser->IsTerminatingBoundary())
+ {
+ RECVLOG(KRemoveBoundary);
+ iMimeParser->RemoveBoundary();
+ RECVLOG(KRemovedBoundary);
+ iMIMEPart_822Header = EFalse;
+
+ if (EntryId()!=iTopMessagePart)
+ {
+ if(iEmailPart==KParentPart)
+ {
+ // rfc822 message which is not not multipart.
+ iEmailPart=KNoPart;
+ MoveToParentEntryL();
+
+ // Embedded message
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ StoreMessageEntryDetailsL();
+ }
+ else if (iEmailEntry->iType==KUidMsvMessageEntry)
+ {
+ // Moving up from a multi embedded rfc822 message.
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ StoreMessageEntryDetailsL();
+ }
+ }
+
+ MoveToParentEntryL();
+ iEntryDataSize = iEmailEntry->iSize;
+
+ if(iServerEntry->Entry().iType == KUidMsvFolderEntry)
+ {
+ iCurrentMultipartFolderEntryId = EntryId();
+ }
+ else if (EntryId()!=iTopMessagePart)
+ {
+ if(iEmailEntry->iType == KUidMsvMessageEntry)
+ {
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ StoreMessageEntryDetailsL();
+ }
+ MoveToParentEntryL();
+ iEntryDataSize += iEmailEntry->iSize;
+ }
+
+ if(!iNewEntry)
+ ResetForNonMimeEntryL();
+
+ RECVLOG(KSkippingData)
+ iSkipData = ETrue;
+ iDefaultEntryType=ETextEntry;
+ }
+ else // if regular boundary
+ {
+ RECVLOG(KSectionHeader)
+ if(iEmailPart==KParentPart && EntryId()!=iTopMessagePart)
+ {
+ // rfc822 message which is not not multipart.
+ iEmailPart=KNoPart;
+ MoveToParentEntryL();
+ // Embedded message
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ StoreMessageEntryDetailsL();
+ }
+
+ if (!iCurrentMultipartFolderEntryId && iEmailPart==KNoPart)
+ MoveToParentEntryL();
+
+ ResetForNewEntryL(iDefaultEntryType);
+ }
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::EndOfHeaderProcessingL()
+//----------------------------------------------------------------------------------------
+ {
+ CreateEntryL();
+ StoreEntryStreamsL(KStore822Header);
+ iEntryDataSize = 0;
+ }
+
+
+// Have just finished processing header, what next.. ?
+// All MIME entry entry creation takes place here.
+//
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::EndOfHeaderMIMEProcessingL()
+//----------------------------------------------------------------------------------------
+ {
+ if (iMimeParser->IsMessageDigest())
+ iDefaultEntryType=EMessageEntry;
+
+ if (iMimeParser->VCard() || iMimeParser->VCalendar() || iMimeParser->ICalendar())
+ {
+ iCurrentPartIsRichText = EFalse;
+ iEntryType = EAttachmentEntry;
+ }
+
+ // Don't create entry if an embedded message header.
+ if (!iMIMEPart_822Header || iTopMessagePart==EntryId() )
+ CreateEntryL();
+
+ if (!iMIMEPart_822Header && !iMimeParser->MimeFieldsExist() && iDefaultEntryType==EMessageEntry)
+ {
+ // MIME header not present. So expecting embedded 822 header
+ iEmailPart = KParentPart;
+ iMIMEPart_822Header=ETrue;
+ iEntryType=ETextEntry;
+ }
+ else if (iMimeParser->ContentType()==EMimeMessage)
+ {
+ // Having received A MIME header of type message/..., store and continue.
+
+ StoreEntryStreamsL(KStore822Header|KStoreMIMEHeader);
+ iMimeParser->ResetForNewEntry();
+ iMIMEPart_822Header=ETrue;
+
+ // Non-multipart embedded message.
+ if (iTopMessagePart==EntryId())
+ {
+ iEntryType = EMessageEntry;
+ ResetForNonMimeEntryL();
+ }
+ else
+ iEntryType=ETextEntry;
+ }
+ else if ( (iTopMessagePart==EntryId() || iMIMEPart_822Header)&&(!iReceivingHeadersOnly) )
+ {
+ // Main rfc822 header or embedded header.
+
+ TImEmailFolderType folderType=iMimeParser->MessageFolderType();
+ if (iMIMEPart_822Header)
+ {
+ iEmailEntry->iDetails.Set(iOutputHeader->From());
+ iEmailEntry->iDescription.Set(iOutputHeader->Subject());
+ iMIMEPart_822Header=EFalse;
+ }
+
+ if (iRfc822Token->i822FieldsExist)
+ StoreEntryStreamsL(KStore822Header|KStoreMIMEHeader);
+ else
+ StoreEntryStreamsL(KStoreMIMEHeader);
+
+
+ if (iMimeParser->ContentType()==EMimeMultipart)
+ {
+ RECVLOG(KSkippingData)
+ ResetForNewEntryL(EFolderEntry);
+ iMimeParser->SetMessageFolderType(folderType);
+ iSkipData = ETrue;
+ iEmailPart = KMultiPart;
+ iCurrentMultipartFolderEntryId=0;
+ }
+ else
+ {
+ // Not multipart but some header data to store.
+ iEntryDataSize = 0;
+ }
+
+ CreateEntryL();
+ }
+
+ iRfc822Token->i822FieldsExist=EFalse;
+ iMimeParser->ResetMimeFieldsExist();
+
+ iFinishedHeader = iMIMEPart_822Header ? EFalse:ETrue;
+ iSkipData = iMimeParser->ContentType()==EMimeMultipart ? ETrue:EFalse;
+ }
+
+
+// Non Mime body parsing
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ParseBodyLineL(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+
+ TInt len=aSourceLine.Length();
+ iTopPartialDownloadCounter+=len; // added for TOP. increment the number of bytes of the attachment downloaded so far
+ TMsvId id=0;
+ if (iBodyId==KMsvNullIndexEntryId)
+ {
+ iBodyId=iEmailEntry->Id();
+ }
+
+ // first check whether this line is a UUEncode start boundary
+ if(CheckUUEStartL(aSourceLine))
+ {
+ RECVLOG(KFoundUUEStartBoundary)
+ TFileName tempStore = iAttachmentName;
+ id = EntryId();
+ if (!iNewNonMIMEBodyPart || id!=iTopMessagePart) // main message entry
+ {
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ StoreEntryStreamsL(KStoreBodyText);
+ }
+ MoveToParentEntryL();
+ if ( !CreateNonMIMEFolderEntryL(id))
+ ResetForNonMimeEntryL();
+
+ iEntryType = EAttachmentEntry;
+ CreateEntryL();
+ SetAttachmentName(tempStore);
+
+ iCurrentPartIsRichText = EFalse;
+ iAlgorithm=EUUDecode;
+ iCommitLine=EFalse;
+ if(!iSavingAttachments)
+ {
+ RECVLOG(KSkippingData)
+ iSkipData=ETrue;
+ }
+ iNewNonMIMEBodyPart=EFalse;
+ }
+ else if(aSourceLine.CompareF(KImcvUueEnd)==0) // Checks for the UUEncode end boundary
+ {
+ RECVLOG(KFoundUUEEndBoundary)
+ CloseAttachmentFileL();
+ StoreEntryDataL();
+ MoveToParentEntryL();
+ iSkipData = EFalse;
+ iCommitLine=EFalse;
+ iNewNonMIMEBodyPart=ETrue;
+ }
+ else if (iNewNonMIMEBodyPart && !( len==2 && aSourceLine[0]==KImcvCR && aSourceLine[1]==KImcvLF ))
+ {
+ id = EntryId();
+ if (!iNewNonMIMEBodyPart || id!=iTopMessagePart)
+ {
+ iEmailEntry->SetComplete(ETrue);
+ iEmailEntry->SetBodyTextComplete(ETrue);
+ StoreEntryStreamsL(KStoreBodyText);
+ }
+ MoveToParentEntryL();
+ if ( !CreateNonMIMEFolderEntryL(id))
+ ResetForNonMimeEntryL();
+ iAlgorithm=ENoAlgorithm;
+ iEntryType = ETextEntry;
+ CreateEntryL();
+ iNewNonMIMEBodyPart=EFalse;
+ }
+ }
+
+
+
+//----------------------------------------------------------------------------------------
+TBool CImRecvConvert::CreateNonMIMEFolderEntryL(TMsvId aCurrentId)
+//----------------------------------------------------------------------------------------
+ {
+ if ( aCurrentId==iTopMessagePart || iCurrentMultipartFolderEntryId )
+ return EFalse;
+
+ // Create Folder.
+ iServerEntry->SetEntry(iTopMessagePart);
+ iEmailPart = KMultiPart;
+ iEntryType = EFolderEntry;
+ CreateEntryL();
+
+ // Move existing child entry under folder.
+ TMsvId destId = EntryId();
+ iServerEntry->SetEntry(iTopMessagePart);
+ iServerEntry->MoveEntryWithinService(aCurrentId, destId);
+ User::LeaveIfError(iServerEntry->SetEntry(aCurrentId));
+ User::LeaveIfError(iServerEntry->SetEntry(iServerEntry->Entry().Parent()));
+
+ // Create MimeHeader.
+ iEmailEntry->SetMessageFolderType(iMimeParser->MessageFolderType());
+
+ RECVLOG(KResetForNewEntry)
+
+ iMimeParser->ResetForNewEntry();
+ if (iStore8BitData)
+ {
+ //Create a buffer to hold the body text as it is down loaded.
+ delete iBodyBuf;
+ iBodyBuf = NULL;
+ iBodyBuf = CBufFlat::NewL(KBodyTextChunkSizeBytes);
+ }
+ else
+ {
+ iOutputBody->Reset();
+ }
+
+ ResetForNonMimeEntryL();
+ RECVLOG(KResetedForNewEntry)
+ return ETrue;
+ }
+
+
+//----------------------------------------------------------------------------------------
+TBool CImRecvConvert::CreateAttachmentL()
+//----------------------------------------------------------------------------------------
+ {
+ // Get and set Attachment File path
+ // added to support TOP command. Reset the download counter each time we have a new
+ // attachment
+ iTopPartialDownloadCounter = 0;
+
+ // Need to check that the complete filename: iAttachmentFullPath & iAttachmentName
+ // does not exceed 256 characters. Greater than this and it cannot be saved as a file.
+
+ TBool addExtension=ETrue;
+ if(iAttachmentName.Length() == 0) //i.e. problem with Attachment name
+ {
+ // No filename present. Generate one.
+ if(iMimeParser->ContentDescription().Length()!=0)
+ {
+ // Use ContentDescription() as default name
+ // - as this is more informative than the default
+
+ TLex sourceLineLex = iMimeParser->ContentDescription();
+ ExtractFilename(sourceLineLex, iAttachmentName);
+ }
+ else
+ iAttachmentName = *iDefaultAttachmentName;
+ }
+ else
+ {
+ // Filename present. Check it is valid.
+ ReplaceInvalidCharacters(iAttachmentName);
+ if (iAttachmentName.Locate(KImcvFullStop)!=KErrNotFound)
+ addExtension=EFalse;
+ }
+
+ // Check length.
+ TInt maxFilenameLength = KMaxFileName-KMaxExtensionLength;
+
+ // Truncate filename if too long.
+ if (maxFilenameLength < iAttachmentName.Length())
+ iAttachmentName.SetLength(maxFilenameLength);
+
+ if (addExtension)
+ AddFileExtension();
+
+ CMsvStore* store = iServerEntry->EditStoreL();
+ CleanupStack::PushL(store);
+ CMsvAttachment* attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
+ CleanupStack::PushL(attachment);
+ attachment->SetAttachmentNameL(iAttachmentName);
+
+ // DEF071099
+ HBufC8* buf = HBufC8::NewLC(iMimeParser->ContentSubType().Length() + iMimeParser->ContentTypeDescription().Length() + 1);
+ TPtr8 ptr(buf->Des());
+ ptr.Copy(iMimeParser->ContentTypeDescription());
+ ptr.Append(KImcvForwardSlash);
+ ptr.Append(iMimeParser->ContentSubType());
+
+ attachment->SetMimeTypeL(ptr);
+ CleanupStack::PopAndDestroy(buf);
+ // DEF071099
+
+ RFile file;
+ // Behaviour for attachment creation alters if it's a CAF session
+ if(ImCafRegistered())
+ {
+ iCaf->PrepareProcessingL(); // Init the CAF session
+ RFile startFile;
+ TFileName suggestedFileName;
+ if(iCaf->GetSuggestedAttachmentFileName(suggestedFileName) == KErrNone) // CAF agent may provide a filename
+ {
+ store->CreateShareProtectedAttachmentL(suggestedFileName,startFile,attachment);
+ }
+ else
+ {
+ store->CreateShareProtectedAttachmentL(iAttachmentName,startFile,attachment);
+ }
+ iCaf->StartProcessing(iDefaultAttachmentName->Des(),attachment->FilePath(),*iServerEntry,startFile); // Init the CAF session
+ startFile.Close(); // Safe to close as CimCaf Duplicate()'s
+ }
+ else
+ {
+ // Normal behaviour
+ store->AttachmentManagerExtensionsL().CreateAttachmentL(iAttachmentName,file,attachment);
+ iAttachmentFile.SetFileHandle(file,TImAttachmentFile::EImFileWrite);
+ }
+ // CreateAttachmentL takes ownership of CMsvAttachment so if call was successful we can pop it here
+ CleanupStack::Pop(attachment);
+
+ store->CommitL();
+ CleanupStack::PopAndDestroy(store);
+
+ if(KErrNone!=iLeaveError)
+ {
+ iAttachmentFileState=EFileFailedToOpen;
+ CloseAttachmentFileL();
+ return EFalse;
+ }
+
+ iAttachmentFileState=EFileIsOpen;
+ return ETrue;
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::WriteToAttachmentL(const TDesC8& text)
+//----------------------------------------------------------------------------------------
+ {
+ if ( (iAttachmentFileState==EFileIsClosed || iAttachmentFileState==EFileNotOpen)
+ && CreateAttachmentL() && iEntryType!=EHtmlEntry)
+ iEmailEntry->SetAttachment(ETrue);
+
+ if(iAttachmentFileState!=EFileIsOpen || !text.Length())
+ {
+ RECVLOG(KSkippingData)
+ iSkipData = ETrue;
+ }
+
+ // write decoded data into a file if there is any data there to write.
+
+ RECVLOG(KWritingToFile)
+
+ // Convert text before writing to attachment.
+
+ if(ImCafProcessing())
+ {
+ iLeaveError = iCaf->WriteData(text);
+ }
+ else
+ {
+ iLeaveError=iAttachmentFile.WriteFile(text);
+ }
+
+ if(KErrNone==iLeaveError)
+ iEntryDataSize += text.Length();
+ else
+ {
+ // the file write failed (eg.there is no space left), set new file state and skip
+ RECVLOG(KFailedToWriteToFile)
+ iAttachmentFileState=EFileIsIncomplete;
+ CloseAttachmentFileL();
+ }
+ RECVLOG(KWroteToFile)
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::CloseAttachmentFileL()
+//----------------------------------------------------------------------------------------
+ {
+ // If anything bad happened a message is sent to the parts CRichText
+ switch(iAttachmentFileState)
+ {
+ case EFileNotOpen:
+ iAttachmentFileState=EFileIsClosed;
+ case EFileIsClosed: //do nothing - this shouldn't happen
+ break;
+ case EFileIsOpen:
+ // SUCCESSFUL attachment decode
+ RECVLOG(KClosingAttachFile)
+ if(ImCafProcessing())
+ {
+ iCaf->EndProcessingL();
+ }
+ else
+ {
+ iAttachmentFile.CloseFile();
+ }
+ iAttachmentFileState=EFileIsClosed;
+ iEmailEntry->SetComplete(ETrue);
+ break;
+ case EFileIsIncomplete: // file write failed
+ case EFileFailedToOpen: // can't open attach file
+ case EFileIsCorrupt: // UU data being decoded is corrupt
+ RECVLOG(KClosingAttachFile)
+ if(ImCafProcessing())
+ {
+ iCaf->EndProcessingL();
+ }
+ else
+ {
+ iAttachmentFile.CloseFile(); //file has to be closed before it can be deleted
+ }
+ RECVLOG(KDeletingAttachFile)
+ {
+ // NOTE - need the braces to stop error for re-definition of store
+ CMsvStore* store = iServerEntry->EditStoreL();
+ CleanupStack::PushL(store);
+ // v2 supports multiple attachments under one attachment entry
+ TInt attachmentCount = store->AttachmentManagerL().AttachmentCount();
+ for(TInt i=0;i<attachmentCount;i++)
+ {
+ // Remove [0] as array is shuffled. Once index[n] is removed n+1 becomes n
+ store->AttachmentManagerExtensionsL().RemoveAttachmentL(0);
+ }
+ if(attachmentCount)
+ {
+ store->CommitL();
+ }
+ CleanupStack::PopAndDestroy(store);
+ }
+ iEmailEntry->SetAttachment(EFalse);
+ iAttachmentFileState=EFileIsClosed;
+ RECVLOG(KDeletedAttachFile)
+
+ if(iSavingAttachments && !iStore8BitData)
+ {
+ WriteToBodyL(KImcvParagraph);
+ WriteToBodyL(*iRemovedAttachmentTag); //lost attachment - notify user
+ TBuf8<KMaxFileName> name;
+ name.Copy(iAttachmentName); //16 to 8
+ WriteToBodyL(name);
+ WriteToBodyL(KImcvParagraph);
+ }
+
+ User::Leave(iLeaveError);
+ // Skip any remaining encoded data in message
+ break;
+ case EFileTopIncomplete:
+ RECVLOG(KClosingAttachFile)
+ if(ImCafProcessing())
+ {
+ iCaf->EndProcessingL();
+ }
+ else
+ {
+ iAttachmentFile.CloseFile(); //file has to be closed before it can be deleted
+ }
+
+ // added for TOP command. Ensure we know correct amount of data for later redownload
+ iSizeOfAttachmentsRemoved+=iTopPartialDownloadCounter;
+
+
+ RECVLOG(KDeletingAttachFile)
+ {
+ // NOTE - need the braces to stop error for re-definition of store
+ CMsvStore* store = iServerEntry->EditStoreL();
+ CleanupStack::PushL(store);
+
+ TInt attachmentCount = store->AttachmentManagerL().AttachmentCount();
+ for(TInt i=0;i<attachmentCount;i++)
+ {
+ // Remove [0] as array is shuffled. Once index[n] is removed n+1 becomes n
+ store->AttachmentManagerExtensionsL().RemoveAttachmentL(0);
+ }
+ if(attachmentCount)
+ {
+ store->CommitL();
+ }
+ CleanupStack::PopAndDestroy(store);
+ }
+ iEmailEntry->SetAttachment(EFalse);
+ iAttachmentFileState=EFileIsClosed;
+ RECVLOG(KDeletedAttachFile);
+ TMsvId id = EntryId();
+ TMsvId parent = iServerEntry->Entry().Parent();
+ MoveToParentEntryL();
+ TMsvId setTo=iServerEntry->Entry().Id();
+ if(setTo!=parent)
+ {
+ iServerEntry->SetEntry(parent);
+ }
+ User::LeaveIfError(iServerEntry->DeleteEntry(id));
+ iServerEntry->SetEntry(setTo);
+ break;
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CImRecvConvert::LineIsAllWhitespace()
+//----------------------------------------------------------------------------------------
+ {// returns 1 if all elements of the current line are whitespace
+ TBool spaceFound = 1;
+ TLex8 aLex = iLineLex;
+
+ while (spaceFound && aLex.Peek()!=KImcvCR)
+ {
+ if (aLex.Peek()==KImcvSP)
+ {
+ spaceFound = 1;
+ aLex.Inc();
+ }
+ else spaceFound = 0;
+ }
+ return (spaceFound);
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CImRecvConvert::CheckUUEStartL(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ // Checks if the descriptor contains the UUE begin header
+ // Extracts the file name if it is
+
+ TInt sourceLength = aSourceLine.Length();
+ if(sourceLength < KImcvUueStart().Length()+3) // can't be it, it's not long enough
+ 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* _ptr = aSourceLine.Ptr();
+ TInt length=KImcvUueStart().Length();// this defines length as 6 ie. "b e g i n "
+ if(TChar(_ptr[length]).IsDigit() && TChar(_ptr[length+1]).IsDigit() && TChar(_ptr[length+2]).IsDigit())
+ {
+ // we've found 'begin ###' at the start of a line -
+ // that's about as good as we can do
+ // now grab the file name and paste it into the document
+ // Extract filename from string, removing any surrounding quote marks
+
+ HBufC16* pBuf16 = HBufC16::NewLC(aSourceLine.Length());
+ pBuf16->Des().Copy(aSourceLine);
+ TLex sourceLineLex = pBuf16->Ptr();
+ //parse until start of filename and mark
+ length+=3; // length (init'd to 6 above) now equals 9 ie. "begin ###"
+ sourceLineLex.Inc(length); // skips "begin ###"
+ sourceLineLex.SkipSpace(); // skips any leading whitespace
+
+ ExtractFilename(sourceLineLex, iAttachmentName);
+ CleanupStack::PopAndDestroy(); // pBuf8
+ return ETrue;
+ }
+ }
+
+ return EFalse;
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::AddFileExtension()
+//----------------------------------------------------------------------------------------
+ {
+ switch (iMimeParser->ContentType())
+ {
+ case EMimeText:
+ // Add on extension to make opening file from email editor possible.
+
+ if ( iMimeParser->ContentSubType()==KImcvHtml )
+ {
+ iAttachmentName.Append(KHtmlExtension);
+ }
+ else if ( iMimeParser->ContentSubType()==KImcvRtf )
+ {
+ iAttachmentName.Append(KRtfExtension);
+ }
+ else if (iMimeParser->VCard())
+ {
+ iAttachmentName.Append(KVCardExtension);
+ }
+ else if (iMimeParser->VCalendar())
+ {
+ iAttachmentName.Append(KVCalExtension);
+ }
+ else if (iMimeParser->ICalendar())
+ {
+ iAttachmentName.Append(KICalExtension);
+ }
+ else //if ( iMimeParser->ContentSubType()==KImcvPlain)
+ {
+ iAttachmentName.Append(KTextExtension);
+ }
+ break;
+ case EMimeImage:
+ case EMimeAudio:
+ case EMimeVideo:
+ if ( (iMimeParser->ContentSubType()==KImcvBmp)
+ || (iMimeParser->ContentSubType()==KImcvGif)
+ || (iMimeParser->ContentSubType()==KImcvJpeg)
+ || (iMimeParser->ContentSubType()==KImcvTiff)
+ || (iMimeParser->ContentSubType()==KImcvWav) )
+ {
+ TBuf<KMaxExtensionLength> buf;
+ buf.Copy(iMimeParser->ContentSubType());
+ iAttachmentName.Append(KImcvFullStop);
+ iAttachmentName.Append(buf);
+ }
+ break;
+ default:
+ break;
+ } // End switch
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::WriteToBodyL(const TDesC8& aText, TBool aBlankLine)
+//----------------------------------------------------------------------------------------
+ {
+ RECVLOG(KWritingToBody)
+
+ if(aText.Length() && aText[aText.Length()-1]==CEditableText::ELineBreak )
+ RECVLOG(KLineHasLineBreak)
+ TInt pos = iOutputBody->DocumentLength();
+
+ // Add bits of body text, converting along the way, till no characters left
+ // .. to convert.
+
+ if(aBlankLine)
+ {
+ RECVLOG(KBlankLine);
+ iOutputBody->InsertL(pos, CEditableText::EParagraphDelimiter);
+ pos++;
+ return;
+ }
+
+ // Convert text before writing to body.
+ TInt rem = 0;
+ HBufC16* text16=HBufC16::NewLC(aText.Length());
+ TPtr16 ptr16(text16->Des());
+ TInt unconvertedChars, firstPos; // not used
+ rem = iCharConv->ConvertToOurCharsetL(aText, ptr16, unconvertedChars, firstPos);
+ if (rem < 0) // error
+ {
+ // Copy unconverted characters.
+ Append(ptr16, aText);
+ iOutputBody->InsertL(pos, ptr16);
+ CleanupStack::PopAndDestroy(); // text16
+ return;
+ }
+ else if (rem && rem < KConversionRemainderLength)
+ iLeftOver.Copy(aText.Right(rem));
+
+ // Make sure that the line is not CRLF terminated
+ // - replace with a line break if necessary.
+
+ TInt length = ptr16.Length();
+ switch(iAlgorithm)
+ {
+ case EBase64Decode:
+ case EUUDecode:
+ {
+ // Check for CRLF throughout the string.
+
+ if (!length)
+ break; // String length zero.
+
+ if (iEncounteredLineEndingInCarriageReturn)
+ {
+ pos--; // overwrite the stored CR.
+ ptr16[0] = CEditableText::ELineBreak;
+ }
+ iEncounteredLineEndingInCarriageReturn = ptr16[length-1]==KImcvCR ? ETrue:EFalse;
+
+ TInt start = 0;
+ TInt offset = ptr16.Find(KImcvCRLF16);
+
+ while (offset != KErrNotFound)
+ {
+ ptr16[offset] = CEditableText::ELineBreak;
+ const TDesC& buf = ptr16.Mid(start, offset-start+1);
+ iOutputBody->InsertL(pos, buf);
+ pos += buf.Length();
+ start=offset+2; // Skip the LF char.
+ offset = ptr16.Find(KImcvCRLF16);
+ }
+
+ if (start<length)
+ {
+ const TDesC& buf = ptr16.Right(length-start);
+ iOutputBody->InsertL(pos, buf);
+ pos += buf.Length();
+ }
+ }
+ break;
+
+ case EQPDecode:
+ case ENoAlgorithm:
+ default:
+ // Check for CRLF at end of line.
+ if(length>=2 && ptr16[length-2]==KImcvCR && ptr16[length-1]==KImcvLF)
+ {
+ ptr16[length-2] = CEditableText::ELineBreak;
+ ptr16.SetLength(length-1);
+ }
+
+ const TDesC& buf = text16->Des();
+ iOutputBody->InsertL(pos, buf);
+ pos += buf.Length();
+ }
+
+ CleanupStack::PopAndDestroy(text16);
+
+ RECVLOG(KWroteToBody)
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::WriteToBodyL(const TDesC16& aText)
+//----------------------------------------------------------------------------------------
+ {
+ RECVLOG(KWritingToBody)
+ if (aText.Length() && aText[aText.Length()-1]==CEditableText::ELineBreak)
+ RECVLOG(KLineHasLineBreak)
+
+ TInt pos = iOutputBody->Read(0).Length();
+ // get the text in before the paragraph marker that's always there
+ pos = pos-1 < 0 ? 0 : pos-1;
+ iOutputBody->InsertL(pos,aText);
+
+ RECVLOG(KWroteToBody)
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ParseRecipientListL(CDesCArray& aList)
+//----------------------------------------------------------------------------------------
+ {
+ HBufC8* pBuf = HBufC8::NewLC(KMaxIMailHeaderReadLineLength);
+ TPtrC8 source(iRfc822Token->OutputLine()->Ptr(), iRfc822Token->OutputLine()->Length());
+ const TUint8* ptr = source.Ptr();
+ const TUint8* lastCharPtr = ptr + source.Length() - 1;
+ TUint8 lookFor = 0;
+ TInt count = 0;
+ TBool finishedEntry = EFalse;
+
+ // get past white space
+ while(*ptr&&((*ptr==KImcvSP)||(*ptr==KImcvSemiColon))) ptr++;
+
+ // Entries are separated by commas or semicolons.
+ // Separators do not count if they appear within
+ // "", <>, () or embedded series of these, eg "(one, two)"
+ // so we need to keep track of these, including nesting.
+ while(*ptr && ptr <= lastCharPtr)
+ {
+ if(!pBuf->Length())
+ {
+ finishedEntry = EFalse;
+ }
+
+ switch(*ptr)
+ {
+ case KImcvLeftBracket:
+ if(lookFor==KImcvRightBracket)
+ { // We've already had a "(", so now we need another one
+ count++;
+ }
+ else if(lookFor==0)
+ { //We weren't looking for anything else, now we need to
+ lookFor = KImcvRightBracket;
+ count = 1;
+ }
+ // else we were already looking for something else, ignore this
+ break;
+ case KImcvLeftChevron:
+ if(lookFor==KImcvRightChevron)
+ { //We've already had a "<", so now we need another one
+ count++;
+ }
+ else if(lookFor==0)
+ { //We weren't looking for anything else
+ lookFor = KImcvRightChevron;
+ count = 1;
+ }
+ // else we were already looking for something else, ignore this
+ break;
+ case KImcvDoubleQuote:
+ if(lookFor==KImcvDoubleQuote)
+ { // We already had a quote, so this matches it
+ lookFor = 0;
+ }
+ else if(lookFor==0)
+ { //We weren't looking for anything else
+ lookFor = KImcvDoubleQuote;
+ }
+ // else we were already looking for something else, ignore this
+ break;
+ case KImcvRightBracket:
+ case KImcvRightChevron:
+ if(*ptr == lookFor)
+ { //If we have found what we were looking for, decrease the count
+ count--;
+ if(count==0)
+ { // Got everything, now we're not looking for anything
+ lookFor = 0;
+ }
+ // else keep looking for the same thing again
+ }
+ // else we're looking for something else, ignore it
+ break;
+ case KImcvComma:
+ case KImcvSemiColon:
+ // If we're not looking for anything, we're finished
+ if (lookFor == 0)
+ finishedEntry = ETrue;
+ // else this comma or semicolon is part of a different token, ignore it
+ break;
+ }
+
+ if(!finishedEntry)
+ {
+ // check we're not about to blow the buffer
+ if(pBuf->Length() >= pBuf->Des().MaxLength())
+ {
+ // ReAlloc will delete the original memory pointed to by pBuf
+ // so we must take it off the cleanup stack...
+ CleanupStack::Pop(pBuf);
+ pBuf = pBuf->ReAlloc(pBuf->Length() + 64); // arbitrary extension
+ // ...and put the new one on instead
+ CleanupStack::PushL(pBuf);
+ }
+ pBuf->Des().Append((TChar)*ptr);
+ // move to the next character
+ ptr++;
+ }
+ else
+ {
+ // that's it! store the address away
+ HBufC16* pBuf16 = HBufC16::NewLC(pBuf->Des().Length());
+ pBuf16->Des().Copy(pBuf->Des());
+ aList.AppendL( (HBufC16&) *pBuf16 );
+ CleanupStack::PopAndDestroy(); // pBuf16
+ pBuf->Des().SetLength(0);
+ finishedEntry = EFalse; //Ready for next entry
+
+ // get past the separator
+ ptr++;
+
+ // get past white space (& any other separators)
+ while(*ptr && (*ptr==KImcvSP || *ptr==KImcvTab || *ptr==KImcvComma || *ptr==KImcvSemiColon)) ptr++;
+ }
+ }
+ // catch the last name in the list
+ if (pBuf)
+ {
+ TInt recipientLength = pBuf->Length();
+ if (recipientLength)
+ {
+ HBufC16* pBuf16 = HBufC16::NewLC(recipientLength);
+ pBuf16->Des().Copy(*pBuf);
+ aList.AppendL(*pBuf16);
+ CleanupStack::PopAndDestroy(); // pBuf16
+ }
+ }
+
+ CleanupStack::PopAndDestroy(); // pBuf
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ExtractFilename(TLex& aLex, TDes& rFileName)
+//----------------------------------------------------------------------------------------
+ {
+ // This function steps through the filename extracting the bare name and checking
+ // the length is less than the max of 256 for EPOC ;checks that all chars are legal for EPOC32
+
+ TChar endChar = KImcvSemiColon;
+
+ aLex.SkipSpace();
+
+ if (aLex.Peek()==KImcvDoubleQuote)
+ {
+ aLex.Inc(); // step over the " character
+ endChar = KImcvDoubleQuote;
+ }
+
+ aLex.Mark(); // marks where we are as this is the first char of the filename
+
+ TInt fileNameLength = 0;
+ TInt maxFileNameLength = rFileName.MaxLength();
+
+ while(!aLex.Eos() && aLex.Peek()!=endChar && aLex.Peek()!=KImcvCR && fileNameLength < maxFileNameLength)
+ //spools through the string until the end and marks char before quote (such that
+ // it extracts only the filename), EOS or before the maximum buffer length is exceeded
+ {
+ fileNameLength++;
+ aLex.Inc();
+ }
+
+ TPtrC marked = aLex.MarkedToken();
+ rFileName.Copy(marked);
+
+ ReplaceInvalidCharacters(rFileName);
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::SetAttachmentName(TDes& aFileName)
+//----------------------------------------------------------------------------------------
+ {
+ ReplaceInvalidCharacters(aFileName);
+ iAttachmentName.Zero();
+
+ TUint delimiter = '.';
+ TInt maxLength = iAttachmentName.MaxLength();
+
+ __ASSERT_DEBUG(
+ maxLength >= aFileName.Length(), gPanic(KPanicReadLengthTooLarge)
+ );
+
+ iAttachmentName.Copy(aFileName);
+ TInt attachmentLen = iAttachmentName.Length();
+ if (attachmentLen == 0)
+ iAttachmentName = *iDefaultAttachmentName;
+ else if (iAttachmentName[0] == delimiter && maxLength >= attachmentLen + iDefaultAttachmentName->Length())
+ iAttachmentName.Insert(0, *iDefaultAttachmentName);
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::ReplaceInvalidCharacters(TDes& rFileName)
+//----------------------------------------------------------------------------------------
+ {
+ TInt length = rFileName.Length();
+ for(TInt index=0; index < length; index++)
+ {
+ //parse extracted filename and replace any illegal chars with a default.
+
+ if(IsIllegalChar((TUint)rFileName[index]))
+ rFileName[index] = KImcvDefaultChar;
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::StoreEntryStreamsL()
+//----------------------------------------------------------------------------------------
+ {
+ StoreEntryStreamsL(KStoreBodyText|KStore822Header|KStoreMIMEHeader);
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::StoreEntryStreamsL(TInt aSettings)
+//----------------------------------------------------------------------------------------
+ {
+ RECVLOG(KStartStoringEntryStream);
+ CMsvStore* entryStore = NULL;
+ TBool commitStore = EFalse;
+ const TInt KChunkSize = 1024;
+
+ if (iReceivingHeadersOnly==EFalse)
+ {
+ //If bodytext has been stored using chunk mechanism.
+ if ((aSettings & KStoreBodyText ) && iPlainBodyTextEntry && iEmailEntry->iType==KUidMsvEmailTextEntry)
+ {
+ iPlainBodyTextEntry->TryCommitL();
+ TRAPD(error, entryStore = iServerEntry->ReadStoreL());
+ if(error==KErrNone)
+ {
+ CleanupStack::PushL(entryStore);
+ CMsvPlainBodyText* plainBodyText = entryStore->InitialisePlainBodyTextForReadL(KChunkSize);
+ iEntryDataSize += plainBodyText->Size();
+ delete plainBodyText;
+ CleanupStack::PopAndDestroy();//entryStore
+ }
+ }
+
+ TRAPD(error, entryStore = iServerEntry->EditStoreL());
+ if(error==KErrNone) // if store does not exist then the entry is the wrong type
+ {
+ CleanupStack::PushL(entryStore);
+ if (aSettings & KStore822Header)
+ Store822HeaderL(*entryStore, commitStore);
+
+ if (aSettings & KStoreMIMEHeader)
+ StoreMIMEHeaderL(*entryStore, commitStore);
+
+ if (aSettings & KStoreBodyText)
+ StoreBodyTextL(*entryStore, commitStore);
+
+ // only commit to the store if I wrote something into it
+ if (commitStore)
+ {
+ RECVLOG(KStoringEntryStream);
+ entryStore->CommitL();
+ }
+ StoreEntryDataL();
+ CleanupStack::PopAndDestroy(); //entryStore
+ }
+ }
+ RECVLOG(KDoneStoringEntryStream);
+ }
+
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::Store822HeaderL(CMsvStore& aStore, TBool& aCommit)
+//----------------------------------------------------------------------------------------
+ {
+ if(iEmptyHeaderSize<(iOutputHeader->DataSize()))
+ {
+ iEntryDataSize += iOutputHeader->DataSize()-iEmptyHeaderSize;
+ RECVLOG(KStoringHeader);
+ iOutputHeader->StoreL(aStore);
+ RECVLOG(KStoredHeader);
+ aCommit = ETrue;
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::StoreMIMEHeaderL(CMsvStore& aStore, TBool& aCommit)
+//----------------------------------------------------------------------------------------
+ {
+ if(iMimeParser->MimeHeaderSize())
+ {
+ RECVLOG(KStoringMIMEHeader);
+ iMimeParser->StoreMimeHeaderWithoutCommitL(aStore);
+ aCommit = ETrue;
+ RECVLOG(KStoredMIMEHeader);
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::StoreBodyTextL(CMsvStore& aStore, TBool& aCommit)
+//----------------------------------------------------------------------------------------
+ {
+ if (iStore8BitData)
+ {
+ if(iBodyBuf->Size())
+ {
+ iEntryDataSize += iBodyBuf->Size();
+ RECVLOG(KStoring8BitBody);
+ iBodyText->StoreL(aStore, *iBodyBuf);
+ aCommit = ETrue;
+ iBodyId=iServerEntry->Entry().Id();
+ RECVLOG(KStored8BitBody);
+ }
+ }
+ else
+ {
+ if(iOutputBody->DocumentLength())
+ {
+ iEntryDataSize += iOutputBody->DocumentLength();
+ RECVLOG(KStoringBody);
+ aStore.StoreBodyTextL(*iOutputBody);
+ iBodyId=iServerEntry->Entry().Id();
+ aCommit = ETrue;
+ RECVLOG(KStoredBody);
+ }
+ }
+
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CImRecvConvert::StoreEntryDataL()
+//----------------------------------------------------------------------------------------
+ {
+ // NB function should only be called if a whole email is being processed
+ TBool commit=EFalse;
+ RECVLOG(KUpdatingEntry)
+
+ if (iEmailEntry->iType==KUidMsvMessageEntry)
+ iParent->At(0).iSize += iEntryDataSize;
+ else
+ {
+ iEmailEntry->iSize += iEntryDataSize;
+ if (iEntryType==EAttachmentEntry || iEntryType==EHtmlEntry)
+ iEmailEntry->iDetails.Set(iAttachmentName);
+ }
+ User::LeaveIfError(iServerEntry->ChangeEntry(*iEmailEntry));
+ RECVLOG(KUpdatedEntry)
+ return commit; // if I wrote data into the store, tell owner
+ }
+
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::MoveToParentEntryL()
+//----------------------------------------------------------------------------------------
+ {
+ // This function changes the context to the current entry's parent entry.
+ RECVLOG(KMoveToParentEntry)
+
+ // Change context to the parent entry
+ if (EntryId()==iTopMessagePart)
+ return; // Already there.
+
+ User::LeaveIfError(iServerEntry->SetEntry(iServerEntry->Entry().Parent()));
+
+ iNewEntry = EFalse;
+
+ // only read and write to store if this is a real email message; headers stored in
+ // the remote mailbox do not require any store information.
+ if (iReceivingHeadersOnly)
+ {
+ RECVLOG(KIgnoringStreams)
+ return;
+ }
+
+ TBool allowAttachmentFlag=ETrue;
+ if(iServerEntry->Entry().iType == KUidMsvFolderEntry)
+ {
+ // Current entry is a folder entry signifying a MIME multipart.
+ // Change context to the parent entry
+
+ TMsvEmailEntry entry = (TMsvEmailEntry) iServerEntry->Entry();
+ iCurrentMultipartFolderEntryId = EntryId();
+ allowAttachmentFlag = !(entry.MessageFolderType()==EFolderTypeRelated ||
+ entry.MessageFolderType()==EFolderTypeAlternative);
+
+
+ if (EntryId()!=iTopMessagePart)
+ User::LeaveIfError(iServerEntry->SetEntry(iServerEntry->Entry().Parent()));
+ }
+
+ TBool childIsMHTML = EFalse;
+ TBool childIsAttachment = EFalse;
+ TBool childIsICalendar = EFalse;
+ TBool childIsVCalendar = EFalse;
+ //Make the parent entry the current entry
+ if(iEmailEntry != NULL)
+ {
+ childIsAttachment = (iEmailEntry->Attachment() || iEmailEntry->iType == KUidMsvMessageEntry) ? ETrue:EFalse;
+
+ childIsICalendar = (iEmailEntry->ICalendar()) ? ETrue:EFalse;
+ childIsVCalendar = (iEmailEntry->VCalendar()) ? ETrue:EFalse;
+
+ // Dont want the flag propogated 'up' past a message entry.
+ if(iEmailEntry->iType != KUidMsvMessageEntry)
+ childIsMHTML = iEmailEntry->MHTMLEmail() ? ETrue:EFalse;
+
+ delete iEmailEntry;
+ iEmailEntry=NULL;
+ }
+
+ iEmailEntry = new (ELeave) TMsvEmailEntry(iServerEntry->Entry());
+
+ if (!iParent->Count())
+ {
+ TParentDetails parentDetails;
+ iParent->InsertL(0,parentDetails);
+ }
+
+ if (! iParent->At(0).iAttachment)
+ {
+ iParent->At(0).iAttachment=(childIsAttachment && allowAttachmentFlag)? ETrue:EFalse;
+
+ // if we aren't allowing attachments because of the folder type
+ // remember there where attachments and check at the end whether
+ // it was an MHTML message or not.
+ if(childIsAttachment && !allowAttachmentFlag)
+ {
+ iRelatedAttachments=ETrue;
+ }
+ }
+ if (!iParent->At(0).iMHTML)
+ {
+ iParent->At(0).iMHTML = childIsMHTML ? ETrue:EFalse;
+ }
+
+ if (!iParent->At(0).iICal)
+ {
+ iParent->At(0).iICal = childIsICalendar ? ETrue:EFalse;
+ }
+ if (!iParent->At(0).iVCal)
+ {
+ iParent->At(0).iVCal = childIsVCalendar ? ETrue:EFalse;
+ }
+
+ iParent->At(0).iSize += iEntryDataSize;
+
+ iOutputHeader->Reset();
+ iEmptyHeaderSize=iOutputHeader->DataSize();
+ iMimeParser->ResetForNewEntry();
+
+ iEntryDataSize=0;
+
+ RECVLOG(KMovedToParentEntry)
+ }
+
+//----------------------------------------------------------------------------------------
+// Helper function to add the partial footer to the email if it exists
+//----------------------------------------------------------------------------------------
+EXPORT_C void CImRecvConvert::WritePartialFooterL(TInt aAmountLeft)
+ {
+ if(iStore8BitData)
+ {
+ WritePartialFooter8L(aAmountLeft);
+ return;
+ }
+ TMsvId msgId=iBodyId;
+
+ if (msgId==KMsvNullIndexEntryId)
+ return;
+ TMsvId id = iServerEntry->Entry().Id();
+ if (iServerEntry->SetEntry(msgId)==KErrNone)
+ {
+ TBool storePresent = iServerEntry->HasStoreL();
+ if (storePresent && iPartialEmailFooter->Length()>0 && aAmountLeft>0)
+ {
+ CMsvStore* store = iServerEntry->ReadStoreL();
+ CleanupStack::PushL(store);
+ if (store->HasBodyTextL())
+ {
+ iOutputBody->Reset();
+ store->RestoreBodyTextL(*iOutputBody);
+ CleanupStack::PopAndDestroy(store);
+ store = NULL;
+ HBufC* msg=NULL;
+ if (iPartialEmailFooter->Find(KIntegerKey)!=KErrNotFound)
+ {
+ // display k left on the server, rounded up if between 1 and 1023 bytes
+ TInt kBytesLeft = aAmountLeft / 1024;
+ if(kBytesLeft == 0)
+ kBytesLeft = 1;
+ msg = HBufC::NewLC(iPartialEmailFooter->Length()+KSpaceToAddNumber);
+ msg->Des().Format(*iPartialEmailFooter,kBytesLeft);
+ }
+ else
+ {
+ msg = iPartialEmailFooter->AllocLC();
+ }
+
+ iOutputBody->AppendParagraphL();
+ TInt length = iOutputBody->DocumentLength();
+ iOutputBody->InsertL(length,*msg);
+
+ CleanupStack::PopAndDestroy(msg);
+
+ store = iServerEntry->EditStoreL();
+ CleanupStack::PushL(store);
+ // If chunk storage mechanism is used then add partial download footer
+ // information to the body text, using CMsvPlainBodyText.
+ if(iStorePlainBodyText)
+ {
+ CMsvPlainBodyText* plainBodyText = store->InitialisePlainBodyTextForWriteL(iStore8BitData, iMimeParser->CurrentCharsetL(),iCharConv->SystemDefaultCharset());
+ CleanupStack::PushL(plainBodyText);
+ plainBodyText->StoreRichTextAsPlainTextL(*iOutputBody);
+ plainBodyText->CommitL();
+ CleanupStack::PopAndDestroy(plainBodyText);
+ }
+ else
+ {
+ store->StoreBodyTextL(*iOutputBody);
+ store->Commit();
+ }
+ CleanupStack::PopAndDestroy(store);
+ }
+ else
+ {
+ CleanupStack::PopAndDestroy(store);
+ }
+ }
+ }
+ iServerEntry->SetEntry(id);
+ }
+
+void CImRecvConvert::WritePartialFooter8L(TInt aAmountLeft)
+ {
+ TMsvId msgId=iBodyId;
+ if (msgId==KMsvNullIndexEntryId)
+ return;
+ TMsvId id = iServerEntry->Entry().Id();
+ if (iServerEntry->SetEntry(msgId)==KErrNone)
+ {
+ // TMsvEmailEntry should also be set to CMsvServerEntry
+ *iEmailEntry = iServerEntry->Entry();
+ TBool storePresent = iServerEntry->HasStoreL();
+ if (storePresent && iPartialEmailFooter8->Length()>0 && aAmountLeft>0)
+ {
+ CMsvStore* store = iServerEntry->ReadStoreL();
+ CleanupStack::PushL(store);
+ if (store->HasBodyTextL())
+ {
+ // if Uid does not Exist in Message Store, return KErrNotSupported.
+ TInt bufLength = iBodyText->GetBodyLengthL(*store);
+ User::LeaveIfError(bufLength);
+ HBufC8* bodyBuffer = HBufC8::NewLC(bufLength);
+ iBodyBuf->Reset();
+ TPtr8 buf = bodyBuffer->Des();
+ iBodyText->GetBodyTextL(*store, buf);
+ if(!iStorePlainBodyText)
+ {
+ iBodyBuf->InsertL(0, buf);
+ }
+ CleanupStack::Pop(bodyBuffer);
+ iPartialRetrievalBody=bodyBuffer;
+ CleanupStack::PopAndDestroy(store);
+
+ HBufC* msg=NULL;
+ TResourceReader reader;
+ reader.SetBuffer(iPartialEmailFooter8);
+
+ if (iPartialEmailFooter8->Find((TDesC8&)KIntegerKey)!=KErrNotFound)
+ {
+ _LIT8(Ktemp9, "....8 bit....Found KIntegerKey");
+ RECVLOG(Ktemp9)
+ // display k left on the server, rounded up if between 1 and 1023 bytes
+ TInt kBytesLeft = aAmountLeft / 1024;
+ if(kBytesLeft == 0)
+ {
+ kBytesLeft = 1;
+ }
+ HBufC* resourceBuf = (reader.ReadTPtrC()).AllocL();
+ CleanupStack::PushL(resourceBuf);
+ msg = HBufC::NewL(resourceBuf->Length()+ 10);
+ msg->Des().Format(*resourceBuf,kBytesLeft);
+ CleanupStack::PopAndDestroy(resourceBuf);
+ CleanupStack::PushL(msg);
+ }
+ else
+ {
+ msg = (reader.ReadTPtrC()).AllocL();
+ CleanupStack::PushL(msg);
+ }
+
+ HBufC8* footerMsg= HBufC8::NewLC(msg->Size()+ 20);
+ if(iBodyText->CharacterSet())
+ {
+ iCharConv->PrepareToConvertToFromOurCharsetL(iBodyText->CharacterSet());
+ }
+ else
+ {
+ iCharConv->PrepareToConvertToFromOurCharsetL(iBodyText->DefaultCharacterSet());
+ }
+ TInt numUnconverted = 0;
+ TInt unConvertedIndex = 0;
+ TPtr8 output(footerMsg->Des());
+ TPtr16 input(msg->Des());
+ iCharConv->ConvertFromOurCharsetL(input, output, numUnconverted, unConvertedIndex);
+ store=NULL;
+ store = iServerEntry->EditStoreL();
+ CleanupStack::PushL(store);
+
+ //If its Plain body Text then Partial download footer information
+ //is added to the body text, using CMsvPlainBodyText.
+ if(iStorePlainBodyText)
+ {
+ CMsvPlainBodyText* plainBodyText = store->InitialisePlainBodyTextForWriteL(iStore8BitData, iMimeParser->CurrentCharsetL(),iCharConv->SystemDefaultCharset());
+ CleanupStack::PushL(plainBodyText);
+ plainBodyText->StoreChunkL(*iPartialRetrievalBody);
+ plainBodyText->StoreChunkL(KImcvCRLF());
+ plainBodyText->StoreChunkL(*footerMsg);
+ plainBodyText->CommitL();
+ CleanupStack::PopAndDestroy(plainBodyText);
+ }
+ else
+ {
+ iBodyBuf->InsertL(iBodyBuf->Size(), KImcvCRLF());
+ iBodyBuf->InsertL(iBodyBuf->Size(), *footerMsg);
+ iBodyText->StoreL(*store, *iBodyBuf);
+ store->Commit();
+ }
+ CleanupStack::PopAndDestroy(3,msg); // msg, footerMsg, store
+ }
+ else
+ {
+ CleanupStack::PopAndDestroy(store);
+ }
+ }
+ }
+ iServerEntry->SetEntry(id);
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::CreateEntryL()
+//----------------------------------------------------------------------------------------
+ {
+ RECVLOG(KCreatingEntry);
+ if(iEmailEntry)
+ {
+ delete iEmailEntry;
+ iEmailEntry=NULL;
+ }
+
+ if (iCurrentMultipartFolderEntryId)
+ {
+ User::LeaveIfError(iServerEntry->SetEntry(iCurrentMultipartFolderEntryId));
+ iCurrentMultipartFolderEntryId=0;
+ }
+
+ iEmailEntry = new (ELeave) TMsvEmailEntry;
+
+ TValidEntryType previousEntryType = iEntryType;
+ if (!iTopMessagePart || iMIMEPart_822Header)
+ {
+ // At the main header, want to create a message entry.
+ // The stored iEntryType will indicate ( from header info) the next entry to be created.
+ // Save temporarily.
+ // Also applies to the special case where a message contains ony 1 embedded message.
+
+ previousEntryType=iEntryType;
+ iEntryType=EMessageEntry;
+ }
+
+ if ((iPopulateMessage) && (!iTopMessagePart))
+ // If this is the root of a message that is being populated then do not create it.
+ {
+ User::LeaveIfError(iServerEntry->SetEntry(iRootEntryId));
+ *iEmailEntry = iServerEntry->Entry();
+ iTopMessagePart=iRootEntryId;
+
+ // Delete all the children of the message entry. This is needed because if the
+ // message has been purged, the entries will still exist. When the message is populated,
+ // new entries are created. If the original entries are not removed, then duplicate
+ // entries will exist.
+ CMsvEntrySelection* children = new(ELeave) CMsvEntrySelection;
+ CleanupStack::PushL(children);
+ User::LeaveIfError(iServerEntry->GetChildren(*children));
+ if (children->Count())
+ iServerEntry->DeleteEntries(*children);
+ CleanupStack::PopAndDestroy(children);
+ }
+ else
+ {
+ iEmailEntry->iMtm=iNewMsgType;
+ iEmailEntry->iServiceId = iEmailServiceId;
+ iEmailEntry->SetComplete(EFalse);
+ iEmailEntry->iSize = 0;
+ iEmailEntry->SetVisible(ETrue);
+ iEmailEntry->SetInPreparation(EFalse);
+ iEmailEntry->SetReceipt(EFalse);
+
+ iEmailEntry->SetVCard(iMimeParser->VCard());
+ iEmailEntry->SetVCalendar(iMimeParser->VCalendar());
+ iEmailEntry->SetICalendar(iMimeParser->ICalendar());
+ iEmailEntry->SetMessageFolderType(iMimeParser->MessageFolderType());
+ iEmailEntry->SetPriority(iImPriority);
+ iEmailEntry->SetNew(EFalse);
+
+ if(iOutputHeader->ReceiptAddress().Length()>0)
+ iEmailEntry->SetReceipt(ETrue);
+
+ iEmailEntry->iDate=iTimeDate;
+ switch(iEntryType)
+ {
+ case EMessageEntry:
+ iParsedTime=EFalse;
+ if(!iTopMessagePart)
+ {
+ iEmailEntry->SetUnread(ETrue);
+ iEmailEntry->SetNew(ETrue);
+ iEmailEntry->SetVisible(EFalse);
+ iEmailEntry->SetInPreparation(ETrue);
+ iEmailEntry->SetSendingState(KMsvSendStateNotApplicable);
+ }
+ else
+ {
+ TParentDetails parentDetails;
+ parentDetails.iMHTML=EFalse;
+ parentDetails.iAttachment=EFalse;
+ parentDetails.iICal=EFalse;
+ parentDetails.iVCal=EFalse;
+ parentDetails.iSize=0;
+ iParent->InsertL(0,parentDetails);
+ }
+ iEmailEntry->iType=KUidMsvMessageEntry;
+ iEmailEntry->iDetails.Set(iOutputHeader->From());
+ iEmailEntry->iDescription.Set(iOutputHeader->Subject());
+ break;
+ case EFolderEntry:
+ iEmailEntry->iType=KUidMsvFolderEntry;
+ if (iMimeParser->MessageFolderType()==EFolderTypeUnknown)
+ {
+ // Get folder type of parent (the message)
+ TMsvEmailEntry entry=iServerEntry->Entry();
+ iEmailEntry->SetMessageFolderType(entry.MessageFolderType());
+ }
+ break;
+ case EAttachmentEntry:
+ iEmailEntry->iType=KUidMsvAttachmentEntry;
+ iEmailEntry->iDetails.Set(iAttachmentName);
+ iEmailEntry->iDescription.Set(iMimeParser->ContentDescription());
+ break;
+ case ETextEntry:
+ if ( iMimeParser->ContentDisposition()!=KImcvAttachment)
+ {
+ iEmailEntry->iType=KUidMsvEmailTextEntry;
+ if(iStorePlainBodyText)
+ {
+ iFirstLinePlainText = ETrue;
+ }
+ }
+ else
+ {
+ iEmailEntry->iType=KUidMsvAttachmentEntry;
+ iEmailEntry->iDetails.Set(iAttachmentName);
+ iEmailEntry->iDescription.Set(iMimeParser->ContentDescription());
+ }
+ break;
+ case EHtmlEntry:
+ iEmailEntry->iType=KUidMsvEmailHtmlEntry;
+ // If disposition not set or is inline..
+ if ( iMimeParser->ContentDisposition()==KImcvAttachment)
+ iEmailEntry->iType=KUidMsvAttachmentEntry;
+ else
+ iEmailEntry->SetMHTMLEmail(ETrue);
+ iEmailEntry->iDetails.Set(iAttachmentName);
+ iEmailEntry->iDescription.Set(iMimeParser->ContentDescription());
+ break;
+ case ERtfEntry:
+ default:
+ iEmailEntry->iType=KUidMsvAttachmentEntry;
+ iEmailEntry->iDetails.Set(iAttachmentName);
+ iEmailEntry->iDescription.Set(iMimeParser->ContentDescription());
+ }
+
+ if (iReceivingHeadersOnly)
+ {
+ User::LeaveIfError(iServerEntry->CreateEntryBulk(*iEmailEntry));
+ }
+ else
+ {
+ User::LeaveIfError(iServerEntry->CreateEntry(*iEmailEntry));
+ }
+ User::LeaveIfError(iServerEntry->SetEntry(iEmailEntry->Id()));
+ if(!iTopMessagePart)
+ iTopMessagePart=iEmailEntry->Id();
+
+ //if (iEntryType==EHtmlEntry && iAttachmentFileState!=EFileIsOpen)
+ // CreateAttachmentL();
+ }
+
+ iEntryType=previousEntryType;
+ iNewEntry = ETrue;
+ RECVLOG(KCreatedEntry);
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::Logging(const TDesC8& aString1, const TDesC8& aString2)
+//----------------------------------------------------------------------------------------
+ {
+ TBuf8<1024> aBuf(aString1);
+
+ aBuf.Append(aString2);
+ RECVLOG(aBuf);
+ }
+
+//----------------------------------------------------------------------------------------
+void CImRecvConvert::StoreMessageEntryDetailsL()
+//----------------------------------------------------------------------------------------
+ {
+ iEmailEntry->SetAttachment(iParent->At(0).iAttachment);
+ iEmailEntry->SetMHTMLEmail(iParent->At(0).iMHTML);
+ iEmailEntry->SetICalendar(iParent->At(0).iICal);
+ iEmailEntry->SetVCalendar(iParent->At(0).iVCal);
+
+ if(iEmailEntry->MHTMLEmail() == EFalse && iEmailEntry->Attachment() == EFalse && iRelatedAttachments !=EFalse)
+ {
+ iEmailEntry->SetAttachment(ETrue);
+ }
+ iRelatedAttachments=EFalse;
+
+ iEmailEntry->iSize = iParent->At(0).iSize;
+ iEmailEntry->SetMessageFolderType(iParent->At(0).iFolder);
+ StoreEntryDataL();
+
+ if (iParent->Count()>1)
+ {
+ iParent->At(1).iSize += iEmailEntry->iSize;
+ iParent->Delete(0);
+ }
+ else
+ {
+ iParent->At(0).iAttachment = EFalse;
+ iParent->At(0).iMHTML = EFalse;
+ iParent->At(0).iICal = EFalse;
+ iParent->At(0).iVCal = EFalse;
+ iParent->At(0).iSize = 0;
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C TInt CImRecvConvert::DeletedAttachmentSize()
+//----------------------------------------------------------------------------------------
+ {
+ return iSizeOfAttachmentsRemoved;
+ }
+
+
+/****************************************************************************
+ Class CMimeParser functions
+*****************************************************************************/
+//----------------------------------------------------------------------------------------
+EXPORT_C CMimeParser* CMimeParser::NewLC(CImRecvConvert& aImRecvConvert)
+//----------------------------------------------------------------------------------------
+ {
+ CMimeParser* self = new (ELeave) CMimeParser(aImRecvConvert);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C CMimeParser* CMimeParser::NewL(CImRecvConvert& aImRecvConvert)
+//----------------------------------------------------------------------------------------
+ {
+ CMimeParser* self = CMimeParser::NewLC(aImRecvConvert);
+ CleanupStack::Pop();
+ return self;
+ }
+
+//----------------------------------------------------------------------------------------
+CMimeParser::CMimeParser(CImRecvConvert& aImRecvConvert): iImRecvConvert(aImRecvConvert),
+ iStartId(NULL)
+//----------------------------------------------------------------------------------------
+ {
+ __DECLARE_NAME(_S("CMimeParser"));
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::ConstructL()
+//----------------------------------------------------------------------------------------
+ {
+ iMimeHeader = CImMimeHeader::NewL();
+
+
+ // Create a Desc array to store the boundary strings of a Mime message
+ iBoundaryText = new (ELeave) CDesC8ArrayFlat(3);
+
+ // Set charset default value
+ iDefaultCharset=iImRecvConvert.CharacterConverter().DefaultCharset();
+ iCharset = iDefaultCharset;
+ ResetMimeFieldsExist();
+
+ Reset();
+ }
+
+//----------------------------------------------------------------------------------------
+EXPORT_C CMimeParser::~CMimeParser()
+//----------------------------------------------------------------------------------------
+ {
+ delete iMimeHeader;
+ delete iBoundaryText;
+ delete iMimeHeaderLine;
+ delete iStartId;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::Reset()
+//----------------------------------------------------------------------------------------
+ {
+ iBoundaryText->Reset();
+ iBoundaryIndex = 0;
+ iBoundaryLength = 0;
+ isMime = EFalse;
+
+ iCharset = iDefaultCharset;
+ ResetForNewEntry();
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::ResetForNewEntry()
+//----------------------------------------------------------------------------------------
+ {
+
+ iMimeHeader->Reset();
+ iEmptyMimeHeaderSize=iMimeHeader->Size();
+
+ iContentType = EMimeUnknownContent;
+ iContentEncoding = EEncodingTypeNone;
+ iContentDescription.Zero();
+ iVCard = EFalse;
+ iVCalendar = EFalse;
+ iICalendar = EFalse;
+ iStartPart=EFalse;
+ iMessageFolderType = EFolderTypeUnknown;
+ iTerminatingBoundary = EFalse;
+ iBoundaryFound = EFalse;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::RestoreMimeParserL(CMsvStore& entryStore)
+//----------------------------------------------------------------------------------------
+ {
+ iMimeHeader->RestoreL(entryStore);
+
+ if(iMimeHeader->ContentType().Compare(KImcvText)==0)
+ iContentType=EMimeText;
+ else
+ if(iMimeHeader->ContentType().Compare(KImcvMessage)==0)
+ iContentType=EMimeMessage;
+ else
+ if(iMimeHeader->ContentType().Compare(KImcvMultipart)==0)
+ iContentType=EMimeMultipart;
+ else
+ if(iMimeHeader->ContentType().Compare(KImcvImage)==0)
+ iContentType=EMimeImage;
+ else
+ if(iMimeHeader->ContentType().Compare(KImcvApplication)==0)
+ iContentType=EMimeApplication;
+ else
+ if(iMimeHeader->ContentType().Compare(KImcvAudio)==0)
+ iContentType=EMimeAudio;
+ else
+ if(iMimeHeader->ContentType().Compare(KImcvVideo)==0)
+ iContentType=EMimeVideo;
+ else
+ iContentType=EMimeUnknownContent;
+
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::ParseLineL(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ if(iMimeHeaderLine==NULL)
+ {
+ iMimeHeaderLine = HBufC8::NewL(aSourceLine.Length());
+ *iMimeHeaderLine = aSourceLine;
+ }
+
+ iLex = *iMimeHeaderLine;
+
+ // find out whether the current line has anything to do with currently understood MIME Content tokens
+ if(!iMimeHeaderLine->MatchF(KImcvMime) || !iMimeHeaderLine->MatchF(KImcvContent))
+ {
+ if(MatchAndRemoveToken(KImcvMimePrompt))
+ DoMimeVersion();
+ else if(MatchAndRemoveToken(KImcvContentType))
+ {
+ // Check CAF for recognition of content-type in RegisterL()
+ if(!iImRecvConvert.iReceivingHeadersOnly)
+ {
+ // CAF processing.
+ // We need to trim any trailing data following and including semicolons
+ // If we leave it to the Register method then it has to create a heap buffer
+ // to do the manipulation. iMimeHeaderLine is modifiable so do it here.
+ TInt orgLength = iMimeHeaderLine->Length();
+ TInt endOffset;
+ // Get the offset of the semicolon if any
+ if((endOffset = iMimeHeaderLine->Locate(KImcvSemiColon)) != KErrNotFound)
+ {
+ // Trim to the content-type token only
+ iMimeHeaderLine->Des().SetLength(endOffset);
+ }
+ // Sets CAF interested if framework to consume
+ iImRecvConvert.ImCafRegisterL(iMimeHeaderLine->Des());
+ // Restore length of the descriptor for futher processing
+ iMimeHeaderLine->Des().SetLength(orgLength);
+ }
+ DoContentTypeL();
+ }
+ else if(MatchAndRemoveToken(KImcvContentLocation))
+ DoContentLocationL();
+ else if(MatchAndRemoveToken(KImcvContentTransferEncoding))
+ DoEncodingL();
+ else if(MatchAndRemoveToken(KImcvContentId))
+ {
+ RemoveSurroundingCharacters(KImcvLeftChevron, KImcvRightChevron, *iMimeHeaderLine);
+ DoContentIdL();
+ }
+ else if(MatchAndRemoveToken(KImcvContentDescription))
+ DoDescriptionL();
+ else if(MatchAndRemoveToken(KImcvContentDisposition))
+ DoDispositionL();
+ else if(MatchAndRemoveToken(KImcvContentBase))
+ DoContentBaseL();
+ }
+ // Start adding to the CAF metadata if CAF's interested
+ if(iImRecvConvert.ImCafRegistered())
+ {
+ iImRecvConvert.ImAddToCafMetaDataL(aSourceLine);
+ }
+
+ delete iMimeHeaderLine; // clean up and null pointer iff CompleteMimeHeader and no foldover append req'd
+ iMimeHeaderLine=NULL;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoMimeVersion()
+//----------------------------------------------------------------------------------------
+ {
+ // extract the MIME version from a header line which we already know
+ // has 'MIME-Version' start of it.
+ if(MatchAndRemoveToken(KImcvMimeVersion))
+ iCorrectMimeVersion = ETrue;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoContentIdL()
+//----------------------------------------------------------------------------------------
+ {
+ iMimeHeader->SetContentIDL(iMimeHeaderLine->Des());
+ if(iStartId && iStartId->CompareF(iMimeHeaderLine->Des())==KErrNone)
+ iStartPart=ETrue;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoContentLocationL()
+//----------------------------------------------------------------------------------------
+ {
+ TInt len = (*iMimeHeaderLine).Length();
+ if (len == 0)
+ return;
+
+ RemoveSurroundingCharacters(KImcvQuote, KImcvQuote, *iMimeHeaderLine);
+
+ HBufC16* locationBuf = HBufC16::NewL( len );
+ CleanupStack::PushL(locationBuf);
+ TPtr locationPtr(locationBuf->Des());
+ iImRecvConvert.iHeaderConverter->DecodeHeaderFieldL( iMimeHeaderLine->Des(), locationPtr);
+ iMimeHeader->SetContentLocationL(locationPtr);
+ CleanupStack::PopAndDestroy(); // locationBuf
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoContentBaseL()
+//----------------------------------------------------------------------------------------
+ {
+ RemoveSurroundingCharacters(KImcvQuote, KImcvQuote, *iMimeHeaderLine);
+ iMimeHeader->SetContentBaseL(iMimeHeaderLine->Des());
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoAttachmentTypeL()
+//----------------------------------------------------------------------------------------
+ {
+ iImRecvConvert.iEntryType = CImRecvConvert::EAttachmentEntry;
+ iImRecvConvert.iCurrentPartIsRichText = EFalse;
+
+ if(MatchAndRemoveToken(KImcvForwardSlash))
+ {
+ if(MatchAndRemoveToken(KImcvBmp))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvBmp);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvGif))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvGif);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvJpeg))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvJpeg);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvTiff))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvTiff);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvWav))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvWav);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvZip))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvZip);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvOctetStream))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvOctetStream);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvExe))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvExe);
+ }
+ else
+ if(MatchAndRemoveToken(KImcvCmd))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvCmd);
+ }
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoMessageTypeL()
+//----------------------------------------------------------------------------------------
+ {
+ iImRecvConvert.iEntryType = CImRecvConvert::EMessageEntry;
+ iImRecvConvert.iEmailPart = CImRecvConvert::KParentPart;
+
+ iContentType=EMimeMessage;
+ iMimeHeader->SetContentTypeL(KImcvMessage);
+
+ if(MatchAndRemoveToken(KImcvForwardSlash))
+ {
+ if(MatchAndRemoveToken(KImcvRfc822))
+ {
+ //iMessageFolderType=EFolderTypeRFC822;
+ iMimeHeader->SetContentSubTypeL(KImcvRfc822);
+ }
+ else if(MatchAndRemoveToken(KImcvExternal))
+ {
+ iMessageFolderType=EFolderTypeExternal;
+ iMimeHeader->SetContentSubTypeL(KImcvExternal);
+ }
+ else if(MatchAndRemoveToken(KImcvPartial))
+ {
+ iMessageFolderType=EFolderTypePartial;
+ iMimeHeader->SetContentSubTypeL(KImcvPartial);
+ }
+ else if(MatchAndRemoveToken(KImcvDeliveryStatus))
+ {
+ // We do not process this part. So store as text.
+ iMimeHeader->SetContentSubTypeL(KImcvDeliveryStatus);
+ iImRecvConvert.iEntryType = CImRecvConvert::EAttachmentEntry;
+ iImRecvConvert.iEmailPart = CImRecvConvert::KNoPart;
+ iContentType=EMimeUnknownContent;
+ iImRecvConvert.iCurrentPartIsRichText=EFalse;
+ iImRecvConvert.iAttachmentName.Copy(KImcvDeliveryStatus);
+ iImRecvConvert.iAttachmentName.Append(KTextExtension);
+ }
+ else
+ {
+ iMessageFolderType=EFolderTypeUnknown;
+ iMimeHeader->SetContentSubTypeL(KImcvUnknown);
+ }
+ }
+ }
+
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoMultipartTypeForNonMIMEL()
+//----------------------------------------------------------------------------------------
+ {
+ ResetForNewEntry();
+ iMessageFolderType=EFolderTypeMixed;
+ iMimeHeader->SetContentTypeL(KImcvMultipart);
+ iMimeHeader->SetContentSubTypeL(KImcvMixed);
+ }
+
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoMultipartTypeL()
+//----------------------------------------------------------------------------------------
+ {
+ iImRecvConvert.iEntryType = CImRecvConvert::EFolderEntry;
+ iImRecvConvert.iEmailPart = CImRecvConvert::KMultiPart;
+
+ iContentType=EMimeMultipart;
+ iMimeHeader->SetContentTypeL(KImcvMultipart);
+ if(MatchAndRemoveToken(KImcvForwardSlash))
+ {
+ if(MatchAndRemoveToken(KImcvMixed))
+ {
+ iMessageFolderType=EFolderTypeMixed;
+ iMimeHeader->SetContentSubTypeL(KImcvMixed);
+ }
+ else if(MatchAndRemoveToken(KImcvRelated))
+ {
+ iMessageFolderType=EFolderTypeRelated;
+ iMimeHeader->SetContentSubTypeL(KImcvRelated);
+ }
+ else if(MatchAndRemoveToken(KImcvAlternative))
+ {
+ iMessageFolderType=EFolderTypeAlternative;
+ iMimeHeader->SetContentSubTypeL(KImcvAlternative);
+ }
+ else if(MatchAndRemoveToken(KImcvEncrypted))
+ {
+// Add this when Encryption is handled iMessageFolderType=EFolderTypeEncrypted;
+ iMimeHeader->SetContentSubTypeL(KImcvEncrypted);
+ }
+ else if(MatchAndRemoveToken(KImcvParallel))
+ {
+ iMessageFolderType=EFolderTypeParallel;
+ iMimeHeader->SetContentSubTypeL(KImcvParallel);
+ }
+ else if(MatchAndRemoveToken(KImcvDigest))
+ {
+ iMessageFolderType=EFolderTypeDigest;
+ iMimeHeader->SetContentSubTypeL(KImcvDigest);
+ isMessageDigest=ETrue;
+ }
+ else if(MatchAndRemoveToken(KImcvSigned))
+ {
+// Add this when Signed is handled iMessageFolderType=EFolderTypeSigned;
+ iMimeHeader->SetContentSubTypeL(KImcvSigned);
+ }
+ else if(MatchAndRemoveToken(KImcvReport))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvReport);
+ }
+ else
+ {
+ iMessageFolderType=EFolderTypeUnknown;
+ iMimeHeader->SetContentSubTypeL(KImcvUnknown);
+ }
+ }
+
+ if (iMessageFolderType==EFolderTypeRelated)
+ iImRecvConvert.iParent->At(0).iFolder=iMessageFolderType;
+
+ // Find any parameters specific to a Multipart content type
+
+ HBufC8* paramValue = NULL;
+
+ // Extracts the boundary string
+ ExtractParameterInfoL(KImcvBoundary, paramValue);
+ if( paramValue!=NULL )
+ {
+ CleanupStack::PushL(paramValue);
+ iBoundaryFound = ETrue;
+ iImRecvConvert.iEmailPart = CImRecvConvert::KMultiPart;
+ SetBoundaryL(*paramValue);
+ CleanupStack::PopAndDestroy(paramValue);
+ }
+
+ // Extracts start ID if it has been defined;
+ ExtractParameterInfoL(KImcvStartPart, paramValue);
+ if( paramValue!=NULL )
+ {
+ delete iStartId;
+ iStartId = paramValue;
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoTextTypeL()
+//----------------------------------------------------------------------------------------
+ {
+ HBufC8* paramValue = NULL;
+
+ iImRecvConvert.iEntryType = CImRecvConvert::ETextEntry;
+ iContentType=EMimeText;
+ iMimeHeader->SetContentTypeL(KImcvText);
+
+ if(MatchAndRemoveToken(KImcvForwardSlash))
+ {
+ if(MatchAndRemoveToken(KImcvPlain))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvPlain);
+ }
+ else if(MatchAndRemoveToken(KImcvRtf))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvRtf);
+ iImRecvConvert.iEntryType = CImRecvConvert::ERtfEntry;
+ iImRecvConvert.iCurrentPartIsRichText = EFalse;
+ }
+ else if(MatchAndRemoveToken(KImcvHtml))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvHtml);
+ iImRecvConvert.iEntryType = CImRecvConvert::EHtmlEntry;
+ iImRecvConvert.iCurrentPartIsRichText = EFalse;
+ }
+ else if(MatchAndRemoveToken(KImcvDirectory))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvDirectory);
+ iMimeHeader->ContentTypeParams().AppendL(KImcvProfile);
+ ExtractParameterInfoL(KImcvProfile, paramValue);
+ // Assume at right context, the email message, not attachment.
+ iMessageFolderType=EFolderTypeDirectory;
+ if( paramValue!=NULL )
+ {
+ CleanupStack::PushL(paramValue);
+ iMimeHeader->ContentTypeParams().AppendL(*paramValue);
+ if(paramValue->MatchF(KImcvVCard) == 0)
+ {
+ iVCard=ETrue;
+ }
+ CleanupStack::PopAndDestroy(paramValue);
+ }
+ }
+ else if(MatchAndRemoveToken(KImcvXVCard))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvXVCard);
+ iVCard=ETrue;
+ }
+ else if(MatchAndRemoveToken(KImcvVCalender))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvVCalender);
+ iVCalendar=ETrue;
+ }
+ else if(MatchAndRemoveToken(KImcvICalendar))
+ {
+ iMimeHeader->SetContentSubTypeL(KImcvICalendar);
+
+ // Get the method value
+ ExtractParameterInfoL(KImcvICalendarMethod, paramValue);
+ if (paramValue!=NULL)
+ {
+ CleanupStack::PushL(paramValue);
+ iMimeHeader->ContentTypeParams().AppendL(KImcvICalendarMethod);
+ iMimeHeader->ContentTypeParams().AppendL(*paramValue);
+ CleanupStack::PopAndDestroy(paramValue);
+ }
+ // Get the component value
+ ExtractParameterInfoL(KImcvICalendarComponent, paramValue);
+ if (paramValue!=NULL)
+ {
+ CleanupStack::PushL(paramValue);
+ iMimeHeader->ContentTypeParams().AppendL(KImcvICalendarComponent);
+ iMimeHeader->ContentTypeParams().AppendL(*paramValue);
+ CleanupStack::PopAndDestroy(paramValue);
+ }
+ iICalendar=ETrue;
+ }
+
+ // Extract the charset value,
+ ExtractParameterInfoL(KImcvCharset, paramValue);
+ if(paramValue!=NULL)
+ {
+ CleanupStack::PushL(paramValue);
+
+ // check if at top level header or Mime part header
+ TUint charsetUid = iImRecvConvert.CharacterConverter().GetMimeCharsetUidL(*paramValue);
+ if(iImRecvConvert.NotFinishedRfc822Header() == EFalse)
+ iCharset=charsetUid;
+
+ // Store in CMimeHeader::iContentTypeParams
+ iMimeHeader->ContentTypeParams().AppendL(KImcvCharset);
+ iMimeHeader->ContentTypeParams().AppendL(*paramValue);
+ CleanupStack::PopAndDestroy(paramValue);
+
+ if (!iImRecvConvert.CharacterConverter().PrepareToConvertToFromOurCharsetL(charsetUid))
+ charsetUid=KUidMsvCharsetNone;
+ iMimeHeader->SetMimeCharset(charsetUid);
+ }
+ else
+ {
+ iMimeHeader->SetMimeCharset(iDefaultCharset);
+ }
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoContentTypeL()
+//----------------------------------------------------------------------------------------
+ {
+ RemoveSurroundingCharacters(KImcvLeftChevron, KImcvRightChevron, *iMimeHeaderLine);
+
+ if(MatchAndRemoveToken(KImcvText))
+ {
+ DoTextTypeL();
+ }
+ else if(MatchAndRemoveToken(KImcvMultipart))
+ {
+ DoMultipartTypeL();
+ }
+ else if(MatchAndRemoveToken(KImcvMessage))
+ {
+ DoMessageTypeL();
+ }
+ else if(MatchAndRemoveToken(KImcvImage))
+ {
+ iContentType=EMimeImage;
+ iMimeHeader->SetContentTypeL(KImcvImage);
+ DoAttachmentTypeL();
+ }
+ else if(MatchAndRemoveToken(KImcvApplication))
+ {
+ iContentType=EMimeApplication;
+ iMimeHeader->SetContentTypeL(KImcvApplication);
+ DoAttachmentTypeL();
+ }
+ else if(MatchAndRemoveToken(KImcvAudio))
+ {
+ iContentType=EMimeAudio;
+ iMimeHeader->SetContentTypeL(KImcvAudio);
+ DoAttachmentTypeL();
+ }
+ else if(MatchAndRemoveToken(KImcvVideo))
+ {
+ iContentType=EMimeVideo;
+ iMimeHeader->SetContentTypeL(KImcvVideo);
+ DoAttachmentTypeL();
+ }
+ else
+ {
+ iContentType=EMimeUnknownContent;
+ iMimeHeader->SetContentTypeL(KImcvUnknown);
+ }
+
+ // Extract the filename if it exists
+ HBufC* paramStore = HBufC::NewLC(MaxMimeParameterValueLength);
+ HBufC8* paramStore8 = NULL;
+ TPtr paramValue(paramStore->Des());
+ ExtractParameterInfoL(KImcvMimeTypeName, paramValue,paramStore8);
+ CleanupStack::PushL(paramStore8);
+ if(paramValue.Length())
+ {
+ iMimeHeader->ContentTypeParams().AppendL(KImcvMimeTypeName);
+ TPtr8 paramValue8(paramStore8->Des());
+ iMimeHeader->ContentTypeParams().AppendL(paramValue8);
+ iImRecvConvert.SetAttachmentName(paramValue);
+ }
+
+ CleanupStack::PopAndDestroy(2,paramStore);
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoEncodingL()
+//----------------------------------------------------------------------------------------
+ {
+ // Some servers will specify the content transfer encoding field, but leave
+ // it's value blank. Check for this, and default to 7 bit if this happens.
+ if ((*iMimeHeaderLine).Length() == 0)
+ {
+ iMimeHeader->SetContentTransferEncodingL(KMiut7BitString);
+ iContentEncoding = EEncodingType7Bit;
+ return;
+ }
+
+ iMimeHeader->SetContentTransferEncodingL(*iMimeHeaderLine);
+ switch ((*iMimeHeaderLine)[0])
+ {
+ case '7': // 7bit
+ iContentEncoding = EEncodingType7Bit;
+ break;
+ case '8': // 8bit
+ iContentEncoding = EEncodingType8Bit;
+ break;
+ case 'q': // quoted-printable
+ case 'Q':
+ iContentEncoding = EEncodingTypeQP;
+ break;
+ case 'b': // binary or base64
+ case 'B':
+ if ((*iMimeHeaderLine).Length() <2)
+ return;
+ iContentEncoding = ((*iMimeHeaderLine)[1] == 'i' || (*iMimeHeaderLine)[1] == 'I' ? EEncodingTypeBinary : EEncodingTypeBASE64);
+ break;
+ case 'x': //in order to support UU encoded within a MIME message
+ case 'X':
+ iContentEncoding = ((*iMimeHeaderLine).FindF(KImcvXUUString)==KErrNotFound ? EEncodingTypeUnknown : EEncodingTypeUU);
+ break;
+ default:
+ iContentEncoding = EEncodingTypeUnknown;
+ break;
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoDescriptionL()
+//----------------------------------------------------------------------------------------
+ {
+ TPtrC8 marked(iMimeHeaderLine->Des());
+ if (marked.Length()>KMaxFileName)
+ marked.Set(marked.Left(KMaxFileName));
+
+ iContentDescription.Copy(marked);
+
+ // remove the CRLF
+
+ TInt length = iContentDescription.Length();
+ if (length>2)
+ {
+ if (iContentDescription[length-2]==KImcvCR && iContentDescription[length-1]==KImcvLF)
+ iContentDescription.SetLength(length-2);
+ }
+ iMimeHeader->SetContentDescriptionL(marked);
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::DoDispositionL()
+//----------------------------------------------------------------------------------------
+ {
+ TBool inLine = EFalse;
+ if(MatchAndRemoveToken(KImcvAttachment))
+ {
+ iMimeHeader->SetContentDispositionL(KImcvAttachment);
+ if (iMimeHeader->ContentSubType()!=KImcvRfc822)
+ {
+ iImRecvConvert.iCurrentPartIsRichText = EFalse;
+ }
+ if (iImRecvConvert.iEntryType!=CImRecvConvert::EMessageEntry)
+ {
+ iImRecvConvert.iEntryType = CImRecvConvert::EAttachmentEntry;
+ }
+ }
+ else if(MatchAndRemoveToken(KImcvInline))
+ inLine = ETrue;
+
+ // Extract the filename if it exists, unless we already have a name for it
+ if(!iImRecvConvert.iAttachmentName.Length())
+ {
+ HBufC* paramStore = HBufC::NewLC(KHeaderBufferLength);
+ TPtr paramValue(paramStore->Des());
+ HBufC8* paramStore8 = NULL;
+ ExtractParameterInfoL(KImcvMimeDispositionFilename, paramValue,paramStore8);
+ CleanupStack::PushL(paramStore8);
+ if(paramValue.Length())
+ {
+ iMimeHeader->ContentTypeParams().AppendL(KImcvMimeDispositionFilename);
+ TPtr8 paramValue8(paramStore8->Des());
+ iMimeHeader->ContentTypeParams().AppendL(paramValue8);
+ iImRecvConvert.SetAttachmentName(paramValue);
+ }
+
+ CleanupStack::PopAndDestroy(2,paramStore); // paramStore, paramStore8
+ }
+
+ if(inLine)
+ {
+ // Content Disposition set to inline
+ if (iImRecvConvert.iAttachmentName.Length())
+ // filename exists
+ {
+ iMimeHeader->SetContentDispositionL(KImcvAttachment);
+
+ if (iMimeHeader->ContentSubType()!=KImcvRfc822)
+ {
+ iImRecvConvert.iCurrentPartIsRichText = EFalse;
+ }
+ if (iImRecvConvert.iEntryType!=CImRecvConvert::EMessageEntry)
+ {
+ iImRecvConvert.iEntryType = CImRecvConvert::EAttachmentEntry;
+ }
+ }
+ else
+ iMimeHeader->SetContentDispositionL(KImcvInline);
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+TPtrC8 CMimeParser::ContentSubType() const
+//----------------------------------------------------------------------------------------
+ {
+ return iMimeHeader->ContentSubType();
+ }
+
+//----------------------------------------------------------------------------------------
+TPtrC8 CMimeParser::ContentTypeDescription() const
+//----------------------------------------------------------------------------------------
+ {
+ return iMimeHeader->ContentType();
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::VCard() const
+//----------------------------------------------------------------------------------------
+ {
+ return iVCard;
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::VCalendar() const
+//----------------------------------------------------------------------------------------
+ {
+ return iVCalendar;
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::ICalendar() const
+//----------------------------------------------------------------------------------------
+ {
+ return iICalendar;
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::StartPart() const
+//----------------------------------------------------------------------------------------
+ {
+ return iStartPart;
+ }
+
+//----------------------------------------------------------------------------------------
+TImEmailFolderType CMimeParser::MessageFolderType() const
+//----------------------------------------------------------------------------------------
+ {
+ return iMessageFolderType;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::SetMessageFolderType(TImEmailFolderType aFolderType)
+//----------------------------------------------------------------------------------------
+ {
+ iMessageFolderType=aFolderType;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::SetBoundaryL(const TDesC8& aBoundaryText)
+//----------------------------------------------------------------------------------------
+ {
+ TBuf8<KMaxBoundaryTextLength+2> tempBoundary(KImcvMimeBoundaryStartEnd);
+// From RFC 1521, (Boundaries) must be no longer than 70 characters.
+// Including beginning and end "--"
+ if (aBoundaryText.Length()>70)
+ tempBoundary.Append(aBoundaryText.Left(70));
+ else
+ tempBoundary.Append(aBoundaryText);
+ iBoundaryText->AppendL(tempBoundary);
+
+ iBoundaryIndex = iBoundaryText->MdcaCount(); //iBoundaryIndex stores a count value not the index here
+ iBoundaryIndex = ((iBoundaryIndex > 0)? (iBoundaryIndex-1): 0);
+ iBoundaryLength = iBoundaryText->MdcaPoint(iBoundaryIndex).Length();
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::IsBoundary(const TDesC8& aSourceLine)
+//----------------------------------------------------------------------------------------
+ {
+ if(iBoundaryText->MdcaCount())
+ {
+ TInt found = 0;
+ TInt aLineLength = aSourceLine.Length();
+ TInt compareLength = aLineLength > iBoundaryLength ? iBoundaryLength : aLineLength;
+
+ if (aLineLength > 3)
+ {
+ // To Handle cases when server sends clipped version of mails which does not have closing
+ // boundary..
+ if (aSourceLine[aLineLength-4] == KImcvHyphen && aSourceLine[aLineLength-3] == KImcvHyphen)
+ {
+ compareLength =aLineLength-4;
+ }
+ }
+
+ TPtrC8 tempSourceLine(aSourceLine.Ptr(), compareLength);
+
+ TInt error = iBoundaryText->Find(tempSourceLine, found, ECmpNormal);
+
+ if(error||(!iBoundaryLength))
+ return KBoundaryNotFound;
+
+ // The following code is executed only if aSourceLine is a boundary
+ if(found!=iBoundaryIndex)
+ {
+ iReceiveError = (iReceiveError)? iReceiveError: KBoundaryError;
+ iBoundaryIndex = found;
+ iBoundaryText->Delete(found++);
+ }
+
+ if(aLineLength >= compareLength+4) // allow for CRLF & then check for the double hyphen
+ {
+ if((aSourceLine[compareLength] == KImcvHyphen) && (aSourceLine[compareLength+1] == KImcvHyphen))
+ iTerminatingBoundary = ETrue; // this is a terminating boundary
+ }
+ else
+ iTerminatingBoundary = EFalse;
+
+ return KBoundaryFound;
+ }
+
+ return KBoundaryNotFound;
+ }
+
+//----------------------------------------------------------------------------------------
+void CMimeParser::RemoveBoundary()
+//----------------------------------------------------------------------------------------
+ {
+ iBoundaryText->Delete(iBoundaryIndex);
+ if(iBoundaryText->MdcaCount())
+ {
+ iBoundaryIndex = iBoundaryText->MdcaCount(); //iBoundaryIndex stores a count value not the index here
+
+ iBoundaryIndex = ((iBoundaryIndex > 0)? (iBoundaryIndex-1): 0);
+ iBoundaryLength = iBoundaryText->MdcaPoint(iBoundaryIndex).Length();
+ }
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::MatchAndRemoveToken( const TDesC8& aToken )
+//----------------------------------------------------------------------------------------
+ {
+ TInt comparison;
+ TInt tokenLength = aToken.Length();
+ TInt desLength = (*iMimeHeaderLine).Length();
+ TInt compareLength = tokenLength > desLength ? desLength : tokenLength;
+ TPtrC8 left((*iMimeHeaderLine).Left(compareLength));
+
+ // now see whether the current line contains the search string
+ comparison = left.CompareF(aToken);
+ if (!comparison)
+ {
+ // found the match string at the start of the output line, so remove it
+ iMimeFieldsExist=ETrue;
+ isMime=ETrue;
+ // get rid of any whitespace betweebn the tag and the data while we have a chance
+ TInt whitespaceLength=0;
+ TInt maxLength=desLength-tokenLength;
+ const TUint8* ptr = (*iMimeHeaderLine).Ptr();
+ while ( whitespaceLength <= maxLength && (ptr[tokenLength+whitespaceLength] == KImcvSP || ptr[tokenLength+whitespaceLength] == KImcvTab) )
+ whitespaceLength++;
+ iMimeHeaderLine->Des().Delete(0, tokenLength+whitespaceLength);
+
+ // Reset iLex, so its length is updated.
+ iLex = *iMimeHeaderLine;
+ }
+ return (comparison==0);
+ }
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::IsSpecialChar( const TUint8 aChar )
+//----------------------------------------------------------------------------------------
+ {
+ return (aChar == '(' || aChar == ')' || aChar == '<' || aChar == '>' || aChar == '@'
+ || aChar == ',' || aChar == ';' || aChar == ':' || aChar == '\\'|| aChar == '"'
+ || aChar == '/' || aChar == '[' || aChar == ']' || aChar == '?' || aChar == '=');
+ }
+
+
+
+// Implicitly the parameter ASSUMED TO BE is not encoded as return parameter, rBuffer,
+// is 8 bit. iLex should currently be pointing at the space after content-type description.
+//----------------------------------------------------------------------------------------
+void CMimeParser::ExtractParameterInfoL(const TDesC8& aTag, HBufC8*& rBuffer)
+//----------------------------------------------------------------------------------------
+ {
+ rBuffer = NULL;
+
+ TLexMark8 mark;
+ TInt offset;
+
+ // Workaround due to this method not being able to
+ // correctly handle multiple content-type parameters
+ iLex = *iMimeHeaderLine;
+
+ if( (offset=iLex.Remainder().FindF(aTag)) != KErrNotFound )
+ {
+ // move past the tag
+ iLex.Inc(offset+aTag.Length());
+
+ // Default : no charset info or folding
+ // move forward to the start of the boundary string itself.
+
+ while (iLex.Peek() != KImcvEquals && !iLex.Eos())
+ iLex.Inc();
+
+ TPtrC8 paramBuffer(ExtractParameterString(mark));
+ if( paramBuffer.Length() > 0 )
+ {
+ rBuffer = paramBuffer.AllocL();
+ }
+ }
+ }
+
+/**
+Searches the passed parameter list for the specified attribute.
+
+@param aTag The name of the attribute to be found
+@param aParameterList The string to be searched
+@return TInt The offset of the data sequence for the first attribute matching aTag
+ from the beginning of the passed parameter list data
+ KErrNotFound, if the data sequence cannot be found.
+ Zero, if the length of the search data sequence is zero
+*/
+TInt CMimeParser::FindAttribute(const TDesC8& aTag,const TDesC8& aParameterList)
+ {
+ TInt offset = KErrNone;
+ TInt cumulativeOffset= 0;
+ TInt tagLength=aTag.Length();
+ TInt aParameterListLength =aParameterList.Length();
+
+ while( offset!= KErrNotFound)
+ {
+ offset = aParameterList.Mid(cumulativeOffset).FindF(aTag);
+ if (offset != KErrNotFound)
+ {
+ // Matching string found - check that it is the correct syntax for an attribute
+ // according to RFC2045 and RFC2231
+
+ // Increment cumulativeOffset - indicates the start of this instance of aTag
+ cumulativeOffset+=offset;
+
+ TBool preCharValid = EFalse;
+
+ // Check the preceeding character is OK.
+ // Note that test for cumulativeOffset==0 must be 1st OR'd condition
+ if (cumulativeOffset==0 ||
+ aParameterList[(cumulativeOffset)-1] == KImcvTab ||
+ aParameterList[(cumulativeOffset)-1] == KImcvSemiColon ||
+ aParameterList[(cumulativeOffset)-1] == KImcvSP ||
+ aParameterList[(cumulativeOffset)-1] == KImcvCR ||
+ aParameterList[(cumulativeOffset)-1] == KImcvLF )
+ {
+ preCharValid = ETrue;
+ }
+
+ // Check following character is OK
+ // Note boundary check must be before character checks
+ if ( (preCharValid) &&
+ ((cumulativeOffset+tagLength) < aParameterListLength) &&
+ (( aParameterList[cumulativeOffset+tagLength] == KImcvAny) ||
+ (aParameterList[cumulativeOffset+tagLength] == KImcvEquals )))
+ {
+ // Match found - return the offset into the original string
+ return cumulativeOffset;
+ }
+ else
+ {
+ // update cumulativeOffset to indicate end of invalid aTag match.
+ cumulativeOffset += tagLength;
+ }
+ }
+ }
+
+ // no match found
+ return KErrNotFound;
+ }
+
+
+// iLex should currently be pointing at the space after content-type description.
+//----------------------------------------------------------------------------------------
+void CMimeParser::ExtractParameterInfoL(const TDesC8& aTag, TDes16& rBuffer, HBufC8*& rBuffer8)
+//----------------------------------------------------------------------------------------
+ {
+ TInt offset;
+
+ rBuffer.Copy(KNullDesC);
+
+ // we need to extract the <aTag> text from the same line
+ // iLex should currently be pointing at the space after content-type description
+ if( (offset=FindAttribute(aTag,iLex.Remainder()))!=KErrNotFound )
+ {
+ // Check which type of encoding present.
+ TLexMark8 initMark;
+ iLex.Mark(initMark);
+ TLexMark8 mark;
+ iLex.Inc(offset+aTag.Length()); // move past the tag
+ const TPtrC8 param = ExtractParameterString(mark);
+ if( param.Length() > 0 )
+ {
+ rBuffer8 = param.AllocLC();
+ }
+ if ( ParseRfc2047ParameterInfoL(param, rBuffer) == EFalse )
+ {
+ iLex.UnGetToMark(initMark);
+ ParseRfc2231ParameterInfoL(aTag, rBuffer, offset );
+ }
+ CleanupStack::Pop(rBuffer8);
+ }
+ }
+
+
+// Detect encoding of form =?charset?Q?" Text "?=
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::ParseRfc2047ParameterInfoL(const TDesC8& aParam, TDes& rBuffer)
+//----------------------------------------------------------------------------------------
+ {
+ TBool parameterPresent = EFalse;
+
+ // Check for =? somewhere in text.
+
+ if ( aParam.Find(KImcvEncodedWordStart) != KErrNotFound )
+ {
+ // Make assumption that '=?' appearing in parameter means
+ //it is part of encoding
+
+ parameterPresent = ETrue;
+ iImRecvConvert.iHeaderConverter->DecodeHeaderFieldL(aParam, rBuffer);
+ }
+ return parameterPresent;
+ }
+
+
+// For extracting parameter data following Mime header fields, in the format
+// *(;tag=data). As specified in rfc2231
+// Assumes parameter data seperated by ';'
+// Takes tag(name) of parameter as input, returning a descriptor with the data
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::ParseRfc2231ParameterInfoL(const TDesC8& aTag, TDes& rBuffer, TInt aOffset)
+//----------------------------------------------------------------------------------------
+ {
+TBool parameterPresent = ETrue;
+
+ // For storing extracted parameter information
+
+ HBufC8* info = HBufC8::NewLC(KHeaderBufferLength);
+
+ // For storing information relating to parameter extraction/conversion
+
+ TInt count = 0;
+ TPtrC8 charset8;
+ TPtrC8 language8;
+ TBool continuation=ETrue; // is RFC2231 section 3
+
+ // Following rfc 2231, parameter may be encoded & folded in the following way
+ //
+ // <name>*0*=us-ascii'en'<encoded data>
+ // <name>*1*=< more encoded data>
+ // <name>*2=<unencoded info>
+ do
+ {
+ // move past the tag
+ iLex.Inc(aOffset+aTag.Length());
+ if (iLex.Peek() != KImcvAny)
+ {
+ // Default : no charset info or folding
+ // move forward to the start of the boundary string itself
+
+ while (iLex.Peek() != KImcvEquals && !iLex.Eos())
+ {
+
+ iLex.Inc();
+ }
+ }
+ else // *, Parameter is encoded
+ {
+ iLex.Inc(); // Past '*'
+
+ // If parameter folded :- need to get several bits of data and join together.
+
+ if ( iLex.Peek()!=KImcvEquals) // Get Number
+ {
+ iLex.Mark(); // Beginnig of number
+ iLex.Inc();
+ while (iLex.Peek() != KImcvEquals && iLex.Peek() != KImcvAny && !iLex.Eos())
+ {
+ iLex.Inc();
+ }
+ TPtrC8 numPtr = iLex.MarkedToken();
+ TLex8 lex(numPtr);
+ lex.Val(count); // Store number in count.
+
+ // if * exists, has language/charset
+ if (iLex.Peek() == KImcvAny)
+ {
+ iLex.Inc(); // Past *
+ // rfc2231 section 4 - has language/charset info
+ continuation = EFalse;
+ }
+ }
+
+
+ // Must get charset & language information etc..
+
+ if (!count && !continuation)
+ {
+ iLex.Inc(); // Past '='
+
+ if (iLex.Peek()==KImcvQuote)
+ {
+ iLex.Inc();
+ }
+
+ // Extract CHARSET token
+ iLex.Mark();
+ while (iLex.Peek() != KImcvSingleQuote && !iLex.Eos())
+ {
+ iLex.Inc();
+ }
+ TPtrC8 marked = iLex.MarkedToken();
+
+ charset8.Set(marked );
+ iLex.Inc(); // Past '
+
+ // Extract LANGUAGE token
+ iLex.Mark();
+ while (iLex.Peek() != KImcvSingleQuote && !iLex.Eos())
+ {
+ iLex.Inc();
+ }
+ TPtrC8 marked1 = iLex.MarkedToken();
+ language8.Set(marked1);
+ }
+
+ } // else, param encoded
+
+ TLexMark8 mark;
+ TPtrC8 param = ExtractParameterString(mark);
+ // Save parameter data
+ TInt maxlen=info->Des().MaxLength();
+ if ((*info).Length() + param.Length() > maxlen )
+ {
+ info = info->ReAllocL(maxlen + param.Length() + MaxMimeParameterValueLength);
+ CleanupStack::Pop();
+ CleanupStack::PushL(info);
+ }
+ info->Des().Append( param );
+ } while( ( aOffset=FindAttribute(aTag,iLex.Remainder()))!=KErrNotFound );
+
+ TPtr8 infoPtr(info->Des());
+
+ DecodeRfc2231ParameterInfoL( infoPtr, rBuffer, charset8/*, language8*/ );
+
+ CleanupStack::PopAndDestroy(info);
+ return parameterPresent;
+ }
+
+
+//----------------------------------------------------------------------------------------
+TBool CMimeParser::DecodeRfc2231ParameterInfoL(TDes8& aInput, TDes& rBufOut,
+ TPtrC8 aCharset/*, TPtrC8 aLanguage*/)
+//----------------------------------------------------------------------------------------
+ {
+ HBufC8* QPdecodedbuf = HBufC8::NewLC( aInput.Length() );
+ TPtr8 ptr(QPdecodedbuf->Des());
+
+ iImRecvConvert.iQPCodec.SetQPChar(KImcvPercentSign);
+ iImRecvConvert.iQPCodec.Decode( aInput, ptr);
+ iImRecvConvert.iQPCodec.SetQPChar(KImcvEquals);
+
+ // Convert parameter, based on charset supplied.
+ CImConvertCharconv& charconv = iImRecvConvert.CharacterConverter();
+ TUint id = charconv.GetMimeCharsetUidL( aCharset);
+
+ if(id==KUidMsvCharsetNone)
+ {
+ id=charconv.SystemDefaultCharset();
+ }
+
+ // DEF055073 - find the maximum length of the descriptor we are filling
+ TInt maxLength = rBufOut.MaxLength();
+
+ if (!charconv.PrepareToConvertToFromOurCharsetL(id))
+ {
+ // DEF055073 - Only attempt copy, if ptr will fit inside rBufOut
+ if(ptr.Length() <= maxLength)
+ {
+ rBufOut.Copy(ptr);
+ }
+ /* Otherwise, just copy what will fit. This is OK, because
+ * charconv.ConvertToOurCharsetL also truncates to avoid
+ * an overflow */
+ else
+ {
+ rBufOut.Copy(ptr.Left(maxLength));
+ }
+ }
+ else
+ {
+ // Charset found, so do conversion
+ TInt unconverted;
+ TInt indexOfFirst;
+ TInt rem = charconv.ConvertToOurCharsetL(ptr, rBufOut, unconverted, indexOfFirst);
+ if (rem < 0) // error
+ Append(rBufOut, ptr);
+ else if (rem && rem < KConversionRemainderLength)
+ {
+ // DEF055073 - Only attempt copy, if ptr will fit inside rBufOut
+ if(ptr.Length() <= maxLength)
+ {
+ rBufOut.Copy(ptr);
+ }
+ /* Otherwise, just copy what will fit. This is OK, because
+ * charconv.ConvertToOurCharsetL also truncates to avoid
+ * an overflow */
+ else
+ {
+ rBufOut.Copy(ptr.Left(maxLength));
+ }
+ }
+
+ }
+
+ CleanupStack::PopAndDestroy(QPdecodedbuf);
+ return ETrue;
+ }
+
+
+// Check for delimiter & mark parameter text string
+//----------------------------------------------------------------------------------------
+TPtrC8 CMimeParser::ExtractParameterString(TLexMark8& rMark)
+//----------------------------------------------------------------------------------------
+ {
+ // move on to the first char of the boundary ; this MIGHT be a double-quote
+
+ TBool delimited = EFalse;
+ TBool encodedWord = EFalse; //Non compliance of RFC-2047 for ContentType & Content Disposition
+ iLex.Inc();
+
+ if ( iLex.Peek() == KImcvDoubleQuote )
+ {
+ delimited = ETrue;
+ iLex.Inc();
+ }
+
+ while ( iLex.Peek().IsSpace() )
+ {
+ iLex.Inc();
+ }
+
+ iLex.Mark( rMark );
+
+ //Check if the encoded_word exists, set the flag to parse the string.
+ //Ex: Content-Disposition: attachment; filename==?KOI8-R?B?RG9jdW1lbnQ2LnR4dA==?=
+ if(iLex.Peek() == KImcvEquals) //search for QuestionMark followed by Equals
+ {
+ iLex.Inc();
+ if(iLex.Peek() == KImcvQuestionMark)
+ {
+ encodedWord = ETrue;
+ iLex.Inc();
+ }
+ else
+ {
+ iLex.UnGet();
+ }
+ }
+
+ TBool finished = EFalse;
+ while ( !finished && !iLex.Eos() )
+ {
+ iLex.Inc();
+
+ if ( delimited )
+ {
+ finished = (iLex.Peek() == KImcvDoubleQuote);
+ }
+ else if(encodedWord) //Parse the string if encoded_word exists.
+ { //Ex: Content-Disposition: attachment; filename==?KOI8-R?B?RG9jdW1lbnQ2LnR4dA==?=
+ if(iLex.Peek() == KImcvQuestionMark) // search for Equals followed by QuestionMark, and set finished flag to ETrue.
+ {
+ iLex.Inc();
+ if(iLex.Peek() == KImcvEquals)
+ {
+ iLex.Inc();
+ finished = ETrue;
+ }
+ else
+ {
+ iLex.UnGet();
+ }
+ }
+ }
+ else
+ {
+ finished = ( iLex.Peek().IsSpace() || IsSpecialChar((TUint8)iLex.Peek()) );
+ }
+ }
+
+ return iLex.MarkedToken( rMark );
+ }
+
+
+// Which charset to use .. ? If the body part charset set, use that.
+// else if the main header charset set, use that... else default to us-ascii.
+
+//----------------------------------------------------------------------------------------
+TUint CMimeParser::CurrentCharsetL() const
+//----------------------------------------------------------------------------------------
+ {
+ TPtrC8 paramCharset = GetContentTypeValue(KImcvCharset);
+
+ TUint charsetId;
+ if (paramCharset.Length())
+ // charset parameter present.
+ charsetId = iImRecvConvert.CharacterConverter().GetMimeCharsetUidL(paramCharset);
+ else if (iCharset)
+ // Main Mime header has default charset value.
+ charsetId = iCharset;
+ else
+ charsetId = iDefaultCharset;
+
+ return charsetId;
+ }
+
+
+//----------------------------------------------------------------------------------------
+const TPtrC8 CMimeParser::GetContentTypeValue(const TDesC8& aContentTypeParameter) const
+//----------------------------------------------------------------------------------------
+ {
+ CDesC8Array& contentTypeParams = iMimeHeader->ContentTypeParams();
+ __ASSERT_DEBUG(!(contentTypeParams.Count()&1), User::Invariant());
+
+ TInt result;
+ if (KErrNone==contentTypeParams.Find(aContentTypeParameter,result,ECmpFolded8))
+ {
+ result++;
+ if ((result&1) && result <= contentTypeParams.Count())
+ {
+ // check result+1 is odd & entry exists
+ return iMimeHeader->ContentTypeParams()[result];
+ }
+ }
+ return TPtrC8(); // couldn't find match so return an empty descriptor
+ }
+
+void CImRecvConvert::ImCafRegisterL(const TDesC8& aMimeLine)
+ {
+ if(iCaf)
+ {
+ iCaf->RegisterL(aMimeLine);
+ }
+ }
+
+void CImRecvConvert::ImAddToCafMetaDataL(const TDesC8& aMimeLine)
+ {
+ if(iCaf)
+ {
+ iCaf->AddToMetaDataL(aMimeLine);
+ }
+ }
+
+void CImRecvConvert::ImAddToCafMetaDataL(const TDesC8& aField, const TDesC8& aData)
+ {
+ if(iCaf)
+ {
+ iCaf->AddToMetaDataL(aField,aData);
+ }
+ }
+
+TBool CImRecvConvert::ImCafProcessing() const
+ {
+ if(iCaf)
+ {
+ return iCaf->Processing();
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+TBool CImRecvConvert::ImCafRegistered() const
+ {
+ if(iCaf)
+ {
+ return iCaf->Registered();
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+