diff -r e8c1ea2c6496 -r 8758140453c0 email/email/smtp/src/Local_imcvrecv.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/email/smtp/src/Local_imcvrecv.cpp Thu Jan 21 12:53:44 2010 +0000 @@ -0,0 +1,4020 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Symbian Foundation License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.symbianfoundation.org/legal/sfl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + + + +/** + @file +*/ + + +// epoc include +#include +#include +#include +#include "Local_imcvrecv.h" +#include // TImConvert +#include + +#include // CImHeader +#include // TImAttachmentFile +#include +#include // resource definition for IMCM + +#include // For including KUidMsgTypePOP3 +#include // TResourceReader + +#include + +#ifdef __WINS__ +#include // for maxfilename lengths +#include +#endif + +#ifndef __WINS__ +#include +#endif +#include +#include +#include +#ifndef _MSG_NO_LOGGING +#define __IMUT_LOGGING + + +// literals used +_LIT(KLogFilePath, "c:\\logs\\mailtext\\in.txt"); +_LIT8(KNewLogHeader, "\r\n------ New Receive Session ------\r\n"); +_LIT8(KFound, "found: "); +_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 CLocalImRecvConvert"); +_LIT8(KReseted, "Resetting CLocalImRecvConvert"); +_LIT8(KDeleted, "Deleting CLocalImRecvConvert"); +_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 + +#define UNUSED_VAR( var ) var = var // fix for armv5 build warnings +const TInt KBodyTextChunkSizeBytes = 512; + +_LIT(KIntegerKey,"%d"); +const TInt KSpaceToAddNumber=20; + +// added this to handle DRM files +_LIT8(KTImcvDrm, "testagent.drm"); + + +/** +IsIllegalChar() +Checks if the character is illegal in EPOC32 file system + +@param aChar +@return +*/ +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 == '?'); + } + +/** +RemoveSurroundingCharacters() + +@param aLeft +@param aRight +@param aString +*/ +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 CLocalRfc822Token Functions +//**************************************************************************************** + + +/** +NewLC() +Static factory constructor. Uses two phase +construction. + +@leave KErrNoMemory +@return +A pointer to the newly created CLocalRfc822Token object. +*/ +CLocalRfc822Token* CLocalRfc822Token::NewLC() + { + CLocalRfc822Token* self = new (ELeave) CLocalRfc822Token; + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + + +/** +ConstructL() + +*/ +void CLocalRfc822Token::ConstructL() + { + iOutputLine = HBufC8::NewL(KMaxIMailHeaderReadLineLength + 1); + Reset(); + } + + +/** +CLocalRfc822Token() +Constructor +*/ +CLocalRfc822Token::CLocalRfc822Token() + { + __DECLARE_NAME(_S("CLocalRfc822Token")); + } + + +/** +~CLocalRfc822Token() +Destructor +*/ +CLocalRfc822Token::~CLocalRfc822Token() + { + delete iOutputLine; + iOutputLine=NULL; + } + + +/** +Reset() + +*/ +void CLocalRfc822Token::Reset() + { + iOutputLine->Des().FillZ(); + iBufferedInput.SetLength(0); + iBufferedInput.FillZ(); + + iLastToken = EFalse; + iFirstLine = ETrue; + i822FieldsExist=EFalse; + } + + +/** +ParseNextLineL() + +@param aSourceLine +*/ +void CLocalRfc822Token::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, but without any leading whitespace + { + // find the first non-whitespace character + const TUint8* ptr = iInputLine.Ptr(); + while((*ptr == KImcvSP || *ptr == KImcvTab) && (ptr - iInputLine.Ptr() < iInputLine.Length())) ptr++; + + // make sure we're not about to exceed the buffer + if((iOutputLine->Length() + iInputLine.Length()) > iOutputLine->Des().MaxLength()) + iOutputLine = iOutputLine->ReAlloc(iOutputLine->Length() + iInputLine.Length()); + + // now copy the remaining data into the buffer + iOutputLine->Des().Append(iInputLine.Right(iInputLine.Length() - (ptr - iInputLine.Ptr()))); + } + } + 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(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; + } + + +/** +PriorityAndReceiptsMatchAndRemoveL() + +@param aPriority +*/ +TBool CLocalRfc822Token::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 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 CLocalImRecvConvert +//**************************************************************************************** + + +/** +NewLC() +Static factory constructor. Uses two phase construction +and pushes the newly created object into the Cleanup stack. + +@param anFs +@param aServerEntry +@param aMsgType +@param aEmailServiceId +@leave KErrNoMemory +@return +A pointer to the newly created CLocalImRecvConvert object. +*/ +CLocalImRecvConvert* CLocalImRecvConvert::NewLC(RFs& anFs, CMsvServerEntry* aServerEntry, + TUid aMsgType, TMsvId aEmailServiceId) + { + CLocalImRecvConvert* self = new (ELeave) CLocalImRecvConvert(anFs, aServerEntry, aMsgType, + aEmailServiceId); + CleanupStack::PushL(self); + self->ConstructL(anFs); + return self; + } + + +/** +NewL() +Static factory constructor. Uses two phase +construction. + +@param anFs +@param aServerEntry +@param aMsgType +@param aEmailServiceId +@leave KErrNoMemory +@return +A pointer to the newly created CLocalImRecvConvert object. +*/ +CLocalImRecvConvert* CLocalImRecvConvert::NewL(RFs& anFs, CMsvServerEntry* aServerEntry, + TUid aMsgType, TMsvId aEmailServiceId) + { + CLocalImRecvConvert* self = CLocalImRecvConvert::NewLC(anFs, aServerEntry, aMsgType, + aEmailServiceId); + CleanupStack::Pop(); + return self; + } + + +/** +ConstructL() + +@param anFs +*/ +void CLocalImRecvConvert::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); + + 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); + 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 = CLocalRfc822Token::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 = CLocalMimeParser::NewL(*this); + + + // logfile stuff + iImcvLog=NULL; + +#ifdef __IMUT_LOGGING + TRAPD(err,iImcvLog=CImLog::NewL(KLogFilePath, EAppend)); + UNUSED_VAR( err ); +#endif + + iRootEntryId = EntryId(); + + iNotFinishedRfc822Header = ETrue; + + iEmailEntry = new (ELeave) TMsvEmailEntry; + iReceivingHeadersOnly=EFalse; + + iParent = new (ELeave) CArrayFixFlat(3); + + iFirstBoundaryReached=EFalse; + + ResetL(); + + RECVLOG( KNewLogHeader ) + } + + +/** +CLocalImRecvConvert() + +@param anFs +@param aServerEntry +@param aMsgType +@param aEmailServiceId +*/ +CLocalImRecvConvert::CLocalImRecvConvert(RFs& anFs, CMsvServerEntry* aServerEntry, + TUid aMsgType, TMsvId aEmailServiceId) + : iServerEntry(aServerEntry), iNewMsgType(aMsgType), iDefaultEntryType(ETextEntry), + iEmailServiceId(aEmailServiceId), iAttachmentFile(anFs) + { + __DECLARE_NAME(_S("CLocalImRecvConvert")); + } + + +/** +~CLocalImRecvConvert() +Destructor +*/ +CLocalImRecvConvert::~CLocalImRecvConvert() + { + delete iEmailEntry; + iEmailEntry=NULL; + delete iOutputHeader; + iOutputHeader=NULL; + delete iBodyText; + iBodyText=NULL; + delete iBodyBuf; + iBodyBuf=NULL; + delete iParaLayer; + iParaLayer=NULL; + delete iCharLayer; + iCharLayer=NULL; + delete iOutputBody; + iOutputBody=NULL; + delete iRemovedAttachmentTag; + iRemovedAttachmentTag=NULL; + delete iDefaultAttachmentName; + iDefaultAttachmentName=NULL; + delete iRfc822Token; + iRfc822Token=NULL; + delete iMimeParser; + iMimeParser=NULL; + delete iHeaderConverter; + iHeaderConverter=NULL; + delete iCharConv; + iCharConv=NULL; + delete iCharacterConverter; + iCharacterConverter=NULL; + delete iAttachmentFullPath; + iAttachmentFullPath=NULL; + delete iImcvUtils; + iImcvUtils=NULL; + delete iParent; + iParent=NULL; + RECVLOG(KDeleted) + delete iImcvLog; + iImcvLog=NULL; + delete iPartialEmailFooter; + iPartialEmailFooter=NULL; + } + + +/** +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 + +*/ +void CLocalImRecvConvert::ResetForHeadersL() + { + ResetL(); + iReceivingHeadersOnly=ETrue; + } + +/** +ResetL() + +*/ +void CLocalImRecvConvert::ResetL() + { + RECVLOG(KReset) + iBodyId=KMsvNullIndexEntryId; + iSizeOfAttachmentsRemoved=0; + + User::LeaveIfError(iServerEntry->SetEntry(iRootEntryId)); + + iReceivingHeadersOnly=EFalse; + iMessageEntryCalled=EFalse; + iLeaveError=KErrNone; + iCurrentMultipartFolderEntryId=0; + + TParentDetails parent; + parent.iAttachment=parent.iMHTML=EFalse; + parent.iSize=0; + iParent->InsertL(0,parent); + + 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 CLocalImRecvConvert (ie RFile::Create) + + iGlobalIndent=0; + iLongestLine=50; + + iImPriority=EMsvMediumPriority; + delete iImcvUtils; + iImcvUtils=NULL; + iImcvUtils=CImcvUtils::NewL(); + + // reset internal date + iTimeDate.HomeTime(); + + 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) + } + + +/** +ResetForNewEntryL() + +@param entryType +*/ +void CLocalImRecvConvert::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); + } + } + + +/** +ResetForNonMimeEntryL() + +*/ +void CLocalImRecvConvert::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) + } + + +/** +ParseNextField() + +@param aSourceLine +*/ +TInt CLocalImRecvConvert::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; + } + + +/** +ParseNextLineL() + +@param aSourceLine +*/ +void CLocalImRecvConvert::ParseNextLineL(const TDesC8& aSourceLine) + { + iParsedMimeBoundaryLast=0; + if(!iFinishedHeader) + { + // start by getting the next token from the header + iRfc822Token->ParseNextLineL(aSourceLine); + + switch(iRfc822Token->iHeaderPart) + { + case CLocalRfc822Token::EUnknown: + case CLocalRfc822Token::ENotFinished: + RECVLOG(KPartLine) + break; + case CLocalRfc822Token::EFrom: + iOutputHeader->SetFromL(*iRfc822Token->OutputLine()); + LOGGING(KFound,KImcvFromPrompt); + break; + case CLocalRfc822Token::EReplyTo: + iOutputHeader->SetReplyToL(*iRfc822Token->OutputLine()); + LOGGING(KFound,KImcvReplyToPrompt); + break; + case CLocalRfc822Token::ETo: + ParseRecipientListL(iOutputHeader->ToRecipients()); + LOGGING(KFound,KImcvToPrompt); + break; + case CLocalRfc822Token::ECc: + ParseRecipientListL(iOutputHeader->CcRecipients()); + LOGGING(KFound,KImcvCcPrompt); + break; + case CLocalRfc822Token::EBcc: + ParseRecipientListL(iOutputHeader->BccRecipients()); + LOGGING(KFound,KImcvBccPrompt); + break; + case CLocalRfc822Token::ESubject: + iOutputHeader->SetSubjectL(*iRfc822Token->OutputLine()); + LOGGING(KFound,KImcvSubjectPrompt); + break; + case CLocalRfc822Token::EDate: + iRfc822Date.ParseDateField(*iRfc822Token->OutputLine() , iTimeDate); + if (iEmailEntry->Id() != iTopMessagePart) + { + iEmailEntry->iDate = iTimeDate; + } + LOGGING(KFound,KImcvDatePrompt); + break; + case CLocalRfc822Token::EMessageId: + iOutputHeader->SetImMsgIdL(*iRfc822Token->OutputLine()); + LOGGING(KFound,KImcvMessageIdPrompt); + break; + case CLocalRfc822Token::EPriority: + iImPriority=iImcvUtils->EvaluatePriorityText(*iRfc822Token->OutputLine()); + LOGGING(KFound,KImcvPriorityPrompt); + break; + case CLocalRfc822Token::EReturnReceiptTo: + iOutputHeader->SetReceiptAddressL(*iRfc822Token->OutputLine()); + LOGGING(KFound,KReturnReceiptTo); + break; + case CLocalRfc822Token::EEndOfHeader: + // the next line goes in the body part + iFinishedHeader = ETrue; + RECVLOG(KEndOFHeader) + break; + default: + RECVLOG(KEndOFHeader) + break; + } + + + if(iRfc822Token->iHeaderPart != CLocalRfc822Token::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(); + + 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); + } + } + + +/** +MessageEntryDetailsL() + +@return +*/ +TMsvEmailEntry CLocalImRecvConvert::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)); + delete iEmailEntry; + iEmailEntry=NULL; + iEmailEntry = new (ELeave) TMsvEmailEntry(iServerEntry->Entry()); + } + + return *iEmailEntry; + } + + +/** +MessageCompleteL() + +@param aEmailEntry +*/ +void CLocalImRecvConvert::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->ChangeEntry(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)); + } + + +/** +MessageCompleteL() + +@param aPartialDownload +*/ +void CLocalImRecvConvert::MessageCompleteL(TBool aPartialDownload) + { + iPartialEmail=aPartialDownload; + MessageCompleteL(); + } + + +/** +MessageCompleteL() +*/ +void CLocalImRecvConvert::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(KStoreBodyText); + MoveToParentEntryL(); + + if( iTopMessagePart==EntryId() && iFinishedHeader) + { + iEmailEntry->SetVisible(ETrue); + iEmailEntry->SetInPreparation(EFalse); + iEmailEntry->SetBodyTextComplete(ETrue); + iEmailEntry->SetComplete(ETrue); + + // 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(NULL!=iEmailEntry) + { + delete iEmailEntry; + iEmailEntry=NULL; + } + iEmailEntry = new (ELeave) TMsvEmailEntry(iServerEntry->Entry()); + iEmailEntry->SetVisible(ETrue); + iEmailEntry->SetInPreparation(EFalse); + iEmailEntry->SetComplete(ETrue); + iEmailEntry->SetBodyTextComplete(ETrue); + + iEmailEntry->SetAttachment(iParent->At(0).iAttachment); + iEmailEntry->SetMHTMLEmail(iParent->At(0).iMHTML); + + 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) + } + + +/** +PrepareDecoder() + +*/ +void CLocalImRecvConvert::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; + } + + +/** +DecodeAndStoreLineL() + +@param aSourceLine +*/ +void CLocalImRecvConvert::DecodeAndStoreLineL(const TDesC8& aSourceLine) + { + TInt sourceLineLength=aSourceLine.Length(); + TBool blankLine = 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); + + 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) + { + if (iLeftOver.Length()) + { + des.Insert(0, iLeftOver); + iLeftOver.SetLength(0); + } + + if(iCurrentPartIsRichText) + { + if (iStore8BitData) + iBodyBuf->InsertL(iBodyBuf->Size(), des); + else + WriteToBodyL(des, blankLine); + } + else + WriteToAttachmentL(des); + } + } // end else + + CleanupStack::PopAndDestroy(); // convertedLine + } + + +/** +ParseMimeLineL() + +@param aSourceLine +*/ +void CLocalImRecvConvert::ParseMimeLineL(const TDesC8& aSourceLine) + { + + 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; + } + } + else + { + // 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); + } + } + } + + +/** +EndOfHeaderProcessingL() + +*/ +void CLocalImRecvConvert::EndOfHeaderProcessingL() + { + CreateEntryL(); + StoreEntryStreamsL(KStore822Header); + iEntryDataSize = 0; + } + + +/** +EndOfHeaderMIMEProcessingL() +Have just finished processing header, what next.. ? +All MIME entry entry creation takes place here. + +*/ +void CLocalImRecvConvert::EndOfHeaderMIMEProcessingL() + { + if (iMimeParser->IsMessageDigest()) + iDefaultEntryType=EMessageEntry; + + if (iMimeParser->VCard() || iMimeParser->VCalendar()) + { + 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; + } + + +/** +ParseBodyLineL() +Non Mime body parsing + +@param aSourceLine +*/ +void CLocalImRecvConvert::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; + } + } + + +/** +CreateNonMIMEFolderEntryL() + +@param aCurrentId +@return +*/ +TBool CLocalImRecvConvert::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; + } + + +/** +CreateAttachmentL() + +@return +*/ +TBool CLocalImRecvConvert::CreateAttachmentL() + { + // Get and set Attachment File path + TFileName filepath; + // 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; + } + if (addExtension) + AddFileExtension(); + + CMsvStore* store = iServerEntry->EditStoreL(); + CleanupStack::PushL(store); + CMsvAttachment* attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile); + RFile file; + store->AttachmentManagerExtensionsL().CreateAttachmentL(iAttachmentName,file,attachment); + + iAttachmentFile.SetFileHandle(file,TImAttachmentFile::EImFileWrite); + store->CommitL(); + CleanupStack::PopAndDestroy(store); // store + + if(KErrNone!=iLeaveError) + { + iAttachmentFileState=EFileFailedToOpen; + CloseAttachmentFileL(); + return EFalse; + } + + iAttachmentFileState=EFileIsOpen; + return ETrue; + } + + +/** +WriteToAttachmentL() + +@param text +*/ +void CLocalImRecvConvert::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. + + // Store as Binary files.. + 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) + } + + +/** +CloseAttachmentFileL() + +*/ +void CLocalImRecvConvert::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) + 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) + 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); + + // Remove the attachment and commit the store. + store->AttachmentManagerExtensionsL().RemoveAttachmentL(0); + store->CommitL(); + CleanupStack::PopAndDestroy(store); + } + iEmailEntry->SetAttachment(EFalse); + iAttachmentFileState=EFileIsClosed; + RECVLOG(KDeletedAttachFile) + + if(iSavingAttachments && !iStore8BitData) + { + WriteToBodyL(KImcvParagraph); + WriteToBodyL(*iRemovedAttachmentTag); //lost attachment - notify user + TBuf8 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) + 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); + + // Remove the attachment and commit the store. + store->AttachmentManagerExtensionsL().RemoveAttachmentL(0); + 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; + } + } + + +/** +LineIsAllWhitespace() + +@return +*/ +TBool CLocalImRecvConvert::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); + } + + +/** +CheckUUEStartL() + +@param aSourceLine +@return +*/ +TBool CLocalImRecvConvert::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; + } + + +/** +AddFileExtension() +*/ +void CLocalImRecvConvert::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->VCard() || iMimeParser->VCalendar()) + iAttachmentName.Append(KVCardExtension); + 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 buf; + buf.Copy(iMimeParser->ContentSubType()); + iAttachmentName.Append(KImcvFullStop); + iAttachmentName.Append(buf); + } + break; + default: + break; + } // End switch + } + + +/** +WriteToBodyL() + +@param aText +@param aBlankLine +*/ +void CLocalImRecvConvert::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 (startInsertL(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) + } + + +/** +WriteToBodyL() + +@param aText +*/ +void CLocalImRecvConvert::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) + } + + +/** +ParseRecipientListL() + +@param aList +*/ +void CLocalImRecvConvert::ParseRecipientListL(CDesCArray& aList) + { + HBufC8* pBuf = HBufC8::NewLC(KMaxIMailHeaderReadLineLength); + TPtrC8 source(iRfc822Token->OutputLine()->Ptr(), iRfc822Token->OutputLine()->Length()); + const TUint8* ptr = source.Ptr(); + TUint8 lookFor = 0; + TInt count = 0; + TBool finishedEntry = EFalse; + + // get past white space + while(*ptr&&((*ptr==KImcvSP)||(*ptr==KImcvSemiColon))) ptr++; + + while(*ptr&&((ptr-source.Ptr())<(source.Length()))) + { + if(!pBuf->Length()) + { + finishedEntry = EFalse; + } + + switch(*ptr) + { + case KImcvLeftBracket: + if(lookFor==KImcvRightBracket) + count++; + else + { + lookFor = KImcvRightBracket; + count = 1; + } + break; + case KImcvLeftChevron: + if(lookFor==KImcvRightChevron) + count++; + else + { + lookFor = KImcvRightChevron; + count = 1; + } + break; + case KImcvDoubleQuote: + lookFor = (TUint8)(lookFor==KImcvDoubleQuote ? 0 : KImcvDoubleQuote); + count = (lookFor ? 1 : 0); + break; + case KImcvRightBracket: + case KImcvRightChevron: + count -= (*ptr == lookFor ? 1 : 0); + lookFor = (TUint8)((*ptr == lookFor)&&(count == 0) ? 0 : lookFor); + break; + case KImcvComma: + case KImcvSemiColon: + finishedEntry = !lookFor; + break; + } + + if(!finishedEntry) + { + // check we're not about to blow the buffer + if(pBuf->Length() >= pBuf->Des().MaxLength()) + { + pBuf = pBuf->ReAlloc(pBuf->Length() + 64); // arbitrary extension + } + 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); + + // 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 + } + + +/** +ExtractFilename() + +@param aLex +@param rFileName +*/ +void CLocalImRecvConvert::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); + } + + +/** +SetAttachmentName() + +@param aFileName +*/ +void CLocalImRecvConvert::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); + } + + +/** +ReplaceInvalidCharacters() + +@param rFileName +*/ +void CLocalImRecvConvert::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; + } + } + + +/** +StoreEntryStreamsL() +*/ +void CLocalImRecvConvert::StoreEntryStreamsL() + { + StoreEntryStreamsL(KStoreBodyText|KStore822Header|KStoreMIMEHeader); + } + + +/** +StoreEntryStreamsL() + +@param aSettings +*/ +void CLocalImRecvConvert::StoreEntryStreamsL(TInt aSettings) + { + RECVLOG(KStartStoringEntryStream); + if (iReceivingHeadersOnly==EFalse) + { + CMsvStore* entryStore = NULL; + TBool commitStore = EFalse; + + 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); + } + + +/** +Store822HeaderL() + +@param aStore +@param aCommit +*/ +void CLocalImRecvConvert::Store822HeaderL(CMsvStore& aStore, TBool& aCommit) + { + if(iEmptyHeaderSize<(iOutputHeader->DataSize())) + { + iEntryDataSize += iOutputHeader->DataSize()-iEmptyHeaderSize; + RECVLOG(KStoringHeader); + iOutputHeader->StoreL(aStore); + RECVLOG(KStoredHeader); + aCommit = ETrue; + } + } + + +/** +StoreMIMEHeaderL() + +@param aStore +@param aCommit +*/ +void CLocalImRecvConvert::StoreMIMEHeaderL(CMsvStore& aStore, TBool& aCommit) + { + if(iMimeParser->MimeHeaderSize()) + { + RECVLOG(KStoringMIMEHeader); + iMimeParser->StoreMimeHeaderWithoutCommitL(aStore); + aCommit = ETrue; + RECVLOG(KStoredMIMEHeader); + } + } + + +/** +StoreBodyTextL() + +@param aStore +@param aCommit +*/ +void CLocalImRecvConvert::StoreBodyTextL(CMsvStore& aStore, TBool& aCommit) + { + if (iStore8BitData) + { + if(iBodyBuf->Size()) + { + iEntryDataSize += iBodyBuf->Size(); + RECVLOG(KStoring8BitBody); + iBodyText->StoreL(aStore, *iBodyBuf); + aCommit = ETrue; + RECVLOG(KStored8BitBody); + } + } + else + { + if(iOutputBody->DocumentLength()) + { + iEntryDataSize += iOutputBody->DocumentLength(); + RECVLOG(KStoringBody); + aStore.StoreBodyTextL(*iOutputBody); + aCommit = ETrue; + RECVLOG(KStoredBody); + } + } + } + + +/** +StoreEntryDataL() + +@return +*/ +TBool CLocalImRecvConvert::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 + } + + +/** +MoveToParentEntryL() +*/ +void CLocalImRecvConvert::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 childIsAttachment = (iEmailEntry->Attachment() || iEmailEntry->iType == KUidMsvMessageEntry) ? ETrue:EFalse; + TBool childIsMHTML=EFalse; + // Dont want the flag propogated 'up' past a message entry. + if(iEmailEntry->iType != KUidMsvMessageEntry) + childIsMHTML = iEmailEntry->MHTMLEmail() ? ETrue:EFalse; + + //Make the parent entry the current entry + if (NULL!=iEmailEntry) + { + 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; + iParent->At(0).iSize += iEntryDataSize; + + iOutputHeader->Reset(); + iEmptyHeaderSize=iOutputHeader->DataSize(); + iMimeParser->ResetForNewEntry(); + + iEntryDataSize=0; + + RECVLOG(KMovedToParentEntry) + } + +/** +WritePartialFooterL() +Helper function to add the partial footer to the email if it exists + +@param aAmountLeft +*/ +void CLocalImRecvConvert::WritePartialFooterL(TInt aAmountLeft) + { + 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); + 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=NULL; + store = iServerEntry->EditStoreL(); + CleanupStack::PushL(store); + store->StoreBodyTextL(*iOutputBody); + store->Commit(); + CleanupStack::PopAndDestroy(store); + } + else + { + CleanupStack::PopAndDestroy(store); + } + } + } + iServerEntry->SetEntry(id); + } + + +/** +CreateEntryL() +*/ +void CLocalImRecvConvert::CreateEntryL() + { + RECVLOG(KCreatingEntry); + if (NULL!=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->SetMessageFolderType(iMimeParser->MessageFolderType()); + iEmailEntry->SetPriority(iImPriority); + iEmailEntry->SetNew(EFalse); + + if(iOutputHeader->ReceiptAddress().Length()>0) + iEmailEntry->SetReceipt(ETrue); + + iEmailEntry->iDate=iTimeDate; + switch(iEntryType) + { + case EMessageEntry: + 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.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; + 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; + default: + iEmailEntry->iType=KUidMsvAttachmentEntry; + iEmailEntry->iDetails.Set(iAttachmentName); + iEmailEntry->iDescription.Set(iMimeParser->ContentDescription()); + } + + 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); + } + + +/** +Logging() + +@param aString1 +@param aString2 +*/ +void CLocalImRecvConvert::Logging(const TDesC8& aString1, const TDesC8& aString2) + { + TBuf8<1024> aBuf(aString1); + + aBuf.Append(aString2); + RECVLOG(aBuf); + } + + +/** +StoreMessageEntryDetailsL() +*/ + void CLocalImRecvConvert::StoreMessageEntryDetailsL() + { + iEmailEntry->SetAttachment(iParent->At(0).iAttachment); + iEmailEntry->SetMHTMLEmail(iParent->At(0).iMHTML); + + 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=iParent->At(0).iMHTML=EFalse; + iParent->At(0).iSize=0; + } + } + + +/** +DeletedAttachmentSize() + +@return +*/ + TInt CLocalImRecvConvert::DeletedAttachmentSize() + { + return iSizeOfAttachmentsRemoved; + } + +/**************************************************************************** + Class CLocalMimeParser functions +*****************************************************************************/ + +/** +NewLC() +Static factory constructor. Uses two phase construction +and pushes the newly created object into the Cleanup stack. + +@param aImRecvConvert +@return +*/ +CLocalMimeParser* CLocalMimeParser::NewLC(CLocalImRecvConvert& aImRecvConvert) + { + CLocalMimeParser* self = new (ELeave) CLocalMimeParser(aImRecvConvert); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + + +/** +NewL() +Static factory constructor. Uses two phase construction. + +@param aImRecvConvert +@return +*/ + CLocalMimeParser* CLocalMimeParser::NewL(CLocalImRecvConvert& aImRecvConvert) + { + CLocalMimeParser* self = CLocalMimeParser::NewLC(aImRecvConvert); + CleanupStack::Pop(); + return self; + } + + +/** +CLocalMimeParser() + +@param aImRecvConvert +@return +*/ +CLocalMimeParser::CLocalMimeParser(CLocalImRecvConvert& aImRecvConvert): iImRecvConvert(aImRecvConvert), + iStartId(NULL) + + { + __DECLARE_NAME(_S("CLocalMimeParser")); + } + + +/** +ConstructL() +*/ +void CLocalMimeParser::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(); + } + + +/** +~CLocalMimeParser() +*/ +CLocalMimeParser::~CLocalMimeParser() + { + delete iMimeHeader; + iMimeHeader=NULL; + delete iBoundaryText; + iBoundaryText=NULL; + delete iMimeHeaderLine; + iMimeHeaderLine=NULL; + delete iStartId; + iStartId=NULL; + } + + +/** +Reset() +*/ +void CLocalMimeParser::Reset() + { + iBoundaryText->Reset(); + iBoundaryIndex = 0; + iBoundaryLength = 0; + isMime = EFalse; + + ResetForNewEntry(); + } + + +/** +ResetForNewEntry() +*/ +void CLocalMimeParser::ResetForNewEntry() + { + iMimeHeader->Reset(); + iEmptyMimeHeaderSize=iMimeHeader->Size(); + + iContentType = EMimeUnknownContent; + iContentEncoding = EEncodingTypeNone; + iContentDescription.Zero(); + iVCard = EFalse; + iVCalendar = EFalse; + iStartPart=EFalse; + iMessageFolderType = EFolderTypeUnknown; + iTerminatingBoundary = EFalse; + iBoundaryFound = EFalse; + } + + +/** +RestoreMimeParserL() + +@param entryStore +*/ +void CLocalMimeParser::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; + + } + + +/** +ParseLineL() + +@param aSourceLine +*/ +void CLocalMimeParser::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)) + 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(); + } + + delete iMimeHeaderLine; // clean up and null pointer iff CompleteMimeHeader and no foldover append req'd + iMimeHeaderLine=NULL; + } + + +/** +DoMimeVersion() +*/ +void CLocalMimeParser::DoMimeVersion() + { + // extract the MIME version from a header line which we already know + // has 'MIME-Version' start of it. + if(MatchAndRemoveToken(KImcvMimeVersion)) + iCorrectMimeVersion = ETrue; + } + + +/** +DoContentIdL() +*/ +void CLocalMimeParser::DoContentIdL() + { + iMimeHeader->SetContentIDL(iMimeHeaderLine->Des()); + if(iStartId && iStartId->CompareF(iMimeHeaderLine->Des())==KErrNone) + iStartPart=ETrue; + } + + +/** +DoContentLocationL() +*/ +void CLocalMimeParser::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 + } + + +/** +DoContentBaseL() +*/ +void CLocalMimeParser::DoContentBaseL() + { + RemoveSurroundingCharacters(KImcvQuote, KImcvQuote, *iMimeHeaderLine); + iMimeHeader->SetContentBaseL(iMimeHeaderLine->Des()); + } + + +/** +DoAttachmentTypeL() +*/ +void CLocalMimeParser::DoAttachmentTypeL() + { + iImRecvConvert.iEntryType = CLocalImRecvConvert::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); + } + else + if(MatchAndRemoveToken(KTImcvDrm)) + { // added this to handle DRM files + iMimeHeader->SetContentSubTypeL(KTImcvDrm); + } + } + } + + +/** +DoMessageTypeL() +*/ +void CLocalMimeParser::DoMessageTypeL() + { + iImRecvConvert.iEntryType = CLocalImRecvConvert::EMessageEntry; + iImRecvConvert.iEmailPart = CLocalImRecvConvert::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 = CLocalImRecvConvert::EAttachmentEntry; + iImRecvConvert.iEmailPart = CLocalImRecvConvert::KNoPart; + iContentType=EMimeUnknownContent; + iImRecvConvert.iCurrentPartIsRichText=EFalse; + iImRecvConvert.iAttachmentName.Copy(KImcvDeliveryStatus); + iImRecvConvert.iAttachmentName.Append(KTextExtension); + } + else + { + iMessageFolderType=EFolderTypeUnknown; + iMimeHeader->SetContentSubTypeL(KImcvUnknown); + } + } + } + + +/** +DoMultipartTypeForNonMIMEL() +*/ +void CLocalMimeParser::DoMultipartTypeForNonMIMEL() + { + ResetForNewEntry(); + iMessageFolderType=EFolderTypeMixed; + iMimeHeader->SetContentTypeL(KImcvMultipart); + iMimeHeader->SetContentSubTypeL(KImcvMixed); + } + + +/** +DoMultipartTypeL() +*/ +void CLocalMimeParser::DoMultipartTypeL() + { + iImRecvConvert.iEntryType = CLocalImRecvConvert::EFolderEntry; + iImRecvConvert.iEmailPart = CLocalImRecvConvert::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 = CLocalImRecvConvert::KMultiPart; + SetBoundaryL(*paramValue); + CleanupStack::PopAndDestroy(paramValue); + } + + // Extracts start ID if it has been defined; + + ExtractParameterInfoL(KImcvStartPart, paramValue); + if( paramValue!=NULL ) + { + delete iStartId; + iStartId=NULL; + iStartId = paramValue; + } + } + + +/** +DoTextTypeL() +*/ +void CLocalMimeParser::DoTextTypeL() + { + HBufC8* paramValue = NULL; + + iImRecvConvert.iEntryType = CLocalImRecvConvert::ETextEntry; + iContentType=EMimeText; + iMimeHeader->SetContentTypeL(KImcvText); + + if(MatchAndRemoveToken(KImcvForwardSlash)) + { + if(MatchAndRemoveToken(KImcvPlain)) + iMimeHeader->SetContentSubTypeL(KImcvPlain); + else + if(MatchAndRemoveToken(KImcvHtml)) + { + iMimeHeader->SetContentSubTypeL(KImcvHtml); + iImRecvConvert.iEntryType = CLocalImRecvConvert::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); + } + } + if(MatchAndRemoveToken(KImcvVCalender)) + iVCalendar=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); + } + } + + +/** +DoContentTypeL() +*/ +void CLocalMimeParser::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 = HBufC8::NewLC(KMimeEncodedParameterSize); + TPtr paramValue(paramStore->Des()); + TPtr8 paramValue8(paramStore8->Des()); + + ExtractParameterInfoL(KImcvMimeTypeName, paramValue,paramValue8); + if(paramValue.Length()) + { + iMimeHeader->ContentTypeParams().AppendL(KImcvMimeTypeName); + iMimeHeader->ContentTypeParams().AppendL(paramValue8); + iImRecvConvert.SetAttachmentName(paramValue); + } + + CleanupStack::PopAndDestroy(2,paramStore); + } + + +/** +DoEncodingL() +*/ +void CLocalMimeParser::DoEncodingL() + { + 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': + 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; + } + } + + +/** +DoDescriptionL() +*/ +void CLocalMimeParser::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); + } + + +/** +DoDispositionL() +*/ +void CLocalMimeParser::DoDispositionL() + { + TBool inLine = EFalse; + if(MatchAndRemoveToken(KImcvAttachment)) + { + iMimeHeader->SetContentDispositionL(KImcvAttachment); + if (iMimeHeader->ContentSubType()!=KImcvRfc822) + { + iImRecvConvert.iCurrentPartIsRichText = EFalse; + } + if (iImRecvConvert.iEntryType!=CLocalImRecvConvert::EMessageEntry) + { + iImRecvConvert.iEntryType = CLocalImRecvConvert::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 = HBufC8::NewLC(KMimeEncodedParameterSize); + TPtr8 paramValue8(paramStore8->Des()); + + ExtractParameterInfoL(KImcvMimeDispositionFilename, paramValue,paramValue8); + if(paramValue.Length()) + { + iMimeHeader->ContentTypeParams().AppendL(KImcvMimeDispositionFilename); + 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); + iImRecvConvert.iCurrentPartIsRichText = EFalse; + if (iImRecvConvert.iEntryType!=CLocalImRecvConvert::EMessageEntry) + { + iImRecvConvert.iEntryType = CLocalImRecvConvert::EAttachmentEntry; + } + } + else + iMimeHeader->SetContentDispositionL(KImcvInline); + } + } + + +/** +ContentSubType() + +@return +*/ +TPtrC8 CLocalMimeParser::ContentSubType() const + { + return iMimeHeader->ContentSubType();; + } + + +/** +VCard() + +@return +*/ +TBool CLocalMimeParser::VCard() const + { + return iVCard; + } + + +/** +VCalendar() + +@return +*/ +TBool CLocalMimeParser::VCalendar() const + { + return iVCalendar; + } + + +/** +StartPart() + +@return +*/ +TBool CLocalMimeParser::StartPart() const + { + return iStartPart; + } + + +/** +MessageFolderType() + +@return +*/ +TImEmailFolderType CLocalMimeParser::MessageFolderType() const + { + return iMessageFolderType; + } + + +/** +SetMessageFolderType() + +@param aFolderType +*/ +void CLocalMimeParser::SetMessageFolderType(TImEmailFolderType aFolderType) + { + iMessageFolderType=aFolderType; + } + + +/** +SetBoundaryL() + +@param aBoundaryText +*/ +void CLocalMimeParser::SetBoundaryL(const TDesC8& aBoundaryText) + { + TBuf8 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)? --iBoundaryIndex: 0; + iBoundaryLength = iBoundaryText->MdcaPoint(iBoundaryIndex).Length(); + } + + +/** +IsBoundary() + +@param aSourceLine + @return +*/ +TBool CLocalMimeParser::IsBoundary(const TDesC8& aSourceLine) + { + if(iBoundaryText->MdcaCount()) + { + TInt found = 0; + TInt aLineLength = aSourceLine.Length(); + TInt compareLength = aLineLength > iBoundaryLength ? iBoundaryLength : aLineLength; + 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 >= iBoundaryLength+4) // allow for CRLF & then check for the double hyphen + { + if((aSourceLine[iBoundaryLength] == KImcvHyphen) && (aSourceLine[iBoundaryLength+1] == KImcvHyphen)) + iTerminatingBoundary = ETrue; // this is a terminating boundary + } + else + iTerminatingBoundary = EFalse; + + return KBoundaryFound; + } + + return KBoundaryNotFound; + } + + +/** +RemoveBoundary() +*/ +void CLocalMimeParser::RemoveBoundary() + { + iBoundaryText->Delete(iBoundaryIndex); + if(iBoundaryText->MdcaCount()) + { + iBoundaryIndex = iBoundaryText->MdcaCount(); //iBoundaryIndex stores a count value not the index here + + iBoundaryIndex = (iBoundaryIndex)? --iBoundaryIndex: 0; + iBoundaryLength = iBoundaryText->MdcaPoint(iBoundaryIndex).Length(); + } + } + + +/** +MatchAndRemoveToken() + +@param aToken +@return +*/ +TBool CLocalMimeParser::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); + } + + +/** +IsSpecialChar() + +@param aChar +@return +*/ +TBool CLocalMimeParser::IsSpecialChar( const TUint8 aChar ) + { + return (aChar == '(' || aChar == ')' || aChar == '<' || aChar == '>' || aChar == '@' + || aChar == ',' || aChar == ';' || aChar == ':' || aChar == '\\'|| aChar == '"' + || aChar == '/' || aChar == '[' || aChar == ']' || aChar == '?' || aChar == '='); + } + + +/** +ExtractParameterInfoL() +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. + +@param aTag +@param rBuffer +*/ +void CLocalMimeParser::ExtractParameterInfoL(const TDesC8& aTag, HBufC8*& rBuffer) + { + rBuffer = NULL; + + TLexMark8 mark; + TInt offset; + + 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(); + } + } + } + + +/** +ExtractParameterInfoL() +iLex should currently be pointing at the space after content-type description. + +@param aTag +@param rBuffer +@param rBuffer8 +*/ +void CLocalMimeParser::ExtractParameterInfoL(const TDesC8& aTag, TDes16& rBuffer, TDes8& rBuffer8) + { + TInt offset; + + rBuffer.Copy(KNullDesC); + + // we need to extract the text from the same line + // iLex should currently be pointing at the space after content-type description + + if( (offset=iLex.Remainder().FindF(aTag))!=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); + + rBuffer8.Copy(param); + + if ( ParseRfc2047ParameterInfoL(param, rBuffer) == EFalse ) + { + iLex.UnGetToMark(initMark); + ParseRfc2231ParameterInfoL(aTag, rBuffer, offset ); + } + } + } + + +/** +ParseRfc2047ParameterInfoL() +Detect encoding of form =?charset?Q?" Text "?= + +@param aParam +@param rBuffer +@return +*/ +TBool CLocalMimeParser::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; + } + + +/** +ParseRfc2231ParameterInfoL() +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 + +@param aTag +@param rBuffer +@param aOffset +@return +*/ +TBool CLocalMimeParser::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; + + // Following rfc 2231, parameter may be encoded & folded in the following way + // + // *0*=us-ascii'en' + // *1*=< more encoded data> + // *2= + + 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 (iLex.Peek() == KImcvAny) + iLex.Inc(); // Past * + } + + + // Must get charset & language information etc.. + + if (!count) + { + + 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=iLex.Remainder().FindF(aTag))!=KErrNotFound ); + + TPtr8 infoPtr(info->Des()); + + DecodeRfc2231ParameterInfoL( infoPtr, rBuffer, charset8/*, language8*/ ); + + CleanupStack::PopAndDestroy(info); + return parameterPresent; + } + + +/** +DecodeRfc2231ParameterInfoL() + +@param aInput +@param rBufOut +@param aCharset +@return +*/ +TBool CLocalMimeParser::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(); + } + + if (!charconv.PrepareToConvertToFromOurCharsetL(id)) + { + rBufOut.Copy(ptr); + } + 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) + rBufOut.Copy(ptr); + + } + + CleanupStack::PopAndDestroy(QPdecodedbuf); + return ETrue; + } + + +/** +ExtractParameterString() +Check for delimiter & mark parameter text string + +@param rMark +@return +*/ +TPtrC8 CLocalMimeParser::ExtractParameterString(TLexMark8& rMark) + { + // move on to the first char of the boundary ; this MIGHT be a double-quote + + TBool delimited = EFalse; + iLex.Inc(); + + if ( iLex.Peek() == KImcvDoubleQuote ) + { + delimited = ETrue; + iLex.Inc(); + } + + while ( iLex.Peek().IsSpace() ) + iLex.Inc(); + + iLex.Mark( rMark ); + + TBool finished = EFalse; + while ( !finished && !iLex.Eos() ) + { + iLex.Inc(); + + if ( delimited ) + finished = (iLex.Peek() == KImcvDoubleQuote); + else + finished = ( iLex.Peek().IsSpace() || IsSpecialChar((TUint8)iLex.Peek()) ); + } + + return iLex.MarkedToken( rMark ); + } + + +/** +CurrentCharsetL() +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. + +@return +*/ +TUint CLocalMimeParser::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; + } + + +/** +GetContentTypeValue() + +@param aContentTypeParameter +@return +*/ +const TPtrC8 CLocalMimeParser::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 + } + + +/**s +EntryId() + +@return +*/ +TMsvId CLocalImRecvConvert::EntryId() + { + // If we are temporarily on the null entry then return the saved entry + if (iServerEntry->Entry().Id() == KMsvNullIndexEntryId) + { + return iSavedEntryId; + } + else + { + return iServerEntry->Entry().Id(); + } + }