sbc: fix call transfers with Replaces through SBC

Replaces header is fixed in either Replaces of INVITE or of REFER
messages. New sbc profile options:
fix_replaces_inv=[yes|no]
fix_replaces_ref=[yes|no]
1.4.3+spce2.8
Stefan Sayer 13 years ago committed by Andrew Pogrebennyk
parent 8b76713c52
commit 135890ac3c

@ -0,0 +1,171 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 2 of the License, or
* (at your option) any later version.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ReplacesMapper.h"
#include "AmUtils.h"
#include "AmUriParser.h"
#include "AmSipHeaders.h"
bool findTag(const string replaces, const string& tag, size_t& p1, size_t& len);
void fixReplaces(AmSipRequest& req, bool is_invite) {
string replaces;
string refer_to;
AmUriParser refer_target;
vector<string> hdrs; // headers from Refer-To URI
vector<string>::iterator replaces_hdr_it; // Replaces header from Refer-To URI
DBG("Replaces handler: fixing %s request\n", is_invite?"INVITE":"REFER");
if (is_invite) {
replaces = getHeader(req.hdrs, SIP_HDR_REPLACES, true);
if (replaces.empty()) {
DBG("Replaces handler: no Replaces in INVITE, ignoring\n");
return;
}
} else {
refer_to = getHeader(req.hdrs, SIP_HDR_REFER_TO, SIP_HDR_REFER_TO_COMPACT, true);
if (refer_to.empty()) {
DBG("Replaces handler: empty Refer-To header, ignoring\n");
return;
}
size_t pos=0; size_t end=0;
if (!refer_target.parse_contact(refer_to, pos, end)) {
DBG("Replaces handler: unable to parse Refer-To name-addr, ignoring\n");
return;
}
if (refer_target.uri_headers.empty()) {
DBG("Replaces handler: no headers in Refer-To target, ignoring\n");
return;
}
hdrs = explode(refer_target.uri_headers, ";");
for (replaces_hdr_it=hdrs.begin(); replaces_hdr_it != hdrs.end(); replaces_hdr_it++) {
string s = URL_decode(*replaces_hdr_it);
const char* Replaces_str = "Replaces";
if ((s.length() >= 8) &&
!strncmp(Replaces_str, s.c_str(), 8)) {
size_t pos = 8;
while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
if (s[pos] != '=')
continue;
pos++;
while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
replaces = s.substr(pos);
break;
}
}
if (replaces_hdr_it == hdrs.end()) {
DBG("Replaces handler: no Replaces headers in Refer-To target, ignoring\n");
return;
}
}
DBG("Replaces found: '%s'\n", replaces.c_str());
size_t ftag_begin; size_t ftag_len;
size_t ttag_begin; size_t ttag_len;
size_t cid_len=0;
// todo: parse full replaces header and reconstruct including unknown params
if (!findTag(replaces, "from-tag=", ftag_begin, ftag_len)) {
WARN("Replaces missing 'from-tag', ignoring\n");
return;
}
if (!findTag(replaces, "to-tag=", ttag_begin, ttag_len)) {
WARN("Replaces missing 'to-tag', ignoring\n");
return;
}
while (cid_len < replaces.size() && replaces[cid_len] != ';')
cid_len++;
string ftag = replaces.substr(ftag_begin, ftag_len);
string ttag = replaces.substr(ttag_begin, ttag_len);
string callid = replaces.substr(0, cid_len);
bool early_only = replaces.find("early-only") != string::npos;
DBG("Replaces handler: found callid='%s', ftag='%s', ttag='%s'\n",
callid.c_str(), ftag.c_str(), ttag.c_str());
SBCCallRegistryEntry other_dlg;
if (SBCCallRegistry::lookupCall(ttag, other_dlg)) {
replaces = other_dlg.callid+
";from-tag="+other_dlg.ltag+";to-tag="+other_dlg.rtag;
if (early_only)
replaces += ";early_only";
DBG("Replaces handler: mapped Replaces to: '%s'\n", replaces.c_str());
if (is_invite) {
removeHeader(req.hdrs, SIP_HDR_REPLACES);
req.hdrs+=SIP_HDR_COLSP(SIP_HDR_REPLACES)+replaces+CRLF;
} else {
string replaces_enc = SIP_HDR_REPLACES "="+URL_encode(replaces);
string new_hdrs;
for (vector<string>::iterator it = hdrs.begin(); it != hdrs.end(); it++) {
if (it != hdrs.begin())
new_hdrs+=";";
if (it != replaces_hdr_it) {
// different hdr, just add it
new_hdrs+=*it;
} else {
//reconstructed replaces hdr
new_hdrs+=replaces_enc;
}
}
refer_target.uri_headers=new_hdrs;
removeHeader(req.hdrs, SIP_HDR_REFER_TO);
removeHeader(req.hdrs, SIP_HDR_REFER_TO_COMPACT);
req.hdrs+=SIP_HDR_COLSP(SIP_HDR_REFER_TO)+refer_target.nameaddr_str()+CRLF;
}
} else {
DBG("Replaces handler: call with tag '%s' not found\n", ttag.c_str());
}
}
bool findTag(const string replaces, const string& tag, size_t& p1, size_t& len)
{
size_t i = replaces.find(tag);
if (i == string::npos) return false;
p1 = i+tag.length();
size_t j = replaces.find(';', p1);
if (j != string::npos) {
len = j - p1;
} else {
len = replaces.size() - i;
}
return true;
}

@ -0,0 +1,34 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 2 of the License, or
* (at your option) any later version.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ReplacesMapper_H
#define _ReplacesMapper_H
#include "SBCCallRegistry.h"
#include "AmSipMsg.h"
void fixReplaces(AmSipRequest& req, bool is_invite);
#endif

@ -47,6 +47,8 @@ SBC - feature-wishlist
#include "HeaderFilter.h"
#include "ParamReplacer.h"
#include "SDPFilter.h"
#include "SBCCallRegistry.h"
#include "ReplacesMapper.h"
using std::map;
@ -513,6 +515,7 @@ SBCDialog::SBCDialog(const SBCCallProfile& call_profile)
SBCDialog::~SBCDialog()
{
SBCCallRegistry::removeCall(dlg.local_tag);
}
void SBCDialog::onInvite(const AmSipRequest& req)
@ -718,6 +721,15 @@ void SBCDialog::onInvite(const AmSipRequest& req)
}
}
call_profile.fix_replaces_inv =
replaceParameters(call_profile.fix_replaces_inv, "fix_replaces_inv", REPLACE_VALS);
call_profile.fix_replaces_ref =
replaceParameters(call_profile.fix_replaces_ref, "fix_replaces_ref", REPLACE_VALS);
if (call_profile.fix_replaces_inv == "yes") {
fixReplaces(invite_req, true);
}
#undef REPLACE_VALS
DBG("SBC: connecting to '%s'\n",ruri.c_str());
@ -786,13 +798,21 @@ void SBCDialog::onControlCmd(string& cmd, AmArg& params) {
}
int SBCDialog::relayEvent(AmEvent* ev) {
if ((call_profile.headerfilter != Transparent) &&
(ev->event_id == B2BSipRequest)) {
// header filter
if (ev->event_id == B2BSipRequest) {
B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
assert(req_ev);
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
assert(req_ev);
if (call_profile.headerfilter != Transparent) {
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
}
if (req_ev->req.method == SIP_METH_REFER &&
call_profile.fix_replaces_ref == "yes") {
fixReplaces(req_ev->req, false);
}
} else {
if (ev->event_id == B2BSipReply) {
if ((call_profile.headerfilter != Transparent) ||
@ -1120,6 +1140,11 @@ void SBCDialog::createCalleeSession()
throw;
}
// A->B
SBCCallRegistry::addCall(dlg.local_tag, SBCCallRegistryEntry(callee_dlg.callid, callee_dlg.local_tag, ""));
// B->A
SBCCallRegistry::addCall(callee_dlg.local_tag, SBCCallRegistryEntry(dlg.callid, dlg.local_tag, dlg.remote_tag));
callee_session->start();
AmSessionContainer* sess_cont = AmSessionContainer::instance();
@ -1143,6 +1168,8 @@ SBCCalleeSession::SBCCalleeSession(const AmB2BCallerSession* caller,
SBCCalleeSession::~SBCCalleeSession() {
if (auth)
delete auth;
SBCCallRegistry::removeCall(dlg.local_tag);
}
inline UACAuthCred* SBCCalleeSession::getCredentials() {
@ -1150,13 +1177,21 @@ inline UACAuthCred* SBCCalleeSession::getCredentials() {
}
int SBCCalleeSession::relayEvent(AmEvent* ev) {
if ((call_profile.headerfilter != Transparent) &&
(ev->event_id == B2BSipRequest)) {
// header filter
if (ev->event_id == B2BSipRequest) {
B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
assert(req_ev);
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
assert(req_ev);
if (call_profile.headerfilter != Transparent) {
inplaceHeaderFilter(req_ev->req.hdrs,
call_profile.headerfilter_list, call_profile.headerfilter);
}
if (req_ev->req.method == SIP_METH_REFER &&
call_profile.fix_replaces_ref == "yes") {
fixReplaces(req_ev->req, false);
}
} else {
if (ev->event_id == B2BSipReply) {
if ((call_profile.headerfilter != Transparent) ||
@ -1232,18 +1267,25 @@ void SBCCalleeSession::onSipReply(const AmSipReply& reply, int old_dlg_status,
bool fwd = t != relayed_req.end();
DBG("onSipReply: %i %s (fwd=%i)\n",reply.code,reply.reason.c_str(),fwd);
DBG("onSipReply: content-type = %s\n",reply.content_type.c_str());
if(fwd) {
CALL_EVENT_H(onSipReply,reply, old_dlg_status, trans_method);
}
if (NULL == auth) {
// update call registry (unfortunately has to be done always -
// not possible to determine if learned in this reply)
if (!dlg.remote_tag.empty()) {
SBCCallRegistry::updateCall(other_id, dlg.remote_tag);
}
if (NULL == auth) {
AmB2BCalleeSession::onSipReply(reply,old_dlg_status, trans_method);
return;
}
unsigned int cseq_before = dlg.cseq;
if (!auth->onSipReply(reply, old_dlg_status, trans_method)) {
AmB2BCalleeSession::onSipReply(reply, old_dlg_status, trans_method);
AmB2BCalleeSession::onSipReply(reply, old_dlg_status, trans_method);
} else {
if (cseq_before != dlg.cseq) {
DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; "

@ -108,6 +108,9 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
}
}
fix_replaces_inv = cfg.getParameter("fix_replaces_inv");
fix_replaces_ref = cfg.getParameter("fix_replaces_ref");;
sst_enabled = cfg.getParameter("enable_session_timer", "no") == "yes";
use_global_sst_config = !cfg.hasParameter("session_expires");
@ -215,6 +218,9 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
sdpfilter_enabled?"en":"dis", FilterType2String(sdpfilter),
sdpfilter_list.size());
INFO("SBC: fixing Replaces in INVITE: '%s'\n", fix_replaces_inv.c_str());
INFO("SBC: fixing Replaces in REFER: '%s'\n", fix_replaces_ref.c_str());
INFO("SBC: RTP relay %sabled\n", rtprelay_enabled?"en":"dis");
if (rtprelay_enabled) {
if (!force_symmetric_rtp.empty()) {

@ -68,6 +68,9 @@ struct SBCCallProfile {
FilterType sdpfilter;
set<string> sdpfilter_list;
string fix_replaces_inv;
string fix_replaces_ref;
bool sst_enabled;
bool use_global_sst_config;

@ -0,0 +1,79 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 2 of the License, or
* (at your option) any later version.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "SBCCallRegistry.h"
#include "log.h"
AmMutex SBCCallRegistry::registry_mutex;
std::map<string, SBCCallRegistryEntry> SBCCallRegistry::registry;
void SBCCallRegistry::addCall(const string& ltag, const SBCCallRegistryEntry& other_dlg) {
registry_mutex.lock();
registry[ltag] = other_dlg;
registry_mutex.unlock();
DBG("SBCCallRegistry: Added call '%s' - mapped to: '%s'\n", ltag.c_str(), other_dlg.ltag.c_str());
}
void SBCCallRegistry::updateCall(const string& ltag, const string& other_rtag) {
registry_mutex.lock();
std::map<string, SBCCallRegistryEntry>::iterator it = registry.find(ltag);
if (it != registry.end()) {
it->second.rtag = other_rtag;
}
registry_mutex.unlock();
DBG("SBCCallRegistry: Updated call '%s' - rtag to: '%s'\n", ltag.c_str(), other_rtag.c_str());
}
bool SBCCallRegistry::lookupCall(const string& ltag, SBCCallRegistryEntry& other_dlg) {
bool res = false;
registry_mutex.lock();
std::map<string, SBCCallRegistryEntry>::iterator it = registry.find(ltag);
if (it != registry.end()) {
res = true;
other_dlg = it->second;
}
registry_mutex.unlock();
if (res) {
DBG("SBCCallRegistry: found call mapping '%s' -> '%s'/'%s'/'%s'\n",
ltag.c_str(), other_dlg.ltag.c_str(), other_dlg.rtag.c_str(), other_dlg.callid.c_str());
} else {
DBG("SBCCallRegistry: no call mapping found for '%s'\n", ltag.c_str());
}
return res;
}
void SBCCallRegistry::removeCall(const string& ltag) {
registry_mutex.lock();
registry.erase(ltag);
registry_mutex.unlock();
DBG("SBCCallRegistry: removed entry for call '%s'\n", ltag.c_str());
}

@ -0,0 +1,61 @@
/*
* Copyright (C) 2013 Stefan Sayer
*
* This file is part of SEMS, a free SIP media server.
*
* SEMS 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 2 of the License, or
* (at your option) any later version.
*
* For a license to use the SEMS software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* SEMS 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SBCCallRegistry_H
#define _SBCCallRegistry_H
#include "AmThread.h"
#include <string>
using std::string;
#include <map>
struct SBCCallRegistryEntry
{
string ltag;
string rtag;
string callid;
SBCCallRegistryEntry() { }
SBCCallRegistryEntry(const string& callid, const string& ltag, const string& rtag)
: ltag(ltag), rtag(rtag), callid(callid) { }
};
class SBCCallRegistry
{
static AmMutex registry_mutex;
static std::map<string, SBCCallRegistryEntry> registry;
public:
SBCCallRegistry() { }
~SBCCallRegistry() { }
static void addCall(const string& ltag, const SBCCallRegistryEntry& other_dlg);
static void updateCall(const string& ltag, const string& other_rtag);
static bool lookupCall(const string& ltag, SBCCallRegistryEntry& other_dlg);
static void removeCall(const string& ltag);
};
#endif

@ -46,6 +46,10 @@
# translate some 6xx class replies to 4xx class:
#reply_translations="603=>488 Not acceptable here|600=>406 Not Acceptable"
## fix replaces for call transfers
# fix_replaces_inv=yes
# fix_replaces_ref=yes
## authentication:
#enable_auth=yes
#auth_user=$P(u)

@ -11,6 +11,8 @@
#define SIP_METH_ACK "ACK"
#define SIP_METH_SUBSCRIBE "SUBSCRIBE"
#define SIP_METH_NOTIFY "NOTIFY"
#define SIP_METH_CANCEL "CANCEL"
#define SIP_METH_REFER "REFER"
#define SIP_HDR_FROM "From"
#define SIP_HDR_TO "To"
@ -26,6 +28,7 @@
#define SIP_HDR_P_ASSERTED_IDENTITY "P-Asserted-Identity"
#define SIP_HDR_P_PREFERRED_IDENTITY "P-Preferred-Identity"
#define SIP_HDR_REFER_TO "Refer-To"
#define SIP_HDR_REFER_TO_COMPACT "r"
#define SIP_HDR_EXPIRES "Expires"
#define SIP_HDR_SESSION_EXPIRES "Session-Expires"
#define SIP_HDR_MIN_SE "Min-SE"
@ -40,6 +43,7 @@
#define SIP_HDR_ACCEPT "Accept"
#define SIP_HDR_EVENT "Event"
#define SIP_HDR_SUBSCRIPTION_STATE "Subscription-State"
#define SIP_HDR_REPLACES "Replaces"
#define SIP_HDR_COL(_hdr) _hdr ":"
#define SIP_HDR_COLSP(_hdr) SIP_HDR_COL(_hdr) " "

@ -2,11 +2,20 @@ NAME=sems_tests
SIP_STACK_DIR=../sip
SIP_STACK=$(SIP_STACK_DIR)/sip_stack.a
CORE_SRCS=$(filter-out ../sems.cpp , $(wildcard ../*.cpp))
CORE_SRCS=$(filter-out ../sems.cpp , $(wildcard ../*.cpp))
CORE_HDRS=$(CORE_SRCS:.cpp=.h)
CORE_OBJS=$(CORE_SRCS:.cpp=.o)
CORE_DEPS=$(subst ../,,$(CORE_SRCS:.cpp=.d))
SBC_DIR=../../apps/sbc/
SBC_SRCS=$(wildcard $(SBC_DIR)*.cpp)
SBC_HDRS=$(SBC_SRCS:.cpp=.h)
SBC_OBJS=$(SBC_SRCS:.cpp=.o)
SBC_DEPS=$(subst $(SBC_DIR),,$(SBC_SRCS:.cpp=.d))
AUTH_DIR=../plug-in/uac_auth
AUTH_OBJS=$(AUTH_DIR)/UACAuth.o
SRCS=$(wildcard *.cpp)
HDRS=$(SRCS:.cpp=.h)
OBJS=$(SRCS:.cpp=.o)
@ -18,7 +27,7 @@ EXTRA_LDFLAGS += -lresolv
.PHONY: all
all: ../../Makefile.defs
-@$(MAKE) core_deps && $(MAKE) deps && \
-@$(MAKE) core_deps && $(MAKE) sbc_deps && $(MAKE) deps && \
$(MAKE) $(NAME) && \
./$(NAME)
@ -40,6 +49,12 @@ deps: $(DEPS)
.PHONY: core_deps
core_deps: $(CORE_DEPS)
.PHONY: sbc_deps
sbc_deps: $(SBC_DEPS)
AUTH_OBJS: $(AUTH_DIR)/UACAuth.cpp $(AUTH_DIR)/UACAuth.h
cd $(AUTH_DIR) ; $(MAKE) AUTH_OBJS
COREPATH=..
include ../../Makefile.defs
@ -50,17 +65,19 @@ include ../../Makefile.defs
%.d : %.cpp %.h ../../Makefile.defs
$(CXX) -MM $< $(CPPFLAGS) $(CXXFLAGS) > $@
%.d : $(SBC_DIR)%.cpp $(SBC_DIR)%.h ../../Makefile.defs
$(CXX) -MM $< $(CPPFLAGS) $(CXXFLAGS) > $@
%.d : ../%.cpp ../%.h ../../Makefile.defs
$(CXX) -MM $< $(CPPFLAGS) $(CXXFLAGS) > $@
$(NAME): $(OBJS) $(CORE_OBJS) $(SIP_STACK) ../../Makefile.defs
$(NAME): $(OBJS) $(CORE_OBJS) $(SBC_OBJS) $(SIP_STACK) ../../Makefile.defs
-@echo ""
-@echo "making $(NAME)"
$(LD) -o $(NAME) $(OBJS) $(CORE_OBJS) $(SIP_STACK) $(LDFLAGS) $(EXTRA_LDFLAGS)
$(LD) -o $(NAME) $(OBJS) $(CORE_OBJS) $(SBC_OBJS) $(SIP_STACK) $(LDFLAGS) $(EXTRA_LDFLAGS) $(AUTH_OBJS)
ifeq '$(NAME)' '$(MAKECMDGOALS)'
include $(DEPS) $(CORE_DEPS)
include $(DEPS) $(CORE_DEPS) $(SBC_DEPS)
endif

@ -24,6 +24,7 @@ FCT_BGN() {
FCTMF_SUITE_CALL(test_headers);
FCTMF_SUITE_CALL(test_jsonarg);
FCTMF_SUITE_CALL(test_uriparser);
FCTMF_SUITE_CALL(test_replaces);
} FCT_END();

@ -0,0 +1,103 @@
#include "fct.h"
#include "log.h"
#include "AmSipHeaders.h"
#include "AmSipMsg.h"
#include "AmUtils.h"
#include "AmUriParser.h"
#include "../../apps/sbc/ReplacesMapper.h"
#include "../../apps/sbc/SBCCallRegistry.h"
FCTMF_SUITE_BGN(test_replaces) {
FCT_TEST_BGN(registry_simple) {
SBCCallRegistryEntry e = SBCCallRegistryEntry("callid2", "ltag2", "rtag2");
SBCCallRegistry::addCall("ltag", e);
fct_chk(SBCCallRegistry::lookupCall("ltag",e));
fct_chk(!SBCCallRegistry::lookupCall("ltag3",e));
SBCCallRegistry::removeCall("ltag");
} FCT_TEST_END();
FCT_TEST_BGN(replaces_fixup_invite) {
SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
SBCCallRegistry::addCall("Ct", e);
SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
SBCCallRegistry::addCall("C2f", e2);
AmSipRequest r;
r.hdrs="Replaces: C;from-tag=Cf;to-tag=Ct\r\n";
fixReplaces(r, true);
DBG("r.hdrs='%s'\n", r.hdrs.c_str());
fct_chk(r.hdrs=="Replaces: C2;from-tag=C2f;to-tag=C2t\r\n");
SBCCallRegistry::removeCall("Ct");
SBCCallRegistry::removeCall("C2f");
} FCT_TEST_END();
FCT_TEST_BGN(replaces_fixup_refer) {
SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
SBCCallRegistry::addCall("Ct", e);
SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
SBCCallRegistry::addCall("C2f", e2);
AmSipRequest r;
string orig_str = "Refer-To: \"Mr. Watson\" <sip:watson@bell-telephone.com?Replaces=C%3Bto-tag%3DCt%3Bfrom-tag%3DCf>;q=0.1";
string new_str = "Refer-To: \"Mr. Watson\" <sip:watson@bell-telephone.com?Replaces=C2%3Bfrom-tag%3DC2f%3Bto-tag%3DC2t>;q=0.1\r\n";
r.hdrs=orig_str+"\r\n";
fixReplaces(r, false);
DBG("r.hdrs='%s'\n", r.hdrs.c_str());
DBG("new s='%s'\n", new_str.c_str());
fct_chk(r.hdrs==new_str);
SBCCallRegistry::removeCall("Ct");
SBCCallRegistry::removeCall("C2f");
} FCT_TEST_END();
FCT_TEST_BGN(replaces_fixup_refer2) {
SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
SBCCallRegistry::addCall("Ct", e);
SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
SBCCallRegistry::addCall("C2f", e2);
AmSipRequest r;
string orig_str = "Refer-To: \"Mr. Watson\" <sip:watson@bell-telephone.com?Require=replaces;Replaces=C%3Bto-tag%3DCt%3Bfrom-tag%3DCf>;q=0.1\r\n";
string new_str = "Refer-To: \"Mr. Watson\" <sip:watson@bell-telephone.com?Require=replaces;Replaces=C2%3Bfrom-tag%3DC2f%3Bto-tag%3DC2t>;q=0.1\r\n";
r.hdrs=orig_str;
fixReplaces(r, false);
DBG("r.hdrs='%s'\n", r.hdrs.c_str());
DBG("new s='%s'\n", new_str.c_str());
fct_chk(r.hdrs==new_str);
SBCCallRegistry::removeCall("Ct");
SBCCallRegistry::removeCall("C2f");
} FCT_TEST_END();
FCT_TEST_BGN(replaces_fixup_refer3) {
SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
SBCCallRegistry::addCall("Ct", e);
SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
SBCCallRegistry::addCall("C2f", e2);
AmSipRequest r;
string orig_str = "Refer-To: \"Mr. Watson\" <sip:watson@bell-telephone.com?Require=replaces;Replaces=C%3Bto-tag%3DCt%3Bfrom-tag%3DCf;Bla=Blub>;q=0.1\r\n";
string new_str = "Refer-To: \"Mr. Watson\" <sip:watson@bell-telephone.com?Require=replaces;Replaces=C2%3Bfrom-tag%3DC2f%3Bto-tag%3DC2t;Bla=Blub>;q=0.1\r\n";
r.hdrs=orig_str;
fixReplaces(r, false);
DBG("r.hdrs='%s'\n", r.hdrs.c_str());
DBG("new s='%s'\n", new_str.c_str());
fct_chk(r.hdrs==new_str);
SBCCallRegistry::removeCall("Ct");
SBCCallRegistry::removeCall("C2f");
} FCT_TEST_END();
} FCTMF_SUITE_END();

@ -123,5 +123,13 @@ FCTMF_SUITE_BGN(test_uriparser) {
fct_chk(orig_str == a_str);
} FCT_TEST_END();
FCT_TEST_BGN(uriparser_url_escape) {
string src = "Replaces: CSADFSD;from-tag=31241231abc;to-tag=235123";
string dst = "Replaces%3A%20CSADFSD%3Bfrom-tag%3D31241231abc%3Bto-tag%3D235123";
fct_chk ( URL_decode(dst)==src );
fct_chk ( URL_encode(src)==dst );
fct_chk ( URL_decode(URL_encode(src))==src );
} FCT_TEST_END();
} FCTMF_SUITE_END();

@ -22,6 +22,7 @@ Features
o reply code translation
o SIP authentication
o SIP Session Timers
o Fixing Call Transfers (Replaces in REFER target and INVITE)
o call timer
o prepaid accounting
o CDR generation
@ -343,6 +344,20 @@ Warning: Changing response codes, especially between different response
code classes, can seriously mess up everything. Use with caution
and only if you know what you are doing!
Fixing Call Transfers (Replaces in REFER target and INVITE)
-----------------------------------------------------------
Using the profile options fix_replaces_inv and fix_replaces_ref Replaces
can be fixed for call transfers going through the SBC.
fix_replaces_inv=yes
fix_replaces_ref=yes
For situations where the call transfer is handled by the UAs (phone handles
the REFER), the Replaces should be fixed in the INVITE message (fix_replaces_inv=yes).
For situations where a PBX handles the call transfer (handles the REFER),
the Replaces should be fixed in the REFER message (fix_replaces_ref=yes).
Reliable 1xx (PRACK)
--------------------

Loading…
Cancel
Save