diff -r 000000000000 -r 72b543305e3a email/pop3andsmtpmtm/popservermtm/src/POPS.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/email/pop3andsmtpmtm/popservermtm/src/POPS.CPP Thu Dec 17 08:44:11 2009 +0200 @@ -0,0 +1,679 @@ +// 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 +#include "POPS.H" +#include "POPSOP.H" +#include + +#include +#include +#include +#include +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) +#include "cpopsaslauthhelper.h" +#endif +#include "POPS.PAN" // impp's own panic codes + +#define KPeriod 30000000 // period of timer + +// Specifies how long a socket is allowed to be inactive before we close it +// down. This handles the situation where the mail server closes down the +// connection, but we don't receive any indication of that. It has been seen +// when connected using GPRS, and a long telephone call is then made. +const TInt KPopSendInactivityTimeMinutes = 30; +const TInt KPopReceiveInactivityTimeMinutes = 30; + +// Utility functions used by both active objects... +GLDEF_C TBool CommandAccepted(TDesC8& aResponse) + { + // +OK signifies a successful command -ERR otherwise + return (0==aResponse.Locate('+')); + } +// +// My own panic command +// +GLDEF_C void Panic(TPopsPanic aPanic) + { + _LIT(KPanicText,"Pop3 session"); + User::Panic(KPanicText, aPanic); + } + +CImPop3Session::CImPop3Session() + : CMsgActive(KImPopSessionPriority) + { + __DECLARE_NAME(_S("CImPop3Session")); + } + + +CImPop3Session* CImPop3Session::NewL(RSocketServ& aServ, CImConnect& aConnect) + { + CImPop3Session* self = new (ELeave) CImPop3Session(); + CleanupStack::PushL(self); + self->ConstructL(aServ, aConnect); + CleanupStack::Pop(); + return self; + } + + +void CImPop3Session::ConstructL(RSocketServ& aServ, CImConnect& aConnect) + { + iSocket=CImTextServerSession::NewL(KPopSendInactivityTimeMinutes, KPopReceiveInactivityTimeMinutes, aServ, aConnect); + iNoMessages=0; + iIdTab=NULL; + iSocketConnected=EFalse; +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + iSaslAuthLogin = EFalse; +#endif + + CActiveScheduler::Add(this); // Add PopSession to scheduler's queue + } + + +CImPop3Session::~CImPop3Session() + { + Cancel(); + + delete iPopApop; + delete iConnectReply; + delete iSocket; + delete [] iIdTab; + delete iCaf; + delete iPopCapabilities; + } + +// +// Connect to Pop3 with User and Pass +// +void CImPop3Session::ConnectL(CImPop3Settings* aPopSettings, const CImIAPPreferences& aPrefs, TRequestStatus& aStatus) + { + __ASSERT_DEBUG(!IsActive(),Panic(EImppAlreadyActive)); + // copy of pop settings owned by mtm + iPopSettings = aPopSettings; + + iCompleted=KErrNone; + iState=EConnectingToPop; // Initialise to 1st state of state machine + + Queue(aStatus); + + if(iPopSettings->SSLWrapper()) + { + iSocket->SSLQueueConnectL(iStatus, iPopSettings->ServerAddress(), aPopSettings->Port(), aPrefs, iPopSettings->TlsSslDomain()); + } + else + { + iSocket->QueueConnectL(iStatus, iPopSettings->ServerAddress(), aPopSettings->Port(), aPrefs, iPopSettings->TlsSslDomain()); + } + SetActive(); + } + +// +// Queue a read if mailbox is in idle state +// +void CImPop3Session::Waiting(TRequestStatus& aStatus) + { + iState=EPopConnected; + iSocket->QueueReceiveNextTextLine(iStatus); + SetActive(); + Queue(aStatus); + } +// +// Put a QUIT function +// +void CImPop3Session::Quit(TRequestStatus& aStatus) + { + __ASSERT_DEBUG(!IsActive(),Panic(EImppAlreadyActive)); + + iState=EStopPop; + + if(iSocketConnected) + { + _LIT8(KPop3CmdQuit, "QUIT\r\n"); + iSocket->SendQueueReceive(iStatus,KPop3CmdQuit()); + SetActive(); + Queue(aStatus); + } + else + { + TRequestStatus* pS=&iStatus; + User::RequestComplete(pS,KErrNone); + } + } + +// +// Pass the TMsg array to our Pop session +// +void CImPop3Session::SetMessageArray(TInt32* anIdArray, TUint aNoMessages) + { + iIdTab=anIdArray; + iNoMessages=aNoMessages; + } + +TInt32* CImPop3Session::MessageArray() + { + return iIdTab; + } + +TInt CImPop3Session::GetNumMessages() + { + return iNoMessages; + } + +// +// Return Pop3 message no for a given message id +// +TInt CImPop3Session::MessageNo(TMsvId aMsgId) +// AF Using TInt reduces the available range of +ve numbers +// but shouldn't be a problem should it? + { + TInt msgNo=KErrNotFound; + TInt arrayCtr=0; + + if(iIdTab==NULL || iNoMessages==0) + { + Panic(EImppBabelMsgArrayNotDefined); + } + + while(arrayCtrDisconnect(); + iSocketConnected=EFalse; + } + iSocket->Cancel(); // clear a pending socket call + CMsgActive::DoCancel(); + } + +// +// Result of last active call +// +void CImPop3Session::DoRunL() + { + if(iState==EStopPop) + { + iSocket->Disconnect(); + iSocketConnected=EFalse; + } + + //get the response + if(iState!=EConnectingToPop && iState!=EStopPop) + { + if(iState != EWaitingForReply) + { + TInt lineState=iSocket->GetCurrentTextLine(iResponseBuffer); + __ASSERT_ALWAYS(lineState==ECRLFTerminated,User::Leave(EImppBufferNotTerminated)); + + if(!CommandAccepted(iResponseBuffer)) + { + User::Leave(GetPopError()); + } + } + + if(iPopSettings->Apop() && iState==EWaitingForReply) + { + HBufC8* connectReply = HBufC8::NewL(KImMailMaxBufferSize); + // Delete iConnectReply if not NULL & assign newly created + delete iConnectReply; + iConnectReply = connectReply; + iConnectReply->Des().Copy(iResponseBuffer); + } + } + + if(iState!=EStopPop) + { + // successful response decide what to do next + switch(iState) + { + case EPopCapabilities: + GetCapabilitiesL(); + iState = EWaitingForReply; + break; + + case EWaitingForReply: + if(iPopSettings->SecureSockets()) + { + iState = ERequestingTLS; + } +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + else if(iPopSettings->POP3Auth()) + { + iState = ESaslAuthInProgress; + SelectAuthExtensionProfileL(); + } +#endif + else if(iPopSettings->Apop()) + { + iState=EAuthorisingApop; + } + else + { + iState++; + } + ChangeStateL(); + break; + +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + case ESaslAuthInProgress: + // To set the server response, accodring the response will be creating + // the authentication string. + iPopAuthHelper->SetLastServerMessageL(iResponseBuffer, ETrue); + iState++; + ChangeStateL(); + break; + + case ESaslAuthIsDone: + iSocketConnected=ETrue; + break; +#endif + + case EConnectingToPop: + case EAuthorisingUser: + iState++; + ChangeStateL(); + break; + + case EAuthorisingPass: + case EAuthorisingApop: + iSocketConnected=ETrue; + break; + + // TLS request accepted + case ERequestingTLS: +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + if(iPopSettings->POP3Auth()) + { + iState = ESaslAuthInProgress; + SelectAuthExtensionProfileL(); + } + else +#endif + { + iPopSettings->Apop() ? iState = EAuthorisingApop : iState =EAuthorisingUser; + } + ChangeStateL(); + break; + } + } + } + +void CImPop3Session::DoComplete(TInt& aCompleteStatus) + { + TInt status=aCompleteStatus; + if (status==KErrNone) // no error + { + return; + } + + if(iSocketConnected && status==KErrCancel) // Cancel() has been called + { + return; + } + + // if we've connected but it all goes wrong send a synchronous quit kludged code + switch (iState) + { + case EWaitingForReply: // stay connected + case EStopPop: + break; + case EAuthorisingUser: + case EAuthorisingPass: + case EAuthorisingApop: + case EPopConnected: // idle read has returned POP server timed out + case EConnectingToPop: // lose connection + case ERequestingTLS: +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + case ESaslAuthInProgress: + case ESaslAuthIsDone: +#endif + iSocket->Disconnect(); + iSocketConnected=EFalse; + break; + } + } + +void CImPop3Session::ChangeStateL() + { + // + // State machine of the POP mail session. + // (But only handle states which can leave) + // Identify state on entry, change to next state and then + // start new operation associated with that new state. + // + + __ASSERT_DEBUG(!IsActive(),Panic(EImppAlreadyActive)); + + switch (iState) + { + case EPopCapabilities: + iSocket->QueueReceiveNextTextLine(iStatus); + break; + +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + case ESaslAuthInProgress: + iPopAuthHelper->GetNextClientMessageL(iResponseBuffer); + iSocket->SendQueueReceive(iStatus, iResponseBuffer); + iSocket->PerformLogging(ETrue); + break; + + case ESaslAuthIsDone: + iPopAuthHelper->GetNextClientMessageL(iResponseBuffer); + iSocket->SendQueueReceive(iStatus, iResponseBuffer); + iSocket->PerformLogging(ETrue); + if(iSaslAuthLogin) + { + iState = ESaslAuthInProgress; + iSaslAuthLogin = EFalse; + } + break; +#endif + + case EAuthorisingUser: + case EAuthorisingApop: + { + TPtrC8 loginName=iPopSettings->LoginName(); + if (iState == EAuthorisingApop) + { + TRAPD(apopErr,ConstructApopL()); + if(apopErr!=KErrNone) + { + User::Leave(KPop3CannotCreateApopLogonString); + } + iSocket->PerformLogging(EFalse); + _LIT8(KPop3CmdApop, "APOP %S %S\r\n"); + iSocket->SendQueueReceive(iStatus, KPop3CmdApop(), &loginName, iPopApop); + iSocket->PerformLogging(ETrue); + delete iPopApop; + iPopApop=NULL; + delete iConnectReply; + iConnectReply=NULL; + } + else + { + iSocket->PerformLogging(EFalse); + _LIT8(KPop3CmdUser, "USER %S\r\n"); + iSocket->SendQueueReceive(iStatus, KPop3CmdUser(), &loginName); + iSocket->PerformLogging(ETrue); + } + break; + } + case EAuthorisingPass: + { + TPtrC8 password=iPopSettings->Password(); + iSocket->PerformLogging(EFalse); + _LIT8(KPop3CmdPass, "PASS %S\r\n"); + iSocket->SendQueueReceive(iStatus, KPop3CmdPass(), &password); + iSocket->PerformLogging(ETrue); + break; + } + // new request to initiate TLS + case ERequestingTLS: + _LIT8(KPop3CmdOk, "+OK"); + iSocket->SetSSLTLSResponseL(KPop3CmdOk()); + _LIT8(KPop3CmdStls, "STLS\r\n"); + iSocket->SendQueueReceive(iStatus, KPop3CmdStls()); + break; + + default: // Unknown state + Panic(EImppBadSessionState); + break; + } + + SetActive(); + } + +// +// return a pointer to our CImTextServerSession so CPop3Operations can use it +// + CImTextServerSession* CImPop3Session::TextServerSession() + { + return iSocket; + } + +// +// Return POP3 error (based on what state the POP login is in +// +TInt CImPop3Session::GetPopError() + { + TInt anError=KErrGeneral; + switch (iState) + { + case EWaitingForReply: + case EPopCapabilities: +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + if(iPopSettings->POP3Auth() && iState != EPopCapabilities) + { + anError=KPop3AuthenticationFailed; + } + else +#endif + { + anError=KPop3CannotConnect; + } + break; + case EAuthorisingUser: + anError=KPop3InvalidUser; + break; + case EAuthorisingPass: + anError=KPop3InvalidLogin; + break; +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) + case ESaslAuthInProgress: + anError=KPop3InvalidUser; + break; + case ESaslAuthIsDone: + anError=KPop3InvalidLogin; + break; +#endif + case EAuthorisingApop: + anError=KPop3InvalidApopLogin; + break; + case EPopConnected: + anError=KErrDisconnected; // assume that this is Pop time out + break; + case ERequestingTLS: + anError = KErrPop3TLSNegotiateFailed; + default: + break; + } + return anError; + } + +//DMC +TInt CImPop3Session::MaxHeaders() + { + return iPopSettings->InboxSynchronisationLimit(); + } + +// +// Set and test state of iOpPending: is a pop operation currently underway? +// +void CImPop3Session::SetPending() + { + iOpPending=ETrue; + } + +void CImPop3Session::SetOpNotPending() + { + iOpPending=EFalse; + } + +TBool CImPop3Session::IsPending() + { + return iOpPending; + } + + +TBool CImPop3Session::IsConnectedToInternet() + { + return iSocketConnected; + } + +// +// construct Apop string using timestamp and user password (and MD5 hash function) +// +void CImPop3Session::ConstructApopL() + { + TPtrC8 pass(iPopSettings->Password()); + TPtrC8 timeStamp; + TInt timeStampEndPos=KErrNotFound; + + const TInt timeStampStartPos=iConnectReply->Des().Locate('<'); + if(timeStampStartPos==KErrNotFound) + { + User::Leave(KErrNotFound); + } + + timeStampEndPos=iConnectReply->Des().Locate('>'); + if(timeStampEndPos!=KErrNotFound && (timeStampEndPos>timeStampStartPos)) + { + TInt timeStampLength=(timeStampEndPos-timeStampStartPos)+1; + timeStamp.Set(iConnectReply->Des().Mid(timeStampStartPos,timeStampLength)); + } + else + { + User::Leave(KErrNotFound); + } + + HBufC8* popBuf = HBufC8::NewLC(timeStamp.Length() + pass.Length()); + TPtr8 bufContent=popBuf->Des(); + bufContent=timeStamp; + bufContent.Append(pass); + + // do MD5 + CMD5* doHash = CMD5::NewL(); + CleanupStack::PushL(doHash); + HBufC8* popApop = HBufC8::NewL(2*doHash->HashSize()); + delete iPopApop; + iPopApop = NULL; + iPopApop = popApop; + TPtr8 hashCont=iPopApop->Des(); + hashCont = doHash->Hash(*popBuf); + + TBuf<32> hexHash; + // MD5 algorithm ALWAYS returns 16 bytes of data - which will be converted into + // 32 characters; each byte represented by a 2 character hex representation, + // eg 255="ff" + + for (TInt j=0;j<16;j++) + { + hexHash.AppendNumFixedWidth(hashCont[j],EHex,2); + } + + // copy Hex values to iPopApop so that it can be used with the APOP command + iPopApop->Des().Copy(hexHash); + + CleanupStack::PopAndDestroy(2); //popBuf & doHash + } + +CImCaf* CImPop3Session::GetCafL(RFs& aFs) + { + if(!iCaf) + { + iCaf = new(ELeave) CImCaf(aFs); + } + return iCaf; + } + +// +// Use CAPA command to get server capabilities +// +void CImPop3Session::GetCapabilitiesL() + { + iPopCapabilities = CImPop3Capa::NewL(this); + + iPopCapabilities->Start(iStatus); + SetActive(); + } + +TBool CImPop3Session::PipeliningSupport() + { + return iPopCapabilities->PipeliningSupport(); + } + + +#if (defined SYMBIAN_EMAIL_CAPABILITY_SUPPORT) +/** +Function to select SASL Authentication Mechanism supported by email server. +If CRAM-MD5 Authentication is not supported by server and if Fallback flag is enabled, +will fallback to less secure authentication mechanism. +Fallback priority will be in a following order when server dosn't support CRAM-MD5. +1. APOP +2. PLAIN +3. LOGIN +4. USER/PASS +*/ +void CImPop3Session::SelectAuthExtensionProfileL() + { + // get the supported SASL Capabilities + TUint supportedAuthProfiles = iPopCapabilities->SaslAuthExtensionFlag(); + + // to check whether CRAM-MD5 supported by the server + if(supportedAuthProfiles & CPopAuthMechanismHelper::ECramMD5) + { + //AUTHENTICATE CRAM-MD5 implementation + iPopAuthHelper = CPopAuthCramMd5MechanismHelper::NewL(*iPopSettings); + } + // if server doesn't support CRAM-MD5, check whether FallBack enabled from POP3 email settings. + else if(iPopSettings->FallBack()) + { + if(iPopSettings->Apop()) + { + //APOP + iState=EAuthorisingApop; + } + else if(supportedAuthProfiles & CPopAuthMechanismHelper::EPlain) + { + //AUTHENTICATE PLAIN implementation + iPopAuthHelper = CPopAuthPlainMechanismHelper::NewL(*iPopSettings); + iState = ESaslAuthIsDone; + } + else if(supportedAuthProfiles & CPopAuthMechanismHelper::ELogin) + { + //AUTHENTICATE LOGIN implementation + iPopAuthHelper = CPopAuthLoginMechanismHelper::NewL(*iPopSettings); + iSaslAuthLogin = ETrue; + } + else + { + //USER/PASS + iState = EAuthorisingUser; + } + } + else + { + // if server doesn't support CRAM-MD5 & FallBack is Off, leave with error. + iState = EWaitingForReply; + User::Leave(GetPopError()); + } + } + +#endif +