Added --no-write to gobble, fixed crash in start --timeout.
Also changed help to output one command per line (instead of columnizing) if not attached to a console. It's the same as what ls does.
// smswatch.cpp
//
// Copyright (c) 2008 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//
#include "smswatch.h"
#include <gsmumsg.h>
#include <gsmubuf.h>
#include <smsustrm.h>
#include "sms.h"
// __TEST_MODE__ provides a dummy autometric command
//#define __TEST_MODE__
#undef ASSERT
#define ASSERT(x) if (!(x)) { iParent.Printf(_L8("Assertion failed at line %d: " #x "\r\n"), __LINE__); PanicMe(__LINE__); }
// local static method definitions
static void PanicMe(TInt aError)
{
_LIT(KSmsWatcherPanic, "AmSms");
User::Panic(KSmsWatcherPanic, aError);
}
//
// CSmsWatcher
//
CSmsWatcher* CSmsWatcher::NewL(CCmdSms& aParent)
{
CSmsWatcher* self = new (ELeave) CSmsWatcher(aParent);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
CSmsWatcher::CSmsWatcher(CCmdSms& aParent)
: CActive(CActive::EPriorityStandard), iParent(aParent), iState(ESmsListenIdle)
{
CActiveScheduler::Add(this);
}
CSmsWatcher::~CSmsWatcher()
{
Cancel();
delete iMessage;
iCommand.Close();
if (iSocket.SubSessionHandle() > 0)
iSocket.Close();
if (iSocketServer.Handle() > 0)
iSocketServer.Close();
}
void CSmsWatcher::ConstructL()
{
// connect to the sms socket
User::LeaveIfError(iSocketServer.Connect());
User::LeaveIfError(iSocket.Open(iSocketServer,KSMSAddrFamily,KSockDatagram,KSMSDatagramProtocol));
iSmsAddr.SetSmsAddrFamily(ESmsAddrMatchText);
_LIT8(KShebang, "#!");
iSmsAddr.SetTextMatch(KShebang());
User::LeaveIfError(iSocket.Bind(iSmsAddr));
}
void CSmsWatcher::DoCancel()
{
switch(iState)
{
case ESmsListenReadSms:
case ESmsListenProcessSms:
case ESmsListenIdle: // This could happen when signalling failure via KIoctlReadMessageFailed
iSocket.CancelIoctl();
break;
default:
PanicMe(__LINE__);
}
}
void CSmsWatcher::RunL()
{
iErrorMessage.Zero();
if (iStatus.Int() < 0)
{
iErrorMessage.AppendFormat(_L("Error reported from CSmsWatcher::RunL"));
User::Leave(iStatus.Int()); // process any errors elsewhere
}
switch(iState)
{
case ESmsListenReadSms:
ReadSmsAndRespondToStackL(); // inform the stack we've got the sms
break;
case ESmsListenProcessSms:
ProcessCommandL(); // process the contents of the sms
WaitForMessage(); // Start listening for another message (RunError will call it if ProcessCommandL leaves)
break;
#ifdef __TEST_MODE__
case ESmsListenTestMode:
{
// a test mode skipping reception & decoding of an autometric sms
//_LIT(KTestMsg, "!#AUTOMETRIC;d:0243261943;?");
_LIT(KTestMsg, "#!fshell\ninfoprint \"hello world!\"\n");
ParseBufferL(KTestMsg, _L("012345678"));
}
break;
#endif
case ESmsListenIdle:
//CompleteWatch(KErrCompletion); // indicate an error with a prior event
break;
default:
PanicMe(KErrUnknown); // design fault
break;
}
}
//
// CSmsWatcher::RunError
// handle any probs with the state machine
//
TInt CSmsWatcher::RunError(TInt aError)
{
switch (iState)
{
case ESmsListenProcessSms:
// Problem such as bad message - log it and carry on
if (iErrorMessage.Length())
{
iParent.PrintError(aError, iErrorMessage);
}
else
{
iParent.PrintError(aError, _L("Error when processing SMS"));
}
WaitForMessage(); // Start listening for another message
break;
case ESmsListenReadSms:
{
// inform the stack we've failed to retrieve the sms. Likely due to OOM when calling PrepBuffersL
iSocket.Ioctl(KIoctlReadMessageFailed, iStatus, &iIoctlResult, KSolSmsProv);
SetActive();
iState = ESmsListenIdle;
}
break;
default:
PanicMe(__LINE__); // design fault
break;
};
return KErrNone;
}
//
// CSmsWatcher::WaitForMessage
// posts an async. read against the sms socket waiting for an incoming sms
//
void CSmsWatcher::WaitForMessage()
{
ASSERT(!IsActive());
#ifndef __TEST_MODE__
iIoctlResult = KSockSelectRead;
ASSERT(iSocket.SubSessionHandle() > 0);
iSocket.Ioctl(KIOctlSelect, iStatus, &iIoctlResult, KSOLSocket);
iState = ESmsListenReadSms;
#else
TRequestStatus* status = &iStatus;
*status = KRequestPending;
User::RequestComplete(status, KErrNone);
iState = ESmsListenTestMode;
#endif
SetActive();
}
//
// CSmsWatcher::ReadSmsAndRespondToStackL
// retrieve the sms and inform the stack whether we've got it
//
void CSmsWatcher::ReadSmsAndRespondToStackL()
{
// prep. buffers used to hold the message
PrepBuffersL();
// retrieve the sms from the stack
RSmsSocketReadStream readstream1(iSocket);
CleanupClosePushL(readstream1);
iMessage->InternalizeL(readstream1);
CleanupStack::PopAndDestroy();
// inform the stack we've got the sms
iSocket.Ioctl(KIoctlReadMessageSucceeded, iStatus, &iIoctlResult, KSolSmsProv);
iState = ESmsListenProcessSms;
SetActive();
}
//
// CSmsWatcher::ProcessCommandL
// having captured the sms that contains the command, slurp it out & process any instructions contained therein
//
void CSmsWatcher::ProcessCommandL()
{
ASSERT(iMessage);
ASSERT(iCommand.Length() == 0);
const TInt length = iMessage->Buffer().Length();
iCommand.CreateL(length);
iMessage->Buffer().Extract(iCommand, 0, length);
TPtrC address = iMessage->ToFromAddress();
ParseBufferL(iCommand, address);
}
//
// CSmsWatcher::PrepBuffersL
// trash the old buffer data, replace with virgin ones
//
void CSmsWatcher::PrepBuffersL()
{
delete iMessage;
iMessage = NULL;
iCommand.Close();
CSmsBuffer* buffer = CSmsBuffer::NewL();
CleanupStack::PushL(buffer);
iMessage = CSmsMessage::NewL(iParent.Fs(), CSmsPDU::ESmsDeliver, buffer);
CleanupStack::Pop(buffer); // iMessage takes ownership
}
//
// CSmsWatcher::ParseInstructionsL
// interpret the instructions received OTA
// eg: "#!fshell\necho Hello!\n"
//
void CSmsWatcher::ParseBufferL(const TDesC& aInstruction, const TDesC& aSenderAddress)
{
TBool accept = EFalse;
RPointerArray<HBufC>& matchStrings = iParent.MatchStrings();
if (matchStrings.Count()==0)
{
accept = ETrue;
}
else
{
for (TInt i=0; i<matchStrings.Count(); ++i)
{
Printf(_L("Sender %S matches %S: "), &aSenderAddress, matchStrings[i]);
if (aSenderAddress.Match(*matchStrings[i])!=KErrNotFound)
{
accept = ETrue;
}
}
}
if (!accept)
{
iErrorMessage.Zero();
iErrorMessage.AppendFormat(_L("Rejecting SMS from %S: did not match authorised sender"), &aSenderAddress);
User::Leave(KErrAccessDenied);
}
if (aInstruction.MatchF(_L("#!fshell*")) == 0 || aInstruction.MatchF(_L("#!perl*")) == 0)
{
CSmsShellCommand* command = CSmsShellCommand::NewL(iParent, aInstruction, aSenderAddress);
command->StartL();
}
else
{
TPtrC firstLine(aInstruction);
TInt pos = firstLine.Find(_L("\n"));
if (pos!=KErrNotFound)
{
firstLine.Set(firstLine.Left(pos));
if ((firstLine.Length()>0)&&(firstLine[firstLine.Length()-1]=='\r'))
{
firstLine.Set(firstLine.Left(firstLine.Length()-1));
}
}
iErrorMessage.Zero();
iErrorMessage.AppendFormat(_L("Unhandled shebang command from %S: %S"), &aSenderAddress, &firstLine);
User::Leave(KErrArgument);
}
}
//
// CSmsWatcher::Message
//
const TDesC& CSmsWatcher::Message()
{
return iCommand;
}