<commit_before>/**@file SIP Base -- SIP IETF RFC-3261, RTP IETF RFC-3550. */
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
* Copyright 2011, 2012, 2014 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for 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.

*/



#define LOG_GROUP LogGroup::SIP		// Can set Log.Level.SIP for debugging

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#include <sys/types.h>
#include <semaphore.h>

#include <ortp/telephonyevents.h>

#include <Logger.h>
#include <Timeval.h>
#include <GSMConfig.h>
#include <ControlTransfer.h>
#include <GSMCommon.h>
#include <GSMLogicalChannel.h>
#include <Reporting.h>
#include <Globals.h>
#include <L3TranEntry.h>	// For HandoverEntry

#include "SIPMessage.h"
#include "SIPParse.h"	// For SipParam
#include "SIPBase.h"
#include "SIPDialog.h"
#include "SIP2Interface.h"
#include "SIPUtility.h"

#undef WARNING

int gCountRtpSessions = 0;
int gCountRtpSockets = 0;
int gCountSipDialogs = 0;

namespace SIP {
using namespace std;
using namespace Control;

const bool rtpUseRealTime = true;	// Enables a bug fix for the RTP library.

bool gPeerIsBuggySmqueue = true;

// These need to be declared here instead of in the header because of interaction with InterthreadQueue.h.
SipEngine::~SipEngine() {}
SipEngine::SipEngine()	 { mTranId = 0; }

void SipBaseProtected::_define_vtable() {}
void DialogMessage::_define_vtable() {}


string makeUriWithTag(string username, string ip, string tag)
{
	return format("<sip:%s@%s>;tag=%s",username,ip,tag);
}
string makeUri(string username, string ip, unsigned port)
{
	if (port) {
		return format("sip:%s@%s:%u",username,ip,port);
	} else {
		return format("sip:%s@%s",username,ip);
	}
}

static int get_rtp_tev_type(char dtmf){
        switch (dtmf){
                case '1': return TEV_DTMF_1;
                case '2': return TEV_DTMF_2;
                case '3': return TEV_DTMF_3;
                case '4': return TEV_DTMF_4;
                case '5': return TEV_DTMF_5;
                case '6': return TEV_DTMF_6;
                case '7': return TEV_DTMF_7;
                case '8': return TEV_DTMF_8;
                case '9': return TEV_DTMF_9;
                case '0': return TEV_DTMF_0;
                case '*': return TEV_DTMF_STAR;
                case '#': return TEV_DTMF_POUND;
                case 'a':
                case 'A': return TEV_DTMF_A;
                case 'B':
                case 'b': return TEV_DTMF_B;
                case 'C':
                case 'c': return TEV_DTMF_C;
                case 'D':
                case 'd': return TEV_DTMF_D;
		case '!': return TEV_FLASH;
                default:
		    LOG(WARNING) << "Bad dtmf: " << dtmf;
		    return -1;
                }
}


const char* SipStateString(SipState s)
{
	switch(s)
	{	
		case SSNullState: return "NullState";
		case SSTimeout: return "Timeout";
		case Starting: return "Starting";
		case Proceeding: return "Proceeding";
		case Ringing: return "Ringing";
		case Connecting: return "Connecting";
		case Active: return "Active";
		case SSFail: return "SSFail"; 
		case MOCBusy: return "MOCBusy";
		case MODClearing: return "MODClearing";
		case MODCanceling: return "MODCanceling";
		case MODError: return "MODError";
		case MTDClearing: return "MTDClearing";
		case MTDCanceling: return "MTDCanceling";
		case Canceled: return "Canceled";
		case Cleared: return "Cleared";
		//case SipRegister: return "SipRegister";
		//case SipUnregister: return "SipUnregister";
		case MOSMSSubmit: return "SMS-Submit";
		case HandoverInbound: return "HandoverInbound";
		//case HandoverInboundReferred: return "HandoverInboundReferred";
		case HandoverOutbound: return "HandoverOutbound";
		//default: return NULL;
	}
	return NULL;
}

ostream& operator<<(ostream& os, SipState s)
{
	const char* str = SipStateString(s);
	if (str) os << str;
	else os << "?" << ((int)s) << "?";
	return os;
}

// Should we cancel a transaction based on the SIP state?
bool SipBaseProtected::sipIsStuck() const
{
	switch (getDialogType()) {
		case SIPDTUndefined:
			LOG(ERR) << "undefined dialog type?"; return false;
		case SIPDTRegister:
		case SIPDTUnregister:
			return false;	// The registrar dialogs are immortal.
		default:
			break;	// Call and Message dialogs should either proceed to Active or eventually die on their own.
	}
	unsigned age = mStateAge.elapsed();
	// These ages were copied from the old pre-l3-rewrite code in TransactionTable.cpp
	switch (getSipState()) {
		case Active:
			return false;		// Things are copascetic. Let this SIP dialog run.
		case SSFail:
		case HandoverInbound:
		case Proceeding:
		case Canceled:
		case Cleared:
			// Stuck in these states longer than 30 seconds?  Grounds for terminating the transaction.
			return age > 30*1000;
		default:
			// Stuck in any other state longer than 180 seconds?  Grounds for terminating the transaction.
			return age > 180*1000;
	}
}

void SipRtp::rtpStop()
{
	if (mSession) {
		RtpSession *save = mSession;
		mSession = NULL;		// Prevent rxFrame and txFrame from using it, which is overkill because we dont call this until the state is not Active.
		rtp_session_destroy(save);
		gCountRtpSessions--;
		gCountRtpSockets--;
	}
}

void SipRtp::rtpInit()
{
	mSession = NULL; 
	mTxTime = 0;
	mRxTime = 0;
	mRxRealTime = 0;
	mTxRealTime = 0;
	mDTMF = 0;
	mDTMFDuration = 0;
	mDTMFEnding = 0;
	mRTPPort = 0; 	//to make sure noise doesn't magically equal a valid RTP port
}

DialogStateVars::DialogStateVars()
{
	// generate a tag now.
	// (pat) It identifies our side of the SIP dialog to the peer.
	// It is placed in the to-tag for both responses and requests.
	// For responses we munge the saved INVITE immediately so all responses use this to-tag.
	//mLocalTag=make_tag();

	// set our CSeq in case we need one
	mLocalCSeq = gSipInterface.nextInviteCSeqNum(); 	// formerly: random()%600;
}

string DialogStateVars::dsToString() const
{
	ostringstream ss;
	ss << LOGVARM(mCallId);
	ss << LOGVARM(mLocalHeader.value())<<LOGVARM(mRemoteHeader.value());
	ss << LOGVARM(mLocalCSeq) << LOGVARM(mRouteSet);
	ss << LOGVAR2("proxy",mProxy.ipToText());
	return ss.str();
}

static unsigned gDialogId = 1;

// (pat) This constructor preforms only base initialization.
// Final initialization is in the dialog constructor.
// For MO the final init of mRemote... is in sendMessage or sendInvite, which is called from the dialog constructor.
void SipBase::SipBaseInit(DialogType wDialogType, string proxy, const char * proxyProvenance) // , TranEntryId wTranId)
{
	//mTranId = 0;
	mDialogId = gDialogId++;
	mLastResponse = NULL;
	mInvite = NULL;
	mDialogType = wDialogType;
	//mTranId = wTranId;

	if (! mProxy.ipSet(proxy,proxyProvenance)) {
		LOG(ALERT) << "cannot resolve IP address for " << proxy << sbText();
	}
}

SipBase::~SipBase()
{
	if (mInvite) { delete mInvite; }
	mInvite = NULL;
	gCountSipDialogs--;
}

void SipRtp::rtpText(std::ostringstream&os) const
{
	os <<LOGVARM(mTxTime) << LOGVARM(mRxTime);
	//os <<LOGVAR(mRTPRemIP);
	os <<LOGVAR(mRTPPort);
	os <<LOGVARM(mCodec);
	// warning: The unbelievably stupid << sends the mDTMS char verbatim, and it is 0, which prematurely terminates the string.
	unsigned dtmf = mDTMF;
	os <<LOGVAR(dtmf) <<LOGVARM(mDTMFDuration)<<LOGVARM(mDTMFStartTime);
}

void SipBase::sbText(std::ostringstream&os, bool verbose) const
{
	os <<" SipBase(";
	//os  <<LOGVARM(mTranId);
	os <<LOGVAR2("localUsername",sipLocalUsername()) << LOGVAR2("remoteUsername",sipRemoteUsername());
	os <<LOGVARM(mInviteViaBranch);
	os << " DialogStateVars=(" << dsToString() << ")";

	// Add the first line of the last response.
	if (mLastResponse) { os << LOGVARM(mLastResponse); }

	if (IS_LOG_LEVEL(DEBUG)) verbose = true;
	if (verbose) {
		rtpText(os);
		//os << "proxy=(" << mProxy.ipToText() << ")";
	}

	os <<LOGVAR2("SipState",getSipState());
	os <<LOGVARM(mDialogType);
	os <<")";
}

string SipBase::sbText() const
{
	std::ostringstream ss;
	sbText(ss);
	return ss.str();
}



void SipBase::saveInviteOrMessage(const SipMessage *INVITE, bool /*mine*/)
{
	mInvite = new SipMessage();
	*mInvite = *INVITE;
}



// This can only be called for MO responses: MOC, MOD.
void SipBase::saveMOResponse(SipMessage *response)
{
	// This is used only for sdp, and only for the invite response, so we should just save that...
	mLastResponse = new SipMessage();
	*mLastResponse = *response;

	// TODO: Multipart messages.
	if (response->msmContentType == "application/sdp") {
		mSdpAnswer = response->msmBody;
	}

	// The To: is the remote party and might have a new tag.
	// (pat) But only for a response to one of our requests. 
	dsSetRemoteHeader(&response->msmTo);
	if (response->msmCSeqMethod == "INVITE") {
		// Handle the Contact.
		// If the INVITE is canceled early we get a 487 error which does not have a Contact.
		// TODO: Handle the routeset.
		string uristr;
		if (! response->msmContactValue.empty() && crackUri(response->msmContactValue,NULL,&uristr,NULL)) {
			SipUri uri(uristr);
			mProxy.ipSet(uri.uriHostAndPort(), "INVITE Contact");
		}
	}
}


void DialogStateVars::dsSetLocalMO(const FullMobileId &msid, bool addTag)
{
	// We dont really have to check for collisions; our ids were vanishingly unlikely to collide
	// before and now the time is encoded into it as well.
	//string callid;
	//do { callid = globallyUniqueId(""); } while (gSipInterface.findDialogsByCallId(callid,false));
	dsSetCallId(globallyUniqueId(""));

	string username;
	{
		// IMSI gets prefixed with "IMSI" to form a SIP username
		username = msid.fmidUsername();
	}
	dsSetLocalUri(makeUri(username,localIPAndPort()));
	//mSipUsername = username;
	LOG(DEBUG) << "set user MO"<<LOGVAR(msid)<<LOGVAR(username)<<LOGVARM(mCallId) << dsToString();
	if (addTag) {
		mLocalHeader.setTag(make_tag());
	}
}

// We always need a local tag, but handover adds its own.
void DialogStateVars::dsSetLocalHeaderMT(SipPreposition *toheader, bool addTag)
{
	LOG(DEBUG);
	// We will parse the contact to see if there is already a tag, which would be an error.
	mLocalHeader = *toheader;
	if (addTag) {
		if (!mLocalHeader.mTag.empty()) {
			LOG(ERR) <<"MT Dialog initiation had a pre-existing to-tag:"<<toheader;
			// Now what?  Keep the tag I guess.
			// The dialog is probably impossible.
		} else {
			mLocalHeader.setTag(make_tag());
		}
	}
}
	
void writePrivateHeaders(SipMessage *msg, const L3LogicalChannel *l3chan)
{
	// P-PHY-Info
	// This is a non-standard private header in OpenBTS.
	// TODO: If we add the MSC params to this, especially L3TI, the SIP message will completely encapsulate handover.
	// TA=<timing advance> TE=<TA error> UpRSSI=<uplink RSSI> TxPwr=<MS tx power> DnRSSIdBm=<downlink RSSI>
	// Get the values.
	if (l3chan) {
		char phy_info[200];
		// (pat) TODO: This is really cheating.
		const GSM::L2LogicalChannel *chan = l3chan->getL2Channel();
		MSPhysReportInfo *phys = chan->getPhysInfo();
		snprintf(phy_info,200,"OpenBTS; TA=%d TE=%f UpRSSI=%f TxPwr=%d DnRSSIdBm=%d time=%9.3lf",
			phys->actualMSTiming(), phys->timingError(),
			phys->RSSI(), phys->actualMSPower(),
			chan->measurementResults().RXLEV_FULL_SERVING_CELL_dBm(),
			phys->timestamp());
		static const string cPhyInfoString("P-PHY-Info");
		msg->smAddHeader(cPhyInfoString,phy_info);
	}

	// P-Access-Network-Info
	// See 3GPP 24.229 7.2.  This is a genuine specified header.
	char cgi_3gpp[256];
	snprintf(cgi_3gpp,256,"3GPP-GERAN; cgi-3gpp=%s%s%04x%04x",
		gConfig.getStr("GSM.Identity.MCC").c_str(),gConfig.getStr("GSM.Identity.MNC").c_str(),
		(unsigned)gConfig.getNum("GSM.Identity.LAC"),(unsigned)gConfig.getNum("GSM.Identity.CI"));
	static const string cAccessNetworkInfoString("P-Access-Network-Info");
	msg->smAddHeader(cAccessNetworkInfoString, cgi_3gpp);
 
	// FIXME -- Use the subscriber registry to look up the E.164
	// and make a second P-Preferred-Identity header.
}


// Like this:  Note no via branch, but registrar added one.
// REGISTER sip:127.0.0.1 SIP/2.0^M		// this is the proxy IP
// Via: SIP/2.0/UDP 127.0.0.1:5062;branch^M
// From: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>;tag=vzmikpkffdomsjvb^M
// To: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>^M
// Call-ID: 196117285@127.0.0.1^M
// CSeq: 226 REGISTER^M
// Contact: <sip:IMSI001010002220002@127.0.0.1:5062>;expires=5400^M
// User-Agent: OpenBTS 3.0TRUNK Build Date May  3 2013^M
// Max-Forwards: 70^M
// P-PHY-Info: OpenBTS; TA=1 TE=0.392578 UpRSSI=-26.000000 TxPwr=33 DnRSSIdBm=-111^M
// P-Access-Network-Info: 3GPP-GERAN; cgi-3gpp=0010103ef000a^M
// P-Preferred-Identity: <sip:IMSI001010002220002@127.0.0.1:5060>^M
// Content-Length: 0^M
// 
// SIP/2.0 401 Unauthorized^M
// Via: SIP/2.0/UDP localhost:5064;branch=1;received=<EMAIL>^M
// Via: SIP/2.0/UDP 127.0.0.1:5062;branch^M
// From: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>;tag=vzmikpkffdomsjvb^M
// To: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>^M
// Call-ID: 196117285@127.0.0.1^M
// CSeq: 226 REGISTER^M
// Contact: <sip:IMSI001010002220002@127.0.0.1:5062>;expires=5400^M
// WWW-Authenticate: Digest  nonce=972ed867f7284fca8670e44a71f97040^M
// User-agent: OpenBTS 3.0TRUNK Build Date May  3 2013^M
// Max-forwards: 70^M
// P-phy-info: OpenBTS; TA=1 TE=0.392578 UpRSSI=-26.000000 TxPwr=33 DnRSSIdBm=-111^M
// P-access-network-info: 3GPP-GERAN; cgi-3gpp=0010103ef000a^M
// P-preferred-identity: <sip:IMSI001010002220002@127.0.0.1:5060>^M
// Content-Length: 0^M
// 
// REGISTER sip:127.0.0.1 SIP/2.0^M
// Via: SIP/2.0/UDP 127.0.0.1:5062;branch^M
// From: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>;tag=yyourwrzymenfffa^M
// To: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>^M
// Call-ID: 2140400952@127.0.0.1^M
// CSeq: 447 REGISTER^M
// Contact: <sip:IMSI001010002220002@127.0.0.1:5062>;expires=5400^M
// Authorization: Digest, nonce=83775658971b9dc469ad88c3c3d5250b, uri=001010002220002, response=4f9eb260^M
// User-Agent: OpenBTS 3.0TRUNK Build Date May  3 2013^M
// Max-Forwards: 70^M
// P-PHY-Info: OpenBTS; TA=1 TE=0.423828 UpRSSI=-63.000000 TxPwr=13 DnRSSIdBm=-48^M
// P-Access-Network-Info: 3GPP-GERAN; cgi-3gpp=0010103ef000a^M
// P-Preferred-Identity: <sip:IMSI001010002220002@127.0.0.1:5060>^M
// Content-Length: 0^M



// We wrap our REGISTER messages inside a dialog object, even though it is technically not a dialog.
SipMessage *SipBase::makeRegisterMsg(DialogType wMethod, const L3LogicalChannel* chan, string RAND, const FullMobileId &msid, const char *SRES)
{
	// TODO: We need to make a transaction here...
	static const string registerStr("REGISTER");
	// The request URI is special for REGISTER;
	string reqUri = string("sip:") + proxyIP();
	// We formerly allocated a new Dialog for each REGISTER message so the IMSI was stashed in the dialog, and localSipUri() worked.
	//SipPreposition myUri(localSipUri());
	string username = msid.fmidUsername();
	// RFC3261 is somewhat contradictory on the From-tag and To-tag.
	// The documentation for 'from' says the from-tag is always included.
	// The examples in 24.1 show a From-tag but no To-tag.
	// The To-tag includes the optional <>, and Paul at null team incorrectly thought the <> were required,
	// so we will include them as that appears to be common practice.

	string myUriString;
	string authUri;
	string authUsername;
	string realm = gConfig.getStr("SIP.Realm");
	if (realm.length() > 0) {
		authUri = string("sip:") + realm;
		authUsername = string("IMSI") + msid.mImsi;
		myUriString = makeUri(username,realm,0);
	} else {
		myUriString = makeUri(username,dsPeer()->mipName,0);	// The port, if any, is already in mipName.
	}

	//string fromUriString = makeUriWithTag(username,dsPeer()->mipName,make_tag());	// The port, if any, is already in mipName.
	SipPreposition toHeader("",myUriString,"");
	SipPreposition fromHeader("",myUriString,make_tag());
	dsNextCSeq();	// Advance CSeqNum.
	SipMessage *msg = makeRequest(registerStr,reqUri,username,&toHeader,&fromHeader,make_branch());

	// Replace the Contact header so we can set the expires option.  What a botched up spec.
	// Replace the P-Preferred-Identity
	unsigned expires;
	if (wMethod == SIPDTRegister ) {
		expires = 60*gConfig.getNum("SIP.RegistrationPeriod");
		if (SRES && strlen(SRES)) {
			if (realm.length() > 0) {
				string response = makeResponse(authUsername, realm, SRES, registerStr, authUri, RAND);
				msg->msmAuthorizationValue = format("Digest realm=\"%s\", username=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", algorithm=MD5 ",
					realm.c_str(), authUsername.c_str(), RAND.c_str(), authUri.c_str(), response.c_str());
			} else {
				msg->msmAuthorizationValue = format("Digest, nonce=%s, uri=%s, response=%s",RAND.c_str(),msid.mImsi.c_str(),SRES);
			}
		}
	} else if (wMethod == SIPDTUnregister ) {
		expires = 0;
	} else { assert(0); }
	// We use myURI instead of localContact because the SIPDialog for REGISTER is shared by all REGISTER
	// users and does not contain the personal info for this user.
	//msg->msmContactValue = format("<%s>;expires=%u",myUriString.c_str(),expires);
	msg->msmContactValue = localContact(username,expires);
	writePrivateHeaders(msg,chan);
	return msg;
}




string SipBase::makeSDPOffer()
{
	SdpInfo sdp;
	sdp.sdpInitOffer(this);
	return sdp.sdpValue();
	//return makeSDP("0","0");
}

// mCodec is an implicit parameter, consisting of the chosen codec.
string SipBase::makeSDPAnswer()
{
	SdpInfo answer;
	answer.sdpInitOffer(this);
	mSdpAnswer = answer.sdpValue();
	return mSdpAnswer;
}


// A request inside an invite is ACK, CANCEL, BYE, INFO, or re-INVITE.
// The only target-refresh-request is re-INVITE, which can change the "Dialog State".
// For ACK sec 17.1.1.3 the To must equal the To of the response being acked, which specifically contains a tag.
// ACK contains only one via == top via of original request with matching branch.
// TODO: ACK must copy route header fields from INVITE.
// sec 9.1 For CANCEL, all fields must == INVITE except the method.  Note CSeq must match too.  Must have single via
// matching the [invite] request being cancelled, with matching branch.  TODO: Must copy route header from request.
// 15.1.1: BYE is a new request within a dialog as per section 12.2.
// transaction constructed as per with a new tag, branch, etc.
// It should not include CONTACT because it is non-target-refresh.
// Implicit parameters from SipBase: mCallId, mRemoteUsername, mRemoteDomain, mCSeq, etc.
SipMessage *SipBase::makeRequest(string method,string requestUri, string whoami, SipPreposition*toHeader, SipPreposition*fromHeader, string branch)
{
	SipMessage *invite = new SipMessage();
	invite->msmReqMethod = method;
	invite->msmCallId = this->mCallId;
	//string toContact = makeUri(mRemoteUsername,mRemoteDomain);	// dialed_number@remote_ip
	//string fromContact = makeUri(mSipUsername,localIP());
	//invite->msmReqUri = makeUri(mRemoteUsername,mRemoteDomain);
	invite->msmReqUri = requestUri;
	invite->smAddViaBranch(this,branch);
	invite->msmCSeqMethod = invite->msmReqMethod;
	invite->msmCSeqNum = mLocalCSeq;		// We dont need to advance for an initial request; caller advances if necessary.
	invite->msmTo = *toHeader;
	invite->msmFrom = *fromHeader;
	invite->msmContactValue = localContact(whoami);
	string prefid = this->preferredIdentity(whoami);
	static const string cPreferredIdentityString("P-Preferred-Identity");
	invite->smAddHeader(cPreferredIdentityString,prefid);

	// The caller has not added the content yet: saveInviteOrMessage(invite,true);
	return invite;
}

// This is an *initial* request only, for INVITE or MESSAGE.
// This version generates the request from the values in the DialogStateVars.
SipMessage *SipBase::makeInitialRequest(string method)
{
	string requestUri = dsRemoteURI();<commit_msg>- handle SMS differently when using a realm (upstream r8194)<commit_after>/**@file SIP Base -- SIP IETF RFC-3261, RTP IETF RFC-3550. */
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
* Copyright 2011, 2012, 2014 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribuion.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for 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.

*/



#define LOG_GROUP LogGroup::SIP		// Can set Log.Level.SIP for debugging

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#include <sys/types.h>
#include <semaphore.h>

#include <ortp/telephonyevents.h>

#include <Logger.h>
#include <Timeval.h>
#include <GSMConfig.h>
#include <ControlTransfer.h>
#include <GSMCommon.h>
#include <GSMLogicalChannel.h>
#include <Reporting.h>
#include <Globals.h>
#include <L3TranEntry.h>	// For HandoverEntry

#include "SIPMessage.h"
#include "SIPParse.h"	// For SipParam
#include "SIPBase.h"
#include "SIPDialog.h"
#include "SIP2Interface.h"
#include "SIPUtility.h"

#undef WARNING

int gCountRtpSessions = 0;
int gCountRtpSockets = 0;
int gCountSipDialogs = 0;

namespace SIP {
using namespace std;
using namespace Control;

const bool rtpUseRealTime = true;	// Enables a bug fix for the RTP library.

bool gPeerIsBuggySmqueue = true;

// These need to be declared here instead of in the header because of interaction with InterthreadQueue.h.
SipEngine::~SipEngine() {}
SipEngine::SipEngine()	 { mTranId = 0; }

void SipBaseProtected::_define_vtable() {}
void DialogMessage::_define_vtable() {}


string makeUriWithTag(string username, string ip, string tag)
{
	return format("<sip:%s@%s>;tag=%s",username,ip,tag);
}
string makeUri(string username, string ip, unsigned port)
{
	if (port) {
		return format("sip:%s@%s:%u",username,ip,port);
	} else {
		return format("sip:%s@%s",username,ip);
	}
}

static int get_rtp_tev_type(char dtmf){
        switch (dtmf){
                case '1': return TEV_DTMF_1;
                case '2': return TEV_DTMF_2;
                case '3': return TEV_DTMF_3;
                case '4': return TEV_DTMF_4;
                case '5': return TEV_DTMF_5;
                case '6': return TEV_DTMF_6;
                case '7': return TEV_DTMF_7;
                case '8': return TEV_DTMF_8;
                case '9': return TEV_DTMF_9;
                case '0': return TEV_DTMF_0;
                case '*': return TEV_DTMF_STAR;
                case '#': return TEV_DTMF_POUND;
                case 'a':
                case 'A': return TEV_DTMF_A;
                case 'B':
                case 'b': return TEV_DTMF_B;
                case 'C':
                case 'c': return TEV_DTMF_C;
                case 'D':
                case 'd': return TEV_DTMF_D;
		case '!': return TEV_FLASH;
                default:
		    LOG(WARNING) << "Bad dtmf: " << dtmf;
		    return -1;
                }
}


const char* SipStateString(SipState s)
{
	switch(s)
	{	
		case SSNullState: return "NullState";
		case SSTimeout: return "Timeout";
		case Starting: return "Starting";
		case Proceeding: return "Proceeding";
		case Ringing: return "Ringing";
		case Connecting: return "Connecting";
		case Active: return "Active";
		case SSFail: return "SSFail"; 
		case MOCBusy: return "MOCBusy";
		case MODClearing: return "MODClearing";
		case MODCanceling: return "MODCanceling";
		case MODError: return "MODError";
		case MTDClearing: return "MTDClearing";
		case MTDCanceling: return "MTDCanceling";
		case Canceled: return "Canceled";
		case Cleared: return "Cleared";
		//case SipRegister: return "SipRegister";
		//case SipUnregister: return "SipUnregister";
		case MOSMSSubmit: return "SMS-Submit";
		case HandoverInbound: return "HandoverInbound";
		//case HandoverInboundReferred: return "HandoverInboundReferred";
		case HandoverOutbound: return "HandoverOutbound";
		//default: return NULL;
	}
	return NULL;
}

ostream& operator<<(ostream& os, SipState s)
{
	const char* str = SipStateString(s);
	if (str) os << str;
	else os << "?" << ((int)s) << "?";
	return os;
}

// Should we cancel a transaction based on the SIP state?
bool SipBaseProtected::sipIsStuck() const
{
	switch (getDialogType()) {
		case SIPDTUndefined:
			LOG(ERR) << "undefined dialog type?"; return false;
		case SIPDTRegister:
		case SIPDTUnregister:
			return false;	// The registrar dialogs are immortal.
		default:
			break;	// Call and Message dialogs should either proceed to Active or eventually die on their own.
	}
	unsigned age = mStateAge.elapsed();
	// These ages were copied from the old pre-l3-rewrite code in TransactionTable.cpp
	switch (getSipState()) {
		case Active:
			return false;		// Things are copascetic. Let this SIP dialog run.
		case SSFail:
		case HandoverInbound:
		case Proceeding:
		case Canceled:
		case Cleared:
			// Stuck in these states longer than 30 seconds?  Grounds for terminating the transaction.
			return age > 30*1000;
		default:
			// Stuck in any other state longer than 180 seconds?  Grounds for terminating the transaction.
			return age > 180*1000;
	}
}

void SipRtp::rtpStop()
{
	if (mSession) {
		RtpSession *save = mSession;
		mSession = NULL;		// Prevent rxFrame and txFrame from using it, which is overkill because we dont call this until the state is not Active.
		rtp_session_destroy(save);
		gCountRtpSessions--;
		gCountRtpSockets--;
	}
}

void SipRtp::rtpInit()
{
	mSession = NULL; 
	mTxTime = 0;
	mRxTime = 0;
	mRxRealTime = 0;
	mTxRealTime = 0;
	mDTMF = 0;
	mDTMFDuration = 0;
	mDTMFEnding = 0;
	mRTPPort = 0; 	//to make sure noise doesn't magically equal a valid RTP port
}

DialogStateVars::DialogStateVars()
{
	// generate a tag now.
	// (pat) It identifies our side of the SIP dialog to the peer.
	// It is placed in the to-tag for both responses and requests.
	// For responses we munge the saved INVITE immediately so all responses use this to-tag.
	//mLocalTag=make_tag();

	// set our CSeq in case we need one
	mLocalCSeq = gSipInterface.nextInviteCSeqNum(); 	// formerly: random()%600;
}

string DialogStateVars::dsToString() const
{
	ostringstream ss;
	ss << LOGVARM(mCallId);
	ss << LOGVARM(mLocalHeader.value())<<LOGVARM(mRemoteHeader.value());
	ss << LOGVARM(mLocalCSeq) << LOGVARM(mRouteSet);
	ss << LOGVAR2("proxy",mProxy.ipToText());
	return ss.str();
}

static unsigned gDialogId = 1;

// (pat) This constructor preforms only base initialization.
// Final initialization is in the dialog constructor.
// For MO the final init of mRemote... is in sendMessage or sendInvite, which is called from the dialog constructor.
void SipBase::SipBaseInit(DialogType wDialogType, string proxy, const char * proxyProvenance) // , TranEntryId wTranId)
{
	//mTranId = 0;
	mDialogId = gDialogId++;
	mLastResponse = NULL;
	mInvite = NULL;
	mDialogType = wDialogType;
	//mTranId = wTranId;

	if (! mProxy.ipSet(proxy,proxyProvenance)) {
		LOG(ALERT) << "cannot resolve IP address for " << proxy << sbText();
	}
}

SipBase::~SipBase()
{
	if (mInvite) { delete mInvite; }
	mInvite = NULL;
	gCountSipDialogs--;
}

void SipRtp::rtpText(std::ostringstream&os) const
{
	os <<LOGVARM(mTxTime) << LOGVARM(mRxTime);
	//os <<LOGVAR(mRTPRemIP);
	os <<LOGVAR(mRTPPort);
	os <<LOGVARM(mCodec);
	// warning: The unbelievably stupid << sends the mDTMS char verbatim, and it is 0, which prematurely terminates the string.
	unsigned dtmf = mDTMF;
	os <<LOGVAR(dtmf) <<LOGVARM(mDTMFDuration)<<LOGVARM(mDTMFStartTime);
}

void SipBase::sbText(std::ostringstream&os, bool verbose) const
{
	os <<" SipBase(";
	//os  <<LOGVARM(mTranId);
	os <<LOGVAR2("localUsername",sipLocalUsername()) << LOGVAR2("remoteUsername",sipRemoteUsername());
	os <<LOGVARM(mInviteViaBranch);
	os << " DialogStateVars=(" << dsToString() << ")";

	// Add the first line of the last response.
	if (mLastResponse) { os << LOGVARM(mLastResponse); }

	if (IS_LOG_LEVEL(DEBUG)) verbose = true;
	if (verbose) {
		rtpText(os);
		//os << "proxy=(" << mProxy.ipToText() << ")";
	}

	os <<LOGVAR2("SipState",getSipState());
	os <<LOGVARM(mDialogType);
	os <<")";
}

string SipBase::sbText() const
{
	std::ostringstream ss;
	sbText(ss);
	return ss.str();
}



void SipBase::saveInviteOrMessage(const SipMessage *INVITE, bool /*mine*/)
{
	mInvite = new SipMessage();
	*mInvite = *INVITE;
}



// This can only be called for MO responses: MOC, MOD.
void SipBase::saveMOResponse(SipMessage *response)
{
	// This is used only for sdp, and only for the invite response, so we should just save that...
	mLastResponse = new SipMessage();
	*mLastResponse = *response;

	// TODO: Multipart messages.
	if (response->msmContentType == "application/sdp") {
		mSdpAnswer = response->msmBody;
	}

	// The To: is the remote party and might have a new tag.
	// (pat) But only for a response to one of our requests. 
	dsSetRemoteHeader(&response->msmTo);
	if (response->msmCSeqMethod == "INVITE") {
		// Handle the Contact.
		// If the INVITE is canceled early we get a 487 error which does not have a Contact.
		// TODO: Handle the routeset.
		string uristr;
		if (! response->msmContactValue.empty() && crackUri(response->msmContactValue,NULL,&uristr,NULL)) {
			SipUri uri(uristr);
			mProxy.ipSet(uri.uriHostAndPort(), "INVITE Contact");
		}
	}
}


void DialogStateVars::dsSetLocalMO(const FullMobileId &msid, bool addTag)
{
	// We dont really have to check for collisions; our ids were vanishingly unlikely to collide
	// before and now the time is encoded into it as well.
	//string callid;
	//do { callid = globallyUniqueId(""); } while (gSipInterface.findDialogsByCallId(callid,false));
	dsSetCallId(globallyUniqueId(""));

	string username;
	{
		// IMSI gets prefixed with "IMSI" to form a SIP username
		username = msid.fmidUsername();
	}
	dsSetLocalUri(makeUri(username,localIPAndPort()));
	//mSipUsername = username;
	LOG(DEBUG) << "set user MO"<<LOGVAR(msid)<<LOGVAR(username)<<LOGVARM(mCallId) << dsToString();
	if (addTag) {
		mLocalHeader.setTag(make_tag());
	}
}

// We always need a local tag, but handover adds its own.
void DialogStateVars::dsSetLocalHeaderMT(SipPreposition *toheader, bool addTag)
{
	LOG(DEBUG);
	// We will parse the contact to see if there is already a tag, which would be an error.
	mLocalHeader = *toheader;
	if (addTag) {
		if (!mLocalHeader.mTag.empty()) {
			LOG(ERR) <<"MT Dialog initiation had a pre-existing to-tag:"<<toheader;
			// Now what?  Keep the tag I guess.
			// The dialog is probably impossible.
		} else {
			mLocalHeader.setTag(make_tag());
		}
	}
}
	
void writePrivateHeaders(SipMessage *msg, const L3LogicalChannel *l3chan)
{
	// P-PHY-Info
	// This is a non-standard private header in OpenBTS.
	// TODO: If we add the MSC params to this, especially L3TI, the SIP message will completely encapsulate handover.
	// TA=<timing advance> TE=<TA error> UpRSSI=<uplink RSSI> TxPwr=<MS tx power> DnRSSIdBm=<downlink RSSI>
	// Get the values.
	if (l3chan) {
		char phy_info[200];
		// (pat) TODO: This is really cheating.
		const GSM::L2LogicalChannel *chan = l3chan->getL2Channel();
		MSPhysReportInfo *phys = chan->getPhysInfo();
		snprintf(phy_info,200,"OpenBTS; TA=%d TE=%f UpRSSI=%f TxPwr=%d DnRSSIdBm=%d time=%9.3lf",
			phys->actualMSTiming(), phys->timingError(),
			phys->RSSI(), phys->actualMSPower(),
			chan->measurementResults().RXLEV_FULL_SERVING_CELL_dBm(),
			phys->timestamp());
		static const string cPhyInfoString("P-PHY-Info");
		msg->smAddHeader(cPhyInfoString,phy_info);
	}

	// P-Access-Network-Info
	// See 3GPP 24.229 7.2.  This is a genuine specified header.
	char cgi_3gpp[256];
	snprintf(cgi_3gpp,256,"3GPP-GERAN; cgi-3gpp=%s%s%04x%04x",
		gConfig.getStr("GSM.Identity.MCC").c_str(),gConfig.getStr("GSM.Identity.MNC").c_str(),
		(unsigned)gConfig.getNum("GSM.Identity.LAC"),(unsigned)gConfig.getNum("GSM.Identity.CI"));
	static const string cAccessNetworkInfoString("P-Access-Network-Info");
	msg->smAddHeader(cAccessNetworkInfoString, cgi_3gpp);
 
	// FIXME -- Use the subscriber registry to look up the E.164
	// and make a second P-Preferred-Identity header.
}


// Like this:  Note no via branch, but registrar added one.
// REGISTER sip:127.0.0.1 SIP/2.0^M		// this is the proxy IP
// Via: SIP/2.0/UDP 127.0.0.1:5062;branch^M
// From: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>;tag=vzmikpkffdomsjvb^M
// To: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>^M
// Call-ID: 196117285@127.0.0.1^M
// CSeq: 226 REGISTER^M
// Contact: <sip:IMSI001010002220002@127.0.0.1:5062>;expires=5400^M
// User-Agent: OpenBTS 3.0TRUNK Build Date May  3 2013^M
// Max-Forwards: 70^M
// P-PHY-Info: OpenBTS; TA=1 TE=0.392578 UpRSSI=-26.000000 TxPwr=33 DnRSSIdBm=-111^M
// P-Access-Network-Info: 3GPP-GERAN; cgi-3gpp=0010103ef000a^M
// P-Preferred-Identity: <sip:IMSI001010002220002@127.0.0.1:5060>^M
// Content-Length: 0^M
// 
// SIP/2.0 401 Unauthorized^M
// Via: SIP/2.0/UDP localhost:5064;branch=1;received=string_address@foo.bar^M
// Via: SIP/2.0/UDP 127.0.0.1:5062;branch^M
// From: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>;tag=vzmikpkffdomsjvb^M
// To: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>^M
// Call-ID: 196117285@127.0.0.1^M
// CSeq: 226 REGISTER^M
// Contact: <sip:IMSI001010002220002@127.0.0.1:5062>;expires=5400^M
// WWW-Authenticate: Digest  nonce=972ed867f7284fca8670e44a71f97040^M
// User-agent: OpenBTS 3.0TRUNK Build Date May  3 2013^M
// Max-forwards: 70^M
// P-phy-info: OpenBTS; TA=1 TE=0.392578 UpRSSI=-26.000000 TxPwr=33 DnRSSIdBm=-111^M
// P-access-network-info: 3GPP-GERAN; cgi-3gpp=0010103ef000a^M
// P-preferred-identity: <sip:IMSI001010002220002@127.0.0.1:5060>^M
// Content-Length: 0^M
// 
// REGISTER sip:127.0.0.1 SIP/2.0^M
// Via: SIP/2.0/UDP 127.0.0.1:5062;branch^M
// From: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>;tag=yyourwrzymenfffa^M
// To: IMSI001010002220002 <sip:IMSI001010002220002@127.0.0.1>^M
// Call-ID: 2140400952@127.0.0.1^M
// CSeq: 447 REGISTER^M
// Contact: <sip:IMSI001010002220002@127.0.0.1:5062>;expires=5400^M
// Authorization: Digest, nonce=83775658971b9dc469ad88c3c3d5250b, uri=001010002220002, response=4f9eb260^M
// User-Agent: OpenBTS 3.0TRUNK Build Date May  3 2013^M
// Max-Forwards: 70^M
// P-PHY-Info: OpenBTS; TA=1 TE=0.423828 UpRSSI=-63.000000 TxPwr=13 DnRSSIdBm=-48^M
// P-Access-Network-Info: 3GPP-GERAN; cgi-3gpp=0010103ef000a^M
// P-Preferred-Identity: <sip:IMSI001010002220002@127.0.0.1:5060>^M
// Content-Length: 0^M



// We wrap our REGISTER messages inside a dialog object, even though it is technically not a dialog.
SipMessage *SipBase::makeRegisterMsg(DialogType wMethod, const L3LogicalChannel* chan, string RAND, const FullMobileId &msid, const char *SRES)
{
	// TODO: We need to make a transaction here...
	static const string registerStr("REGISTER");
	// The request URI is special for REGISTER;
	string reqUri = string("sip:") + proxyIP();
	// We formerly allocated a new Dialog for each REGISTER message so the IMSI was stashed in the dialog, and localSipUri() worked.
	//SipPreposition myUri(localSipUri());
	string username = msid.fmidUsername();
	// RFC3261 is somewhat contradictory on the From-tag and To-tag.
	// The documentation for 'from' says the from-tag is always included.
	// The examples in 24.1 show a From-tag but no To-tag.
	// The To-tag includes the optional <>, and Paul at null team incorrectly thought the <> were required,
	// so we will include them as that appears to be common practice.

	string myUriString;
	string authUri;
	string authUsername;
	string realm = gConfig.getStr("SIP.Realm");
	if (realm.length() > 0) {
		authUri = string("sip:") + realm;
		authUsername = string("IMSI") + msid.mImsi;
		myUriString = makeUri(username,realm,0);
	} else {
		myUriString = makeUri(username,dsPeer()->mipName,0);	// The port, if any, is already in mipName.
	}

	//string fromUriString = makeUriWithTag(username,dsPeer()->mipName,make_tag());	// The port, if any, is already in mipName.
	SipPreposition toHeader("",myUriString,"");
	SipPreposition fromHeader("",myUriString,make_tag());
	dsNextCSeq();	// Advance CSeqNum.
	SipMessage *msg = makeRequest(registerStr,reqUri,username,&toHeader,&fromHeader,make_branch());

	// Replace the Contact header so we can set the expires option.  What a botched up spec.
	// Replace the P-Preferred-Identity
	unsigned expires;
	if (wMethod == SIPDTRegister ) {
		expires = 60*gConfig.getNum("SIP.RegistrationPeriod");
		if (SRES && strlen(SRES)) {
			if (realm.length() > 0) {
				string response = makeResponse(authUsername, realm, SRES, registerStr, authUri, RAND);
				msg->msmAuthorizationValue = format("Digest realm=\"%s\", username=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", algorithm=MD5 ",
					realm.c_str(), authUsername.c_str(), RAND.c_str(), authUri.c_str(), response.c_str());
			} else {
				msg->msmAuthorizationValue = format("Digest, nonce=%s, uri=%s, response=%s",RAND.c_str(),msid.mImsi.c_str(),SRES);
			}
		}
	} else if (wMethod == SIPDTUnregister ) {
		expires = 0;
	} else { assert(0); }
	// We use myURI instead of localContact because the SIPDialog for REGISTER is shared by all REGISTER
	// users and does not contain the personal info for this user.
	//msg->msmContactValue = format("<%s>;expires=%u",myUriString.c_str(),expires);
	msg->msmContactValue = localContact(username,expires);
	writePrivateHeaders(msg,chan);
	return msg;
}




string SipBase::makeSDPOffer()
{
	SdpInfo sdp;
	sdp.sdpInitOffer(this);
	return sdp.sdpValue();
	//return makeSDP("0","0");
}

// mCodec is an implicit parameter, consisting of the chosen codec.
string SipBase::makeSDPAnswer()
{
	SdpInfo answer;
	answer.sdpInitOffer(this);
	mSdpAnswer = answer.sdpValue();
	return mSdpAnswer;
}


// A request inside an invite is ACK, CANCEL, BYE, INFO, or re-INVITE.
// The only target-refresh-request is re-INVITE, which can change the "Dialog State".
// For ACK sec 172.16.58.3 the To must equal the To of the response being acked, which specifically contains a tag.
// ACK contains only one via == top via of original request with matching branch.
// TODO: ACK must copy route header fields from INVITE.
// sec 9.1 For CANCEL, all fields must == INVITE except the method.  Note CSeq must match too.  Must have single via
// matching the [invite] request being cancelled, with matching branch.  TODO: Must copy route header from request.
// 15.1.1: BYE is a new request within a dialog as per section 12.2.
// transaction constructed as per with a new tag, branch, etc.
// It should not include CONTACT because it is non-target-refresh.
// Implicit parameters from SipBase: mCallId, mRemoteUsername, mRemoteDomain, mCSeq, etc.
SipMessage *SipBase::makeRequest(string method,string requestUri, string whoami, SipPreposition*toHeader, SipPreposition*fromHeader, string branch)
{
	SipMessage *invite = new SipMessage();
	invite->msmReqMethod = method;
	invite->msmCallId = this->mCallId;
	//string toContact = makeUri(mRemoteUsername,mRemoteDomain);	// dialed_number@remote_ip
	//string fromContact = makeUri(mSipUsername,localIP());
	//invite->msmReqUri = makeUri(mRemoteUsername,mRemoteDomain);
	invite->msmReqUri = requestUri;
	invite->smAddViaBranch(this,branch);
	invite->msmCSeqMethod = invite->msmReqMethod;
	invite->msmCSeqNum = mLocalCSeq;		// We dont need to advance for an initial request; caller advances if necessary.

	string realm = gConfig.getStr("SIP.Realm");
	if (realm.length() > 0) {
		string tousername = toHeader->uriUsername();
		string fromusername = fromHeader->uriUsername();
		string toTag = toHeader->getTag();
		string fromTag = fromHeader->getTag();
		string toUriString = makeUri(tousername ,realm,0);
		string fromUriString = makeUri(fromusername ,realm,0);
		invite->msmTo = SipPreposition("",toUriString, toTag );
		invite->msmFrom = SipPreposition("",fromUriString, fromTag );
	} else {
		invite->msmTo = *toHeader;
		invite->msmFrom = *fromHeader;
	}

	invite->msmContactValue = localContact(whoami);
	string prefid = this->preferredIdentity(whoami);
	static const string cPreferredIdentityString("P-Preferred-Identity");
	invite->smAddHeader(cPreferredIdentityString,prefid);

	// The caller has not added the content yet: saveInviteOrMessage(invite,true);
	return invite;
}

// This is an *initial* request only, for INVITE or MESSAGE.
// This version generates the request from the values in the DialogStateVars.
SipMessage *SipBase::makeInitialRequest(string method)
{
	string requestUri = dsRemoteURI();
{'fraction_non_alphanumeric': 0.13033152147299348, 'fraction_numerical': 0.04255423338719945, 'mean_word_length': 4.129741271037428, 'pattern_counts': {'":': 0, '<': 268, '<?xml version=': 0, '>': 163, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 43, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7610027', 'n_tokens_mistral': 16026, 'n_tokens_neox': 14652, 'n_words': 3911}