email/imap4mtm/imaptransporthandler/src/csocketconnector.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 "csocketconnector.h"
       
    17 
       
    18 #include "csocket.h"
       
    19 #include "msocketconnectobserver.h"
       
    20 #include "msocketconnectorstore.h"
       
    21 #include "msocketcontrollerfactory.h"
       
    22 #include "mcommsinfoprovider.h"
       
    23 #include "csocketcontroller.h"
       
    24 #include "imappaniccodes.h"
       
    25 #include "imaptransportmanagercommon.h"
       
    26 
       
    27 #if defined (_DEBUG) && defined (_LOGGING)
       
    28 const TInt KHostNameSize	= 126;
       
    29 #endif
       
    30 
       
    31 /**
       
    32 The factory constructor.
       
    33 
       
    34 @param 	aStore 						The socket connector store.
       
    35 @param 	aSocketControllerFactory		The socket controller factory.
       
    36 @param 	aCommsInfoProvider			The comms info provider.
       
    37 @return	A pointer to a fully constructed object.	
       
    38 */
       
    39 CSocketConnector* CSocketConnector::NewL(MSocketConnectorStore&	aStore, 
       
    40                                          MSocketControllerFactory& aSocketControllerFactory, 
       
    41                                          MCommsInfoProvider& aCommsInfoProvider)
       
    42 	{
       
    43 	return new (ELeave) CSocketConnector(aStore, aSocketControllerFactory, aCommsInfoProvider);
       
    44 	}
       
    45 
       
    46 /**
       
    47 Destructor.
       
    48 */
       
    49 CSocketConnector::~CSocketConnector()
       
    50 	{
       
    51 	// Cancel any outstanding requests
       
    52 	Cancel();
       
    53 
       
    54 	// Cleanup...
       
    55 	delete iHost;
       
    56 	delete iConnectingSocket;
       
    57 	iHostResolver.Close();
       
    58 	}
       
    59 
       
    60 /**
       
    61 Constructor.
       
    62 
       
    63 @param	aStore						The socket connector store.
       
    64 @param	aSocketControllerFactory	The socket controller factory.
       
    65 @param	aCommsInfoProvider			The comms info provider.
       
    66 */
       
    67 CSocketConnector::CSocketConnector(MSocketConnectorStore& aStore,
       
    68 								   MSocketControllerFactory& aSocketControllerFactory,
       
    69 								   MCommsInfoProvider& aCommsInfoProvider) : CActive(CActive::EPriorityStandard), iStore(aStore),
       
    70 									   										 iSocketControllerFactory(aSocketControllerFactory),
       
    71 									   										 iCommsInfoProvider(aCommsInfoProvider)
       
    72 	{
       
    73 	CActiveScheduler::Add(this);
       
    74 	}
       
    75 
       
    76 /**
       
    77 Start connection to specified remote host. The socket connector starts 
       
    78 connecting to the specified remote host on the specified port.
       
    79 
       
    80 @param	aRemoteHost		The host name/IP address of the remote host
       
    81 @param	aRemotePort		The port number of the remote host
       
    82 @pre	The socket connector is in the Idle state.
       
    83 @post	The socket connector is not in the Idle state.
       
    84 */
       
    85 void CSocketConnector::ConnectL(MSocketConnectObserver& aObserver, const TDesC8& aRemoteHost, TUint16 aRemotePort)
       
    86 	{
       
    87 	__ASSERT_DEBUG( iState == EIdle, TImapServerPanic::ImapPanic(TImapServerPanic::EBadSocketConnectorState) );
       
    88 
       
    89 	// Set the observer
       
    90 	iObserver = &aObserver;
       
    91 
       
    92 	__ASSERT_DEBUG(!iHost, User::Invariant());
       
    93 
       
    94 	// Copy the remote host IP address and port
       
    95 	iHost = HBufC::NewL(aRemoteHost.Length());
       
    96 	iHost->Des().Copy(aRemoteHost);
       
    97 	iPort = aRemotePort;
       
    98 
       
    99 	// Move to the PendingDNSLookup state and self complete.
       
   100 	iState = EPendingDNSLookup;
       
   101 	CompleteSelf();
       
   102 	}
       
   103 
       
   104 /**
       
   105 Requests that the socket connector complete itself. This will caused the 
       
   106 RunL() to be called by the scheduler at the next opportunity.
       
   107 
       
   108 @pre	The socket connector object is not active.
       
   109 @post	The socket connector object is active and the request has been
       
   110 		completed.
       
   111 */
       
   112 void CSocketConnector::CompleteSelf()
       
   113 	{
       
   114 	TRequestStatus* pStat = &iStatus;
       
   115 	User::RequestComplete(pStat, KErrNone);
       
   116 	SetActive();
       
   117 	}
       
   118 
       
   119 /**
       
   120 Commit a suicide on the socket connector.
       
   121 */
       
   122 void CSocketConnector::Suicide()
       
   123 	{
       
   124 	// Inform store that connection is complete and then suicide.
       
   125 	iStore.ConnectionCompleted(*this);
       
   126 	delete this;
       
   127 	}
       
   128 
       
   129 /**
       
   130 Stop the connect.
       
   131 
       
   132 @see MSocketConnector
       
   133 */
       
   134 void CSocketConnector::StopConnect()
       
   135 	{
       
   136 	// Cancel any outstanding requests.
       
   137 	Cancel();
       
   138 
       
   139 #if defined (_DEBUG) && defined (_LOGGING)
       
   140 	TBuf8<KHostNameSize> host;
       
   141 	host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
       
   142 
       
   143 	__FLOG_0(_T8("!! Stopping connection"));
       
   144 	__FLOG_1(_T8("-> connect to host %S stopped"), &host);
       
   145 #endif
       
   146 
       
   147 	Suicide();
       
   148 	}
       
   149 
       
   150 /*
       
   151  *	Methods from CActive
       
   152  */
       
   153 
       
   154 /**
       
   155 The request servicing function. Behaviour depends on the state of the socket
       
   156 connector.
       
   157 
       
   158 @leave	The function will leave if the request status is an error.
       
   159 */
       
   160 void CSocketConnector::RunL()
       
   161 	{
       
   162 	// Leave if there has been an error
       
   163 	User::LeaveIfError(iStatus.Int());
       
   164 
       
   165 	switch( iState )
       
   166 		{
       
   167 	case EPendingDNSLookup:
       
   168 		{
       
   169 #if defined (_DEBUG) && defined (_LOGGING)
       
   170 		TBuf8<KHostNameSize> host;
       
   171 		host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
       
   172 
       
   173 		__FLOG_1(_T8("Doing DNS lookup -> searching for host %S"), &host);
       
   174 #endif
       
   175 
       
   176 		// Open the host resolver session
       
   177 		User::LeaveIfError(iHostResolver.Open(
       
   178 											 iCommsInfoProvider.SocketServer(),
       
   179 											 iCommsInfoProvider.ProtocolDescription().iAddrFamily, 
       
   180 											 KProtocolInetTcp,
       
   181 											 iCommsInfoProvider.Connection()
       
   182 											 ));
       
   183 		// Start the DNS lookup for the remote host name.
       
   184 		iHostResolver.GetByName(*iHost, iHostDnsEntry, iStatus);
       
   185 
       
   186 		// Move to the Connecting state and go active
       
   187 		iState = EConnecting;
       
   188 		SetActive();
       
   189 		} break;
       
   190 	case EConnecting:
       
   191 		{
       
   192 		// DNS lookup successful - form the internet address object
       
   193 		iAddress = TInetAddr(iHostDnsEntry().iAddr);
       
   194 		iAddress.SetPort(iPort);
       
   195 
       
   196 #if defined (_DEBUG) && defined (_LOGGING)
       
   197 		TBuf8<KHostNameSize> host;
       
   198 		host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
       
   199 
       
   200 		TBuf<KIpv6MaxAddrSize> ip16bit;
       
   201 		iAddress.Output(ip16bit);
       
   202 
       
   203 		TBuf8<KIpv6MaxAddrSize> ip;
       
   204 		ip.Copy(ip16bit);
       
   205 		
       
   206 		__FLOG_2(_T8("DNS lookup complete -> host %S has IP address %S"), &host, &ip);
       
   207 #endif
       
   208 
       
   209 		// Create the connecting socket
       
   210 		iConnectingSocket = CSocket::NewL(iCommsInfoProvider, CSocket::EProtocolSocket);
       
   211 
       
   212 		// Start connecting to the remote client
       
   213 		iConnectingSocket->Connect(iAddress, iStatus);
       
   214 
       
   215 		// Move to the Connected state and go active
       
   216 		iState = EConnected;
       
   217 		SetActive();
       
   218 		} break;
       
   219 	case EConnected:
       
   220 		{
       
   221 #if defined (_DEBUG) && defined (_LOGGING)
       
   222 		TBuf8<KHostNameSize> host;
       
   223 		host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
       
   224 
       
   225 		TInetAddr addr;
       
   226 		iConnectingSocket->RemoteName(addr);
       
   227 
       
   228 		TBuf<KIpv6MaxAddrSize> ip16bit;
       
   229 		addr.Output(ip16bit);
       
   230 
       
   231 		TBuf8<KIpv6MaxAddrSize> ip;
       
   232 		ip.Copy(ip16bit);
       
   233 
       
   234 		TInetAddr local;
       
   235 		iConnectingSocket->LocalName(local);
       
   236 
       
   237 		__FLOG_1(_T8("!! Connection with host %S established"), &host);
       
   238 		__FLOG_3(_T8("-> on local port %d with %S, remote port %d"), local.Port(), &ip, addr.Port());
       
   239 #endif
       
   240 
       
   241 		// A connection has been made with the remote client - lose ownership of
       
   242 		// the connecting socket.
       
   243 		CSocket* connectedSocket = iConnectingSocket;
       
   244 		iConnectingSocket = NULL;
       
   245 
       
   246 		// Create a socket controller object to own the connected socket.
       
   247 		CSocketController* socketController = iSocketControllerFactory.CreateSocketControllerLC(connectedSocket);
       
   248 
       
   249 		// Inform the socket connect observer that a TCP connection is established - 
       
   250 		// pass back the input and output stream objects.
       
   251 		iObserver->ConnectionMadeL(socketController->InputStream(), socketController->OutputStream());
       
   252 
       
   253 		// Remove socket controller from cleanup stack - transferring ownership
       
   254 		// to the store.
       
   255 		CleanupStack::Pop(socketController);
       
   256 
       
   257 		// Add the socket controller in the store - ownership is transferred to
       
   258 		// the store.
       
   259 		iSocketControllerFactory.AddToStoreL(socketController);
       
   260 
       
   261 		// Socket connector is finished - suicide.
       
   262 		Suicide();
       
   263 		} break;
       
   264 	case EIdle:
       
   265 	default:
       
   266 		TImapServerPanic::ImapPanic(TImapServerPanic::EBadSocketConnectorState);
       
   267 		break;
       
   268 		}
       
   269 	}
       
   270 
       
   271 /**
       
   272 The asynchronous request cancel.
       
   273 */
       
   274 void CSocketConnector::DoCancel()
       
   275 	{
       
   276 	// Check state
       
   277 	switch( iState )
       
   278 		{
       
   279 	case EConnecting:
       
   280 		{
       
   281 		// DNS lookup is pending - cancel
       
   282 		iHostResolver.Cancel();
       
   283 		} break;
       
   284 	case EConnected:
       
   285 		{
       
   286 		if( iConnectingSocket )
       
   287 			{
       
   288 			// Connection is pending - cancel and delete the socket
       
   289 			iConnectingSocket->CancelConnect();
       
   290 			delete iConnectingSocket;
       
   291 			iConnectingSocket = NULL;
       
   292 			}
       
   293 		} break;
       
   294 	case EIdle:
       
   295 	case EPendingDNSLookup:
       
   296 	default:
       
   297 		// Do nothing...
       
   298 		break;
       
   299 		}
       
   300 	}
       
   301 
       
   302 /**
       
   303 The error handler for when RunL() leaves. This function does any necessary
       
   304 cleanup. The socket connector is then set to suicide.
       
   305 
       
   306 @param	aError	The leave code.
       
   307 @return	A value of KErrNone.if the error has been handled, any other 
       
   308 		value if it has not been handled.
       
   309 @post	The socket connector is in the Suicide state.
       
   310 */
       
   311 TInt CSocketConnector::RunError(TInt aError)
       
   312 	{
       
   313 #if defined (_DEBUG) && defined (_LOGGING)
       
   314 	TBuf8<KHostNameSize> host;
       
   315 	host.Copy((*iHost).Left(KHostNameSize)); //just get the KHostNameSize characters
       
   316 
       
   317 	__FLOG_1(_T8("!! Error : %d"), aError);
       
   318 	__FLOG_1(_T8("-> connect to host %S failed"), &host);
       
   319 #endif
       
   320 
       
   321 	TInt errorToPropagate = aError;
       
   322 	TInt error = KErrNone;
       
   323 
       
   324 	switch( iState )
       
   325 		{
       
   326 	case EPendingDNSLookup:
       
   327 	case EConnecting:
       
   328 		{
       
   329 		// In EReadyForDNS or EReadyToConnect states, KErrNotReady may be 
       
   330 		// returned by the comms call that require a connection. The RConnection
       
   331 		// that is passed in MUST be started or this error will occur. If we own
       
   332 		// the RConnection we should start the RConnection again, else if our
       
   333 		// client own the RConnection, we just propagate the Comms error.
       
   334 		if( iCommsInfoProvider.OwnsConnection() && aError == KErrNotReady )
       
   335 			{
       
   336 			// We own the RConnection and the error is KErrNotReady
       
   337 			errorToPropagate = iCommsInfoProvider.Connection().Start();
       
   338 
       
   339 			if( errorToPropagate == KErrNone )
       
   340 				{
       
   341 				// RConnection started successfully, try to connect again
       
   342 				CompleteSelf();
       
   343 				return KErrNone;
       
   344 				}
       
   345 			}
       
   346 		// Opening the connecting socket has failed or the DNS lookup completed
       
   347 		// with an error code - suicide after notifying the observer - do nothing 
       
   348 		// except drop through to the Connected state case...
       
   349 		}
       
   350 	case EConnected:
       
   351 		{
       
   352 		// Either the socket controller factory left in AddToStoreL() or in
       
   353 		// CreateSocketControllerLC(), or the observer left in ConnectionMadeL()
       
   354 		// or the connect request completed with an error code. Inform the 
       
   355 		// observer of the error.
       
   356 		error = iObserver->HandleConnectError(errorToPropagate);
       
   357 		} break;
       
   358 	case EIdle:
       
   359 	default:
       
   360 		TImapServerPanic::ImapPanic(TImapServerPanic::EBadSocketConnectorState);
       
   361 		break;
       
   362 		}
       
   363 
       
   364 	Suicide();
       
   365 
       
   366 	return error;
       
   367 	}