--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/email/pop3andsmtpmtm/smtpservermtm/src/IMSM.CPP Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1312 @@
+// 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:
+// Internet SMTP Transport Driver
+//
+//
+
+#include <msvstd.h> // TMsvEntry, CMsvEntrySelection
+#include <msventry.h> // CMsvServerEntry
+#include <msvstore.h> // needed for CMsvStore::~CMsvStore
+#include <imcvsend.h> // CImSendConvert
+
+#include "IMSM.H"
+#include "IMSMSEND.H"
+#include "SMTSUTIL.H" // forward declarations for utility fns
+#include "csmtpsessionmanager.h"
+#include "csmtpsettings.h"
+#include "cimmobilitymanager.h"
+
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+#include "csmtpupsresponsewaiter.h"
+#endif
+
+
+const TInt KBccArraySegment = 100;
+const TUid KUidSmtpServerMtm = {0x10003C79};
+
+/**
+Constructor
+
+@param aEntrySelection Selection of messages to send
+@param aServerEntry SMTP server entry
+*/
+CMsgImOutboxSend::CMsgImOutboxSend(const CMsvEntrySelection& aEntrySelection,CMsvServerEntry& aServerEntry)
+ : CActive(KMsgImOutboxSendPriority), iEntrySelection(aEntrySelection),iServerEntry(aServerEntry)
+ {
+ }
+
+/**
+Factory constructor
+
+@param aEntrySelection Selection of messages to send
+@param aServerEntry SMTP server entry
+@param aService SMTP service ID
+
+@return Constructed class
+*/
+CMsgImOutboxSend* CMsgImOutboxSend::NewLC(const CMsvEntrySelection& aEntrySelection,CMsvServerEntry& aServerEntry, TMsvId aService)
+ {
+ CMsgImOutboxSend* self = new (ELeave) CMsgImOutboxSend(aEntrySelection,aServerEntry);
+ CleanupStack::PushL(self);
+ self->ConstructL(aService);
+ return self;
+ }
+
+/**
+Factory constructor
+
+@param aEntrySelection Selection of messages to send
+@param aServerEntry SMTP server entry
+@param aService SMTP service ID
+
+@return Constructed class
+*/
+CMsgImOutboxSend* CMsgImOutboxSend::NewL(const CMsvEntrySelection& aEntrySelection,CMsvServerEntry& aServerEntry, TMsvId aService)
+ {
+ CMsgImOutboxSend* self=CMsgImOutboxSend::NewLC(aEntrySelection,aServerEntry,aService);
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CMsgImOutboxSend::ConstructL(TMsvId aService)
+ {
+ iTotalMessages = iEntrySelection.Count();
+ iCurrentMessageNo = -1;
+ iProgress.SetServiceId(aService);
+
+ //Load service info iServerEntry context..
+ User::LeaveIfError(iServerEntry.SetEntry(aService));
+ __ASSERT_DEBUG(iServerEntry.Entry().iType==KUidMsvServiceEntry,gPanic(EImsmNoServiceInfo)); // Assert is ServiceEntry..
+
+ iSettings = CSmtpSettings::NewL(iServerEntry);
+
+ iBccRcptArray = new (ELeave) CDesCArrayFlat(KBccArraySegment);
+
+ // Assert descriptors != empty
+ __ASSERT_DEBUG (iSettings->ServerAddress().Length() >0 , gPanic(EImsmEmptyPostOffice));
+ __ASSERT_DEBUG (iSettings->EmailAddress().Length() >0 , gPanic(EImsmEmptyEmailAddress));
+
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+ iWaiter = CSmtpUpsResponseWaiter::NewL();
+#endif
+
+ if (iSettings->BearerMobility())
+ {
+ iMobilityManager = CImMobilityManager::NewL(KUidSmtpServerMtm, aService, *this);
+ }
+
+ CActiveScheduler::Add(this);
+ }
+
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+void CMsgImOutboxSend::StartL(TRequestStatus& aStatus, TThreadId aClientThreadId, TBool aHasCapability)
+ {
+ SetupStartL();
+
+ iState = EStateUserPrompting;
+ iWaiter->AuthoriseAndConnectL(iSettings->SmtpSettings(), aHasCapability, aClientThreadId, iStatus);
+ SetActive();
+
+ aStatus = KRequestPending;
+ iReport = &aStatus;
+ }
+#endif
+
+void CMsgImOutboxSend::StartL(TRequestStatus& aStatus)
+ {
+ SetupStartL();
+ StartConnectingL();
+
+ aStatus = KRequestPending;
+ iReport = &aStatus;
+ }
+
+void CMsgImOutboxSend::SetupStartL()
+ {
+ iSetDisconnected=EFalse;
+
+ // Get count messages.. leave if none.
+ if (iTotalMessages == 0)
+ {
+ // No messages leave..
+ User::Leave(KErrEof);
+ }
+
+ // Set the connected flag messages..
+ User::LeaveIfError(iServerEntry.SetEntry(KMsvGlobalOutBoxIndexEntryIdValue));
+ User::LeaveIfError(iServerEntry.ChangeAttributes( iEntrySelection, KMsvConnectedAttribute, 0));
+ iServerEntry.SetEntry(KMsvNullIndexEntryId);
+
+ // Initialise the progress object..Create and start a new sesion..
+ iProgress.InitialiseTotal(iTotalMessages); // Sets status=EMsgOutboxProgressWaiting etc.
+ iProgress.SetStatus(EMsgOutboxProgressConnecting);
+ iProgress.SetMsgNo(-1);
+ iCurrentMessageNo = -1;
+ }
+
+void CMsgImOutboxSend::StartConnectingL()
+ {
+ iState = EStateConnectingSession;
+ iMobilityOperation = EMobilityOperationIdle;
+ iSession = NULL;
+ if (!iSessionManager)
+ {
+ iSessionManager = CSmtpSessionManager::NewL(iMobilityManager, iProgress.ServiceId());
+ }
+ iSessionManager->GetSessionL(iServerEntry, *iSettings, iSession, iStatus);
+ SetActive();
+ }
+
+/**
+A bearer switch has been requested and we should try to switch over to it.
+The action parameter indicates what should happen to the current operation.
+
+@param aAction What should happen to the current operation.
+@param aIsSeamless Is this a seamless switchover.
+*/
+void CMsgImOutboxSend::PrepareForNewCarrier(TImMobilityAction aAction, TBool /*aIsSeamless*/)
+ {
+ switch (aAction)
+ {
+ case KAcceptImmediately:
+ {
+ // Just kill the current operation, and then signal that we are
+ // ready to migrate
+ CarrierLost();
+ SignalMigrate();
+ break;
+ }
+
+ case KAcceptStopCurrent:
+ {
+ PrepareForNewCarrierAfterOperation(EMobilityOperationStoppingCurrent);
+ break;
+ }
+
+ case KAcceptCompleteCurrent:
+ {
+ PrepareForNewCarrierAfterOperation(EMobilityOperationCompletingCurrent);
+ break;
+ }
+
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedMobilityAction));
+ break;
+ }
+ }
+ }
+
+/**
+A bearer switch has been requested and the current bearer should be dropped
+immediately.
+*/
+void CMsgImOutboxSend::CarrierLost()
+ {
+ switch (iState)
+ {
+ case EStateIdle:
+ case EStateWaitingNewCarrier:
+ case EStateMobilityError:
+ {
+ // do nothing
+ break;
+ }
+
+ case EStateConnectingSession:
+ {
+ CancelForMigrate();
+ StartWaitingNewCarrier();
+ break;
+ }
+
+ case EStateSendingFiles:
+ {
+ CancelForMigrate();
+ delete iSession;
+ iSession = NULL;
+ StartWaitingNewCarrier();
+ break;
+ }
+
+ case EStateClosingSession:
+ {
+ CancelForMigrate();
+
+ // If we have stopped the current operation and there are still some
+ // messages left to send then we need to wait for the migration to
+ // occur.
+ // If we have completed the operation, or we have sent all the messages
+ // then we need to exit now. We do this by pretending that no bearer
+ // migration is currently taking place, and self completing. Execution
+ // will then pass to the RunL which will just assume that the session
+ // has closed at the end of the operation and we will then exit.
+ if (PreparingForMigration() && iCurrentMessageNo < iTotalMessages)
+ {
+ StartWaitingNewCarrier();
+ }
+ else
+ {
+ iMobilityOperation = EMobilityOperationIdle;
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ SetActive();
+ }
+
+ break;
+ }
+
+ case EStateUserPrompting:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState2));
+ break;
+ }
+ }
+ }
+
+/**
+The new bearer is now active. Try to start using it.
+
+@param aNewAp New access point
+@param aIsSeamless Is this a seamless switchover
+*/
+void CMsgImOutboxSend::NewCarrierActive(TAccessPointInfo /*aNewAp*/, TBool /*aIsSeamless*/)
+ {
+ if (iState == EStateWaitingNewCarrier)
+ {
+ // Self complete to get the active object running again
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ }
+ else
+ {
+ if (iMobilityManager)
+ {
+ iMobilityManager->NewCarrierAccepted();
+ }
+ }
+ }
+
+/**
+An error has occured during a bearer mobility switch
+
+@param aError Error code
+*/
+void CMsgImOutboxSend::MobilityError(TUint /*aError*/)
+ {
+ CancelForMigrate();
+
+ TInt err = KErrNone;
+
+ // If the mobility error has occurred when we are closing the connection
+ // after sending all the messages then we can just self complete to make
+ // it look like the session close completed successfully.
+ // If the mobility error occurs in other states, or while closing the
+ // session for a migration, then we should self complete with an error
+ // so that the RunL routine cleans up and exits.
+ if ((iState != EStateClosingSession) ||
+ (PreparingForMigration() && iCurrentMessageNo < iTotalMessages))
+ {
+ err = KErrDisconnected;
+ }
+
+ iMobilityOperation = EMobilityOperationIdle;
+ iState = EStateMobilityError;
+
+ // Self complete with error code
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, err);
+ SetActive();
+ }
+
+/**
+Get progress information for mobility plugin
+
+@return Packaged progress information
+*/
+const TDesC8& CMsgImOutboxSend::MobilityProgress()
+ {
+ iMobilityProgressBuffer = Progress();
+ return iMobilityProgressBuffer;
+ }
+
+/**
+A bearer switch has been requested and we should either stop the current
+operation or complete it before indicating that the migration can proceed.
+
+@param aMobilityOperation Type of mobility operation that has been requested
+*/
+void CMsgImOutboxSend::PrepareForNewCarrierAfterOperation(TMobilityOperation aMobilityOperation)
+ {
+ switch (iState)
+ {
+ case EStateIdle:
+ case EStateWaitingNewCarrier:
+ case EStateMobilityError:
+ {
+ SignalMigrate();
+ break;
+ }
+
+ case EStateConnectingSession:
+ {
+ CancelForMigrate();
+ SignalMigrate();
+ StartWaitingNewCarrier();
+ break;
+ }
+
+ case EStateSendingFiles:
+ case EStateClosingSession:
+ {
+ // Just need to store what mobility operation has been requested.
+ // Note that if we are sending files, the file sending operation will
+ // see this and halt at the requested time.
+ iMobilityOperation = aMobilityOperation;
+ break;
+ }
+
+ case EStateUserPrompting:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState1));
+ break;
+ }
+ }
+ }
+
+void CMsgImOutboxSend::RunL()
+ {
+ TInt status = iStatus.Int();
+
+ if (status == KErrNone)
+ {
+ switch (iState)
+ {
+ case EStateUserPrompting:
+ {
+ StartConnectingL();
+ break;
+ }
+
+ case EStateConnectingSession:
+ {
+ SessionConnectedL();
+ break;
+ }
+
+ case EStateSendingFiles:
+ {
+ SentFiles();
+ break;
+ }
+
+ case EStateClosingSession:
+ {
+ SessionClosed();
+ break;
+ }
+
+ case EStateWaitingNewCarrier:
+ {
+ MigratedL();
+ break;
+ }
+
+ case EStateMobilityError:
+ {
+ // A mobility error occurred while we were closing the session after
+ // sending all the messages.
+ // do nothing
+ break;
+ }
+
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState3));
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (iState)
+ {
+ case EStateUserPrompting:
+ {
+ // do nothing
+ break;
+ }
+
+ case EStateConnectingSession:
+ {
+ SessionConnectionFailed();
+ break;
+ }
+
+ case EStateSendingFiles:
+ {
+ // closing Session with server
+ SentFiles();
+ break;
+ }
+
+ case EStateClosingSession:
+ {
+ // Failure to close a session can be ignored as it will have been
+ // tidied up.
+ // Continue by assuming the close was successful.
+ SessionClosed();
+ break;
+ }
+
+ case EStateMobilityError:
+ {
+ // do nothing
+ break;
+ }
+
+ case EStateWaitingNewCarrier:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState4));
+ }
+ }
+ }
+
+ // If we are not active then this means we should complete
+ if (!IsActive())
+ {
+ if (iSession)
+ {
+ iProgress.iSendFileProgress = iSession->FileProgress();
+ }
+ else
+ {
+ iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+ iProgress.iSendFileProgress.iBytesSent = 0;
+ iProgress.iSendFileProgress.iBytesToSend = 0;
+ }
+
+ Complete(status);
+ }
+ }
+
+/**
+Handles leaves during RunL
+
+@param aError Error code
+*/
+TInt CMsgImOutboxSend::RunError(TInt aError)
+ {
+ switch (iState)
+ {
+ case EStateConnectingSession:
+ {
+ if (iMobilityOperation != EMobilityOperationMigrating)
+ {
+ iProgress.iSendFileProgress.iSessionState = EConnectingToSmtp;
+ iProgress.iSendFileProgress.iBytesSent = 0;
+ iProgress.iSendFileProgress.iBytesToSend = 0;
+ iProgress.SetMsgNo(KErrNotFound);
+ iProgress.SetConnectionIAP(KErrNotFound);
+ }
+ break;
+ }
+
+ case EStateSendingFiles:
+ {
+ if (iSession)
+ {
+ iProgress.iSendFileProgress = iSession->FileProgress();
+ }
+ iProgress.iSendFileProgress.iSessionState = ESendingImail;
+ break;
+ }
+
+ case EStateUserPrompting:
+ case EStateClosingSession:
+ case EStateWaitingNewCarrier:
+ case EStateMobilityError:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState6));
+ break;
+ }
+ }
+
+ Complete(aError);
+
+ return KErrNone;
+ }
+
+/**
+A new session has been connected
+*/
+void CMsgImOutboxSend::SessionConnectedL()
+ {
+ if (iMobilityOperation == EMobilityOperationMigrating)
+ {
+ if (iMobilityManager)
+ {
+ iMobilityManager->NewCarrierAccepted();
+ }
+
+ if (iDecrementMessageCountAfterMigration)
+ {
+ --iCurrentMessageNo;
+ iDecrementMessageCountAfterMigration = EFalse;
+ }
+ }
+
+ iMobilityOperation = EMobilityOperationIdle;
+
+ iState = EStateSendingFiles;
+ iSession->SendFilesL(*this, iStatus);
+ SetActive();
+ }
+
+/**
+We have completed sending the files
+*/
+void CMsgImOutboxSend::SentFiles()
+ {
+ iProgress.iSendFileProgress = iSession->FileProgress();
+
+ iState = EStateClosingSession;
+ iSessionManager->DeleteSession(*iSession, iStatus);
+ iSession = NULL;
+ SetActive();
+ }
+
+/**
+The session has been closed
+*/
+void CMsgImOutboxSend::SessionClosed()
+ {
+ // If the session has been closed for a bearer migration, and there are still more
+ // messages to send, then start waiting for the migration.
+ if (iMobilityManager)
+ {
+ if (PreparingForMigration() && iCurrentMessageNo < iTotalMessages)
+ {
+ SignalMigrate();
+ StartWaitingNewCarrier();
+ }
+ }
+ }
+
+/**
+The mobility framework has told us that the new bearer is now active. We need
+to conect to it.
+*/
+void CMsgImOutboxSend::MigratedL()
+ {
+ iState = EStateConnectingSession;
+ iSession = NULL;
+ iSessionManager->GetSessionL(iServerEntry, *iSettings, iSession, iStatus);
+ SetActive();
+ }
+
+/**
+Failed to connect a new session
+*/
+void CMsgImOutboxSend::SessionConnectionFailed()
+ {
+ if (iMobilityOperation != EMobilityOperationMigrating)
+ {
+ iSessionManager->ConnectionProgress(iProgress);
+ }
+
+ // If we support bearer mobility, then we should tell the mobility manager
+ // to reject the carrier and then wait for a new one.
+ // If however we are doing the initial (non migration) session connection,
+ // and the failure is because the network connection did not start then we
+ // won't have registed with the mobility manager so we should just exit.
+ if (iMobilityManager &&
+ (iMobilityOperation == EMobilityOperationMigrating ||
+ iSessionManager->IsConnectionStarted()))
+ {
+ iMobilityManager->NewCarrierRejected();
+ StartWaitingNewCarrier();
+ }
+ }
+
+/**
+Tell the mobility manager that we are ready for the migration to proceed
+*/
+void CMsgImOutboxSend::SignalMigrate()
+ {
+ if (iMobilityManager)
+ {
+ iMobilityManager->MigrateToNewCarrier();
+ }
+ }
+
+/**
+Start waiting for the bearer migration to complete
+*/
+void CMsgImOutboxSend::StartWaitingNewCarrier()
+ {
+ iState = EStateWaitingNewCarrier;
+ iMobilityOperation = EMobilityOperationMigrating;
+ iStatus = KRequestPending;
+ SetActive();
+ }
+
+/**
+Cancel the current operations prior to a migration taking place. This
+performs the same as a normal cancel, except that it does not complete
+the caller.
+*/
+void CMsgImOutboxSend::CancelForMigrate()
+ {
+ iCancellingForMigrate = ETrue;
+ Cancel();
+ iCancellingForMigrate = EFalse;
+ }
+
+void CMsgImOutboxSend::DoCancel()
+ {
+ switch (iState)
+ {
+ case EStateUserPrompting:
+ {
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+ iWaiter->Cancel();
+#endif
+ break;
+ }
+
+ case EStateConnectingSession:
+ {
+ if (iMobilityOperation != EMobilityOperationMigrating)
+ {
+ iSessionManager->ConnectionProgress(iProgress);
+ }
+ iSessionManager->Cancel();
+ break;
+ }
+
+ case EStateSendingFiles:
+ {
+ __ASSERT_DEBUG(iSession, gPanic(EImsmSessionNotDefined));
+ if (iSession)
+ {
+ // We should not update the file progress information if we are
+ // cancelling for a bearer migration. For a bearer migration we
+ // collected the progress information at the end of the last
+ // successfully completed file and we don't want to overwrite it
+ // with the information about the file whose send we have cancelled.
+ if (!iCancellingForMigrate)
+ {
+ iProgress.iSendFileProgress = iSession->FileProgress();
+ }
+
+ iSession->Cancel();
+ delete iSession;
+ iSession = NULL;
+
+ // If we are cancelling for bearer migration, make sure that the
+ // message being cancelled is restored to its original state.
+ // We should also decrease our message count so that it indicates
+ // the last sent message so that we restart from the correct place
+ // after the migration.
+ if (iCancellingForMigrate)
+ {
+ TRAP_IGNORE(RestoreBccRecipientsToHeaderL());
+ --iCurrentMessageNo;
+ }
+ }
+ else
+ {
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrCancel);
+ }
+ break;
+ }
+
+ case EStateClosingSession:
+ {
+ // If the session is being closed because all the emails have been sent, set
+ // the final progress information. If the session is closing for a bearer
+ // migration we don't want to overwrite the existing progress values.
+ if (!PreparingForMigration())
+ {
+ iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+ iProgress.iSendFileProgress.iBytesSent = 0;
+ iProgress.iSendFileProgress.iBytesToSend = 0;
+ }
+
+ iSessionManager->Cancel();
+ break;
+ }
+
+ case EStateWaitingNewCarrier:
+ {
+ // There is no outstanding async request so we need to self complete
+ // to keep the active object going.
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrCancel);
+ break;
+ }
+
+ case EStateMobilityError:
+ case EStateIdle:
+ default:
+ {
+ __ASSERT_DEBUG(EFalse, gPanic(EImsmUnexpectedState5));
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrCancel);
+ break;
+ }
+ }
+
+ if (!iCancellingForMigrate)
+ {
+ // Make sure that all message not dealt with
+ // already are set connected EFalse...
+ TRAP_IGNORE(DisconnectUnsentMessagesL());
+ iSetDisconnected=ETrue;
+
+ Complete(KErrCancel);
+ }
+ }
+
+/**
+The sending of all the emails has completed.
+Tidy up and complete the caller.
+*/
+void CMsgImOutboxSend::Complete(TInt status)
+ {
+ if (iState != EStateMobilityError)
+ {
+ // Respond to any outstanding bearer mobility requests as we are about
+ // to exit and we don't need to hold up the migration.
+ if (PreparingForMigration())
+ {
+ SignalMigrate();
+ }
+ else if (iMobilityOperation == EMobilityOperationMigrating &&
+ iState == EStateConnectingSession)
+ {
+ if (iMobilityManager)
+ {
+ iMobilityManager->NewCarrierAccepted();
+ }
+ }
+ }
+
+ delete iSession;
+ iSession = NULL;
+ delete iSessionManager;
+ iSessionManager = NULL;
+
+ iState = EStateIdle;
+ iMobilityOperation = EMobilityOperationIdle;
+ iDecrementMessageCountAfterMigration = EFalse;
+
+ iProgress.SetStatus(EMsgOutboxProgressDone);
+ iProgress.SetError(status);
+
+ User::RequestComplete(iReport, status);
+ }
+
+//
+// Progress()
+//
+// Args: None
+//
+// Return Value: Reference to a TImSmtpProgress object maintained
+// in CImOutboxSend...
+//
+// Remarks: Returns the current iProgress object...
+//
+const TImSmtpProgress& CMsgImOutboxSend::Progress()
+ {
+ switch (iState)
+ {
+ case EStateUserPrompting:
+ {
+ iProgress.iSendFileProgress.iSessionState = EConnectingToSmtp;
+ iProgress.iSendFileProgress.iBytesSent = 0;
+ iProgress.iSendFileProgress.iBytesToSend = 0;
+ iProgress.SetMsgNo(KErrNotFound);
+ iProgress.SetConnectionIAP(KErrNotFound);
+ break;
+ }
+
+ case EStateConnectingSession:
+ {
+ if (iMobilityOperation != EMobilityOperationMigrating)
+ {
+ if (iSessionManager)
+ {
+ iSessionManager->ConnectionProgress(iProgress);
+ }
+ }
+ break;
+ }
+
+ case EStateSendingFiles:
+ {
+ if (iSession)
+ {
+ iProgress.iSendFileProgress = iSession->FileProgress();
+ }
+ break;
+ }
+
+ case EStateClosingSession:
+ {
+ if (iMobilityOperation != EMobilityOperationMigrating)
+ {
+ iProgress.SetStatus(EMsgOutboxProgressDone);
+ iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+ }
+ break;
+ }
+
+ case EStateWaitingNewCarrier:
+ {
+ // do nothing
+ break;
+ }
+
+ case EStateIdle:
+ case EStateMobilityError:
+ default:
+ {
+ iProgress.SetStatus(EMsgOutboxProgressDone);
+ iProgress.iSendFileProgress.iSessionState = EClosingSmtp;
+ break;
+ }
+ }
+
+ if (iProgress.MsgNo() > iProgress.SendTotal())
+ {
+ iProgress.SetMsgNo(iProgress.SendTotal());
+ }
+
+ if (iProgress.MsgNo() < 0)
+ {
+ iProgress.SetMsgNo(0);
+ }
+
+ return iProgress;
+ }
+
+//
+// NextFile()
+//
+// Args:None.
+//
+// Return Value TInt kErrXXX
+//
+// Remarks: Called from CImStmpSession::SelectNextState()
+// Moves iServerEntry and iProgress::MsgNo() to point
+// to the next (or first entry to send)
+// Locking done implicitly in SetEntry() call.
+//
+TInt CMsgImOutboxSend::NextFile()
+ {
+ if(++iCurrentMessageNo >= iTotalMessages) // Stepped past the messages..return
+ {
+ return KErrNotFound;
+ }
+
+ // If we are stopping for migration, exit now
+ if (iMobilityOperation == EMobilityOperationStoppingCurrent)
+ {
+ iDecrementMessageCountAfterMigration = ETrue;
+ return KErrNotFound;
+ }
+
+ // Set the ServerEntry context to the next message.. and check if the message is suspended.
+ //if suspended, don't send and try to send the next message
+ TInt err = iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+
+ if(err == KErrNotFound)
+ {
+ return KErrNotFound;
+ }
+
+ TMsvEntry entry=iServerEntry.Entry();
+ while(entry.SendingState()==KMsvSendStateSuspended && iServerEntry.Entry().iType==KUidMsvMessageEntry)
+ {
+ if (++iCurrentMessageNo >= iTotalMessages)
+ {
+ err=KErrNotFound;
+ iProgress.UpdateFailedToSend(); //this is not the correct update. There should be UpdateNotSent() method.
+ break;
+ }
+ iProgress.UpdateFailedToSend();
+ err = iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+ entry=iServerEntry.Entry();
+ }
+
+ if(err == KErrNone && iServerEntry.Entry().iType != KUidMsvMessageEntry)
+ {
+ // Next context is not a SMTP message type..
+ err = KErrBadHandle;
+ }
+ if(err == KErrNone)
+ {
+ iProgress.SetMsgNo(iCurrentMessageNo); // Update iProgress..
+
+ //if this is the first message we are sending then reset the iProgress.iSent member.
+ if (iCurrentMessageNo==0)
+ {
+ iProgress.SetConnectionIAP(0);
+ }
+
+ iProgress.SetStatus(EMsgOutboxProgressSending);
+ }
+ return(err);
+ }
+
+//
+// SetLastMessageStatusL()
+//
+// Args: TTime -- time stamp..
+// TInt -- completion reason from the last send..
+//
+// Return Value: void.
+//
+// Remarks: Called by CImSmtpSession::SelectNextState() to set CMsvServeEntry
+// data for last file sent.. Either updates iDate etc.. or if 1st call
+// with messages to be sent resets iProgress.SetStatus().
+//
+
+void CMsgImOutboxSend::SetLastMessageStatusL(const TTime& aTimeNow, TInt aCompletionReason)
+ {
+ // If its the first message && there are messages to be sent change status..
+ if (iTotalMessages>0)
+ {
+ iProgress.SetStatus(EMsgOutboxProgressSending);
+ }
+
+ // Store the file progress for the last file sent. This will be used in
+ // the situation where we cancel the operation to do a bearer migration
+ // so that the progress information is for the last file completed as
+ // opposed to the one we have just cancelled.
+ if (iSession)
+ {
+ iProgress.iSendFileProgress = iSession->FileProgress();
+ }
+
+ // Fill in the iServerEntry details with data from the last message
+ // IMCV may had left this inconsistent... so reset the iServerEntry
+ // explicitly....
+ if (iCurrentMessageNo != -1)
+ {
+ TInt err;
+ err = iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+ __ASSERT_DEBUG( err == KErrNone, gPanic(EImsmServerError));
+ TMsvEntry entry = iServerEntry.Entry();
+
+ // Set date info and completion data..update the iServerEntry with this data..
+ entry.iDate=aTimeNow;
+ if(aCompletionReason!=KErrCancel)
+ {
+ entry.SetFailed(aCompletionReason != KErrNone);
+ entry.SetSendingState(aCompletionReason==KErrNone? KMsvSendStateSent: KMsvSendStateWaiting); //set it to send agian.
+ if (aCompletionReason)
+ entry.iError=CalculateError(aCompletionReason);
+ }
+ else
+ {
+ entry.SetFailed(EFalse);
+ entry.SetSendingState(KMsvSendStateSuspended);
+ entry.iError=KErrNone;
+ }
+
+// if (aCompletionReason<=KErrNone)
+ // {
+ // ignore any +ve errors which may leak from the SMTP code
+// entry.iError=aCompletionReason;
+// entry.SetSendingState(aCompletionReason==KErrNone? KMsvSendStateSent: KMsvSendStateWaiting); //set it to send agian.
+// }
+ RestoreBccRecipientsToHeaderL();
+
+ entry.SetConnected(EFalse);
+ err = iServerEntry.ChangeEntry(entry);
+ __ASSERT_DEBUG( err == KErrNone, gPanic(EImsmServerError));
+ UpdateSummaryInfo(aCompletionReason);
+
+ // If it went move to the "Sent" folder..
+ if(!entry.Failed() && aCompletionReason!=KErrCancel)
+ {
+ TMsvId id = entry.Id();
+ err = iServerEntry.SetEntry(KMsvSentEntryIdValue);
+ if(err == KErrNone)
+ {
+ err = iServerEntry.SetEntry(KMsvGlobalOutBoxIndexEntryId);
+ if(err == KErrNone)
+ {
+ // Move it....
+ err = iServerEntry.MoveEntryWithinService(id, KMsvSentEntryIdValue);
+ }
+ }
+ }
+ }
+ }
+
+TInt CMsgImOutboxSend::CalculateError(TInt aCompletionReason)
+ {
+ switch (aCompletionReason)
+ {
+ case ESmtpMailboxNoAccess:
+ case ESmtpMailboxName:
+ case ESmtpTransactionFailed:
+ return KErrAccessDenied; //KSmtpLoginRefused;
+ default:
+ break;
+ }
+ return KErrUnknown;
+ }
+
+//
+// UpdateSummaryInfo()
+//
+// Args: TInt -- KErrXXX from calling function
+//
+// Return value: None
+//
+// Remarks: Called from SetLastMessage Status just increments
+// iProgress count for sent or not sent messages...
+//
+void CMsgImOutboxSend::UpdateSummaryInfo(TInt& aReason)
+ {
+ if (aReason==KErrNone) // sent message successfully
+ {
+ iProgress.UpdateSent();
+ }
+ // MRG 26/08/98 -- Switch statement in IdentifySmtpError() returns no
+ // error information at the moment..
+ // else if edited accordingly..
+ else /*if (IdentifySmtpError(aReason))*/
+ {
+ iProgress.UpdateFailedToSend();
+ }
+ }
+
+
+//
+// SessionIsConnected()
+//
+// Ask for state of iSessions iSocket...
+//
+//
+TBool CMsgImOutboxSend::SessionIsConnected()
+ {
+ if (iState == EStateConnectingSession)
+ {
+ return iSessionManager->IsSessionConnected();
+ }
+ else if(iSession)
+ {
+ return iSession->IsConnected();
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+
+void CMsgImOutboxSend::DisconnectUnsentMessagesL()
+ {
+ // Pos errors
+ TInt err = KErrNone;
+ // Temp entry selection...
+ CMsvEntrySelection* unsentSelection = new (ELeave) CMsvEntrySelection();
+ CleanupStack::PushL(unsentSelection);
+ // Append unsent messages into the temp array..
+ for(TInt i=0; i<iEntrySelection.Count(); i++)
+ {
+ err = iServerEntry.SetEntry((iEntrySelection)[i]);
+ // If not found carry on along the array...
+ if(err != KErrNone)
+ continue;
+ if(iServerEntry.Entry().Parent() == KMsvGlobalOutBoxIndexEntryIdValue)
+ {
+ unsentSelection->AppendL((iEntrySelection)[i]);
+ }
+ }
+
+ // Reset the iConnected flag on the lot.
+ User::LeaveIfError(iServerEntry.SetEntry(KMsvGlobalOutBoxIndexEntryIdValue));
+ if(unsentSelection->Count() > 0)
+ {
+ User::LeaveIfError(iServerEntry.ChangeAttributes(*unsentSelection, 0, KMsvConnectedAttribute));
+ }
+ iServerEntry.SetEntry(KMsvNullIndexEntryId);
+ CleanupStack::PopAndDestroy(); // unsentSelection..
+ }
+
+void CMsgImOutboxSend::CleanUpOnDestructL()
+ {
+ if(!iSetDisconnected)
+ {
+ DisconnectUnsentMessagesL();
+ }
+
+ if (iStatus.Int() != KErrNone)
+ {
+ if ((iCurrentMessageNo != -1) && (iCurrentMessageNo<iTotalMessages))
+ {
+ TInt err=iServerEntry.SetEntry(iEntrySelection.At(iCurrentMessageNo));
+ if(err == KErrNotFound)
+ {
+ User::Leave(KErrNotFound);
+ }
+ __ASSERT_DEBUG(err == KErrNone, gPanic(EImsmServerError));
+ TMsvEntry entry = iServerEntry.Entry();
+
+ RestoreBccRecipientsToHeaderL();
+
+ // Set date info and completion data..update the iServerEntry with this data..
+ TInt errorCode = (TSmtpSessionError)iStatus.Int();
+ entry.iDate.UniversalTime();
+ if(errorCode!=KErrCancel)
+ {
+ entry.SetFailed(errorCode != KErrNone);
+ entry.SetSendingState(errorCode==KErrNone? KMsvSendStateSent: KMsvSendStateWaiting); //set it to send agian.
+ if (errorCode)
+ entry.iError=CalculateError(errorCode);
+ }
+ else
+ {
+ entry.SetFailed(EFalse);
+ entry.SetSendingState(KMsvSendStateSuspended);
+ entry.iError=KErrNone;
+ }
+ entry.SetConnected(EFalse);
+ #ifdef _DEBUG
+ err =
+ #endif
+ iServerEntry.ChangeEntry(entry);
+ __ASSERT_DEBUG(err == KErrNone, gPanic(EImsmServerError));
+ }
+ }
+ }
+
+//
+// ~CMsgImOutboxSend -- destructor.
+//
+CMsgImOutboxSend::~CMsgImOutboxSend( )
+ {
+ Cancel();
+ TRAP_IGNORE(CleanUpOnDestructL());
+
+ delete iSession; //should be dead
+ delete iSettings;
+
+ if (iBccRcptArray)
+ {
+ iBccRcptArray->Reset();
+ delete iBccRcptArray;
+ }
+#if (defined SYMBIAN_USER_PROMPT_SERVICE)
+ delete iWaiter;
+#endif
+
+ delete iSessionManager;
+ delete iMobilityManager;
+ }
+
+CDesCArray& CMsgImOutboxSend::BccRcptArray()
+ {
+ return *iBccRcptArray;
+ }
+
+void CMsgImOutboxSend::ResetBccRcptArrayL()
+ {
+ if(iBccRcptArray)
+ {
+ iBccRcptArray->Reset();
+ delete iBccRcptArray;
+ iBccRcptArray=0;
+ }
+ iBccRcptArray = new (ELeave) CDesCArrayFlat(KBccArraySegment);
+ }
+
+
+void CMsgImOutboxSend::RestoreBccRecipientsToHeaderL()
+ {
+ if (!iServerEntry.HasStoreL() || !iBccRcptArray || iBccRcptArray->Count()==0)
+ return; // no recipients to restore.
+
+ CMsvStore* store = iServerEntry.EditStoreL();
+ CleanupStack::PushL(store);
+
+ // Must have an rfc822 header.
+ CImHeader* header = CImHeader::NewLC();
+ if (store->IsPresentL( KUidMsgFileIMailHeader) )
+ {
+ header->RestoreL(*store);
+ header->BccRecipients().Reset();
+
+ TInt ii = iBccRcptArray->Count();
+ while (ii-- > 0)
+ header->BccRecipients().InsertL(0, (*iBccRcptArray)[ii]);
+ header->StoreL(*store);
+ store->CommitL();
+ }
+
+ // To stop the array growing, delete and recreate.
+ iBccRcptArray->Reset();
+ delete iBccRcptArray;
+ iBccRcptArray=0;
+ iBccRcptArray = new (ELeave) CDesCArrayFlat(KBccArraySegment);
+
+ CleanupStack::PopAndDestroy(header);
+ CleanupStack::PopAndDestroy(store);
+ }
+
+/**
+Indicates if we are preparing to do a migration based on the mobility
+operation value.
+
+@return ETrue if preparing to do a migration, EFalse otherwise
+*/
+TBool CMsgImOutboxSend::PreparingForMigration()
+ {
+ if (iMobilityOperation == EMobilityOperationStoppingCurrent ||
+ iMobilityOperation == EMobilityOperationCompletingCurrent)
+ {
+ return ETrue;
+ }
+
+ return EFalse;
+ }
+
+/**
+Gets the access point ID in use for the connection to the server
+
+@param aAccessPointId On return stores the access point ID value
+
+@return KErrNone if successful, or a system wide error code
+*/
+TInt CMsgImOutboxSend::GetAccessPointIdForConnection(TUint32& aAccessPointId) const
+ {
+ if (iSessionManager)
+ {
+ return iSessionManager->GetAccessPointIdForConnection(aAccessPointId);
+ }
+
+ return KErrNotFound;
+ }