email/imap4mtm/imaptransporthandler/src/csocketwriter.cpp
changeset 0 72b543305e3a
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "csocketwriter.h"
       
    17 
       
    18 #include "csocket.h"
       
    19 #include "msocketcontroller.h"
       
    20 #include "moutputstreamobserver.h"
       
    21 #include "moutputstreamsecureobserver.h"
       
    22 #include "imappaniccodes.h"
       
    23 #include "imaptransportmanagercommon.h"
       
    24 #include "cimaplogger.h"
       
    25 
       
    26 const TInt KMicroSecondsToSeconds = 1000000;
       
    27 /**
       
    28 The factory constructor.
       
    29 
       
    30 @param		aSocket		The connected socket. This owned by the observer.
       
    31 @param		aController	The socket controller that owns the socket.
       
    32 @return		A pointer to a fully constructed object.
       
    33 */
       
    34 CSocketWriter* CSocketWriter::NewL(CSocket& aSocket, MSocketController& aController)
       
    35 	{
       
    36 	CSocketWriter* self = new (ELeave) CSocketWriter(aSocket, aController);
       
    37 	CleanupStack::PushL(self);
       
    38 	self->ConstructL();
       
    39 	CleanupStack::Pop(self);
       
    40 	return self;
       
    41 	}
       
    42 	
       
    43 void CSocketWriter::ConstructL()
       
    44 	{
       
    45 	iTimer = CImapObservableTimer::NewL(*this);
       
    46 	}
       
    47 
       
    48 /**
       
    49 Destructor.
       
    50 */
       
    51 CSocketWriter::~CSocketWriter()
       
    52 	{
       
    53 	__ASSERT_DEBUG( iState == EIdle || iState == EClosed, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) );
       
    54 
       
    55 	// Cancel any outstanding requests
       
    56 	Cancel();
       
    57 	delete iTimer;
       
    58 	}
       
    59 
       
    60 /**
       
    61 Constructor.
       
    62 
       
    63 @param	aSocket		The connected socket. This owned by the observer.
       
    64 @param	aController	The socket controller that owns the socket.
       
    65 */
       
    66 CSocketWriter::CSocketWriter(CSocket& aSocket, MSocketController& aController)
       
    67 	: CActive(CActive::EPriorityStandard), iSocket(aSocket), iController(aController)
       
    68 	{
       
    69 	CActiveScheduler::Add(this);
       
    70 	}
       
    71 
       
    72 /**
       
    73 Notifies the output stream that the socket is closed. The output stream 
       
    74 observer is notified that the stream is closed. No more data can be sent to
       
    75 the socket.
       
    76 
       
    77 @param	aError	The error code explaining why the socket has closed. A
       
    78 				value of KErrNone indicates that the input stream 
       
    79 				observer requested that the socket be closed.
       
    80 @pre	None.
       
    81 @post	The output stream is in the Closed state.
       
    82 */
       
    83 void CSocketWriter::SocketClosed(TInt aError)
       
    84 	{
       
    85 	// Cancel any outstanding requests
       
    86 	Cancel();
       
    87 
       
    88 	// The socket has shutdown. Inform the output stream observer that the 
       
    89 	// output stream is closed.
       
    90 	if( iObserver )
       
    91 		iObserver->OutputStreamCloseInd(aError);
       
    92 
       
    93 	// Move to the Closed state
       
    94 	iState = EClosed;
       
    95 	}
       
    96 
       
    97 /*
       
    98  *	Methods from MOutputStream
       
    99  */
       
   100 
       
   101 /**
       
   102 @see MOutputStream
       
   103 */
       
   104 void CSocketWriter::Bind(MOutputStreamObserver& aObserver, TInt aLogId)
       
   105 	{
       
   106 	__ASSERT_DEBUG( iState == EIdle || iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) );
       
   107 
       
   108 	// Bind to the output stream observer
       
   109 	iObserver = &aObserver;
       
   110 
       
   111 	iLogId = aLogId;
       
   112 
       
   113 	// Move to the PendingSend state
       
   114 	iState = EPendingSend;
       
   115 	}
       
   116 
       
   117 void CSocketWriter::BindSecure(MOutputStreamSecureObserver& aObserver)
       
   118 	{
       
   119 	__ASSERT_DEBUG( iState == EIdle || iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) );
       
   120 
       
   121 	// Bind to the output stream observer
       
   122 	iSecureObserver = &aObserver;
       
   123 	}
       
   124 
       
   125 /**
       
   126 @see MOutputStream
       
   127 */
       
   128 void CSocketWriter::SendDataReq(const TDesC8& aBuffer, TInt aIdleTime)
       
   129 	{
       
   130 	
       
   131 	if(iState == ESentData)
       
   132 		return;
       
   133 
       
   134 	__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) );
       
   135 	__ASSERT_DEBUG( iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) );
       
   136 
       
   137 	__LOG_DATA_OUT(iLogId, aBuffer);
       
   138 
       
   139 	// The output stream observer requests that the supplied buffer is sent to
       
   140 	// the socket. Request a write to the socket
       
   141 	iSocket.Send(aBuffer, iStatus);
       
   142 
       
   143 	// Start the timer, send request must complete within aIdleTime, else there 
       
   144 	// is a problem with the socket connection.
       
   145 	if(aIdleTime > 0)
       
   146 		{
       
   147 		iTimer->After(aIdleTime*KMicroSecondsToSeconds);
       
   148 		}
       
   149 #if defined (_DEBUG) && defined (_LOGGING)
       
   150 	TBuf8<KIpv6MaxAddrSize> ip;
       
   151 	TUint16	remotePort;
       
   152 	TUint16 localPort;
       
   153 	iController.ConnectionInfo(ip, remotePort, localPort);
       
   154 
       
   155 	__FLOG_4(_T8("Sending %d bytes on local port %d to %S, remote port %d"), aBuffer.Length(), localPort, &ip, remotePort);
       
   156 	__FLOG_0(_T8("----------"));
       
   157 	__FLOG_HEXDUMP(aBuffer.Ptr(), aBuffer.Length());
       
   158 	__FLOG_0(_T8("----------"));
       
   159 #endif
       
   160 
       
   161 	// Move to the SentData and go active
       
   162 	iState = ESentData;
       
   163 	SetActive();
       
   164 	}
       
   165 
       
   166 /**
       
   167 @see MOutputStream
       
   168 */
       
   169 void CSocketWriter::SecureClientReq(const TDesC8& aSSLDomainName)
       
   170 	{
       
   171 	__ASSERT_DEBUG( iSecureObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBoundSecure) );
       
   172 	__ASSERT_DEBUG( iState == EPendingSend, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) );
       
   173 
       
   174 #if defined (_DEBUG) && defined (_LOGGING)
       
   175 	TBuf8<KIpv6MaxAddrSize> ip;
       
   176 	TUint16	remotePort;
       
   177 	TUint16 localPort;
       
   178 	iController.ConnectionInfo(ip, remotePort, localPort);
       
   179 
       
   180 	__FLOG_0(_T8("!! Upgrading to secure (client) connection"));
       
   181 	__FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort);
       
   182 #endif
       
   183 
       
   184 	// Notify the controller to suspend the input stream.
       
   185 	iController.StreamSuspended(MSocketController::EOutputStream);
       
   186 	
       
   187 	// Store the host name as required for secure certificate domain name 
       
   188 	// checking. Move to the StartSecureHandshake state and self complete.
       
   189 	iSSLDomainName.Set(aSSLDomainName);
       
   190 	iState = EStartSecureHandshake;
       
   191 	CompleteSelf();
       
   192 	}
       
   193 
       
   194 /**
       
   195 @see MOutputStream
       
   196 */
       
   197 void CSocketWriter::Close()
       
   198 	{
       
   199 	__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) );
       
   200 	__ASSERT_DEBUG( iState != EClosing || iState != EClosed, TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState) );
       
   201 
       
   202 	// Cancel any outstanding requests
       
   203 	Cancel();
       
   204 
       
   205 #if defined (_DEBUG) && defined (_LOGGING)
       
   206 	TBuf8<KIpv6MaxAddrSize> ip;
       
   207 	TUint16	remotePort;
       
   208 	TUint16 localPort;
       
   209 	iController.ConnectionInfo(ip, remotePort, localPort);
       
   210 
       
   211 	__FLOG_0(_T8("!! Closing output stream"));
       
   212 	__FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort);
       
   213 #endif
       
   214 
       
   215 	// There is no need to do anything here - by informing the socket controller
       
   216 	// that the output stream is closed it will notify the input stream and then
       
   217 	// close the socket.
       
   218 
       
   219 	// Move to the Closed state
       
   220 	iState = EClosed;
       
   221 
       
   222 	// Inform the socket controller that the output stream is closed
       
   223 	iController.StreamClosed(KErrCancel, MSocketController::EOutputStream);
       
   224 	}
       
   225 
       
   226 /**
       
   227 Completes this active object.
       
   228 */
       
   229 void CSocketWriter::CompleteSelf()
       
   230 	{
       
   231 	TRequestStatus* pStat = &iStatus;
       
   232 	User::RequestComplete(pStat, KErrNone);
       
   233 	SetActive();
       
   234 	}
       
   235 
       
   236 /*
       
   237  * Methods from CActive
       
   238  */
       
   239 
       
   240 /**
       
   241 The request servicing function. Behaviour depends on the state of the output
       
   242 stream.
       
   243 
       
   244 @leave	The function will leave if the request status is an error.
       
   245 */
       
   246 void CSocketWriter::RunL()
       
   247 	{
       
   248 	// Leave if the socket reported an error
       
   249 	User::LeaveIfError(iStatus.Int());
       
   250 
       
   251 	switch( iState )
       
   252 		{
       
   253 	case ESentData:
       
   254 		{
       
   255 		__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) );
       
   256 		// Send complete without error so cancel the idle timer.
       
   257 		iTimer->Cancel();
       
   258 
       
   259 		// The data has successfully been written to the socket - move to the 
       
   260 		// PendingSend state. 
       
   261 		iState = EPendingSend;
       
   262 
       
   263 		// Inform the observer that the data has been sent.
       
   264 		iObserver->SendDataCnf();
       
   265 		} break;
       
   266 	case EClosing:
       
   267 		{
       
   268 		__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) );
       
   269 
       
   270 		// The socket has shutdown - move to the Closed state
       
   271 		iState = EClosed;
       
   272 
       
   273 		// Inform the observer that the output stream is closed.
       
   274 		iObserver->OutputStreamCloseInd(KErrNone);
       
   275 
       
   276 		// Inform the socket controller that the output stream is closed
       
   277 		iController.StreamClosed(KErrNone, MSocketController::EOutputStream);
       
   278 		} break;
       
   279 	case EStartSecureHandshake:
       
   280 		{
       
   281 		// Start the secure handshake
       
   282 		iState = ESecureHandshakeComplete;
       
   283 		iSocket.UpgradeToSecureL(iStatus, iSSLDomainName);
       
   284 		SetActive();
       
   285 		} break;
       
   286 	case ESecureHandshakeComplete:
       
   287 		{
       
   288 		__ASSERT_DEBUG( iSecureObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBoundSecure) );
       
   289 
       
   290 		// Secure handshake has completed successfully so inform the observer.
       
   291 		iState = EPendingSend;
       
   292 
       
   293 		iSecureObserver->SecureClientCnf(KErrNone);
       
   294 
       
   295 #if defined (_DEBUG) && defined (_LOGGING)
       
   296 		TBuf8<KIpv6MaxAddrSize> ip;
       
   297 		TUint16	remotePort;
       
   298 		TUint16 localPort;
       
   299 		iController.ConnectionInfo(ip, remotePort, localPort);
       
   300 
       
   301 		__FLOG_0(_T8("!! Upgrade to secure (client) connection successful"));
       
   302 		__FLOG_3(_T8("-> on local port %d with %S, remote port %d"), localPort, &ip, remotePort);
       
   303 #endif
       
   304 
       
   305 		// Notify the controller to resume the input stream.
       
   306 		iController.StreamResumed(MSocketController::EOutputStream);
       
   307 		} break;
       
   308 	case EPendingSend:
       
   309 	case EClosed:
       
   310 	case EIdle:
       
   311 	default:
       
   312 		TImapServerPanic::ImapPanic(TImapServerPanic::EBadOutputStreamState);
       
   313 		break;
       
   314 		}
       
   315 	}
       
   316 
       
   317 /**
       
   318 The asynchronous request cancel function. If the output stream has requested
       
   319 a write to the socket (ie it is in the SentData state) this function cancels
       
   320 the write request.
       
   321 */
       
   322 void CSocketWriter::DoCancel()
       
   323 	{
       
   324 	iTimer->Cancel();
       
   325 	if( iState == ESentData )
       
   326 		{
       
   327 		// There is a pending write request - cancel it
       
   328 		iSocket.CancelSend();
       
   329 		}
       
   330 	else if( iState == ESecureHandshakeComplete )
       
   331 		{
       
   332 		// Cancel the upgrade to secure connection.
       
   333 		iSocket.CancelUpgradeToSecure();
       
   334 		}
       
   335 	}
       
   336 
       
   337 /**
       
   338 The error handler for when RunL() leaves. If this has been called then the 
       
   339 write request or the socket shutdown request has failed. The socket can no 
       
   340 longer be used. The output stream observer is notified that the stream is 
       
   341 closed. No more data can be sent to the socket.
       
   342 
       
   343 @return		A value of KErrNone indicating that the the error has been 
       
   344 			handled.
       
   345 */
       
   346 TInt CSocketWriter::RunError(TInt aError)
       
   347 	{
       
   348 	CloseStreams(aError);
       
   349 	return KErrNone;
       
   350 	}
       
   351 #ifdef _DEBUG
       
   352 void CSocketWriter::OnTimerL(const CImapObservableTimer& aSourceTimer)
       
   353 #else
       
   354 void CSocketWriter::OnTimerL(const CImapObservableTimer& /*aSourceTimer*/)
       
   355 #endif
       
   356 	{
       
   357 	__ASSERT_DEBUG(&aSourceTimer == iTimer, TImapServerPanic::ImapPanic(TImapServerPanic::ESessionUnknownTimer));
       
   358 	Cancel();
       
   359 	CloseStreams(KErrTimedOut);
       
   360 	}
       
   361 	
       
   362 	
       
   363 void CSocketWriter::CloseStreams(TInt aError)
       
   364 	{
       
   365 #if defined (_DEBUG) && defined (_LOGGING)
       
   366 	TBuf8<KIpv6MaxAddrSize> ip;
       
   367 	TUint16	remotePort;
       
   368 	TUint16 localPort;
       
   369 	iController.ConnectionInfo(ip, remotePort, localPort);
       
   370 
       
   371 	__FLOG_1(_T8("!! Output stream error : %d"), aError);
       
   372 	__FLOG_3(_T8("-> Connection on local port %d with %S, remote port %d closed"), localPort, &ip, remotePort);
       
   373 #endif
       
   374 
       
   375 	if (iState == ESecureHandshakeComplete)
       
   376 		{
       
   377 		__ASSERT_DEBUG( iSecureObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBoundSecure) );
       
   378 
       
   379 		// The request to upgrade to a secure socket has failed. Inform the output
       
   380 		// stream secure observer of the error.
       
   381 		
       
   382 		if (aError == KErrEof )
       
   383 			{
       
   384 			aError = KErrDisconnected;
       
   385 			}
       
   386 			
       
   387 		iSecureObserver->SecureClientCnf(aError);
       
   388 
       
   389 		// If an observer has bound, tell it that the stream is closed.
       
   390 		// It is possible that the secure socket will be setup before anyone tries
       
   391 		// to bind an output stream observer
       
   392 		if (iObserver != NULL)
       
   393 			{
       
   394 			iObserver->OutputStreamCloseInd(aError);
       
   395 			}
       
   396 		}
       
   397 	else
       
   398 		{
       
   399 		__ASSERT_DEBUG( iObserver != NULL, TImapServerPanic::ImapPanic(TImapServerPanic::EOutputStreamNotBound) );
       
   400 
       
   401 		// The socket request has failed - the socket connection is broken. Need
       
   402 		// to inform the output stream observer that the output stream is closed.
       
   403 
       
   404 		iObserver->OutputStreamCloseInd(aError);
       
   405 		}
       
   406 
       
   407 	// Move to the Closed state
       
   408 	iState = EClosed;
       
   409 
       
   410 	// Inform the socket controller that the output stream is closed
       
   411 	iController.StreamClosed(aError, MSocketController::EOutputStream);
       
   412 	}