email/imap4mtm/imaptransporthandler/src/csocketreader.cpp
changeset 0 72b543305e3a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/email/imap4mtm/imaptransporthandler/src/csocketreader.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,452 @@
+// 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 "csocketreader.h"
+
+#include "csocket.h"
+#include "msocketcontroller.h"
+#include "minputstreamobserver.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.
+*/
+CSocketReader* CSocketReader::NewL(CSocket& aSocket, MSocketController& aController)
+	{
+	 CSocketReader* self = new(ELeave) CSocketReader(aSocket, aController);
+	 CleanupStack::PushL(self);
+	 self->ConstructL();
+	 CleanupStack::Pop(self);
+	 return self;
+	}
+
+void CSocketReader::ConstructL()
+	{
+	iTimer = CImapObservableTimer::NewL(*this);
+	}	
+/**
+Destructor.
+*/
+CSocketReader::~CSocketReader()
+	{
+	__ASSERT_DEBUG( iObserver == NULL || iState == EClosed, TImapServerPanic::ImapPanic(TImapServerPanic::EBadInputStreamState) );
+
+	// 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.
+*/
+CSocketReader::CSocketReader(CSocket& aSocket, MSocketController& aController)
+	: CActive(CActive::EPriorityStandard), iSocket(aSocket), iController(aController)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+/**
+Requests that the input stream complete itself. This will caused the RunL()
+to be called by the scheduler at the next opportunity.
+
+@pre	The input stream is not active.
+@post	The input stream object is active and the request has been
+		completed.
+*/
+void CSocketReader::CompleteSelf()
+	{
+	__LOG_TEXT(iLogId, "CSocketReader::CompleteSelf");
+	TRequestStatus* pStat = &iStatus;
+	User::RequestComplete(pStat, KErrNone);
+	SetActive();
+	}
+
+/**
+Notifies the input stream that the socket is closed. The input stream 
+observer is notified that the stream is closed. No more data can be received
+from the socket.
+
+@param	aError	The error code explaining why the socket has closed. A
+				value of KErrNone indicates that the output stream 
+				observer requested that the socket be closed.
+@pre	None.
+@post	The input stream is in the Closed state.
+*/
+void CSocketReader::SocketClosed(TInt aError)
+	{
+	// Cancel any outstanding requests
+	Cancel();
+
+	// The socket has shutdown. Inform the input stream observer that the input
+	// stream is closed.
+	if( iObserver )
+		{
+		iObserver->InputStreamCloseInd(aError);
+		}		
+
+	// Move to the Closed state
+	SetState(EClosed);
+	}
+
+/**
+Notifies the input stream that the socket is to be suspended.
+*/
+void CSocketReader::Suspend()
+	{
+	__ASSERT_DEBUG( !iSuspended, TImapServerPanic::ImapPanic(TImapServerPanic::EInputStreamAlreadySuspended) );
+
+	// 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("!! Suspending input stream"));
+	__FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort);
+#endif
+
+	// Check to see a read requests had been issued.
+	if( iState == EReceivedData )
+		{
+		// Yep - change to the Read state so that read request is re-issued when
+		// the stream resumes.
+		SetState(ERead);
+		}
+
+	// Stream is now suspended.
+	iSuspended = ETrue;
+	}
+
+/**
+Notifies the input stream that the socket is to be resumed.
+*/
+void CSocketReader::Resume()
+	{
+	__ASSERT_DEBUG( iSuspended, TImapServerPanic::ImapPanic(TImapServerPanic::EInputStreamNotSuspended) );
+
+#if defined (_DEBUG) && defined (_LOGGING)
+	TBuf8<KIpv6MaxAddrSize> ip;
+	TUint16	remotePort;
+	TUint16 localPort;
+	iController.ConnectionInfo(ip, remotePort, localPort);
+
+	__FLOG_0(_T8("!! Resumimng input stream"));
+	__FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort);
+#endif
+
+	// Check to see a read requests needs to be issued.
+	if( iState == ERead )
+		{
+		if (!iBufferDataDelivered && iBuffer.Length() > 0)
+			{
+			// We have undelivered data in the buffer (probably caused by the Cancel() in Suspend())
+			// Deliver it now - rather than requesting more.
+			SetState(EReceivedData);
+			}		
+		
+		// Self complete to get the read request issued or to deliver the undelivered data.
+		CompleteSelf();
+		}
+
+	// Stream is no longer suspended.
+	iSuspended = EFalse;
+	}
+
+/**
+@see MInputStream
+*/
+void CSocketReader::Bind(MInputStreamObserver& aObserver, TInt aLogId)
+	{
+	__ASSERT_DEBUG( iState == EIdle, TImapServerPanic::ImapPanic(TImapServerPanic::EBadInputStreamState) );
+
+	// Bind to the input stream observer
+	iObserver = &aObserver;
+	
+	iLogId = aLogId;
+	}
+
+/**
+@see MInputStream
+*/
+void CSocketReader::ReadReq(TInt aIdleTime)
+	{
+	// Do we have an observer to report the incoming data to?
+	__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EInputStreamNotBound) );
+	// Are we already reading?
+	__ASSERT_DEBUG( iState == EIdle, TImapServerPanic::ImapPanic(TImapServerPanic::EBadInputStreamState) );
+	if (aIdleTime > 0)
+		{
+		iIdleTime = aIdleTime;
+		}
+	
+	// The input stream observer has requested some data.
+	// Check whether the data in the buffer has already been delivered to an observer
+	if (iBufferDataDelivered)
+		{
+		// We don't want to deliver it twice, so zero the buffer.
+		iBuffer.Zero();
+		}
+
+	if (iSuspended)
+		{
+		// Set the start to read so that data will be requested/delivered when Resume() is called.
+		SetState(ERead);
+		}
+	else
+		{
+		// If we still have data left to deliver, then deliver it now.
+		// This can happen after a CancelRecv(), as the buffer will have been populated with data received 
+		// at the time of cancel, but will not yet have been delivered.
+		if (iBuffer.Length() > 0)
+			{
+			__LOG_TEXT(iLogId, "CSocketReader - Delivering data received prior to cancel");
+			SetState(EReceivedData);
+			}
+		// Otherwise read some more from the socket
+		else
+			{
+			SetState(ERead);
+			}
+			
+		CompleteSelf();
+		}	
+	}
+	
+/**
+@see MInputStream
+*/
+TBool CSocketReader::IsReading()
+	{
+	return (iState == ERead || iState == EReceivedData);
+	}
+
+/**
+@see MInputStream
+*/
+void CSocketReader::CancelReadReq()
+	{
+	__ASSERT_DEBUG( iState == EIdle || iState == ERead || iState == EReceivedData, TImapServerPanic::ImapPanic(TImapServerPanic::EBadInputStreamState) );
+	
+	iTimer->Cancel();
+	Cancel();	
+	
+	SetState(EIdle);
+	}
+
+/*
+ *	Methods from CActive
+ */
+
+/**
+The request servicing function. Behaviour depends on the state of the input
+stream.
+
+@leave	The function will leave if the request status is an error.
+*/
+void CSocketReader::RunL()
+	{
+	__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EInputStreamNotBound) );
+
+	__LOG_FORMAT((iLogId, "CSocketReader::RunL - State %d, Status %d", iState, iStatus.Int()));
+
+	iTimer->Cancel();
+	// Leave if the socket reported an error
+	User::LeaveIfError(iStatus.Int());
+
+	switch( iState )
+		{
+	case ERead:
+		{
+		// Request a read from the socket
+		__ASSERT_DEBUG( iBuffer.Length() == 0, TImapServerPanic::ImapPanic(TImapServerPanic::EInputStreamBufferNotEmpty) );
+		iBufferDataDelivered = EFalse;
+		iSocket.RecvOneOrMore(iBuffer, iStatus);
+
+		// Move to the Received state and go active - wait for read to complete
+		SetState(EReceivedData);
+		SetActive();		
+		if(iIdleTime > 0)
+			{
+			// The recieve from socket should complete in iIdleTime seconds.
+			iTimer->After(iIdleTime*KMicroSecondsToSeconds);
+			iIdleTime = 0;
+			}
+		} break;
+	case EReceivedData:
+		{
+		// Received data - move to the PendingAck state and wait for observer to 
+		// notify that it has finished with the buffer.
+		SetState(EIdle);
+
+		__LOG_DATA_IN(iLogId, iBuffer);
+
+		// Inform the observer of the received data.
+		iBufferDataDelivered = ETrue;
+		iObserver->ReceivedDataIndL(iBuffer);
+
+#if defined (_DEBUG) && defined (_LOGGING)
+		TBuf8<KIpv6MaxAddrSize> ip;
+		TUint16	remotePort;
+		TUint16 localPort;
+		iController.ConnectionInfo(ip, remotePort, localPort);
+	
+		__FLOG_4(_T8("Received %d bytes on local port %d from %S, remote port %d"), iBuffer.Length(), localPort, &ip, remotePort);
+		__FLOG_0(_T8("----------"));
+		__FLOG_HEXDUMP(iBuffer.Ptr(), iBuffer.Length());
+		__FLOG_0(_T8("----------"));
+#endif
+		} break;
+	case EClosing:
+		{
+		// The socket has shutdown - move to the Closed state
+		SetState(EClosed);
+
+		// Inform the observer that the input stream is closed.
+		iObserver->InputStreamCloseInd(KErrNone);
+
+		// Inform the socket controller that the input stream is now shut
+		iController.StreamClosed(KErrNone, MSocketController::EInputStream);
+		} break;
+	case EClosed:
+	case EIdle:
+	default:
+		TImapServerPanic::ImapPanic(TImapServerPanic::EBadInputStreamError);
+		break;
+		}
+	}
+
+/**
+The asynchronous request cancel function. If the input stream has requested a 
+read from the socket (ie it is in the ReceivedData state) it cancels the read
+request.
+*/	
+void CSocketReader::DoCancel()
+	{
+	iTimer->Cancel();
+	if( iState == EReceivedData )
+		{
+		// There is a pending read request - cancel it
+		iSocket.CancelRecv();
+		}
+	}
+
+/**
+The asynchronous request error handler. If this has been called then the 
+read request or the socket shutdown request has failed. The socket can no 
+longer be used. The input stream observer is notified that the stream is 
+closed. No more data can be received from the socket.
+*/	
+TInt CSocketReader::RunError(TInt aError)
+	{
+	CloseStreams(aError);
+
+	return KErrNone;
+	}
+
+void CSocketReader::SetState(TInputState aInputState)
+	{
+#ifdef __CSOCKETREADER_VERBOSE_LOGGING
+#ifdef __IMAP_LOGGING
+
+	_LIT8(KTxtInputStateIdle, "EIdle");
+	_LIT8(KTxtInputStateRead, "ERead");
+	_LIT8(KTxtInputStateReceivedData, "EReceivedData");
+	_LIT8(KTxtInputStateClosing, "EClosing");
+	_LIT8(KTxtInputStateClosed, "EClosed");
+	_LIT8(KTxtInputStateUnknown, "Unknown");
+				
+	TPtrC8 ptrOldState(KTxtInputStateUnknown);
+	TPtrC8 ptrNewState(KTxtInputStateUnknown);
+	
+	switch(iState)
+		{
+		case EIdle: 		ptrOldState.Set(KTxtInputStateIdle); 			break;
+		case ERead:			ptrOldState.Set(KTxtInputStateRead); 			break;
+		case EReceivedData:	ptrOldState.Set(KTxtInputStateReceivedData);	break;
+		case EClosing:		ptrOldState.Set(KTxtInputStateClosing); 		break;
+		case EClosed:		ptrOldState.Set(KTxtInputStateClosed); 			break;
+		}
+		
+	switch(aInputState)
+		{
+		case EIdle: 		ptrNewState.Set(KTxtInputStateIdle); 			break;
+		case ERead:			ptrNewState.Set(KTxtInputStateRead); 			break;
+		case EReceivedData:	ptrNewState.Set(KTxtInputStateReceivedData);	break;
+		case EClosing:		ptrNewState.Set(KTxtInputStateClosing); 		break;
+		case EClosed:		ptrNewState.Set(KTxtInputStateClosed); 			break;
+		}
+	
+	_LIT8(KLogFormat, "CSocketReader::iState %S ==> %S");
+	__LOG_FORMAT((iLogId, KLogFormat, &ptrOldState, &ptrNewState));
+	
+#endif //__IMAP_LOGGING
+#endif //__CSOCKETREADER_VERBOSE_LOGGING
+	
+	iState = aInputState;
+	}
+void CSocketReader::CloseStreams(TInt aError)
+	{
+	__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EInputStreamNotBound) );
+
+#if defined (_DEBUG) && defined (_LOGGING)
+	TBuf8<KIpv6MaxAddrSize> ip;
+	TUint16	remotePort;
+	TUint16 localPort;
+	iController.ConnectionInfo(ip, remotePort, localPort);
+
+	__FLOG_1(_T8("!! Input stream error : %d"), aError);
+	__FLOG_3(_T8("-> Connection on local port %d with %S, remote port %d closed"), localPort, &ip, remotePort);
+#endif
+
+	// Move to the Closed state
+	SetState(EClosed);
+
+	// The socket request has failed - the socket connection is broken. Need
+	// to inform the input stream observer that the input stream is closed.
+	
+	if (aError == KErrAbort || aError == KErrEof )
+		{
+		aError = KErrDisconnected;
+		}
+	iObserver->InputStreamCloseInd(aError);
+
+	// Inform the socket controller that the input stream is closed
+	iController.StreamClosed(aError, MSocketController::EInputStream);
+	}
+
+
+#ifdef _DEBUG
+void CSocketReader::OnTimerL(const CImapObservableTimer& aSourceTimer)
+#else
+void CSocketReader::OnTimerL(const CImapObservableTimer& /*aSourceTimer*/)
+#endif
+	{
+	__ASSERT_DEBUG(&aSourceTimer == iTimer, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionUnknownTimer));
+	Cancel();
+	CloseStreams(KErrTimedOut);
+	}