--- /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<KIpv6MaxAddrSize> 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<KIpv6MaxAddrSize> 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<KIpv6MaxAddrSize> 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<KIpv6MaxAddrSize> 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<KIpv6MaxAddrSize> 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);
+ }