Merge commit 'USSD'
This commit is contained in:
commit
20ea06422d
|
@ -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 ]
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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); }
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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".
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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.";
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 \
|
||||
|
|
|
@ -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/USSD is adding/updating transaction: "<< transaction;
|
||||
gTransactionTable.add(transaction);
|
||||
|
||||
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.
|
||||
}
|
||||
LOG(INFO) << "MTC MTSMS making transaction and add to transaction table: "<< 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.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Reference in New Issue