laforge
/
openbts-osmo
Archived
1
0
Fork 0

Initial USSD support commit.

This commit is contained in:
Ivan Kluchnikov 2010-08-10 17:04:04 +04:00
parent 5a969569c5
commit 3889999846
22 changed files with 2587 additions and 30 deletions

View File

@ -363,7 +363,17 @@ int sendrrlp(int argc, char** argv, ostream& os, istream& is)
return SUCCESS;
}
/** Send USSD to an IMSI. */
int sendUSSD(int argc, char** argv, ostream& os, istream& is)
{
if (argc!=2) return BAD_NUM_ARGS;
char *IMSI = argv[1];
GSM::L3MobileIdentity mobileIdentity(IMSI);
Control::MTTestHandler handler(mobileIdentity, (unsigned)1, (unsigned)0, Control::USSDData::REGrequest, std::string("REGrequest"));
handler.run();
os << "MT USSD session end." << endl;
return SUCCESS;
}
/** Print current usage loads. */
int printStats(int argc, char** argv, ostream& os, istream& is)
@ -762,6 +772,7 @@ Parser::Parser()
addCommand("findimsi", findimsi, "[IMSIPrefix] -- prints all imsi's that are prefixed by IMSIPrefix");
addCommand("sendsms", sendsms, "<IMSI> <src> -- send SMS to <IMSI>, addressed from <src>, after prompting.");
addCommand("sendrrlp", sendrrlp, "<IMSI> <hexstring> -- send RRLP message <hexstring> to <IMSI>.");
addCommand("sendussd", sendUSSD, "<IMSI> -- send USSD to <IMSI>");
addCommand("load", printStats, "-- print the current activity loads.");
addCommand("cellid", cellID, "[MCC MNC LAC CI] -- get/set location area identity (MCC, MNC, LAC) and cell ID (CI)");
addCommand("calls", calls, "-- print the transaction table");

View File

@ -95,6 +95,15 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
}
/** Wait for semaphore to be signaled with timeout.
* @returns 0 on success, -1 on error or timeout.
*/
int ThreadSemaphore::wait(unsigned timeout) const
{
Timeval then(timeout);
struct timespec waitTime = then.timespec();
return sem_timedwait(&mSem,&waitTime);
}
void Thread::start(void *(*task)(void*), void *arg)
{

View File

@ -29,6 +29,7 @@
#include <pthread.h>
#include <iostream>
#include <assert.h>
#include <semaphore.h>
class Mutex;
@ -120,7 +121,37 @@ class Signal {
};
/** Semaphore */
class ThreadSemaphore {
private:
mutable sem_t mSem;
public:
ThreadSemaphore(int pshared = 0, unsigned value = 0) { assert(sem_init(&mSem,pshared,value)!=-1); }
~ThreadSemaphore() { sem_destroy(&mSem); }
/** Wait for semaphore to be signaled with timeout.
* @returns 0 on success, -1 on error or timeout.
*/
int wait (unsigned timeout) const;
/** Wait for semaphore to be signaled infinitely.
* @returns 0 on success, -1 on error.
*/
int wait() const { return sem_wait(&mSem); }
/** Check if semaphore has been signaled and disarm it.
* @returns 0 if semaphore has been signaled, -1 in other cases.
*/
int trywait() const { return sem_trywait(&mSem); }
int post() { return sem_post (&mSem); }
};
#define START_THREAD(thread,function,argument) \
thread.start((void *(*)(void*))function, (void*)argument);

View File

@ -51,8 +51,26 @@ TransactionTable gTransactionTable;
TMSITable gTMSITable;
ostream& Control::operator<<(ostream& os, USSDData::USSDMessageType type)
{
switch (type) {
case USSDData::REGrequest: os << "register request"; break;
case USSDData::request: os << "request"; break;
case USSDData::notify: os << "notify"; break;
case USSDData::response: os << "response"; break;
case USSDData::error: os << "error"; break;
case USSDData::release: os << "release"; break;
default: os << "?" << (int)type << "?";
}
return os;
}
ostream& Control::operator<<(ostream& os, const USSDData& data)
{
os << "USSD message type: " << data.MessageType();
os << "USSD string: " << data.USSDString();
return os;
}
TransactionEntry::TransactionEntry()
:mID(gTransactionTable.newID()),
@ -123,6 +141,24 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
mMessage[0]='\0';
}
/** This form is used for USSD. */
TransactionEntry::TransactionEntry(const GSM::L3MobileIdentity& wSubscriber,
const GSM::L3CMServiceType& wService,
unsigned wTIFlag,
unsigned wTIValue,
USSDData* wUSSDData)
:mID(gTransactionTable.newID()),
mSubscriber(wSubscriber),mService(wService),
mTIFlag(wTIFlag),mTIValue(wTIValue),mUSSDData(wUSSDData),
mQ931State(NullState),
mT301(T301ms), mT302(T302ms), mT303(T303ms),
mT304(T304ms), mT305(T305ms), mT308(T308ms),
mT310(T310ms), mT313(T313ms),
mT3113(gConfig.getNum("GSM.T3113")),
mTR1M(GSM::TR1Mms)
{
mMessage[0]='\0';
}
bool TransactionEntry::timerExpired() const
@ -207,6 +243,8 @@ ostream& Control::operator<<(ostream& os, TransactionEntry::Q931CallState state)
case TransactionEntry::ReleaseRequest: os << "release request"; break;
case TransactionEntry::SMSDelivering: os << "SMS delivery"; break;
case TransactionEntry::SMSSubmitting: os << "SMS submission"; break;
case TransactionEntry::USSDworking: os << "USSD working"; break;
case TransactionEntry::USSDclosing: os << "USSD closing"; break;
default: os << "?" << (int)state << "?";
}
return os;
@ -572,7 +610,270 @@ void TMSITable::load(const char* filename)
fclose(fp);
}
unsigned USSDHandler::waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout = USSDHandler::infinitely)
{
TransactionEntry transaction;
// Wait for MS to signal that data is ready
gTransactionTable.find(mTransactionID, transaction);
if (timeout >= USSDHandler::infinitely)
{
if (transaction.ussdData()->waitMS()!=0)
{
LOG(ERROR) << "USSDDate semaphore returned error: " << errno;
return 2;
}
}
else if((timeout > USSDHandler::trywait) && (timeout < USSDHandler::infinitely))
{
//wait
if (transaction.ussdData()->waitMS(timeout)!=0)
{
LOG(DEBUG) << "USSDDate semaphore returned error or timeout";
return 2;
}
}
else
{
//trywait
if (transaction.ussdData()->trywaitMS()!=0)
{
LOG(DEBUG) << "USSDDate semaphore returned error";
return 2;
}
}
// Get data from MS and check for closing condition
gTransactionTable.find(mTransactionID, transaction);
if (transaction.Q931State() == Control::TransactionEntry::USSDclosing)
{
clearTransactionHistory(transaction);
LOG(DEBUG) << "USSD clearing....";
return 1;
}
*messageType = transaction.ussdData()->MessageType();
*USSDString = transaction.ussdData()->USSDString();
return 0;
}
void USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType, std::string USSDString)
{
if (USSDString.length()>183)
{
mString = USSDString;
mString.erase(mString.begin(), mString.begin()+181);
USSDString.erase(USSDString.begin()+181, USSDString.end());
USSDString+=">";
}
else mString = "";
TransactionEntry transaction;
gTransactionTable.find(mTransactionID, transaction);
transaction.ussdData()->MessageType(messageType);
transaction.ussdData()->USSDString(USSDString);
gTransactionTable.update(transaction);
transaction.ussdData()->postNW();
}
void *USSDHandler::runWrapper(void *pThis)
{
((USSDHandler*)pThis)->run();
return NULL;
}
void MOTestHandler::run()
{
LOG(DEBUG) << "USSD MO Test Handler RUN";
while(true)
{
Control::USSDData::USSDMessageType messageType;
std::string USSDString;
if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break;
if (USSDString == ">")
{
if (mString == "")
{
messageType = USSDData::release;
}
else
{
USSDString = mString;
messageType = USSDData::request;
}
}
else if (USSDString == "*100#")
{
USSDString = "handle response ";
messageType = USSDData::response;
}
else if(USSDString == "*101#")
{
USSDString = "handle request String objects are a special type of container, specifically designed to operate with sequences of characters. Unlike traditional c-strings, which are mere sequences of characters in a memory array, C++ string objects belong to a class with many built-in features to operate with strings in a more intuitive way and with some additional useful features common to C++ containers. The string class is an instantiation of the basic_string class template, defined in string as:";
messageType = USSDData::request;
}
else if(USSDString == "*1011#")
{
USSDString = "handle request";
messageType = USSDData::request;
}
else if(USSDString == "*102#")
{
USSDString = "handle notify";
messageType = USSDData::notify;
}
else if(USSDString == "*103#")
{
messageType = USSDData::release;
}
else if(USSDString == "*104#")
{
messageType = USSDData::error;
}
else
{
messageType = USSDData::release;
}
postUSSDData(messageType, USSDString);
}
}
void MOHttpHandler::run()
{
LOG(DEBUG) << "USSD MO Http Handler RUN";
while(true)
{
Control::USSDData::USSDMessageType messageType;
std::string USSDString;
if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break;
if (USSDString == ">")
{
if (mString == "")
{
messageType = USSDData::release;
}
else
{
USSDString = mString;
messageType = USSDData::request;
}
}
else if(USSDString == "*101#")
{
USSDString = "send command";
messageType = USSDData::request;
}
else
{
char command[2048];
sprintf(command,"wget -T 5 -q -O - \"http://%s/http/%s&to=%s&text=%s\"",
gConfig.getStr("USSD.HTTP.Gateway"),
gConfig.getStr("USSD.HTTP.AccessString"),
"server", USSDString.c_str());
LOG(NOTICE) << "MOUSSD: send HTTP sending with " << command;
// HTTP "GET" method with wget.
char mystring [182];
FILE* wget = popen(command,"r");
if (!wget) {
LOG(NOTICE) << "cannot open wget with " << command;
USSDString = "cannot open wget";
messageType = USSDData::release;
}
fgets (mystring , 182 , wget);
LOG(NOTICE) << "wget response " << mystring;
std::string tmpStr(mystring);
USSDString = tmpStr;
messageType = USSDData::request;
}
postUSSDData(messageType, USSDString);
}
}
void MOCLIHandler::run()
{
LOG(DEBUG) << "USSD MO CLI Handler RUN";
while(true)
{
Control::USSDData::USSDMessageType messageType;
std::string USSDString;
if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break;
if (USSDString == ">")
{
if (mString == "")
{
messageType = USSDData::release;
}
else
{
USSDString = mString;
messageType = USSDData::request;
}
}
else if(USSDString == "*101#")
{
USSDString = "send command";
messageType = USSDData::request;
}
else
{
const char* line;
line = USSDString.c_str();
std::ostringstream os;
std::istringstream is;
gParser.process(line, os, is);
LOG(INFO) << "Running line \"" << line << "\" returned result \"" << os.str() << "\"";
USSDString = os.str();
messageType = USSDData::request;
}
postUSSDData(messageType, USSDString);
}
}
void MTTestHandler::run()
{
LOG(DEBUG) << "USSD MT Test Handler RUN";
while(true)
{
Control::USSDData::USSDMessageType messageType;
std::string USSDString;
if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break;
if(messageType == USSDData::REGrequest)
{
USSDString = "REGrequest message";
}
else if(messageType == USSDData::response)
{
if (USSDString == "111")
{
USSDString = "release message";
messageType = USSDData::release;
}
else if (USSDString == "100")
{
USSDString = "request message";
messageType = USSDData::request;
}
else if (USSDString == "101")
{
messageType = USSDData::error;
}
else if (USSDString == "102")
{
USSDString = "notify message";
messageType = USSDData::notify;
}
}
else
{
USSDString = "release message";
messageType = USSDData::release;
}
postUSSDData(messageType, USSDString);
}
}
bool Control::waitForPrimitive(LogicalChannel *LCH, Primitive primitive, unsigned timeout_ms)
@ -728,7 +1029,53 @@ void Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH
}
}
unsigned Control::USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue,Control::USSDData::USSDMessageType messageType, string ussdString, bool MO)
{
TransactionEntry transaction(mobileIdentity, GSM::L3CMServiceType::SupplementaryService, TIFlag, TIValue, new USSDData(messageType));
gTransactionTable.add(transaction);
LOG(DEBUG) << "USSD Dispatcher";
transaction.ussdData()->USSDString(ussdString);
if (MO)
{
//MO
transaction.Q931State(Control::TransactionEntry::USSDworking);
transaction.ussdData()->postMS();
gTransactionTable.update(transaction);
std::string handleName = gConfig.getStr("USSD.Handler.MO");
Thread* thread = new Thread;
if (handleName=="HTTP")
{
MOHttpHandler* handler = new MOHttpHandler(transaction.ID());
thread->start((void*(*)(void*))USSDHandler::runWrapper, handler);
}
else if (handleName=="CLI")
{
MOCLIHandler* handler = new MOCLIHandler(transaction.ID());
thread->start((void*(*)(void*))USSDHandler::runWrapper, handler);
}
else if (handleName=="Test")
{
MOTestHandler* handler = new MOTestHandler(transaction.ID());
thread->start((void*(*)(void*))USSDHandler::runWrapper, handler);
}
else
{
MOTestHandler* handler = new MOTestHandler(transaction.ID());
thread->start((void*(*)(void*))USSDHandler::runWrapper, handler);
}
}
else
{
//MT
transaction.ussdData()->postNW();
transaction.Q931State(Control::TransactionEntry::Paging);
//gTransactionTable.add(transaction);
gTransactionTable.update(transaction);
LOG(DEBUG) << "USSD Start Paging";
gBTS.pager().addID(transaction.subscriber(),GSM::SDCCHType,transaction);
}
return transaction.ID();
}
// vim: ts=4 sw=4

View File

@ -145,7 +145,14 @@ void clearTransactionHistory(unsigned transactionID);
//@}
/**@name USSD */
//@{
/** MO USSD controller */
void MOUSSDController(const GSM::L3CMServiceRequest *req, GSM::LogicalChannel *LCH);
/** MTUSSD controller */
void MTUSSDController(TransactionEntry& transaction, GSM::LogicalChannel *LCH);
//@}
/**@name Functions for mobility manangement operations. */
//@{
@ -208,6 +215,8 @@ void MTSMSController(TransactionEntry& transaction,
GSM::LogicalChannel *LCH);
//@}
/** Create a new transaction entry and start paging. */
void initiateMTTransaction(TransactionEntry& transaction,
GSM::ChannelType chanType, unsigned pageTime);
@ -242,7 +251,6 @@ void resolveIMSI(GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
/**@ Paging mechanisms */
//@{
@ -365,8 +373,59 @@ void *PagerServiceLoopAdapter(Pager*);
//@} // paging mech
/**@name USSD data */
//@{
class USSDData {
public:
/** USSD message types based on GSM 03.09 */
enum USSDMessageType {
REGrequest,
request,
notify,
response,
error,
release
};
private:
ThreadSemaphore mSemWaitMS;
ThreadSemaphore mSemWaitNW;
USSDMessageType mType; // USSD message type
std::string mUSSDString; // USSD message string
public:
USSDData(const USSDMessageType& wType)
:mType(wType)
{}
void MessageType(USSDMessageType wType) { mType=wType; }
USSDMessageType MessageType() const { return mType; }
std::string USSDString() const { return mUSSDString; }
void USSDString(std::string wUSSDString) { mUSSDString = wUSSDString; }
int waitMS() { return mSemWaitMS.wait(); }
int waitNW() { return mSemWaitNW.wait(); }
int waitMS(unsigned timeout) { return mSemWaitMS.wait(timeout); }
int waitNW(unsigned timeout) { return mSemWaitNW.wait(timeout); }
int trywaitMS() { return mSemWaitMS.trywait(); }
int trywaitNW() { return mSemWaitNW.trywait(); }
int postMS() { return mSemWaitMS.post(); }
int postNW() { return mSemWaitNW.post(); }
};
std::ostream& operator<<(std::ostream& os, USSDData::USSDMessageType);
std::ostream& operator<<(std::ostream& os, const USSDData&);
//@}
/**@name Transaction Table mechanisms. */
//@{
@ -395,6 +454,8 @@ class TransactionEntry {
ReleaseRequest,
SMSDelivering,
SMSSubmitting,
USSDworking,
USSDclosing
};
private:
@ -412,6 +473,8 @@ class TransactionEntry {
Q931CallState mQ931State; ///< the GSM/ISDN/Q.931 call state
Timeval mStateTimer; ///< timestamp of last state change.
USSDData* mUSSDData; ///< USSD message data
char mMessage[256]; ///< text messaging payload
/**@name Timers from GSM and Q.931 (network side) */
@ -452,6 +515,13 @@ class TransactionEntry {
unsigned wTIValue,
const GSM::L3CallingPartyBCDNumber& wCalling);
/** This form is used for USSD. */
TransactionEntry(const GSM::L3MobileIdentity& wSubscriber,
const GSM::L3CMServiceType& wService,
unsigned wTIFlag,
unsigned wTIValue,
USSDData* wUSSDData);
/**@name Accessors. */
//@{
unsigned TIValue() const { return mTIValue; }
@ -488,6 +558,9 @@ class TransactionEntry {
Q931CallState Q931State() const { return mQ931State; }
void ussdData(USSDData* wUSSDData) { mUSSDData=wUSSDData; }
USSDData* ussdData() const { return mUSSDData; }
unsigned stateAge() const { return mStateTimer.elapsed(); }
/**@name Timer access. */
@ -695,7 +768,7 @@ class TMSITable {
/**
Find an IMSI in the table.
This is a log-time operation.
@param TMSI The TMSI to find.
@param TMSI The TMSI to find.post to
@return Pointer to c-string IMSI or NULL.
*/
const char* IMSI(unsigned TMSI) const;
@ -783,9 +856,10 @@ class ControlLayerException {
/** Thrown when the control layer gets the wrong message */
class UnexpectedMessage : public ControlLayerException {
public:
UnexpectedMessage(unsigned wTransactionID=0)
:ControlLayerException(wTransactionID)
UnexpectedMessage(unsigned wTransactionID=0, const GSM::L3Frame *pFrame=NULL)
:ControlLayerException(wTransactionID), mpFrame(pFrame)
{}
const GSM::L3Frame *mpFrame;
};
/** Thrown when recvL3 returns NULL */
@ -819,13 +893,7 @@ class Q931TimerExpired : public ControlLayerException {
:ControlLayerException(wTransactionID)
{}
};
//@}
} //Control
}
/**@addtogroup Globals */
@ -837,6 +905,93 @@ extern Control::TMSITable gTMSITable;
//@}
namespace Control {
unsigned USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString, bool MO);
class USSDHandler {
public:
enum USSDtimeout {
trywait = 0,
infinitely = 120000
};
private:
unsigned mTransactionID;
protected:
std::string mString;
public:
/** This form is used for MO USSD */
USSDHandler(unsigned wTransactionID)
:mTransactionID(wTransactionID),
mString("")
{}
/** This form is used for MT USSD */
USSDHandler(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString)
:mString("")
{
mTransactionID = USSDDispatcher(mobileIdentity, TIFlag, TIValue, messageType, ussdString, false);
}
/** Wait USSD data from MS. Return: 0 - successful, 1 - clear transaction, 2 - error or timeout */
unsigned waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout);
/** Post USSD data and update transaction with new USSDData (messageType and USSDString)*/
void postUSSDData( Control::USSDData::USSDMessageType messageType, std::string USSDString);
unsigned transactionID() { return mTransactionID; }
void transactionID(unsigned wTransactionID) { wTransactionID = mTransactionID; }
static void *runWrapper(void *pThis);
protected:
virtual void run() = 0;
};
class MOTestHandler : public USSDHandler {
public:
MOTestHandler(unsigned wTransactionID)
:USSDHandler(wTransactionID)
{}
void run();
};
class MOHttpHandler : public USSDHandler {
public:
MOHttpHandler(unsigned wTransactionID)
:USSDHandler(wTransactionID)
{}
void run();
};
class MOCLIHandler : public USSDHandler {
public:
MOCLIHandler(unsigned wTransactionID)
:USSDHandler(wTransactionID)
{}
void run();
};
class MTTestHandler : public USSDHandler {
public:
MTTestHandler(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString)
:USSDHandler(mobileIdentity,TIFlag, TIValue, messageType, ussdString)
{}
void run();
};
//@}
} //Control
#endif

View File

@ -102,14 +102,18 @@ void DCCHDispatchRR(const L3RRMessage* req, LogicalChannel *DCCH)
/** Example of a closed-loop, persistent-thread control function for the DCCH. */
void Control::DCCHDispatcher(LogicalChannel *DCCH)
{
const L3Message *message = NULL;
while (1) {
try {
// Wait for a transaction to start.
LOG(DEBUG) << "waiting for " << DCCH->type() << " ESTABLISH";
waitForPrimitive(DCCH,ESTABLISH);
// Pull the first message and dispatch a new transaction.
const L3Message *message = getMessage(DCCH);
LOG(DEBUG) << "received " << *message;
if (!message)
{
// Wait for a transaction to start.
LOG(DEBUG) << "waiting for " << DCCH->type() << " ESTABLISH";
waitForPrimitive(DCCH,ESTABLISH);
// Pull the first message and dispatch a new transaction.
message = getMessage(DCCH);
LOG(DEBUG) << "received " << *message;
}
// Each protocol has it's own sub-dispatcher.
switch (message->PD()) {
case L3MobilityManagementPD:
@ -120,9 +124,12 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH)
break;
default:
LOG(NOTICE) << "unhandled protocol " << message->PD() << " on " << DCCH->type();
delete message;
message = NULL;
throw UnsupportedMessage();
}
delete message;
message = NULL;
}
// Catch the various error cases.
@ -142,8 +149,16 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH)
catch (UnexpectedMessage except) {
clearTransactionHistory(except.transactionID());
LOG(NOTICE) << "UnexpectedMessage";
// Cause 0x62 means "message type not not compatible with protocol state".
DCCH->send(L3ChannelRelease(0x62));
if (except.mpFrame)
{
message = parseL3(*except.mpFrame);
delete except.mpFrame;
}
else
{
// Cause 0x62 means "message type not not compatible with protocol state".
DCCH->send(L3ChannelRelease(0x62));
}
}
catch (UnsupportedMessage except) {
clearTransactionHistory(except.transactionID());
@ -175,7 +190,4 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH)
}
}
// vim: ts=4 sw=4

View File

@ -30,12 +30,13 @@ noinst_LTLIBRARIES = libcontrol.la
libcontrol_la_SOURCES = \
CallControl.cpp \
SMSControl.cpp \
USSDControl.cpp \
ControlCommon.cpp \
MobilityManagement.cpp \
RadioResource.cpp \
DCCHDispatch.cpp \
CollectMSInfo.cpp \
RRLPQueryController.cpp
RRLPQueryController.cpp
# TODO - move CollectMSInfo.cpp and RRLPQueryController.cpp to RRLP directory.

View File

@ -92,7 +92,7 @@ CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
libcontrol_la_LIBADD =
am_libcontrol_la_OBJECTS = CallControl.lo SMSControl.lo \
am_libcontrol_la_OBJECTS = CallControl.lo SMSControl.lo USSDControl.lo\
ControlCommon.lo MobilityManagement.lo RadioResource.lo \
DCCHDispatch.lo CollectMSInfo.lo RRLPQueryController.lo
libcontrol_la_OBJECTS = $(am_libcontrol_la_OBJECTS)
@ -271,6 +271,7 @@ noinst_LTLIBRARIES = libcontrol.la
libcontrol_la_SOURCES = \
CallControl.cpp \
SMSControl.cpp \
USSDControl.cpp \
ControlCommon.cpp \
MobilityManagement.cpp \
RadioResource.cpp \
@ -344,6 +345,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RRLPQueryController.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RadioResource.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SMSControl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/USSDControl.Plo@am__quote@
.cpp.o:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \

View File

@ -64,6 +64,9 @@ void Control::CMServiceResponder(const L3CMServiceRequest* cmsrq, LogicalChannel
case L3CMServiceType::EmergencyCall:
EmergencyCall(cmsrq,DCCH);
break;
case L3CMServiceType::SupplementaryService:
MOUSSDController(cmsrq,DCCH);
break;
default:
LOG(NOTICE) << "service not supported for " << *cmsrq;
// Cause 0x20 means "serivce not supported".

View File

@ -279,6 +279,9 @@ void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel
case L3CMServiceType::MobileTerminatedShortMessage:
MTSMSController(transaction, DCCH);
return;
case L3CMServiceType::SupplementaryService:
MTUSSDController(transaction, DCCH);
return;
default:
// Flush stray MOC entries.
// There should not be any, but...

View File

@ -0,0 +1,405 @@
/**@file USSD Control (L3) */
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <GSMLogicalChannel.h>
#include <GSML3MMMessages.h>
#include "ControlCommon.h"
#include <Regexp.h>
#include "GSML3NonCallSSMessages.h"
#include "GSML3NonCallSSComponents.h"
#include "GSML3NonCallSSElements.h"
#include <algorithm>
using namespace std;
using namespace GSM;
using namespace Control;
#include "SIPInterface.h"
#include "SIPUtility.h"
#include "SIPMessage.h"
#include "SIPEngine.h"
using namespace SIP;
/** @file USSDControl.cpp
USSD string coding schema is described at GSM 02.90 5.1.2, 5.1.4
and GSM 02.30 4.5
TODO:
1) We should support Network -> MS USSD too.
BUGS:
1) *101# does not work with Siemens A52 (but does work with A65).
After phone receives "Send more data" it can't perform any more USSD
and can't reply to this request too. If you try, phone displays
"NOT EXECUTED". BUT when you switch it off afterwards, it sends
ReleaseComplete, so it seems it's stuck waiting for something from
network?
2) On Alcatel BG3 message sent with Notify disappears as soon as
phone receives our ReleaseComplete. Probably we should delay it
by a few seconds?
*/
L3Frame* getFrameUSSD (LogicalChannel *LCH, GSM::Primitive primitive=DATA)
{
L3Frame *retVal = LCH->recv(gConfig.getNum("USSD.timeout"),0);
if (!retVal) {
LOG(NOTICE) <<"TIME";
//throw ChannelReadTimeout();
retVal = NULL;
}
else if (retVal->primitive() != primitive) {
LOG(NOTICE) << "unexpected primitive, expecting " << primitive << ", got " << *retVal;
//throw UnexpectedPrimitive();
retVal = NULL;
}
else if ((retVal->primitive() == DATA) && (retVal->PD() != L3NonCallSSPD)) {
LOG(NOTICE) << "unexpected (non-USSD) protocol in frame " << *retVal;
//throw UnexpectedMessage(0, retVal);
retVal = NULL;
}
return retVal;
}
void USSDSend(string USSDString, unsigned InvokeID, unsigned TI, unsigned TIFlag, LogicalChannel* LCH, Control::USSDData::USSDMessageType messageType)
{
L3Frame Frame;
if (TIFlag) TIFlag = 0;
else TIFlag = 1;
if (messageType == Control::USSDData::REGrequest)
{
//SEND MT REGISTER, UnstructuredSSRequest
L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID),
L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSRequest));
ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str()));
L3NonCallSSRegisterMessage RegisterMessage(0, 0, ComponentPtr);
RegisterMessage.write(Frame);
LOG(DEBUG) << "Sending Register Message: " << RegisterMessage;
LCH->send(Frame);
}
else if (messageType == Control::USSDData::request)
{
//SEND MT Facility, UnstructuredSSRequest
L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID),
L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSRequest));
ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str()));
L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr);
FacilityMessage.write(Frame);
LOG(DEBUG) << "Sending Register Message: " << FacilityMessage;
LCH->send(Frame);
}
else if (messageType == Control::USSDData::response)
{
//send response
L3NonCallSSComponentReturnResult* ComponentPtr = new L3NonCallSSComponentReturnResult(L3NonCallSSInvokeID(InvokeID));
ComponentPtr->operationCode(L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::ProcessUnstructuredSSRequest));
ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str()));
L3NonCallSSReleaseCompleteMessage ReleaseCompleteMessage(TIFlag, TI);
ReleaseCompleteMessage.component(ComponentPtr);
ReleaseCompleteMessage.write(Frame);
LOG(DEBUG) << "Sending Release Complete Message with response: " << ReleaseCompleteMessage;
LCH->send(Frame);
//send L3 Channel Release
LOG(DEBUG) << "L3 Channel Release.";
LCH->send(L3ChannelRelease());
}
else if (messageType == Control::USSDData::request )
{
//send request
if(InvokeID == 0xff) InvokeID = 0;
else InvokeID++;
L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID),
L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSRequest));
ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str()));
L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr);
FacilityMessage.write(Frame);
LOG(DEBUG) << "Sending Facility Message with request: " << FacilityMessage;
LCH->send(Frame);
}
else if (messageType == Control::USSDData::notify)
{
//send notify
if(InvokeID == 0xff) InvokeID = 0;
else InvokeID++;
L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID),
L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSNotify));
ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str()));
L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr);
FacilityMessage.write(Frame);
LOG(DEBUG) << "Sending Facility Message with notify: " << FacilityMessage;
LCH->send(Frame);
}
else if (messageType == Control::USSDData::error)
{
//send error
L3NonCallSSComponentReturnError* ComponentPtr = new L3NonCallSSComponentReturnError(L3NonCallSSInvokeID(InvokeID),
L3NonCallSSErrorCode(GSM::L3NonCallSSErrorCode::SystemFailure));
L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr);
FacilityMessage.write(Frame);
LOG(DEBUG) << "Sending Facility Message with error System Failure: " << FacilityMessage;
LCH->send(Frame);
}
else if(messageType == Control::USSDData::release)
{
// NOTE: On some phones (e.g. Nokia 3310, Alcatel BG3) if this parameteris set to a non-empty string,
// when it is sent after Notify USSD, this string is shown on the screen after showing Notify
// message just for a second. I.e. it replaces (obscures) original Notify message. Thus we
// recommend to set this string to an empty string.
L3NonCallSSComponentReturnResult* ComponentPtr = new L3NonCallSSComponentReturnResult(L3NonCallSSInvokeID(InvokeID));
ComponentPtr->operationCode(L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::ProcessUnstructuredSSRequest));
ComponentPtr->parameters(L3NonCallSSParameters("release"));
L3NonCallSSReleaseCompleteMessage ReleaseCompleteMessage(TIFlag, TI);
if (TIFlag == 1)
{
ReleaseCompleteMessage.component(ComponentPtr);
}
ReleaseCompleteMessage.write(Frame);
LOG(DEBUG) << "Sending Release Complete Message: " << ReleaseCompleteMessage;
LCH->send(Frame);
//send L3 Channel Release
LOG(DEBUG) << "L3 Channel Release.";
LCH->send(L3ChannelRelease());
}
}
Control::USSDData::USSDMessageType USSDParse(L3NonCallSSMessage* Message, string* USSDString, unsigned* InvokeID )
{
L3Frame Frame;
Control::USSDData::USSDMessageType messageType = Control::USSDData::error;
// Low-level mesage flow is described at GSM 04.90 6.1.
// High-level message flow is described at GSM 03.90 6.2.
// Process REQUEST message
if (Message->MTI() == GSM::L3NonCallSSMessage::Register)
{
LOG(DEBUG) << "Received Register Message";
L3NonCallSSRegisterMessage* RegisterMessage = (L3NonCallSSRegisterMessage*)Message;
if (RegisterMessage->l3NonCallSSComponent()->NonCallSSComponentTypeTag()== GSM::L3NonCallSSComponent::Invoke)
{
LOG(DEBUG) << "Received Invoke Component";
L3NonCallSSComponentInvoke* Component;
Component = (L3NonCallSSComponentInvoke*)RegisterMessage->l3NonCallSSComponent();
*USSDString = Component->l3NonCallSSParameters().data();
LOG(DEBUG) << "Read USSD string";
*InvokeID = (unsigned)Component->invokeID().value();
messageType = Control::USSDData::request;
}
}
// Process RESPONSE message
else if (Message->MTI() == GSM::L3NonCallSSMessage::Facility)
{
LOG(DEBUG) << "Received Facility Message";
L3NonCallSSFacilityMessage* FacilityMessage = (L3NonCallSSFacilityMessage*)Message;
if (FacilityMessage->l3NonCallSSComponent()->NonCallSSComponentTypeTag()== GSM::L3NonCallSSComponent::ReturnResult)
{
LOG(DEBUG) << "Return Result Component";
L3NonCallSSComponentReturnResult* Component;
Component = (L3NonCallSSComponentReturnResult*)FacilityMessage->l3NonCallSSComponent();
if (Component->haveL3NonCallSSParameters()) *USSDString = Component->l3NonCallSSParameters().data();
else *USSDString = "";
*InvokeID = (unsigned)Component->invokeID().value();
messageType = Control::USSDData::response;
}
}
// Process RELEASE or ERROR
else // MTI == GSM::L3NonCallSSMessage::ReleaseComplete
{
L3NonCallSSReleaseCompleteMessage* ReleaseCompleteMessage = (L3NonCallSSReleaseCompleteMessage*)Message;
if (ReleaseCompleteMessage->haveL3NonCallSSComponent())
{
*InvokeID = ReleaseCompleteMessage->l3NonCallSSComponent()->invokeID().value();
messageType = Control::USSDData::release;
}
else
{
InvokeID = 0;
messageType = Control::USSDData::release;
}
*USSDString = "";
}
return messageType;
}
void Control::MOUSSDController(const L3CMServiceRequest *request, LogicalChannel *LCH)
{
assert(request);
assert(LCH);
LOG(DEBUG) << "Get L3 CM Service Request: " << *request;
L3MobileIdentity mobileIdentity = request->mobileIdentity();
LOG(DEBUG) << "mobileIdentity: "<<mobileIdentity;
LOG(DEBUG) << "Send L3 CM Service Accept";
LCH->send(L3CMServiceAccept());
//Get USSD frame from MS
LOG(DEBUG) << "Get USSD frame from MS";
L3Frame *USSDFrame = getFrameUSSD(LCH);
//Parse USSD frame
LOG(DEEPDEBUG) << "Parse USSD frame";
L3NonCallSSMessage* USSDMessage;
USSDMessage = parseL3NonCallSS(*USSDFrame);
LOG(INFO) << "USSD message:"<<*USSDMessage;
string USSDString = "";
unsigned InvokeID = 0;
Control::USSDData::USSDMessageType messageType = USSDParse(USSDMessage, &USSDString, &InvokeID);
unsigned transactionID = USSDDispatcher (mobileIdentity, USSDMessage->TIFlag(), USSDMessage->TIValue(), messageType, USSDString, true);
unsigned TI = USSDMessage->TIValue();
TransactionEntry transaction;
while(gTransactionTable.find(transactionID, transaction))
{
if (transaction.Q931State() == Control::TransactionEntry::USSDclosing)
{
break;
}
else if (transaction.ussdData()->waitNW()==0)
{
gTransactionTable.find(transactionID, transaction);
//SEND
USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 0, LCH, transaction.ussdData()->MessageType());
if((transaction.ussdData()->MessageType() == Control::USSDData::response)||
(transaction.ussdData()->MessageType() == Control::USSDData::release))
{
LOG(DEBUG) << "USSD waitMS response||relese";
transaction.Q931State(Control::TransactionEntry::USSDclosing);
}
else
{
//RECV
L3Frame *USSDFrame = getFrameUSSD(LCH);
if (USSDFrame == NULL)
{
USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 0, LCH, Control::USSDData::release);
transaction.Q931State(Control::TransactionEntry::USSDclosing);
}
else
{
//Parse USSD frame
L3NonCallSSMessage* USSDMessage = parseL3NonCallSS(*USSDFrame);
TI = USSDMessage->TIValue();
LOG(INFO) << "USSD message before PARSE:"<<*USSDMessage;
Control::USSDData::USSDMessageType messageType = USSDParse(USSDMessage, &USSDString, &InvokeID);
LOG(INFO) << "MO USSD message: "<< *USSDMessage << " MO USSD type: "<< messageType
<< " USSD string: " << USSDString << " InvokeID: " << InvokeID;
transaction.ussdData()->USSDString(USSDString);
transaction.ussdData()->MessageType(messageType);
delete USSDMessage;
}
delete USSDFrame;
}
gTransactionTable.update(transaction);
transaction.ussdData()->postMS();
}
}
}
void Control::MTUSSDController(TransactionEntry& transaction, LogicalChannel *LCH)
{
assert(LCH);
LOG(INFO) << "MTUSSD: transaction: "<< transaction;
LCH->transactionID(transaction.ID());
string USSDString = "";
unsigned InvokeID = 0;
unsigned TI = 0;
unsigned transactionID = transaction.ID();
transaction.Q931State(Control::TransactionEntry::USSDworking);
gTransactionTable.update(transaction);
while(gTransactionTable.find(transactionID, transaction))
{
if (transaction.Q931State() == Control::TransactionEntry::USSDclosing)
{
break;
}
else if (transaction.ussdData()->waitNW()==0)
{
gTransactionTable.find(transactionID, transaction);
//SEND
USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 1, LCH, transaction.ussdData()->MessageType());
if((transaction.ussdData()->MessageType() == Control::USSDData::response)||
(transaction.ussdData()->MessageType() == Control::USSDData::release))
{
LOG(DEBUG) << "USSD waitMS response||relese";
transaction.Q931State(Control::TransactionEntry::USSDclosing);
}
else
{
//RECV
L3Frame *USSDFrame = getFrameUSSD(LCH);
if (USSDFrame == NULL)
{
USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 1, LCH, Control::USSDData::release);
transaction.Q931State(Control::TransactionEntry::USSDclosing);
}
else
{
//Parse USSD frame
L3NonCallSSMessage* USSDMessage = parseL3NonCallSS(*USSDFrame);
TI = USSDMessage->TIValue();
Control::USSDData::USSDMessageType messageType = USSDParse(USSDMessage, &USSDString, &InvokeID);
LOG(INFO) << "MT USSD message: "<< *USSDMessage << "MT USSD type: "<< messageType
<< " USSD string: " << USSDString << " InvokeID: " << InvokeID;
transaction.ussdData()->MessageType(messageType);
transaction.ussdData()->USSDString(USSDString);
delete USSDMessage;
}
delete USSDFrame;
}
gTransactionTable.update(transaction);
transaction.ussdData()->postMS();
}
}
LOG(DEBUG) << "USSD session done.";
}

View File

@ -980,9 +980,10 @@ void L2LAPDm::sendIFrame(const BitVector& payload, bool MBit)
bool L2LAPDm::stuckChannel(const L2Frame& frame)
{
// Check for excessive idling.
if (frame.DCCHIdle()) mIdleCount++;
else mIdleCount=0;
return mIdleCount > maxIdle();
//if (frame.DCCHIdle()) mIdleCount++;
//else mIdleCount=0;
//return mIdleCount > maxIdle();
return false;
}

View File

@ -28,6 +28,7 @@
#include "GSML3RRMessages.h"
#include "GSML3MMMessages.h"
#include "GSML3CCMessages.h"
#include "GSML3NonCallSSMessages.h"
#include <Logger.h>
@ -148,6 +149,7 @@ GSM::L3Message* GSM::parseL3(const GSM::L3Frame& source)
case L3RadioResourcePD: retVal=parseL3RR(source); break;
case L3MobilityManagementPD: retVal=parseL3MM(source); break;
case L3CallControlPD: retVal=parseL3CC(source); break;
case L3NonCallSSPD: retVal=parseL3NonCallSS(source); break;
//case L3SMSPD: return parseSMS(source);
default:
LOG(NOTICE) << "L3 parsing failed for unsupported protocol " << PD;

View File

@ -0,0 +1,295 @@
/**@file Call independent Supplementary Service Control. Facility element components, GSM 04.80 3.6.1. */
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include "GSML3NonCallSSComponents.h"
#include <Logger.h>
using namespace std;
using namespace GSM;
ostream& GSM::operator<<(ostream& os, const L3NonCallSSComponent& msg)
{
msg.text(os);
return os;
}
ostream& GSM::operator<<(std::ostream& os, L3NonCallSSComponent::ComponentTypeTag val)
{
switch (val) {
case L3NonCallSSComponent::Invoke:
os << "Invoke"; break;
case L3NonCallSSComponent::ReturnResult:
os << "ReturnResult"; break;
case L3NonCallSSComponent::ReturnError:
os << "ReturnError"; break;
case L3NonCallSSComponent::Reject:
os << "Reject"; break;
default: os << hex << "0x" << (int)val << dec;
}
return os;
}
L3NonCallSSComponent * GSM::L3NonCallSSComponentFactory(L3NonCallSSComponent::ComponentTypeTag CTT)
{
switch (CTT) {
case L3NonCallSSComponent::Invoke: return new L3NonCallSSComponentInvoke();
case L3NonCallSSComponent::ReturnResult: return new L3NonCallSSComponentReturnResult();
case L3NonCallSSComponent::ReturnError: return new L3NonCallSSComponentReturnError();
case L3NonCallSSComponent::Reject: return new L3NonCallSSComponentReject();
default: {
//LOG(NOTICE) << "no L3 NonCallSSComponent factory support for message "<< CTT;
return NULL;
}
}
}
L3NonCallSSComponent * GSM::parseL3NonCallSSComponent (const L3Frame& source, size_t &rp)
{
L3NonCallSSComponent::ComponentTypeTag CTT = (L3NonCallSSComponent::ComponentTypeTag)source.readField(rp, 8);
LOG(DEBUG) << "Component Type Tag = "<<(int)CTT;
L3NonCallSSComponent *retVal = L3NonCallSSComponentFactory(CTT);
if (retVal==NULL) return NULL;
retVal->parse(source,rp);
LOG(DEBUG) << "parse L3 Non Call SS Component " << *retVal;
return retVal;
}
void L3NonCallSSComponent::write(L3Frame& dest, size_t &wp)
{
dest.writeField(wp,NonCallSSComponentTypeTag(),8);//Component type tag
writeBody(dest,wp);
}
void L3NonCallSSComponent::parse(const L3Frame& source, size_t &rp)
{
mComponentLength.parseV(source,rp);//ComponentLength
rp+=16;//InvokeID Tag and InvokeIDLength
mInvokeID.parseV(source, rp, 1);//InvokeID
parseBody(source, rp);
}
void L3NonCallSSComponent::text(ostream& os) const
{
os << " ComponentTypeTag = " << NonCallSSComponentTypeTag();
os << " InvokeID = " << mInvokeID;
}
void L3NonCallSSComponentInvoke::writeBody( L3Frame &dest, size_t &wp )
{
this->length();
mComponentLength.writeV(dest, wp);//ComponentLength
dest.writeField(wp,0x2,8);//InvokeID tag
dest.writeField(wp,0x1,8);//InvokeIDLength
mInvokeID.writeV(dest,wp,8);//InvokeID
if (mHaveLinkedID)
{
dest.writeField(wp,0x80,8);//LinkedID tag
dest.writeField(wp,0x1,8);//LinkedIDLength
mLinkedID.writeV(dest, wp, 8);//LinkedID
}
dest.writeField(wp,0x2,8);//Operation Cod tag
dest.writeField(wp,0x1,8);//Operation Cod length
mOperationCode.writeV(dest, wp); //Operation Cod
if(mHaveParameters)
{
mParameters.write(dest, wp);//Parameters
}
}
void L3NonCallSSComponentInvoke::parseBody( const L3Frame &source, size_t &rp )
{
if(source.readField(rp, 8) == 0x80)
{
rp+=8;//LinkedID tag and LinkedIDLength
mLinkedID.parseV(source, rp, 1);//LinkedID
mHaveLinkedID = true;
}
rp+=8;//OperationCodTag and OperationCodLength
mOperationCode.parseV(source, rp);//OperationCode
if((mComponentLength.value()>9)&&(source.readField(rp, 8) == 0x30))
{
rp-=8;
mParameters.parseV(source, rp);//Parameters
mHaveParameters = true;
}
}
size_t L3NonCallSSComponentInvoke::length()
{
uint64_t ComponentLength = 0x6;
if (mHaveLinkedID) { ComponentLength += 0x3; }
if(mHaveParameters) { ComponentLength += mParameters.length(); }
mComponentLength.setValue(ComponentLength);
return mComponentLength.value()+mComponentLength.lengthV()+0x1;
}
void L3NonCallSSComponentInvoke::text(std::ostream& os) const
{
L3NonCallSSComponent::text(os);
if (mHaveLinkedID) os << " LinkedID = " << mLinkedID;
os << " OperationCode = " << mOperationCode;
if (mHaveParameters) os << " Parameters = (" << mParameters<< ")";
}
void L3NonCallSSComponentReturnResult::writeBody( L3Frame &dest, size_t &wp )
{
this->length();
mComponentLength.writeV(dest, wp);//ComponentLength
dest.writeField(wp,0x2,8);//InvokeID tag
dest.writeField(wp,0x1,8);//InvokeIDLength
mInvokeID.writeV(dest,wp,8);//InvokeID
if(mHaveParameters)
{
dest.writeField(wp,0x30,8); //Sequence tag
mSequenceLength.writeV(dest, wp);//SequenceLength
dest.writeField(wp,0x2,8);//Operation Cod tag
dest.writeField(wp,0x1,8);//Operation Cod length
mOperationCode.writeV(dest, wp); //Operation Cod
mParameters.write(dest, wp);//Parameters
}
}
void L3NonCallSSComponentReturnResult::parseBody( const L3Frame &source, size_t &rp )
{
if ((mComponentLength.value() > 0x3)&&(source.readField(rp, 8) == 0x30))
{
mSequenceLength.parseV(source,rp);//SequenceLength
rp+=16;//OperationCodTag and OperationCodLength
mOperationCode.parseV(source, rp);//OperationCode
mParameters.parseV(source, rp);//Parameters
mHaveParameters = true;
}
}
size_t L3NonCallSSComponentReturnResult::length()
{
uint64_t ComponentLength = 0x3;
if(mHaveParameters)
{
mSequenceLength.setValue(mParameters.length()+0x3);
ComponentLength += mSequenceLength.value() + mSequenceLength.lengthV() + 0x1;
}
mComponentLength.setValue(ComponentLength);
return mComponentLength.value()+mComponentLength.lengthV()+0x1;
}
void L3NonCallSSComponentReturnResult::text(std::ostream& os) const
{
L3NonCallSSComponent::text(os);
if (mHaveParameters)
{
os << " OperationCode = " << mOperationCode;
os << " Parameters = (" << mParameters << ")";
}
}
void L3NonCallSSComponentReturnError::writeBody( L3Frame &dest, size_t &wp )
{
this->length();
mComponentLength.writeV(dest, wp);//ComponentLength
dest.writeField(wp,0x2,8);//InvokeID tag
dest.writeField(wp,0x1,8);//InvokeIDLength
mInvokeID.writeV(dest,wp,8);//InvokeID
dest.writeField(wp,0x2,8);//Error Cod tag
dest.writeField(wp,0x1,8);//Error Cod length
mErrorCode.writeV(dest, wp); //Error Cod
if(mHaveParameters)
{
mParameters.write(dest, wp);//Parameters
}
}
void L3NonCallSSComponentReturnError::parseBody( const L3Frame &source, size_t &rp )
{
rp+=16;//ErrorCodTag and ErrorCodLength
mErrorCode.parseV(source, rp);//ErrorCode
if((mComponentLength.value() > 0x6)&&(source.readField(rp, 8) == 0x30))
{
rp-=8;
mParameters.parseV(source, rp);//Parameters
mHaveParameters = true;
}
}
size_t L3NonCallSSComponentReturnError::length()
{
uint64_t ComponentLength = 0x6;
if(mHaveParameters) { ComponentLength += mParameters.length();}
mComponentLength.setValue(ComponentLength);
return mComponentLength.value()+mComponentLength.lengthV()+0x1;
}
void L3NonCallSSComponentReturnError::text(std::ostream& os) const
{
L3NonCallSSComponent::text(os);
os << " ErrorCode = " << mErrorCode;
if (mHaveParameters) os << " Parameters = " << mParameters;
}
void L3NonCallSSComponentReject::writeBody( L3Frame &dest, size_t &wp )
{
this->length();
mComponentLength.writeV(dest, wp);//ComponentLength
dest.writeField(wp,0x6,8);//ComponentLength
dest.writeField(wp,0x2,8);//InvokeID tag
dest.writeField(wp,0x1,8);//InvokeIDLength
mInvokeID.writeV(dest,wp,8);//InvokeID
mProblemCodeTag.writeV(dest, wp);//Problem Code Tag
dest.writeField(wp,0x1,8);//Problem Code length
mProblemCode.writeV(dest, wp); //Problem Code Cod
}
void L3NonCallSSComponentReject::parseBody( const L3Frame &source, size_t &rp )
{
mProblemCodeTag.parseV(source, rp);//ProblemCodeTag
rp+=8;//Problem Code length
mProblemCode.parseV(source, rp);//ProblemCode
}
size_t L3NonCallSSComponentReject::length()
{
mComponentLength.setValue(0x6);
return mComponentLength.value()+mComponentLength.lengthV()+0x1;
}
void L3NonCallSSComponentReject::text(std::ostream& os) const
{
L3NonCallSSComponent::text(os);
os << " ProblemCodeTag = " << mProblemCodeTag;
os << " ProblemCode = " << mProblemCode;
}

View File

@ -0,0 +1,270 @@
/**@file Call independent Supplementary Service Control. Facility element components, GSM 04.80 3.6.1. */
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GSML3NONCALLSSCOMPONENT_H
#define GSML3NONCALLSSCOMPONENT_H
#include "GSML3NonCallSSElements.h"
#include "assert.h"
namespace GSM {
/**
This is virtual base class for the facility element components, GSM 04.80 3.6.1.
*/
class L3NonCallSSComponent {
protected:
L3NonCallSSInvokeID mInvokeID;
L3NonCallSSLengthField mComponentLength;
public:
enum ComponentTypeTag {
Invoke = 0xa1,
ReturnResult = 0xa2,
ReturnError = 0xa3,
Reject = 0xa4
};
L3NonCallSSComponent (const L3NonCallSSInvokeID& wInvokeID = L3NonCallSSInvokeID())
:mInvokeID(wInvokeID),
mComponentLength(L3NonCallSSLengthField())
{}
/** Return the expected component body length in bytes */
virtual size_t length() = 0;
/** Return number of BITS needed to hold component. */
size_t bitsNeeded() { return 8*length(); }
/**
The parse() method reads and decodes L3 NonCallSS facility component bits.
This method invokes parseBody, assuming that the component header
has already been read.
*/
virtual void parse(const L3Frame& source, size_t &rp);
/**
Write component data bits into a BitVector buffer.
This method invokes writeBody.
*/
virtual void write(L3Frame& dest, size_t &wp);
/** Return the ComponentTypeTag. */
virtual ComponentTypeTag NonCallSSComponentTypeTag() const =0;
const L3NonCallSSInvokeID& invokeID() const { return mInvokeID; }
protected:
/**
Write the component body, a method defined in some subclasses.
*/
virtual void writeBody(L3Frame& dest, size_t &writePosition) =0;
/**
The parseBody() method starts processing at the first byte following the
invokeID field in the component, which the caller indicates with the
readPosition argument.
*/
virtual void parseBody(const L3Frame& source, size_t &readPosition) =0;
public:
/** Generate a human-readable representation of a component. */
virtual void text(std::ostream& os) const;
};
std::ostream& operator<<(std::ostream& os, L3NonCallSSComponent::ComponentTypeTag CTT);
/**
Parse facility component into its object type.
@param source The L3 bits.
@return A pointer to a new component or NULL on failure.
*/
L3NonCallSSComponent* parseL3NonCallSSComponent(const L3Frame& source, size_t &rp);
/**
A Factory function to return a L3NonCallSSComponent of the specified CTT.
Returns NULL if the CTT is not supported.
*/
L3NonCallSSComponent* L3NonCallSSComponentFactory(L3NonCallSSComponent::ComponentTypeTag CTT);
std::ostream& operator<<(std::ostream& os, const GSM::L3NonCallSSComponent& msg);
/** Invoke component GSM 04.80 3.6.1 Table 3.3 */
class L3NonCallSSComponentInvoke : public L3NonCallSSComponent {
private:
//Mandatory
L3NonCallSSOperationCode mOperationCode;
//Optionally
L3NonCallSSLinkedID mLinkedID;
L3NonCallSSParameters mParameters;
bool mHaveLinkedID;
bool mHaveParameters;
public:
L3NonCallSSComponentInvoke(
const L3NonCallSSInvokeID& wInvokeID = 1,
const L3NonCallSSOperationCode& wOperationCode = L3NonCallSSOperationCode())
:L3NonCallSSComponent(wInvokeID),
mOperationCode(wOperationCode),
mHaveLinkedID(false),
mHaveParameters(false)
{}
void linkedID (L3NonCallSSLinkedID LinkedID) {mLinkedID = LinkedID; mHaveLinkedID = true;}
void parameters (L3NonCallSSParameters Parameters) {mParameters = Parameters; mHaveParameters = true;}
const L3NonCallSSOperationCode& operationCode() const {return mOperationCode;}
const L3NonCallSSLinkedID& l3NonCallSSLinkedID() const {assert(mHaveLinkedID); return mLinkedID;}
const L3NonCallSSParameters& l3NonCallSSParameters() const {assert (mHaveParameters); return mParameters;}
bool haveL3NonCallSSLinkedID() const {return mHaveLinkedID;}
bool haveL3NonCallSSParameters() const {return mHaveParameters;}
ComponentTypeTag NonCallSSComponentTypeTag() const { return Invoke; }
void writeBody( L3Frame &dest, size_t &wp );
void parseBody( const L3Frame &src, size_t &rp );
size_t length();
void text(std::ostream&) const;
};
/**Return Result component GSM 04.80 3.6.1 Table 3.4 */
class L3NonCallSSComponentReturnResult : public L3NonCallSSComponent {
private:
//Optionally
L3NonCallSSOperationCode mOperationCode;
L3NonCallSSParameters mParameters;
L3NonCallSSLengthField mSequenceLength;
bool mHaveParameters;
public:
L3NonCallSSComponentReturnResult(const L3NonCallSSInvokeID& wInvokeID = 1)
:L3NonCallSSComponent(wInvokeID),
mSequenceLength(L3NonCallSSLengthField()),
mHaveParameters(false)
{}
void operationCode (L3NonCallSSOperationCode OperationCode) {mOperationCode = OperationCode;}
void parameters (L3NonCallSSParameters Parameters) {mParameters = Parameters; mHaveParameters = true;}
const L3NonCallSSOperationCode& l3NonCallSSOperationCode() const {return mOperationCode;}
const L3NonCallSSParameters& l3NonCallSSParameters() const {assert(mHaveParameters); return mParameters;}
bool haveL3NonCallSSParameters() const {return mHaveParameters;}
ComponentTypeTag NonCallSSComponentTypeTag() const { return ReturnResult; }
void writeBody( L3Frame &dest, size_t &wp );
void parseBody( const L3Frame &src, size_t &rp );
size_t length();
void text(std::ostream&) const;
};
/** Return Error component GSM 04.80 3.6.1 Table 3.5 */
class L3NonCallSSComponentReturnError : public L3NonCallSSComponent {
private:
//Mandatory
L3NonCallSSErrorCode mErrorCode;
//Optionally
L3NonCallSSParameters mParameters;
bool mHaveParameters;
public:
L3NonCallSSComponentReturnError(
const L3NonCallSSInvokeID& wInvokeID = 1,
const L3NonCallSSErrorCode& wErrorCode = L3NonCallSSErrorCode())
:L3NonCallSSComponent(wInvokeID),
mErrorCode(wErrorCode),
mHaveParameters(false)
{}
void parameters (L3NonCallSSParameters Parameters) {mParameters = Parameters; mHaveParameters = true;}
const L3NonCallSSErrorCode& errorCode() const {return mErrorCode;}
const L3NonCallSSParameters& l3NonCallSSParameters() const {assert(mHaveParameters); return mParameters;}
ComponentTypeTag NonCallSSComponentTypeTag() const { return ReturnError; }
bool haveL3NonCallSSParameters() const {return mHaveParameters;}
void writeBody( L3Frame &dest, size_t &wp );
void parseBody( const L3Frame &src, size_t &rp );
size_t length();
void text(std::ostream&) const;
};
/** Reject component GSM 04.80 3.6.1 Table 3.6 */
class L3NonCallSSComponentReject : public L3NonCallSSComponent {
private:
//Mandatory
L3NonCallSSProblemCodeTag mProblemCodeTag;
L3NonCallSSProblemCode mProblemCode;
public:
//Mandatory ProblemCodeTag and ProblemCode
L3NonCallSSComponentReject(
const L3NonCallSSInvokeID& wInvokeID = 1,
const L3NonCallSSProblemCodeTag& wProblemCodeTag = L3NonCallSSProblemCodeTag(),
const L3NonCallSSProblemCode& wProblemCode = L3NonCallSSProblemCode())
:L3NonCallSSComponent (wInvokeID),
mProblemCodeTag(wProblemCodeTag),
mProblemCode(wProblemCode)
{}
const L3NonCallSSProblemCodeTag& problemCodeTag() const {return mProblemCodeTag;}
const L3NonCallSSProblemCode& problemCode() const {return mProblemCode;}
ComponentTypeTag NonCallSSComponentTypeTag() const { return Reject; }
void writeBody( L3Frame &dest, size_t &wp );
void parseBody( const L3Frame &src, size_t &rp );
size_t length();
void text(std::ostream&) const;
};
}
#endif

View File

@ -0,0 +1,214 @@
/**@file Elements for call independent Supplementary Service Control, GSM 04.80 3. */
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GSML3NonCallSSElements.h"
#include <Logger.h>
using namespace std;
using namespace GSM;
void L3NonCallSSElementOctetLength::writeV( L3Frame& dest, size_t &wp ) const
{
dest.writeField(wp,mValue,8);
}
void L3NonCallSSElementOctetLength::parseV( const L3Frame& src, size_t &rp)
{
mValue = src.readField(rp, 8);
setValue();
}
size_t L3NonCallSSElementUndefLength::lengthV() const
{
size_t size = sizeof(mValue);
uint64_t tmp = 0;
do
{
size--;
tmp = mValue>>(size*8);
} while ((tmp == 0) &&(size>0));
size++;
return size;
}
void L3NonCallSSElementUndefLength::writeV( L3Frame& dest, size_t &wp ) const
{
dest.writeField(wp,mValue,lengthV()*8);
}
void L3NonCallSSElementUndefLength::writeV( L3Frame& dest, size_t &wp, size_t numBits) const
{
dest.writeField(wp,mValue,numBits);
}
void L3NonCallSSElementUndefLength::parseV( const L3Frame& src, size_t &rp, size_t numOctets)
{
mValue = src.readField(rp, numOctets*8);
}
void L3NonCallSSOperationCode::text(ostream& os) const
{
switch (mOperationCode){
case ProcessUnstructuredSSRequest:
os << "ProcessUnstructuredSSRequest"; break;
case UnstructuredSSRequest:
os << "UnstructuredSSRequest"; break;
case UnstructuredSSNotify:
os << "UnstructuredSSNotify"; break;
}
}
size_t L3NonCallSSLengthField::lengthV() const
{
size_t length = 0;
if (mValue <= 0x7f) length = 1;
else
{
uint64_t tmp = mValue;
do
{
length++;
tmp>>=8;
} while(tmp!=0);
length++;
}
return length;
}
void L3NonCallSSLengthField::writeV( L3Frame& dest, size_t &wp ) const
{
if (mValue <= 0x7f) { dest.writeField(wp,mValue,8); }
else
{
uint64_t tmp = mValue;
size_t numOctets = 0;
do
{
numOctets++;
tmp>>=8;
} while(tmp!=0);
dest.writeField(wp,numOctets|0x80,8);
dest.writeField(wp,mValue,numOctets*8);
}
}
void L3NonCallSSLengthField::parseV( const L3Frame& source, size_t &rp)
{
uint64_t tmp = source.readField(rp, 8);
if ((tmp>>7)==0) mValue = tmp;
else mValue = source.readField(rp, (tmp&0x7f)*8);
}
size_t L3NonCallSSParameters::length()
{
unsigned numChar = strlen(mData);
unsigned ussdStringLength = (numChar*7)/8;
if ((numChar*7)%8!=0) ussdStringLength +=1;
mDataLength.setValue(ussdStringLength);
unsigned sequenceLength = mDataLength.value()+mDataLength.lengthV()+4;
if (mHaveAlertingPattern) sequenceLength+=0x3;
mSequenceLength.setValue(sequenceLength);
return mSequenceLength.value()+mSequenceLength.lengthV()+1;
}
void L3NonCallSSParameters::parseV(const L3Frame& src, size_t& rp)
{
rp+=8;
mSequenceLength.parseV(src, rp);
rp+=16;
mDataCodingScheme = src.readField(rp,8);
rp+=8;
mDataLength.parseV(src, rp);
unsigned numChar = mDataLength.value()*8/7;
BitVector chars(src.tail(rp));
chars.LSB8MSB();
size_t crp=0;
for (int i=0; i<numChar; i++)
{
char gsm = chars.readFieldReversed(crp,7);
mData[i] = decodeGSMChar(gsm);
}
mData[numChar]='\0';
if (crp%8) crp += 8 - crp%8;
rp += crp;
if ((mSequenceLength.value()-0x4-mDataLength.lengthV()-mDataLength.value())!=0)
if(src.readField(rp,8)==0x4)
{
rp+=8;
mAlertingPattern = src.readField(rp,8);
mHaveAlertingPattern = true;
}
}
void L3NonCallSSParameters::write(L3Frame& dest, size_t& wp)
{
dest.writeField(wp,0x30,8); //Sequence Tag GSM 04.80 3.6.5.
unsigned numChar = strlen(mData);
unsigned ussdStringLength = (numChar*7)/8;
if ((numChar*7)%8!=0) ussdStringLength +=1;
mDataLength.setValue(ussdStringLength);
unsigned sequenceLength = mDataLength.value()+mDataLength.lengthV()+4;
if (mHaveAlertingPattern) sequenceLength+=0x3;
mSequenceLength.setValue(sequenceLength);
mSequenceLength.writeV(dest, wp); //Sequence Length
dest.writeField(wp,0x4,8); // ANS1 Octet String Tag
dest.writeField(wp,0x1,8); // String Length
dest.writeField(wp,mDataCodingScheme,8); // DataCodingScheme
dest.writeField(wp,0x4,8); // ANS1 Octet String Tag
mDataLength.writeV(dest, wp); // String Length
//USSD String
BitVector chars = dest.tail(wp);
chars.zero();
for (int i=0; i<numChar; i++) {
char gsm = encodeGSMChar(mData[i]);
dest.writeFieldReversed(wp,gsm,7);
}
chars.LSB8MSB();
if (mHaveAlertingPattern)
{
dest.writeField(wp,0x4,8); // ANS1 Octet String Tag
dest.writeField(wp,0x1,8); // String Length
dest.writeField(wp,mAlertingPattern,8); // Alerting Pattern
}
}
void L3NonCallSSParameters::text(ostream& os) const
{
os << " DataCodingScheme = " << hex << "0x" << mDataCodingScheme << dec;
os << " Data = " << mData;
if (mHaveAlertingPattern) os << " AlertingPattern = " << mAlertingPattern;
}

View File

@ -0,0 +1,367 @@
/**@file Elements for call independent Supplementary Service Control, GSM 04.80 3. */
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GSML3SSELEMENTS_H
#define GSML3SSELEMENTS_H
#include "GSML3Message.h"
#include <iostream>
namespace GSM {
/**
This a virtual class for L3 call independent Supplementary Service Control Elements
with length one octet.
*/
class L3NonCallSSElementOctetLength : public L3ProtocolElement {
protected:
uint64_t mValue;
virtual void setValue() = 0;
public:
L3NonCallSSElementOctetLength(uint64_t wValue = 0)
:L3ProtocolElement(),
mValue(wValue)
{}
size_t lengthV() const { return 1; }
void writeV( L3Frame& dest, size_t &wp) const;
void parseV(const L3Frame&, size_t&, size_t) { abort(); }
void parseV(const L3Frame& src, size_t& rp);
};
/**
This a virtual class for L3 call independent Supplementary Service Control Elements
with undefined length.
*/
class L3NonCallSSElementUndefLength : public L3ProtocolElement {
protected:
uint64_t mValue;
public:
L3NonCallSSElementUndefLength(uint64_t wValue = 0)
:L3ProtocolElement(),
mValue(wValue)
{}
size_t lengthV() const;
void writeV( L3Frame& dest, size_t &wp) const;
void writeV( L3Frame& dest, size_t &wp, size_t numOctets) const;
void parseV(const L3Frame&, size_t&, size_t expectedLength);
void parseV(const L3Frame& src, size_t& rp){ abort(); }
void text(std::ostream& os) const {os << mValue;}
uint64_t value() const { return mValue; }
};
/**
Classes for L3 call independent Supplementary Service Control Elements, GSM 04.80 3 and GSM 04.80 2.
*/
class L3NonCallSSComponentTypeTag : public L3NonCallSSElementOctetLength {
public:
/** GSM 04.80, Table 3.7 */
enum ComponentTypeTag {
Invoke = 0xa1,
ReturnResult = 0xa2,
ReturnError = 0xa3,
Reject = 0xa4
};
private:
ComponentTypeTag mComponentTypeTag;
void setValue () {mComponentTypeTag = (ComponentTypeTag)mValue;}
public:
L3NonCallSSComponentTypeTag(ComponentTypeTag wComponentTypeTag = Invoke)
:L3NonCallSSElementOctetLength(wComponentTypeTag),
mComponentTypeTag(wComponentTypeTag)
{}
void text(std::ostream& os) const {os << mComponentTypeTag;}
ComponentTypeTag NonCallSSComponentTypeTag() const {return mComponentTypeTag;}
};
class L3NonCallSSOperationCode : public L3NonCallSSElementOctetLength {
public:
enum OperationCode {
ProcessUnstructuredSSRequest = 0x3b,
UnstructuredSSRequest = 0x3c,
UnstructuredSSNotify = 0x3d
};
private:
OperationCode mOperationCode;
void setValue () {mOperationCode = (OperationCode)mValue;}
public:
L3NonCallSSOperationCode(OperationCode wOperationCode = ProcessUnstructuredSSRequest)
:L3NonCallSSElementOctetLength(wOperationCode),
mOperationCode(wOperationCode)
{}
void text(std::ostream& os) const;
OperationCode NonCallSSOperationCode() const {return mOperationCode;}
};
class L3NonCallSSErrorCode : public L3NonCallSSElementOctetLength {
public:
enum ErrorCode {
SystemFailure = 0x22,
DataMissing = 0x23,
UnexpectedDataValue = 0x24,
UnknownAlphabet = 0x47,
CallBarred = 0xd,
AbsentSubscriber = 0x1b,
IllegalSubscriber = 0x9,
IllegalEquipment = 0xc,
UssdBusy = 0x48
};
private:
ErrorCode mErrorCode;
void setValue () {mErrorCode = (ErrorCode)mValue;}
public:
L3NonCallSSErrorCode(ErrorCode wErrorCode = SystemFailure)
:L3NonCallSSElementOctetLength(wErrorCode),
mErrorCode(wErrorCode)
{}
void text(std::ostream& os) const {os << mErrorCode;}
ErrorCode NonCallSSErrorCode() const {return mErrorCode;}
};
class L3NonCallSSProblemCodeTag : public L3NonCallSSElementOctetLength {
public:
/** GSM 04.80, Table 3.13 */
enum ProblemCodeTag {
GeneralProblemTag = 0x80,
InvokeProblemTag = 0x81,
ReturnResultProblemTag = 0x82,
ReturnErrorProblemTag =0x83
};
private:
ProblemCodeTag mProblemCodeTag;
void setValue () {mProblemCodeTag = (ProblemCodeTag)mValue;}
public:
L3NonCallSSProblemCodeTag(ProblemCodeTag wProblemCodeTag = GeneralProblemTag)
:L3NonCallSSElementOctetLength(wProblemCodeTag),
mProblemCodeTag(wProblemCodeTag)
{}
void text(std::ostream& os) const {os << mProblemCodeTag;}
ProblemCodeTag NonCallSSProblemCodeTag() const {return mProblemCodeTag;}
};
class L3NonCallSSProblemCode : public L3NonCallSSElementOctetLength {
public:
/** GSM 04.80, Table 3.14 - 3.17 */
enum ProblemCode {
DuplicateInvokeID = 0x0,
UnrecognizedOperation = 0x1,
MistypedParameter = 0x2,
ResourceLimitation = 0x3,
InitiatingRelease = 0x4,
UnrecognizedLinkedID = 0x5,
LinkedResponseUnexpected = 0x6,
UnexpectedLinkedOperation = 0x7
};
private:
ProblemCode mProblemCode;
void setValue () {mProblemCode = (ProblemCode)mValue;}
public:
L3NonCallSSProblemCode(ProblemCode wProblemCode = DuplicateInvokeID)
:L3NonCallSSElementOctetLength(wProblemCode),
mProblemCode(wProblemCode)
{}
void text(std::ostream& os) const {os << mProblemCode;}
ProblemCode NonCallSSProblemCode() const {return mProblemCode;}
};
class L3NonCallSSVersionIndicator : public L3NonCallSSElementOctetLength {
public:
enum VersionIndicator {
Indicator1 = 0x0,
Indicator2 = 0x1
};
private:
VersionIndicator mVersionIndicator;
void setValue () {mVersionIndicator = (VersionIndicator)mValue;}
public:
L3NonCallSSVersionIndicator(VersionIndicator wVersionIndicator = Indicator1)
:L3NonCallSSElementOctetLength(wVersionIndicator),
mVersionIndicator(wVersionIndicator)
{}
void text(std::ostream& os) const {os << mVersionIndicator;}
VersionIndicator NonCallSSVersionIndicator() const {return mVersionIndicator;}
};
class L3NonCallSSInvokeID : public L3NonCallSSElementUndefLength {
public:
L3NonCallSSInvokeID (uint64_t wValue = 1)
:L3NonCallSSElementUndefLength(wValue)
{}
};
class L3NonCallSSLinkedID : public L3NonCallSSElementUndefLength {
public:
L3NonCallSSLinkedID (uint64_t wValue = 0)
:L3NonCallSSElementUndefLength(wValue)
{}
};
class L3NonCallSSCause : public L3NonCallSSElementUndefLength {
public:
L3NonCallSSCause (uint64_t wValue = 0)
:L3NonCallSSElementUndefLength(wValue)
{}
};
class L3NonCallSSLengthField : public L3ProtocolElement {
protected:
uint64_t mValue;
public:
L3NonCallSSLengthField(uint64_t wValue = 0)
:L3ProtocolElement(),
mValue(wValue)
{}
size_t lengthV() const;
void writeV( L3Frame& dest, size_t &wp) const;
void parseV(const L3Frame& src, size_t& rp);
void parseV(const L3Frame&, size_t&, size_t) { abort(); }
void text(std::ostream& os) const {os << mValue;}
uint64_t value() const { return mValue; }
void setValue (uint64_t value) {mValue = value;}
};
class L3NonCallSSParameters : public L3ProtocolElement {
private:
unsigned mDataCodingScheme; ///< data coding scheme
char mData[184]; ///< actual data, as a C string
L3NonCallSSLengthField mSequenceLength;
L3NonCallSSLengthField mDataLength;
unsigned mAlertingPattern;
bool mHaveAlertingPattern;
public:
L3NonCallSSParameters(unsigned wDataCodingScheme=0xF)
:L3ProtocolElement(),
mDataCodingScheme(wDataCodingScheme),
mSequenceLength(L3NonCallSSLengthField()),
mDataLength(L3NonCallSSLengthField()),
mAlertingPattern(0),
mHaveAlertingPattern(false)
{ mData[0]='\0'; }
/** Initize from a simple C string. */
L3NonCallSSParameters(const char* text)
:L3ProtocolElement(),
mDataCodingScheme(0xF),
mSequenceLength(L3NonCallSSLengthField()),
mDataLength(L3NonCallSSLengthField()),
mAlertingPattern(0),
mHaveAlertingPattern(false)
{ strncpy(mData,text,182);
mData[sizeof(mData) - 1] = '\0';}
void DataCodingScheme(unsigned wDataCodingScheme) { mDataCodingScheme=wDataCodingScheme; }
unsigned DataCodingScheme() { return mDataCodingScheme; }
void alertingPattern (unsigned AlertingPattern) {mAlertingPattern = AlertingPattern; mHaveAlertingPattern = true;}
const char* data() const { return mData; }
const unsigned& alertingPattern() const {assert(mHaveAlertingPattern); return mAlertingPattern;}
bool haveAlertingPattern() const {return mHaveAlertingPattern;}
size_t lengthV() const { abort(); }
size_t length();
void parseV(const L3Frame&, size_t&, size_t) { abort(); }
void parseV(const L3Frame&, size_t&);
void writeV(L3Frame&, size_t&) const { abort(); }
void write(L3Frame&, size_t&);
void text(std::ostream&) const;
};
}
#endif

View File

@ -0,0 +1,211 @@
/** @file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include "GSML3NonCallSSMessages.h"
#include <Logger.h>
using namespace std;
using namespace GSM;
ostream& operator<<(ostream& os, const L3NonCallSSMessage& msg)
{
msg.text(os);
return os;
}
ostream& GSM::operator<<(ostream& os, L3NonCallSSMessage::MessageType val)
{
switch (val) {
case L3NonCallSSMessage::ReleaseComplete:
os << "ReleaseComplete"; break;
case L3NonCallSSMessage::Facility:
os << "Facility"; break;
case L3NonCallSSMessage::Register:
os << "Register"; break;
default: os << hex << "0x" << (int)val << dec;
}
return os;
}
L3NonCallSSMessage * GSM::L3NonCallSSFactory(L3NonCallSSMessage::MessageType MTI)
{
LOG(DEBUG) << "Factory MTI"<< (int)MTI;
switch (MTI) {
case L3NonCallSSMessage::Facility: return new L3NonCallSSFacilityMessage();
case L3NonCallSSMessage::Register: return new L3NonCallSSRegisterMessage();
case L3NonCallSSMessage::ReleaseComplete: return new L3NonCallSSReleaseCompleteMessage();
default: {
//LOG(NOTICE) << "no L3 NonCallSS factory support for message "<< MTI;
return NULL;
}
}
}
L3NonCallSSMessage * GSM::parseL3NonCallSS(const L3Frame& source)
{
// mask out bit #7 (1011 1111) so use 0xbf
L3NonCallSSMessage::MessageType MTI = (L3NonCallSSMessage::MessageType)(0xbf & source.MTI());
LOG(DEBUG) << "MTI= "<< (int)MTI;
L3NonCallSSMessage *retVal = L3NonCallSSFactory(MTI);
if (retVal==NULL) return NULL;
retVal->TIValue(source.TIValue());
retVal->TIFlag(source.TIFlag());
retVal->parse(source);
LOG(DEBUG) << "parse L3 Non Call SS Message" << *retVal;
return retVal;
}
void L3NonCallSSMessage::write(L3Frame& dest) const
{
// We override L3Message::write for the transaction identifier.
size_t l3len = bitsNeeded();
if (dest.size()!=l3len) dest.resize(l3len);
size_t wp = 0;
dest.writeField(wp,mTIFlag,1);
dest.writeField(wp,mTIValue,3);
dest.writeField(wp,PD(),4);
dest.writeField(wp,MTI(),8);
writeBody(dest,wp);
}
void L3NonCallSSMessage::text(ostream& os) const
{
os << " MessageType = " <<(MessageType) MTI();
os << " TI = (" << mTIFlag << "," << mTIValue << ")";
}
void L3NonCallSSFacilityMessage::writeBody( L3Frame &dest, size_t &wp ) const
{
dest.writeField(wp,mL3NonCallSSComponent->length(),8);
mL3NonCallSSComponent->write(dest, wp);
}
void L3NonCallSSFacilityMessage::parseBody( const L3Frame &src, size_t &rp )
{
rp+=8;
mL3NonCallSSComponent = parseL3NonCallSSComponent(src, rp);
}
void L3NonCallSSFacilityMessage::text(ostream& os) const
{
L3NonCallSSMessage::text(os);
os << " L3NonCallSSComponent = (" << *mL3NonCallSSComponent << ")";
}
void L3NonCallSSRegisterMessage::writeBody( L3Frame &dest, size_t &wp ) const
{
dest.writeField(wp,0x1c,8);
dest.writeField(wp,mL3NonCallSSComponent->length(),8);
mL3NonCallSSComponent->write(dest, wp);
}
void L3NonCallSSRegisterMessage::parseBody( const L3Frame &src, size_t &rp )
{
rp+=16;
mL3NonCallSSComponent = parseL3NonCallSSComponent(src, rp);
if((src.length() > mL3NonCallSSComponent->length()+2) && (src.readField(rp,8) == 0x7f))
{
rp+=8;
mSSVersionIndicator.parseV(src, rp);
mHaveSSVersionIndicator = true;
}
}
size_t L3NonCallSSRegisterMessage::bodyLength() const
{
size_t bodyLength;
bodyLength = mL3NonCallSSComponent->length()+2;
if(mHaveSSVersionIndicator) bodyLength+=3;
return bodyLength;
}
void L3NonCallSSRegisterMessage::text(ostream& os) const
{
L3NonCallSSMessage::text(os);
os << " L3NonCallSSComponent = (" << *mL3NonCallSSComponent<< ")";
if(mHaveSSVersionIndicator) os << " L3SSVersionIndicator = " << mSSVersionIndicator;
}
void L3NonCallSSReleaseCompleteMessage::writeBody( L3Frame &dest, size_t &wp ) const
{
if(mHaveCause)
{
dest.writeField(wp,0x08,8);
dest.writeField(wp,mCause.lengthV(),8);
mCause.writeV(dest, wp);
}
if(mHaveL3NonCallSSComponent)
{
dest.writeField(wp,0x1c,8);
dest.writeField(wp,mL3NonCallSSComponent->length(),8);
mL3NonCallSSComponent->write(dest, wp);
}
}
void L3NonCallSSReleaseCompleteMessage::parseBody( const L3Frame &src, size_t &rp )
{
if (src.length()>2)
{
if(mCause.parseTLV(0x08, src,rp))
{
mHaveCause = true;
if ((src.length() > mCause.lengthTLV()+2)&&(src.readField(rp,8) == 0x1c))
{
rp+=8;
mL3NonCallSSComponent = parseL3NonCallSSComponent(src, rp);
mHaveL3NonCallSSComponent = true;
}
}
else if (src.readField(rp,8) == 0x1c)
{
rp+=8;
mL3NonCallSSComponent = parseL3NonCallSSComponent(src, rp);
mHaveL3NonCallSSComponent = true;
}
}
}
size_t L3NonCallSSReleaseCompleteMessage::bodyLength() const
{
size_t bodyLength = 0;
if(mHaveCause) bodyLength+=mCause.lengthTLV();
if(mHaveL3NonCallSSComponent) bodyLength+=mL3NonCallSSComponent->length()+2;
return bodyLength;
}
void L3NonCallSSReleaseCompleteMessage::text(ostream& os) const
{
L3NonCallSSMessage::text(os);
if(mHaveL3NonCallSSComponent) os << " L3NonCallSSComponent = (" << *mL3NonCallSSComponent << ")";
if(mHaveCause) os << " L3Cause = " << mCause;
}

View File

@ -0,0 +1,187 @@
/**@file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GSML3NONCALLSSMESSAGES_H
#define GSML3NONCALLSSMESSAGES_H
#include "GSMCommon.h"
#include "GSML3Message.h"
#include "GSML3NonCallSSElements.h"
#include "GSML3NonCallSSComponents.h"
namespace GSM {
/**
This a virtual class for L3 Messages for call independent Supplementary Service Control.
These messages are defined in GSM 04.80 2.2.
*/
class L3NonCallSSMessage : public L3Message {
private:
unsigned mTIValue; ///< short transaction ID, GSM 04.07 11.2.3.1.3.
unsigned mTIFlag; ///< 0 for originating side, see GSM 04.07 11.2.3.1.3.
public:
/** GSM 04.80, Table 3.1 */
enum MessageType {
/**@name Clearing message */
//@{
ReleaseComplete=0x2a,
//@}
/**@name Miscellaneous message group */
//@{
Facility=0x3a,
Register=0x3b
//@}
};
L3NonCallSSMessage(unsigned wTIFlag, unsigned wTIValue)
:L3Message(),mTIValue(wTIValue),mTIFlag(wTIFlag)
{}
/** Override the write method to include transaction identifiers in header. */
void write(L3Frame& dest) const;
L3PD PD() const { return L3NonCallSSPD; }
unsigned TIValue() const { return mTIValue; }
void TIValue(unsigned wTIValue) { mTIValue = wTIValue; }
unsigned TIFlag() const { return mTIFlag; }
void TIFlag(unsigned wTIFlag) { mTIFlag = wTIFlag; }
void text(std::ostream&) const;
};
std::ostream& operator<<(std::ostream& os, const GSM::L3NonCallSSComponent& msg);
std::ostream& operator<<(std::ostream& os, L3NonCallSSMessage::MessageType MTI);
/**
Parse a complete L3 call independent supplementary service control message into its object type.
@param source The L3 bits.
@return A pointer to a new message or NULL on failure.
*/
L3NonCallSSMessage* parseL3NonCallSS(const L3Frame& source);
/**
A Factory function to return a L3NonCallSSMessage of the specified MTI.
Returns NULL if the MTI is not supported.
*/
L3NonCallSSMessage* L3NonCallSSFactory(L3NonCallSSMessage::MessageType MTI);
/** Facility Message GSM 04.80 2.3. */
class L3NonCallSSFacilityMessage : public L3NonCallSSMessage {
private:
L3NonCallSSComponent* mL3NonCallSSComponent;
public:
L3NonCallSSFacilityMessage(unsigned wTIFlag = 0, unsigned wTIValue = 7,
L3NonCallSSComponent* wL3NonCallSSComponent = NULL)
:L3NonCallSSMessage(wTIFlag,wTIValue),
mL3NonCallSSComponent (wL3NonCallSSComponent)
{}
L3NonCallSSComponent* l3NonCallSSComponent() const {return mL3NonCallSSComponent;}
int MTI() const { return Facility; }
void writeBody( L3Frame &dest, size_t &wp ) const;
void parseBody( const L3Frame &src, size_t &rp );
size_t bodyLength() const {return mL3NonCallSSComponent->length()+1;}
void text(std::ostream& os) const;
};
/** Register Message GSM 04.80 2.4. */
class L3NonCallSSRegisterMessage : public L3NonCallSSMessage {
private:
L3NonCallSSComponent* mL3NonCallSSComponent;
L3NonCallSSVersionIndicator mSSVersionIndicator;
bool mHaveSSVersionIndicator;
public:
L3NonCallSSRegisterMessage(unsigned wTIFlag = 0, unsigned wTIValue = 7,
L3NonCallSSComponent* wL3NonCallSSComponent = NULL)
:L3NonCallSSMessage(wTIFlag,wTIValue),
mL3NonCallSSComponent (wL3NonCallSSComponent),
mHaveSSVersionIndicator(false)
{}
L3NonCallSSComponent* l3NonCallSSComponent() const {return mL3NonCallSSComponent;}
const L3NonCallSSVersionIndicator& SSVersionIndicator() const {assert(mHaveSSVersionIndicator); return mSSVersionIndicator;}
bool haveL3NonCallSSVersionIndicator() const {return mHaveSSVersionIndicator;}
int MTI() const { return Register; }
void writeBody( L3Frame &dest, size_t &wp ) const;
void parseBody( const L3Frame &src, size_t &rp );
size_t bodyLength() const;
void text(std::ostream&) const;
};
/** Release Complete Message GSM 04.80 2.5. */
class L3NonCallSSReleaseCompleteMessage : public L3NonCallSSMessage {
private:
L3NonCallSSComponent* mL3NonCallSSComponent;
bool mHaveL3NonCallSSComponent;
L3NonCallSSCause mCause;
bool mHaveCause;
public:
L3NonCallSSReleaseCompleteMessage(unsigned wTIFlag = 0, unsigned wTIValue = 7)
:L3NonCallSSMessage(wTIFlag,wTIValue),
mHaveL3NonCallSSComponent(false),
mHaveCause(false)
{}
void component (L3NonCallSSComponent* Component) {mL3NonCallSSComponent = Component; mHaveL3NonCallSSComponent = true;}
void cause (L3NonCallSSCause Cause) {mCause = Cause; mHaveCause = true;}
L3NonCallSSComponent* l3NonCallSSComponent() const {assert(mHaveL3NonCallSSComponent); return mL3NonCallSSComponent;}
bool haveL3NonCallSSComponent() const {return mHaveL3NonCallSSComponent;}
const L3NonCallSSCause& l3NonCallSSCause() const {assert(mHaveCause); return mCause;}
bool haveL3NonCallSSCause() const {return mHaveCause;}
int MTI() const { return ReleaseComplete; }
void writeBody( L3Frame &dest, size_t &wp ) const;
void parseBody( const L3Frame &src, size_t &rp );
size_t bodyLength() const;
void text(std::ostream&) const;
};
}
#endif

View File

@ -37,6 +37,9 @@ libGSM_la_SOURCES = \
GSML3Message.cpp \
GSML3MMElements.cpp \
GSML3MMMessages.cpp \
GSML3NonCallSSComponents.cpp \
GSML3NonCallSSElements.cpp \
GSML3NonCallSSMessages.cpp \
GSML3RRElements.cpp \
GSML3RRMessages.cpp \
GSMLogicalChannel.cpp \
@ -58,6 +61,9 @@ noinst_HEADERS = \
GSML3Message.h \
GSML3MMElements.h \
GSML3MMMessages.h \
GSML3NonCallSSComponents.h \
GSML3NonCallSSElements.h \
GSML3NonCallSSMessages.h \
GSML3RRElements.h \
GSML3RRMessages.h \
GSMLogicalChannel.h \

View File

@ -96,6 +96,7 @@ am_libGSM_la_OBJECTS = GSM610Tables.lo GSMCommon.lo GSMConfig.lo \
GSML1FEC.lo GSML2LAPDm.lo GSML3CCElements.lo \
GSML3CCMessages.lo GSML3CommonElements.lo GSML3Message.lo \
GSML3MMElements.lo GSML3MMMessages.lo GSML3RRElements.lo \
GSML3NonCallSSComponents.lo GSML3NonCallSSElements.lo GSML3NonCallSSMessages.lo \
GSML3RRMessages.lo GSMLogicalChannel.lo GSMSAPMux.lo \
GSMTDMA.lo GSMTransfer.lo GSMTAPDump.lo PowerManager.lo
libGSM_la_OBJECTS = $(am_libGSM_la_OBJECTS)
@ -282,6 +283,9 @@ libGSM_la_SOURCES = \
GSML3Message.cpp \
GSML3MMElements.cpp \
GSML3MMMessages.cpp \
GSML3NonCallSSComponents.cpp \
GSML3NonCallSSElements.cpp \
GSML3NonCallSSMessages.cpp \
GSML3RRElements.cpp \
GSML3RRMessages.cpp \
GSMLogicalChannel.cpp \
@ -303,6 +307,9 @@ noinst_HEADERS = \
GSML3Message.h \
GSML3MMElements.h \
GSML3MMMessages.h \
GSML3NonCallSSComponents.h \
GSML3NonCallSSElements.h \
GSML3NonCallSSMessages.h \
GSML3RRElements.h \
GSML3RRMessages.h \
GSMLogicalChannel.h \
@ -374,6 +381,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3CommonElements.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3MMElements.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3MMMessages.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3NonCallSSComponents.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3NonCallSSElements.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3NonCallSSMessages.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3Message.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3RRElements.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3RRMessages.Plo@am__quote@

View File

@ -177,6 +177,21 @@ SMS.DefaultDestSMSC 0000
#SMS.HTTP.AccessString sendmsg?user=xxxx&password=xxxx&api_id=xxxx
#
# USSD parameters
#
# USSD timeout for waiting MS response
USSD.timeout 100000
# USSD MO Handler (HTTP, CLI or Test)
USSD.Handler.MO CLI
# The USSD HTTP gateway.
USSD.HTTP.Gateway api.clickatell.com
# IF USSD.HTTP.Gateway IS DEFINED, USSD.HTTP.AccessString MUST ALSO BE DEFINED.
USSD.HTTP.AccessString sendmsg?user=xxxx&password=xxxx&api_id=xxxx
# Things to query during registration updates.