From 388999984644131f2481200d3487348e1a12a4a2 Mon Sep 17 00:00:00 2001 From: Ivan Kluchnikov Date: Tue, 10 Aug 2010 17:04:04 +0400 Subject: [PATCH 01/30] Initial USSD support commit. --- public-trunk/CLI/CLI.cpp | 13 +- public-trunk/CommonLibs/Threads.cpp | 9 + public-trunk/CommonLibs/Threads.h | 31 ++ public-trunk/Control/ControlCommon.cpp | 351 ++++++++++++++- public-trunk/Control/ControlCommon.h | 177 +++++++- public-trunk/Control/DCCHDispatch.cpp | 34 +- public-trunk/Control/Makefile.am | 3 +- public-trunk/Control/Makefile.in | 4 +- public-trunk/Control/MobilityManagement.cpp | 3 + public-trunk/Control/RadioResource.cpp | 3 + public-trunk/Control/USSDControl.cpp | 405 ++++++++++++++++++ public-trunk/GSM/GSML2LAPDm.cpp | 7 +- public-trunk/GSM/GSML3Message.cpp | 2 + public-trunk/GSM/GSML3NonCallSSComponents.cpp | 295 +++++++++++++ public-trunk/GSM/GSML3NonCallSSComponents.h | 270 ++++++++++++ public-trunk/GSM/GSML3NonCallSSElements.cpp | 214 +++++++++ public-trunk/GSM/GSML3NonCallSSElements.h | 367 ++++++++++++++++ public-trunk/GSM/GSML3NonCallSSMessages.cpp | 211 +++++++++ public-trunk/GSM/GSML3NonCallSSMessages.h | 187 ++++++++ public-trunk/GSM/Makefile.am | 6 + public-trunk/GSM/Makefile.in | 10 + public-trunk/apps/OpenBTS.config.example | 15 + 22 files changed, 2587 insertions(+), 30 deletions(-) create mode 100644 public-trunk/Control/USSDControl.cpp create mode 100644 public-trunk/GSM/GSML3NonCallSSComponents.cpp create mode 100644 public-trunk/GSM/GSML3NonCallSSComponents.h create mode 100644 public-trunk/GSM/GSML3NonCallSSElements.cpp create mode 100644 public-trunk/GSM/GSML3NonCallSSElements.h create mode 100644 public-trunk/GSM/GSML3NonCallSSMessages.cpp create mode 100644 public-trunk/GSM/GSML3NonCallSSMessages.h diff --git a/public-trunk/CLI/CLI.cpp b/public-trunk/CLI/CLI.cpp index 19fba44..4ecc06b 100644 --- a/public-trunk/CLI/CLI.cpp +++ b/public-trunk/CLI/CLI.cpp @@ -363,7 +363,17 @@ int sendrrlp(int argc, char** argv, ostream& os, istream& is) return SUCCESS; } - +/** Send USSD to an IMSI. */ +int sendUSSD(int argc, char** argv, ostream& os, istream& is) +{ + if (argc!=2) return BAD_NUM_ARGS; + char *IMSI = argv[1]; + GSM::L3MobileIdentity mobileIdentity(IMSI); + Control::MTTestHandler handler(mobileIdentity, (unsigned)1, (unsigned)0, Control::USSDData::REGrequest, std::string("REGrequest")); + handler.run(); + os << "MT USSD session end." << endl; + return SUCCESS; +} /** Print current usage loads. */ int printStats(int argc, char** argv, ostream& os, istream& is) @@ -762,6 +772,7 @@ Parser::Parser() addCommand("findimsi", findimsi, "[IMSIPrefix] -- prints all imsi's that are prefixed by IMSIPrefix"); addCommand("sendsms", sendsms, " -- send SMS to , addressed from , after prompting."); addCommand("sendrrlp", sendrrlp, " -- send RRLP message to ."); + addCommand("sendussd", sendUSSD, " -- send USSD to "); addCommand("load", printStats, "-- print the current activity loads."); addCommand("cellid", cellID, "[MCC MNC LAC CI] -- get/set location area identity (MCC, MNC, LAC) and cell ID (CI)"); addCommand("calls", calls, "-- print the transaction table"); diff --git a/public-trunk/CommonLibs/Threads.cpp b/public-trunk/CommonLibs/Threads.cpp index df2e3a1..2628ab1 100644 --- a/public-trunk/CommonLibs/Threads.cpp +++ b/public-trunk/CommonLibs/Threads.cpp @@ -95,6 +95,15 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime); } +/** Wait for semaphore to be signaled with timeout. +* @returns 0 on success, -1 on error or timeout. +*/ +int ThreadSemaphore::wait(unsigned timeout) const +{ + Timeval then(timeout); + struct timespec waitTime = then.timespec(); + return sem_timedwait(&mSem,&waitTime); +} void Thread::start(void *(*task)(void*), void *arg) { diff --git a/public-trunk/CommonLibs/Threads.h b/public-trunk/CommonLibs/Threads.h index 0f12972..1b0d962 100644 --- a/public-trunk/CommonLibs/Threads.h +++ b/public-trunk/CommonLibs/Threads.h @@ -29,6 +29,7 @@ #include #include #include +#include class Mutex; @@ -120,7 +121,37 @@ class Signal { }; +/** Semaphore */ +class ThreadSemaphore { + private: + + mutable sem_t mSem; + + public: + + ThreadSemaphore(int pshared = 0, unsigned value = 0) { assert(sem_init(&mSem,pshared,value)!=-1); } + + ~ThreadSemaphore() { sem_destroy(&mSem); } + + /** Wait for semaphore to be signaled with timeout. + * @returns 0 on success, -1 on error or timeout. + */ + int wait (unsigned timeout) const; + + /** Wait for semaphore to be signaled infinitely. + * @returns 0 on success, -1 on error. + */ + int wait() const { return sem_wait(&mSem); } + + /** Check if semaphore has been signaled and disarm it. + * @returns 0 if semaphore has been signaled, -1 in other cases. + */ + int trywait() const { return sem_trywait(&mSem); } + + int post() { return sem_post (&mSem); } + +}; #define START_THREAD(thread,function,argument) \ thread.start((void *(*)(void*))function, (void*)argument); diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 9f92f53..609f16b 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -51,8 +51,26 @@ TransactionTable gTransactionTable; TMSITable gTMSITable; +ostream& Control::operator<<(ostream& os, USSDData::USSDMessageType type) +{ + switch (type) { + case USSDData::REGrequest: os << "register request"; break; + case USSDData::request: os << "request"; break; + case USSDData::notify: os << "notify"; break; + case USSDData::response: os << "response"; break; + case USSDData::error: os << "error"; break; + case USSDData::release: os << "release"; break; + default: os << "?" << (int)type << "?"; + } + return os; +} - +ostream& Control::operator<<(ostream& os, const USSDData& data) +{ + os << "USSD message type: " << data.MessageType(); + os << "USSD string: " << data.USSDString(); + return os; +} TransactionEntry::TransactionEntry() :mID(gTransactionTable.newID()), @@ -123,6 +141,24 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber, mMessage[0]='\0'; } +/** This form is used for USSD. */ +TransactionEntry::TransactionEntry(const GSM::L3MobileIdentity& wSubscriber, + const GSM::L3CMServiceType& wService, + unsigned wTIFlag, + unsigned wTIValue, + USSDData* wUSSDData) + :mID(gTransactionTable.newID()), + mSubscriber(wSubscriber),mService(wService), + mTIFlag(wTIFlag),mTIValue(wTIValue),mUSSDData(wUSSDData), + mQ931State(NullState), + mT301(T301ms), mT302(T302ms), mT303(T303ms), + mT304(T304ms), mT305(T305ms), mT308(T308ms), + mT310(T310ms), mT313(T313ms), + mT3113(gConfig.getNum("GSM.T3113")), + mTR1M(GSM::TR1Mms) +{ + mMessage[0]='\0'; +} bool TransactionEntry::timerExpired() const @@ -207,6 +243,8 @@ ostream& Control::operator<<(ostream& os, TransactionEntry::Q931CallState state) case TransactionEntry::ReleaseRequest: os << "release request"; break; case TransactionEntry::SMSDelivering: os << "SMS delivery"; break; case TransactionEntry::SMSSubmitting: os << "SMS submission"; break; + case TransactionEntry::USSDworking: os << "USSD working"; break; + case TransactionEntry::USSDclosing: os << "USSD closing"; break; default: os << "?" << (int)state << "?"; } return os; @@ -572,7 +610,270 @@ void TMSITable::load(const char* filename) fclose(fp); } +unsigned USSDHandler::waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout = USSDHandler::infinitely) +{ + TransactionEntry transaction; + // Wait for MS to signal that data is ready + gTransactionTable.find(mTransactionID, transaction); + + if (timeout >= USSDHandler::infinitely) + { + if (transaction.ussdData()->waitMS()!=0) + { + LOG(ERROR) << "USSDDate semaphore returned error: " << errno; + return 2; + } + } + else if((timeout > USSDHandler::trywait) && (timeout < USSDHandler::infinitely)) + { + //wait + if (transaction.ussdData()->waitMS(timeout)!=0) + { + LOG(DEBUG) << "USSDDate semaphore returned error or timeout"; + return 2; + } + } + else + { + //trywait + if (transaction.ussdData()->trywaitMS()!=0) + { + LOG(DEBUG) << "USSDDate semaphore returned error"; + return 2; + } + } + // Get data from MS and check for closing condition + gTransactionTable.find(mTransactionID, transaction); + if (transaction.Q931State() == Control::TransactionEntry::USSDclosing) + { + clearTransactionHistory(transaction); + LOG(DEBUG) << "USSD clearing...."; + return 1; + } + *messageType = transaction.ussdData()->MessageType(); + *USSDString = transaction.ussdData()->USSDString(); + return 0; +} + +void USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType, std::string USSDString) +{ + if (USSDString.length()>183) + { + mString = USSDString; + mString.erase(mString.begin(), mString.begin()+181); + USSDString.erase(USSDString.begin()+181, USSDString.end()); + USSDString+=">"; + } + else mString = ""; + TransactionEntry transaction; + gTransactionTable.find(mTransactionID, transaction); + transaction.ussdData()->MessageType(messageType); + transaction.ussdData()->USSDString(USSDString); + gTransactionTable.update(transaction); + transaction.ussdData()->postNW(); +} + +void *USSDHandler::runWrapper(void *pThis) +{ + ((USSDHandler*)pThis)->run(); + return NULL; +} + +void MOTestHandler::run() +{ + LOG(DEBUG) << "USSD MO Test Handler RUN"; + while(true) + { + Control::USSDData::USSDMessageType messageType; + std::string USSDString; + if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break; + if (USSDString == ">") + { + if (mString == "") + { + messageType = USSDData::release; + } + else + { + USSDString = mString; + messageType = USSDData::request; + } + } + else if (USSDString == "*100#") + { + USSDString = "handle response "; + messageType = USSDData::response; + } + else if(USSDString == "*101#") + { + USSDString = "handle request String objects are a special type of container, specifically designed to operate with sequences of characters. Unlike traditional c-strings, which are mere sequences of characters in a memory array, C++ string objects belong to a class with many built-in features to operate with strings in a more intuitive way and with some additional useful features common to C++ containers. The string class is an instantiation of the basic_string class template, defined in string as:"; + messageType = USSDData::request; + } + else if(USSDString == "*1011#") + { + USSDString = "handle request"; + messageType = USSDData::request; + } + else if(USSDString == "*102#") + { + USSDString = "handle notify"; + messageType = USSDData::notify; + } + else if(USSDString == "*103#") + { + messageType = USSDData::release; + } + else if(USSDString == "*104#") + { + messageType = USSDData::error; + } + else + { + messageType = USSDData::release; + } + + postUSSDData(messageType, USSDString); + } +} + +void MOHttpHandler::run() +{ + LOG(DEBUG) << "USSD MO Http Handler RUN"; + while(true) + { + Control::USSDData::USSDMessageType messageType; + std::string USSDString; + if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break; + + if (USSDString == ">") + { + if (mString == "") + { + messageType = USSDData::release; + } + else + { + USSDString = mString; + messageType = USSDData::request; + } + } + else if(USSDString == "*101#") + { + USSDString = "send command"; + messageType = USSDData::request; + } + else + { + char command[2048]; + sprintf(command,"wget -T 5 -q -O - \"http://%s/http/%s&to=%s&text=%s\"", + gConfig.getStr("USSD.HTTP.Gateway"), + gConfig.getStr("USSD.HTTP.AccessString"), + "server", USSDString.c_str()); + LOG(NOTICE) << "MOUSSD: send HTTP sending with " << command; + // HTTP "GET" method with wget. + char mystring [182]; + FILE* wget = popen(command,"r"); + if (!wget) { + LOG(NOTICE) << "cannot open wget with " << command; + USSDString = "cannot open wget"; + messageType = USSDData::release; + } + fgets (mystring , 182 , wget); + LOG(NOTICE) << "wget response " << mystring; + std::string tmpStr(mystring); + USSDString = tmpStr; + messageType = USSDData::request; + } + postUSSDData(messageType, USSDString); + } +} + +void MOCLIHandler::run() +{ + LOG(DEBUG) << "USSD MO CLI Handler RUN"; + while(true) + { + Control::USSDData::USSDMessageType messageType; + std::string USSDString; + if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break; + + if (USSDString == ">") + { + if (mString == "") + { + messageType = USSDData::release; + } + else + { + USSDString = mString; + messageType = USSDData::request; + } + } + else if(USSDString == "*101#") + { + USSDString = "send command"; + messageType = USSDData::request; + } + else + { + const char* line; + line = USSDString.c_str(); + std::ostringstream os; + std::istringstream is; + gParser.process(line, os, is); + LOG(INFO) << "Running line \"" << line << "\" returned result \"" << os.str() << "\""; + USSDString = os.str(); + messageType = USSDData::request; + } + postUSSDData(messageType, USSDString); + } +} + +void MTTestHandler::run() +{ + LOG(DEBUG) << "USSD MT Test Handler RUN"; + while(true) + { + Control::USSDData::USSDMessageType messageType; + std::string USSDString; + if (waitUSSDData(&messageType, &USSDString, gConfig.getNum("USSD.timeout"))) break; + + if(messageType == USSDData::REGrequest) + { + USSDString = "REGrequest message"; + } + else if(messageType == USSDData::response) + { + if (USSDString == "111") + { + USSDString = "release message"; + messageType = USSDData::release; + } + else if (USSDString == "100") + { + USSDString = "request message"; + messageType = USSDData::request; + } + else if (USSDString == "101") + { + messageType = USSDData::error; + } + else if (USSDString == "102") + { + USSDString = "notify message"; + messageType = USSDData::notify; + } + } + else + { + USSDString = "release message"; + messageType = USSDData::release; + } + + postUSSDData(messageType, USSDString); + } +} bool Control::waitForPrimitive(LogicalChannel *LCH, Primitive primitive, unsigned timeout_ms) @@ -728,7 +1029,53 @@ void Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH } } - +unsigned Control::USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue,Control::USSDData::USSDMessageType messageType, string ussdString, bool MO) +{ + TransactionEntry transaction(mobileIdentity, GSM::L3CMServiceType::SupplementaryService, TIFlag, TIValue, new USSDData(messageType)); + gTransactionTable.add(transaction); + LOG(DEBUG) << "USSD Dispatcher"; + transaction.ussdData()->USSDString(ussdString); + if (MO) + { + //MO + transaction.Q931State(Control::TransactionEntry::USSDworking); + transaction.ussdData()->postMS(); + gTransactionTable.update(transaction); + std::string handleName = gConfig.getStr("USSD.Handler.MO"); + Thread* thread = new Thread; + if (handleName=="HTTP") + { + MOHttpHandler* handler = new MOHttpHandler(transaction.ID()); + thread->start((void*(*)(void*))USSDHandler::runWrapper, handler); + } + else if (handleName=="CLI") + { + MOCLIHandler* handler = new MOCLIHandler(transaction.ID()); + thread->start((void*(*)(void*))USSDHandler::runWrapper, handler); + } + else if (handleName=="Test") + { + MOTestHandler* handler = new MOTestHandler(transaction.ID()); + thread->start((void*(*)(void*))USSDHandler::runWrapper, handler); + } + else + { + MOTestHandler* handler = new MOTestHandler(transaction.ID()); + thread->start((void*(*)(void*))USSDHandler::runWrapper, handler); + } + } + else + { + //MT + transaction.ussdData()->postNW(); + transaction.Q931State(Control::TransactionEntry::Paging); + //gTransactionTable.add(transaction); + gTransactionTable.update(transaction); + LOG(DEBUG) << "USSD Start Paging"; + gBTS.pager().addID(transaction.subscriber(),GSM::SDCCHType,transaction); + } + return transaction.ID(); +} // vim: ts=4 sw=4 diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index ff950b5..db22e99 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -145,7 +145,14 @@ void clearTransactionHistory(unsigned transactionID); //@} +/**@name USSD */ +//@{ +/** MO USSD controller */ +void MOUSSDController(const GSM::L3CMServiceRequest *req, GSM::LogicalChannel *LCH); +/** MTUSSD controller */ +void MTUSSDController(TransactionEntry& transaction, GSM::LogicalChannel *LCH); +//@} /**@name Functions for mobility manangement operations. */ //@{ @@ -208,6 +215,8 @@ void MTSMSController(TransactionEntry& transaction, GSM::LogicalChannel *LCH); //@} + + /** Create a new transaction entry and start paging. */ void initiateMTTransaction(TransactionEntry& transaction, GSM::ChannelType chanType, unsigned pageTime); @@ -242,7 +251,6 @@ void resolveIMSI(GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH); - /**@ Paging mechanisms */ //@{ @@ -365,8 +373,59 @@ void *PagerServiceLoopAdapter(Pager*); //@} // paging mech +/**@name USSD data */ +//@{ + +class USSDData { + + public: + /** USSD message types based on GSM 03.09 */ + enum USSDMessageType { + REGrequest, + request, + notify, + response, + error, + release + }; + + private: + + ThreadSemaphore mSemWaitMS; + ThreadSemaphore mSemWaitNW; + USSDMessageType mType; // USSD message type + std::string mUSSDString; // USSD message string + + public: + + USSDData(const USSDMessageType& wType) + :mType(wType) + {} + + void MessageType(USSDMessageType wType) { mType=wType; } + USSDMessageType MessageType() const { return mType; } + std::string USSDString() const { return mUSSDString; } + void USSDString(std::string wUSSDString) { mUSSDString = wUSSDString; } + + int waitMS() { return mSemWaitMS.wait(); } + int waitNW() { return mSemWaitNW.wait(); } + + int waitMS(unsigned timeout) { return mSemWaitMS.wait(timeout); } + int waitNW(unsigned timeout) { return mSemWaitNW.wait(timeout); } + + int trywaitMS() { return mSemWaitMS.trywait(); } + int trywaitNW() { return mSemWaitNW.trywait(); } + int postMS() { return mSemWaitMS.post(); } + int postNW() { return mSemWaitNW.post(); } + +}; + +std::ostream& operator<<(std::ostream& os, USSDData::USSDMessageType); +std::ostream& operator<<(std::ostream& os, const USSDData&); + +//@} /**@name Transaction Table mechanisms. */ //@{ @@ -395,6 +454,8 @@ class TransactionEntry { ReleaseRequest, SMSDelivering, SMSSubmitting, + USSDworking, + USSDclosing }; private: @@ -412,6 +473,8 @@ class TransactionEntry { Q931CallState mQ931State; ///< the GSM/ISDN/Q.931 call state Timeval mStateTimer; ///< timestamp of last state change. + USSDData* mUSSDData; ///< USSD message data + char mMessage[256]; ///< text messaging payload /**@name Timers from GSM and Q.931 (network side) */ @@ -452,6 +515,13 @@ class TransactionEntry { unsigned wTIValue, const GSM::L3CallingPartyBCDNumber& wCalling); + /** This form is used for USSD. */ + TransactionEntry(const GSM::L3MobileIdentity& wSubscriber, + const GSM::L3CMServiceType& wService, + unsigned wTIFlag, + unsigned wTIValue, + USSDData* wUSSDData); + /**@name Accessors. */ //@{ unsigned TIValue() const { return mTIValue; } @@ -488,6 +558,9 @@ class TransactionEntry { Q931CallState Q931State() const { return mQ931State; } + void ussdData(USSDData* wUSSDData) { mUSSDData=wUSSDData; } + USSDData* ussdData() const { return mUSSDData; } + unsigned stateAge() const { return mStateTimer.elapsed(); } /**@name Timer access. */ @@ -695,7 +768,7 @@ class TMSITable { /** Find an IMSI in the table. This is a log-time operation. - @param TMSI The TMSI to find. + @param TMSI The TMSI to find.post to @return Pointer to c-string IMSI or NULL. */ const char* IMSI(unsigned TMSI) const; @@ -783,9 +856,10 @@ class ControlLayerException { /** Thrown when the control layer gets the wrong message */ class UnexpectedMessage : public ControlLayerException { public: - UnexpectedMessage(unsigned wTransactionID=0) - :ControlLayerException(wTransactionID) + UnexpectedMessage(unsigned wTransactionID=0, const GSM::L3Frame *pFrame=NULL) + :ControlLayerException(wTransactionID), mpFrame(pFrame) {} + const GSM::L3Frame *mpFrame; }; /** Thrown when recvL3 returns NULL */ @@ -819,13 +893,7 @@ class Q931TimerExpired : public ControlLayerException { :ControlLayerException(wTransactionID) {} }; - - -//@} - - -} //Control - +} /**@addtogroup Globals */ @@ -837,6 +905,93 @@ extern Control::TMSITable gTMSITable; //@} +namespace Control { + +unsigned USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString, bool MO); + + +class USSDHandler { + + public: + enum USSDtimeout { + trywait = 0, + infinitely = 120000 + }; + private: + unsigned mTransactionID; + + protected: + std::string mString; + + public: + /** This form is used for MO USSD */ + USSDHandler(unsigned wTransactionID) + :mTransactionID(wTransactionID), + mString("") + {} + /** This form is used for MT USSD */ + USSDHandler(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString) + :mString("") + { + mTransactionID = USSDDispatcher(mobileIdentity, TIFlag, TIValue, messageType, ussdString, false); + } + /** Wait USSD data from MS. Return: 0 - successful, 1 - clear transaction, 2 - error or timeout */ + unsigned waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout); + /** Post USSD data and update transaction with new USSDData (messageType and USSDString)*/ + void postUSSDData( Control::USSDData::USSDMessageType messageType, std::string USSDString); + unsigned transactionID() { return mTransactionID; } + void transactionID(unsigned wTransactionID) { wTransactionID = mTransactionID; } + static void *runWrapper(void *pThis); + + protected: + virtual void run() = 0; + +}; + + +class MOTestHandler : public USSDHandler { + public: + MOTestHandler(unsigned wTransactionID) + :USSDHandler(wTransactionID) + {} + void run(); +}; + +class MOHttpHandler : public USSDHandler { + public: + MOHttpHandler(unsigned wTransactionID) + :USSDHandler(wTransactionID) + {} + + void run(); +}; + +class MOCLIHandler : public USSDHandler { + public: + MOCLIHandler(unsigned wTransactionID) + :USSDHandler(wTransactionID) + {} + + void run(); +}; + +class MTTestHandler : public USSDHandler { + public: + MTTestHandler(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString) + :USSDHandler(mobileIdentity,TIFlag, TIValue, messageType, ussdString) + {} + + void run(); +}; + +//@} + + +} //Control + + + + #endif diff --git a/public-trunk/Control/DCCHDispatch.cpp b/public-trunk/Control/DCCHDispatch.cpp index 2d893b8..94b4122 100644 --- a/public-trunk/Control/DCCHDispatch.cpp +++ b/public-trunk/Control/DCCHDispatch.cpp @@ -102,14 +102,18 @@ void DCCHDispatchRR(const L3RRMessage* req, LogicalChannel *DCCH) /** Example of a closed-loop, persistent-thread control function for the DCCH. */ void Control::DCCHDispatcher(LogicalChannel *DCCH) { + const L3Message *message = NULL; while (1) { try { - // Wait for a transaction to start. - LOG(DEBUG) << "waiting for " << DCCH->type() << " ESTABLISH"; - waitForPrimitive(DCCH,ESTABLISH); - // Pull the first message and dispatch a new transaction. - const L3Message *message = getMessage(DCCH); - LOG(DEBUG) << "received " << *message; + if (!message) + { + // Wait for a transaction to start. + LOG(DEBUG) << "waiting for " << DCCH->type() << " ESTABLISH"; + waitForPrimitive(DCCH,ESTABLISH); + // Pull the first message and dispatch a new transaction. + message = getMessage(DCCH); + LOG(DEBUG) << "received " << *message; + } // Each protocol has it's own sub-dispatcher. switch (message->PD()) { case L3MobilityManagementPD: @@ -120,9 +124,12 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH) break; default: LOG(NOTICE) << "unhandled protocol " << message->PD() << " on " << DCCH->type(); + delete message; + message = NULL; throw UnsupportedMessage(); } delete message; + message = NULL; } // Catch the various error cases. @@ -142,8 +149,16 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH) catch (UnexpectedMessage except) { clearTransactionHistory(except.transactionID()); LOG(NOTICE) << "UnexpectedMessage"; - // Cause 0x62 means "message type not not compatible with protocol state". - DCCH->send(L3ChannelRelease(0x62)); + if (except.mpFrame) + { + message = parseL3(*except.mpFrame); + delete except.mpFrame; + } + else + { + // Cause 0x62 means "message type not not compatible with protocol state". + DCCH->send(L3ChannelRelease(0x62)); + } } catch (UnsupportedMessage except) { clearTransactionHistory(except.transactionID()); @@ -175,7 +190,4 @@ void Control::DCCHDispatcher(LogicalChannel *DCCH) } } - - - // vim: ts=4 sw=4 diff --git a/public-trunk/Control/Makefile.am b/public-trunk/Control/Makefile.am index 9a55cd6..8d371dc 100644 --- a/public-trunk/Control/Makefile.am +++ b/public-trunk/Control/Makefile.am @@ -30,12 +30,13 @@ noinst_LTLIBRARIES = libcontrol.la libcontrol_la_SOURCES = \ CallControl.cpp \ SMSControl.cpp \ + USSDControl.cpp \ ControlCommon.cpp \ MobilityManagement.cpp \ RadioResource.cpp \ DCCHDispatch.cpp \ CollectMSInfo.cpp \ - RRLPQueryController.cpp + RRLPQueryController.cpp # TODO - move CollectMSInfo.cpp and RRLPQueryController.cpp to RRLP directory. diff --git a/public-trunk/Control/Makefile.in b/public-trunk/Control/Makefile.in index 96f6237..3a92274 100644 --- a/public-trunk/Control/Makefile.in +++ b/public-trunk/Control/Makefile.in @@ -92,7 +92,7 @@ CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libcontrol_la_LIBADD = -am_libcontrol_la_OBJECTS = CallControl.lo SMSControl.lo \ +am_libcontrol_la_OBJECTS = CallControl.lo SMSControl.lo USSDControl.lo\ ControlCommon.lo MobilityManagement.lo RadioResource.lo \ DCCHDispatch.lo CollectMSInfo.lo RRLPQueryController.lo libcontrol_la_OBJECTS = $(am_libcontrol_la_OBJECTS) @@ -271,6 +271,7 @@ noinst_LTLIBRARIES = libcontrol.la libcontrol_la_SOURCES = \ CallControl.cpp \ SMSControl.cpp \ + USSDControl.cpp \ ControlCommon.cpp \ MobilityManagement.cpp \ RadioResource.cpp \ @@ -344,6 +345,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RRLPQueryController.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RadioResource.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SMSControl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/USSDControl.Plo@am__quote@ .cpp.o: @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ diff --git a/public-trunk/Control/MobilityManagement.cpp b/public-trunk/Control/MobilityManagement.cpp index cad2b16..cf8c7f0 100644 --- a/public-trunk/Control/MobilityManagement.cpp +++ b/public-trunk/Control/MobilityManagement.cpp @@ -64,6 +64,9 @@ void Control::CMServiceResponder(const L3CMServiceRequest* cmsrq, LogicalChannel case L3CMServiceType::EmergencyCall: EmergencyCall(cmsrq,DCCH); break; + case L3CMServiceType::SupplementaryService: + MOUSSDController(cmsrq,DCCH); + break; default: LOG(NOTICE) << "service not supported for " << *cmsrq; // Cause 0x20 means "serivce not supported". diff --git a/public-trunk/Control/RadioResource.cpp b/public-trunk/Control/RadioResource.cpp index 51a7bd2..9fb83c4 100644 --- a/public-trunk/Control/RadioResource.cpp +++ b/public-trunk/Control/RadioResource.cpp @@ -279,6 +279,9 @@ void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel case L3CMServiceType::MobileTerminatedShortMessage: MTSMSController(transaction, DCCH); return; + case L3CMServiceType::SupplementaryService: + MTUSSDController(transaction, DCCH); + return; default: // Flush stray MOC entries. // There should not be any, but... diff --git a/public-trunk/Control/USSDControl.cpp b/public-trunk/Control/USSDControl.cpp new file mode 100644 index 0000000..a468922 --- /dev/null +++ b/public-trunk/Control/USSDControl.cpp @@ -0,0 +1,405 @@ +/**@file USSD Control (L3) */ +/* +* Copyright 2008, 2009 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + + +#include +#include +#include +#include "ControlCommon.h" +#include +#include "GSML3NonCallSSMessages.h" +#include "GSML3NonCallSSComponents.h" +#include "GSML3NonCallSSElements.h" +#include + + +using namespace std; +using namespace GSM; +using namespace Control; + + +#include "SIPInterface.h" +#include "SIPUtility.h" +#include "SIPMessage.h" +#include "SIPEngine.h" +using namespace SIP; + +/** @file USSDControl.cpp + +USSD string coding schema is described at GSM 02.90 5.1.2, 5.1.4 +and GSM 02.30 4.5 + +TODO: +1) We should support Network -> MS USSD too. + +BUGS: +1) *101# does not work with Siemens A52 (but does work with A65). +After phone receives "Send more data" it can't perform any more USSD +and can't reply to this request too. If you try, phone displays +"NOT EXECUTED". BUT when you switch it off afterwards, it sends +ReleaseComplete, so it seems it's stuck waiting for something from +network? + +2) On Alcatel BG3 message sent with Notify disappears as soon as +phone receives our ReleaseComplete. Probably we should delay it +by a few seconds? +*/ + + +L3Frame* getFrameUSSD (LogicalChannel *LCH, GSM::Primitive primitive=DATA) +{ + L3Frame *retVal = LCH->recv(gConfig.getNum("USSD.timeout"),0); + if (!retVal) { + LOG(NOTICE) <<"TIME"; + //throw ChannelReadTimeout(); + retVal = NULL; + } + else if (retVal->primitive() != primitive) { + LOG(NOTICE) << "unexpected primitive, expecting " << primitive << ", got " << *retVal; + //throw UnexpectedPrimitive(); + retVal = NULL; + } + else if ((retVal->primitive() == DATA) && (retVal->PD() != L3NonCallSSPD)) { + LOG(NOTICE) << "unexpected (non-USSD) protocol in frame " << *retVal; + //throw UnexpectedMessage(0, retVal); + retVal = NULL; + } + return retVal; +} + +void USSDSend(string USSDString, unsigned InvokeID, unsigned TI, unsigned TIFlag, LogicalChannel* LCH, Control::USSDData::USSDMessageType messageType) +{ + L3Frame Frame; + + if (TIFlag) TIFlag = 0; + else TIFlag = 1; + + if (messageType == Control::USSDData::REGrequest) + { + //SEND MT REGISTER, UnstructuredSSRequest + L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID), + L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSRequest)); + ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str())); + L3NonCallSSRegisterMessage RegisterMessage(0, 0, ComponentPtr); + RegisterMessage.write(Frame); + LOG(DEBUG) << "Sending Register Message: " << RegisterMessage; + LCH->send(Frame); + } + + else if (messageType == Control::USSDData::request) + { + //SEND MT Facility, UnstructuredSSRequest + L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID), + L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSRequest)); + ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str())); + L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr); + FacilityMessage.write(Frame); + LOG(DEBUG) << "Sending Register Message: " << FacilityMessage; + LCH->send(Frame); + } + + else if (messageType == Control::USSDData::response) + { + //send response + L3NonCallSSComponentReturnResult* ComponentPtr = new L3NonCallSSComponentReturnResult(L3NonCallSSInvokeID(InvokeID)); + ComponentPtr->operationCode(L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::ProcessUnstructuredSSRequest)); + ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str())); + L3NonCallSSReleaseCompleteMessage ReleaseCompleteMessage(TIFlag, TI); + ReleaseCompleteMessage.component(ComponentPtr); + ReleaseCompleteMessage.write(Frame); + LOG(DEBUG) << "Sending Release Complete Message with response: " << ReleaseCompleteMessage; + LCH->send(Frame); + //send L3 Channel Release + LOG(DEBUG) << "L3 Channel Release."; + LCH->send(L3ChannelRelease()); + } + + else if (messageType == Control::USSDData::request ) + { + //send request + if(InvokeID == 0xff) InvokeID = 0; + else InvokeID++; + L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID), + L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSRequest)); + + ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str())); + L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr); + FacilityMessage.write(Frame); + LOG(DEBUG) << "Sending Facility Message with request: " << FacilityMessage; + LCH->send(Frame); + } + + else if (messageType == Control::USSDData::notify) + { + //send notify + if(InvokeID == 0xff) InvokeID = 0; + else InvokeID++; + L3NonCallSSComponentInvoke* ComponentPtr = new L3NonCallSSComponentInvoke(L3NonCallSSInvokeID(InvokeID), + L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::UnstructuredSSNotify)); + ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str())); + L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr); + FacilityMessage.write(Frame); + LOG(DEBUG) << "Sending Facility Message with notify: " << FacilityMessage; + LCH->send(Frame); + } + + else if (messageType == Control::USSDData::error) + { + //send error + L3NonCallSSComponentReturnError* ComponentPtr = new L3NonCallSSComponentReturnError(L3NonCallSSInvokeID(InvokeID), + L3NonCallSSErrorCode(GSM::L3NonCallSSErrorCode::SystemFailure)); + L3NonCallSSFacilityMessage FacilityMessage(TIFlag, TI, ComponentPtr); + FacilityMessage.write(Frame); + LOG(DEBUG) << "Sending Facility Message with error System Failure: " << FacilityMessage; + LCH->send(Frame); + } + + else if(messageType == Control::USSDData::release) + { + // NOTE: On some phones (e.g. Nokia 3310, Alcatel BG3) if this parameteris set to a non-empty string, + // when it is sent after Notify USSD, this string is shown on the screen after showing Notify + // message just for a second. I.e. it replaces (obscures) original Notify message. Thus we + // recommend to set this string to an empty string. + L3NonCallSSComponentReturnResult* ComponentPtr = new L3NonCallSSComponentReturnResult(L3NonCallSSInvokeID(InvokeID)); + ComponentPtr->operationCode(L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::ProcessUnstructuredSSRequest)); + ComponentPtr->parameters(L3NonCallSSParameters("release")); + L3NonCallSSReleaseCompleteMessage ReleaseCompleteMessage(TIFlag, TI); + if (TIFlag == 1) + { + ReleaseCompleteMessage.component(ComponentPtr); + } + ReleaseCompleteMessage.write(Frame); + LOG(DEBUG) << "Sending Release Complete Message: " << ReleaseCompleteMessage; + LCH->send(Frame); + //send L3 Channel Release + LOG(DEBUG) << "L3 Channel Release."; + LCH->send(L3ChannelRelease()); + } + + + +} + +Control::USSDData::USSDMessageType USSDParse(L3NonCallSSMessage* Message, string* USSDString, unsigned* InvokeID ) +{ + L3Frame Frame; + Control::USSDData::USSDMessageType messageType = Control::USSDData::error; + // Low-level mesage flow is described at GSM 04.90 6.1. + // High-level message flow is described at GSM 03.90 6.2. + + // Process REQUEST message + if (Message->MTI() == GSM::L3NonCallSSMessage::Register) + { + LOG(DEBUG) << "Received Register Message"; + L3NonCallSSRegisterMessage* RegisterMessage = (L3NonCallSSRegisterMessage*)Message; + if (RegisterMessage->l3NonCallSSComponent()->NonCallSSComponentTypeTag()== GSM::L3NonCallSSComponent::Invoke) + { + LOG(DEBUG) << "Received Invoke Component"; + L3NonCallSSComponentInvoke* Component; + Component = (L3NonCallSSComponentInvoke*)RegisterMessage->l3NonCallSSComponent(); + *USSDString = Component->l3NonCallSSParameters().data(); + LOG(DEBUG) << "Read USSD string"; + *InvokeID = (unsigned)Component->invokeID().value(); + messageType = Control::USSDData::request; + } + + } + // Process RESPONSE message + else if (Message->MTI() == GSM::L3NonCallSSMessage::Facility) + { + LOG(DEBUG) << "Received Facility Message"; + L3NonCallSSFacilityMessage* FacilityMessage = (L3NonCallSSFacilityMessage*)Message; + if (FacilityMessage->l3NonCallSSComponent()->NonCallSSComponentTypeTag()== GSM::L3NonCallSSComponent::ReturnResult) + { + LOG(DEBUG) << "Return Result Component"; + L3NonCallSSComponentReturnResult* Component; + Component = (L3NonCallSSComponentReturnResult*)FacilityMessage->l3NonCallSSComponent(); + if (Component->haveL3NonCallSSParameters()) *USSDString = Component->l3NonCallSSParameters().data(); + else *USSDString = ""; + *InvokeID = (unsigned)Component->invokeID().value(); + messageType = Control::USSDData::response; + } + } + + // Process RELEASE or ERROR + else // MTI == GSM::L3NonCallSSMessage::ReleaseComplete + { + L3NonCallSSReleaseCompleteMessage* ReleaseCompleteMessage = (L3NonCallSSReleaseCompleteMessage*)Message; + if (ReleaseCompleteMessage->haveL3NonCallSSComponent()) + { + *InvokeID = ReleaseCompleteMessage->l3NonCallSSComponent()->invokeID().value(); + messageType = Control::USSDData::release; + } + else + { + InvokeID = 0; + messageType = Control::USSDData::release; + } + *USSDString = ""; + } + + return messageType; +} + +void Control::MOUSSDController(const L3CMServiceRequest *request, LogicalChannel *LCH) +{ + assert(request); + assert(LCH); + + LOG(DEBUG) << "Get L3 CM Service Request: " << *request; + L3MobileIdentity mobileIdentity = request->mobileIdentity(); + LOG(DEBUG) << "mobileIdentity: "<send(L3CMServiceAccept()); + + //Get USSD frame from MS + LOG(DEBUG) << "Get USSD frame from MS"; + L3Frame *USSDFrame = getFrameUSSD(LCH); + //Parse USSD frame + LOG(DEEPDEBUG) << "Parse USSD frame"; + L3NonCallSSMessage* USSDMessage; + USSDMessage = parseL3NonCallSS(*USSDFrame); + LOG(INFO) << "USSD message:"<<*USSDMessage; + + string USSDString = ""; + unsigned InvokeID = 0; + + Control::USSDData::USSDMessageType messageType = USSDParse(USSDMessage, &USSDString, &InvokeID); + unsigned transactionID = USSDDispatcher (mobileIdentity, USSDMessage->TIFlag(), USSDMessage->TIValue(), messageType, USSDString, true); + unsigned TI = USSDMessage->TIValue(); + + TransactionEntry transaction; + while(gTransactionTable.find(transactionID, transaction)) + { + if (transaction.Q931State() == Control::TransactionEntry::USSDclosing) + { + break; + } + else if (transaction.ussdData()->waitNW()==0) + { + gTransactionTable.find(transactionID, transaction); + //SEND + USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 0, LCH, transaction.ussdData()->MessageType()); + if((transaction.ussdData()->MessageType() == Control::USSDData::response)|| + (transaction.ussdData()->MessageType() == Control::USSDData::release)) + { + LOG(DEBUG) << "USSD waitMS response||relese"; + transaction.Q931State(Control::TransactionEntry::USSDclosing); + } + else + { + //RECV + L3Frame *USSDFrame = getFrameUSSD(LCH); + if (USSDFrame == NULL) + { + USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 0, LCH, Control::USSDData::release); + transaction.Q931State(Control::TransactionEntry::USSDclosing); + } + else + { + //Parse USSD frame + L3NonCallSSMessage* USSDMessage = parseL3NonCallSS(*USSDFrame); + TI = USSDMessage->TIValue(); + LOG(INFO) << "USSD message before PARSE:"<<*USSDMessage; + Control::USSDData::USSDMessageType messageType = USSDParse(USSDMessage, &USSDString, &InvokeID); + LOG(INFO) << "MO USSD message: "<< *USSDMessage << " MO USSD type: "<< messageType + << " USSD string: " << USSDString << " InvokeID: " << InvokeID; + transaction.ussdData()->USSDString(USSDString); + transaction.ussdData()->MessageType(messageType); + delete USSDMessage; + } + delete USSDFrame; + + } + gTransactionTable.update(transaction); + transaction.ussdData()->postMS(); + } + } +} + + +void Control::MTUSSDController(TransactionEntry& transaction, LogicalChannel *LCH) +{ + + assert(LCH); + LOG(INFO) << "MTUSSD: transaction: "<< transaction; + LCH->transactionID(transaction.ID()); + string USSDString = ""; + unsigned InvokeID = 0; + unsigned TI = 0; + unsigned transactionID = transaction.ID(); + + transaction.Q931State(Control::TransactionEntry::USSDworking); + gTransactionTable.update(transaction); + + while(gTransactionTable.find(transactionID, transaction)) + { + if (transaction.Q931State() == Control::TransactionEntry::USSDclosing) + { + break; + } + else if (transaction.ussdData()->waitNW()==0) + { + gTransactionTable.find(transactionID, transaction); + //SEND + USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 1, LCH, transaction.ussdData()->MessageType()); + if((transaction.ussdData()->MessageType() == Control::USSDData::response)|| + (transaction.ussdData()->MessageType() == Control::USSDData::release)) + { + LOG(DEBUG) << "USSD waitMS response||relese"; + transaction.Q931State(Control::TransactionEntry::USSDclosing); + } + else + { + //RECV + L3Frame *USSDFrame = getFrameUSSD(LCH); + if (USSDFrame == NULL) + { + USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 1, LCH, Control::USSDData::release); + transaction.Q931State(Control::TransactionEntry::USSDclosing); + } + else + { + //Parse USSD frame + L3NonCallSSMessage* USSDMessage = parseL3NonCallSS(*USSDFrame); + TI = USSDMessage->TIValue(); + Control::USSDData::USSDMessageType messageType = USSDParse(USSDMessage, &USSDString, &InvokeID); + LOG(INFO) << "MT USSD message: "<< *USSDMessage << "MT USSD type: "<< messageType + << " USSD string: " << USSDString << " InvokeID: " << InvokeID; + transaction.ussdData()->MessageType(messageType); + transaction.ussdData()->USSDString(USSDString); + delete USSDMessage; + } + delete USSDFrame; + } + gTransactionTable.update(transaction); + transaction.ussdData()->postMS(); + } + } + + LOG(DEBUG) << "USSD session done."; +} + + diff --git a/public-trunk/GSM/GSML2LAPDm.cpp b/public-trunk/GSM/GSML2LAPDm.cpp index e367c6f..df08d47 100644 --- a/public-trunk/GSM/GSML2LAPDm.cpp +++ b/public-trunk/GSM/GSML2LAPDm.cpp @@ -980,9 +980,10 @@ void L2LAPDm::sendIFrame(const BitVector& payload, bool MBit) bool L2LAPDm::stuckChannel(const L2Frame& frame) { // Check for excessive idling. - if (frame.DCCHIdle()) mIdleCount++; - else mIdleCount=0; - return mIdleCount > maxIdle(); + //if (frame.DCCHIdle()) mIdleCount++; + //else mIdleCount=0; + //return mIdleCount > maxIdle(); + return false; } diff --git a/public-trunk/GSM/GSML3Message.cpp b/public-trunk/GSM/GSML3Message.cpp index a4e3063..bd62ffb 100644 --- a/public-trunk/GSM/GSML3Message.cpp +++ b/public-trunk/GSM/GSML3Message.cpp @@ -28,6 +28,7 @@ #include "GSML3RRMessages.h" #include "GSML3MMMessages.h" #include "GSML3CCMessages.h" +#include "GSML3NonCallSSMessages.h" #include @@ -148,6 +149,7 @@ GSM::L3Message* GSM::parseL3(const GSM::L3Frame& source) case L3RadioResourcePD: retVal=parseL3RR(source); break; case L3MobilityManagementPD: retVal=parseL3MM(source); break; case L3CallControlPD: retVal=parseL3CC(source); break; + case L3NonCallSSPD: retVal=parseL3NonCallSS(source); break; //case L3SMSPD: return parseSMS(source); default: LOG(NOTICE) << "L3 parsing failed for unsupported protocol " << PD; diff --git a/public-trunk/GSM/GSML3NonCallSSComponents.cpp b/public-trunk/GSM/GSML3NonCallSSComponents.cpp new file mode 100644 index 0000000..1eb0449 --- /dev/null +++ b/public-trunk/GSM/GSML3NonCallSSComponents.cpp @@ -0,0 +1,295 @@ +/**@file Call independent Supplementary Service Control. Facility element components, GSM 04.80 3.6.1. */ + +/* +* Copyright 2008, 2009 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + + +#include +#include "GSML3NonCallSSComponents.h" +#include + + + +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; +} diff --git a/public-trunk/GSM/GSML3NonCallSSComponents.h b/public-trunk/GSM/GSML3NonCallSSComponents.h new file mode 100644 index 0000000..82c9ef4 --- /dev/null +++ b/public-trunk/GSM/GSML3NonCallSSComponents.h @@ -0,0 +1,270 @@ +/**@file Call independent Supplementary Service Control. Facility element components, GSM 04.80 3.6.1. */ +/* +* Copyright 2008 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + + + +#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 diff --git a/public-trunk/GSM/GSML3NonCallSSElements.cpp b/public-trunk/GSM/GSML3NonCallSSElements.cpp new file mode 100644 index 0000000..28e77f7 --- /dev/null +++ b/public-trunk/GSM/GSML3NonCallSSElements.cpp @@ -0,0 +1,214 @@ +/**@file Elements for call independent Supplementary Service Control, GSM 04.80 3. */ +/* +* Copyright 2008, 2009 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + + + + +#include "GSML3NonCallSSElements.h" +#include + +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 + + +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 diff --git a/public-trunk/GSM/GSML3NonCallSSMessages.cpp b/public-trunk/GSM/GSML3NonCallSSMessages.cpp new file mode 100644 index 0000000..b012884 --- /dev/null +++ b/public-trunk/GSM/GSML3NonCallSSMessages.cpp @@ -0,0 +1,211 @@ +/** @file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */ + +/* +* Copyright 2008, 2009 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + + + +#include +#include "GSML3NonCallSSMessages.h" +#include + + +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; +} + diff --git a/public-trunk/GSM/GSML3NonCallSSMessages.h b/public-trunk/GSM/GSML3NonCallSSMessages.h new file mode 100644 index 0000000..6946a68 --- /dev/null +++ b/public-trunk/GSM/GSML3NonCallSSMessages.h @@ -0,0 +1,187 @@ +/**@file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */ + +/* +* Copyright 2008, 2009 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + + + +#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 diff --git a/public-trunk/GSM/Makefile.am b/public-trunk/GSM/Makefile.am index 9f116da..bcb3b35 100644 --- a/public-trunk/GSM/Makefile.am +++ b/public-trunk/GSM/Makefile.am @@ -37,6 +37,9 @@ libGSM_la_SOURCES = \ GSML3Message.cpp \ GSML3MMElements.cpp \ GSML3MMMessages.cpp \ + GSML3NonCallSSComponents.cpp \ + GSML3NonCallSSElements.cpp \ + GSML3NonCallSSMessages.cpp \ GSML3RRElements.cpp \ GSML3RRMessages.cpp \ GSMLogicalChannel.cpp \ @@ -58,6 +61,9 @@ noinst_HEADERS = \ GSML3Message.h \ GSML3MMElements.h \ GSML3MMMessages.h \ + GSML3NonCallSSComponents.h \ + GSML3NonCallSSElements.h \ + GSML3NonCallSSMessages.h \ GSML3RRElements.h \ GSML3RRMessages.h \ GSMLogicalChannel.h \ diff --git a/public-trunk/GSM/Makefile.in b/public-trunk/GSM/Makefile.in index 3b5ddf1..3a5c203 100644 --- a/public-trunk/GSM/Makefile.in +++ b/public-trunk/GSM/Makefile.in @@ -96,6 +96,7 @@ am_libGSM_la_OBJECTS = GSM610Tables.lo GSMCommon.lo GSMConfig.lo \ GSML1FEC.lo GSML2LAPDm.lo GSML3CCElements.lo \ GSML3CCMessages.lo GSML3CommonElements.lo GSML3Message.lo \ GSML3MMElements.lo GSML3MMMessages.lo GSML3RRElements.lo \ + GSML3NonCallSSComponents.lo GSML3NonCallSSElements.lo GSML3NonCallSSMessages.lo \ GSML3RRMessages.lo GSMLogicalChannel.lo GSMSAPMux.lo \ GSMTDMA.lo GSMTransfer.lo GSMTAPDump.lo PowerManager.lo libGSM_la_OBJECTS = $(am_libGSM_la_OBJECTS) @@ -282,6 +283,9 @@ libGSM_la_SOURCES = \ GSML3Message.cpp \ GSML3MMElements.cpp \ GSML3MMMessages.cpp \ + GSML3NonCallSSComponents.cpp \ + GSML3NonCallSSElements.cpp \ + GSML3NonCallSSMessages.cpp \ GSML3RRElements.cpp \ GSML3RRMessages.cpp \ GSMLogicalChannel.cpp \ @@ -303,6 +307,9 @@ noinst_HEADERS = \ GSML3Message.h \ GSML3MMElements.h \ GSML3MMMessages.h \ + GSML3NonCallSSComponents.h \ + GSML3NonCallSSElements.h \ + GSML3NonCallSSMessages.h \ GSML3RRElements.h \ GSML3RRMessages.h \ GSMLogicalChannel.h \ @@ -374,6 +381,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3CommonElements.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3MMElements.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3MMMessages.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3NonCallSSComponents.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3NonCallSSElements.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3NonCallSSMessages.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3Message.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3RRElements.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GSML3RRMessages.Plo@am__quote@ diff --git a/public-trunk/apps/OpenBTS.config.example b/public-trunk/apps/OpenBTS.config.example index bd09446..bd650fb 100644 --- a/public-trunk/apps/OpenBTS.config.example +++ b/public-trunk/apps/OpenBTS.config.example @@ -177,6 +177,21 @@ SMS.DefaultDestSMSC 0000 #SMS.HTTP.AccessString sendmsg?user=xxxx&password=xxxx&api_id=xxxx +# +# USSD parameters +# +# USSD timeout for waiting MS response +USSD.timeout 100000 + +# USSD MO Handler (HTTP, CLI or Test) +USSD.Handler.MO CLI + +# The USSD HTTP gateway. +USSD.HTTP.Gateway api.clickatell.com + +# IF USSD.HTTP.Gateway IS DEFINED, USSD.HTTP.AccessString MUST ALSO BE DEFINED. +USSD.HTTP.AccessString sendmsg?user=xxxx&password=xxxx&api_id=xxxx + # Things to query during registration updates. From d4da240011f979e6f4dc82c1ccc42aab2f437f2d Mon Sep 17 00:00:00 2001 From: Ivan Kluchnikov Date: Tue, 10 Aug 2010 17:25:37 +0400 Subject: [PATCH 02/30] This shoud not be committed. When this code is enabled transaction will be closed after about 15 seconds of inactivity. It is too short for USSD and should be fixed later. --- public-trunk/GSM/GSML2LAPDm.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/public-trunk/GSM/GSML2LAPDm.cpp b/public-trunk/GSM/GSML2LAPDm.cpp index df08d47..e367c6f 100644 --- a/public-trunk/GSM/GSML2LAPDm.cpp +++ b/public-trunk/GSM/GSML2LAPDm.cpp @@ -980,10 +980,9 @@ void L2LAPDm::sendIFrame(const BitVector& payload, bool MBit) bool L2LAPDm::stuckChannel(const L2Frame& frame) { // Check for excessive idling. - //if (frame.DCCHIdle()) mIdleCount++; - //else mIdleCount=0; - //return mIdleCount > maxIdle(); - return false; + if (frame.DCCHIdle()) mIdleCount++; + else mIdleCount=0; + return mIdleCount > maxIdle(); } From 3d6e95356c21bbebe8544c688cc24251445725cb Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Tue, 10 Aug 2010 19:18:33 +0400 Subject: [PATCH 03/30] Remove Makefile which is generated by ./configure. --- public-trunk/RRLP/Makefile | 412 ------------------------------------- 1 file changed, 412 deletions(-) delete mode 100644 public-trunk/RRLP/Makefile diff --git a/public-trunk/RRLP/Makefile b/public-trunk/RRLP/Makefile deleted file mode 100644 index de9ee77..0000000 --- a/public-trunk/RRLP/Makefile +++ /dev/null @@ -1,412 +0,0 @@ -# Makefile.in generated by automake 1.9.4 from Makefile.am. -# RRLP/Makefile. Generated from Makefile.in by configure. - -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004 Free Software Foundation, Inc. -# This Makefile.in is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - - - -# -# Copyright 2008 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 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 . -# - -# -# Copyright 2008 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 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 . -# -srcdir = . -top_srcdir = .. - -pkgdatadir = $(datadir)/openbts -pkglibdir = $(libdir)/openbts -pkgincludedir = $(includedir)/openbts -top_builddir = .. -am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -INSTALL = /opt/local/bin/ginstall -c -install_sh_DATA = $(install_sh) -c -m 644 -install_sh_PROGRAM = $(install_sh) -c -install_sh_SCRIPT = $(install_sh) -c -INSTALL_HEADER = $(INSTALL_DATA) -transform = $(program_transform_name) -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : -build_triplet = i686-apple-darwin8.11.1 -host_triplet = i686-apple-darwin8.11.1 -target_triplet = i686-apple-darwin8.11.1 -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(top_srcdir)/Makefile.common -subdir = RRLP -ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/configure.ac -am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ - $(ACLOCAL_M4) -mkinstalldirs = $(install_sh) -d -CONFIG_HEADER = $(top_builddir)/config.h -CONFIG_CLEAN_FILES = -SOURCES = -DIST_SOURCES = -DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = ${SHELL} /Users/dburgess/OpenBTSProject/openbts-SF/public-trunk/missing --run aclocal-1.9 -AMDEP_FALSE = # -AMDEP_TRUE = -AMTAR = ${SHELL} /Users/dburgess/OpenBTSProject/openbts-SF/public-trunk/missing --run tar -AR = ar -AS = as -AUTOCONF = ${SHELL} /Users/dburgess/OpenBTSProject/openbts-SF/public-trunk/missing --run autoconf -AUTOHEADER = ${SHELL} /Users/dburgess/OpenBTSProject/openbts-SF/public-trunk/missing --run autoheader -AUTOMAKE = ${SHELL} /Users/dburgess/OpenBTSProject/openbts-SF/public-trunk/missing --run automake-1.9 -AWK = gawk -CC = gcc -CCAS = gcc -CCASFLAGS = -g -O2 -CCDEPMODE = depmode=gcc3 -CFLAGS = -g -O2 -CPP = gcc -E -CPPFLAGS = -CXX = g++ -CXXCPP = g++ -E -CXXDEPMODE = depmode=gcc3 -CXXFLAGS = -g -O2 -CYGPATH_W = echo -DEFS = -DHAVE_CONFIG_H -DEPDIR = .deps -DLLTOOL = dlltool -ECHO = echo -ECHO_C = -ECHO_N = -n -ECHO_T = -EGREP = /usr/bin/grep -E -EXEEXT = -F77 = gfortran -FFLAGS = -g -O2 -GREP = /usr/bin/grep -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_PROGRAM = ${INSTALL} -INSTALL_SCRIPT = ${INSTALL} -INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s -LDFLAGS = -LIBOBJS = -LIBS = -lreadline -LIBTOOL = $(SHELL) $(top_builddir)/libtool -LN_S = ln -s -LTLIBOBJS = -MAKEINFO = ${SHELL} /Users/dburgess/OpenBTSProject/openbts-SF/public-trunk/missing --run makeinfo -OBJDUMP = objdump -OBJEXT = o -ORTP_CFLAGS = -D_REENTRANT -DORTP_INET6 -I/usr/local/include -ORTP_LIBS = -L/usr/local/lib -lortp -lpthread -OSIP_CFLAGS = -DOSIP_MT -I/usr/local/include -OSIP_LIBS = -L/usr/local/lib -losipparser2 -losip2 -PACKAGE = openbts -PACKAGE_BUGREPORT = -PACKAGE_NAME = openbts -PACKAGE_STRING = openbts 2.6PUBLIC -PACKAGE_TARNAME = openbts -PACKAGE_VERSION = 2.6PUBLIC -PATH_SEPARATOR = : -PKG_CONFIG = /sw/bin/pkg-config -RANLIB = ranlib -RM_PROG = /bin/rm -SET_MAKE = -SHELL = /bin/sh -STRIP = strip -USRP_CFLAGS = -DOMNITHREAD_POSIX=1 -I/usr/local/include/gnuradio -I/sw/include -I/usr/local/include -USRP_LIBS = -L/usr/local/lib -L/sw/lib -lusrp -lusb -lgromnithread -VERSION = 2.6PUBLIC -ac_ct_CC = gcc -ac_ct_CXX = g++ -ac_ct_F77 = gfortran -am__fastdepCC_FALSE = # -am__fastdepCC_TRUE = -am__fastdepCXX_FALSE = # -am__fastdepCXX_TRUE = -am__include = include -am__leading_dot = . -am__quote = -am__tar = ${AMTAR} chof - "$$tardir" -am__untar = ${AMTAR} xf - -bindir = ${exec_prefix}/bin -build = i686-apple-darwin8.11.1 -build_alias = -build_cpu = i686 -build_os = darwin8.11.1 -build_vendor = apple -datadir = ${datarootdir} -datarootdir = ${prefix}/share -docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} -dvidir = ${docdir} -exec_prefix = ${prefix} -host = i686-apple-darwin8.11.1 -host_alias = -host_cpu = i686 -host_os = darwin8.11.1 -host_vendor = apple -htmldir = ${docdir} -includedir = ${prefix}/include -infodir = ${datarootdir}/info -install_sh = /Users/dburgess/OpenBTSProject/openbts-SF/public-trunk/install-sh -libdir = ${exec_prefix}/lib -libexecdir = ${exec_prefix}/libexec -localedir = ${datarootdir}/locale -localstatedir = ${prefix}/var -mandir = ${datarootdir}/man -mkdir_p = $(install_sh) -d -oldincludedir = /usr/include -pdfdir = ${docdir} -prefix = /usr/local -program_transform_name = s,x,x, -psdir = ${docdir} -sbindir = ${exec_prefix}/sbin -sharedstatedir = ${prefix}/com -sysconfdir = ${prefix}/etc -target = i686-apple-darwin8.11.1 -target_alias = -target_cpu = i686 -target_os = darwin8.11.1 -target_vendor = apple -COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs -CONTROL_INCLUDEDIR = $(top_srcdir)/Control -GSM_INCLUDEDIR = $(top_srcdir)/GSM -SIP_INCLUDEDIR = $(top_srcdir)/SIP -SMS_INCLUDEDIR = $(top_srcdir)/SMS -TRX_INCLUDEDIR = $(top_srcdir)/TRXManager -GLOBALS_INCLUDEDIR = $(top_srcdir)/Globals -CLI_INCLUDEDIR = $(top_srcdir)/CLI -#SQL_INCLUDEDIR = $(top_srcdir)/SQL -HLR_INCLUDEDIR = $(top_srcdir)/HLR -STD_DEFINES_AND_INCLUDES = \ - -I$(COMMON_INCLUDEDIR) \ - -I$(CONTROL_INCLUDEDIR) \ - -I$(GSM_INCLUDEDIR) \ - -I$(SIP_INCLUDEDIR) \ - -I$(SMS_INCLUDEDIR) \ - -I$(TRX_INCLUDEDIR) \ - -I$(GLOBALS_INCLUDEDIR) \ - -I$(CLI_INCLUDEDIR) \ - -I$(HLR_INCLUDEDIR) - -# -I$(SQL_INCLUDEDIR) -COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la -GSM_LA = $(top_builddir)/GSM/libGSM.la -SIP_LA = $(top_builddir)/SIP/libSIP.la -SMS_LA = $(top_builddir)/SMS/libSMS.la -TRX_LA = $(top_builddir)/TRXManager/libtrxmanager.la -CONTROL_LA = $(top_builddir)/Control/libcontrol.la -GLOBALS_LA = $(top_builddir)/Globals/libglobals.la -CLI_LA = $(top_builddir)/CLI/libcli.la -HLR_LA = $(top_builddir)/HLR/libHLR.la -#SQL_LA = $(top_builddir)/SQL/libSQL.la -MOSTLYCLEANFILES = *~ -EXTRA_DIST = \ - RRLPMessages.cpp \ - RRLPTest.cpp \ - RRLPMessages.h - -all: all-am - -.SUFFIXES: -$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Makefile.common $(am__configure_deps) - @for dep in $?; do \ - case '$(am__configure_deps)' in \ - *$$dep*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ - && exit 0; \ - exit 1;; \ - esac; \ - done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu RRLP/Makefile'; \ - cd $(top_srcdir) && \ - $(AUTOMAKE) --gnu RRLP/Makefile -.PRECIOUS: Makefile -Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status - @case '$?' in \ - *config.status*) \ - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ - *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ - esac; - -$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -$(top_srcdir)/configure: $(am__configure_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -$(ACLOCAL_M4): $(am__aclocal_m4_deps) - cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - -distclean-libtool: - -rm -f libtool -uninstall-info-am: -tags: TAGS -TAGS: - -ctags: CTAGS -CTAGS: - - -distdir: $(DISTFILES) - $(mkdir_p) $(distdir)/.. - @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ - list='$(DISTFILES)'; for file in $$list; do \ - case $$file in \ - $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ - $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ - esac; \ - if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ - dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test "$$dir" != "$$file" && test "$$dir" != "."; then \ - dir="/$$dir"; \ - $(mkdir_p) "$(distdir)$$dir"; \ - else \ - dir=''; \ - fi; \ - if test -d $$d/$$file; then \ - if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ - cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ - fi; \ - cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ - else \ - test -f $(distdir)/$$file \ - || cp -p $$d/$$file $(distdir)/$$file \ - || exit 1; \ - fi; \ - done -check-am: all-am -check: check-am -all-am: Makefile -installdirs: -install: install-am -install-exec: install-exec-am -install-data: install-data-am -uninstall: uninstall-am - -install-am: all-am - @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am - -installcheck: installcheck-am -install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install -mostlyclean-generic: - -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) - -clean-generic: - -distclean-generic: - -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -maintainer-clean-generic: - @echo "This command is intended for maintainers to use" - @echo "it deletes files that may require special tools to rebuild." -clean: clean-am - -clean-am: clean-generic clean-libtool mostlyclean-am - -distclean: distclean-am - -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-libtool - -dvi: dvi-am - -dvi-am: - -html: html-am - -info: info-am - -info-am: - -install-data-am: - -install-exec-am: - -install-info: install-info-am - -install-man: - -installcheck-am: - -maintainer-clean: maintainer-clean-am - -rm -f Makefile -maintainer-clean-am: distclean-am maintainer-clean-generic - -mostlyclean: mostlyclean-am - -mostlyclean-am: mostlyclean-generic mostlyclean-libtool - -pdf: pdf-am - -pdf-am: - -ps: ps-am - -ps-am: - -uninstall-am: uninstall-info-am - -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-exec install-exec-am \ - install-info install-info-am install-man install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ - uninstall-info-am - -# Tell versions [3.59,3.63) of GNU make to not export all variables. -# Otherwise a system limit (for SysV at least) may be exceeded. -.NOEXPORT: From ae0def5cc762de3002611ebe172a50d1ddaab777 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Tue, 10 Aug 2010 19:55:37 +0400 Subject: [PATCH 04/30] Implemented "trans" CLI command to list all active transacions. --- public-trunk/CLI/CLI.cpp | 14 ++++++++++++++ public-trunk/Control/ControlCommon.cpp | 12 ++++++++++++ public-trunk/Control/ControlCommon.h | 3 +++ 3 files changed, 29 insertions(+) diff --git a/public-trunk/CLI/CLI.cpp b/public-trunk/CLI/CLI.cpp index 19fba44..ba3564f 100644 --- a/public-trunk/CLI/CLI.cpp +++ b/public-trunk/CLI/CLI.cpp @@ -290,6 +290,19 @@ int tmsis(int argc, char** argv, ostream& os, istream& is) } +/** Print the transactions table. */ +int trans(int argc, char** argv, ostream& os, istream& is) +{ + if (argc!=1) return BAD_NUM_ARGS; + +// os << "TMSI IMSI IMEI age used" << endl; + gTransactionTable.dump(os); + os << endl << gTransactionTable.size() << " transactions in table" << endl; + return SUCCESS; +} + + + int findimsi(int argc, char** argv, ostream& os, istream& is) { @@ -759,6 +772,7 @@ Parser::Parser() addCommand("help", showHelp, "[command] -- list available commands or gets help on a specific command."); addCommand("exit", exit_function, "[wait] -- exit the application, either immediately, or waiting for existing calls to clear with a timeout in seconds"); addCommand("tmsis", tmsis, "[\"clear\"] or [\"dump\" filename] -- print/clear the TMSI table or dump it to a file."); + addCommand("trans", trans, "-- print the transactions table."); addCommand("findimsi", findimsi, "[IMSIPrefix] -- prints all imsi's that are prefixed by IMSIPrefix"); addCommand("sendsms", sendsms, " -- send SMS to , addressed from , after prompting."); addCommand("sendrrlp", sendrrlp, " -- send RRLP message to ."); diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 9f92f53..f0c5aaa 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -347,6 +347,18 @@ size_t TransactionTable::size() } +void TransactionTable::dump(ostream& os) const +{ + mLock.lock(); + TransactionMap::const_iterator tp = mTable.begin(); + while (tp != mTable.end()) { + os << hex << "0x" << tp->first << " " << dec << tp->second << endl; + ++tp; + } + mLock.unlock(); +} + + void Control::clearTransactionHistory( TransactionEntry& transaction ) { SIP::SIPEngine& engine = transaction.SIP(); diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index ff950b5..c518989 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -613,6 +613,9 @@ class TransactionTable { //@} size_t size(); + + /** Write entries as text to a stream. */ + void dump(std::ostream&) const; }; //@} // Transaction Table From 2da5a28a076566d45ef861fbc5571fc3f059e685 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Wed, 11 Aug 2010 22:02:28 +0400 Subject: [PATCH 05/30] Typos in comments and debug logs fixed. --- public-trunk/Control/ControlCommon.h | 2 +- public-trunk/SIP/SIPInterface.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index c518989..0448e08 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -596,7 +596,7 @@ class TransactionTable { Also clears dead entries during search. @param mobileID The mobile at to search for. @param target A TransactionEntry to accept the found record. - @return true is the mobile ID was foind. + @return true is the mobile ID was found. */ bool find(const GSM::L3MobileIdentity& mobileID, TransactionEntry& target); diff --git a/public-trunk/SIP/SIPInterface.cpp b/public-trunk/SIP/SIPInterface.cpp index 5f6c367..7caf1e4 100644 --- a/public-trunk/SIP/SIPInterface.cpp +++ b/public-trunk/SIP/SIPInterface.cpp @@ -300,12 +300,12 @@ bool SIPInterface::checkInvite( osip_message_t * msg ) // IMSIs are 14 or 15 char + "IMSI" prefix unsigned namelen = strlen(IMSI); if ((namelen>19)||(namelen<18)) { - LOG(WARN) << "INVITE with malformed username \"" << IMSI << "\""; + LOG(WARN) << "INVITE/MESSAGE with malformed username \"" << IMSI << "\""; return false; } // Skip first 4 char "IMSI". IMSI+=4; - // Make the mobile id we need for transaction and paging enties. + // Make the mobile id we need for transaction and paging entries. L3MobileIdentity mobile_id(IMSI); // Check SIP map. Repeated entry? Page again. @@ -343,7 +343,7 @@ bool SIPInterface::checkInvite( osip_message_t * msg ) if (!callerID) { callerID = emptyString; callerHost = emptyString; - LOG(NOTICE) << "INVITE with no From: username for " << mobile_id; + LOG(NOTICE) << "INVITE/MESSAGE with no From: username for " << mobile_id; } LOG(DEBUG) << "callerID " << callerID << "@" << callerHost; // Build the transaction table entry. From e13940488427cf7e9456bf9123dac5030838e218 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Wed, 18 Aug 2010 22:52:33 +0400 Subject: [PATCH 06/30] Implementation of Semaphore. --- public-trunk/CommonLibs/Threads.cpp | 9 +++++++++ public-trunk/CommonLibs/Threads.h | 31 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/public-trunk/CommonLibs/Threads.cpp b/public-trunk/CommonLibs/Threads.cpp index df2e3a1..2628ab1 100644 --- a/public-trunk/CommonLibs/Threads.cpp +++ b/public-trunk/CommonLibs/Threads.cpp @@ -95,6 +95,15 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime); } +/** Wait for semaphore to be signaled with timeout. +* @returns 0 on success, -1 on error or timeout. +*/ +int ThreadSemaphore::wait(unsigned timeout) const +{ + Timeval then(timeout); + struct timespec waitTime = then.timespec(); + return sem_timedwait(&mSem,&waitTime); +} void Thread::start(void *(*task)(void*), void *arg) { diff --git a/public-trunk/CommonLibs/Threads.h b/public-trunk/CommonLibs/Threads.h index 0f12972..4b65215 100644 --- a/public-trunk/CommonLibs/Threads.h +++ b/public-trunk/CommonLibs/Threads.h @@ -29,6 +29,7 @@ #include #include #include +#include class Mutex; @@ -120,7 +121,37 @@ class Signal { }; +/** Semaphore */ +class ThreadSemaphore { + private: + + mutable sem_t mSem; + + public: + + ThreadSemaphore(int pshared = 0, unsigned value = 0) { assert(sem_init(&mSem,pshared,value)!=-1); } + + ~ThreadSemaphore() { sem_destroy(&mSem); } + + /** Wait for semaphore to be signaled with timeout. + * @returns 0 on success, -1 on error or timeout. + */ + int wait (unsigned timeout) const; + + /** Wait for semaphore to be signaled infinitely. + * @returns 0 on success, -1 on error. + */ + int wait() const { return sem_wait(&mSem); } + + /** Check if semaphore has been signaled and disarm it. + * @returns 0 if semaphore has been signaled, -1 in other cases. + */ + int trywait() const { return sem_trywait(&mSem); } + + int post() { return sem_post (&mSem); } + +}; #define START_THREAD(thread,function,argument) \ thread.start((void *(*)(void*))function, (void*)argument); From 82dd78d69847ccbf0056c0917bf34c5e6e1d7cfa Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Tue, 24 Aug 2010 20:45:59 +0400 Subject: [PATCH 07/30] Fix a bug with Timeval difference calculation on 64-bit Linux. Subtracting uint32_t from uin32_t gives you uint32_t. And assigning result to a long doesn't make it a signed value, because on 64-bit Linux long is 64 bits. --- public-trunk/CommonLibs/Timeval.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public-trunk/CommonLibs/Timeval.cpp b/public-trunk/CommonLibs/Timeval.cpp index d61de0c..ae57345 100644 --- a/public-trunk/CommonLibs/Timeval.cpp +++ b/public-trunk/CommonLibs/Timeval.cpp @@ -70,8 +70,8 @@ double Timeval::seconds() const long Timeval::delta(const Timeval& other) const { // 2^31 milliseconds is just over 4 years. - long deltaS = other.sec() - sec(); - long deltaUs = other.usec() - usec(); + int32_t deltaS = other.sec() - sec(); + int32_t deltaUs = other.usec() - usec(); return 1000*deltaS + deltaUs/1000; } From d0b797e62c808ec7ae1629c122dff8d560539d33 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 17:59:37 +0400 Subject: [PATCH 08/30] Don't put real code into assert(). If you compile with optimization and NDEBUG is defined, then the whole assert() is replaced with ((void) 0) and your code is not executed at all. --- public-trunk/CommonLibs/Threads.cpp | 20 +++++++++++++------- public-trunk/CommonLibs/Threads.h | 6 +++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/public-trunk/CommonLibs/Threads.cpp b/public-trunk/CommonLibs/Threads.cpp index 2628ab1..6180c2d 100644 --- a/public-trunk/CommonLibs/Threads.cpp +++ b/public-trunk/CommonLibs/Threads.cpp @@ -72,16 +72,17 @@ void unlockCerr() Mutex::Mutex() { - assert(!pthread_mutexattr_init(&mAttribs)); - assert(!pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE)); - assert(!pthread_mutex_init(&mMutex,&mAttribs)); + pthread_mutexattr_init(&mAttribs); + int s = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE); + assert(s!=0); + pthread_mutex_init(&mMutex,&mAttribs); } Mutex::~Mutex() { pthread_mutex_destroy(&mMutex); - assert(!pthread_mutexattr_destroy(&mAttribs)); + pthread_mutexattr_destroy(&mAttribs); } @@ -107,10 +108,15 @@ int ThreadSemaphore::wait(unsigned timeout) const void Thread::start(void *(*task)(void*), void *arg) { + int s; assert(mThread==((pthread_t)0)); - assert(!pthread_attr_init(&mAttrib)); - assert(!pthread_attr_setstacksize(&mAttrib, mStackSize)); - assert(!pthread_create(&mThread, &mAttrib, task, arg)); + + s = pthread_attr_init(&mAttrib); + assert(s == 0); + s = pthread_attr_setstacksize(&mAttrib, mStackSize); + assert(s == 0); + s = pthread_create(&mThread, &mAttrib, task, arg); + assert(s == 0); } diff --git a/public-trunk/CommonLibs/Threads.h b/public-trunk/CommonLibs/Threads.h index 4b65215..f390d9a 100644 --- a/public-trunk/CommonLibs/Threads.h +++ b/public-trunk/CommonLibs/Threads.h @@ -98,7 +98,7 @@ class Signal { public: - Signal() { assert(!pthread_cond_init(&mSignal,NULL)); } + Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(s == 0); } ~Signal() { pthread_cond_destroy(&mSignal); } @@ -176,14 +176,14 @@ class Thread { Destroy the Thread. It should be stopped and joined. */ - ~Thread() { assert(!pthread_attr_destroy(&mAttrib)); } + ~Thread() { int s = pthread_attr_destroy(&mAttrib); assert(s==0); } /** Start the thread on a task. */ void start(void *(*task)(void*), void *arg); /** Join a thread that will stop on its own. */ - void join() { assert(!pthread_join(mThread,NULL)); } + void join() { int s = pthread_join(mThread,NULL); assert(s==0); } }; From d712b09cdda5bc5a6e26be4dcdb59591062ced2b Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:01:55 +0400 Subject: [PATCH 09/30] Fix compiler warning about char* and constant strings. --- public-trunk/smqueue/smnet.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public-trunk/smqueue/smnet.cpp b/public-trunk/smqueue/smnet.cpp index 94e2c08..5d46021 100644 --- a/public-trunk/smqueue/smnet.cpp +++ b/public-trunk/smqueue/smnet.cpp @@ -420,8 +420,9 @@ SMnet::string_addr (struct sockaddr *sa, socklen_t len, bool withport) bool SMnet::parse_addr (const char *str, char *sockad, socklen_t maxlen, socklen_t *len) { - char *p = strchr(str, ':'); - char *host, *port; + const char *p = strchr(str, ':'); + char *host; + const char *port; if (p == NULL) return false; From 6cddd36139621e6c51533ac9a7068c2e1c5c2a4e Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:23:36 +0400 Subject: [PATCH 10/30] Typos in comments fixed. --- public-trunk/Control/ControlCommon.cpp | 2 +- public-trunk/Control/ControlCommon.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index f0c5aaa..39e9c20 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -322,7 +322,7 @@ bool TransactionTable::find(const L3MobileIdentity& mobileID, TransactionEntry& // Since clearDeadEntries is also linear, do that here, too. - // Brtue force search. + // Brute force search. bool foundIt = false; mLock.lock(); clearDeadEntries(); diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index 0448e08..7a4355f 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -516,7 +516,7 @@ class TransactionEntry { /** Reset all Q.931 timers. */ void resetTimers(); - /** Retrns true if the transaction is "dead". */ + /** Returns true if the transaction is "dead". */ bool dead() const; private: From bac46e3b7dd0a5c2dfa105f17cafae58ff9a8883 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:00:49 +0400 Subject: [PATCH 11/30] Better documentation and error reporting for ThreadSemaphore. --- public-trunk/CommonLibs/Threads.cpp | 65 ++++++++++++++++++++++++++--- public-trunk/CommonLibs/Threads.h | 43 +++++++++++++++---- 2 files changed, 93 insertions(+), 15 deletions(-) diff --git a/public-trunk/CommonLibs/Threads.cpp b/public-trunk/CommonLibs/Threads.cpp index 6180c2d..dd22087 100644 --- a/public-trunk/CommonLibs/Threads.cpp +++ b/public-trunk/CommonLibs/Threads.cpp @@ -28,6 +28,7 @@ #include "Threads.h" #include "Timeval.h" +#include using namespace std; @@ -96,14 +97,66 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime); } -/** Wait for semaphore to be signaled with timeout. -* @returns 0 on success, -1 on error or timeout. -*/ -int ThreadSemaphore::wait(unsigned timeout) const +ThreadSemaphore::Result ThreadSemaphore::wait(unsigned timeoutMs) { - Timeval then(timeout); + Timeval then(timeoutMs); struct timespec waitTime = then.timespec(); - return sem_timedwait(&mSem,&waitTime); + int s; + while ((s = sem_timedwait(&mSem,&waitTime)) == -1 && errno == EINTR) + continue; + + if (s == -1) + { + if (errno == ETIMEDOUT) + { + return TSEM_TIMEOUT; + } + return TSEM_ERROR; + } + return TSEM_OK; +} + +ThreadSemaphore::Result ThreadSemaphore::wait() +{ + int s; + while ((s = sem_wait(&mSem)) == -1 && errno == EINTR) + continue; + + if (s == -1) + { + return TSEM_ERROR; + } + return TSEM_OK; +} + +ThreadSemaphore::Result ThreadSemaphore::trywait() +{ + int s; + while ((s = sem_trywait(&mSem)) == -1 && errno == EINTR) + continue; + + if (s == -1) + { + if (errno == EAGAIN) + { + return TSEM_TIMEOUT; + } + return TSEM_ERROR; + } + return TSEM_OK; +} + +ThreadSemaphore::Result ThreadSemaphore::post() +{ + if (sem_post(&mSem) != 0) + { + if (errno == EOVERFLOW) + { + return TSEM_OVERFLOW; + } + return TSEM_ERROR; + } + return TSEM_OK; } void Thread::start(void *(*task)(void*), void *arg) diff --git a/public-trunk/CommonLibs/Threads.h b/public-trunk/CommonLibs/Threads.h index f390d9a..66928e2 100644 --- a/public-trunk/CommonLibs/Threads.h +++ b/public-trunk/CommonLibs/Threads.h @@ -126,30 +126,55 @@ class ThreadSemaphore { private: - mutable sem_t mSem; + sem_t mSem; public: - ThreadSemaphore(int pshared = 0, unsigned value = 0) { assert(sem_init(&mSem,pshared,value)!=-1); } + enum Result { + TSEM_OK, ///< Success. + TSEM_TIMEOUT, ///< wait() or trywait() timed out. + TSEM_OVERFLOW, ///< post() overflows a semaphore + TSEM_ERROR ///< Generic error. + }; + + /** Create and initialize semaphore. + * @param[in] value - initial semaphore value. + */ + ThreadSemaphore(unsigned value = 0) + { + int s = sem_init(&mSem,0,value); + assert(s == 0); + } ~ThreadSemaphore() { sem_destroy(&mSem); } /** Wait for semaphore to be signaled with timeout. - * @returns 0 on success, -1 on error or timeout. + * @param[in] timeoutMs - timeout in milliseconds + * + * @retval TSEM_OK on success. + * @retval TSEM_TIMEOUT on timeout. + * @retval TSEM_ERROR on error. */ - int wait (unsigned timeout) const; + Result wait(unsigned timeoutMs); /** Wait for semaphore to be signaled infinitely. - * @returns 0 on success, -1 on error. + * @retval TSEM_OK on success. + * @retval TSEM_ERROR on error. */ - int wait() const { return sem_wait(&mSem); } + Result wait(); /** Check if semaphore has been signaled and disarm it. - * @returns 0 if semaphore has been signaled, -1 in other cases. + * @retval TSEM_OK is semaphore is signaled. + * @retval TSEM_TIMEOUT if semaphore is not signaled. + * @retval TSEM_ERROR on error. */ - int trywait() const { return sem_trywait(&mSem); } + Result trywait(); - int post() { return sem_post (&mSem); } + /** Signal semaphore. + * @retval TSEM_OK on success. + * @retval TSEM_ERROR on error. + */ + Result post(); }; From b39dc1bc78fefcce1dee4fdc4fdd1f60798ac988 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Tue, 10 Aug 2010 21:32:45 +0400 Subject: [PATCH 12/30] Fix license header in USSD-specific files to AGPLv3. --- public-trunk/Control/USSDControl.cpp | 10 ++++---- public-trunk/GSM/GSML3NonCallSSComponents.cpp | 25 +++++++++---------- public-trunk/GSM/GSML3NonCallSSComponents.h | 24 +++++++++--------- public-trunk/GSM/GSML3NonCallSSElements.cpp | 24 +++++++++--------- public-trunk/GSM/GSML3NonCallSSElements.h | 24 +++++++++--------- public-trunk/GSM/GSML3NonCallSSMessages.cpp | 25 +++++++++---------- public-trunk/GSM/GSML3NonCallSSMessages.h | 25 +++++++++---------- 7 files changed, 77 insertions(+), 80 deletions(-) diff --git a/public-trunk/Control/USSDControl.cpp b/public-trunk/Control/USSDControl.cpp index a468922..a204102 100644 --- a/public-trunk/Control/USSDControl.cpp +++ b/public-trunk/Control/USSDControl.cpp @@ -1,24 +1,24 @@ /**@file USSD Control (L3) */ /* -* Copyright 2008, 2009 Free Software Foundation, Inc. +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * -* This software is distributed under the terms of the GNU Public License. +* 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 General Public License as published by + 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 General Public License for more details. + GNU Affero General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ diff --git a/public-trunk/GSM/GSML3NonCallSSComponents.cpp b/public-trunk/GSM/GSML3NonCallSSComponents.cpp index 1eb0449..c5da168 100644 --- a/public-trunk/GSM/GSML3NonCallSSComponents.cpp +++ b/public-trunk/GSM/GSML3NonCallSSComponents.cpp @@ -1,26 +1,25 @@ /**@file Call independent Supplementary Service Control. Facility element components, GSM 04.80 3.6.1. */ - /* -* Copyright 2008, 2009 Free Software Foundation, Inc. +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * -* This software is distributed under the terms of the GNU Public License. +* 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 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 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 General Public License for more details. + 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 General Public License -along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ diff --git a/public-trunk/GSM/GSML3NonCallSSComponents.h b/public-trunk/GSM/GSML3NonCallSSComponents.h index 82c9ef4..49670bf 100644 --- a/public-trunk/GSM/GSML3NonCallSSComponents.h +++ b/public-trunk/GSM/GSML3NonCallSSComponents.h @@ -1,25 +1,25 @@ /**@file Call independent Supplementary Service Control. Facility element components, GSM 04.80 3.6.1. */ /* -* Copyright 2008 Free Software Foundation, Inc. +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * -* This software is distributed under the terms of the GNU Public License. +* 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 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 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 General Public License for more details. + 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 General Public License -along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ diff --git a/public-trunk/GSM/GSML3NonCallSSElements.cpp b/public-trunk/GSM/GSML3NonCallSSElements.cpp index 28e77f7..b7145c6 100644 --- a/public-trunk/GSM/GSML3NonCallSSElements.cpp +++ b/public-trunk/GSM/GSML3NonCallSSElements.cpp @@ -1,25 +1,25 @@ /**@file Elements for call independent Supplementary Service Control, GSM 04.80 3. */ /* -* Copyright 2008, 2009 Free Software Foundation, Inc. +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * -* This software is distributed under the terms of the GNU Public License. +* 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 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 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 General Public License for more details. + 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 General Public License -along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ diff --git a/public-trunk/GSM/GSML3NonCallSSElements.h b/public-trunk/GSM/GSML3NonCallSSElements.h index 6eb289c..db98fb6 100644 --- a/public-trunk/GSM/GSML3NonCallSSElements.h +++ b/public-trunk/GSM/GSML3NonCallSSElements.h @@ -1,25 +1,25 @@ /**@file Elements for call independent Supplementary Service Control, GSM 04.80 3. */ /* -* Copyright 2008, 2009 Free Software Foundation, Inc. +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * -* This software is distributed under the terms of the GNU Public License. +* 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 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 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 General Public License for more details. + 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 General Public License -along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ diff --git a/public-trunk/GSM/GSML3NonCallSSMessages.cpp b/public-trunk/GSM/GSML3NonCallSSMessages.cpp index b012884..7e47737 100644 --- a/public-trunk/GSM/GSML3NonCallSSMessages.cpp +++ b/public-trunk/GSM/GSML3NonCallSSMessages.cpp @@ -1,26 +1,25 @@ /** @file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */ - /* -* Copyright 2008, 2009 Free Software Foundation, Inc. +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * -* This software is distributed under the terms of the GNU Public License. +* 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 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 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 General Public License for more details. + 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 General Public License -along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ diff --git a/public-trunk/GSM/GSML3NonCallSSMessages.h b/public-trunk/GSM/GSML3NonCallSSMessages.h index 6946a68..c5bc7ed 100644 --- a/public-trunk/GSM/GSML3NonCallSSMessages.h +++ b/public-trunk/GSM/GSML3NonCallSSMessages.h @@ -1,26 +1,25 @@ /**@file Messages for call independent Supplementary Service Control, GSM 04.80 2.2. */ - /* -* Copyright 2008, 2009 Free Software Foundation, Inc. +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. * -* This software is distributed under the terms of the GNU Public License. +* 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 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 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 General Public License for more details. + 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 General Public License -along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ From 7f5fbdb9064fedd8442231713d4806d24cc97be5 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Tue, 10 Aug 2010 21:45:47 +0400 Subject: [PATCH 13/30] Create a constant for max USSD message length and make continuation string configurable. --- public-trunk/Control/ControlCommon.cpp | 11 ++++++----- public-trunk/Control/ControlCommon.h | 15 +++++++++++++-- public-trunk/apps/OpenBTS.config.example | 3 +++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 18da26c..a984a3a 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -670,12 +670,13 @@ unsigned USSDHandler::waitUSSDData(Control::USSDData::USSDMessageType* messageTy void USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType, std::string USSDString) { - if (USSDString.length()>183) + if (USSDString.length()>USSD_MAX_CHARS_7BIT) { - mString = USSDString; - mString.erase(mString.begin(), mString.begin()+181); - USSDString.erase(USSDString.begin()+181, USSDString.end()); - USSDString+=">"; + 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 = ""; TransactionEntry transaction; diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index ffcbc3c..f47e6bc 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -61,6 +61,9 @@ class L3IMSIDetachIndication; class L3PagingResponse; }; +// Reference to a global config table, used all over the system. +extern ConfigurationTable gConfig; + /**@namespace Control This namepace is for use by the control layer. */ namespace Control { @@ -920,24 +923,32 @@ class USSDHandler { trywait = 0, infinitely = 120000 }; + enum + { + USSD_MAX_CHARS_7BIT = 182 ///< See GSM 03.38 5 Cell Broadcast Data Coding Scheme + }; + private: unsigned mTransactionID; protected: std::string mString; + std::string mContinueStr; public: /** This form is used for MO USSD */ USSDHandler(unsigned wTransactionID) :mTransactionID(wTransactionID), - mString("") + mContinueStr(gConfig.getStr("USSD.ContinueStr")) {} + /** This form is used for MT USSD */ USSDHandler(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString) - :mString("") + :mContinueStr(gConfig.getStr("USSD.ContinueStr")) { mTransactionID = USSDDispatcher(mobileIdentity, TIFlag, TIValue, messageType, ussdString, false); } + /** Wait USSD data from MS. Return: 0 - successful, 1 - clear transaction, 2 - error or timeout */ unsigned waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout); /** Post USSD data and update transaction with new USSDData (messageType and USSDString)*/ diff --git a/public-trunk/apps/OpenBTS.config.example b/public-trunk/apps/OpenBTS.config.example index bd650fb..ba7fd1d 100644 --- a/public-trunk/apps/OpenBTS.config.example +++ b/public-trunk/apps/OpenBTS.config.example @@ -192,6 +192,9 @@ 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. From dd366df4f441bac8441701991060189fa210ead0 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Wed, 11 Aug 2010 16:39:46 +0400 Subject: [PATCH 14/30] BUGFIX: Forgotten parentheses in USSDHandler::postUSSDData(). --- public-trunk/Control/ControlCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index a984a3a..ff7b7d7 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -674,7 +674,7 @@ void USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType, s { int contLen = mContinueStr.length(); mString = USSDString.substr(USSD_MAX_CHARS_7BIT-contLen, - USSDString.length()-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; } From 58821a8f74b0d6222096c99676a66804b46d6c5a Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Wed, 11 Aug 2010 22:04:03 +0400 Subject: [PATCH 15/30] Add comments anf better formating to USSDHandler::postUSSDData(). --- public-trunk/Control/ControlCommon.cpp | 39 ++++++++++++++++---------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index ff7b7d7..17116f6 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -670,21 +670,30 @@ unsigned USSDHandler::waitUSSDData(Control::USSDData::USSDMessageType* messageTy void USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType, std::string USSDString) { - 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 = ""; - TransactionEntry transaction; - gTransactionTable.find(mTransactionID, transaction); - transaction.ussdData()->MessageType(messageType); - transaction.ussdData()->USSDString(USSDString); - gTransactionTable.update(transaction); - transaction.ussdData()->postNW(); + 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; + gTransactionTable.find(mTransactionID, transaction); + + // Step 2 -- Update transaction with the data to send + transaction.ussdData()->MessageType(messageType); + transaction.ussdData()->USSDString(USSDString); + gTransactionTable.update(transaction); + + // Step 3 -- Notify the dispatcher thread that data is ready to be sent + transaction.ussdData()->postNW(); } void *USSDHandler::runWrapper(void *pThis) From 7d9b0497bcd14993e54e2fcacded453449bb681d Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 19 Aug 2010 00:10:15 +0400 Subject: [PATCH 16/30] Added MO-USSD handlers map to config. Now you can configure which request will go to which handler. --- public-trunk/Control/ControlCommon.cpp | 34 ++++++++++++++++++++---- public-trunk/Control/ControlCommon.h | 5 +++- public-trunk/apps/OpenBTS.config.example | 11 ++++++-- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 17116f6..555d4ae 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -37,6 +37,7 @@ #include #include +#include using namespace std; using namespace GSM; @@ -1051,7 +1052,30 @@ void Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH } } -unsigned Control::USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue,Control::USSDData::USSDMessageType messageType, string ussdString, bool MO) +bool 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); @@ -1063,19 +1087,19 @@ unsigned Control::USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned transaction.Q931State(Control::TransactionEntry::USSDworking); transaction.ussdData()->postMS(); gTransactionTable.update(transaction); - std::string handleName = gConfig.getStr("USSD.Handler.MO"); + Thread* thread = new Thread; - if (handleName=="HTTP") + if (USSDMatchHandler("HTTP", ussdString)) { MOHttpHandler* handler = new MOHttpHandler(transaction.ID()); thread->start((void*(*)(void*))USSDHandler::runWrapper, handler); } - else if (handleName=="CLI") + else if (USSDMatchHandler("CLI", ussdString)) { MOCLIHandler* handler = new MOCLIHandler(transaction.ID()); thread->start((void*(*)(void*))USSDHandler::runWrapper, handler); } - else if (handleName=="Test") + else if (USSDMatchHandler("Test", ussdString)) { MOTestHandler* handler = new MOTestHandler(transaction.ID()); thread->start((void*(*)(void*))USSDHandler::runWrapper, handler); diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index f47e6bc..db509bb 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -913,7 +913,10 @@ extern Control::TMSITable gTMSITable; namespace Control { -unsigned USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString, bool MO); +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 { diff --git a/public-trunk/apps/OpenBTS.config.example b/public-trunk/apps/OpenBTS.config.example index ba7fd1d..f038e09 100644 --- a/public-trunk/apps/OpenBTS.config.example +++ b/public-trunk/apps/OpenBTS.config.example @@ -183,8 +183,15 @@ SMS.DefaultDestSMSC 0000 # USSD timeout for waiting MS response USSD.timeout 100000 -# USSD MO Handler (HTTP, CLI or Test) -USSD.Handler.MO CLI +# USSD handlers map. +# Option key has form USSD.Handler., where 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 From 7ae0243308e6a6c9d7a719f6f5dfa57661d7119c Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:02:43 +0400 Subject: [PATCH 17/30] Implement USSD short code in smqueue for testing purposes. --- public-trunk/smqueue/smcommands.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/public-trunk/smqueue/smcommands.cpp b/public-trunk/smqueue/smcommands.cpp index 38acd9a..dde22c8 100644 --- a/public-trunk/smqueue/smcommands.cpp +++ b/public-trunk/smqueue/smcommands.cpp @@ -82,6 +82,18 @@ shortcode_quick_chk (const char *imsi, const char *msgtext, return SCA_REPLY; } +enum short_code_action +shortcode_ussd_test (const char *imsi, const char *msgtext, + short_code_params *scp) +{ + ostringstream answer; + + answer << "3" << endl; + answer << "USSD test ok!"; + scp->scp_reply = new_strdup(answer.str().c_str()); + return SCA_REPLY; +} + /* * 411 -- information. * Start by telling people their phone number. @@ -379,5 +391,6 @@ SMqueue::init_smcommands (short_code_map_t *scm) (*scm)[gConfig.getStr("SC.ZapQueued.Code")] = shortcode_zap_queued; if (gConfig.defines("SC.WhiplashQuit.Code")) (*scm)[gConfig.getStr("SC.WhiplashQuit.Code")] = whiplash_quit; + (*scm)["USSD"] = shortcode_ussd_test; // (*scm)["666"] = shortcode_text_access; } From 86b066da63b0d6b4625dc68f193a231e315d2523 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:04:53 +0400 Subject: [PATCH 18/30] Fix comments typos and code formating. --- public-trunk/Control/USSDControl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public-trunk/Control/USSDControl.cpp b/public-trunk/Control/USSDControl.cpp index a204102..68b2700 100644 --- a/public-trunk/Control/USSDControl.cpp +++ b/public-trunk/Control/USSDControl.cpp @@ -169,16 +169,16 @@ void USSDSend(string USSDString, unsigned InvokeID, unsigned TI, unsigned TIFlag { //send error L3NonCallSSComponentReturnError* ComponentPtr = new L3NonCallSSComponentReturnError(L3NonCallSSInvokeID(InvokeID), - L3NonCallSSErrorCode(GSM::L3NonCallSSErrorCode::SystemFailure)); + 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) + else if(messageType == Control::USSDData::release) { - // NOTE: On some phones (e.g. Nokia 3310, Alcatel BG3) if this parameteris set to a non-empty string, + // 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. @@ -206,7 +206,7 @@ Control::USSDData::USSDMessageType USSDParse(L3NonCallSSMessage* Message, string { L3Frame Frame; Control::USSDData::USSDMessageType messageType = Control::USSDData::error; - // Low-level mesage flow is described at GSM 04.90 6.1. + // 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 From 77d380565bd82c81118ff0ab85ff7997d8e2ed19 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:06:25 +0400 Subject: [PATCH 19/30] Send correct text string with USSD release message. --- public-trunk/Control/USSDControl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public-trunk/Control/USSDControl.cpp b/public-trunk/Control/USSDControl.cpp index 68b2700..93f6431 100644 --- a/public-trunk/Control/USSDControl.cpp +++ b/public-trunk/Control/USSDControl.cpp @@ -184,7 +184,8 @@ void USSDSend(string USSDString, unsigned InvokeID, unsigned TI, unsigned TIFlag // recommend to set this string to an empty string. L3NonCallSSComponentReturnResult* ComponentPtr = new L3NonCallSSComponentReturnResult(L3NonCallSSInvokeID(InvokeID)); ComponentPtr->operationCode(L3NonCallSSOperationCode(GSM::L3NonCallSSOperationCode::ProcessUnstructuredSSRequest)); - ComponentPtr->parameters(L3NonCallSSParameters("release")); + // TODO:: How to drop session silently? + ComponentPtr->parameters(L3NonCallSSParameters(USSDString.c_str())); L3NonCallSSReleaseCompleteMessage ReleaseCompleteMessage(TIFlag, TI); if (TIFlag == 1) { From e8ffcdb72f3b7a1c398309019337de9e82c7a571 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:13:40 +0400 Subject: [PATCH 20/30] Update SIP interface to support SIP MESSAGEs for USSD. --- public-trunk/SIP/SIPInterface.cpp | 83 ++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/public-trunk/SIP/SIPInterface.cpp b/public-trunk/SIP/SIPInterface.cpp index 7caf1e4..28f8929 100644 --- a/public-trunk/SIP/SIPInterface.cpp +++ b/public-trunk/SIP/SIPInterface.cpp @@ -248,6 +248,7 @@ bool SIPInterface::checkInvite( osip_message_t * msg ) // Check for INVITE or MESSAGE methods. GSM::ChannelType requiredChannel; bool channelAvailable = false; + bool shouldPage = true; GSM::L3CMServiceType serviceType; if (strcmp(method,"INVITE") == 0) { // INVITE is for MTC. @@ -264,10 +265,25 @@ bool SIPInterface::checkInvite( osip_message_t * msg ) serviceType = L3CMServiceType::MobileTerminatedCall; } else if (strcmp(method,"MESSAGE") == 0) { - // MESSAGE is for MTSMS. - requiredChannel = GSM::SDCCHType; - channelAvailable = gBTS.SDCCHAvailable(); - serviceType = L3CMServiceType::MobileTerminatedShortMessage; + // MESSAGE is for MTSMS or USSD + if ( strcmp(gConfig.getStr("USSD.SIP.user"), msg->from->url->username)==0 + && strcmp(gConfig.getStr("USSD.SIP.domain"), msg->from->url->host)==0) + { + LOG(INFO) << "received MESSAGE is USSD from: " + << msg->from->url->username << "@" << msg->from->url->host; + requiredChannel = GSM::SDCCHType; + // TODO:: Understand how to behave when we need to page? + channelAvailable = true; //gBTS.SDCCHAvailable(); + serviceType = L3CMServiceType::SupplementaryService; + } + else + { + LOG(INFO) << "received MESSAGE is SMS from: " + << msg->from->url->username << "@" << msg->from->url->host; + requiredChannel = GSM::SDCCHType; + channelAvailable = gBTS.SDCCHAvailable(); + serviceType = L3CMServiceType::MobileTerminatedShortMessage; + } } else { // We must not handle this method. @@ -309,7 +325,8 @@ bool SIPInterface::checkInvite( osip_message_t * msg ) L3MobileIdentity mobile_id(IMSI); // Check SIP map. Repeated entry? Page again. - if (mSIPMap.map().readNoBlock(call_id_num) != NULL) { + // Skip this for USSD. + if ( mSIPMap.map().readNoBlock(call_id_num) != NULL) { TransactionEntry transaction; if (!gTransactionTable.find(mobile_id,transaction)) { // FIXME -- Send "call leg non-existent" response on SIP interface. @@ -346,15 +363,33 @@ bool SIPInterface::checkInvite( osip_message_t * msg ) LOG(NOTICE) << "INVITE/MESSAGE with no From: username for " << mobile_id; } LOG(DEBUG) << "callerID " << callerID << "@" << callerHost; - // Build the transaction table entry. - // This constructor sets TI flag=0, TI=0 for an MT transaction. - TransactionEntry transaction(mobile_id,serviceType,callerID); + + TransactionEntry transaction; + // In case of USSD we should check for existing transaction first, because + // SIP MESSAGEs are sent out of call, our internal while USSD transaction + // stays alive for the whole duration of a session. + if ( serviceType == L3CMServiceType::SupplementaryService + && gTransactionTable.find(mobile_id,L3CMServiceType::SupplementaryService,transaction)) + { + // It's old USSD. No need to page. + shouldPage = false; + LOG(DEBUG) << "Existing USSD transaction found: " << transaction; + } + else + { + // It's not USSD or there are no existing transaction for it. + // Build new transaction table entry. + // This constructor sets TI flag=0, TI=0 for an MT transaction. + transaction = TransactionEntry(mobile_id,serviceType,callerID); + LOG(DEBUG) << "Created new transaction"; + } LOG(DEBUG) << "call_id_num \"" << call_id_num << "\""; LOG(DEBUG) << "IMSI \"" << IMSI << "\""; transaction.SIP().User(call_id_num,IMSI,callerID,callerHost); transaction.SIP().saveINVITE(msg); - if (serviceType == L3CMServiceType::MobileTerminatedShortMessage) { + if ( serviceType == L3CMServiceType::MobileTerminatedShortMessage + || serviceType == L3CMServiceType::SupplementaryService) { osip_body_t *body; osip_message_get_body(msg,0,&body); if (!body) return false; @@ -362,15 +397,29 @@ bool SIPInterface::checkInvite( osip_message_t * msg ) if (text) { transaction.message(text, body->length); } - 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; } From 00b1810997b1b2d07da036e803ec272d178ddbc0 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:15:16 +0400 Subject: [PATCH 21/30] Store mobile IMSI instead of TMSI in MOUSSD transaction. --- public-trunk/Control/USSDControl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public-trunk/Control/USSDControl.cpp b/public-trunk/Control/USSDControl.cpp index 93f6431..39fd0d3 100644 --- a/public-trunk/Control/USSDControl.cpp +++ b/public-trunk/Control/USSDControl.cpp @@ -270,8 +270,11 @@ void Control::MOUSSDController(const L3CMServiceRequest *request, LogicalChannel 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(); - LOG(DEBUG) << "mobileIdentity: "<mobileIdentity()<<" IMSI:"<send(L3CMServiceAccept()); From c288b250b612750ff7d7eec3030867ec87a58ed2 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:18:14 +0400 Subject: [PATCH 22/30] Remove second USSDHandler constructor. --- public-trunk/CLI/CLI.cpp | 3 ++- public-trunk/Control/ControlCommon.h | 11 ++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/public-trunk/CLI/CLI.cpp b/public-trunk/CLI/CLI.cpp index 6bb653a..0f29485 100644 --- a/public-trunk/CLI/CLI.cpp +++ b/public-trunk/CLI/CLI.cpp @@ -382,7 +382,8 @@ int sendUSSD(int argc, char** argv, ostream& os, istream& is) if (argc!=2) return BAD_NUM_ARGS; char *IMSI = argv[1]; GSM::L3MobileIdentity mobileIdentity(IMSI); - Control::MTTestHandler handler(mobileIdentity, (unsigned)1, (unsigned)0, Control::USSDData::REGrequest, std::string("REGrequest")); + 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; diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index db509bb..dffd20d 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -945,13 +945,6 @@ class USSDHandler { mContinueStr(gConfig.getStr("USSD.ContinueStr")) {} - /** This form is used for MT USSD */ - USSDHandler(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString) - :mContinueStr(gConfig.getStr("USSD.ContinueStr")) - { - mTransactionID = USSDDispatcher(mobileIdentity, TIFlag, TIValue, messageType, ussdString, false); - } - /** Wait USSD data from MS. Return: 0 - successful, 1 - clear transaction, 2 - error or timeout */ unsigned waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout); /** Post USSD data and update transaction with new USSDData (messageType and USSDString)*/ @@ -994,8 +987,8 @@ class MOCLIHandler : public USSDHandler { class MTTestHandler : public USSDHandler { public: - MTTestHandler(GSM::L3MobileIdentity &mobileIdentity, unsigned TIFlag, unsigned TIValue, Control::USSDData::USSDMessageType messageType, std::string ussdString) - :USSDHandler(mobileIdentity,TIFlag, TIValue, messageType, ussdString) + MTTestHandler(unsigned wTransactionID) + :USSDHandler(wTransactionID) {} void run(); From 9c542926ac5d6bb7e96191df1d7a8b3a01da4fd8 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:19:15 +0400 Subject: [PATCH 23/30] Always initialize mUSSDData in a transaction. --- public-trunk/Control/ControlCommon.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 555d4ae..d2a8ab3 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -76,6 +76,7 @@ ostream& Control::operator<<(ostream& os, const USSDData& data) TransactionEntry::TransactionEntry() :mID(gTransactionTable.newID()), mQ931State(NullState), + mUSSDData(NULL), mT301(T301ms), mT302(T302ms), mT303(T303ms), mT304(T304ms), mT305(T305ms), mT308(T308ms), mT310(T310ms), mT313(T313ms), @@ -95,6 +96,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber, mTIFlag(1), mTIValue(0), mCalling(wCalling), mQ931State(NullState), + mUSSDData(NULL), mT301(T301ms), mT302(T302ms), mT303(T303ms), mT304(T304ms), mT305(T305ms), mT308(T308ms), mT310(T310ms), mT313(T313ms), @@ -115,6 +117,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber, mTIFlag(0), mTIValue(wTIValue), mCalled(wCalled), mQ931State(NullState), + mUSSDData(NULL), mT301(T301ms), mT302(T302ms), mT303(T303ms), mT304(T304ms), mT305(T305ms), mT308(T308ms), mT310(T310ms), mT313(T313ms), @@ -133,6 +136,7 @@ TransactionEntry::TransactionEntry(const L3MobileIdentity& wSubscriber, mSubscriber(wSubscriber),mService(wService), mTIFlag(1),mTIValue(wTIValue),mCalling(wCalling), mQ931State(NullState), + mUSSDData(NULL), mT301(T301ms), mT302(T302ms), mT303(T303ms), mT304(T304ms), mT305(T305ms), mT308(T308ms), mT310(T310ms), mT313(T313ms), @@ -150,8 +154,9 @@ TransactionEntry::TransactionEntry(const GSM::L3MobileIdentity& wSubscriber, USSDData* wUSSDData) :mID(gTransactionTable.newID()), mSubscriber(wSubscriber),mService(wService), - mTIFlag(wTIFlag),mTIValue(wTIValue),mUSSDData(wUSSDData), + mTIFlag(wTIFlag),mTIValue(wTIValue), mQ931State(NullState), + mUSSDData(wUSSDData), mT301(T301ms), mT302(T302ms), mT303(T303ms), mT304(T304ms), mT305(T305ms), mT308(T308ms), mT310(T310ms), mT313(T313ms), From 9f707665ba75da51baf295e49fedb68066f1cbb0 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:21:47 +0400 Subject: [PATCH 24/30] Better result reporting for waitUSSDData() and postUSSDData(). --- public-trunk/Control/ControlCommon.cpp | 93 +++++++++++++++++++------- public-trunk/Control/ControlCommon.h | 36 ++++++---- 2 files changed, 92 insertions(+), 37 deletions(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index d2a8ab3..23526ed 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -628,54 +628,81 @@ void TMSITable::load(const char* filename) fclose(fp); } -unsigned USSDHandler::waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout = USSDHandler::infinitely) +USSDHandler::ResultCode USSDHandler::waitUSSDData(Control::USSDData::USSDMessageType* messageType, + std::string* USSDString, + unsigned timeout) { TransactionEntry transaction; - // Wait for MS to signal that data is ready - gTransactionTable.find(mTransactionID, 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) { - if (transaction.ussdData()->waitMS()!=0) + // wait infinitely + if (pUssdData->waitMS()!=0) { LOG(ERROR) << "USSDDate semaphore returned error: " << errno; - return 2; + return USSD_ERROR; } } - else if((timeout > USSDHandler::trywait) && (timeout < USSDHandler::infinitely)) + else if ((timeout > USSDHandler::trywait) && (timeout < USSDHandler::infinitely)) { - //wait - if (transaction.ussdData()->waitMS(timeout)!=0) + // wait + if (pUssdData->waitMS(timeout)!=0) { LOG(DEBUG) << "USSDDate semaphore returned error or timeout"; - return 2; + return USSD_TIMEOUT; } } else { - //trywait - if (transaction.ussdData()->trywaitMS()!=0) + // trywait + if (pUssdData->trywaitMS()!=0) { LOG(DEBUG) << "USSDDate semaphore returned error"; - return 2; + return USSD_TIMEOUT; } } - // Get data from MS and check for closing condition - gTransactionTable.find(mTransactionID, transaction); + // 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) << "USSD clearing...."; - return 1; + LOG(DEBUG) << "Clearing USSD transaction: " << transaction; + return USSD_CLEARED; } - *messageType = transaction.ussdData()->MessageType(); - *USSDString = transaction.ussdData()->USSDString(); - return 0; + *messageType = pUssdData->MessageType(); + *USSDString = pUssdData->USSDString(); + return USSD_OK; } -void USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType, std::string USSDString) +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(); @@ -691,15 +718,31 @@ void USSDHandler::postUSSDData(Control::USSDData::USSDMessageType messageType, s // Step 1 -- Find transaction TransactionEntry transaction; - gTransactionTable.find(mTransactionID, 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 - transaction.ussdData()->MessageType(messageType); - transaction.ussdData()->USSDString(USSDString); + pUssdData->MessageType(messageType); + pUssdData->USSDString(USSDString); gTransactionTable.update(transaction); // Step 3 -- Notify the dispatcher thread that data is ready to be sent - transaction.ussdData()->postNW(); + if (pUssdData->postNW() != 0) + { + return USSD_ERROR; + } + + // Success. + return USSD_OK; } void *USSDHandler::runWrapper(void *pThis) diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index dffd20d..69f0123 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -410,19 +410,21 @@ class USSDData { std::string USSDString() const { return mUSSDString; } void USSDString(std::string wUSSDString) { mUSSDString = wUSSDString; } - int waitMS() { return mSemWaitMS.wait(); } - int waitNW() { return mSemWaitNW.wait(); } + ThreadSemaphore::Result waitMS() { return mSemWaitMS.wait(); } + ThreadSemaphore::Result waitNW() { return mSemWaitNW.wait(); } + ThreadSemaphore::Result waitIncomingData() { return mSemIncomingData.wait(); } - int waitMS(unsigned timeout) { return mSemWaitMS.wait(timeout); } - int waitNW(unsigned timeout) { return mSemWaitNW.wait(timeout); } + 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); } - int trywaitMS() { return mSemWaitMS.trywait(); } - int trywaitNW() { return mSemWaitNW.trywait(); } + ThreadSemaphore::Result trywaitMS() { return mSemWaitMS.trywait(); } + ThreadSemaphore::Result trywaitNW() { return mSemWaitNW.trywait(); } + ThreadSemaphore::Result trywaitIncomingData() { return mSemIncomingData.trywait(); } - - int postMS() { return mSemWaitMS.post(); } - int postNW() { return mSemWaitNW.post(); } - + 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); @@ -930,6 +932,16 @@ class USSDHandler { { 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; @@ -946,9 +958,9 @@ class USSDHandler { {} /** Wait USSD data from MS. Return: 0 - successful, 1 - clear transaction, 2 - error or timeout */ - unsigned waitUSSDData(Control::USSDData::USSDMessageType* messageType, std::string* USSDString, unsigned timeout); + 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)*/ - void postUSSDData( Control::USSDData::USSDMessageType messageType, std::string 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); From a2298288fe60001cc5af6409bf0e62f8d31bc751 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:22:55 +0400 Subject: [PATCH 25/30] Other USSD code beautification and fixes. --- public-trunk/Control/ControlCommon.cpp | 5 +++-- public-trunk/Control/ControlCommon.h | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 23526ed..a5dbaa6 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -801,6 +801,7 @@ void MOTestHandler::run() } else { + USSDString = ""; messageType = USSDData::release; } @@ -848,7 +849,7 @@ void MOHttpHandler::run() if (!wget) { LOG(NOTICE) << "cannot open wget with " << command; USSDString = "cannot open wget"; - messageType = USSDData::release; + messageType = USSDData::error; } fgets (mystring , 182 , wget); LOG(NOTICE) << "wget response " << mystring; @@ -1100,7 +1101,7 @@ void Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH } } -bool USSDMatchHandler(const std::string &handlerName, const std::string &ussdString) +bool Control::USSDMatchHandler(const std::string &handlerName, const std::string &ussdString) { std::string handlerKeyName("USSD.Handler."); handlerKeyName += handlerName; diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index 69f0123..a3390df 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -392,12 +392,13 @@ class USSDData { release }; - private: + protected: ThreadSemaphore mSemWaitMS; - ThreadSemaphore mSemWaitNW; - USSDMessageType mType; // USSD message type - std::string mUSSDString; // USSD message string + ThreadSemaphore mSemWaitNW; + ThreadSemaphore mSemIncomingData; ///< External entity has sent us some data + USSDMessageType mType; ///< USSD message type + std::string mUSSDString; ///< USSD message string public: From aeedcc50b6e24197aeff05114ec3f556928bb30f Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:24:10 +0400 Subject: [PATCH 26/30] Print USSDData pointer when printing TransactionEntry. --- public-trunk/Control/ControlCommon.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index a5dbaa6..8addb83 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -266,6 +266,7 @@ ostream& Control::operator<<(ostream& os, const TransactionEntry& entry) if (entry.calling().digits()[0]) os << " from=" << entry.calling().digits(); os << " Q.931State=" << entry.Q931State(); os << " SIPState=" << entry.SIP().state(); + os << " USSDData=" << entry.ussdData(); os << " (" << (entry.stateAge()+500)/1000 << " sec)"; if (entry.message()[0]) os << " message=\"" << entry.message() << "\""; return os; From bf4c962d41f6538a7dec4c67bd855385b7eb21d5 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:24:57 +0400 Subject: [PATCH 27/30] Implement another flavor of TransactionTable::find() with mobile ID and service type. --- public-trunk/Control/ControlCommon.cpp | 28 ++++++++++++++++++++++++++ public-trunk/Control/ControlCommon.h | 10 +++++++++ 2 files changed, 38 insertions(+) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 8addb83..67695fb 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -386,6 +386,34 @@ bool TransactionTable::find(const L3MobileIdentity& mobileID, TransactionEntry& return foundIt; } + + +bool TransactionTable::find(const L3MobileIdentity& mobileID, GSM::L3CMServiceType serviceType, TransactionEntry& target) +{ + // Yes, it's linear time. + // Even in a 6-ARFCN system, it should rarely be more than a dozen entries. + + // Since clearDeadEntries is also linear, do that here, too. + + // Brute force search. + bool foundIt = false; + mLock.lock(); + clearDeadEntries(); + TransactionMap::const_iterator itr = mTable.begin(); + while (itr!=mTable.end()) { + const TransactionEntry& transaction = itr->second; + if (transaction.subscriber()==mobileID && transaction.service()==serviceType) { + // No need to check dead(), since we just cleared the table. + foundIt = true; + target = transaction; + break; + } + ++itr; + } + mLock.unlock(); + return foundIt; +} + size_t TransactionTable::size() { return mTable.size(); diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index a3390df..a3eb6c4 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -679,6 +679,16 @@ class TransactionTable { */ bool find(const GSM::L3MobileIdentity& mobileID, TransactionEntry& target); + /** + Find an entry by its mobile ID and service type. + Also clears dead entries during search. + @param mobileID The mobile at to search for. + @param serviceType Service type we're looking for. + @param target A TransactionEntry to accept the found record. + @return true is the mobile ID was found. + */ + bool find(const GSM::L3MobileIdentity& mobileID, GSM::L3CMServiceType serviceType, TransactionEntry& target); + /** Remove "dead" entries from the table. A "dead" entry is a transaction that is no longer active. From 94523e388af3278074a9defb72b60ce72b49c347 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:25:58 +0400 Subject: [PATCH 28/30] USSD SIP handler initial implementation. It works for basic requests, but needs more tsting for timeouts and other exceptional cases. --- public-trunk/Control/ControlCommon.cpp | 119 ++++++++++++++++++++++++- public-trunk/Control/ControlCommon.h | 13 +++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 67695fb..5dfa2dd 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -973,7 +973,119 @@ void MTTestHandler::run() } 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()); + 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); + } } @@ -1182,6 +1294,11 @@ unsigned Control::USSDDispatcher(GSM::L3MobileIdentity &mobileIdentity, 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()); diff --git a/public-trunk/Control/ControlCommon.h b/public-trunk/Control/ControlCommon.h index a3eb6c4..966aa2c 100644 --- a/public-trunk/Control/ControlCommon.h +++ b/public-trunk/Control/ControlCommon.h @@ -999,6 +999,19 @@ class MOHttpHandler : public USSDHandler { 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) From ce66ecb51d165b6731c7f26f52aba05626aa143a Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:26:24 +0400 Subject: [PATCH 29/30] Some more changes. --- public-trunk/Control/ControlCommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public-trunk/Control/ControlCommon.cpp b/public-trunk/Control/ControlCommon.cpp index 5dfa2dd..a7f03e3 100644 --- a/public-trunk/Control/ControlCommon.cpp +++ b/public-trunk/Control/ControlCommon.cpp @@ -822,6 +822,7 @@ void MOTestHandler::run() } else if(USSDString == "*103#") { + USSDString = ""; messageType = USSDData::release; } else if(USSDString == "*104#") @@ -830,7 +831,6 @@ void MOTestHandler::run() } else { - USSDString = ""; messageType = USSDData::release; } From 62a2d77ccc6a8056ad83903ec9be6fddc22bd8c7 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Sep 2010 18:27:02 +0400 Subject: [PATCH 30/30] Better code for MOUSSDController(). --- public-trunk/Control/USSDControl.cpp | 97 +++++++++++++++++----------- 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/public-trunk/Control/USSDControl.cpp b/public-trunk/Control/USSDControl.cpp index 39fd0d3..e8ac5c9 100644 --- a/public-trunk/Control/USSDControl.cpp +++ b/public-trunk/Control/USSDControl.cpp @@ -297,49 +297,68 @@ void Control::MOUSSDController(const L3CMServiceRequest *request, LogicalChannel TransactionEntry transaction; while(gTransactionTable.find(transactionID, transaction)) { - if (transaction.Q931State() == Control::TransactionEntry::USSDclosing) + USSDData *pUssdData = NULL; + + // Wait for handler + if ((pUssdData = transaction.ussdData()) == NULL) { + LOG(DEBUG) << "Transaction has no USSD data: " << transaction; break; } - else if (transaction.ussdData()->waitNW()==0) - { - gTransactionTable.find(transactionID, transaction); - //SEND - USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 0, LCH, transaction.ussdData()->MessageType()); - if((transaction.ussdData()->MessageType() == Control::USSDData::response)|| - (transaction.ussdData()->MessageType() == Control::USSDData::release)) - { - LOG(DEBUG) << "USSD waitMS response||relese"; - transaction.Q931State(Control::TransactionEntry::USSDclosing); - } - else - { - //RECV - L3Frame *USSDFrame = getFrameUSSD(LCH); - if (USSDFrame == NULL) - { - USSDSend(transaction.ussdData()->USSDString(), InvokeID, TI, 0, LCH, Control::USSDData::release); - transaction.Q931State(Control::TransactionEntry::USSDclosing); - } - else - { - //Parse USSD frame - L3NonCallSSMessage* USSDMessage = parseL3NonCallSS(*USSDFrame); - TI = USSDMessage->TIValue(); - LOG(INFO) << "USSD message before PARSE:"<<*USSDMessage; - Control::USSDData::USSDMessageType messageType = USSDParse(USSDMessage, &USSDString, &InvokeID); - LOG(INFO) << "MO USSD message: "<< *USSDMessage << " MO USSD type: "<< messageType - << " USSD string: " << USSDString << " InvokeID: " << InvokeID; - transaction.ussdData()->USSDString(USSDString); - transaction.ussdData()->MessageType(messageType); - delete USSDMessage; - } - delete USSDFrame; - - } - gTransactionTable.update(transaction); - transaction.ussdData()->postMS(); + 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(); } }