Bug 3539. Update localisation mappings for messaging.
// 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 <e32std.h>
#include "POPSMBX.H"
#include "POPS.H"
#include "POPSOP.H" //CImPop3Operations
// includes for IMCV stuff
#include <txtetext.h>
#include <txtrich.h>
#include <miuthdr.h>
#include <imcvrecv.h>
#include <msvapi.h>
#include <s32mem.h>
#include <s32file.h>
#include "POPS.PAN" // imrc's own panic codes
// Oyster includes
#include <msvstd.h>
#include <msventry.h> // CMsvServerEntry
#include <msvstore.h>
#include <mmsvattachmentmanager.h>
#include <mmsvattachmentmanagersync.h>
#include <miutset.h>
#include <imcvutil.h>
#include <popsmtm.h>
#include "POPSRFSH.h"
#include "POPS.H"
#include "POPSOP.H" //CImPop3Operations
#include "POPSMBX.H"
_LIT(KUidlFile,"UIDLS");
const TInt KMaxStringLength = 1024;
//
// My own panic command
//
GLREF_C void Panic(TPopsPanic aPanic);
CImPop3RefreshMailBox::CImPop3RefreshMailBox(CImPop3Session& aPopSession): CMsgActive( KMsgPop3RefreshMailboxPriority ), iPopSession( aPopSession)
{
__DECLARE_NAME(_S("CImPop3RefreshMailBox"));
}
CImPop3RefreshMailBox* CImPop3RefreshMailBox::NewL(CMsvServerEntry& aRemoteServerEntry, CImPop3Session& aPopSession, RFs& anFs)
{
CImPop3RefreshMailBox* self = new (ELeave) CImPop3RefreshMailBox( aPopSession);
CleanupStack::PushL(self);
self->ConstructL(aRemoteServerEntry, anFs);
CleanupStack::Pop();
return self;
}
void CImPop3RefreshMailBox::ConstructL(CMsvServerEntry& aRemoteServerEntry, RFs& anFs)
{
iRefreshOperation = CImPop3RefreshOperation::NewL(aRemoteServerEntry, &iPopSession, anFs);
_LIT8(KCrLfStr,".\r\n");
iFullStopTerminator = KCrLfStr();
iTextServer=iPopSession.TextServerSession();
CActiveScheduler::Add(this); // Add PopSession to scheduler's queue
}
CImPop3RefreshMailBox::~CImPop3RefreshMailBox()
{
Cancel();
delete iRefreshOperation;
}
void CImPop3RefreshMailBox::DoCancel()
{
switch(iState)
{
case EPop3RefreshBoxDefault:
case EPop3RefreshBoxTop:
{
if (iMigratingToNewBearer)
{
iRefreshOperation->CancelAllowResume();
}
else
{
iRefreshOperation->Cancel();
}
break;
}
case EPop3RefreshBoxPurgeInput:
{
iTextServer->Cancel();
break;
}
default:
{
__ASSERT_DEBUG(EFalse, Panic(EImppUnknownRefreshState));
}
break;
}
CMsgActive::DoCancel();
}
void CImPop3RefreshMailBox::Start(TRequestStatus& aStatus, CArrayFixFlat<TMsvId>* aMsvIdArray)
{
Queue(aStatus);
iMsvIdArray = aMsvIdArray;// Provided for transparency
TRAPD(stateErr,ChangeStateL(EPop3RefreshBoxDefault));
if(stateErr!=KErrNone)
{
Complete(stateErr);
}
}
void CImPop3RefreshMailBox::DoRunL()
{
if (iMigratingToNewBearer)
{
// The Pops MTM is currently migrating so no processing is required here
// This call to doRunl is probably as a result of CImPop3RefreshOperation::Pause()
// completing us.
return;
}
switch (iState)
{
case EPop3RefreshBoxDefault:
if (iStatus.Int() == KPop3RefreshUidlEquate)
{
ChangeStateL(EPop3RefreshBoxPurgeInput);
}
break;
case EPop3RefreshBoxPurgeInput:
iTextServer->GetCurrentTextLine(iTextServerResponse);
if(iTextServerResponse.Compare(iFullStopTerminator)!=0)
{
iTextServer->QueueReceiveNextTextLine(iStatus);
SetActive();
}
else
{
ChangeStateL(EPop3RefreshBoxTop);
}
break;
case EPop3RefreshBoxTop:
break;
default:
break;
};
}
void CImPop3RefreshMailBox::ChangeStateL(TState aState)
{
switch (aState)
{
case EPop3RefreshBoxDefault:
// Start refresh with UIDL
iRefreshOperation->Start(iStatus,iMsvIdArray);
break;
case EPop3RefreshBoxPurgeInput:
iPopSession.SetOpNotPending();
iTextServer->GetCurrentTextLine(iTextServerResponse);
if(iTextServerResponse.Compare(iFullStopTerminator)!=0)
{
iTextServer->QueueReceiveNextTextLine(iStatus);
// Break here to make sure Async function is called before SetActive() is called,
// else we'll get a Stray Signal
break;
}
case EPop3RefreshBoxTop:
iRefreshOperation->Cancel();
iRefreshOperation->Start(iStatus); // Restart refresh with Top
break;
default:
break;
}
iState = aState;
SetActive();
}
TPop3Progress CImPop3RefreshMailBox::Progress()
{
return iRefreshOperation->Progress();
}
void CImPop3RefreshMailBox::SetMessagesToKeepL(const CMsvEntrySelection* aMessagesToKeep)
{
iRefreshOperation->SetMessagesToKeepL(aMessagesToKeep);
}
TUint CImPop3RefreshMailBox::RemoteMessageSizeL(TMsvId aId)
{
return iRefreshOperation->RemoteMessageSizeL(aId);
}
// ******************************************************************************************
// This is called by the POP Client MTM when it starts Migrating Bearer
//
// ******************************************************************************************
void CImPop3RefreshMailBox::Pause()
{
iMigratingToNewBearer = ETrue;
// Ask the Refresh Operation to prepare itself for Migration
switch(iState)
{
case EPop3RefreshBoxDefault:
case EPop3RefreshBoxTop:
{
iRefreshOperation->Pause();
break;
}
case EPop3RefreshBoxPurgeInput:
{
// Cancel the Text Server request, so we can complete to migrate.
iTextServer->Cancel();
break;
}
default:
{
__ASSERT_DEBUG(EFalse, Panic(EImppUnknownRefreshState));
}
break;
}
}
// ******************************************************************************************
// This is called by the POP Client MTM when it starts Migrating Bearer
//
// ******************************************************************************************
void CImPop3RefreshMailBox::CancelAllowResume()
{
iMigratingToNewBearer = ETrue;
Cancel();
}
// Called by the POP Client MTM, once it has completed Migrating to new bearer
void CImPop3RefreshMailBox::ResumeL(CImPop3Session* aPopSession, TRequestStatus& aStatus)
{
Queue(aStatus);
iMigratingToNewBearer = EFalse;
if (iState == EPop3RefreshBoxPurgeInput)
{
ChangeStateL(EPop3RefreshBoxTop);
}
else
{
// Inform the Refresh Operation of Migration Completion
iRefreshOperation->ResumeL(aPopSession, iStatus);
SetActive();
}
}
//================================================================================
CImPop3RefreshOperation::CImPop3RefreshOperation(CMsvServerEntry& aRemoteServerEntry, CImPop3Session* aPopSession, RFs& anFs)
: CMsgActive( KMsgPop3RefreshMailboxPriority ), iUidlKey(_FOFF(TMsgUidlStore,iMsvId),ECmpTUint),
iRemoteServerEntry(aRemoteServerEntry),iPopSession(aPopSession),iFs(anFs)
{
__DECLARE_NAME(_S("CImPop3RefreshOperation"));
}
CImPop3RefreshOperation* CImPop3RefreshOperation::NewL(CMsvServerEntry& aRemoteServerEntry, CImPop3Session* aPopSession, RFs& anFs)
{
CImPop3RefreshOperation* self = new (ELeave) CImPop3RefreshOperation( aRemoteServerEntry, aPopSession, anFs);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
void CImPop3RefreshOperation::ConstructL()
{
// store id of remote collection
iRemoteId=iRemoteServerEntry.Entry().Id();
iRecvConverter=CImRecvConvert::NewL( iFs, &iRemoteServerEntry, KUidMsgTypePOP3, iRemoteId);
iRecvConverter->SetCaf(*iPopSession->GetCafL(iFs));
iPopTop=CImPop3Top::NewL(iPopSession,iRecvConverter);
iPopRetr=CImPop3Retr::NewL(iPopSession,iRecvConverter, iFs);
iUidlArray = new(ELeave) CArrayFixSeg<TMsgUidlStore>(8);
iMsvSelection = new (ELeave) CMsvEntrySelection;
iUnwantedEntries = new (ELeave) CMsvEntrySelection;
// store id of remote collection
// set recv conv.
iRecvConverter->SetMsvId(iRemoteId);
CActiveScheduler::Add(this); // Add CImPop3RefreshOperation to scheduler's queue
iCurrentDrive = MessageServer::CurrentDriveL(iFs);
}
CImPop3RefreshOperation::~CImPop3RefreshOperation()
{
Cancel();
delete iMsvSelection;
// delete everything here
delete iPopStat;
delete iPopList;
delete iPopUidl;
delete iPopTop;
delete iPopRetr;
iUniqueUidlPosArray.Close();
iTemporaryUidlPosArray.Close();
if (iUidlArray)
{
for (TInt ii=iUidlArray->Count();--ii>=0;)
DeleteUidl(ii);
delete iUidlArray;
}
delete iPop3Uidls;
delete [] iSize;
delete iRecvConverter;
delete iUnwantedEntries;
delete iMessagesToKeep;
}
//
// Cancel any current operation
//
void CImPop3RefreshOperation::DoCancel()
{
iPopSession->SetOpNotPending();
switch(iState)
{
case EPopRefreshStat:
if(iPopStat)
{
iPopStat->Cancel();
}
break;
case EPopRefreshList:
if(iPopList)
{
iPopList->Cancel();
}
break;
case EPopRefreshUidl:
if(iPopUidl)
{
iPopUidl->Cancel();
}
break;
case EPopRefreshGetHeader:
if(iPopTop)
{
iPopTop->Cancel();
}
if(iPopRetr)
{
iPopRetr->Cancel();
}
break;
default: // do nothing for all other states
break;
}
CMsgActive::DoCancel();
}
//
// what to do on completion
//
void CImPop3RefreshOperation::DoComplete(TInt& aCompleteStatus)
{
// if we had empty headers report back to user with error code
switch (iState)
{
case EPopRefreshComplete:
break;
case EPopRefreshGetHeader:
if (aCompleteStatus!=KErrNone)
{// If there is no memory to download further, complete
// the operation without calling MessageCompleteL.
if(aCompleteStatus == KErrNoMemory)
{
break;
}
// try and remember what we've done, at least
TRAPD(ignore,iRecvConverter->MessageCompleteL());
TRAP(ignore,RefreshFinishedL());
}
break;
default:
break;
}
}
//
// Start me up
//
void CImPop3RefreshOperation::Start(TRequestStatus& aStatus, CArrayFixFlat<TMsvId>* aMsgIdArray)
{
Queue(aStatus);
iCheckDiskSpaceCounter = 0;
iNewMsg=aMsgIdArray; // take ownership of the message id array
TRAPD(stateErr,ChangeStateL(EPopRefreshSyncCollection));
if(stateErr!=KErrNone)
{
Complete(stateErr);
}
}
//
// Start me up and skip UIDL downloading
// (BFSW1-2016)
void CImPop3RefreshOperation::Start(TRequestStatus& aStatus)
{
Queue(aStatus);
__ASSERT_ALWAYS(iNewMsg,Panic(ETopStartBeforeUidlStart));
iCheckDiskSpaceCounter = 0;
if (iUidlArray)
{ // Delete unreliable UIDL list (BFSW1-2016)
for (TInt ii=iUidlArray->Count();--ii>=0;)
DeleteUidl(ii);
}
TRAPD(stateErr,ChangeStateL(EPopRefreshGetHeader));
if(stateErr!=KErrNone)
{
Complete(stateErr);
}
}
// ******************************************************************************************
// Resume function called by the POP Client MTM, once it has completed Migrating to new bearer
//
// ******************************************************************************************
void CImPop3RefreshOperation::ResumeL(CImPop3Session* aPopSession, TRequestStatus& aStatus)
{
iMigratingToNewBearer = EFalse;
iPopSession = aPopSession;
delete iRecvConverter;
iRecvConverter = NULL;
iRecvConverter=CImRecvConvert::NewL( iFs, &iRemoteServerEntry, KUidMsgTypePOP3, iRemoteId);
iRecvConverter->SetCaf(*iPopSession->GetCafL(iFs));
delete iPopTop;
iPopTop = NULL;
iPopTop=CImPop3Top::NewL(iPopSession,iRecvConverter);
delete iPopRetr;
iPopRetr = NULL;
iPopRetr=CImPop3Retr::NewL(iPopSession,iRecvConverter, iFs);
iRecvConverter->SetMsvId(iRemoteId);
Queue(aStatus);
iCheckDiskSpaceCounter = 0;
TRAPD(stateErr,ChangeStateL(iState));
if(stateErr!=KErrNone)
{
Complete(stateErr);
}
}
//
// Result of last active call
//
void CImPop3RefreshOperation::DoRunL()
{
switch(iState)
{
case EPopRefreshSyncCollection:
if(iRemoteArrayCtr>0)
{
SyncCollectionL();
QueueRemoteUpdate();
}
else
{
CompleteSyncCollection();
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshStat);
return;
}
else
{
ChangeStateL(EPopRefreshStat);
}
}
break;
case EPopRefreshStat:
// initialise progress object
iProgress.iMsgsToProcess=iNoMessages;
iProgress.iTotalMsgs=iNoMessages;
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
iEntry = iRemoteServerEntry.Entry();
iEntry.SetMtmData3(iNoMessages);
User::LeaveIfError(iRemoteServerEntry.ChangeEntry(iEntry));
delete iPopStat;
iPopStat=NULL;
if(iNoMessages)
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshList);
return;
}
ChangeStateL(EPopRefreshList);
}
else
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshDeleteDeadMessages);
return;
}
ChangeStateL(EPopRefreshDeleteDeadMessages);
}
break;
case EPopRefreshList:
delete iPopList;
iPopList=NULL;
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshUidl);
return;
}
ChangeStateL(EPopRefreshUidl);
break;
case EPopRefreshUidl:
__ASSERT_ALWAYS(iPopUidl, Panic(EPopNullPointer));
iUidlExists=iPopUidl->PopCommandAccepted();
delete iPopUidl;
iPopUidl=NULL;
if (iUidlExists)
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshSyncUidl);
return;
}
ChangeStateL(EPopRefreshSyncUidl);
}
else
{
// delete any entries with a zero length inet msg id
//scan UIDL file for msgs with no POP id. If found, delete from UIDL file and Messaging Server
//??? why would they have a zero length POP id ?
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
CArrayFix<TMsgUidlStore>& uidlarray=*iUidlArray;
TInt row = uidlarray.Count();
while(--row>=0)
{
if(uidlarray[row].iPopId->Length()==0)
{
#ifdef _DEBUG
TInt error =
#endif
iRemoteServerEntry.DeleteEntry( uidlarray[row].iMsvId);
__ASSERT_DEBUG( error == KErrNone, Panic(EPopFailedDebugAssert));
DeleteUidl(row);
}
}
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshGetHeader);
return;
}
ChangeStateL(EPopRefreshGetHeader);
}
break;
case EPopRefreshSyncUidl:
if(iRemoteArrayCtr>0)
{
SyncUidlL();
QueueRemoteUpdate();
}
else
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshGetHeader);
return;
}
ChangeStateL(EPopRefreshGetHeader);
}
break;
case EPopRefreshGetHeader:
//At this point iMsgCtr contains the index of the hdr to download
if(iFirstCallToTop)
{
__ASSERT_ALWAYS(iPopTop, Panic(EPopNullPointer));
iTopExists=iPopTop->PopCommandAccepted();
}
if ( !iFirstCallToTop || iTopExists )
{
//This is not the 1st call to TOP, or the TOP command exists
// Don't create a message if the header has none of the following RFC 822 fields:
// To:, Cc:, Bcc:, From:, ReturnPath
// I assume that if it is missing ALL of these fields then the Email message MUST be broken!
if(iUidlExists || CompareIdAndSizeL())
{
// create new TMsvEntry here in folder CMsvEntry
TMsvEntry msvEntry = iRecvConverter->MessageEntryDetailsL();
TRAP_IGNORE(CreateNewHeaderEntryL( msvEntry ));
iIdTab[iMsgCtr]=msvEntry.Id();
__ASSERT_ALWAYS(iNewMsg, Panic(EPopNullPointer));
iNewMsg->AppendL(msvEntry.Id()); // cannot fail, as we have SetReserveL()'d
if (iTopExists)
{
iRecvConverter->MessageCompleteL(msvEntry);
}
}
else
{
TMsvEntry msvEntry = iRecvConverter->MessageEntryDetailsL();
iRecvConverter->MessageCompleteL(msvEntry);
}
iProgress.iMsgsToProcess--;
iMsgCtr--;
}
iFirstCallToTop=EFalse;
if (GetMessagesIfAny())
{
RetrieveHeadersL();
}
else
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshDeleteExcessMessages);
return;
}
ChangeStateL(EPopRefreshDeleteExcessMessages);
if(iUidlExists==EFalse)
{
//We need to remove TOPed or RETRed msgs that were already downloaded
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
for( TInt loop = iUnwantedEntries->Count(); --loop>=0;)
{
#if defined(_DEBUG)
TInt err = iRemoteServerEntry.DeleteEntry((*iUnwantedEntries)[loop]);
__ASSERT_DEBUG(err == KErrNone, Panic(EPopFailedDebugAssert));
#else
iRemoteServerEntry.DeleteEntry((*iUnwantedEntries)[loop]);
#endif
}
}
}
break;
case EPopRefreshDeleteExcessMessages:
if (!iUidlExists)
{
// Fix to stop the deletion of existing headers when POP MTM should connect but not synch
if (iPopSession->MaxHeaders()==0)
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshDeleteDeadMessages);
return;
}
ChangeStateL(EPopRefreshDeleteDeadMessages);
break;
}
else
{
while (--iRemoteArrayCtr>=0)
{
if (iRemoteArrayCtr<TInt(iNoMessages-iPopSession->MaxHeaders()))
{
iRemoteServerEntry.SetEntry(iIdTab[iRemoteArrayCtr]);
const TMsvEmailEntry& entry=iRemoteServerEntry.Entry();
// Check if the message entry is marked for offline delete operation.
TBool notMarkedForOfflineDelete = EFalse;
TImDisconnectedOperationType opType;
if((opType = entry.DisconnectedOperation())!= EDisconnectedDeleteOperation)
{
notMarkedForOfflineDelete = ETrue;
}
if (CanDeleteEntry(entry))
{
// Don't handle the error
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
// The message entry is not marked for offline delete, so delete the entry from
// message INDEX(mail2) file.
if(notMarkedForOfflineDelete)
{
User::LeaveIfError(iRemoteServerEntry.DeleteEntry(iIdTab[iRemoteArrayCtr]));
}
DeleteUidl(iRemoteArrayCtr);
QueueRemoteUpdate();
break;
}
}
}
}
}
else
// Fix to stop the deletion of existing headers when POP MTM should connect but not synch
if (iPopSession->MaxHeaders()==0)
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshDeleteDeadMessages);
return;
}
ChangeStateL(EPopRefreshDeleteDeadMessages);
break;
}
else
while (--iRemoteArrayCtr>=0)
{
const TMsgUidlStore& uidl=iUidlArray->At(iRemoteArrayCtr);
if (iPop3Uidls->MsgNo(*uidl.iPopId) < TInt(iNoMessages-iPopSession->MaxHeaders()))
{
iRemoteServerEntry.SetEntry(uidl.iMsvId);
const TMsvEmailEntry& entry=iRemoteServerEntry.Entry();
// Check if the message entry is marked for offline delete operation.
TBool notMarkedForOfflineDelete = EFalse;
TImDisconnectedOperationType opType;
if((opType = entry.DisconnectedOperation())!= EDisconnectedDeleteOperation)
{
notMarkedForOfflineDelete = ETrue;
}
if (CanDeleteEntry(entry))
{
// Don't handle the error
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
// The message entry is not marked for offline delete, so delete the entry from
// message INDEX(mail2) file.
if(notMarkedForOfflineDelete)
{
User::LeaveIfError(iRemoteServerEntry.DeleteEntry(uidl.iMsvId));
}
DeleteUidl(iRemoteArrayCtr);
QueueRemoteUpdate();
break;
}
}
}
if (iRemoteArrayCtr<0)
{
if (iMigratingToNewBearer)
{
ChangeStateForMigrate(EPopRefreshDeleteDeadMessages);
return;
}
ChangeStateL(EPopRefreshDeleteDeadMessages);
}
break;
case EPopRefreshDeleteDeadMessages: // dispose of un-present messages
if (!iUidlExists)
{
//delete all messages from the Messaging Server whose MsvID does NOT appear in
//the iIdTab[] array.
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
CMsvEntrySelection* pop3Entries = new (ELeave) CMsvEntrySelection;
CleanupStack::PushL(pop3Entries);
User::LeaveIfError(iRemoteServerEntry.GetChildren(*pop3Entries ));
if (iNoMessages == 0)
{
// Clear the entries from the local POP3 Mailbox, and delete the UIDs
if (pop3Entries ->Count()!=0)
{
iRemoteServerEntry.DeleteEntries(*pop3Entries);
}
if (iUidlArray)
{ // Delete UIDL list
for (TInt ii=iUidlArray->Count();--ii>=0;)
DeleteUidl(ii);
}
}
else
{
// Downloaded each header in turn to get the uid list.
// Delete any messages in remote folder that aren't in the iUidlArray.
//
CMsvEntrySelection* deadEntries = new (ELeave) CMsvEntrySelection;
CleanupStack::PushL(deadEntries);
TInt msgCtr = pop3Entries->Count();
//nested loops! - not an ideal search method
while (--msgCtr>=0)
{
TBool found = EFalse;
for (TUint index = 0; index < iNoMessages && !found; index++)
{
if ((*pop3Entries)[msgCtr] == iIdTab[index])
{
found = ETrue;
}
}
if (!found)
{
deadEntries->AppendL((*pop3Entries)[msgCtr]);
}
}
if (deadEntries->Count())
{
User::LeaveIfError(iRemoteServerEntry.DeleteEntries(*deadEntries));
}
CleanupStack::PopAndDestroy(deadEntries);
}
CleanupStack::PopAndDestroy(pop3Entries);
ChangeStateL(EPopRefreshComplete);
break;
}
else
{
while (--iRemoteArrayCtr>=0)
{
const TMsgUidlStore& uidl=iUidlArray->At(iRemoteArrayCtr);
if (uidl.iSelectionPos!=TMsgUidlStore::EPresent)
{
// Don't handle the error
iRemoteServerEntry.DeleteEntry(uidl.iMsvId);
User::LeaveIfError(iRemoteServerEntry.DeleteEntry(uidl.iMsvId));
DeleteUidl(iRemoteArrayCtr);
QueueRemoteUpdate();
break;
}
}
}
if (iRemoteArrayCtr<0)
{
ChangeStateL(EPopRefreshComplete);
}
break;
default: // Unknown state
Panic(EImppUnknownRefreshState);
break;
}
}
//
// Finished refresh so tidy up
//
//
void CImPop3RefreshOperation::RefreshFinishedL()
{
iRemoteServerEntry.CompleteBulk();
CreateUidlFileL();
delete iPopTop;
iPopTop=NULL;
delete iPopRetr;
iPopRetr=NULL;
__ASSERT_ALWAYS(iNewMsg, Panic(EPopNullPointer));
iNewMsg->Reset(); // all new messages are sound
}
// retrieve headers(or whole messages) else complete
//
TBool CImPop3RefreshOperation::GetMessagesIfAny()
{
while (iMsgCtr>=iLastHeaderToGet && iIdTab[iMsgCtr]!=0)
iMsgCtr--;
if (iMsgCtr < 0 )
{
return EFalse;
}
return (iMsgCtr>=iLastHeaderToGet);
}
void CImPop3RefreshOperation::ChangeStateForMigrate(TState aState)
{
iState = aState;
delete iPopTop;
iPopTop=NULL;
delete iPopRetr;
iPopRetr=NULL;
}
void CImPop3RefreshOperation::ChangeStateL(TState aState)
{
//
// State machine of the whole POP mail session.
//
// Identify state on entry, change to next state and then
// start new operation associated with that new state.
//
switch (aState)
{
case EPopRefreshSyncCollection:
GetRemoteMessagesL();
QueueRemoteUpdate();
break;
case EPopRefreshStat:
GetNoMessagesL();
break;
case EPopRefreshList:
AllocateArraysL();
GetMsgSizesL();
break;
case EPopRefreshUidl:
GetMsgUidlsL();
break;
case EPopRefreshSyncUidl:
iUniqueUidlPosArray.Reset();
iTemporaryUidlPosArray.Reset();
iPreviousIndex = KErrGeneral;
iRemoteArrayCtr=iUidlArray->Count();
QueueRemoteUpdate();
break;
case EPopRefreshGetHeader:
// start retrieving headers
iFirstCallToTop=ETrue;
iTopExists=ETrue;
iMsgCtr=iNoMessages-1;
if (!iUidlExists)
{
iLastHeaderToGet =0;
}
else
{
iLastHeaderToGet=iPopSession->MaxHeaders();
if (iLastHeaderToGet >= 0)
iLastHeaderToGet=iNoMessages-iLastHeaderToGet;
if (iLastHeaderToGet<0)
iLastHeaderToGet=0;
}
if(GetMessagesIfAny())
{
if(iPopSession->PipeliningSupport())
{
__ASSERT_ALWAYS(iPopTop, Panic(EPopNullPointer));
iPopTop->SetStartAndEndMessageIndex(iLastHeaderToGet, iMsgCtr);
}
RetrieveHeadersL();
break;
}
aState=EPopRefreshDeleteExcessMessages;
// nothing to do: drop through
case EPopRefreshDeleteExcessMessages:
//Need to commit downloaded headers to file before doing any deletions
iRemoteServerEntry.CompleteBulk();
if (iLastHeaderToGet==0) //no excess messages !
{
aState=EPopRefreshDeleteDeadMessages;
}
// nothing to do: drop through
case EPopRefreshDeleteDeadMessages:
//Need to commit downloaded headers to file before doing any deletions
iRemoteServerEntry.CompleteBulk();
// make sure we're pointing to correct context
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
iRemoteArrayCtr=iUidlArray->Count();
QueueRemoteUpdate();
break;
case EPopRefreshComplete:
RefreshFinishedL();
break;
default: // Unknown state
Panic(EImppUnknownRefreshState);
break;
}
iState=aState;
}
//
// Use Pop STAT command to obtain of messages in remote mailbox
//
void CImPop3RefreshOperation::GetNoMessagesL()
{
CImPop3Stat* popStat = CImPop3Stat::NewL(iPopSession);
if ( iPopStat )
{
delete iPopStat;
iPopStat = NULL;
}
iPopStat = popStat;
iPopStat->Start(iStatus,iNoMessages,iMboxSize);
SetActive();
}
//
// Use LIST command to get message sizes
//
void CImPop3RefreshOperation::GetMsgSizesL()
{
CImPop3List* popList = CImPop3List::NewL(iPopSession);
if ( iPopList )
{
delete iPopList;
iPopList = NULL;
}
iPopList = popList;
iPopList->Start(iStatus,iSize);
SetActive();
}
//
// Use UIDL to obtain POP3 unique message Ids
//
void CImPop3RefreshOperation::GetMsgUidlsL()
{
CImPop3Uidl* popUidl = CImPop3Uidl::NewL(iPopSession);
if ( iPopUidl )
{
delete iPopUidl;
iPopUidl = NULL;
}
iPopUidl = popUidl;
iPopUidl->Start(iStatus,*iPop3Uidls);
SetActive();
}
//
// Post Stat allocate the various arrays
// we know how many messages there are
void CImPop3RefreshOperation::AllocateArraysL()
{
// Allocate the message id array and give it to the POP session
// so that any operation can access it.For example, Top requires
// access to this for pipelining
iIdTab = new(ELeave) TInt32[iNoMessages];
Mem::FillZ(iIdTab,iNoMessages*sizeof(TInt32));
iPopSession->SetMessageArray(iIdTab, iNoMessages);
iSize = new(ELeave) TUint[iNoMessages];
__ASSERT_ALWAYS(iNewMsg, Panic(EPopNullPointer));
iNewMsg->SetReserveL(iNoMessages); // no failure during AppendL()
iPop3Uidls = CImPop3UidlMap::NewL(iNoMessages);
}
void CImPop3RefreshOperation::DeleteUidl(TInt anIndex)
{
TMsgUidlStore& uidl=(*iUidlArray)[anIndex];
delete uidl.iPopId;
iUidlArray->Delete(anIndex);
}
//
// Open uidl file
//
void CImPop3RefreshOperation::GetRemoteMessagesL()
{
TMsvSelectionOrdering order;
order.SetShowInvisibleEntries( ETrue );
iRemoteServerEntry.SetSort( order );
TInt selErr=iRemoteServerEntry.GetChildren(*iMsvSelection);
if(selErr!=KErrNone)
{
User::Leave(selErr);
}
iRemoteArrayCtr=iMsvSelection->Count();
TRAP_IGNORE(OpenUidlFileL());
}
//
// Kick off async operation
//
void CImPop3RefreshOperation::QueueRemoteUpdate()
{
TRequestStatus *pS = &iStatus;
SetActive();
User::RequestComplete(pS,KErrNone);
}
//
// Sets the element with the INDEX in the message server
//
void CImPop3RefreshOperation::SyncCollectionL()
{
//iUidlArray has been read from the UIDL file
//Look for the Messaging Server ID in iUidlArray
//If found, set the member in the array element that points to the message INDEX (not id) in the Messaging Server
--iRemoteArrayCtr; // one at a time
TMsgUidlStore aUidlStore;;
aUidlStore.iMsvId=(*iMsvSelection)[iRemoteArrayCtr];
TInt selectRow;
if(iUidlArray->FindIsq(aUidlStore,iUidlKey,selectRow))
{
// Don't handle error in release mode.
#ifdef _DEBUG
TInt err =
#endif
iRemoteServerEntry.DeleteEntry(aUidlStore.iMsvId);
__ASSERT_DEBUG(err == KErrNone, Panic(EPopFailedDebugAssert));
}
else
iUidlArray->At(selectRow).iSelectionPos=iRemoteArrayCtr;
}
void CImPop3RefreshOperation::CompleteSyncCollection()
//
// Complete the synchronization of the collection and local UIDL store
//
{
for(TInt ii=iUidlArray->Count();--ii>=0;)
{
if(iUidlArray->At(ii).iSelectionPos==TMsgUidlStore::EInvalid)
{
DeleteUidl(ii);
}
}
}
//
// Sync the UIDL file for the Message Server to the POP server.
//
void CImPop3RefreshOperation::SyncUidlL()
{
//iPop3Uidls now contains the message ids of the messages on the POP3 server
--iRemoteArrayCtr;
TMsgUidlStore& uidl=(*iUidlArray)[iRemoteArrayCtr];
__ASSERT_DEBUG(uidl.iSelectionPos>=0,User::Invariant());
TInt popUidlPos = 0;
TInt popUidlRow = iPop3Uidls->MsgUidlNo(*uidl.iPopId,popUidlPos);
// For duplicated case get the actual positions of the UIDLs.
if(popUidlRow == KErrAlreadyExists)
{
while ( popUidlPos > 0 )
{
if (iPop3Uidls->MsgUidl(popUidlPos-1) == *uidl.iPopId)
{
--popUidlPos;
}
else
{
break;
}
}
TInt uidlExists = iUniqueUidlPosArray.FindInOrder(popUidlPos);
if(uidlExists != KErrNotFound)
{
//If aUidl already exists find position of next duplicated uidl.
popUidlPos = iTemporaryUidlPosArray[uidlExists] + 1;
// Remove the last duplicated Uidl position value.
iTemporaryUidlPosArray.Remove(uidlExists);
//Insert the position of the present duplicate uidl to the array.
iTemporaryUidlPosArray.Insert(popUidlPos,uidlExists);
}
else
{
//iUniqueUidlPosArray will contain the first occurrences(pos)of all duplicated UIDLs
iUniqueUidlPosArray.InsertInOrder(popUidlPos);
// For getting next position of duplicate Uidl.
iTemporaryUidlPosArray.InsertInOrder(popUidlPos);
}
//Maximum value of popUidlPos should not exceed count(No. of uidls in the popserver - 1).
TInt count = iPop3Uidls->MsgCount() - 1;
popUidlRow = -1;
if(count >= popUidlPos)
{
// Check Whether the UIDL at popUidlRow of POP server matches the UIDL in Uidl file..
if(iPop3Uidls->MsgUidl(popUidlPos) == *uidl.iPopId)
{
popUidlRow = iPop3Uidls->MsgIndex(popUidlPos);
}
}
}
//Check whether the UIDL index has already been passed.
//This is to Handle the deletion of all duplicated mails from server using other email client.
if(iPreviousIndex == popUidlRow)
{
popUidlRow = -1;
}
else
{
iPreviousIndex = popUidlRow;
}
if(popUidlRow<0)
{
//A Message is in the Messaging Server but not on the POP3 server, so delete it from the
//the Messaging Server
#ifdef _DEBUG
TInt err =
#endif
iRemoteServerEntry.DeleteEntry(uidl.iMsvId);// Don't handle the error in release mode
__ASSERT_DEBUG(err == KErrNone, Panic(EPopFailedDebugAssert));
// & delete it from the UIDL file
DeleteUidl(iRemoteArrayCtr);
}
else
{
//A message is in the Messaging Server *and* is on the POP3 server,
//so mark it as present in the UIDL file, and decrement the number of headers to download
iIdTab[popUidlRow]=uidl.iMsvId; //GetMessagesIfAny() checks this to see if the header is already present
iProgress.iMsgsToProcess--;
uidl.iSelectionPos=TMsgUidlStore::EPresent;
}
}
//
// Report the refreshing news back to the UI
//
TPop3Progress CImPop3RefreshOperation::Progress()
{
return iProgress;
}
//
// Gets Headers or Messages
//
void CImPop3RefreshOperation::RetrieveHeadersL()
{
// Check to see we have at least the minimum free disk space available
if (--iCheckDiskSpaceCounter <= 0)
{
// If we are running low on disk space then leave
ImCheckDiskSpace::LeaveIfLowDiskL(iFs, iCurrentDrive);
iCheckDiskSpaceCounter = KCheckDiskSpaceEveryNMessages;
}
// get header or msg
iTopExists ? GetHeaderByIndexL(iMsgCtr) : GetMessageByIndexL(iMsgCtr);
SetActive();
}
//
// Use Top to Retrieve the message header
//
void CImPop3RefreshOperation::GetHeaderByIndexL(TInt anIndex)
{
__ASSERT_ALWAYS(iPopTop, Panic(EPopNullPointer));
iPopTop->SetMessageIndexAndLines(anIndex+1,1);
iPopTop->StartL(iStatus);
}
//
// If no Top need to retrieve whole message
//
void CImPop3RefreshOperation::GetMessageByIndexL(TInt anIndex)
{
__ASSERT_ALWAYS(iPopRetr, Panic(EPopNullPointer));
iPopRetr->SetMessageIndex(anIndex+1);
iPopRetr->StartL(iStatus);
}
//
//
// Get TMsvEntry from CImHeader via CMsgIMail add to msg collection?
//
void CImPop3RefreshOperation::CreateNewHeaderEntryL(TMsvEntry& aNewEntry)
{
TPtrC8 id;
if(iTopExists)
{
__ASSERT_ALWAYS(iPopTop, Panic(EPopNullPointer));
iPopTop->EntryId();
id.Set(iPopTop->ImMsgId());
}
else
{
__ASSERT_ALWAYS(iPopRetr, Panic(EPopNullPointer));
iPopRetr->EntryId();
id.Set(iPopRetr->ImMsgId());
}
aNewEntry.SetComplete(EFalse);
aNewEntry.SetAttachment(EFalse);
aNewEntry.iSize = iSize[iMsgCtr];
aNewEntry.iServiceId = iRemoteId;
aNewEntry.SetVisible(ETrue);
aNewEntry.SetInPreparation(EFalse);
// add message to list uidl list
TMsgUidlStore msgUidlStore;
if(iUidlExists)
{
msgUidlStore.iPopId=(*iPop3Uidls)[iMsgCtr].AllocLC();
}
else
#if defined _UNICODE
{
msgUidlStore.iPopId = HBufC8::NewLC(id.Length());
msgUidlStore.iPopId->Des().Copy(id);
}
#else
msgUidlStore.iPopId=id.AllocLC();
#endif
msgUidlStore.iMsvId=(aNewEntry.Id());
msgUidlStore.iRemoteSize=iSize[iMsgCtr];
msgUidlStore.iSelectionPos=TMsgUidlStore::EPresent;
iUidlArray->InsertIsqL(msgUidlStore,iUidlKey);
CleanupStack::Pop(); //iPopId
}
//
// Compare internet msg id of retrieved header to what we have stored and compare sizes
//
TBool CImPop3RefreshOperation::CompareIdAndSizeL()
{
TPtrC8 id;
__ASSERT_ALWAYS(iPopTop && iPopRetr, Panic(EPopNullPointer));
iTopExists ? id.Set(iPopTop->ImMsgId()) : id.Set(iPopRetr->ImMsgId());
CArrayFix<TMsgUidlStore>& uidlarray=*iUidlArray;
TInt row=uidlarray.Count();
while (--row>=0 && *uidlarray[row].iPopId!=id)
;
if (row>=0)
{
//we've found an entry in the UIDL file which matches our msg id
//so we don't need to create a new message
TMsgUidlStore& uidlstore=uidlarray[row];
if (uidlstore.iRemoteSize==iSize[iMsgCtr])
{
iIdTab[iMsgCtr]=uidlstore.iMsvId;
__ASSERT_DEBUG(uidlstore.iSelectionPos>=0,User::Invariant());
uidlstore.iSelectionPos=TMsgUidlStore::EPresent;
//as the UIDL command is not supported, we have downloaded the header or the message body
//we're throwing this away now because the message already exists.
__ASSERT_ALWAYS(iPopTop && iPopRetr, Panic(EPopNullPointer));
TMsvId newId;
iTopExists ? newId = iPopTop->EntryId() : newId = iPopRetr->EntryId();
iUnwantedEntries->AppendL(newId);
return EFalse;
}
// Set the current context to the POP3 service entry else DeleteEntry panics
User::LeaveIfError(iRemoteServerEntry.SetEntry(iRemoteId));
// Don't handle the error in release mode.
#ifdef _DEBUG
TInt err =
#endif
iRemoteServerEntry.DeleteEntry(uidlstore.iMsvId); // should this happen?
__ASSERT_DEBUG(err == KErrNone, Panic(EPopFailedDebugAssert));
DeleteUidl(row);
}
return ETrue;
}
//
// Open a file of existing POP Uidls with corresponding Babel MsgIds
//
void CImPop3RefreshOperation::OpenUidlFileL()
{
// get path of uidls file from id of remote server entry
TInt error = KErrNone;
TParse p;
TFileName path;
CMsvStore* store = iRemoteServerEntry.ReadStoreL();
CleanupStack::PushL(store);
MMsvAttachmentManager& attachmentMgr = store->AttachmentManagerL();
// if there is no attachment then a UIDL file has not been
// created, so not found.
if(attachmentMgr.AttachmentCount() < 1)
{
User::Leave(KErrNotFound);
}
CMsvAttachment* attachment = attachmentMgr.GetAttachmentInfoL(0);
CleanupStack::PushL(attachment);
path = attachment->FilePath();
p.Set(path,NULL,NULL);
CleanupStack::PopAndDestroy(2,store);
RFile f;
error = f.Open(iFs,p.FullName(),EFileShareReadersOnly);
User::LeaveIfError(error);
CDirectFileStore* index=CDirectFileStore::FromLC(f);
const TUidType type(KDirectFileStoreLayoutUid,KNullUid,KMsgFileRemoteIdIndex);
if (type != index->Type())
{
User::Leave(KErrCorrupt);
}
RStoreReadStream in;
in.OpenLC(*index,index->Root());
TInt entries = in.ReadInt32L();
TMsgUidlStore entry;
while (--entries>=0)
{
HBufC8* popid=HBufC8::NewLC( in, KMaxStringLength );
// internalization code... inline to handle cleanup issues
entry.iPopId=popid;
entry.iMsvId=in.ReadUint32L();
entry.iRemoteSize=in.ReadUint32L();
entry.iSelectionPos=TMsgUidlStore::EInvalid; // not present in selection (yet)
//
iUidlArray->AppendL(entry);
CleanupStack::Pop(popid); // HBufC8's
}
CleanupStack::PopAndDestroy(2); // in, index
}
//
// Create new POP Uidl file
//
void CImPop3RefreshOperation::CreateUidlFileL()
{
// create the new index of uidl stuff
// put it in folder for current server entry (correct remote mailbox folder)
TParse p;
TFileName path;
TInt fileErr = iRemoteServerEntry.SetEntry( iRemoteId );
if(fileErr!=KErrNone)
{
return;
}
CMsvStore* store = iRemoteServerEntry.EditStoreL();
CleanupStack::PushL(store);
// UIDL file is stored as the first attachment to the server entry
// if it is already present then overwrite, else create it,
MMsvAttachmentManager& attachmentMgr = store->AttachmentManagerL();
RFile file;
if(attachmentMgr.AttachmentCount() < 1)
{
MMsvAttachmentManagerSync& attachmentMgrSync = store->AttachmentManagerExtensionsL();
CMsvAttachment *attachment = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
attachmentMgrSync.CreateAttachmentL(KUidlFile,file,attachment); // Takes ownership of attachment
CleanupClosePushL(file);
store->CommitL();
CleanupStack::Pop(&file);
}
else
{
file=attachmentMgr.GetAttachmentFileForWriteL(0);
}
CleanupStack::PopAndDestroy(store);
CDirectFileStore* index = CDirectFileStore::NewLC(file);
const TUidType type(KDirectFileStoreLayoutUid,KNullUid,KMsgFileRemoteIdIndex);
index->SetTypeL(type);
RStoreWriteStream out;
TStreamId id = out.CreateLC(*index);
TInt entries = iUidlArray->Count();
out.WriteInt32L(entries);
for(TInt number = 0; number<entries; number++) // must keep order, go forwards
{
out << (*iUidlArray)[number];
}
out.CommitL(); // Commit stream : Is this needed?
CleanupStack::PopAndDestroy(); // Destroy stream
index->SetRootL(id);
index->CommitL(); // Commit store : I guess this is needed!
CleanupStack::PopAndDestroy(); // Destroy store
}
//
// Externalise/Internalise Uidl structure
//
void CImPop3RefreshOperation::TMsgUidlStore::ExternalizeL(RWriteStream& aStream) const
{
aStream << *iPopId;
aStream.WriteUint32L(iMsvId);
aStream.WriteUint32L(iRemoteSize);
}
//
// Sets the selection of entries to keep
//
void CImPop3RefreshOperation::SetMessagesToKeepL(const CMsvEntrySelection* aMessagesToKeep)
{
delete iMessagesToKeep;
iMessagesToKeep = NULL;
if (aMessagesToKeep)
{
iMessagesToKeep = aMessagesToKeep->CopyL();
}
}
//
// Check if ok to delete message
//
TBool CImPop3RefreshOperation::CanDeleteEntry(const TMsvEmailEntry& aEntry) const
// Don't delete email if body exists or if it is an email that is being opened immediately after
// the inbox refreshing (See defect PEN-5ESAWM)
{
// iMessagesToKeep may be Null
if (iMessagesToKeep && iMessagesToKeep->Find(aEntry.Id()) != KErrNotFound)
{
return EFalse;
}
return ETrue;
}
//
// Gets the size of the remote message
//
TUint CImPop3RefreshOperation::RemoteMessageSizeL(TMsvId aId)
{
TInt count = iUidlArray->Count();
TUint retVal = NULL;
while( count-- )
{
const TMsgUidlStore &store=iUidlArray->At(count);
if(store.iMsvId == aId)
{
// We have found a match, so return the size for this item
retVal = store.iRemoteSize;
break;
}
}
if (!retVal)
{
// Leave if none of the MsvIds match
User::Leave(KErrNotFound);
}
return retVal;
}
// ******************************************************************************************
// This is called by the POP Client MTM when it starts Migrating Bearer
//
// ******************************************************************************************
void CImPop3RefreshOperation::Pause()
{
// Set the Migration flag
iMigratingToNewBearer = ETrue;
}
// ******************************************************************************************
// This is called by the POP Client MTM when it starts Migrating Bearer
//
// ******************************************************************************************
void CImPop3RefreshOperation::CancelAllowResume()
{
// Cancel the copying of the current message and decrement counters
// so we can restart from this message onwards when we have migrated.
// Use the normal cancel, as we really need to cancel here.
Cancel();
}