// 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;
}