cbsatplugin/atmisccmdplugin/src/cmgwcommandhandler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 19 Aug 2010 10:05:41 +0300
branchRCL_3
changeset 54 0ba996a9b75d
permissions -rw-r--r--
Revision: 201031 Kit: 201033

/*
 * Copyright (c) 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 :
 *
 */

#include "cmgwcommandhandler.h"
#include <mmretrieve.h>
#include <mmlist.h> 
#include <exterror.h>

#include "atmisccmdpluginconsts.h"
#include "debug.h"

const TUint KEOT = 26;    // End of Transmission
const TUint KESC = 27;   // Escape

const TUint8 KSCATonBitMask = 0x70;
const TUint8 KSCANpiBitMask = 0x0F;

CCMGWCommandHandler* CCMGWCommandHandler::NewL(MATMiscCmdPlugin* aCallback, TAtCommandParser& aATCmdParser, RMobilePhone& aPhone)
    {
    TRACE_FUNC_ENTRY
    CCMGWCommandHandler* self = new (ELeave) CCMGWCommandHandler(aCallback, aATCmdParser, aPhone);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    TRACE_FUNC_EXIT
    return self;
    }

CCMGWCommandHandler::CCMGWCommandHandler(MATMiscCmdPlugin* aCallback, TAtCommandParser& aATCmdParser, RMobilePhone& aPhone) :
    CATCmdAsyncBase(aCallback, aATCmdParser, aPhone),
    iEntryPckg(iEntry)
    {
    TRACE_FUNC_ENTRY
    TRACE_FUNC_EXIT
    }

void CCMGWCommandHandler::ConstructL()
    {
    TRACE_FUNC_ENTRY
    iReply.CreateL(KDefaultCmdBufLength);
    iTPDU.CreateL(KDefaultCmdBufLength);
    
    User::LeaveIfError( iMobileSmsMessaging.Open(iPhone) );

    TInt err = iMobileSmsStore.Open(iMobileSmsMessaging, KETelIccSmsStore);
    if (err != KErrNone)
        {
        iState = ECMGWStateSimStoreNotSupported;
        }
    iRetrieveMobilePhoneSmspList = CRetrieveMobilePhoneSmspList::NewL(iMobileSmsMessaging);

    TRACE_FUNC_EXIT
    }

CCMGWCommandHandler::~CCMGWCommandHandler()
    {
    TRACE_FUNC_ENTRY
    Cancel();
    delete iRetrieveMobilePhoneSmspList;
    delete iMobilePhoneSmspList;
    iMobileSmsStore.Close();
    iMobileSmsMessaging.Close();
    iReply.Close();
    iTPDU.Close();
    TRACE_FUNC_EXIT
    }

/**
 * Set message format
 * 0: PDU mode
 * 1: Text mode - not supported
 */
void CCMGWCommandHandler::SetMessageFormat(TInt aFormat)
    {
    TRACE_FUNC_ENTRY
    if (aFormat == 0 || aFormat == 1)
        {
        iMsgFormat = aFormat;
        }
    Trace(_L("Message format: %d"), iMsgFormat);
    TRACE_FUNC_ENTRY
    }

void CCMGWCommandHandler::HandleCommand(const TDesC8& aCmd, RBuf8& /*aReply*/, TBool /*aReplyNeeded*/)
    {
    TRACE_FUNC_ENTRY
    
    if (iMsgFormat == 1 || iState == ECMGWStateSimStoreNotSupported )
        {
        // Reply "ERROR" if text mode is set
        // Reply "ERROR" if SIM store is not supported
        iCallback->CreateReplyAndComplete( EReplyTypeError );
        TRACE_FUNC_EXIT
        return;
        }
    
    TInt err = KErrNone;
    
    TAtCommandParser::TCommandHandlerType cmdHandlerType = iATCmdParser.CommandHandlerType();
    
    switch (cmdHandlerType)
        {
        case (TAtCommandParser::ECmdHandlerTypeTest):
            {
            iCallback->CreateReplyAndComplete( EReplyTypeOk );
            break;
            }
        case (TAtCommandParser::ECmdHandlerTypeSet): 
            {
            switch(iState)
                {
                case ECMGWStateIdle:
                    {
                    // Parse parameters
                    err = ParseParameters();
                    if (err == KErrNone)
                        {
                        Trace(_L("Parse parameters OK."));
                        Trace(_L("Length = %d"), iTPDULength);
                        Trace(_L("stat = %d"), iTPDUStat);
                        
                        iTPDU.Zero();
                        iState = ECMGWStateEditMode;
                        iCallback->CreateReplyAndComplete( EReplyTypeEditor );
                        }
                    else
                        {
                        // Syntax error
                        Trace(_L("Syntax error. err = %d"), err);
                        iState = ECMGWStateIdle;
                        iCallback->CreateReplyAndComplete(EReplyTypeError);
                        }
                    break;
                    }
                case ECMGWStateEditMode:   // Edit state
                    {
                    HandleEditModeCommand(aCmd);
                    break;
                    }
                default:    // Other states
                    {
                    Cancel();
                    iCallback->CreateReplyAndComplete( EReplyTypeError );
                    }
                }
            break;
            }
        default:
            {
            iCallback->CreateReplyAndComplete( EReplyTypeError );
            break;
            }
        }
    
    TRACE_FUNC_EXIT
    }

void CCMGWCommandHandler::HandleEditModeCommand( const TDesC8& aCmd )
    {
    TRACE_FUNC_ENTRY
    
    TInt err = KErrNone;
    TUint8 cmdCharVal = 0;
    if (aCmd.Length())
        {
        cmdCharVal = aCmd[0];
        }

    switch ( cmdCharVal ) 
        {
        case KEOT:  // End of Transmission: Now write the message
            {
            // Extract SCA fro PDU
            err = ExtractSCA();
            if (err == KErrNotFound)
                {
                // SCA not provided by client
                if (iMobileSmspEntry.iServiceCentre.iTelNumber.Length() == 0)
                    {
                    // Retrieve SMS parameter list
                    iRetrieveMobilePhoneSmspList->Start(iStatus);
                    iState = ECMGWStateRetrieveSCA;
                    SetActive();
                    }
                else
                    {
                    // Got the SCA from SIM params already - self complete
                    iServiceCentre = iMobileSmspEntry.iServiceCentre;
                    
                    TRequestStatus* status = &iStatus;
                    User::RequestComplete(status, KErrNone);
                    iState = ECMGWStatePreparePDU;
                    SetActive();
                    }
                }
            else if( err == KErrNone )
                {
                // Got the SCA from client (in iService Centre) - self complete
                TRequestStatus* status = &iStatus;
                User::RequestComplete(status, KErrNone);
                iState = ECMGWStatePreparePDU;
                SetActive();            
                }
            else
                {
                // Extract SCA failed
                iState = ECMGWStateIdle;
                iCallback->CreateCMSReplyAndComplete(KErrGsmSMSInvalidPDUModeParameter);
                }
             break;
            }
        case KESC:  // Escape
            {
            iState = ECMGWStateIdle;
            iCallback->CreateReplyAndComplete( EReplyTypeOk );
            break;
            }
        default:    // Still entering PDU data
            {
            iTPDU.Append( aCmd );
            iCallback->CreateReplyAndComplete( EReplyTypeEditor );
            break;
            }
        }
    
    TRACE_FUNC_EXIT
    }

void CCMGWCommandHandler::RunL()
    {
    TRACE_FUNC_ENTRY
    
    iReply.Zero();
    TInt err = iStatus.Int();
    Trace(_L("State = %d, err = %d"), iState, err);
    
    if (err == KErrNone)
        {
        switch (iState)
            {
            case ECMGWStateRetrieveSCA:
                {
                // Got SCA from SIM params - update iServiceCentre
                iMobilePhoneSmspList = iRetrieveMobilePhoneSmspList->RetrieveListL();
                iMobileSmspEntry = iMobilePhoneSmspList->GetEntryL(0);
                iServiceCentre = iMobileSmspEntry.iServiceCentre;
                
                // Complete self to send PDU in next state
                TRequestStatus* status = &iStatus;
                User::RequestComplete(status, KErrNone);
                iState = ECMGWStatePreparePDU;
                SetActive();
                }
                break;
            case ECMGWStatePreparePDU:
                {
                // Create an SMS entry from PDU
                iEntry.iServiceCentre = iServiceCentre;
                
                err = CreateSmsEntry();
                if (err == KErrNone)
                    {
                    Trace(_L("Create SMS entry OK."));
                    Trace(_L("Service center: %S"),
                            &iEntry.iServiceCentre.iTelNumber);
                    Trace(_L("Type of number: %d"),
                            iEntry.iServiceCentre.iTypeOfNumber);
                    Trace(_L("Number plan: %d"),
                            iEntry.iServiceCentre.iNumberPlan);
                    Trace(_L("Message status: %d"), iEntry.iMsgStatus);

                    // Start to write PDU
                    iEntry.iIndex = -1;
                    iMobileSmsStore.Write(iStatus, iEntryPckg);
                    iState = ECMGWStateWritePDU;
                    SetActive();
                    }
                else
                    {
                    // Create failed
                    iState = ECMGWStateIdle;
                    iCallback->CreateCMSReplyAndComplete(KErrGsmSMSInvalidPDUModeParameter);
                    }                
                break;
                }
            case ECMGWStateWritePDU:
                {
                Trace(_L("Write successful. Index = %d"), iEntry.iIndex);
                
                iReply.Append(KCRLF);
                iReply.Append(KAtCMGW);
                iReply.AppendNum(iEntry.iIndex);
                iState = ECMGWStateIdle;
                iCallback->CreateReplyAndComplete(EReplyTypeOk, iReply);
                }
                break;
            default:
                iState = ECMGWStateIdle;
                iCallback->CreateReplyAndComplete(EReplyTypeError);
                break;
            }
        }
    else
        {
        iState = ECMGWStateIdle;
        iCallback->CreateCMSReplyAndComplete(err);
        }
    
    TRACE_FUNC_EXIT
    }

TInt CCMGWCommandHandler::RunError(TInt aError)
    {
    TRACE_FUNC_ENTRY
    
    delete iMobilePhoneSmspList;
    iMobilePhoneSmspList = NULL;
    iState = ECMGWStateIdle;
    iCallback->CreateCMSReplyAndComplete(aError);
    
    TRACE_FUNC_EXIT
    return KErrNone;
    }

void CCMGWCommandHandler::DoCancel() 
    {
    TRACE_FUNC_ENTRY
    
    switch (iState)
        {
        case ECMGWStateRetrieveSCA:
            {
            iRetrieveMobilePhoneSmspList->Cancel();
            break;
            }
        case ECMGWStateWritePDU:
            {
            iMobileSmsStore.CancelAsyncRequest(EMobilePhoneStoreWrite);
            break;
            }
        }
    iState = ECMGWStateIdle;
    
    TRACE_FUNC_EXIT
    }
/**
 * Parse parameters of +CMGW=<length>,<stat>
 */
TInt CCMGWCommandHandler::ParseParameters()
    {
    TRACE_FUNC_ENTRY
    
    TInt ret = KErrNone;
    iTPDULength = 0;
    iTPDUStat = 0; // default value
    TInt otherParams = 0;
    // Get length
    TInt retLength = iATCmdParser.NextIntParam(iTPDULength);
    // Get status
    TInt retStat = iATCmdParser.NextIntParam(iTPDUStat);
    // Get other parameters
    TInt retOther = iATCmdParser.NextIntParam(otherParams);
    // syntax error happens if
    // a)there is no param 1
    // b)there are 3 params
    // c)param 2 is not 0,1,2 or 3
    TBool noParam1 = (retLength != KErrNone);
    TBool badParam2 = (retStat == KErrGeneral);
    TBool tooManyParams = (retOther != KErrNotFound);
    
    if (noParam1 || badParam2 || tooManyParams)
        {
        ret = KErrArgument;
        }
    else
        {
        switch (iTPDUStat)
            {
            case 0:
                // to receive unread message
                iMessageStatus = RMobileSmsStore::EStoredMessageUnread;
                break;
            case 1:
                // to receive read message
                iMessageStatus = RMobileSmsStore::EStoredMessageRead;
                break;
            case 2:
                // Unsent is not supported in this version
                ret = KErrNotSupported;
                break;
            case 3:
                // Sent is not supported in this version
                ret = KErrNotSupported;
                break;
            default:
                ret = KErrArgument;
                break;
            }
        iEntry.iMsgStatus = iMessageStatus;
        }
    
    TRACE_FUNC_EXIT
    return ret;
    }

/**
 * Create an SMS entry from the PDU string
 */
TInt CCMGWCommandHandler::CreateSmsEntry()
    {
    TRACE_FUNC_ENTRY
    
    TInt err = KErrNone;
    
    // Check the length
    if (iTPDU.Length() != (iSCALength+iTPDULength+1)*2)
        {
        TRACE_FUNC_EXIT
        return KErrArgument;
        }
    
    RBuf8 buf;
    err = buf.Create(iTPDULength);
    if (err != KErrNone)
        {
        TRACE_FUNC_EXIT
        return err;
        }
    // Convert to binary format
    for(TInt i=(iSCALength+1)*2; i< iTPDU.Length(); i+=2)
        {
        TLex8 lex(iTPDU.Mid(i, 2));
        TUint8 val = 0;
        err = lex.Val(val, EHex);
        if (err != KErrNone)
            {
            buf.Close();
            TRACE_FUNC_EXIT
            return err;
            }
        buf.Append(val);
        }
    iEntry.iMsgData.Copy(buf);
    
    buf.Close();
    TRACE_FUNC_EXIT
    return KErrNone;
    }

/**
 * Extract the SMS service center address from the head of PDU string
 */
TInt CCMGWCommandHandler::ExtractSCA()
    {
    TRACE_FUNC_ENTRY
    
    TInt err = KErrNone;
    TLex8 lex;
    RMobilePhone::TMobileAddress sca;
    // SCA length
    lex.Assign(iTPDU.Left(2));
    err = lex.Val(iSCALength, EHex); 
    if (err != KErrNone)
        {
        TRACE_FUNC_EXIT
        return err;
        }
    TInt length = iTPDU.Length();
    if (iSCALength == 0)
        {
        // Service center is not found in PDU
        err = KErrNotFound;
        }
    else if (iSCALength > (length-2)/2)
        {
        // Service certer length error
        err = KErrArgument;
        }
    else
        {
        // SCA is given
        // Parse SCA TON and NPI
        TUint8 val = 0;
        lex.Assign(iTPDU.Mid(2,2));
        err = lex.Val(val, EHex);
        if (err != KErrNone)
            {
            TRACE_FUNC_EXIT
            return err;
            }
        TUint8 ton = (val&KSCATonBitMask)>>4;
        TUint8 npi = val&KSCANpiBitMask;
        switch (ton) // TON
            {
            case 0: // 000
                sca.iTypeOfNumber = RMobilePhone::EUnknownNumber;
                break;
            case 1: // 001
                sca.iTypeOfNumber = RMobilePhone::EInternationalNumber;
                sca.iTelNumber.Append('+');
                break;
            case 2: // 010
                sca.iTypeOfNumber = RMobilePhone::ENationalNumber;
                break;
            default: 
                // CMCC doesn't support other types
                TRACE_FUNC_EXIT
                return KErrArgument;
            }
        switch (npi) // NPI
            {
            case 0: // 0000
                sca.iNumberPlan = RMobilePhone::EUnknownNumberingPlan;
                break;
            case 1: // 0001
                sca.iNumberPlan = RMobilePhone::EIsdnNumberPlan;
                break;
            default:
                // CMCC doesn't support other number plans
                TRACE_FUNC_EXIT
                return KErrArgument;
            }
        // Extract SCA number
        for (TInt i=4; i<(iSCALength+1)*2; i+=2)
            {
            sca.iTelNumber.Append(iTPDU[i+1]);
            sca.iTelNumber.Append(iTPDU[i]);
            }
        if(sca.iTelNumber[sca.iTelNumber.Length()-1] == 'F' 
                || sca.iTelNumber[sca.iTelNumber.Length()-1] == 'f')
            {
            sca.iTelNumber.Delete(sca.iTelNumber.Length()-1, 1);
            }
        iServiceCentre = sca;
        }
    TRACE_FUNC_EXIT
    return err;
    }