diff -r 000000000000 -r 72b543305e3a email/imap4mtm/imaptransporthandler/src/csocketwriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/imap4mtm/imaptransporthandler/src/csocketwriter.cpp Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,412 @@ +// 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 "csocketwriter.h" + +#include "csocket.h" +#include "msocketcontroller.h" +#include "moutputstreamobserver.h" +#include "moutputstreamsecureobserver.h" +#include "imappaniccodes.h" +#include "imaptransportmanagercommon.h" +#include "cimaplogger.h" + +const TInt KMicroSecondsToSeconds = 1000000; +/** +The factory constructor. + +@param aSocket The connected socket. This owned by the observer. +@param aController The socket controller that owns the socket. +@return A pointer to a fully constructed object. +*/ +CSocketWriter* CSocketWriter::NewL(CSocket& aSocket, MSocketController& aController) + { + CSocketWriter* self = new (ELeave) CSocketWriter(aSocket, aController); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +void CSocketWriter::ConstructL() + { + iTimer = CImapObservableTimer::NewL(*this); + } + +/** +Destructor. +*/ +CSocketWriter::~CSocketWriter() + { + __ASSERT_DEBUG( iState == EIdle || iState == EClosed, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) ); + + // Cancel any outstanding requests + Cancel(); + delete iTimer; + } + +/** +Constructor. + +@param aSocket The connected socket. This owned by the observer. +@param aController The socket controller that owns the socket. +*/ +CSocketWriter::CSocketWriter(CSocket& aSocket, MSocketController& aController) + : CActive(CActive::EPriorityStandard), iSocket(aSocket), iController(aController) + { + CActiveScheduler::Add(this); + } + +/** +Notifies the output stream that the socket is closed. The output stream +observer is notified that the stream is closed. No more data can be sent to +the socket. + +@param aError The error code explaining why the socket has closed. A + value of KErrNone indicates that the input stream + observer requested that the socket be closed. +@pre None. +@post The output stream is in the Closed state. +*/ +void CSocketWriter::SocketClosed(TInt aError) + { + // Cancel any outstanding requests + Cancel(); + + // The socket has shutdown. Inform the output stream observer that the + // output stream is closed. + if( iObserver ) + iObserver->OutputStreamCloseInd(aError); + + // Move to the Closed state + iState = EClosed; + } + +/* + * Methods from MOutputStream + */ + +/** +@see MOutputStream +*/ +void CSocketWriter::Bind(MOutputStreamObserver& aObserver, TInt aLogId) + { + __ASSERT_DEBUG( iState == EIdle || iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) ); + + // Bind to the output stream observer + iObserver = &aObserver; + + iLogId = aLogId; + + // Move to the PendingSend state + iState = EPendingSend; + } + +void CSocketWriter::BindSecure(MOutputStreamSecureObserver& aObserver) + { + __ASSERT_DEBUG( iState == EIdle || iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) ); + + // Bind to the output stream observer + iSecureObserver = &aObserver; + } + +/** +@see MOutputStream +*/ +void CSocketWriter::SendDataReq(const TDesC8& aBuffer, TInt aIdleTime) + { + + if(iState == ESentData) + return; + + __ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) ); + __ASSERT_DEBUG( iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) ); + + __LOG_DATA_OUT(iLogId, aBuffer); + + // The output stream observer requests that the supplied buffer is sent to + // the socket. Request a write to the socket + iSocket.Send(aBuffer, iStatus); + + // Start the timer, send request must complete within aIdleTime, else there + // is a problem with the socket connection. + if(aIdleTime > 0) + { + iTimer->After(aIdleTime*KMicroSecondsToSeconds); + } +#if defined (_DEBUG) && defined (_LOGGING) + TBuf8 ip; + TUint16 remotePort; + TUint16 localPort; + iController.ConnectionInfo(ip, remotePort, localPort); + + __FLOG_4(_T8("Sending %d bytes on local port %d to %S, remote port %d"), aBuffer.Length(), localPort, &ip, remotePort); + __FLOG_0(_T8("----------")); + __FLOG_HEXDUMP(aBuffer.Ptr(), aBuffer.Length()); + __FLOG_0(_T8("----------")); +#endif + + // Move to the SentData and go active + iState = ESentData; + SetActive(); + } + +/** +@see MOutputStream +*/ +void CSocketWriter::SecureClientReq(const TDesC8& aSSLDomainName) + { + __ASSERT_DEBUG( iSecureObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBoundSecure) ); + __ASSERT_DEBUG( iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) ); + +#if defined (_DEBUG) && defined (_LOGGING) + TBuf8 ip; + TUint16 remotePort; + TUint16 localPort; + iController.ConnectionInfo(ip, remotePort, localPort); + + __FLOG_0(_T8("!! Upgrading to secure (client) connection")); + __FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort); +#endif + + // Notify the controller to suspend the input stream. + iController.StreamSuspended(MSocketController::EOutputStream); + + // Store the host name as required for secure certificate domain name + // checking. Move to the StartSecureHandshake state and self complete. + iSSLDomainName.Set(aSSLDomainName); + iState = EStartSecureHandshake; + CompleteSelf(); + } + +/** +@see MOutputStream +*/ +void CSocketWriter::Close() + { + __ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) ); + __ASSERT_DEBUG( iState != EClosing || iState != EClosed, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) ); + + // Cancel any outstanding requests + Cancel(); + +#if defined (_DEBUG) && defined (_LOGGING) + TBuf8 ip; + TUint16 remotePort; + TUint16 localPort; + iController.ConnectionInfo(ip, remotePort, localPort); + + __FLOG_0(_T8("!! Closing output stream")); + __FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort); +#endif + + // There is no need to do anything here - by informing the socket controller + // that the output stream is closed it will notify the input stream and then + // close the socket. + + // Move to the Closed state + iState = EClosed; + + // Inform the socket controller that the output stream is closed + iController.StreamClosed(KErrCancel, MSocketController::EOutputStream); + } + +/** +Completes this active object. +*/ +void CSocketWriter::CompleteSelf() + { + TRequestStatus* pStat = &iStatus; + User::RequestComplete(pStat, KErrNone); + SetActive(); + } + +/* + * Methods from CActive + */ + +/** +The request servicing function. Behaviour depends on the state of the output +stream. + +@leave The function will leave if the request status is an error. +*/ +void CSocketWriter::RunL() + { + // Leave if the socket reported an error + User::LeaveIfError(iStatus.Int()); + + switch( iState ) + { + case ESentData: + { + __ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) ); + // Send complete without error so cancel the idle timer. + iTimer->Cancel(); + + // The data has successfully been written to the socket - move to the + // PendingSend state. + iState = EPendingSend; + + // Inform the observer that the data has been sent. + iObserver->SendDataCnf(); + } break; + case EClosing: + { + __ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) ); + + // The socket has shutdown - move to the Closed state + iState = EClosed; + + // Inform the observer that the output stream is closed. + iObserver->OutputStreamCloseInd(KErrNone); + + // Inform the socket controller that the output stream is closed + iController.StreamClosed(KErrNone, MSocketController::EOutputStream); + } break; + case EStartSecureHandshake: + { + // Start the secure handshake + iState = ESecureHandshakeComplete; + iSocket.UpgradeToSecureL(iStatus, iSSLDomainName); + SetActive(); + } break; + case ESecureHandshakeComplete: + { + __ASSERT_DEBUG( iSecureObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBoundSecure) ); + + // Secure handshake has completed successfully so inform the observer. + iState = EPendingSend; + + iSecureObserver->SecureClientCnf(KErrNone); + +#if defined (_DEBUG) && defined (_LOGGING) + TBuf8 ip; + TUint16 remotePort; + TUint16 localPort; + iController.ConnectionInfo(ip, remotePort, localPort); + + __FLOG_0(_T8("!! Upgrade to secure (client) connection successful")); + __FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort); +#endif + + // Notify the controller to resume the input stream. + iController.StreamResumed(MSocketController::EOutputStream); + } break; + case EPendingSend: + case EClosed: + case EIdle: + default: + TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState); + break; + } + } + +/** +The asynchronous request cancel function. If the output stream has requested +a write to the socket (ie it is in the SentData state) this function cancels +the write request. +*/ +void CSocketWriter::DoCancel() + { + iTimer->Cancel(); + if( iState == ESentData ) + { + // There is a pending write request - cancel it + iSocket.CancelSend(); + } + else if( iState == ESecureHandshakeComplete ) + { + // Cancel the upgrade to secure connection. + iSocket.CancelUpgradeToSecure(); + } + } + +/** +The error handler for when RunL() leaves. If this has been called then the +write request or the socket shutdown request has failed. The socket can no +longer be used. The output stream observer is notified that the stream is +closed. No more data can be sent to the socket. + +@return A value of KErrNone indicating that the the error has been + handled. +*/ +TInt CSocketWriter::RunError(TInt aError) + { + CloseStreams(aError); + return KErrNone; + } +#ifdef _DEBUG +void CSocketWriter::OnTimerL(const CImapObservableTimer& aSourceTimer) +#else +void CSocketWriter::OnTimerL(const CImapObservableTimer& /*aSourceTimer*/) +#endif + { + __ASSERT_DEBUG(&aSourceTimer == iTimer, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionUnknownTimer)); + Cancel(); + CloseStreams(KErrTimedOut); + } + + +void CSocketWriter::CloseStreams(TInt aError) + { +#if defined (_DEBUG) && defined (_LOGGING) + TBuf8 ip; + TUint16 remotePort; + TUint16 localPort; + iController.ConnectionInfo(ip, remotePort, localPort); + + __FLOG_1(_T8("!! Output stream error : %d"), aError); + __FLOG_3(_T8("-> Connection on local port %d with %S, remote port %d closed"), localPort, &ip, remotePort); +#endif + + if (iState == ESecureHandshakeComplete) + { + __ASSERT_DEBUG( iSecureObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBoundSecure) ); + + // The request to upgrade to a secure socket has failed. Inform the output + // stream secure observer of the error. + + if (aError == KErrEof ) + { + aError = KErrDisconnected; + } + + iSecureObserver->SecureClientCnf(aError); + + // If an observer has bound, tell it that the stream is closed. + // It is possible that the secure socket will be setup before anyone tries + // to bind an output stream observer + if (iObserver != NULL) + { + iObserver->OutputStreamCloseInd(aError); + } + } + else + { + __ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) ); + + // The socket request has failed - the socket connection is broken. Need + // to inform the output stream observer that the output stream is closed. + + iObserver->OutputStreamCloseInd(aError); + } + + // Move to the Closed state + iState = EClosed; + + // Inform the socket controller that the output stream is closed + iController.StreamClosed(aError, MSocketController::EOutputStream); + }