/*
* Copyright (c) 2007 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: This file implements class CIpsPlgSmtpOperation.
*
*/


#include "emailtrace.h"
#include "ipsplgheaders.h"


const TInt KIpsSmtpOperationCharLessThan = '<';
const TInt KIpsSmtpOperationCharMoreThan = '>';

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
EXPORT_C CIpsPlgSmtpOperation* CIpsPlgSmtpOperation::NewL( 
    CMsvSession& aMsvSession, 
    TInt aPriority, TRequestStatus& 
    aObserverRequestStatus,
    TBool /*aUsePublishSubscribe*/ )
    {
    FUNC_LOG;
    CIpsPlgSmtpOperation* self = CIpsPlgSmtpOperation::NewLC(
        aMsvSession, aPriority, aObserverRequestStatus, ETrue );
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
EXPORT_C CIpsPlgSmtpOperation* CIpsPlgSmtpOperation::NewLC( 
    CMsvSession& aMsvSession, 
    TInt aPriority, 
    TRequestStatus& aObserverRequestStatus,
    TBool /*aUsePublishSubscribe*/ )
    {
    FUNC_LOG;
    CIpsPlgSmtpOperation* self = new( ELeave ) CIpsPlgSmtpOperation(
        aMsvSession, aPriority, aObserverRequestStatus );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }
   
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
CIpsPlgSmtpOperation::CIpsPlgSmtpOperation( 
    CMsvSession& aMsvSession, 
    TInt aPriority, 
    TRequestStatus& aObserverRequestStatus ) :
    CMsvOperation( aMsvSession, aPriority, aObserverRequestStatus ),
    iSmtpMtm( NULL ),
    iOperation( NULL ),
    iSelection( NULL ),  
    iMtmRegistry( NULL )
    {
    FUNC_LOG;
    }
  
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//    
CIpsPlgSmtpOperation::~CIpsPlgSmtpOperation() 
    {
    FUNC_LOG;
    Cancel();
    delete iSmtpMtm;
    delete iMtmRegistry;
    delete iSelection;
    delete iOperation;
    } 

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::ConstructL()
    {
    FUNC_LOG;
    iSelection = new (ELeave) CMsvEntrySelection();
    CActiveScheduler::Add( this );
    }
    
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::RunL()
    {
    FUNC_LOG;
    
    
    if ( iState == EMovingOutbox )
        {
        delete iOperation;
        iOperation = NULL;
        iSelection->InsertL(0, iSmtpService );
        CallSendL( );
        }
    else if ( iState == ESending )
        {
        TImSmtpProgress prog;
        TPckg<TImSmtpProgress> param(prog);
        param.Copy( iOperation->FinalProgress() ); 
        
        TRequestStatus* status = &iObserverRequestStatus;
        User::RequestComplete( status, prog.Error() );
        }    
    }
    
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//    
void CIpsPlgSmtpOperation::DoCancel()
    {
    FUNC_LOG;
    
    if ( iOperation )
        {
        iOperation->Cancel();
        }
    TRequestStatus* status = &iObserverRequestStatus;
    User::RequestComplete(status, KErrCancel);
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//   
const TDesC8& CIpsPlgSmtpOperation::ProgressL()
    {
    FUNC_LOG;
    
    if( iOperation && iState == ESending  )
        {
        return iOperation->ProgressL();
        }
    return KNullDesC8;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//       
EXPORT_C void CIpsPlgSmtpOperation::StartSendL( TMsvId aMessageId )
    {
    FUNC_LOG;
    
    TMsvId service;
    TMsvEntry entry;
    User::LeaveIfError( 
        iMsvSession.GetEntry( aMessageId, service, entry ) );
    if ( entry.Id() == KMsvNullIndexEntryIdValue )
        {
        User::Leave( KErrNotFound );
        }
    
    iSmtpService = entry.iServiceId;
    ConstructSmtpMtmL();
    iSmtpMtm->SwitchCurrentEntryL( entry.Id() );
    
    if ( entry.Parent() == KMsvGlobalOutBoxIndexEntryIdValue )
        {
        iSelection->Reset();
        // smtp service id have to be first in array
        // here must be iSmtpService
        iSelection->AppendL( iSmtpService );
        // then message id
        iSelection->AppendL( entry.Id() );
        CallSendL( );
        }
    else
        {
        // 1. perform validity checks for recipient addresses
        // 2. move message to outbox
        // Perform validity checks for recipient addresses
        // before moving
        CMsvEntry* msgCEntry = CMsvEntry::NewL(
            iMsvSession, aMessageId, TMsvSelectionOrdering() );
        CleanupStack::PushL( msgCEntry );
        ValidateRecipientsL( *msgCEntry );
        CleanupStack::PopAndDestroy( msgCEntry );
        msgCEntry = NULL;
        
        iSelection->Reset();
        // get message entry
        msgCEntry = CMsvEntry::NewL(
            iMsvSession, entry.Parent(), TMsvSelectionOrdering() );
        CleanupStack::PushL( msgCEntry );

        // do the move
        iSelection->AppendL( entry.Id());
        iStatus = KRequestPending;
        delete iOperation;
        iOperation = NULL;
        iOperation = msgCEntry->MoveL( 
            *iSelection, KMsvGlobalOutBoxIndexEntryIdValue, iStatus );
        SetActive();
        CleanupStack::PopAndDestroy( msgCEntry );
        iState = EMovingOutbox; 
        }
    }
    
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//    
EXPORT_C TInt CIpsPlgSmtpOperation::EmptyOutboxFromPendingMessagesL( 
    TMsvId aMailboxId  )
    {
    FUNC_LOG;
    TMsvId service;
    TMsvEntry entry;
    User::LeaveIfError( 
        iMsvSession.GetEntry( aMailboxId, service, entry ) );
    
    iSmtpService = entry.iRelatedId;
    
    TInt retValue = KErrNone;
    
    iSelection->Reset();
    ConstructSmtpMtmL();
    iSmtpMtm->SwitchCurrentEntryL( KMsvGlobalOutBoxIndexEntryIdValue );
    
    // in sending first entry in selection array have to be service id
    iSelection->AppendL(iSmtpService);
    //index to first message entry of selection array
    
    CMsvEntrySelection* childs = GetOutboxChildrensL( );
    CleanupStack::PushL( childs );
    for ( TInt i = 0; i < childs->Count(); i++ )
        {
        iMsvSession.GetEntry( childs->At(i), service, entry );
        TUint sendState = entry.SendingState();
        
        if ( iSmtpService == entry.iServiceId &&
            ( sendState != KMsvSendStateUnknown
              || sendState != KMsvSendStateUponRequest
              || sendState != KMsvSendStateSent ) )
            {
            // chage suspended flag to waiting
            // is it safe to change flag from suspended to waiting?
            if ( sendState == KMsvSendStateSuspended )
                {
                CMsvEntry* cEntry = CMsvEntry::NewL(
                    iMsvSession, entry.Id(), TMsvSelectionOrdering() );
                CleanupStack::PushL( cEntry );
                CIpsPlgOperationWait* wait = CIpsPlgOperationWait::NewLC( );
                entry.SetSendingState( KMsvSendStateWaiting );
                cEntry->ChangeL( entry, wait->iStatus );
                wait->Start();
                CleanupStack::PopAndDestroy(2, cEntry ); // wait, cEntry
                }
            // add to send list
            iSelection->AppendL( entry.Id() );
            ++retValue;
            }
        }
    CleanupStack::PopAndDestroy( childs );
    if (retValue > 0)
        {
        CallSendL();
        }
    return retValue;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
EXPORT_C CMsvEntrySelection* CIpsPlgSmtpOperation::GetOutboxChildrensL( )
    {
    FUNC_LOG;
    ConstructSmtpMtmL();
    iSmtpMtm->SwitchCurrentEntryL( KMsvGlobalOutBoxIndexEntryIdValue );
    return iSmtpMtm->Entry().ChildrenL();
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::CallSendL( )
    {
    FUNC_LOG;
    TBuf8<1> params;
    
    iOperation = iSmtpMtm->InvokeAsyncFunctionL(
        KSMTPMTMSendOnNextConnection, *iSelection, params, iStatus);
    SetActive();
    iState = ESending;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::ConstructSmtpMtmL()
    {
    FUNC_LOG;
    if ( !iMtmRegistry )
        {
        iMtmRegistry = CClientMtmRegistry::NewL( iMsvSession );
        }
    if ( !iSmtpMtm )
        {
        iSmtpMtm = static_cast<CSmtpClientMtm*>(
            iMtmRegistry->NewMtmL( KSenduiMtmSmtpUid ) );
        }
    }
    
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// 
void CIpsPlgSmtpOperation::ValidateRecipientsL( CMsvEntry& aEntry )
    {
    FUNC_LOG;
    
    // get store object
    CMsvStore* store = aEntry.ReadStoreL();
    CleanupStack::PushL( store );   // << store
    
    // create symbian email header object
    CImHeader* header = CImHeader::NewLC(); // << header
    header->RestoreL( *store );

    // checking separate recipient fields
    ValidateAddressArrayL( header->ToRecipients() );
    ValidateAddressArrayL( header->CcRecipients() );
    ValidateAddressArrayL( header->BccRecipients() );
    
    // Cleanup
    CleanupStack::PopAndDestroy( header );
    CleanupStack::PopAndDestroy( store );
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// 
void CIpsPlgSmtpOperation::ValidateAddressArrayL( const CDesCArray& aRecipients )
    {
    FUNC_LOG;
    
    TInt count( aRecipients.Count() );
    for ( TInt i(0); i < count; i++ )
        {
        TPtrC fullName( aRecipients[i] );
        
        // Need to locate angle brackets from end of the data, if there
        // is display name with special characters
        TInt start( fullName.LocateReverse( KIpsSmtpOperationCharLessThan ) ); 
        TInt end( fullName.LocateReverse( KIpsSmtpOperationCharMoreThan ) );
        if ( KErrNotFound != start && KErrNotFound != end )
            {
            start += 1;
            fullName.Set( aRecipients[i].Mid( start, ( end - start ) ) );
            }
        // make basic sanity checks for email address
        if( !IpsSetUtils::IsValidEmailAddressL( fullName ) )
            {
            User::Leave( KErrBadName );
            }
        }
    }

// End of File

