email/imap4mtm/imaptransporthandler/src/csocketwriter.cpp
changeset 0 72b543305e3a
--- /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);
+	}