/*
* Copyright (c) 2003-2010 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:  A connection to a smart card
*
*/



// INCLUDE FILES
#include    "ScardConnector.h"
#include    "ScardConnectionRegistry.h"
#include    "ScardAccessControl.h"
#include    "ScardAccessControlRegistry.h"
#include    "ScardConnectionTimer.h"
#include    "ScardServer.h"
#include    "ScardServerBase.h"
#include    "WimTrace.h"

#ifdef _DEBUG // for logging
#include    "ScardLogs.h"
#include    <flogger.h> 
#endif

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// TConnectionParameter::TConnectionParameter
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
TConnectionParameter::TConnectionParameter()
    : iReaderName( NULL ), 
      iExcluded( NULL ), 
      iATRBytes( NULL ),
      iAIDBytes( NULL ),
      iNewCardOnly( EFalse ), 
      iNewReaderOnly( EFalse )
    {
    _WIMTRACE(_L("WIM|Scard|TConnectionParameter::TConnectionParameter|Begin"));
    }

// -----------------------------------------------------------------------------
// TConnection::TConnection
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
TConnection::TConnection()
    : iCtrl( NULL ), 
      iReader( NULL ), 
      iReaderID( 0 ), 
      iSessionID( ENoSession )
    {
    }

// -----------------------------------------------------------------------------
// Compare two connections
// -----------------------------------------------------------------------------
//
TBool operator==(
    const TConnection& aConnection1, 
    const TConnection& aConnection2 )
    {
    if ( aConnection1.iReader == aConnection2.iReader && 
        aConnection1.iCtrl == aConnection2.iCtrl &&
        aConnection1.iSessionID == aConnection2.iSessionID &&
        aConnection1.iReaderID == aConnection2.iReaderID )
        {
        return ETrue;
        }
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CScardConnector::CScardConnector
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CScardConnector::CScardConnector( CScardConnectionRegistry* aConnRegistry ) :
        CScardSession(), iConnectionRegistry( aConnRegistry ), iState( EActive )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::CScardConnector|Begin"));
    }

// -----------------------------------------------------------------------------
// CScardConnector::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CScardConnector::ConstructL( const RMessage2& aMessage )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::ConstructL|Begin"));
    iConnections = new( ELeave ) CArrayFixFlat<TConnection>( 1 );
    iStack = CScardEventStack::NewL( this );
    iClientMessage = new( ELeave ) RMessage2( aMessage ); 
    iState = EActive;
    }

// -----------------------------------------------------------------------------
// CScardConnector::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CScardConnector* CScardConnector::NewL(
    CScardConnectionRegistry* aConnRegistry,
    const RMessage2& aMessage )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::NewL|Begin"));
    CScardConnector* self = new( ELeave ) CScardConnector( aConnRegistry );

    CleanupStack::PushL( self );
    self->ConstructL( aMessage );
    CleanupStack::Pop( self );

    return self;
    }

    
// Destructor
CScardConnector::~CScardConnector()
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::~CScardConnector|Begin"));
    delete iTimer;
    
    //  Delete members of the parameter block
    delete iParameters.iReaderName;
    delete iParameters.iExcluded;
    delete iParameters.iATRBytes;
    delete iParameters.iAIDBytes;

    //  Detach from all connections
    if ( iConnections->Count() )
        {
        TConnection& connection = iConnections->At( 0 );
        //  Detach and delete all connections
        while ( iConnections->Count() )
            {
            connection = iConnections->At( 0 );
            TInt err = KErrNone;
            TRAP( err, connection.iCtrl->CancelTransmissionsL(
                                                      connection.iSessionID ) );
            connection.iCtrl->DetachSessionFromReader( connection.iSessionID );
            iConnections->Delete( 0 );
            }
        }

    iConnections->Reset();
    delete iConnections;
    delete iClientMessage;
    delete iStack;
    }


// -----------------------------------------------------------------------------
// CScardConnector::ConnectToReaderL
// The connection is attempted and if it is successful
// the session requesting the connection is notified. If the connection
// can not yet be made, this object will remain waiting until all the
// required parameters are matched or a timeout occurs.
// -----------------------------------------------------------------------------
//
void CScardConnector::ConnectToReaderL()
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::ConnectToReaderL|Begin"));
    // Get the limit factors
    TRAPD( messageError, ReadLimitsL() ); 
    if ( messageError == KScErrBadArgument || 
        messageError == KScErrNotSupported )
        {
#ifdef _DEBUG
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
            EFileLoggingModeAppend, 
            _L( "CScardConnector::ConnectToReader: ReadLimits failed: %d \n" ), 
            messageError );
#endif
        iConnectionRegistry->ConnectDone( this, 0, messageError );
        iConnectionRegistry->RemoveConnector( this );
        delete this;
        return;
        }
    else if ( messageError != KErrNone )
        {
        User::Panic( _L( "Not enough free memory" ), KScPanicNoMemory );
        }

    //  Now go to all the ACs you need and attach to them. If the reader 
    //  is spesific, only one entry is required. Otherwise all are.
    if ( iParameters.iReaderName )
        {
#ifdef _DEBUG
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,     
            EFileLoggingModeAppend, 
            _L( "CScardConnector::ConnectToReader Connecting to specific\
            reader: %S\n" ),
           iParameters.iReaderName);
#endif
        iOneReaderMode = ETrue;
        //  Is the chosen one supported ??
        if ( !( iConnectionRegistry->Server()->ReaderSupported( 
            *iParameters.iReaderName ) ) )
            {
#ifdef _DEBUG
            RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
                EFileLoggingModeAppend, 
                _L( "CScardConnector::ConnectToReader ReaderSupported\
                failed: \n" ) );
#endif
            iConnectionRegistry->ConnectDone( this, 0, KScErrUnknownReader );
            iConnectionRegistry->RemoveConnector( this );
            delete this;
            return;
            }

        //  Grab the reader id (this shouldn't fail if the previous step 
        //  went ok)
        const TReaderID readerID = 
            iConnectionRegistry->Server()->ReaderID( *iParameters.iReaderName );

        //  If the reader can't be open when connecting, check it
        if ( iParameters.iNewReaderOnly )
            {
            if ( iConnectionRegistry->Server()->AccessRegistry()->
                ReaderHandlerLoaded( readerID ) )
                {
#ifdef _DEBUG
                RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
                    EFileLoggingModeAppend, 
                    _L( "CScardConnector::ConnectToReader Failed! Only new\
                    readers allowed!\n" ) );
#endif
                iConnectionRegistry->ConnectDone( this, 0, 
                    KScErrAlreadyExists );
                iConnectionRegistry->RemoveConnector( this );
                delete this;
                return;
                }
            }
        //  Create the access controller and the reader
        TBool opened( EFalse );
        TRAPD( readerError, opened = NewConnectionL( (*iClientMessage), 
            readerID ) );
        
        //  If the reader object could not be created, look no further, 
        //  we're in trouble
        if ( readerError != KErrNone )
            {
#ifdef _DEBUG
            RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
                EFileLoggingModeAppend, 
                _L( "CScardConnector::ConnectToReader NewConnectionL failed:\
                %d \n" ), readerError );
#endif
            iConnectionRegistry->ConnectDone( this, 0, readerError );
            iConnectionRegistry->RemoveConnector( this );
            delete this;
            return;
            }

        //  The reader was created successfully.

        //  1.  If the reader is open and if old cards are acceptable, 
        //  check conditions

        if ( opened && !iParameters.iNewCardOnly )
            {
            TScardATR atr;
            iConnections->At( 0 ).iReader->GetATR( atr );
            const TBool ok( CheckConditions( iConnections->At( 0 ), &atr ) );
            if ( ok )
                {
#ifdef _DEBUG
                RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
                    EFileLoggingModeAppend, 
                    _L( "CScardConnector::ConnectToReader: Connection\
                    succesfully established.\n" ) );
#endif
                ConnectionDone( iConnections->At( 0 ).iReaderID, KErrNone );
                return;
                }
#ifdef _DEBUG
            RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
                EFileLoggingModeAppend, 
                _L( "CScardConnector::ConnectToReader CheckConditions\
                failed! \n" ) );
#endif
            }

        //  2.  In other cases, just stay waiting for either the OpenReader 
        //  or condition checking to go through or for a card event to occur.
        return;
        }
    else
        {
        //  Contact (almost) all readers, dead or alive...
#ifdef _DEBUG
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,     
            EFileLoggingModeAppend, 
           _L("CScardConnector::ConnectToReader Connecting to all readers.\n") );
#endif
        CScardReaderRegistry* rr = 
            iConnectionRegistry->Server()->ReaderRegistry();

        //  First list the ones not yet opened
        CArrayFixFlat<TReaderID>* closelist = NULL;
        closelist = new( ELeave ) CArrayFixFlat<TReaderID>( 1 );
        CleanupStack::PushL( closelist );
        rr->ListClosedReadersL( closelist );

        //  then list the ones that are currently open (if necessary)
        CArrayPtrFlat<CScardAccessControl>* openlist = NULL;
        if ( !iParameters.iNewReaderOnly )
            {
            openlist = new( ELeave ) CArrayPtrFlat<CScardAccessControl>( 1 );
            CleanupStack::PushL( openlist );
            rr->ListOpenReadersL( openlist );
            }

        TReaderID excludeID( -1 );
        if ( iParameters.iExcluded )
            {
#ifdef _DEBUG
            RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,     
                EFileLoggingModeAppend, 
               _L( "CScardConnector::ConnectToReader Get ID of excluded\
               reader.\n" ) );
#endif
            excludeID = iConnectionRegistry->Server()->ReaderID( 
                *iParameters.iExcluded );
            }
        
        //  Attach to open readers
        if ( !iParameters.iNewReaderOnly )
            {
#ifdef _DEBUG
            RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,     
                EFileLoggingModeAppend, 
               _L( "CScardConnector::ConnectToReader Attach to open readers" ) );
#endif
            for ( TInt i( 0 ); i < openlist->Count(); i++ )
                {
                TReaderID next = openlist->At( i )->ReaderID();
                if ( next != excludeID )
                    {
                    TBool open( EFalse );
                    //  Create the connection
                    TRAPD( readerError, 
                        open = NewConnectionL( *iClientMessage, next ) );

                    //  If the reader is already open and old cards are 
                    //  acceptable, check SC for the parameters.

                    if ( !readerError && open && !iParameters.iNewCardOnly )
                        {
                        const TInt index = iConnections->Count() - 1;
                        TScardATR atr;
                        iConnections->At( index ).iReader->GetATR( atr );
                        TBool ok = CheckConditions( iConnections->At( 
                            iConnections->Count() - 1 ), &atr );
                        if ( ok )
                            {
                            //delete openlist & closelist
                            CleanupStack::PopAndDestroy( 2 ); 
                            ConnectionDone( next, KErrNone );
                            return;
                            }
                        }
                    else if ( open && iParameters.iNewCardOnly )
                        {
                        //delete openlist & closelist
                        CleanupStack::PopAndDestroy( 2 ); 
                        // old cards are not acceptable, return error
                        ConnectionDone( next, KScErrNotFound );
                        return;
                        }    
                    }
                }
            CleanupStack::PopAndDestroy( openlist );  //  delete openlist
            }
        
        //  attach to new readers
        for ( TInt i( 0 ); i < closelist->Count(); i++ )
            {
#ifdef _DEBUG
            RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,     
                EFileLoggingModeAppend, 
               _L( "CScardConnector::ConnectToReader Attach to new readers" ) );
#endif
            TReaderID next = closelist->At( i );
            if ( next != excludeID )
                {
                TInt err = KErrNone;
                TRAP( err, NewConnectionL( (*iClientMessage), next ) );
                }
            }

        CleanupStack::PopAndDestroy( closelist );  //  delete closelist
        }

    //  If no readers could be contacted at all ( == the handlers could not be 
    //  created) why continue?
    if ( !iConnections->Count() )
        {
        iState = EConnectionComplete;
        ConnectionDone( 0, KScErrNotFound );
        return;
        }

    if ( iConnections->Count() == 1 )
        {
        iOneReaderMode = ETrue;
        }
    }

// -----------------------------------------------------------------------------
// CScardConnector::CardEvent
// Handle card event
// -----------------------------------------------------------------------------
//
void CScardConnector::CardEvent(
    const TScardServiceStatus aEvent, 
    const TScardATR& aATR, 
    const TReaderID& aReaderID )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::CardEvent|Begin"));
    //  Card event handling is postponed if waiting for confirmation on
    //  a connection attempt
    if ( iState == EWaitingForConfirm )
        {
        iStack->QueueEvent( aReaderID, aEvent );
        return;
        }

    //  Find the connection handling the reader and see if it meets the 
    //  criteria
    TConnection* conn = NULL;
    TRAPD( err, conn = &( FindReaderConnectionL( aReaderID ) ) );

    //  The connection must be found...
    __ASSERT_ALWAYS( !err, User::Panic( _L( "Connector stack failure" ), 
        KScServerPanicInternalError ) );

    //  Now see what kind of event has occurred
    switch ( aEvent )
        {
        case EScardRemoved: // Flow through
        case EReaderRemoved:
            {
            break;
            }
        case EScardInserted:
            {
            //  A new card has been inserted, match it against the parameters
            const TBool ok = CheckConditions( *conn, &aATR );
            if ( ok )
                {
                ConnectionDone( conn->iReaderID, KErrNone );
                }
            break;
            }
        default:
            {
            break;
            }
        }
    }

// -----------------------------------------------------------------------------
// CScardConnector::ReadLimitsL
// Extract the limit parameters for this connection attempt
// -----------------------------------------------------------------------------
//
void CScardConnector::ReadLimitsL()
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::ReadLimitsL|Begin"));
    TInt limiters = (*iClientMessage).Int0();

    //  Check out the limiting factors
    
    //  The name of the reader is specified
    if ( limiters & KExplicitReader )
        {
        TInt desLen = (*iClientMessage).GetDesLength( 1 );
        HBufC* name = HBufC::NewL( desLen );
        CleanupStack::PushL( name );
        iParameters.iReaderName = new( ELeave ) TPtr( name->Des() );
        TRAPD( err, (*iClientMessage).ReadL( 1, *iParameters.iReaderName ) );
        if ( err != KErrNone )
            {
            (*iClientMessage).Panic( _L( "SCardServer" ), 
                KScServerPanicBadDescriptor );
            User::Leave( KScErrBadArgument );
            }
        CleanupStack::Pop( name );
        }

    //  The specified must not be contacted
    else if ( limiters & KExcludedReader )
        {
        TInt desLen = (*iClientMessage).GetDesLength( 1 );
        HBufC* bytes = HBufC::NewL( desLen );
        CleanupStack::PushL( bytes );
        iParameters.iExcluded = new( ELeave ) TScardReaderName( bytes->Des() );
        TRAPD( err, (*iClientMessage).ReadL( 1, *iParameters.iExcluded ) );
        if ( err != KErrNone )
            {
            (*iClientMessage).Panic( _L( "SCardServer" ), 
                KScServerPanicBadDescriptor );
            User::Leave( KScErrBadArgument );
            }
        CleanupStack::Pop( bytes );
        }

    //  Only newly inserted cards are acceptable
    if ( limiters & KNewReadersOnly )
        {
        iParameters.iNewReaderOnly = ETrue;
        }

    //  The ATR bytes of the card are spesified
    if ( limiters & KATRSpesified )
        {
        User::Leave( KScErrNotSupported );
        }

    //  The spesified application must be located within the SC
    else if ( limiters & KApplicationSpesified )
        {
        User::Leave( KScErrNotSupported );
        }

    //  Only newly inserted cards are acceptable
    if ( limiters & KNewCardsOnly )
        {
        iParameters.iNewCardOnly = ETrue;
        }

    //  Is there a time limit as well ?
    TInt32 timeOut = reinterpret_cast< TInt32 >( iClientMessage->Ptr3() );

    if ( timeOut )
        {
        iTimer = CScardConnectionTimer::NewL( this, timeOut );
        }
    }

// -----------------------------------------------------------------------------
// CScardConnector::CheckConditions
// Check if connection conditions are met.
// -----------------------------------------------------------------------------
//
TBool CScardConnector::CheckConditions(
    TConnection& /*aConnection*/, 
    const TScardATR* aATR )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::CheckConditions|Begin"));
    //  If no parameters are spesified, announce a match
    if ( !(iParameters.iATRBytes || iParameters.iAIDBytes ) )
        {
#ifdef _DEBUG
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
            EFileLoggingModeAppend, 
            _L( "CScardConnector::CheckConditions: no parameters\
            specified.\n" ) );
#endif

        iState = EConnectionComplete;
        return ETrue;
        }

    //  Now check the parameters one by one

    //  ATR match ?
    if ( iParameters.iATRBytes )
        {
        if ( aATR )
            {
            if ( *iParameters.iATRBytes == *aATR )
                {
#ifdef _DEBUG
                RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
                    EFileLoggingModeAppend, 
                    _L( "CScardConnector::CheckConditions: ATR match." ) );
#endif

                iState = EConnectionComplete;
                return ETrue;
                }
            }
#ifdef _DEBUG
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,     
            EFileLoggingModeAppend, 
            _L( "CScardConnector::CheckConditions: ATR mismatch!\n" ) );
#endif

        return EFalse;
        }

    //  The card has the requiered application ?
    else if ( iParameters.iAIDBytes )
        {
#ifdef _DEBUG
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,     
            EFileLoggingModeAppend, 
            _L( "CScardConnector::CheckConditions: AIDBytes not supported!\n" ) );
#endif
        return EFalse;
        }
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CScardConnector::NewConnectionL
// Create new connection to the reader.
// -----------------------------------------------------------------------------
//
TBool CScardConnector::NewConnectionL(
    const RMessage2 aMessage, 
    const TReaderID aReaderID )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::NewConnectionL|Begin"));
    TConnection conn;

    conn.iReaderID = aReaderID;

    //  Assign an access controller to this connection
    conn.iCtrl = iConnectionRegistry->Server()->AccessRegistry()->
        AccessController( aReaderID );
    
    //  Make sure there was enough memory for the access controller
    __ASSERT_MEMORY( !(conn.iCtrl) );

    //  attempt to create the connection's reader
    TRAPD( readerError, conn.iReader = conn.iCtrl->AttachSessionToReaderL( 
        this, conn.iSessionID ) );

    //  If creating the reader succeeds, attempt to initialise the reader
    if ( readerError == KErrNone )
        {
#ifdef _DEBUG    
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
            EFileLoggingModeAppend, 
            _L( "CScardConnector::NewConnectionL: Reader (ID: %d)\
            succesfully created.\n" ), aReaderID );
#endif
        const TBool ok( conn.iCtrl->InitialiseReader( conn.iSessionID, 
            aMessage ) );
        iConnections->AppendL( conn );
        return ok;
        }

#ifdef _DEBUG
    RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName,         
        EFileLoggingModeAppend, 
        _L( "CScardConnector::NewConnectionL: FAILED! error: %d\n" ),
        readerError );
#endif

    conn.iCtrl->DetachSessionFromReader( conn.iSessionID );
    User::Leave( readerError );
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CScardConnector::FindReaderConnectionL
// Find connection for given reader.
// -----------------------------------------------------------------------------
//
TConnection& CScardConnector::FindReaderConnectionL(
    const TReaderID& aReaderID )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::FindReaderConnectionL|Begin"));
    TInt count = iConnections->Count();
    for ( TInt i( 0 ); i < count; i++ )
        {
        if ( iConnections->At( i ).iReaderID == aReaderID )
            {
            return iConnections->At( i );
            }
        }
    User::Leave( KScErrNotFound );
    return iConnections->At( 0 ); //just to satisfy compiler
    }

// -----------------------------------------------------------------------------
// CScardConnector::ConnectionDone
// Connection has been established. Remove connector.
// -----------------------------------------------------------------------------
//
void CScardConnector::ConnectionDone(
    const TReaderID aReaderID, 
    const TInt& aErrorCode )
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::ConnectionDone|Begin"));
#ifdef _DEBUG
    RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
        EFileLoggingModeAppend, _L( "CScardConnector::ConnectionDone \
        status: %d \n" ), aErrorCode );
#endif
    if ( aErrorCode != KErrNone )
        {
        iState = EConnectionComplete;
        }
    if ( iTimer )
        {
#ifdef _DEBUG
        RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
            EFileLoggingModeAppend, _L( "CScardConnector::ConnectionDone Cancel\
            timer. \n" ) );
#endif
        iTimer->Cancel();
        }

    if ( aErrorCode == KScErrTimeOut )
        {
        iConnectionRegistry->Server()->
            FindAccessControl( aReaderID )->Cancel();
      
        }

    iConnectionRegistry->ConnectDone( this, aReaderID, aErrorCode );
    if ( iState == EConnectionComplete )
        {
        iConnectionRegistry->RemoveConnector( this );
        delete this;
        }
    }

// -----------------------------------------------------------------------------
// CScardConnector::Cancel
// Cancel connection. Call ConnectionDone with error code KScErrCancelled.
// -----------------------------------------------------------------------------
//
void CScardConnector::Cancel()
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::Cancel|Begin"));
    iState = EConnectionComplete;
    ConnectionDone( 0, KScErrCancelled );
    }

// -----------------------------------------------------------------------------
// CScardConnector::Message
// Return client message.
// -----------------------------------------------------------------------------
//
RMessage2& CScardConnector::Message()
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::Message|Begin"));
    return *iClientMessage;
    }

// -----------------------------------------------------------------------------
// CScardConnector::ConnectionTimedOut
// Timeout has occurred. Connectin done with KScErrTimeOut.
// -----------------------------------------------------------------------------
//
void CScardConnector::ConnectionTimedOut()
    {
    _WIMTRACE(_L("WIM|Scard|CScardConnector::ConnectionTimedOut|Begin"));
#ifdef _DEBUG
    RFileLogger::WriteFormat( KScardLogDir, KScardLogFileName, 
        EFileLoggingModeAppend, 
        _L( "CScardConnector::ConnectionTimedOut state=%d\n" ), iState );
#endif
    if ( iState == EWaitingForConfirm )
        {
        iState = ETimedOut;
        return;
        }
    else
        {
        iState = EConnectionComplete;
        ConnectionDone( iConnections->At( 0 ).iReaderID, KScErrTimeOut );
        }
    }

//  End of File  
