ipsservices/ipssosplugin/src/ipsplgsmtpoperation.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 19 Feb 2010 22:37:30 +0200
branchRCL_3
changeset 8 e1b6206813b4
parent 4 e7aa27f58ae1
child 24 d189ee25cf9d
permissions -rw-r--r--
Revision: 201003 Kit: 201007

/*
* 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"
#include "miut_err.h" // SMTP error codes


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 ),
    iState( EIdle ),
    iEventHandler( NULL )
    {
    FUNC_LOG;
    }
  
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//    
CIpsPlgSmtpOperation::~CIpsPlgSmtpOperation() 
    {
    FUNC_LOG;
    Cancel();
    delete iSmtpMtm;
    delete iMtmRegistry;
    delete iSelection;
    delete iOperation;
    iOperation = NULL;
    iEventHandler = NULL;
    iState = EIdle;
    } 

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::ConstructL()
    {
    FUNC_LOG;
    iSelection = new (ELeave) CMsvEntrySelection();
    CActiveScheduler::Add( this );
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::CompleteObserver( TInt aStatus /*= KErrNone*/ )
    {
    FUNC_LOG;

    TRequestStatus* status = &iObserverRequestStatus;
    if ( status && status->Int() == KRequestPending )
        {
        User::RequestComplete( status, aStatus );
        }
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::CompleteThis()
    {
    FUNC_LOG;

    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, KErrNone );
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::RunL()
    {
    FUNC_LOG;

    switch ( iState )
        {
        case EMovingOutbox:
            {
            delete iOperation;
            iOperation = NULL;
            iSelection->InsertL( 0, iSmtpService );
            CallSendL();
            break;
            }
        case ESending:
            {
            TImSmtpProgress prog;
            TPckg<TImSmtpProgress> param(prog);
            param.Copy( iOperation->FinalProgress() );

            if ( prog.Error() == KSmtpLoginRefused )
                {
                // Login details are wrong. Trying to ask for password
                if ( !QueryUserPassL() )
                    {
                    CompleteObserver( prog.Error() );
                    }
                }
            else if ( prog.Error() == KSmtpUnknownErr )
                {
                // try to handle empty password problem
                CEmailAccounts* accounts = CEmailAccounts::NewLC();
                CImSmtpSettings* smtpSettings = new(ELeave) CImSmtpSettings();
                CleanupStack::PushL( smtpSettings );
                TSmtpAccount smtpAccount;
                accounts->GetSmtpAccountL( iSmtpService, smtpAccount );
                accounts->LoadSmtpSettingsL( smtpAccount, *smtpSettings );

                if ( smtpSettings->Password().Length() == 0 )
                    {
                    if ( !QueryUserPassL() )
                        {
                        CompleteObserver( prog.Error() );
                        }
                    }
                CleanupStack::PopAndDestroy( 2, accounts );
                }
            else
                {
                CompleteObserver( prog.Error() );
                }
            break;
            }
        case EQueryingDetails:
            {
            delete iOperation;
            iOperation = NULL;
            // querying pass finished - try to resend mail
            CallSendL();
            break;
            }
        }
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//    
void CIpsPlgSmtpOperation::DoCancel()
    {
    FUNC_LOG;
    
    if ( iOperation )
        {
        iOperation->Cancel();
        }

    CompleteObserver( 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();
        }
    else
        {
        // nothing to do, finish operation
        CompleteObserver( KErrNone );
        }

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

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// 
void CIpsPlgSmtpOperation::SetEventHandler( TAny* aEventHandler )
    {
    iEventHandler = aEventHandler;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
TBool CIpsPlgSmtpOperation::QueryUserPassL()
    {
    if ( iEventHandler )
        {
        CIpsPlgEventHandler* eventHandler = static_cast<CIpsPlgEventHandler*>(iEventHandler);
        TMsvEntry entry;
        TMsvId service;
        User::LeaveIfError( iMsvSession.GetEntry( iSmtpService, service, entry ) );

        // ask for credentials for smtp account and wait for callback
        if ( eventHandler->QueryUsrPassL( entry.iRelatedId, this, EFalse ) )
            {
            iState = EQueryingDetails;
            }
        else
            {
            // another operation is waiting for password
            iState = EQueryingDetailsBusy;
            }

        return ETrue;
        }

    return EFalse;
    }

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
void CIpsPlgSmtpOperation::CredientialsSetL( TInt aEvent )
    {
    FUNC_LOG;

    if ( iState == EQueryingDetails )
        {
        // response for our operation`s query
        if ( aEvent == EIPSSosCredientialsCancelled )
            {
            // user canceled operation
            CompleteObserver( KErrCancel );
            }

        // password has been set, continue with operation
        SetActive();
        CompleteThis();
        }
    else if ( iState == EQueryingDetailsBusy )
        {
        // response for other operation`s query
        // we could try to ask for password now,
        // but decision was made to cancel operation
        CompleteObserver( KErrCancel );
        SetActive();
        CompleteThis();
        }
    }

// End of File