diff -r 000000000000 -r 72b543305e3a email/pop3andsmtpmtm/smtpservermtm/src/IMSMSEND.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/pop3andsmtpmtm/smtpservermtm/src/IMSMSEND.CPP Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,1143 @@ +// Copyright (c) 1998-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: +// Implementation file for class CImStmpSession -- representing +// a connection to a remote SMTP server. +// +// + +#include // TImMessageField +#include // CImSendConvert +#include +#include + +#include "SMTS.H" +#include "IMSM.H" // CImSmtpSession +#include "IMSMSEND.H" // CImSmtpFile +#include "SMTSUTIL.H" // forward declarations for utility fns +#include +#include "mobilitytestmtmapi.h" + +const TInt KSmtpAuthCodeSuccessful=235; +const TInt KSmtpAuthCodeFailed=535; +const TInt KSmtpUnableToAuthAtPresent=435; +const TInt KSmtpAuthCodeReadyResponse=334; +const TInt KSmtpBadParameter=501; + +/** +Factory constructor + +@param aServerEntry Server entry +@param aSettings SMTP settings +@param aServ Socket server +@param aConnect Network connection +@return Constructed class +*/ +CImSmtpSession* CImSmtpSession::NewL(CMsvServerEntry& aServerEntry, CSmtpSettings& aSettings, RSocketServ& aServ, CImConnect& aConnect, TMsvId aServiceId) + { + CImSmtpSession* self = new (ELeave) CImSmtpSession(aServerEntry, aSettings, aServiceId); + CleanupStack::PushL(self); + self->ConstructL(aServ, aConnect); + CleanupStack::Pop(self); + return self; + } + +/** +Connect the session to the server + +@param aClientStatus Client status to be completed +*/ +void CImSmtpSession::ConnectL(TRequestStatus& aClientStatus) + { + __ASSERT_DEBUG(!iSocketIsConnected, gPanic(EImsmSocketAlreadyConnected)); + + if (!iSocketIsConnected) + { + if(iSettings.SecureSockets()) + iThisSession = ESecureSession; // if the user has chosen to use TLS then we have to use ESMTP+TLS + else if(iSettings.SMTPAuth()) + iThisSession = EEnhancedSmtpSession; // if the user has chosen to use AUTH, when we have to use ESMTP + else + iThisSession = ESmtpSession; // use SMTP session for now + + iCompleted = KErrNone; + iState = EConnectingToSmtp; // Initialise to 1st state of state machine + DoStateL(); // Here we go... + Queue(aClientStatus); + } + else + { + // Nothing to do so just complete the caller + RequestComplete(aClientStatus, KErrNone); + } + } + +/** +Send all the emails to the server + +@param aSendFiles Files to send +@param aClientStatus Client status to be completed +*/ +void CImSmtpSession::SendFilesL(CMsgImOutboxSend& aSendFiles, TRequestStatus& aClientStatus) + { + __ASSERT_DEBUG(iSocketIsConnected, gPanic(EImsmSocketNotConnected1)); + + TInt error = aSendFiles.NextFile(); + if (iSocketIsConnected && error == KErrNone) + { + iSendFiles = &aSendFiles; + iState = ESendingImail; + DoStateL(); + Queue(aClientStatus); + } + else if(error == KErrNotFound) + { + // Nothing to do so just complete the caller + RequestComplete(aClientStatus, KErrNotFound); + } + else + { + // Nothing to do so just complete the caller + RequestComplete(aClientStatus, KErrNone); + } + } + +/** +Quit from the server + +@param aClientStatus Client status to be completed +*/ +void CImSmtpSession::QuitL(TRequestStatus& aClientStatus) + { + __ASSERT_DEBUG(iSocketIsConnected, gPanic(EImsmSocketNotConnected2)); + + if (iSocketIsConnected) + { + iState = EClosingSmtp; + DoStateL(); + Queue(aClientStatus); + } + else + { + // Nothing to do so just complete the caller + RequestComplete(aClientStatus, KErrNone); + } + } + +/** +Constructor + +@param aServerEntry Server entry +@param aSettings SMTP settings +*/ +CImSmtpSession::CImSmtpSession(CMsvServerEntry& aServerEntry, CSmtpSettings& aSettings, TMsvId aServiceId) + : CMsgActive(KImSmtpSessionPriority), + iServerEntry(aServerEntry), + iSettings(aSettings), + iServiceId(aServiceId), + iCurrentAuthProfile(CSmtpAuthMechanismHelper::EUndefined) // always start with undefined + { + __DECLARE_NAME(_S("CImSmtpSession")); + } + +/** +Second phase constructor + +@param aServ Socket server +@param aConnect Network connection +*/ +void CImSmtpSession::ConstructL(RSocketServ& aServ, CImConnect& aConnect) + { + iSocket = CImTextServerSession::NewL(aServ, aConnect); + //if log fails, ignore the error and continue. + TRAP_IGNORE(iLogMessage = CImLogMessage::NewL(iServerEntry.FileSession())); + CActiveScheduler::Add(this); // Add SmtpSession to scheduler's queue + } + +/** +Destructor +*/ +CImSmtpSession::~CImSmtpSession() + { + Cancel(); + if (iSocketIsConnected) + { + iSocket->Disconnect(); + } + delete iSmtpFile; // should be dead already + delete iSocket; + delete iSmtpAuthHelper; + delete iLogMessage; + } + +// +// CreateFormattedAddressL() +// +// Creates a new HDesC* containing the concateneatd alias and address strings +// +HBufC* CImSmtpSession::CreateFormattedAddressLC(const TDesC& aString, const TDesC& aAlias) + { + TImMessageField msgField; + + // Make a buffer.. + TInt strLen = aString.Length(); + TInt aliasLen = aAlias.Length(); + + HBufC* outString = HBufC::NewLC(strLen+aliasLen+5); + HBufC* buffer = HBufC::NewL(strLen); + + *buffer = msgField.GetValidInternetEmailAddressFromString(aString); + + if (aliasLen) + { + outString->Des().Format(KSmtpEmailAddressAndAliasFmtStr,&aAlias,buffer); + } + else + { + *outString = *buffer; // just copy email address; as there's no alias to be added + } + + delete buffer; + return outString; + } + + +TImImailFileProgress CImSmtpSession::FileProgress() + { + // Return info about how much of message has been sent so far + TImImailFileProgress progress; + + if (iSmtpFile && (iState==ESendingImail)) + { + // get progress from CImSmtpFile object as it is doing the work at the moment + iSmtpFile->GetProgress(progress); + + // If there is more than 1 message being sent(ie when sending to BCC's), then + // update the Progress to take account of the Total Num of Messages sent + if (iNumMsgsToSend > 1) + { + // Calc the Total number of Bytes to send + TInt fileByteSize = iSmtpFile->BytesToSend(); + TInt bytesSentBefore = (iNumMsgsSent-1)*fileByteSize; // iNumMsgsSent-1, as this msg is not yet complete + TInt totalBytesSent = bytesSentBefore + progress.iBytesSent; + + // Update the Progress + progress.iBytesToSend = iTotalBytesToSend; + progress.iBytesSent = totalBytesSent; + } + } + else + { + progress.iSessionState = EClosingSmtp; + progress.iBytesSent = 0; + progress.iBytesToSend = 0; + } + return progress; + } + + +void CImSmtpSession::SendFileL() + { + // Start the 'file mailer' object + __ASSERT_DEBUG(!iSmtpFile,gPanic(EImsmSmtpFileObjectAlreadyExists)); + // log the info of the message to be sent + if (iLogMessage) + iLogMessage->Reset(); + // Update the header info for this message.. + CImHeader* header = CImHeader::NewLC(); + CMsvStore* store = iServerEntry.EditStoreL(); + CleanupStack::PushL(store); + header->RestoreL(*store); + + TBool isBccRcpt = EFalse; + CDesCArray* rcptArray = NULL; + TBool sendBccNow = iToRcptHeaderUpdated; + + if(!iToRcptHeaderUpdated) + { + // Set the new info... + //set from address + HBufC* formatedAddress = CreateFormattedAddressLC(iSettings.EmailAddress(), iSettings.EmailAlias());//Format the address fields + header->SetFromL(*formatedAddress); + CleanupStack::PopAndDestroy(formatedAddress); + formatedAddress = NULL; + + // set ReceiptAddress if one exists in the settings + if( header->ReceiptAddress().Length() == 0 && iSettings.ReceiptAddress().Length() > 0 ) + { + formatedAddress=CreateFormattedAddressLC(iSettings.ReceiptAddress(),iSettings.EmailAlias());//Format the address fields + header->SetReceiptAddressL(*formatedAddress); + CleanupStack::PopAndDestroy(formatedAddress); + formatedAddress = NULL; + } + + if(header->ToRecipients().Count()==0 && header->CcRecipients().Count()==0) + { + sendBccNow=ETrue; + } + + // ReplyToAddress + if ((header->ReplyTo().Length() == 0 || header->ReplyTo() == iSettings.ReplyToAddress()) && + (iSettings.ReplyToAddress().Length() > 0)) + { + formatedAddress= CreateFormattedAddressLC(iSettings.ReplyToAddress(),iSettings.EmailAlias());//Format the address fields + header->SetReplyToL(*formatedAddress); + CleanupStack::PopAndDestroy(formatedAddress); + formatedAddress = NULL; + } + + //make a copy of 'Bcc' recipients then remove from Msg header + rcptArray= &(header->BccRecipients()); + if (iSettings.SendCopyToSelf()==ESendCopyAsBccRecipient) + { + if ( header->ReceiptAddress().Length() ) + rcptArray->AppendL(header->ReceiptAddress()); + else if ( header->ReplyTo().Length() ) + rcptArray->AppendL(header->ReplyTo()); + } + + if(rcptArray->Count() > 0) + { + iBccRcptFound=ETrue; // 'Bcc' recipients exists + iBccRcptIndex=0; //reset the counter + iFinishedWithBccRcpts=EFalse; + + iSendFiles->ResetBccRcptArrayL(); + + TInt numberRcpts = rcptArray->Count(); + if(numberRcpts) + { + iSendFiles->BccRcptArray().AppendL((*rcptArray)[0]); + CDesCArray& tempArr = iSendFiles->BccRcptArray(); + for(TInt counter=1; counter 0) + iSendFiles->BccRcptArray().AppendL((*rcptArray)[counter]); + } + } + rcptArray->Reset(); + } + + iToRcptHeaderUpdated=ETrue; + isBccRcpt=EFalse; + } + + // Reset the number of Sent Messages, if all previous messages have been sent + if (iNumMsgsSent >= iNumMsgsToSend) + { + iNumMsgsSent = 0; + } + + // Calc the num of messages we're sending. If there is a To or CC, + // recipient then there will be a message sent + if(header->ToRecipients().Count()>0 || header->CcRecipients().Count()>0) + iNumMsgsToSend = 1; + else + iNumMsgsToSend = 0; + + // If there are BCC recipients, then we will be sending a message to each + if (iBccRcptFound) + iNumMsgsToSend += iSendFiles->BccRcptArray().Count(); + + // Make sure there are recipients to send to. + __ASSERT_ALWAYS(iNumMsgsToSend,gPanic(EImsmZeroRecipientsInMessage)); + + // sending Msg to 'Bcc' recipients so update header + if (sendBccNow && iBccRcptFound) + { + rcptArray= &(header->BccRecipients()); + rcptArray->Reset(); + header->BccRecipients().AppendL(iSendFiles->BccRcptArray()[iBccRcptIndex]); + + ++iBccRcptIndex;//for the next recipient + if(iBccRcptIndex < iSendFiles->BccRcptArray().Count()) + iFinishedWithBccRcpts=EFalse; + else + iFinishedWithBccRcpts=ETrue; + + isBccRcpt=ETrue; + } + + header->StoreL(*store); + store->CommitL(); + if (iLogMessage) + { + iLogMessage->LogEvent().SetEventType(KLogMailEventTypeUid); + if ((header->ToRecipients().Count() + + header->CcRecipients().Count() + + header->BccRecipients().Count()) > 1) + // If there are multiple recipients then set the recipient to 'multiple' + { + TBuf multipleRecipientsString; + iLogMessage->GetString(multipleRecipientsString, R_LOG_REMOTE_MULTIPLE); + iLogMessage->LogEvent().SetRemoteParty(multipleRecipientsString); + } + else + // If there is only one recipient then set the recipient name + { + if (header->ToRecipients().Count()) + iLogMessage->LogEvent().SetRemoteParty(header->ToRecipients()[0]); + + else if (header->CcRecipients().Count()) + iLogMessage->LogEvent().SetRemoteParty(header->CcRecipients()[0]); + + else if (header->BccRecipients().Count()) + iLogMessage->LogEvent().SetRemoteParty(header->BccRecipients()[0]); + } + + iLogMessage->LogEvent().SetSubject(header->Subject()); + + TBuf outString; + iLogMessage->GetString(outString, R_LOG_DIR_OUT); + iLogMessage->LogEvent().SetDirection(outString); + + iLogMessage->LogEvent().SetLink(iServerEntry.Entry().Id()); + } + + CleanupStack::PopAndDestroy(2, header); + + iTimeNow.UniversalTime(); // Set time/date of sending this message file + if (iLogMessage) + iLogMessage->LogEvent().SetTime(iTimeNow); + TRAPD(error,iSmtpFile = CImSmtpFile::NewL( *iSocket, + iServerEntry, + iTimeNow, + iSmtpBuffer, + iSettings, + isBccRcpt)); // Create the file mailing object + + if(error == KErrNone) + { + // If this is the first message to send, then calc the total bytes to send + if (iNumMsgsSent == 0) + { + iTotalBytesToSend = iSmtpFile->TotalMsgSizeL() * iNumMsgsToSend; + } + + // Tell the file how many bytes it will be sending. This is just a + // rough estimate for progress + iSmtpFile->SetBytesToSend(iTotalBytesToSend/iNumMsgsToSend); + + // Increment the number of messages sent + ++iNumMsgsSent; + + iSmtpFile->StartL(iStatus); // start sending the message + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpSendFile); + } + else + { + iCompleted=error; + RequestComplete(iStatus,0-error); // Failed to create File object, but don't want to stop session prematurely, so negate error code + } + } + +void CImSmtpSession::DoCancel() + { + if (iSmtpFile) + { + iSmtpFile->Cancel(); //which calls iSocket->Cancel() + delete iSmtpFile; // this is expected to be deleted + iSmtpFile=NULL; + } + else if (iSocket) + { + iSocket->Cancel(); + } + + if (iLogMessage) + { + iLogMessage->Cancel(); + } + CMsgActive::DoCancel(); // this MUST be the last statement in this function + } + + +void CImSmtpSession::DoRunL() + { + // Received a KErrNone signal completing last pending asynchronous operation. + // So now decide what to do next... + + delete iSmtpFile; // delete mailing object; (new one created later if need to mail another message) + iSmtpFile=NULL; + + if (iStatus.Int() != KErrNone && iState!=ELogDataEvent) + { + iCompleted=iStatus.Int(); // retain any non-zero completion code for future reference when DoComplete() is called + } + + iState = NextStateL(); + + if (!iOperationComplete) + { + if (iSmtpMultiLineResponse) + { + // Don't change state, just read next line of multi-line + // response from remote SMTP server... + iSmtpLastMultiLineResponse=EFalse; + iSocket->QueueReceiveNextTextLine(iStatus); + SetActive(); + } + else + { + DoStateL(); // move to state that was chosen by this function + } + } + else + { + iOperationComplete = EFalse; + } + } + + + +void CImSmtpSession::DoComplete(TInt& aStatus) + { + if (aStatus==KErrDisconnected && iState==EClosingSmtp) + { + // ignore sudden disconnect if in closing state + aStatus = KErrNone; + } + + if (iCompleted && aStatus==KErrNone) // last non-zero error in aStatus takes preference over older error stored in iCompleted + { + aStatus = iCompleted; + } + + if (aStatus>KErrNone) + { + switch (aStatus) + { + case ESmtpMailboxNoAccess: + case ESmtpMailboxName: + case ESmtpTransactionFailed: + aStatus=KSmtpLoginRefused; + break; + case -KSmtpBadMailFromAddress: + case -KSmtpBadRcptToAddress: + case -KSmtpNoMailFromErr: + case -KSmtpLoginRefused: + // Negate the status because it was negated earlier so that it wouldn't be fatal + aStatus = -aStatus; + break; + default: + aStatus=KSmtpUnknownErr; + } + } + } + + +TInt CImSmtpSession::NextStateL() +// this returns the next state according to the current state and the response text received from the server +// if the text has not been accepted by the server, then we close our session with it, i.e. return EClosingSmtp. + { + switch(iState) + { + case EConnectingToSmtp: + return EWaitingForReply; + + case EWaitingForReply: + return NextStateFromWaitingForReply(); + + case EAuthorisingSmtp: + return NextStateFromAuthorisingSmtpL(); + + case ESendingStarttls: + return NextStateFromSendStarttls(); + + case ESettingSecurity: + return NextStateFromSettingSecurityL(); + + case ESendingImail: + if (iBccRcptFound && !iFinishedWithBccRcpts) + { + return ESendingImail; + } + else + { + // 'Bcc' recipients are dealt with at this point + iSendFiles->SetLastMessageStatusL(iTimeNow,(TSmtpSessionError)iStatus.Int()); + // ... which unlocks the server entry (except the first time) + + // now move on to the next Email message + iToRcptHeaderUpdated=EFalse; // for the new msg + iBccRcptFound=EFalse; + iFinishedWithBccRcpts=ETrue; + return ELogDataEvent; + } + + case ELogDataEvent: + return NextStateFromNextFile(); + + case EClosingSmtp: + { + iOperationComplete = ETrue; + return iState; + } + + case EAuthInProgress: + // for SMTP auth, call specific function, to stop cluttering up this one + return NextStateFromAuthInProgressL(); + + case EResetSmtp: + return NextStateFromResetSmtp(); + + default: + return iState; + } + } + +TInt CImSmtpSession::NextStateFromWaitingForReply() + { + if (CommandAccepted()) + { + if (!iEsmtpSpokenHere) + { + iEsmtpSpokenHere = (iSmtpBuffer.Match(KEsmtpMatchString) != KErrNotFound); + if (iEsmtpSpokenHere && iThisSession == ESmtpSession) + iThisSession = EEnhancedSmtpSession; + } + + if (iSmtpMultiLineResponse) + { + // If this is not last reply of a mullti-line response... request next + // line and don't change state + return EWaitingForReply; + } + return EAuthorisingSmtp; + } + else + { + // SMTP server didn't return "220" to "HELO" command, so quit session + // without sending any Emails. + return EClosingSmtp; + } + } + +TInt CImSmtpSession::NextStateFromAuthorisingSmtpL() + { + if (CommandAccepted()) + { + UpdateAuthorisingInfo(); + + if (iSmtpMultiLineResponse) + { + // If this is not last reply of a mullti-line response... request next + // line and don't change state. + return EAuthorisingSmtp; + } + + if (iThisSession==ESecureSession) + { + if(iStartTlsAcceptedHere) + { + return ESendingStarttls; + } + else + { + // The SMTP server doesn't support TLS. + // Save the error and close down the session. + iCompleted = KErrSmtpTLSNegotiateFailed; + return EClosingSmtp; + } + } + else if (iThisSession==EEnhancedSmtpSession) + { + if (iSettings.SMTPAuth() && SelectNextSMTPAuthProfileL()) + { + // if possible, perform SMTP AUTH + return EAuthInProgress; + } + + iOperationComplete = ETrue; + return iState; + } + else + { + __ASSERT_DEBUG(iThisSession==ESmtpSession, gPanic(EImsmBadSessionState)); + iOperationComplete = ETrue; + return iState; + } + } + else + { + // EHLO or HELO rejected by server + if( iSettings.SecureSockets() ) + { + // Cannot continue if we're trying for secure? + iCompleted = KErrSmtpTLSNegotiateFailed; + return EClosingSmtp; + } + else if((iLastSmtpCode == KSmtpBadParameter) && (iRetryAuthWithHostname==EFalse)) + { + // The server did not like the supplied IP parameter, reset and retry with a named + // string as paramter using the same EHLO or HELO command if it hasn't already been + // attempted + iRetryAuthWithHostname = ETrue; + iCompleted = KErrNone; + return EResetSmtp; + } + else if(iThisSession==EEnhancedSmtpSession) + { + // EHLO has failed, reset and retry with HELO + iCompleted = KErrNone; + iRetryAuthWithHostname = EFalse; + iThisSession = ESmtpSession; + return EResetSmtp; + } + else + { + // Every attempt has failed, iCompleted has been set with an appropriate + // error code in the CommandAccepted() call, so simply close the smtp session + return EClosingSmtp; + } + } + } + +TInt CImSmtpSession::NextStateFromSendStarttls() + { + if (CommandAccepted() && iSmtpMultiLineResponse) + { + // If this is not last reply of a mullti-line response... request next + // line and don't change state + return ESendingStarttls; + } + + if(iCompleted) + return EClosingSmtp; + else + return ESettingSecurity; + } + +TInt CImSmtpSession::NextStateFromSettingSecurityL() + { + if ( CommandAccepted() ) + { + UpdateAuthorisingInfo(); + + if (iSmtpMultiLineResponse) + { + // If this is not last reply of a mullti-line response... request next + // line and don't change state + return ESettingSecurity; + } + } + + if(iCompleted) + return EClosingSmtp; + + if (iSettings.SMTPAuth() && SelectNextSMTPAuthProfileL()) + return EAuthInProgress; + else + { + iOperationComplete = ETrue; + return iState; + } + } + +TInt CImSmtpSession::NextStateFromAuthInProgressL() + { + TInt smtpCode=ESmtpNoReturnCode; + TBool commandAccepted=GetCurrentTextLine(); + if (commandAccepted) + { + // parse out SMTP code from text response + smtpCode=SmtpResponseCode(iSmtpBuffer,iSmtpMultiLineResponse,iSmtpLastMultiLineResponse); + if (iSmtpMultiLineResponse) + { + // Check for response code 535 + if (smtpCode == KSmtpAuthCodeFailed) + { + User::Leave(KSmtpLoginRefused); + } + iSmtpAuthHelper->SetLastServerMessageL(iSmtpBuffer,iSmtpMultiLineResponse); + return iState; + } + if(smtpCode==KSmtpAuthCodeSuccessful || smtpCode==KSmtpAuthCodeFailed || smtpCode==KSmtpUnableToAuthAtPresent) + { + //if KSmtpAuthCodeSuccessful then SmtpAuth concluded - move onto next state. + //if KSmtpAuthCodeFailed then SmtpAuth failed, but try to send anyway + iOperationComplete = ETrue; + return iState; + } + else if(smtpCode==ESmtpParamNotImplemented) + { + // this mechanism not recognised by the server. + delete iSmtpAuthHelper; + iSmtpAuthHelper=NULL; + if (SelectNextSMTPAuthProfileL()) + { + return EAuthInProgress; + } + else + { + // all AUTH attempts fail, try to send anyway + iOperationComplete = ETrue; + return iState; + } + } + else if(smtpCode==KSmtpAuthCodeReadyResponse) + { + iSmtpAuthHelper->SetLastServerMessageL(iSmtpBuffer,iSmtpMultiLineResponse); + return EAuthInProgress; + } + // if here, we will execute default error case, unless someone adds + // a clause not resulting in a "return" statement + } + // default error case + iCompleted=SmtpSessionError(smtpCode); + return EClosingSmtp; + + } + +TInt CImSmtpSession::NextStateFromResetSmtp() + { + if( CommandAccepted() ) + { + if (iSmtpMultiLineResponse) + { + // If this is not last reply of a mullti-line response... request next + // line and don't change state + return EResetSmtp; + } + // The reset was accepted - send the HELO command. + return EAuthorisingSmtp; + } + + // The reset was not accepted - didn't get a 2xx response. Might as + // well end the SMTP session. + return EClosingSmtp; + } + +void CImSmtpSession::DoStateL() +// performs the operation required by iState + { + switch (iState) + { + case EConnectingToSmtp: + iEsmtpSpokenHere = EFalse; + i8BitMimeAcceptedHere = EFalse; + iSizeAcceptedHere = EFalse; + iStartTlsAcceptedHere = EFalse; + if(iSettings.SSLWrapper()) + { + // Open secure socket on port 465 for SMTP session + iSocket->SSLQueueConnectL(iStatus, iSettings.ServerAddress(), iSettings.Port(), iSettings.IapPrefs(), iSettings.TlsSslDomain()); + } + else + { + // Open socket on port 25 for SMTP session + iSocket->QueueConnectL(iStatus, iSettings.ServerAddress(), iSettings.Port(), iSettings.IapPrefs(), iSettings.TlsSslDomain()); + } + + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpConnectingToSmtp); + break; + + case EWaitingForReply: + iSocketIsConnected=ETrue; + iSocket->QueueReceiveNextTextLine(iStatus); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpWaitingForReply); + break; + + case EAuthorisingSmtp: + { + TBuf8 address; + GetIpAddress(address); + + if(iRetryAuthWithHostname) + { + // Try sending a text string hostname instead of IP address + _LIT(KDefaultNameString, "localhost"); + address.Copy(KDefaultNameString); + } + + if (iThisSession!=ESmtpSession) + iSmtpBuffer.Format(KSmtpEhloCommand,&address); + else + iSmtpBuffer.Format(KSmtpHeloCommand,&address); + + iSocket->SendQueueReceive(iStatus, iSmtpBuffer); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpAuthorisingSmtp); + break; + } + case EAuthInProgress: + { + iSmtpAuthHelper->GetNextClientMessageL(iSmtpBuffer); + iSocket->SendQueueReceive(iStatus, iSmtpBuffer); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpAuthInProgress); + break; + } + case ESendingStarttls: + { + // User has chosen to use Tls - send the STARTTLS cmd. + iSocket->SetSSLTLSResponseL(KSmtpTlsResponse); + iSocket->SendQueueReceive(iStatus, KSmtpStartTlsCommand()); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpSendingStartTls); + break; + } + case ESettingSecurity: + { + // Reset the Auth profiles and flags. + i8BitMimeAcceptedHere = EFalse; + iSizeAcceptedHere = EFalse; + iStartTlsAcceptedHere = EFalse; + iSupportedAuthProfiles = ENone; + + // Set Security, send a NOOP + // and then queues asynch read request for a response + // from the remote SMTP server. + TBuf8 address; + GetIpAddress(address); + + iSmtpBuffer.Format(KSmtpEhloCommand, &address); + iSocket->SendQueueReceive(iStatus,iSmtpBuffer); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpSettingSecurity); + break; + } + case ESendingImail: // send one Imail message + SendFileL(); + break; + + case ELogDataEvent: + { + if (iLogMessage) + { + const TInt logError = iServerEntry.Entry().iError; + if (logError == KErrNone) + { + TBuf status; + if (iLogMessage->GetString(status, R_LOG_DEL_SENT) == KErrNone) + iLogMessage->LogEvent().SetStatus(status); + } + iLogMessage->Start(logError, iStatus); + } + else + { + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + break; + } + case EResetSmtp: + iSocket->SendQueueReceive(iStatus, KSmtpResetCommand()); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpResetSmtp); + break; + + case EClosingSmtp: //finished running - end SMTP session + iSocket->SendQueueReceive(iStatus, KSmtpQuitCommand()); + MOBILITY_TEST_MTM_STATE(iServiceId, KMobilityTestMtmStateSmtpClosingSmtp); + break; + + default: // Unknown state + gPanic(EImsmBadSessionState); + break; + } + SetActive(); + } + + +TInt CImSmtpSession::SmtpSessionError(const TInt aSmtpErrorCode) + { + // This function is called when no socket/memory error occurred whilst completing + // last state, but the remote SMTP server did not return the expected 3 digit + // completion code. + // Select an appropriate error code determined from the current state of CImSmtpSession. + switch (iState) + { + case EAuthorisingSmtp: + if (aSmtpErrorCode==ESmtpSyntaxError) + return (KSmtpLoginRefused); + break; + + case EWaitingForReply: + // assume that ANY failure in this state means I have been refused connection to SMTP server + return (KSmtpLoginRefused); + + case ESendingStarttls: + case ESettingSecurity: + case EConnectingToSmtp: + case ESendingImail: + case EResetSmtp: + case EClosingSmtp: + break; + + case EAuthInProgress: + if (aSmtpErrorCode==KSmtpAuthCodeFailed || aSmtpErrorCode==KSmtpUnableToAuthAtPresent) + return (KSmtpLoginRefused); + break; + + default: // Unknown state + gPanic(EImsmBadSessionState); + break; + } + + return IdentifySmtpError(aSmtpErrorCode); + } + +TBool CImSmtpSession::GetCurrentTextLine() + { + TInt result=iSocket->GetCurrentTextLine(iSmtpBuffer); + __ASSERT_DEBUG(result==ECRLFTerminated,gPanic(EImsmBadSmtpBuffer)); + + if(result==ECRLFTerminated) + return ETrue; + else + return EFalse; + } + +void CImSmtpSession::GetIpAddress(TDes8& aAddress) +/** Gets the local IP address from the socket without any trailing scope identifiers +@param aAddress Buffer in which the IP address of the socket will be returned */ + { + aAddress.Copy(iSocket->LocalName()); + + // Remove the %nn scope identifier if present Eg. x.x.x.x.x.x%1 becomes x.x.x.x.x.x + // This only occurs with IPv6 and was a requirement of multi-homing. + TInt pos = aAddress.Locate(TChar('%')); + if (pos>0) + { + aAddress.SetLength(pos); + } + } + +/* +From knowledge of the last profile used, and the profiles available, select a next profile +to try, create the helper class +otherwise return false +*/ +TBool CImSmtpSession::SelectNextSMTPAuthProfileL() + { + CSmtpAuthMechanismHelper::TSmtpAuthProfileFlag nextProfile=NextSMTPAuthProfile(iCurrentAuthProfile); + while (!(nextProfile&iSupportedAuthProfiles) && nextProfile!=CSmtpAuthMechanismHelper::ENoProfile) // while "nextProfile" is not supported + // and there are profiles to try + nextProfile=NextSMTPAuthProfile(nextProfile); // try the next one + iCurrentAuthProfile=nextProfile; + if (iCurrentAuthProfile==CSmtpAuthMechanismHelper::ENoProfile) // run out of profiles to try + return EFalse; + else + { + __ASSERT_DEBUG(!iSmtpAuthHelper, gPanic(EImsmSmtpAuthHelperAlreadyExists)); + iSmtpAuthHelper=CreateSMTPAuthHelperL(iCurrentAuthProfile,iSettings); + return ETrue; + } + } + +// Get the value of the connecting IAP from CImTextServerSession +// Returns the value of the currently connecting IAP or a system-wide error code. +TInt CImSmtpSession::GetConnectionIAP() + { + TUint32 iap; + TInt err = iSocket->GetIAPValue(iap); + if (err == KErrNone) + { + return iap; + } + else + { + return err; + } + } + +CSmtpAuthMechanismHelper::TSmtpAuthProfileFlag CImSmtpSession::NextSMTPAuthProfile(CSmtpAuthMechanismHelper::TSmtpAuthProfileFlag aPreviousProfile) + { + // we try the following profiles in this order, CRAM-MD5, LOGIN, PLAIN + switch(aPreviousProfile) + { + case CSmtpAuthMechanismHelper::EUndefined: + return CSmtpAuthMechanismHelper::ECramMD5; + + case CSmtpAuthMechanismHelper::ECramMD5: + return CSmtpAuthMechanismHelper::ELogin; + + case CSmtpAuthMechanismHelper::ELogin: + return CSmtpAuthMechanismHelper::EPlain; + + case CSmtpAuthMechanismHelper::EPlain: + return CSmtpAuthMechanismHelper::ENoProfile; + + default: + gPanic(EImsmBadSmtpAuthProfile1); + } + return CSmtpAuthMechanismHelper::ENoProfile; + } + +TInt CImSmtpSession::NextStateFromNextFile() + { + if (iSendFiles->NextFile() == KErrNone) + { + return ESendingImail; + } + else + { + iOperationComplete = ETrue; + return iState; + } + } + +TBool CImSmtpSession::CommandAccepted() + { + iLastSmtpCode = ESmtpNoReturnCode; + TBool commandAccepted = GetCurrentTextLine(); + if (commandAccepted) + { + iLastSmtpCode = SmtpResponseCode(iSmtpBuffer, iSmtpMultiLineResponse, iSmtpLastMultiLineResponse); // parse out SMTP code from text response + + commandAccepted = LastSmtpCommandAccepted(iLastSmtpCode, 2); // was response accepted by remote server? + } + + if (!commandAccepted) + { + // overrride completion code with suitable error number + iCompleted = SmtpSessionError(iLastSmtpCode); + } + return commandAccepted; + } + +void CImSmtpSession::UpdateAuthorisingInfo() + { + if (!i8BitMimeAcceptedHere) + { + // i.e. don't do test again if flag is already set + i8BitMimeAcceptedHere = (iSmtpBuffer.Match(K8BitMimeMatchString) != KErrNotFound); + } + if (!iSizeAcceptedHere) + { + iSizeAcceptedHere = (iSmtpBuffer.Match(KSizeMatchString) != KErrNotFound); + // ESMTP: V1.1 parse string to extract max message size from string here... + } + if (!iStartTlsAcceptedHere) + { + iStartTlsAcceptedHere = (iSmtpBuffer.Match(KStartTlsMatchString) != KErrNotFound); + } + //Support added for Domino server. + if (iSmtpBuffer.Match(KAuthMatchString) != KErrNotFound || + iSmtpBuffer.Match(KAuthDominoMatchString) != KErrNotFound) + { + // check for each supported profile + if (iSmtpBuffer.Match(KAuthPlainMatchString) != KErrNotFound) + iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::EPlain; + if (iSmtpBuffer.Match(KAuthLoginMatchString) != KErrNotFound) + iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ELogin; + if (iSmtpBuffer.Match(KAuthCramMD5MatchString) != KErrNotFound) + iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ECramMD5; + // Special case for Domino servers tagging "=" after "AUTH" + if (iSmtpBuffer.Match(KAuthDominoPlainMatchString) != KErrNotFound) + iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::EPlain; + if (iSmtpBuffer.Match(KAuthDominoLoginMatchString) != KErrNotFound) + iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ELogin; + if (iSmtpBuffer.Match(KAuthDominoCramMD5MatchString) != KErrNotFound) + iSupportedAuthProfiles|=CSmtpAuthMechanismHelper::ECramMD5; + + } + }