laforge
/
openbts-osmo
Archived
1
0
Fork 0

Merge commit 'USSD'

This commit is contained in:
Ivan Kluchnikov 2011-08-22 16:52:29 +04:00
commit 20ea06422d
21 changed files with 2918 additions and 45 deletions

View File

@ -27,6 +27,7 @@
#include <config.h>
#include "CLI.h"
#include "CLIParser.h"
#include <Logger.h>
#include <Globals.h>
@ -63,6 +64,7 @@ void CommandLine::runCLI(ParserBase *processor)
history_file_len + 1);
read_history(history_name);
}
}
#endif // HAVE_LIBREADLINE ]
@ -104,6 +106,7 @@ void CommandLine::runCLI(ParserBase *processor)
free(history_name);
history_name = 0;
}
#endif // HAVE_LIBREADLINE ]
}

View File

@ -391,7 +391,18 @@ int sendrrlp(int argc, char** argv, ostream& os)
return SUCCESS;
}
/** Send USSD to an IMSI. */
int sendUSSD(int argc, char** argv, ostream& os)
{
if (argc!=2) return BAD_NUM_ARGS;
char *IMSI = argv[1];
GSM::L3MobileIdentity mobileIdentity(IMSI);
unsigned transactionId = USSDDispatcher(mobileIdentity, (unsigned)1, (unsigned)0, Control::USSDData::REGrequest, std::string("REGrequest"), false);
Control::MTTestHandler handler(transactionId);
handler.run();
os << "MT USSD session end." << endl;
return SUCCESS;
}
/** Print current usage loads. */
int cliPrintStats(int argc, char** argv, ostream& os)
@ -874,6 +885,7 @@ Parser::Parser()
addCommand("sendsmsrpdu", sendsmsrpdu, "<IMSI> <src> <RPDU hex string> -- send pre-encoded SMS RPDU to <IMSI>, addressed from <src>.");
addCommand("sendsms", sendsms, "<IMSI> <src> <smsc> <text> -- send SMS to <IMSI>, addressed from <src> with SMS-Center <smsc>.");
addCommand("sendrrlp", sendrrlp, "<IMSI> <hexstring> -- send RRLP message <hexstring> to <IMSI>.");
addCommand("sendussd", sendUSSD, "<IMSI> -- send USSD to <IMSI>");
addCommand("load", cliPrintStats, "-- 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

@ -208,7 +208,8 @@ class Thread {
void start(void *(*task)(void*), void *arg);
/** Join a thread that will stop on its own. */
void join() { pthread_join(mThread,NULL); }
void join() { int s = pthread_join(mThread,NULL); assert(s==0); }
};

View File

@ -26,7 +26,7 @@
#include "ControlCommon.h"
#include <CLIParser.h>
#include <GSMLogicalChannel.h>
#include <GSML3Message.h>
#include <GSML3CCMessages.h>
@ -37,11 +37,12 @@
#include <SIPEngine.h>
#include <SIPInterface.h>
#include <Regexp.h>
using namespace std;
using namespace GSM;
using namespace Control;
using namespace CommandLine;
// The global transaction table.
@ -51,12 +52,31 @@ 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()),
mQ931State(NullState),
mUSSDData(NULL),
mT301(T301ms), mT302(T302ms), mT303(T303ms),
mT304(T304ms), mT305(T305ms), mT308(T308ms),
mT310(T310ms), mT313(T313ms),
@ -75,6 +95,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
mTIFlag(1), mTIValue(0),
mCalling(wCalling),
mQ931State(NullState),
mUSSDData(NULL),
mT301(T301ms), mT302(T302ms), mT303(T303ms),
mT304(T304ms), mT305(T305ms), mT308(T308ms),
mT310(T310ms), mT313(T313ms),
@ -94,6 +115,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
mTIFlag(0), mTIValue(wTIValue),
mCalled(wCalled),
mQ931State(NullState),
mUSSDData(NULL),
mT301(T301ms), mT302(T302ms), mT303(T303ms),
mT304(T304ms), mT305(T305ms), mT308(T308ms),
mT310(T310ms), mT313(T313ms),
@ -111,6 +133,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
mSubscriber(wSubscriber),mService(wService),
mTIFlag(1),mTIValue(wTIValue),mCalling(wCalling),
mQ931State(NullState),
mUSSDData(NULL),
mT301(T301ms), mT302(T302ms), mT303(T303ms),
mT304(T304ms), mT305(T305ms), mT308(T308ms),
mT310(T310ms), mT313(T313ms),
@ -119,6 +142,25 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
{
}
/** 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),
mQ931State(NullState),
mUSSDData(wUSSDData),
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
@ -203,6 +245,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;
@ -218,6 +262,7 @@ ostream& Control::operator<<(ostream& os, const TransactionEntry& entry)
if (entry.calling().digits()[0]) os << " from=" << entry.calling().digits();
os << " Q.931State=" << entry.Q931State();
os << " SIPState=" << entry.SIP().state();
os << " USSDData=" << entry.ussdData();
os << " (" << (entry.stateAge()+500)/1000 << " sec)";
if (entry.message()[0]) os << " message=\"" << entry.message() << "\"";
return os;
@ -337,6 +382,34 @@ bool TransactionTable::find(const L3MobileIdentity& mobileID, TransactionEntry&
return foundIt;
}
bool TransactionTable::find(const L3MobileIdentity& mobileID, GSM::L3CMServiceType serviceType, TransactionEntry& target)
{
// Yes, it's linear time.
// Even in a 6-ARFCN system, it should rarely be more than a dozen entries.
// Since clearDeadEntries is also linear, do that here, too.
// Brute force search.
bool foundIt = false;
mLock.lock();
clearDeadEntries();
TransactionMap::const_iterator itr = mTable.begin();
while (itr!=mTable.end()) {
const TransactionEntry& transaction = itr->second;
if (transaction.subscriber()==mobileID && transaction.service()==serviceType) {
// No need to check dead(), since we just cleared the table.
foundIt = true;
target = transaction;
break;
}
++itr;
}
mLock.unlock();
return foundIt;
}
size_t TransactionTable::size()
{
return mTable.size();
@ -597,7 +670,436 @@ void TMSITable::load(const char* filename)
fclose(fp);
}
USSDHandler::ResultCode USSDHandler::waitUSSDData(Control::USSDData::USSDMessageType* messageType,
std::string* USSDString,
unsigned timeout)
{
TransactionEntry transaction;
USSDData *pUssdData = NULL;
// Step 1 -- Find transaction
if (!gTransactionTable.find(mTransactionID, transaction))
{
LOG(DEBUG) << "Transaction with ID=" << mTransactionID << " not found";
return USSD_NO_TRANSACTION;
}
if ((pUssdData = transaction.ussdData()) == NULL)
{
LOG(DEBUG) << "Transaction has no USSD data: " << transaction;
return USSD_BAD_STATE;
}
// Step 2 -- Wait for MS to signal that data is ready
if (timeout >= USSDHandler::infinitely)
{
// wait infinitely
if (pUssdData->waitMS()!=0)
{
LOG(ERROR) << "USSDDate semaphore returned error: " << errno;
return USSD_ERROR;
}
}
else if ((timeout > USSDHandler::trywait) && (timeout < USSDHandler::infinitely))
{
// wait
if (pUssdData->waitMS(timeout)!=0)
{
LOG(DEBUG) << "USSDDate semaphore returned error or timeout";
return USSD_TIMEOUT;
}
}
else
{
// trywait
if (pUssdData->trywaitMS()!=0)
{
LOG(DEBUG) << "USSDDate semaphore returned error";
return USSD_TIMEOUT;
}
}
// Step 3 -- Get data from MS and check for closing condition
if (!gTransactionTable.find(mTransactionID, transaction))
{
LOG(DEBUG) << "Transaction with ID=" << mTransactionID << " not found";
return USSD_NO_TRANSACTION;
}
if ((pUssdData = transaction.ussdData()) == NULL)
{
LOG(DEBUG) << "Transaction has no USSD data: " << transaction;
return USSD_BAD_STATE;
}
if (transaction.Q931State() == Control::TransactionEntry::USSDclosing)
{
clearTransactionHistory(transaction);
LOG(DEBUG) << "Clearing USSD transaction: " << transaction;
return USSD_CLEARED;
}
*messageType = pUssdData->MessageType();
*USSDString = pUssdData->USSDString();
return USSD_OK;
}
USSDHandler::ResultCode USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType,
const std::string &iUSSDString)
{
// Step 0 -- Prepare long strings for continuation
std::string USSDString(iUSSDString);
if (USSDString.length()>USSD_MAX_CHARS_7BIT)
{
int contLen = mContinueStr.length();
mString = USSDString.substr(USSD_MAX_CHARS_7BIT-contLen,
USSDString.length()-(USSD_MAX_CHARS_7BIT-contLen));
USSDString.erase(USSDString.begin()+USSD_MAX_CHARS_7BIT-contLen, USSDString.end());
USSDString += mContinueStr;
}
else
{
mString = "";
}
// Step 1 -- Find transaction
TransactionEntry transaction;
USSDData *pUssdData = NULL;
if (!gTransactionTable.find(mTransactionID, transaction))
{
LOG(DEBUG) << "Transaction with ID=" << mTransactionID << " not found";
return USSD_NO_TRANSACTION;
}
if ((pUssdData = transaction.ussdData()) == NULL)
{
LOG(DEBUG) << "Transaction has no USSD data: " << transaction;
return USSD_BAD_STATE;
}
// Step 2 -- Update transaction with the data to send
pUssdData->MessageType(messageType);
pUssdData->USSDString(USSDString);
gTransactionTable.update(transaction);
// Step 3 -- Notify the dispatcher thread that data is ready to be sent
if (pUssdData->postNW() != 0)
{
return USSD_ERROR;
}
// Success.
return USSD_OK;
}
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#")
{
USSDString = "";
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::error;
}
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);
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);
}
}
void UssdSipHandler::run()
{
LOG(DEBUG) << "USSD SIP Handler RUN";
mNum = 0;
while(true)
{
Control::USSDData::USSDMessageType messageType;
std::string USSDString;
unsigned timeout = 100000;
// Get next data string from ME
ResultCode result = waitUSSDData(&messageType, &USSDString, timeout);
if (result != USSD_OK)
{
// Operation has been canceled by ME or timed out.
// Finish USSD session if it hasn't been not cleared in waitUSSDData();
postUSSDData(USSDData::error, "");
// TODO:: Send error to SIP side too?
break;
}
if (USSDString == ">")
{
if (mString == "")
{
messageType = USSDData::release;
}
else
{
USSDString = mString;
messageType = USSDData::request;
}
} else {
// Steps:
// 1 -- Setup SIP part of the transaction record.
// 2 -- Send the message to the server.
// 3 -- Wait for a response message and parse it.
// Step 1 -- Setup SIP part of the transaction record.
TransactionEntry transaction;
if (!gTransactionTable.find(transactionID(), transaction))
{
// Transaction not found. Something is wrong. Bail out.
break;
}
SIP::SIPEngine& engine = transaction.SIP();
// If we got a TMSI, find the IMSI.
L3MobileIdentity mobileID = transaction.subscriber();
if (mobileID.type()==TMSIType) {
const char *IMSI = gTMSITable.IMSI(mobileID.TMSI());
if (IMSI) mobileID = L3MobileIdentity(IMSI);
else {
// Something is wrong on the ME side.
postUSSDData(USSDData::error, "");
break;
}
}
engine.User(mobileID.digits());
LOG(DEBUG) << "MOUSSD: transaction: " << transaction;
// Step 2 -- Send the message to the server.
std::ostringstream outSipBody;
outSipBody << (int)messageType << std::endl << USSDString;
LOG(DEBUG) << "Created USSD SIP message: " << outSipBody.str();
engine.MOSMSSendMESSAGE(gConfig.getStr("USSD.SIP.user"),
gConfig.getStr("USSD.SIP.domain"),
outSipBody.str().c_str(), true);
SIP::SIPState state = engine.MOSMSWaitForSubmit();
LOG(DEBUG) << "Clearing call ID " << engine.callID()
<< " from transaction " << transaction.ID();
gSIPInterface.removeCall(engine.callID());
if (state != SIP::Cleared)
{
// Something is wrong on the SIP side.
postUSSDData(USSDData::error, "");
break;
}
// Step 3 -- Wait for a response SIP message and ACK it.
transaction.ussdData()->waitIncomingData(/*TODO:: timeout */);
engine.MTSMSSendOK();
LOG(DEBUG) << "Clearing call ID " << engine.callID()
<< " from transaction " << transaction.ID();
gSIPInterface.removeCall(engine.callID());
// Step 4 -- Get response and parse it.
if (!gTransactionTable.find(transactionID(), transaction))
{
// Transaction not found. Something is wrong. Bail out.
break;
}
std::istringstream inSipBody(transaction.message());
std::stringbuf messageText;
int tmp;
inSipBody >> tmp >> &messageText;
messageType = (Control::USSDData::USSDMessageType)tmp;
USSDString = messageText.str();
LOG(DEBUG) << "Parsed USSD server response. messageType=" << messageType
<< "(" << tmp << ")"
<< " string=\"" << USSDString << "\"";
}
postUSSDData(messageType, USSDString);
}
}
bool Control::waitForPrimitive(LogicalChannel *LCH, Primitive primitive, unsigned timeout_ms)
@ -770,7 +1272,81 @@ void Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH
}
}
bool Control::USSDMatchHandler(const std::string &handlerName, const std::string &ussdString)
{
std::string handlerKeyName("USSD.Handler.");
handlerKeyName += handlerName;
if (gConfig.defines(handlerKeyName))
{
std::string handlerRegexpStr = gConfig.getStr(handlerKeyName);
Regexp handlerRegexp(handlerRegexpStr.data());
if (handlerRegexp.match(ussdString.data()))
{
LOG(DEBUG) << "Request " << ussdString << " matches regexp \""
<< handlerRegexpStr << "\" for USSD handler " << handlerName;
return true;
}
}
return false;
}
unsigned Control::USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity,
unsigned TIFlag,
unsigned TIValue,
Control::USSDData::USSDMessageType messageType,
const std::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);
Thread* thread = new Thread;
if (USSDMatchHandler("HTTP", ussdString))
{
MOHttpHandler* handler = new MOHttpHandler(transaction.ID());
thread->start((void*(*)(void*))USSDHandler::runWrapper, handler);
}
else if (USSDMatchHandler("CLI", ussdString))
{
MOCLIHandler* handler = new MOCLIHandler(transaction.ID());
thread->start((void*(*)(void*))USSDHandler::runWrapper, handler);
}
else if (USSDMatchHandler("Test", ussdString))
{
MOTestHandler* handler = new MOTestHandler(transaction.ID());
thread->start((void*(*)(void*))USSDHandler::runWrapper, handler);
}
else if (USSDMatchHandler("SIP", ussdString))
{
UssdSipHandler* handler = new UssdSipHandler(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

@ -61,6 +61,9 @@ class L3IMSIDetachIndication;
class L3PagingResponse;
};
// Reference to a global config table, used all over the system.
extern ConfigurationTable gConfig;
/**@namespace Control This namepace is for use by the control layer. */
namespace Control {
@ -145,7 +148,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 +218,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 +254,6 @@ void resolveIMSI(GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
/**@ Paging mechanisms */
//@{
@ -365,8 +376,62 @@ 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
};
protected:
ThreadSemaphore mSemWaitMS;
ThreadSemaphore mSemWaitNW;
ThreadSemaphore mSemIncomingData; ///< External entity has sent us some data
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; }
ThreadSemaphore::Result waitMS() { return mSemWaitMS.wait(); }
ThreadSemaphore::Result waitNW() { return mSemWaitNW.wait(); }
ThreadSemaphore::Result waitIncomingData() { return mSemIncomingData.wait(); }
ThreadSemaphore::Result waitMS(unsigned timeout) { return mSemWaitMS.wait(timeout); }
ThreadSemaphore::Result waitNW(unsigned timeout) { return mSemWaitNW.wait(timeout); }
ThreadSemaphore::Result waitIncomingData(unsigned timeout) { return mSemIncomingData.wait(timeout); }
ThreadSemaphore::Result trywaitMS() { return mSemWaitMS.trywait(); }
ThreadSemaphore::Result trywaitNW() { return mSemWaitNW.trywait(); }
ThreadSemaphore::Result trywaitIncomingData() { return mSemIncomingData.trywait(); }
ThreadSemaphore::Result postMS() { return mSemWaitMS.post(); }
ThreadSemaphore::Result postNW() { return mSemWaitNW.post(); }
ThreadSemaphore::Result signalIncomingData() { return mSemIncomingData.post(); }
};
std::ostream& operator<<(std::ostream& os, USSDData::USSDMessageType);
std::ostream& operator<<(std::ostream& os, const USSDData&);
//@}
/**@name Transaction Table mechanisms. */
//@{
@ -395,6 +460,8 @@ class TransactionEntry {
ReleaseRequest,
SMSDelivering,
SMSSubmitting,
USSDworking,
USSDclosing
};
private:
@ -414,6 +481,8 @@ class TransactionEntry {
std::string mMessage; ///< text messaging payload
USSDData* mUSSDData; ///< USSD message data
/**@name Timers from GSM and Q.931 (network side) */
//@{
// If you add a timer, remember to add it to
@ -452,6 +521,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; }
@ -486,6 +562,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. */
@ -598,6 +677,16 @@ class TransactionTable {
*/
bool find(const GSM::L3MobileIdentity& mobileID, TransactionEntry& target);
/**
Find an entry by its mobile ID and service type.
Also clears dead entries during search.
@param mobileID The mobile at to search for.
@param serviceType Service type we're looking for.
@param target A TransactionEntry to accept the found record.
@return true is the mobile ID was found.
*/
bool find(const GSM::L3MobileIdentity& mobileID, GSM::L3CMServiceType serviceType, TransactionEntry& target);
/**
Remove "dead" entries from the table.
A "dead" entry is a transaction that is no longer active.
@ -701,7 +790,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;
@ -788,9 +877,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 */
@ -824,13 +914,7 @@ class Q931TimerExpired : public ControlLayerException {
:ControlLayerException(wTransactionID)
{}
};
//@}
} //Control
}
/**@addtogroup Globals */
@ -842,6 +926,120 @@ extern Control::TMSITable gTMSITable;
//@}
namespace Control {
bool USSDMatchHandler(const std::string &handlerName, const std::string &ussdString);
unsigned USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag,
unsigned TIValue, Control::USSDData::USSDMessageType messageType,
const std::string &ussdString, bool MO);
class USSDHandler {
public:
enum USSDtimeout {
trywait = 0,
infinitely = 120000
};
enum
{
USSD_MAX_CHARS_7BIT = 182 ///< See GSM 03.38 5 Cell Broadcast Data Coding Scheme
};
enum ResultCode {
USSD_OK = 0, ///< Operation performed
USSD_CLEARED, ///< USSD state has been cleared as a result of operation
USSD_TIMEOUT, ///< USSD operation timed out
USSD_NO_TRANSACTION, ///< Can't find transaction
USSD_BAD_STATE, ///< Wrong transaction state
USSD_ERROR ///< Generic error
};
private:
unsigned mTransactionID;
protected:
std::string mString;
std::string mContinueStr;
public:
/** This form is used for MO USSD */
USSDHandler(unsigned wTransactionID)
:mTransactionID(wTransactionID),
mContinueStr(gConfig.getStr("USSD.ContinueStr"))
{}
/** Wait USSD data from MS. Return: 0 - successful, 1 - clear transaction, 2 - error or timeout */
USSDHandler::ResultCode waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout = USSDHandler::infinitely);
/** Post USSD data and update transaction with new USSDData (messageType and USSDString)*/
USSDHandler::ResultCode postUSSDData( Control::USSDData::USSDMessageType messageType, const 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 UssdSipHandler : public USSDHandler {
private:
std::string mStr;
unsigned mNum;
public:
UssdSipHandler(unsigned wTransactionID)
: USSDHandler(wTransactionID)
{}
void run();
};
class MOCLIHandler : public USSDHandler {
public:
MOCLIHandler(unsigned wTransactionID)
:USSDHandler(wTransactionID)
{}
void run();
};
class MTTestHandler : public USSDHandler {
public:
MTTestHandler(unsigned wTransactionID)
:USSDHandler(wTransactionID)
{}
void run();
};
//@}
} //Control
#endif

View File

@ -105,14 +105,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:
@ -123,9 +127,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.
@ -145,8 +152,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());
@ -178,7 +193,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

@ -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,428 @@
/**@file USSD Control (L3) */
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero 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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero 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 parameter is 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));
// TODO:: How to drop session silently?
ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str()));
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 message 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;
// If we got a TMSI, find the IMSI.
// Note that this is a copy, not a reference.
L3MobileIdentity mobileIdentity = request->mobileIdentity();
resolveIMSI(mobileIdentity,LCH);
LOG(DEBUG) << "mobileIdentity: "<<request->mobileIdentity()<<" IMSI:"<<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))
{
USSDData *pUssdData = NULL;
// Wait for handler
if ((pUssdData = transaction.ussdData()) == NULL)
{
LOG(DEBUG) << "Transaction has no USSD data: " << transaction;
break;
}
if (pUssdData->waitNW()!=0)
{
LOG(DEBUG) << "waitNW() returned error for USSD transaction: " << transaction;
clearTransactionHistory(transaction);
break;
}
// Send to ME
if (!gTransactionTable.find(transactionID, transaction))
{
LOG(DEBUG) << "Transaction with ID=" << transactionID << " not found";
break;
}
if ((pUssdData = transaction.ussdData()) == NULL)
{
LOG(DEBUG) << "Transaction has no USSD data: " << transaction;
break;
}
USSDSend(pUssdData->USSDString(), InvokeID, TI, 0, LCH, pUssdData->MessageType());
if((pUssdData->MessageType() == Control::USSDData::response)||
(pUssdData->MessageType() == Control::USSDData::release))
{
LOG(DEBUG) << "waitMS received response or release. Closing";
transaction.Q931State(Control::TransactionEntry::USSDclosing);
LOG(DEBUG) << "Clearing USSD transaction: " << transaction;
clearTransactionHistory(transaction);
break;
}
// Receive from ME
L3Frame *USSDFrame = getFrameUSSD(LCH);
if (USSDFrame == NULL)
{
USSDSend(pUssdData->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;
pUssdData->USSDString(USSDString);
pUssdData->MessageType(messageType);
delete USSDMessage;
}
delete USSDFrame;
// Notify handler
gTransactionTable.update(transaction);
pUssdData->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

@ -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,294 @@
/**@file Call independent Supplementary Service Control. Facility element components, GSM 04.80 3.6.1. */
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero 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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero 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, 2009, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero 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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero 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, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero 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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero 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, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero 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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero 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,210 @@
/** @file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero 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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero 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,186 @@
/**@file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero 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 Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero 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

@ -251,6 +251,7 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
// Check for INVITE or MESSAGE methods.
GSM::ChannelType requiredChannel;
bool channelAvailable = false;
bool shouldPage = true;
GSM::L3CMServiceType serviceType;
if (strcmp(method,"INVITE") == 0) {
// INVITE is for MTC.
@ -267,10 +268,25 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
serviceType = L3CMServiceType::MobileTerminatedCall;
}
else if (strcmp(method,"MESSAGE") == 0) {
// MESSAGE is for MTSMS.
requiredChannel = GSM::SDCCHType;
channelAvailable = gBTS.SDCCHAvailable();
serviceType = L3CMServiceType::MobileTerminatedShortMessage;
// MESSAGE is for MTSMS or USSD
if ( strcmp(gConfig.getStr("USSD.SIP.user"), msg->from->url->username)==0
&& strcmp(gConfig.getStr("USSD.SIP.domain"), msg->from->url->host)==0)
{
LOG(INFO) << "received MESSAGE is USSD from: "
<< msg->from->url->username << "@" << msg->from->url->host;
requiredChannel = GSM::SDCCHType;
// TODO:: Understand how to behave when we need to page?
channelAvailable = true; //gBTS.SDCCHAvailable();
serviceType = L3CMServiceType::SupplementaryService;
}
else
{
LOG(INFO) << "received MESSAGE is SMS from: "
<< msg->from->url->username << "@" << msg->from->url->host;
requiredChannel = GSM::SDCCHType;
channelAvailable = gBTS.SDCCHAvailable();
serviceType = L3CMServiceType::MobileTerminatedShortMessage;
}
}
else {
// We must not handle this method.
@ -312,7 +328,8 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
L3MobileIdentity mobile_id(IMSI);
// Check SIP map. Repeated entry? Page again.
if (mSIPMap.map().readNoBlock(call_id_num) != NULL) {
// Skip this for USSD.
if ( mSIPMap.map().readNoBlock(call_id_num) != NULL) {
TransactionEntry transaction;
if (!gTransactionTable.find(mobile_id,transaction)) {
// FIXME -- Send "call leg non-existent" response on SIP interface.
@ -349,15 +366,33 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
LOG(NOTICE) << "INVITE/MESSAGE with no From: username for " << mobile_id;
}
LOG(DEBUG) << "callerID " << callerID << "@" << callerHost;
// Build the transaction table entry.
// This constructor sets TI flag=0, TI=0 for an MT transaction.
TransactionEntry transaction(mobile_id,serviceType,callerID);
TransactionEntry transaction;
// In case of USSD we should check for existing transaction first, because
// SIP MESSAGEs are sent out of call, our internal while USSD transaction
// stays alive for the whole duration of a session.
if ( serviceType == L3CMServiceType::SupplementaryService
&& gTransactionTable.find(mobile_id,L3CMServiceType::SupplementaryService,transaction))
{
// It's old USSD. No need to page.
shouldPage = false;
LOG(DEBUG) << "Existing USSD transaction found: " << transaction;
}
else
{
// It's not USSD or there are no existing transaction for it.
// Build new transaction table entry.
// This constructor sets TI flag=0, TI=0 for an MT transaction.
transaction = TransactionEntry(mobile_id,serviceType,callerID);
LOG(DEBUG) << "Created new transaction";
}
LOG(DEBUG) << "call_id_num \"" << call_id_num << "\"";
LOG(DEBUG) << "IMSI \"" << IMSI << "\"";
transaction.SIP().User(call_id_num,IMSI,callerID,callerHost);
transaction.SIP().saveINVITE(msg);
if (serviceType == L3CMServiceType::MobileTerminatedShortMessage) {
if ( serviceType == L3CMServiceType::MobileTerminatedShortMessage
|| serviceType == L3CMServiceType::SupplementaryService) {
osip_body_t *body;
osip_message_get_body(msg,0,&body);
if (!body) return false;
@ -365,15 +400,29 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
if (text) {
transaction.message(text);
}
else LOG(NOTICE) << "MTSMS incoming MESSAGE method with no message body for " << mobile_id;
else LOG(NOTICE) << "MTSMS/USSD incoming MESSAGE method with no message body for " << mobile_id;
}
LOG(INFO) << "MTC MTSMS making transaction and add to transaction table: "<< transaction;
LOG(INFO) << "MTC/MTSMS/USSD is adding/updating transaction: "<< transaction;
gTransactionTable.add(transaction);
// Add to paging list and tell the remote SIP end that we are trying.
LOG(DEBUG) << "MTC MTSMS new SIP invite, initial paging for mobile ID " << mobile_id;
gBTS.pager().addID(mobile_id,requiredChannel,transaction);
// FIXME -- Send TRYING? See MTCSendTrying for example.
if (serviceType == L3CMServiceType::SupplementaryService)
{
// TODO:: What to do in case of MT-USSD?
USSDData *ussdData = transaction.ussdData();
if (ussdData)
{
LOG(DEBUG) << "Signaling incoming USSD data";
ussdData->signalIncomingData();
}
}
if (shouldPage)
{
// Add to paging list and tell the remote SIP end that we are trying.
LOG(DEBUG) << "MTC/MTSMS/USSD new SIP invite, initial paging for mobile ID " << mobile_id;
gBTS.pager().addID(mobile_id,requiredChannel,transaction);
// FIXME -- Send TRYING? See MTCSendTrying for example.
}
return true;
}

View File

@ -213,6 +213,31 @@ SMS.DefaultDestSMSC 0000
SIP.SMSC smsc
#
# USSD parameters
#
# USSD timeout for waiting MS response
USSD.timeout 100000
# USSD handlers map.
# Option key has form USSD.Handler.<handler>, where <handler> is
# one of the following: HTTP, CLI and Test.
# Option value is a regexp. If it matches initial MO-USSD request,
# then request is passed for processing to according USSD handler.
# Comment out a handler option to completely disable the handler.
#USSD.Handler.CLI \*101#
USSD.Handler.Test \*(100|1011|102)#
#USSD.Handler.HTTP
# 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
# We split long messages into few screens. This string is appended
# to the end of each screen to notify user that text is to be continued.
USSD.ContinueStr ...1>
# Things to query during registration updates.

View File

@ -83,6 +83,18 @@ shortcode_quick_chk (const char *imsi, const char *msgtext,
return SCA_REPLY;
}
enum short_code_action
shortcode_ussd_test (const char *imsi, const char *msgtext,
short_code_params *scp)
{
ostringstream answer;
answer << "3" << endl;
answer << "USSD test ok!";
scp->scp_reply = new_strdup(answer.str().c_str());
return SCA_REPLY;
}
/*
* 411 -- information.
* Start by telling people their phone number.
@ -383,5 +395,6 @@ SMqueue::init_smcommands (short_code_map_t *scm)
if (gConfig.defines("SC.SMSC.Code")) {
(*scm)[gConfig.getStr("SC.SMSC.Code")] = shortcode_smsc;
}
(*scm)["USSD"] = shortcode_ussd_test;
// (*scm)["666"] = shortcode_text_access;
}