Merge commit 'USSD'
This commit is contained in:
commit
20ea06422d
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include "CLI.h"
|
#include "CLI.h"
|
||||||
|
#include "CLIParser.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
#include <Globals.h>
|
#include <Globals.h>
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ void CommandLine::runCLI(ParserBase *processor)
|
||||||
history_file_len + 1);
|
history_file_len + 1);
|
||||||
read_history(history_name);
|
read_history(history_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif // HAVE_LIBREADLINE ]
|
#endif // HAVE_LIBREADLINE ]
|
||||||
|
|
||||||
|
@ -104,6 +106,7 @@ void CommandLine::runCLI(ParserBase *processor)
|
||||||
free(history_name);
|
free(history_name);
|
||||||
history_name = 0;
|
history_name = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBREADLINE ]
|
#endif // HAVE_LIBREADLINE ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -391,7 +391,18 @@ int sendrrlp(int argc, char** argv, ostream& os)
|
||||||
return SUCCESS;
|
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. */
|
/** Print current usage loads. */
|
||||||
int cliPrintStats(int argc, char** argv, ostream& os)
|
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("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("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("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("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("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");
|
addCommand("calls", calls, "-- print the transaction table");
|
||||||
|
|
|
@ -208,7 +208,8 @@ class Thread {
|
||||||
void start(void *(*task)(void*), void *arg);
|
void start(void *(*task)(void*), void *arg);
|
||||||
|
|
||||||
/** Join a thread that will stop on its own. */
|
/** 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 "ControlCommon.h"
|
||||||
|
#include <CLIParser.h>
|
||||||
#include <GSMLogicalChannel.h>
|
#include <GSMLogicalChannel.h>
|
||||||
#include <GSML3Message.h>
|
#include <GSML3Message.h>
|
||||||
#include <GSML3CCMessages.h>
|
#include <GSML3CCMessages.h>
|
||||||
|
@ -37,11 +37,12 @@
|
||||||
#include <SIPEngine.h>
|
#include <SIPEngine.h>
|
||||||
#include <SIPInterface.h>
|
#include <SIPInterface.h>
|
||||||
|
|
||||||
|
#include <Regexp.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace GSM;
|
using namespace GSM;
|
||||||
using namespace Control;
|
using namespace Control;
|
||||||
|
using namespace CommandLine;
|
||||||
|
|
||||||
|
|
||||||
// The global transaction table.
|
// The global transaction table.
|
||||||
|
@ -51,12 +52,31 @@ TransactionTable gTransactionTable;
|
||||||
TMSITable gTMSITable;
|
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()
|
TransactionEntry::TransactionEntry()
|
||||||
:mID(gTransactionTable.newID()),
|
:mID(gTransactionTable.newID()),
|
||||||
mQ931State(NullState),
|
mQ931State(NullState),
|
||||||
|
mUSSDData(NULL),
|
||||||
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
||||||
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
||||||
mT310(T310ms), mT313(T313ms),
|
mT310(T310ms), mT313(T313ms),
|
||||||
|
@ -75,6 +95,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
|
||||||
mTIFlag(1), mTIValue(0),
|
mTIFlag(1), mTIValue(0),
|
||||||
mCalling(wCalling),
|
mCalling(wCalling),
|
||||||
mQ931State(NullState),
|
mQ931State(NullState),
|
||||||
|
mUSSDData(NULL),
|
||||||
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
||||||
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
||||||
mT310(T310ms), mT313(T313ms),
|
mT310(T310ms), mT313(T313ms),
|
||||||
|
@ -94,6 +115,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
|
||||||
mTIFlag(0), mTIValue(wTIValue),
|
mTIFlag(0), mTIValue(wTIValue),
|
||||||
mCalled(wCalled),
|
mCalled(wCalled),
|
||||||
mQ931State(NullState),
|
mQ931State(NullState),
|
||||||
|
mUSSDData(NULL),
|
||||||
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
||||||
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
||||||
mT310(T310ms), mT313(T313ms),
|
mT310(T310ms), mT313(T313ms),
|
||||||
|
@ -111,6 +133,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber,
|
||||||
mSubscriber(wSubscriber),mService(wService),
|
mSubscriber(wSubscriber),mService(wService),
|
||||||
mTIFlag(1),mTIValue(wTIValue),mCalling(wCalling),
|
mTIFlag(1),mTIValue(wTIValue),mCalling(wCalling),
|
||||||
mQ931State(NullState),
|
mQ931State(NullState),
|
||||||
|
mUSSDData(NULL),
|
||||||
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
mT301(T301ms), mT302(T302ms), mT303(T303ms),
|
||||||
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
mT304(T304ms), mT305(T305ms), mT308(T308ms),
|
||||||
mT310(T310ms), mT313(T313ms),
|
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
|
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::ReleaseRequest: os << "release request"; break;
|
||||||
case TransactionEntry::SMSDelivering: os << "SMS delivery"; break;
|
case TransactionEntry::SMSDelivering: os << "SMS delivery"; break;
|
||||||
case TransactionEntry::SMSSubmitting: os << "SMS submission"; 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 << "?";
|
default: os << "?" << (int)state << "?";
|
||||||
}
|
}
|
||||||
return os;
|
return os;
|
||||||
|
@ -218,6 +262,7 @@ ostream& Control::operator<<(ostream& os, const TransactionEntry& entry)
|
||||||
if (entry.calling().digits()[0]) os << " from=" << entry.calling().digits();
|
if (entry.calling().digits()[0]) os << " from=" << entry.calling().digits();
|
||||||
os << " Q.931State=" << entry.Q931State();
|
os << " Q.931State=" << entry.Q931State();
|
||||||
os << " SIPState=" << entry.SIP().state();
|
os << " SIPState=" << entry.SIP().state();
|
||||||
|
os << " USSDData=" << entry.ussdData();
|
||||||
os << " (" << (entry.stateAge()+500)/1000 << " sec)";
|
os << " (" << (entry.stateAge()+500)/1000 << " sec)";
|
||||||
if (entry.message()[0]) os << " message=\"" << entry.message() << "\"";
|
if (entry.message()[0]) os << " message=\"" << entry.message() << "\"";
|
||||||
return os;
|
return os;
|
||||||
|
@ -337,6 +382,34 @@ bool TransactionTable::find(const L3MobileIdentity& mobileID, TransactionEntry&
|
||||||
return foundIt;
|
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()
|
size_t TransactionTable::size()
|
||||||
{
|
{
|
||||||
return mTable.size();
|
return mTable.size();
|
||||||
|
@ -597,7 +670,436 @@ void TMSITable::load(const char* filename)
|
||||||
fclose(fp);
|
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)
|
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
|
// vim: ts=4 sw=4
|
||||||
|
|
|
@ -61,6 +61,9 @@ class L3IMSIDetachIndication;
|
||||||
class L3PagingResponse;
|
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 This namepace is for use by the control layer. */
|
||||||
namespace Control {
|
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. */
|
/**@name Functions for mobility manangement operations. */
|
||||||
//@{
|
//@{
|
||||||
|
@ -208,6 +218,8 @@ void MTSMSController(TransactionEntry& transaction,
|
||||||
GSM::LogicalChannel *LCH);
|
GSM::LogicalChannel *LCH);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Create a new transaction entry and start paging. */
|
/** Create a new transaction entry and start paging. */
|
||||||
void initiateMTTransaction(TransactionEntry& transaction,
|
void initiateMTTransaction(TransactionEntry& transaction,
|
||||||
GSM::ChannelType chanType, unsigned pageTime);
|
GSM::ChannelType chanType, unsigned pageTime);
|
||||||
|
@ -242,7 +254,6 @@ void resolveIMSI(GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**@ Paging mechanisms */
|
/**@ Paging mechanisms */
|
||||||
//@{
|
//@{
|
||||||
|
|
||||||
|
@ -365,8 +376,62 @@ void *PagerServiceLoopAdapter(Pager*);
|
||||||
//@} // paging mech
|
//@} // 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. */
|
/**@name Transaction Table mechanisms. */
|
||||||
//@{
|
//@{
|
||||||
|
@ -395,6 +460,8 @@ class TransactionEntry {
|
||||||
ReleaseRequest,
|
ReleaseRequest,
|
||||||
SMSDelivering,
|
SMSDelivering,
|
||||||
SMSSubmitting,
|
SMSSubmitting,
|
||||||
|
USSDworking,
|
||||||
|
USSDclosing
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -414,6 +481,8 @@ class TransactionEntry {
|
||||||
|
|
||||||
std::string mMessage; ///< text messaging payload
|
std::string mMessage; ///< text messaging payload
|
||||||
|
|
||||||
|
USSDData* mUSSDData; ///< USSD message data
|
||||||
|
|
||||||
/**@name Timers from GSM and Q.931 (network side) */
|
/**@name Timers from GSM and Q.931 (network side) */
|
||||||
//@{
|
//@{
|
||||||
// If you add a timer, remember to add it to
|
// If you add a timer, remember to add it to
|
||||||
|
@ -452,6 +521,13 @@ class TransactionEntry {
|
||||||
unsigned wTIValue,
|
unsigned wTIValue,
|
||||||
const GSM::L3CallingPartyBCDNumber& wCalling);
|
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. */
|
/**@name Accessors. */
|
||||||
//@{
|
//@{
|
||||||
unsigned TIValue() const { return mTIValue; }
|
unsigned TIValue() const { return mTIValue; }
|
||||||
|
@ -486,6 +562,9 @@ class TransactionEntry {
|
||||||
|
|
||||||
Q931CallState Q931State() const { return mQ931State; }
|
Q931CallState Q931State() const { return mQ931State; }
|
||||||
|
|
||||||
|
void ussdData(USSDData* wUSSDData) { mUSSDData=wUSSDData; }
|
||||||
|
USSDData* ussdData() const { return mUSSDData; }
|
||||||
|
|
||||||
unsigned stateAge() const { return mStateTimer.elapsed(); }
|
unsigned stateAge() const { return mStateTimer.elapsed(); }
|
||||||
|
|
||||||
/**@name Timer access. */
|
/**@name Timer access. */
|
||||||
|
@ -598,6 +677,16 @@ class TransactionTable {
|
||||||
*/
|
*/
|
||||||
bool find(const GSM::L3MobileIdentity& mobileID, TransactionEntry& target);
|
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.
|
Remove "dead" entries from the table.
|
||||||
A "dead" entry is a transaction that is no longer active.
|
A "dead" entry is a transaction that is no longer active.
|
||||||
|
@ -701,7 +790,7 @@ class TMSITable {
|
||||||
/**
|
/**
|
||||||
Find an IMSI in the table.
|
Find an IMSI in the table.
|
||||||
This is a log-time operation.
|
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.
|
@return Pointer to c-string IMSI or NULL.
|
||||||
*/
|
*/
|
||||||
const char* IMSI(unsigned TMSI) const;
|
const char* IMSI(unsigned TMSI) const;
|
||||||
|
@ -788,9 +877,10 @@ class ControlLayerException {
|
||||||
/** Thrown when the control layer gets the wrong message */
|
/** Thrown when the control layer gets the wrong message */
|
||||||
class UnexpectedMessage : public ControlLayerException {
|
class UnexpectedMessage : public ControlLayerException {
|
||||||
public:
|
public:
|
||||||
UnexpectedMessage(unsigned wTransactionID=0)
|
UnexpectedMessage(unsigned wTransactionID=0, const GSM::L3Frame *pFrame=NULL)
|
||||||
:ControlLayerException(wTransactionID)
|
:ControlLayerException(wTransactionID), mpFrame(pFrame)
|
||||||
{}
|
{}
|
||||||
|
const GSM::L3Frame *mpFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Thrown when recvL3 returns NULL */
|
/** Thrown when recvL3 returns NULL */
|
||||||
|
@ -824,13 +914,7 @@ class Q931TimerExpired : public ControlLayerException {
|
||||||
:ControlLayerException(wTransactionID)
|
:ControlLayerException(wTransactionID)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
//@}
|
|
||||||
|
|
||||||
|
|
||||||
} //Control
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**@addtogroup Globals */
|
/**@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
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -105,14 +105,18 @@ void DCCHDispatchRR(const L3RRMessage* req, LogicalChannel *DCCH)
|
||||||
/** Example of a closed-loop, persistent-thread control function for the DCCH. */
|
/** Example of a closed-loop, persistent-thread control function for the DCCH. */
|
||||||
void Control::DCCHDispatcher(LogicalChannel *DCCH)
|
void Control::DCCHDispatcher(LogicalChannel *DCCH)
|
||||||
{
|
{
|
||||||
|
const L3Message *message = NULL;
|
||||||
while (1) {
|
while (1) {
|
||||||
try {
|
try {
|
||||||
// Wait for a transaction to start.
|
if (!message)
|
||||||
LOG(DEBUG) << "waiting for " << DCCH->type() << " ESTABLISH";
|
{
|
||||||
waitForPrimitive(DCCH,ESTABLISH);
|
// Wait for a transaction to start.
|
||||||
// Pull the first message and dispatch a new transaction.
|
LOG(DEBUG) << "waiting for " << DCCH->type() << " ESTABLISH";
|
||||||
const L3Message *message = getMessage(DCCH);
|
waitForPrimitive(DCCH,ESTABLISH);
|
||||||
LOG(DEBUG) << "received " << *message;
|
// Pull the first message and dispatch a new transaction.
|
||||||
|
message = getMessage(DCCH);
|
||||||
|
LOG(DEBUG) << "received " << *message;
|
||||||
|
}
|
||||||
// Each protocol has it's own sub-dispatcher.
|
// Each protocol has it's own sub-dispatcher.
|
||||||
switch (message->PD()) {
|
switch (message->PD()) {
|
||||||
case L3MobilityManagementPD:
|
case L3MobilityManagementPD:
|
||||||
|
@ -123,9 +127,12 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(NOTICE) << "unhandled protocol " << message->PD() << " on " << DCCH->type();
|
LOG(NOTICE) << "unhandled protocol " << message->PD() << " on " << DCCH->type();
|
||||||
|
delete message;
|
||||||
|
message = NULL;
|
||||||
throw UnsupportedMessage();
|
throw UnsupportedMessage();
|
||||||
}
|
}
|
||||||
delete message;
|
delete message;
|
||||||
|
message = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch the various error cases.
|
// Catch the various error cases.
|
||||||
|
@ -145,8 +152,16 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH)
|
||||||
catch (UnexpectedMessage except) {
|
catch (UnexpectedMessage except) {
|
||||||
clearTransactionHistory(except.transactionID());
|
clearTransactionHistory(except.transactionID());
|
||||||
LOG(NOTICE) << "UnexpectedMessage";
|
LOG(NOTICE) << "UnexpectedMessage";
|
||||||
// Cause 0x62 means "message type not not compatible with protocol state".
|
if (except.mpFrame)
|
||||||
DCCH->send(L3ChannelRelease(0x62));
|
{
|
||||||
|
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) {
|
catch (UnsupportedMessage except) {
|
||||||
clearTransactionHistory(except.transactionID());
|
clearTransactionHistory(except.transactionID());
|
||||||
|
@ -178,7 +193,4 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// vim: ts=4 sw=4
|
// vim: ts=4 sw=4
|
||||||
|
|
|
@ -30,12 +30,13 @@ noinst_LTLIBRARIES = libcontrol.la
|
||||||
libcontrol_la_SOURCES = \
|
libcontrol_la_SOURCES = \
|
||||||
CallControl.cpp \
|
CallControl.cpp \
|
||||||
SMSControl.cpp \
|
SMSControl.cpp \
|
||||||
|
USSDControl.cpp \
|
||||||
ControlCommon.cpp \
|
ControlCommon.cpp \
|
||||||
MobilityManagement.cpp \
|
MobilityManagement.cpp \
|
||||||
RadioResource.cpp \
|
RadioResource.cpp \
|
||||||
DCCHDispatch.cpp \
|
DCCHDispatch.cpp \
|
||||||
CollectMSInfo.cpp \
|
CollectMSInfo.cpp \
|
||||||
RRLPQueryController.cpp
|
RRLPQueryController.cpp
|
||||||
|
|
||||||
# TODO - move CollectMSInfo.cpp and RRLPQueryController.cpp to RRLP directory.
|
# TODO - move CollectMSInfo.cpp and RRLPQueryController.cpp to RRLP directory.
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,9 @@ void Control::CMServiceResponder(const L3CMServiceRequest* cmsrq, LogicalChannel
|
||||||
case L3CMServiceType::EmergencyCall:
|
case L3CMServiceType::EmergencyCall:
|
||||||
EmergencyCall(cmsrq,DCCH);
|
EmergencyCall(cmsrq,DCCH);
|
||||||
break;
|
break;
|
||||||
|
case L3CMServiceType::SupplementaryService:
|
||||||
|
MOUSSDController(cmsrq,DCCH);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(NOTICE) << "service not supported for " << *cmsrq;
|
LOG(NOTICE) << "service not supported for " << *cmsrq;
|
||||||
// Cause 0x20 means "serivce not supported".
|
// Cause 0x20 means "serivce not supported".
|
||||||
|
|
|
@ -279,6 +279,9 @@ void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel
|
||||||
case L3CMServiceType::MobileTerminatedShortMessage:
|
case L3CMServiceType::MobileTerminatedShortMessage:
|
||||||
MTSMSController(transaction, DCCH);
|
MTSMSController(transaction, DCCH);
|
||||||
return;
|
return;
|
||||||
|
case L3CMServiceType::SupplementaryService:
|
||||||
|
MTUSSDController(transaction, DCCH);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
// Flush stray MOC entries.
|
// Flush stray MOC entries.
|
||||||
// There should not be any, but...
|
// 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 "GSML3RRMessages.h"
|
||||||
#include "GSML3MMMessages.h"
|
#include "GSML3MMMessages.h"
|
||||||
#include "GSML3CCMessages.h"
|
#include "GSML3CCMessages.h"
|
||||||
|
#include "GSML3NonCallSSMessages.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,6 +149,7 @@ GSM::L3Message* GSM::parseL3(const GSM::L3Frame& source)
|
||||||
case L3RadioResourcePD: retVal=parseL3RR(source); break;
|
case L3RadioResourcePD: retVal=parseL3RR(source); break;
|
||||||
case L3MobilityManagementPD: retVal=parseL3MM(source); break;
|
case L3MobilityManagementPD: retVal=parseL3MM(source); break;
|
||||||
case L3CallControlPD: retVal=parseL3CC(source); break;
|
case L3CallControlPD: retVal=parseL3CC(source); break;
|
||||||
|
case L3NonCallSSPD: retVal=parseL3NonCallSS(source); break;
|
||||||
//case L3SMSPD: return parseSMS(source);
|
//case L3SMSPD: return parseSMS(source);
|
||||||
default:
|
default:
|
||||||
LOG(NOTICE) << "L3 parsing failed for unsupported protocol " << PD;
|
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 \
|
GSML3Message.cpp \
|
||||||
GSML3MMElements.cpp \
|
GSML3MMElements.cpp \
|
||||||
GSML3MMMessages.cpp \
|
GSML3MMMessages.cpp \
|
||||||
|
GSML3NonCallSSComponents.cpp \
|
||||||
|
GSML3NonCallSSElements.cpp \
|
||||||
|
GSML3NonCallSSMessages.cpp \
|
||||||
GSML3RRElements.cpp \
|
GSML3RRElements.cpp \
|
||||||
GSML3RRMessages.cpp \
|
GSML3RRMessages.cpp \
|
||||||
GSMLogicalChannel.cpp \
|
GSMLogicalChannel.cpp \
|
||||||
|
@ -58,6 +61,9 @@ noinst_HEADERS = \
|
||||||
GSML3Message.h \
|
GSML3Message.h \
|
||||||
GSML3MMElements.h \
|
GSML3MMElements.h \
|
||||||
GSML3MMMessages.h \
|
GSML3MMMessages.h \
|
||||||
|
GSML3NonCallSSComponents.h \
|
||||||
|
GSML3NonCallSSElements.h \
|
||||||
|
GSML3NonCallSSMessages.h \
|
||||||
GSML3RRElements.h \
|
GSML3RRElements.h \
|
||||||
GSML3RRMessages.h \
|
GSML3RRMessages.h \
|
||||||
GSMLogicalChannel.h \
|
GSMLogicalChannel.h \
|
||||||
|
|
|
@ -251,6 +251,7 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
|
||||||
// Check for INVITE or MESSAGE methods.
|
// Check for INVITE or MESSAGE methods.
|
||||||
GSM::ChannelType requiredChannel;
|
GSM::ChannelType requiredChannel;
|
||||||
bool channelAvailable = false;
|
bool channelAvailable = false;
|
||||||
|
bool shouldPage = true;
|
||||||
GSM::L3CMServiceType serviceType;
|
GSM::L3CMServiceType serviceType;
|
||||||
if (strcmp(method,"INVITE") == 0) {
|
if (strcmp(method,"INVITE") == 0) {
|
||||||
// INVITE is for MTC.
|
// INVITE is for MTC.
|
||||||
|
@ -267,10 +268,25 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
|
||||||
serviceType = L3CMServiceType::MobileTerminatedCall;
|
serviceType = L3CMServiceType::MobileTerminatedCall;
|
||||||
}
|
}
|
||||||
else if (strcmp(method,"MESSAGE") == 0) {
|
else if (strcmp(method,"MESSAGE") == 0) {
|
||||||
// MESSAGE is for MTSMS.
|
// MESSAGE is for MTSMS or USSD
|
||||||
requiredChannel = GSM::SDCCHType;
|
if ( strcmp(gConfig.getStr("USSD.SIP.user"), msg->from->url->username)==0
|
||||||
channelAvailable = gBTS.SDCCHAvailable();
|
&& strcmp(gConfig.getStr("USSD.SIP.domain"), msg->from->url->host)==0)
|
||||||
serviceType = L3CMServiceType::MobileTerminatedShortMessage;
|
{
|
||||||
|
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 {
|
else {
|
||||||
// We must not handle this method.
|
// We must not handle this method.
|
||||||
|
@ -312,7 +328,8 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
|
||||||
L3MobileIdentity mobile_id(IMSI);
|
L3MobileIdentity mobile_id(IMSI);
|
||||||
|
|
||||||
// Check SIP map. Repeated entry? Page again.
|
// 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;
|
TransactionEntry transaction;
|
||||||
if (!gTransactionTable.find(mobile_id,transaction)) {
|
if (!gTransactionTable.find(mobile_id,transaction)) {
|
||||||
// FIXME -- Send "call leg non-existent" response on SIP interface.
|
// 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(NOTICE) << "INVITE/MESSAGE with no From: username for " << mobile_id;
|
||||||
}
|
}
|
||||||
LOG(DEBUG) << "callerID " << callerID << "@" << callerHost;
|
LOG(DEBUG) << "callerID " << callerID << "@" << callerHost;
|
||||||
// Build the transaction table entry.
|
|
||||||
// This constructor sets TI flag=0, TI=0 for an MT transaction.
|
TransactionEntry transaction;
|
||||||
TransactionEntry transaction(mobile_id,serviceType,callerID);
|
// 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) << "call_id_num \"" << call_id_num << "\"";
|
||||||
LOG(DEBUG) << "IMSI \"" << IMSI << "\"";
|
LOG(DEBUG) << "IMSI \"" << IMSI << "\"";
|
||||||
|
|
||||||
transaction.SIP().User(call_id_num,IMSI,callerID,callerHost);
|
transaction.SIP().User(call_id_num,IMSI,callerID,callerHost);
|
||||||
transaction.SIP().saveINVITE(msg);
|
transaction.SIP().saveINVITE(msg);
|
||||||
if (serviceType == L3CMServiceType::MobileTerminatedShortMessage) {
|
if ( serviceType == L3CMServiceType::MobileTerminatedShortMessage
|
||||||
|
|| serviceType == L3CMServiceType::SupplementaryService) {
|
||||||
osip_body_t *body;
|
osip_body_t *body;
|
||||||
osip_message_get_body(msg,0,&body);
|
osip_message_get_body(msg,0,&body);
|
||||||
if (!body) return false;
|
if (!body) return false;
|
||||||
|
@ -365,15 +400,29 @@ bool SIPInterface::checkInvite( osip_message_t * msg )
|
||||||
if (text) {
|
if (text) {
|
||||||
transaction.message(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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,31 @@ SMS.DefaultDestSMSC 0000
|
||||||
SIP.SMSC smsc
|
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.
|
# Things to query during registration updates.
|
||||||
|
|
|
@ -83,6 +83,18 @@ shortcode_quick_chk (const char *imsi, const char *msgtext,
|
||||||
return SCA_REPLY;
|
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.
|
* 411 -- information.
|
||||||
* Start by telling people their phone number.
|
* 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")) {
|
if (gConfig.defines("SC.SMSC.Code")) {
|
||||||
(*scm)[gConfig.getStr("SC.SMSC.Code")] = shortcode_smsc;
|
(*scm)[gConfig.getStr("SC.SMSC.Code")] = shortcode_smsc;
|
||||||
}
|
}
|
||||||
|
(*scm)["USSD"] = shortcode_ussd_test;
|
||||||
// (*scm)["666"] = shortcode_text_access;
|
// (*scm)["666"] = shortcode_text_access;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue