diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imapsession/src/cimapappend.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imapsession/src/cimapappend.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,301 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "cimapappend.h" + +#include + +#include "cimapsessionconsts.h" +#include "moutputstream.h" +#include "cimaplogger.h" +#include "cimapsession.h" + +_LIT8(KTxtAppendFormat, "%d APPEND %S {%d}\r\n"); + +// TCallBack return values +static const TInt KDontCallAgain = 0; +static const TInt KCallAgain = 1; + +/** +The factory constructor. Part of two phased construction +*/ +CImapAppend* CImapAppend::NewL(CImapFolderInfo* aSelectedFolderData, TInt aLogId, CImSendMessage& aSource, const TDesC& aDestinationMailboxName, CImapSession& aParentSession) +// static method + { + CImapAppend* self = new(ELeave) CImapAppend(aSelectedFolderData, aLogId, aSource, aParentSession); + CleanupStack::PushL(self); + self->ConstructL(aDestinationMailboxName); + CleanupStack::Pop(self); + return self; + } + +void CImapAppend::ConstructL( const TDesC& aDestinationMailboxName) + { + iLineBufferForSend = HBufC8::NewL(KImMailMaxBufferSize); + iDestinationMailboxName = EncodeMailboxNameForSendL(aDestinationMailboxName); + iBackgroundCalculationStepper = CIdle::NewL(CActive::EPriorityIdle); + } + +/** +Constructor. +*/ +CImapAppend::CImapAppend(CImapFolderInfo* aSelectedFolderData, TInt aLogId, CImSendMessage& aSource, CImapSession& aParentSession) + : CImapCommandEx(aSelectedFolderData, aLogId) + , iSource(aSource) + , iParentSession(aParentSession) + , iAppendCommandSendState(EAppendWaitingToStart) + { + } + +/** +Destructor. +*/ +CImapAppend::~CImapAppend() + { + delete iBackgroundCalculationStepper; + delete iLineBufferForSend; + delete iDestinationMailboxName; + } + +/** +Formats and sends the IMAP APPEND command. +@param aTagId Command sequence id which will be sent along with the IMAP command. +@param aStream A wrapper for the outbound stream of a connected socket, using which +the command will be send to the server +*/ +void CImapAppend::SendMessageL(TInt aTagId, MOutputStream& aStream) + { + // From RFC3501 + // append = "APPEND" SP mailbox [SP flag-list] [SP date-time] SP literal + // + // we have chosen not to supply the optional flag-list or date-time + + __ASSERT_DEBUG(iAppendCommandSendState==EAppendWaitingToStart, TImapServerPanic::ImapPanic(TImapServerPanic::EAppendInvalidState)); + + iTagId = aTagId; + iStream = &aStream; + + // Calculate the size of the message + __LOG_TEXT(iLogId, "CImapAppend::SendMessageL() - Start calculating message size"); + iAppendCommandSendState = EAppendCalculatingSize; + iBackgroundCalculationStepper->Start(TCallBack(MessageSizeCalculationStep, this)); + } + +/** +Static callback method that was requested through iBackgroundCalculationStepper. +It calls the equivalent MessageSizeCalculationStep on the supplied CImapAppend object +which performs a single step of the message size calculation. +@param aSelf The CImapAppend object that requested the asynchronous callback. +@return Wheher this method needs to be called again, to calculate the next step. +*/ +TInt CImapAppend::MessageSizeCalculationStep(TAny* aSelf) +// static method + { + CImapAppend* self = reinterpret_cast(aSelf); + TInt callAgain = KDontCallAgain; + TRAPD(err, callAgain = self->MessageSizeCalculationStepL()); + + if (err) + { + self->iParentSession.AsyncSendFailed(err); + } + + return callAgain; + } + +/** +Performs a single step of the message size calculation. +@return whether this method needs to be called again, to calculate the next step. +*/ +TInt CImapAppend::MessageSizeCalculationStepL() + { + __ASSERT_DEBUG(iAppendCommandSendState==EAppendCalculatingSize, TImapServerPanic::ImapPanic(TImapServerPanic::EAppendInvalidState)); + + TInt callAgain = KDontCallAgain; + + TPtr8 nextLine = iLineBufferForSend->Des(); + TInt status = iSource.NextLineL(nextLine, iPadCount); + + iTotalMessageSize += iLineBufferForSend->Length(); + +#ifdef __IMAP_LOGGING + { + // Log the first few bytes only + TPtrC8 truncatedData = iLineBufferForSend->Left(60); + // only log up to the first \r\n + TInt pos = truncatedData.Find(KImapTxtCrlf); + if (pos >= 0) + { + truncatedData.Set(truncatedData.Left(pos)); + } + else + { + // \r\n may have been split by the intial truncation, + // so check for the \r + pos = truncatedData.Locate('\r'); + if (pos >= 0) + { + truncatedData.Set(truncatedData.Left(pos)); + } + } + + __LOG_FORMAT((iLogId, "CImapAppend::MessageSizeCalculationStepL() - %3d ==> %3d [%S]", iLineBufferForSend->Length(), iTotalMessageSize, &truncatedData)); + } +#endif //__IMAP_LOGGING + + if (status == KImCvFinished) + { + iSource.Reset(); + iPadCount = 0; + nextLine.Zero(); + + SendAppendCommandWithSizeL(); + } + else + { + callAgain = KCallAgain; + } + + return callAgain; + } + +/** +Sends the IMAP APPEND command. +This method is called after the size of the message has been calculated. +*/ +void CImapAppend::SendAppendCommandWithSizeL() + { + TInt bufferLength = KTxtAppendFormat().Length() + TagLength(iTagId) + iDestinationMailboxName->Length() + TagLength(iTotalMessageSize); + + delete iOutputBuffer; + iOutputBuffer=NULL; + + iOutputBuffer = HBufC8::NewL(bufferLength); + iOutputBuffer->Des().Format(KTxtAppendFormat, iTagId, iDestinationMailboxName, iTotalMessageSize); + + // Send the data on the output stream + iAppendCommandSendState = EAppendWaitingForContinuationResponse; + iStream->SendDataReq(*iOutputBuffer); + } + +/** +Sends the first line of the message upon reciept of a continuation response from server. +The next line will be sent SendDataCnfL() is called, indicating that the first line has been succesfully sent. +*/ +void CImapAppend::ParseContinuationResponseL() + { + if (iAppendCommandSendState != EAppendWaitingForContinuationResponse) + { + CorruptDataL(); + } + + iAppendCommandSendState = EAppendSendingMessageData; + SendNextMessageLineL(); + } + +/** +This is called when the previous line has been succesfully sent. +If there are more lines of the message to send, then the next line is sent now. +@return ECompleteUntagged +*/ +void CImapAppend::SendDataCnfL() + { + if (iAppendCommandSendState == EAppendSendingMessageData) + { + SendNextMessageLineL(); + } + else if (iAppendCommandSendState == EAppendWaitingForContinuationResponse) + { + __LOG_TEXT(iLogId, "CImapAppend::SendDataCnfL() - Initial Command Sent"); + } + else + { + __ASSERT_DEBUG(iAppendCommandSendState == EAppendSendingLastMessageData, TImapServerPanic::ImapPanic(TImapServerPanic::EAppendInvalidState)); + __LOG_TEXT(iLogId, "CImapAppend::SendDataCnfL() - Message Data Sent"); + + iAppendCommandSendState = EAppendFinishedSending; + } + } + +/** +Sends the first or next line of the message to the server. +SendDataCnfL() will be called when the line has been succefully sent. +*/ +void CImapAppend::SendNextMessageLineL() + { + __ASSERT_DEBUG(iAppendCommandSendState == EAppendSendingMessageData, TImapServerPanic::ImapPanic(TImapServerPanic::EAppendInvalidState)); + + // Receive the next line to be sent, by passing in a modifyable descriptor. + // Note that iPadCount receives the padding count, which will be passed into the next call to NextLineL() + TPtr8 nextLine = iLineBufferForSend->Des(); + TInt sendStatus = iSource.NextLineL(nextLine, iPadCount); + + __LOG_TEXT(iLogId, "CImapAppend::SendNextMessageLineL()"); + + if (sendStatus == KImCvFinished) + { + // As well as sending the last part of the literal block, + // we need to send a CRLF to terminate the APPEND command. + // The CRLF is not part of the message data. + + // Is there enough room to append the CRLF? + TInt lengthWithCrlf = nextLine.Length() + KImapTxtCrlf().Length(); + if (nextLine.MaxLength() < lengthWithCrlf) + { + // Make enough space for the CRLF + iLineBufferForSend = iLineBufferForSend->ReAllocL(lengthWithCrlf); + nextLine.Set(iLineBufferForSend->Des()); + } + + __LOG_TEXT(iLogId, "CImapAppend::SendNextMessageLineL() - appending command terminator"); + nextLine.Append(KImapTxtCrlf()); + + // Indicate that this is the final line to be sent + iAppendCommandSendState = EAppendSendingLastMessageData; + } + + // Send the data, using a non-modifyable descriptor. + iStream->SendDataReq(*iLineBufferForSend); + } + +/** +Overrides the default implementation of Cancel. +If the command is still calculating the message size, then the calculation is cancelled +*/ +void CImapAppend::Cancel() + { + __LOG_TEXT(iLogId, "CImapAppend::Cancel()"); // Overrides CImapCommand::Cancel() + + iBackgroundCalculationStepper->Cancel(); + + EnterFlushingState(); + } + +/** +Overrides the default implementation of CanCompleteFlushNow. +If the command has not yet sent any data, then it can be completed now +as it is not yet expecting to receive any response data. +@return EFalse if data has been sent. + ETrue if still at the message size calculation stage +*/ +TBool CImapAppend::CanCompleteFlushNow() const + { + // Any command that has completed should have been destroyed before this + // method is called. + __ASSERT_DEBUG(ParseState() != ECommandComplete, TImapServerPanic::ImapPanic(TImapServerPanic::ECommandInvalidParseState1)); + __ASSERT_DEBUG(Flushing(), TImapServerPanic::ImapPanic(TImapServerPanic::ECommandNotFlushing)); + + return (iAppendCommandSendState <= EAppendCalculatingSize) ? ETrue: EFalse; + }