laforge
/
openbts-osmo
Archived
1
0
Fork 0
This repository has been archived on 2022-03-30. You can view files and clone it, but cannot push or open issues or pull requests.
openbts-osmo/public-trunk/Control/USSDControl.cpp

429 lines
15 KiB
C++

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