telephonyserverplugins/multimodetsy/Multimode/sms/smsbase.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 19 Aug 2010 11:03:36 +0300
branchRCL_3
changeset 61 17af172ffa5f
parent 0 3553901f7fa8
child 24 6638e7f4bd8f
permissions -rw-r--r--
Revision: 201033 Kit: 201033

// Copyright (c) 1997-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:
// AT Command Class for Sms
// Also contains member functions used by different ATCommands in the GsmTsy.
// 
//

#include "mSLOGGER.H"
#include "ATIO.H"
#include "ATSTD.H"
#include "mSMSSTOR.H"
#include "Matstd.h"
#include "smsbase.H"
#include "smsutil.h"	// for CATSmsUtils

#include <exterror.h>		// for KErrGsmSMS... error codes

// Constants

//
// Macros
//
// This macro is used to help keep un-intereting details outside of the main code
// in this file. The macro uses logging functions and always prefixes writes to the 
// log with the name of the class which is implemented in this file. This macro must
// only be used by code in this file.
#ifdef __LOGDEB__
_LIT8(KLogEntry,"CATSmsCommands::%S\t%S");
#define LOCAL_LOGTEXT(function,text) {_LIT8(F,function);_LIT8(T,text);LOGTEXT3(KLogEntry,&F,&T);}
#else
#define LOCAL_LOGTEXT(function,text)
#endif

//
// CATSmsCommands definitions
//

CATSmsCommands::CATSmsCommands(CATIO* aIo, CTelObject* aTelObject, CATInit* aInit,CPhoneGlobals* aGlobals)
	:CATCommands(aIo,aTelObject,aInit,aGlobals)
/**
 * C++ constructor
 */
 	{
	}

CATSmsCommands::~CATSmsCommands()
/**
 * C++ destructor
 */
 	{}

CATSmsCommands::TRequest CATSmsCommands::RequestATCompleted()
/**
 * This method is a query function which returns the TRequest value for a request which 
 * has completed, otherwise ENone is returned to denote that no request has completed.
 *
 * If this method returns that a request has completed then this method will also
 * reset the iRequestCompleted flag so that this method that the completion of a 
 * request is reported only once. 
 *
 * @return The TRequest value of the request that completed, otherwise ENone
 */
 	{
	LOCAL_LOGTEXT("RequestATCompleted","enter function");
	if(iRequestCompleted)
		{
		const TRequest tmp(iRequest);
		iRequest=ENone;
		iRequestCompleted=EFalse;
		return tmp;
		}

	return ENone;
	}

TBool CATSmsCommands::RequestATActive() const
	{
	LOCAL_LOGTEXT("RequestATActive","enter function");
	return iRequest!=ENone;
	}


void CATSmsCommands::RequestATCommandCancel()
/**
 */
	{
	LOCAL_LOGTEXT("RequestATCommandCancel","enter function");
	if(!RequestATActive())
		return;	// There's nothing to do if we're not active

	// Cancel operations
	LOCAL_LOGTEXT("RequestATCommandCancel","Cancelling...");
	iRequestCancel=ETrue;
	}

void CATSmsCommands::InitializeRequestData(TRequest aReq)
	{
	LOCAL_LOGTEXT("InitializeRequestData","enter function");

	iRequest=aReq;
	iRequestStage=0;	
	iRequestCompleted=EFalse;
	iRequestError=KErrNone;
	iRequestCancel=EFalse;
	}

TInt CATSmsCommands::RequestATCommand(TRequest aReq)
	{
	LOCAL_LOGTEXT("RequestATCommand","enter function");

	// Ensure we are not allready handling a request
	if(RequestATActive())
		{
		LOCAL_LOGTEXT("RequestATCommand","A request is already in progress, returning KErrInUse");
		return KErrInUse;
		}

	//Initialize request data
	InitializeRequestData(aReq);

	// Handle request 
	TInt ret(KErrNone);
	const TEventSource nullSource(TEventSource(-1)); 		// This fiddle is so we can pass a 'null' event source

	switch(aReq)
		{
	case EGetSCAFromPhone:
		ret=GetSCAFromPhone(nullSource);
		break;

	case ESetSCAInPhone:
		ret=SetSCAInPhone(nullSource);	
		break;

	case ESetPhoneToCMTMode:
		ret=SetPhoneToCMTMode(nullSource);
		break;

	case ESetPhoneToCMTIMode:
		ret=SetPhoneToCMTIMode(nullSource);
		break;

	case ENone:		// Must not be caught by default case
		LOCAL_LOGTEXT("RequestATCommand","Request is ENone, so will not do anything");
		break;
	
	default:
		LOCAL_LOGTEXT("RequestATCommand","Unknown request");
		__ASSERT_DEBUG(EFalse,Panic(EATSmsCommandsUnknownRequest));
		ret=KErrNotSupported;
		}

	LOCAL_LOGTEXT("RequestATCommand","exit function");
	return ret;
	}


TInt CATSmsCommands::SetSCAInPhone(const TEventSource& aEventSource)
/**
 * Sets phone's default SCA to be that which is stored in iRequestSCA.
 * IMPORTANT: If this method returns an error then EventSignal will complete our request.
 * @return Standard KErr... codes
 */
	{
	LOCAL_LOGTEXT("SetSCAInPhone","enter function");
	__ASSERT_DEBUG(iRequest==ESetSCAInPhone,Panic(EATSmsCommandsWrongRequest));

	TInt ret(KErrNone);
	switch(iRequestStage)
		{
	case EStage0:
		if(iRequestCancel)		// Handle request cancellation, if needed
			{
			LOCAL_LOGTEXT("SetSCAInPhone","Request has been cancelled");
			ret=KErrCancel;
			}
		else
			{
			// Set the SCA to use as the default in the phone's memory
			iTxBuffer.Copy(KSmsSetSCCommand);
			iTxBuffer.Append(KDblQuoteChar);
			const TInt count(iRequestSCA.iTelNumber.Length());
			for(TInt i=0;i<count;++i)
				{
				if(CATSmsUtils::IsAddressChar(TChar(iRequestSCA.iTelNumber[i])))
					iTxBuffer.Append(TChar(iRequestSCA.iTelNumber[i]));
				}
			iTxBuffer.Append(KDblQuoteChar);
			iTxBuffer.Append(KCommaChar);

			TUint val;
			CATSmsUtils::GetTypeOfAddressInDecimal(iRequestSCA,val);		
			iTxBuffer.AppendNum(val,EDecimal);

			iTxBuffer.Append(KCarriageReturn);
			iIo->Write(this,iTxBuffer);
			iIo->SetTimeOut(this,KATWriteTimeout);
			}
		break;
		
	case EStage1:
		HandleWriteCompletion(aEventSource);
		break;
	
	case EStage2:
		// If we did not get an error
		if(HandleResponseCompletion(aEventSource)==KErrNone)	
			CompleteRequest();
		else
			{
			if(iRequestCancel)		// Handle request cancellation, if needed
				{
				LOCAL_LOGTEXT("SetSCAInPhone","Request has been cancelled");
				ret=KErrCancel;
				}
			else
				{
				// An error occurred
				// Try to set SCA again, but this time don't use <tosca>
				// The Motorola Timeport needs this behaviour
				iTxBuffer.Copy(KSmsSetSCCommand);
				iTxBuffer.Append(KDblQuoteChar);

				// Prefix phone number with a '+' if it's international
				if(iRequestSCA.iTypeOfNumber==RMobilePhone::EInternationalNumber)
					iTxBuffer.Append(KPlusChar);

				const TInt count(iRequestSCA.iTelNumber.Length());
				for(TInt i=0;i<count;++i)
					{
					if(CATSmsUtils::IsAddressChar(TChar(iRequestSCA.iTelNumber[i])))
						iTxBuffer.Append(TChar(iRequestSCA.iTelNumber[i]));
					}
				iTxBuffer.Append(KDblQuoteChar);
				iTxBuffer.Append(KCarriageReturn);
				iIo->Write(this,iTxBuffer);
				iIo->SetTimeOut(this,KATWriteTimeout);
				}
			}
		break;

	case EStage3:
		HandleWriteCompletion(aEventSource);
		break;
	
	case EStage4:
		if((ret=HandleResponseCompletion(aEventSource))==KErrNone)
			{
			// We've finished the request
			CompleteRequest();
			}
		break;

	default:
		LOCAL_LOGTEXT("SetSCAInPhone","Unknown request stage");
		__ASSERT_DEBUG(EFalse,Panic(EATSmsCommandsUnknownRequestStage));
		ret=KErrGeneral;
		}

	++iRequestStage;		// Increment request stage for next iteration

	return ret;
	}


TInt CATSmsCommands::GetSCAFromPhone(const TEventSource& aEventSource)
/**
 * Gets phone's default SCA from phone and saves it in iRequestSCA.
 * IMPORTANT: If this method returns an error then EventSignal will complete our request.
 * @return Standard KErr... codes
 */
	{
	LOCAL_LOGTEXT("GetSCAFromPhone","enter function");
	__ASSERT_DEBUG(iRequest==EGetSCAFromPhone,Panic(EATSmsCommandsWrongRequest));
	
	TInt ret(KErrNone);
	switch(iRequestStage)
		{
	case EStage0:
		if(iRequestCancel)		// Handle request cancellation, if needed
			{
			LOCAL_LOGTEXT("GetSCAFromPhone","Request has been cancelled");
			ret=KErrCancel;
			}
		else
			{	
			// Initialize data
			iRequestSCA.iTypeOfNumber=RMobilePhone::EUnknownNumber;
			iRequestSCA.iNumberPlan=RMobilePhone::EUnknownNumberingPlan;
			iRequestSCA.iTelNumber.Zero();
	
			// Send SCA request to phone 
			iTxBuffer.Format(KServiceCentreQueryCommand);
			iIo->Write(this,iTxBuffer);
			iIo->SetTimeOut(this,KATWriteTimeout);
			}
		break;
		
	case EStage1:
		HandleWriteCompletion(aEventSource);
		break;
	
	case EStage2:
		ret=HandleResponseCompletion(aEventSource);
		if(ret!=KErrNone)
			break;

		// Did the phone have an SCA in default memory
		ret=ParseRxResultsForCSCAResponse(iRequestSCA);
		if(ret==KErrNone && iRequestSCA.iTelNumber.Length()>0)
			{
			// We've finished the request
			CompleteRequest();
			ret=KErrNone;
			}
		else
			{
			if(iRequestCancel)		// Handle request cancellation, if needed
				{
				LOCAL_LOGTEXT("GetSCAFromPhone","Request has been cancelled");
				ret=KErrCancel;
				}
			else
				{
				// Check if the phone has a SCA after doing a memory refresh
				iTxBuffer.Format(KServiceCentreQueryCommandWithCRES);
				iIo->Write(this,iTxBuffer);
				iIo->SetTimeOut(this,KATWriteTimeout);
				}
			}
		break;

	case EStage3:
		HandleWriteCompletion(aEventSource);
		break;

	case EStage4:
		ret=HandleResponseCompletion(aEventSource);
		if(ret!=KErrNone)
			break;

		// Did the phone have an SCA in default memory, if so initialise iRequestSCA
		ret=ParseRxResultsForCSCAResponse(iRequestSCA);
		if(ret==KErrNone)
			{
			// We've finished the request
			CompleteRequest();
			ret=KErrNone;
			}
		else
			{
			// We do not have an SCA and have exhausted all possibilities ;-(
			LOCAL_LOGTEXT("GetSCAFromPhone","Have not been able to find an SCA to use");

			//
			// We have to complete with KErrNone so that the second part of the GetSMSP 
			// retrieval IPC is called. If we do not do this the MSMSMESS file will get
			// it's iGetSMSPList array out of sync.	
			CompleteRequest();
			ret=KErrNone;
			}
		break;

	default:
		LOCAL_LOGTEXT("GetSCAFromPhone","Unknown request stage");
		__ASSERT_DEBUG(EFalse,Panic(EATSmsCommandsUnknownRequestStage));
		ret=KErrGeneral;
		}
			
	++iRequestStage;		// Increment request stage for next iteration

	return ret;
	}


TInt CATSmsCommands::SetPhoneToCMTMode(const TEventSource& aEventSource)
/**
 * Sets the phone into CMT mode by using 'AT+CNMI=x,2'
 * IMPORTANT: If this method returns an error then EventSignal will complete our request.
 * @return Standard KErr... codes
 */
	{
	LOCAL_LOGTEXT("SetPhoneToCMTMode","enter function");
	__ASSERT_DEBUG(iRequest==ESetPhoneToCMTMode,Panic(EATSmsCommandsWrongRequest));
	
	TInt ret(KErrNone);
	switch(iRequestStage)
		{
	case EStage0:
		if(iRequestCancel)		// Handle request cancellation, if needed
			{
			LOCAL_LOGTEXT("SetPhoneToCMTMode","Request has been cancelled");
			ret=KErrCancel;
			}
		else
			{	
			// Send 'AT+CNMI=?' to find out phones capabilities
			iTxBuffer.Format(KGetCNMIConfigCommand);
			iIo->Write(this,iTxBuffer);
			iIo->SetTimeOut(this,KATWriteTimeout);
			}
		break;
		
	case EStage1:
		HandleWriteCompletion(aEventSource);
		break;
	
	case EStage2:
		{						// Curly brackets used to scope use of cnmiFirstValue
		//
		// If the phone returns ERROR then ignore the error and assume the 
		// CNMI first parameter should be 2	
		TInt cnmiFirstValue(ECnmiMode2);
		if(HandleResponseCompletion(aEventSource)==KErrNone)
			{
			// Parse the response from the phone
			TRAP(ret,ParseCNMIFirstCapabilityL(cnmiFirstValue));
			ret=KErrNone;		// Ignore errors from ParseCNMIFirstCapabilityL call
			}
				
		// Send 'AT+CNMI=x,2' to phone to set it to CMT mode
		iTxBuffer.Format(KSmsSetCMTMode,cnmiFirstValue);
		iIo->Write(this,iTxBuffer);
		iIo->SetTimeOut(this,KATWriteTimeout);
		}
		break;

	case EStage3:
		HandleWriteCompletion(aEventSource);
		break;

	case EStage4:
		ret=HandleResponseCompletion(aEventSource);
		if(ret==KErrNone)
			{
			// We have completed our request
			CompleteRequest();
			}
		break;

	default:
		LOCAL_LOGTEXT("SetPhoneToCMTMode","Unknown request stage");
		__ASSERT_DEBUG(EFalse,Panic(EATSmsCommandsUnknownRequestStage));
		ret=KErrGeneral;
		}
			
	++iRequestStage;		// Increment request stage for next iteration

	return ret;
	}


TInt CATSmsCommands::SetPhoneToCMTIMode(const TEventSource& aEventSource)
/**
 * Sets the phone into CMTI mode by using 'AT+CNMI=x,1'
 * IMPORTANT: If this method returns an error then EventSignal will complete our request.
 * @return Standard KErr... codes
 */
	{
	LOCAL_LOGTEXT("SetPhoneToCMTIMode","enter function");
	__ASSERT_DEBUG(iRequest==ESetPhoneToCMTIMode,Panic(EATSmsCommandsWrongRequest));
	
	TInt ret(KErrNone);
	switch(iRequestStage)
		{
	case EStage0:
		if(iRequestCancel)		// Handle request cancellation, if needed
			{
			LOCAL_LOGTEXT("SetPhoneToCMTIMode","Request has been cancelled");
			ret=KErrCancel;
			}
		else
			{	
			// Send 'AT+CNMI=?' to find out phones capabilities
			iTxBuffer.Format(KGetCNMIConfigCommand);
			iIo->Write(this,iTxBuffer);
			iIo->SetTimeOut(this,KATWriteTimeout);
			}
		break;
		
	case EStage1:
		HandleWriteCompletion(aEventSource);
		break;
	
	case EStage2:
		{						// Curly brackets used to scope use of cnmiFirstValue
		//
		// If the phone returned ERROR then ignore the error and assume the 
		// CNMI first parameter should be 2	
		TInt cnmiFirstValue(2);
		if(HandleResponseCompletion(aEventSource)==KErrNone)
			{
			// Parse the response from the phone
			TRAP(ret,ParseCNMIFirstCapabilityL(cnmiFirstValue));
			ret=KErrNone;		// Ignore errors from ParseCNMIFirstCapabilityL call
			}
				
		// Send 'AT+CNMI=x,1' to phone to set it to CMTI mode
		iTxBuffer.Format(KSmsSetCMTIMode,cnmiFirstValue);
		iIo->Write(this,iTxBuffer);
		iIo->SetTimeOut(this,KATWriteTimeout);
		}
		break;

	case EStage3:
		HandleWriteCompletion(aEventSource);
		break;

	case EStage4:
		ret=HandleResponseCompletion(aEventSource);
		if(ret==KErrNone)
			{
			// We have completed our request
			CompleteRequest();
			}
		break;

	default:
		LOCAL_LOGTEXT("SetPhoneToCMTIMode","Unknown request stage");
		__ASSERT_DEBUG(EFalse,Panic(EATSmsCommandsUnknownRequestStage));
		ret=KErrGeneral;
		}
			
	++iRequestStage;		// Increment request stage for next iteration

	return ret;
	}



void CATSmsCommands::CompleteRequest(TInt aError)
/**
 * Common code for local AT request functions to complete a request from a derived class.
 * In the header file smsbase.h the default value for aError ir KErrNone.
 */
 	{
	LOCAL_LOGTEXT("CompleteRequest","enter function");
	iRequestStage=0;	
	iRequestCompleted=ETrue;
	iRequestError=aError;
	}

void CATSmsCommands::HandleWriteCompletion(TEventSource aSource)
/**
 * Common code for handling a write event from the CATIO class when communicating
 * with the modem.
 */
	{
	LOCAL_LOGTEXT("HandleWriteCompletion","enter function");
	__ASSERT_DEBUG(aSource==EWriteCompletion,Panic(EATCommand_IllegalCompletionWriteExpected));
	iIo->WriteAndTimerCancel(this);
	AddCmsErrorExpectString();
	StandardWriteCompletionHandler(aSource,KATResponseTimeout);
	}


TInt CATSmsCommands::HandleResponseCompletion(TEventSource aSource,TBool aValidateExpectString)
/**
 * Common code for handling a read event from the CATIO class when communicating
 * with the modem. Does some parsing of response to see if 'OK' or 'ERROR' was responded.
 */
	{
	LOCAL_LOGTEXT("HandleResponseCompletion","enter function");

	//
	// The below is an ASSERT ALWAYS just so that we do not get a WINSCW UREL warning.
	// The below only needs to be a ASSERT_DEBUG
	__ASSERT_ALWAYS(aSource==EReadCompletion,Panic(EATCommand_IllegalCompletionReadExpected));
	iIo->WriteAndTimerCancel(this);

	TInt ret(KErrNone);
	if(aValidateExpectString)
		ret=ValidateExpectString();		// This will only check for 'OK' or 'ERROR'

	if(ret!=KErrNone)
		{
		//
		// If we received some kind of error, see if the CMS ERROR stuff can provide
		// a more meanigful error code
		ret=ConvertCMSErrorToKErr(CMSErrorValue());
		}

	RemoveCmsErrorExpectString();
	RemoveStdExpectStrings();
	return ret;
	}

void CATSmsCommands::EventSignal(TEventSource aEventSource)
/**
 * Handles events for local AT commands
 * Will complete request if a handler returns anything other than KErrNone
 */
 	{
	LOCAL_LOGTEXT("EventSignal","enter function");
	TInt error(KErrNone);
	// Check if a timeout occurred	
	if(aEventSource==ETimeOutCompletion)
		{
		LOCAL_LOGTEXT("EventSignal","Timeout event has occurred");
		iIo->RemoveExpectStrings(this);
		iIo->WriteAndTimerCancel(this);
		error=KErrTimedOut;
		}
	else
		{
		// Dispatch event to appropriate request handler
		switch(iRequest)
			{
		case EGetSCAFromPhone:
			error=GetSCAFromPhone(aEventSource);
			break;

		case ESetSCAInPhone:
			error=SetSCAInPhone(aEventSource);
			break;

		case ESetPhoneToCMTMode:
			error=SetPhoneToCMTMode(aEventSource);
			break;

		case ESetPhoneToCMTIMode:
			error=SetPhoneToCMTIMode(aEventSource);
			break;

		case ENone:		// Must not be caught by default case	
			break;
		default:
			LOCAL_LOGTEXT("EventSignal","Unknown request");
			__ASSERT_DEBUG(EFalse,Panic(EATSmsCommandsUnknownRequest));
			}
		}

	if(error!=KErrNone)
		CompleteRequest(error);
	}


TBool CATSmsCommands::ComparePrefMem(const TStorageType& aName) const
	{
	LOCAL_LOGTEXT("ComparePrefMem","enter function");
	return iPhoneGlobals->iPhonePrefMem==aName;
	}

void CATSmsCommands::SetCurrentPrefMem(const TStorageType& aStorageName)
	{
	LOCAL_LOGTEXT("SetCurrentPrefMem","enter function");
	iTxBuffer.Format(KSetPrefMemCommandWithString,&aStorageName,&aStorageName); // Setting both the read and write memory to one and the same store(ME or SM).
	iIo->Write(this,iTxBuffer);
	iIo->SetTimeOut(this,KATWriteTimeout);
	}



TInt CATSmsCommands::ParseRxResultsForCSCAResponse(RMobilePhone::TMobileAddress& aTelNumber)
/**
 * Analyse rx results for a CSCA response and attempt to parse into provided tel.
 * The actual work is done in DoParseRxResultsForCSCAResponseL. Since this method
 * can't leave and ParseBufferLC() has to be called and ParseBufferLC() puts something
 * on the cleanup stack, the "Do" method had to be added.
 * @return Standard KErr... values
 */
	{
	LOCAL_LOGTEXT("ParseRxResultsForCSCAResponse","enter function");
	TInt ret(KErrNone);
	TRAPD(leaveCode,ret = DoParseRxResultsForCSCAResponseL(aTelNumber));
	if(leaveCode)
		return leaveCode;
	else
		return ret;
	}


TInt CATSmsCommands::DoParseRxResultsForCSCAResponseL(RMobilePhone::TMobileAddress& aTelNumber)
/**
 * Analyse rx results for a CSCA response and attempt to parse into provided tel
 * number object
 * @return Standard KErr... values
 */
	{
	LOCAL_LOGTEXT("DoParseRxResultsForCSCAResponseL","enter function");
	TInt ret(KErrNotFound);

	// Parse buffer
	ParseBufferLC();
	TDblQueIter<CATParamListEntry> iter(iRxResults);

	// Extract tokens from parser
	CATParamListEntry* cscaEntry=iter++;
	
	if((cscaEntry)&&(cscaEntry->iResultPtr == KCSCAResponseString))
		{
		CATParamListEntry* telNumberEntry=iter++;
		if (telNumberEntry)
			{
			//
			// Parse tokens into a TMobileAddress
			//
			// Type of number field <tosca> of +CSCA is optional so we have to accomodate for that.
			// If <tosca> was missing then we subsitute it with a dummy '000' <tosca>.
			_LIT8(KDummyNumberTypeEntry,"000");
			CATParamListEntry* numberTypeEntry=iter++;		
			if (!numberTypeEntry)
				ret=CATSmsUtils::CopyAddressStringToAddressStruct(telNumberEntry->iResultPtr,KDummyNumberTypeEntry,aTelNumber);
			else
				ret=CATSmsUtils::CopyAddressStringToAddressStruct(telNumberEntry->iResultPtr,numberTypeEntry->iResultPtr,aTelNumber);
			}
		}

	CleanupStack::PopAndDestroy();		// ParseBufferLC
	return ret;
	}


TInt CATSmsCommands::CMSErrorValue()
/*
 * Returns the error code decoded from the response string .eg '+CMS ERROR: xxx'.
 * This functions considers '+CMS ERROR: 500' and 'ERROR' as the error 500.
 * @return if there was an error the ETSI or KErr error value
 * @return if there was not an error then KErrNone
  */
 	{
	LOCAL_LOGTEXT("CMSErrorValue","enter function");
	// Locate error string
	iBuffer.Set(iIo->Buffer());
	TInt pos=iBuffer.FindF(KERRORResponseString);
	if (pos==KErrNotFound)
		return KErrNone;		// If we can't find the 'ERROR' string then that means there was not an error ;-)
	
	// Locate error value
	// (ie. read in all digits form the first found to the end of the string)
	const TInt length=iBuffer.Length();
	pos+=KCMGSResponseStringLength;
	while(pos<length && !(TChar(iBuffer[pos]).IsDigit()))
		pos++;
	
	TInt ret=500;		// 500 is the error value for 'undefined'
	if(pos<length) 	// Only decode error value if we found some digits
		{
		TLex8 lex(iBuffer.Mid(pos));
		TUint16 val;
		if(lex.Val(val,EDecimal)==KErrNone)
			ret=val;
		}
	
	LOGTEXT2(_L8("CATSmsCommands::CmsErrorValue\terror = %d"),ret);
	return ret;
	}


TInt CATSmsCommands::ConvertCMSErrorToKErr(const TInt& aCmsError) const 
	{
	LOCAL_LOGTEXT("ConvertCMSErrorToKErr","enter function");
	if(aCmsError==0)
		return KErrNone;

	return KErrGsmSmsBase-aCmsError;
	}


void CATSmsCommands::ProcessCapsElementL(TDblQueIter<CATParamListEntry>& aIter,TInt32& aCapsMask)
//
// Process a single element, i.e. a range or a bracketed list
//
	{
	TInt r=KErrNone;
_LIT8(KOpenBracket,  "(");
_LIT8(KCloseBracket, ")");
_LIT8(KDashChar,	 "-");

	CATParamListEntry* entry=aIter;
	if(!entry)
		return;

	aIter++;

	TBool listPresent=EFalse;					// Is there a list here?
	if(entry->iResultPtr==KOpenBracket)
		{
		listPresent=ETrue;
		entry=aIter++;
		if(!entry)
			return;
		}

// Process the element
	do
		{
// It might be a range, signified by a '-'
		TInt pos=entry->iResultPtr.Find(KDashChar);
		if(pos!=KErrNotFound)
			{
			TInt32 rangeStart;
			TLex8 rangeStartString(entry->iResultPtr.Left(pos));
			if((r=rangeStartString.Val(rangeStart))!=KErrNone)
				User::Leave(r);		

			TInt32 rangeEnd;
			TInt l;
			if((l=entry->iResultPtr.Length()-pos-1)<=0)
				User::Leave(KErrGeneral);
			TLex8 rangeEndString(entry->iResultPtr.Right(l));
			if((r=rangeEndString.Val(rangeEnd))!=KErrNone)
				User::Leave(r);

			if((rangeStart<0)||(rangeStart>31)||(rangeEnd<0)||(rangeEnd>31))
				User::Leave(KErrGeneral);

			for(TInt i=rangeStart;i<(rangeEnd+1);i++)
				aCapsMask|=(1<<i);
			}
// Or it might be the close bracket ')'
		else if(entry->iResultPtr==KCloseBracket)
			listPresent=EFalse;
// Or it might be a single value
		else
			{
			TInt32 value;
			TLex8 string(entry->iResultPtr);
			if((r=string.Val(value))!=KErrNone)
				User::Leave(r);

			if((value<0)||(value>31))
				User::Leave(KErrGeneral);

			aCapsMask|=(1<<value);
			}
		entry=aIter++;
		}
	while((listPresent)&&(entry!=NULL));
	aIter--;
	}


void CATSmsCommands::ParseCNMIFirstCapabilityL(TInt& aHighestValue)
	{
	ParseBufferLC(ETrue);
	TDblQueIter<CATParamListEntry> iter(iRxResults);
	CATParamListEntry* entry=iter++;

	if((entry==NULL)||(entry->iResultPtr!=KCNMIResponseString))
		{
		LOGTEXT(_L8("Error - Cannot find CNMI: string"));
		User::Leave(KErrNotFound);
		}

	TInt32 bitmap(0);
	ProcessCapsElementL(iter,bitmap);

	aHighestValue=ECnmiMode0;		// bitmap&0x01 denotes highest value of 0

	if(bitmap&KCapsCnmiMode2) 
		aHighestValue=ECnmiMode2;
	else if(bitmap&KCapsCnmiMode1)  // Mode 1 only set if mode 2 not supported
		aHighestValue=ECnmiMode1;
	else if(bitmap&KCapsCnmiMode3)  // Only set to mode 3 if this is the only mode supported
		aHighestValue=ECnmiMode3;

	CleanupStack::PopAndDestroy();		// ParseBufferLC object
	}