Browse code

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]

Conflicts:

apps/sbc/SBC.cpp
apps/sbc/SBCCallProfile.cpp
apps/sbc/SBCCallProfile.h
core/AmSipHeaders.h
core/tests/Makefile
core/tests/sems_tests.cpp

Stefan Sayer authored on 16/08/2013 14:32:30
Showing 19 changed files
... ...
@@ -31,6 +31,7 @@
31 31
 #include "AmSipHeaders.h"
32 32
 #include "AmUtils.h"
33 33
 #include "AmRtpReceiver.h"
34
+#include "SBCCallRegistry.h"
34 35
 
35 36
 #define TRACE DBG
36 37
 
... ...
@@ -230,6 +231,15 @@ CallLeg::CallLeg(const CallLeg* caller, AmSipDialog* p_dlg, AmSipSubscription* p
230 231
   setEnableDtmfTranscoding(caller->getEnableDtmfTranscoding());
231 232
   caller->getLowFiPLs(lowfi_payloads);
232 233
   setLowFiPLs(lowfi_payloads);
234
+
235
+ 
236
+  // A->B
237
+  SBCCallRegistry::addCall(caller_dlg->getLocalTag(),
238
+			   SBCCallRegistryEntry(dlg->getCallid(), dlg->getLocalTag(), ""));
239
+  // B->A
240
+  SBCCallRegistry::addCall(dlg->getLocalTag(),
241
+			   SBCCallRegistryEntry(caller_dlg->getCallid(), caller_dlg->getLocalTag(), caller_dlg->getRemoteTag()));
242
+
233 243
 }
234 244
 
235 245
 // caller
... ...
@@ -266,6 +276,8 @@ CallLeg::~CallLeg()
266 276
     pending_updates.pop_front();
267 277
     delete u;
268 278
   }
279
+
280
+  SBCCallRegistry::removeCall(getLocalTag());
269 281
 }
270 282
 
271 283
 void CallLeg::terminateOtherLeg()
... ...
@@ -1017,6 +1029,13 @@ void CallLeg::onSipReply(const AmSipRequest& req, const AmSipReply& reply, AmSip
1017 1029
       terminateLeg(); // commit suicide (don't let the master to kill us)
1018 1030
     }
1019 1031
   }
1032
+
1033
+  // update call registry (unfortunately has to be done always -
1034
+  // not possible to determine if learned in this reply (?))
1035
+  if (!dlg->getRemoteTag().empty()) {
1036
+    SBCCallRegistry::updateCall(getOtherId(), dlg->getRemoteTag());
1037
+  }
1038
+
1020 1039
 }
1021 1040
 
1022 1041
 // was for caller only
1023 1042
new file mode 100644
... ...
@@ -0,0 +1,171 @@
1
+/*
2
+ * Copyright (C) 2013 Stefan Sayer
3
+ *
4
+ * This file is part of SEMS, a free SIP media server.
5
+ *
6
+ * SEMS is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * For a license to use the SEMS software under conditions
12
+ * other than those described here, or to purchase support for this
13
+ * software, please contact iptel.org by e-mail at the following addresses:
14
+ *    info@iptel.org
15
+ *
16
+ * SEMS is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
+ * GNU General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU General Public License
22
+ * along with this program; if not, write to the Free Software
23
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
+ */
25
+
26
+#include "ReplacesMapper.h"
27
+#include "AmUtils.h"
28
+#include "AmUriParser.h"
29
+#include "AmSipHeaders.h"
30
+
31
+bool findTag(const string replaces, const string& tag, size_t& p1, size_t& len);
32
+
33
+void fixReplaces(string& req_hdrs, bool is_invite) {
34
+
35
+  string replaces;
36
+  string refer_to;
37
+  AmUriParser refer_target;
38
+  vector<string> hdrs;                      // headers from Refer-To URI
39
+  vector<string>::iterator replaces_hdr_it; // Replaces header from Refer-To URI
40
+
41
+  DBG("Replaces handler: fixing %s request\n", is_invite?"INVITE":"REFER");
42
+
43
+  if (is_invite) {
44
+    replaces = getHeader(req_hdrs, SIP_HDR_REPLACES, true);
45
+    if (replaces.empty()) {
46
+      DBG("Replaces handler: no Replaces in INVITE, ignoring\n");
47
+      return;
48
+    }
49
+  } else {
50
+    refer_to = getHeader(req_hdrs, SIP_HDR_REFER_TO, SIP_HDR_REFER_TO_COMPACT, true);
51
+    if (refer_to.empty()) {
52
+      DBG("Replaces handler: empty Refer-To header, ignoring\n");
53
+      return;
54
+    }
55
+
56
+    size_t pos=0; size_t end=0;
57
+    if (!refer_target.parse_contact(refer_to, pos, end)) {
58
+      DBG("Replaces handler: unable to parse Refer-To name-addr, ignoring\n");
59
+      return;
60
+    }
61
+
62
+    if (refer_target.uri_headers.empty()) {
63
+      DBG("Replaces handler: no headers in Refer-To target, ignoring\n");
64
+      return;
65
+    }
66
+
67
+    hdrs = explode(refer_target.uri_headers, ";");
68
+    for (replaces_hdr_it=hdrs.begin(); replaces_hdr_it != hdrs.end(); replaces_hdr_it++) {
69
+
70
+      string s = URL_decode(*replaces_hdr_it);
71
+      const char* Replaces_str = "Replaces";
72
+      if ((s.length() >= 8) &&
73
+	  !strncmp(Replaces_str, s.c_str(), 8)) {
74
+	size_t pos = 8;
75
+	while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
76
+	if (s[pos] != '=')
77
+	  continue;
78
+	pos++;
79
+	while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
80
+	replaces = s.substr(pos);
81
+	break;
82
+      }
83
+    }
84
+    
85
+    if (replaces_hdr_it == hdrs.end()) {
86
+      DBG("Replaces handler: no Replaces headers in Refer-To target, ignoring\n");
87
+      return;
88
+    }
89
+  }
90
+
91
+  DBG("Replaces found: '%s'\n", replaces.c_str());
92
+  size_t ftag_begin; size_t ftag_len;
93
+  size_t ttag_begin; size_t ttag_len;
94
+  size_t cid_len=0;
95
+ 
96
+  // todo: parse full replaces header and reconstruct including unknown params
97
+  if (!findTag(replaces, "from-tag=", ftag_begin, ftag_len)) {
98
+    WARN("Replaces missing 'from-tag', ignoring\n");
99
+    return;
100
+  }
101
+
102
+  if (!findTag(replaces, "to-tag=", ttag_begin, ttag_len)) {
103
+    WARN("Replaces missing 'to-tag', ignoring\n");
104
+    return;
105
+  }
106
+  while (cid_len < replaces.size() && replaces[cid_len] != ';')
107
+    cid_len++;
108
+
109
+  string ftag = replaces.substr(ftag_begin, ftag_len);
110
+  string ttag = replaces.substr(ttag_begin, ttag_len);
111
+  string callid = replaces.substr(0, cid_len);
112
+  bool early_only = replaces.find("early-only") != string::npos;
113
+
114
+  DBG("Replaces handler: found callid='%s', ftag='%s', ttag='%s'\n",
115
+      callid.c_str(), ftag.c_str(), ttag.c_str());
116
+
117
+  SBCCallRegistryEntry other_dlg;
118
+  if (SBCCallRegistry::lookupCall(ttag, other_dlg)) {
119
+    replaces = other_dlg.callid+
120
+      ";from-tag="+other_dlg.ltag+";to-tag="+other_dlg.rtag;
121
+    if (early_only)
122
+      replaces += ";early_only";
123
+    DBG("Replaces handler: mapped Replaces to: '%s'\n", replaces.c_str());
124
+
125
+    if (is_invite) {
126
+      removeHeader(req_hdrs, SIP_HDR_REPLACES);
127
+      req_hdrs+=SIP_HDR_COLSP(SIP_HDR_REPLACES)+replaces+CRLF;
128
+    } else {
129
+      string replaces_enc = SIP_HDR_REPLACES "="+URL_encode(replaces);
130
+      string new_hdrs;
131
+      for (vector<string>::iterator it = hdrs.begin(); it != hdrs.end(); it++) {
132
+	if (it != hdrs.begin())
133
+	  new_hdrs+=";";
134
+
135
+	if (it != replaces_hdr_it) {
136
+	  // different hdr, just add it
137
+	  new_hdrs+=*it;
138
+	} else {
139
+	  //reconstructed replaces hdr
140
+	  new_hdrs+=replaces_enc;
141
+	}
142
+      }
143
+      refer_target.uri_headers=new_hdrs;
144
+      removeHeader(req_hdrs, SIP_HDR_REFER_TO);
145
+      removeHeader(req_hdrs, SIP_HDR_REFER_TO_COMPACT);
146
+      req_hdrs+=SIP_HDR_COLSP(SIP_HDR_REFER_TO)+refer_target.nameaddr_str()+CRLF;
147
+    }
148
+
149
+  } else {
150
+    DBG("Replaces handler: call with tag '%s' not found\n", ttag.c_str());
151
+  }
152
+
153
+ 
154
+}
155
+
156
+bool findTag(const string replaces, const string& tag, size_t& p1, size_t& len)
157
+{
158
+  size_t i = replaces.find(tag);
159
+  if (i == string::npos) return false;
160
+
161
+  p1 = i+tag.length();
162
+  size_t j = replaces.find(';', p1);
163
+  
164
+  if (j != string::npos) {
165
+    len = j - p1;
166
+  } else {
167
+    len = replaces.size() - i;
168
+  }
169
+  return true;
170
+}
171
+
0 172
new file mode 100644
... ...
@@ -0,0 +1,34 @@
1
+/*
2
+ * Copyright (C) 2013 Stefan Sayer
3
+ *
4
+ * This file is part of SEMS, a free SIP media server.
5
+ *
6
+ * SEMS is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * For a license to use the SEMS software under conditions
12
+ * other than those described here, or to purchase support for this
13
+ * software, please contact iptel.org by e-mail at the following addresses:
14
+ *    info@iptel.org
15
+ *
16
+ * SEMS is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
+ * GNU General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU General Public License
22
+ * along with this program; if not, write to the Free Software
23
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
+ */
25
+
26
+#ifndef _ReplacesMapper_H
27
+#define _ReplacesMapper_H
28
+
29
+#include "SBCCallRegistry.h"
30
+#include "AmSipMsg.h"
31
+
32
+void fixReplaces(string& req_hdrs, bool is_invite);
33
+
34
+#endif
... ...
@@ -717,7 +717,6 @@ bool SBCFactory::CCRoute(const AmSipRequest& req,
717 717
     di_args.push(AmArg());
718 718
     di_args.back().push((int) 0);
719 719
     di_args.back().push((int) 0);
720
-
721 720
     di_args.push(AmArg());
722 721
     AmArg& vals = di_args.back();
723 722
     vals.assertStruct();
... ...
@@ -810,7 +809,6 @@ bool SBCFactory::CCRoute(const AmSipRequest& req,
810 809
 	}
811 810
 
812 811
 	}
813
-
814 812
       }
815 813
     }
816 814
 
... ...
@@ -45,6 +45,7 @@
45 45
 #include "sip/sip_trans.h"
46 46
 
47 47
 #include "HeaderFilter.h"
48
+#include "ReplacesMapper.h"
48 49
 #include "ParamReplacer.h"
49 50
 #include "SDPFilter.h"
50 51
 #include "SBCEventLog.h"
... ...
@@ -405,6 +406,11 @@ int SBCCallLeg::relayEvent(AmEvent* ev)
405 406
             inplaceHeaderFilter(req_ev->req.hdrs, call_profile.headerfilter);
406 407
           }
407 408
 
409
+	  if (req_ev->req.method == SIP_METH_REFER &&
410
+	      call_profile.fix_replaces_ref == "yes") {
411
+	    fixReplaces(req_ev->req.hdrs, false);
412
+	  }
413
+
408 414
           DBG("filtering body for request '%s' (c/t '%s')\n",
409 415
               req_ev->req.method.c_str(), req_ev->req.body.getCTStr().c_str());
410 416
           // todo: handle filtering errors
... ...
@@ -871,6 +877,10 @@ void SBCCallLeg::onInvite(const AmSipRequest& req)
871 877
 
872 878
   inplaceHeaderFilter(invite_req.hdrs, call_profile.headerfilter);
873 879
 
880
+  if (call_profile.fix_replaces_inv == "yes") {
881
+    fixReplaces(invite_req.hdrs, true);
882
+  }
883
+
874 884
   if (call_profile.append_headers.size() > 2) {
875 885
     string append_headers = call_profile.append_headers;
876 886
     assertEndCRLF(append_headers);
... ...
@@ -238,6 +238,9 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
238 238
     CP_SST_CFGVAR("aleg_", "accept_501_reply", sst_a_cfg);
239 239
   }
240 240
 #undef CP_SST_CFGVAR
241
+
242
+  fix_replaces_inv = cfg.getParameter("fix_replaces_inv");
243
+  fix_replaces_ref = cfg.getParameter("fix_replaces_ref");;
241 244
   
242 245
   auth_enabled = cfg.getParameter("enable_auth", "no") == "yes";
243 246
   auth_credentials.user = cfg.getParameter("auth_user");
... ...
@@ -466,6 +469,9 @@ bool SBCCallProfile::readFromConfiguration(const string& name,
466 469
     INFO("SBC:      SDP filter is %sabled, %s, %zd items in list\n",
467 470
 	 mediafilter.size()?"en":"dis", filter_type.c_str(), filter_elems);
468 471
 
472
+    INFO("SBC:      fixing Replaces in INVITE: '%s'\n", fix_replaces_inv.c_str());
473
+    INFO("SBC:      fixing Replaces in REFER: '%s'\n", fix_replaces_ref.c_str());
474
+
469 475
     INFO("SBC:      RTP relay %sabled\n", rtprelay_enabled?"en":"dis");
470 476
     if (rtprelay_enabled) {
471 477
       if (!force_symmetric_rtp.empty()) {
... ...
@@ -808,6 +814,9 @@ bool SBCCallProfile::evaluate(ParamReplacerCtx& ctx,
808 814
 						      "auth_aleg_pwd", req);
809 815
   }
810 816
 
817
+  fix_replaces_inv = ctx.replaceParameters(fix_replaces_inv, "fix_replaces_inv", req);
818
+  fix_replaces_ref = ctx.replaceParameters(fix_replaces_ref, "fix_replaces_ref", req);
819
+
811 820
   REPLACE_IFACE_SIP(outbound_interface, outbound_interface_value);
812 821
 
813 822
   if (!codec_prefs.evaluate(ctx,req)) return false;
... ...
@@ -164,6 +164,9 @@ struct SBCCallProfile
164 164
   AmConfigReader sst_a_cfg;    // SST config (A leg)
165 165
   AmConfigReader sst_b_cfg;    // SST config (B leg)
166 166
 
167
+  string fix_replaces_inv;
168
+  string fix_replaces_ref;
169
+
167 170
   bool auth_enabled;
168 171
   UACAuthCred auth_credentials;
169 172
 
170 173
new file mode 100644
... ...
@@ -0,0 +1,79 @@
1
+/*
2
+ * Copyright (C) 2013 Stefan Sayer
3
+ *
4
+ * This file is part of SEMS, a free SIP media server.
5
+ *
6
+ * SEMS is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * For a license to use the SEMS software under conditions
12
+ * other than those described here, or to purchase support for this
13
+ * software, please contact iptel.org by e-mail at the following addresses:
14
+ *    info@iptel.org
15
+ *
16
+ * SEMS is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
+ * GNU General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU General Public License
22
+ * along with this program; if not, write to the Free Software
23
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
+ */
25
+
26
+#include "SBCCallRegistry.h"
27
+#include "log.h"
28
+
29
+AmMutex SBCCallRegistry::registry_mutex;
30
+std::map<string, SBCCallRegistryEntry> SBCCallRegistry::registry;
31
+
32
+void SBCCallRegistry::addCall(const string& ltag, const SBCCallRegistryEntry& other_dlg) {
33
+  registry_mutex.lock();
34
+  registry[ltag] = other_dlg;
35
+  registry_mutex.unlock();
36
+
37
+  DBG("SBCCallRegistry: Added call '%s' - mapped to: '%s'\n", ltag.c_str(), other_dlg.ltag.c_str());
38
+}
39
+
40
+void SBCCallRegistry::updateCall(const string& ltag, const string& other_rtag) {
41
+  registry_mutex.lock();
42
+
43
+  std::map<string, SBCCallRegistryEntry>::iterator it = registry.find(ltag);
44
+  if (it != registry.end()) {
45
+    it->second.rtag = other_rtag;
46
+  }
47
+
48
+  registry_mutex.unlock();
49
+
50
+  DBG("SBCCallRegistry: Updated call '%s' - rtag to: '%s'\n", ltag.c_str(), other_rtag.c_str());
51
+}
52
+
53
+bool SBCCallRegistry::lookupCall(const string& ltag, SBCCallRegistryEntry& other_dlg) {
54
+  bool res = false;
55
+
56
+  registry_mutex.lock();
57
+  std::map<string, SBCCallRegistryEntry>::iterator it = registry.find(ltag);
58
+  if (it != registry.end()) {
59
+    res = true;
60
+    other_dlg = it->second;
61
+  }
62
+  registry_mutex.unlock();
63
+
64
+  if (res) {
65
+    DBG("SBCCallRegistry: found call mapping '%s' -> '%s'/'%s'/'%s'\n",
66
+	ltag.c_str(), other_dlg.ltag.c_str(), other_dlg.rtag.c_str(), other_dlg.callid.c_str());
67
+  } else {
68
+    DBG("SBCCallRegistry: no call mapping found for '%s'\n", ltag.c_str());
69
+  }
70
+  return res;
71
+}
72
+
73
+void SBCCallRegistry::removeCall(const string& ltag) {
74
+  registry_mutex.lock();
75
+  registry.erase(ltag);
76
+  registry_mutex.unlock();  
77
+
78
+  DBG("SBCCallRegistry: removed entry for call '%s'\n", ltag.c_str());
79
+}
0 80
new file mode 100644
... ...
@@ -0,0 +1,61 @@
1
+/*
2
+ * Copyright (C) 2013 Stefan Sayer
3
+ *
4
+ * This file is part of SEMS, a free SIP media server.
5
+ *
6
+ * SEMS is free software; you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation; either version 2 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * For a license to use the SEMS software under conditions
12
+ * other than those described here, or to purchase support for this
13
+ * software, please contact iptel.org by e-mail at the following addresses:
14
+ *    info@iptel.org
15
+ *
16
+ * SEMS is distributed in the hope that it will be useful,
17
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
+ * GNU General Public License for more details.
20
+ *
21
+ * You should have received a copy of the GNU General Public License
22
+ * along with this program; if not, write to the Free Software
23
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
+ */
25
+
26
+#ifndef _SBCCallRegistry_H
27
+#define _SBCCallRegistry_H
28
+
29
+#include "AmThread.h"
30
+
31
+#include <string>
32
+using std::string;
33
+#include <map>
34
+
35
+struct SBCCallRegistryEntry
36
+{
37
+  string ltag;
38
+  string rtag;
39
+  string callid;
40
+  
41
+  SBCCallRegistryEntry() { }
42
+SBCCallRegistryEntry(const string& callid, const string& ltag, const string& rtag)
43
+  : ltag(ltag), rtag(rtag), callid(callid) { }
44
+};
45
+
46
+class SBCCallRegistry 
47
+{
48
+  static AmMutex registry_mutex;
49
+  static std::map<string, SBCCallRegistryEntry> registry;
50
+
51
+ public:
52
+  SBCCallRegistry() { }
53
+  ~SBCCallRegistry() { }
54
+
55
+  static void addCall(const string& ltag, const SBCCallRegistryEntry& other_dlg);
56
+  static void updateCall(const string& ltag, const string& other_rtag);
57
+  static bool lookupCall(const string& ltag, SBCCallRegistryEntry& other_dlg);
58
+  static void removeCall(const string& ltag);
59
+};
60
+
61
+#endif                           
... ...
@@ -6,6 +6,7 @@
6 6
 #include "AmEventQueueProcessor.h"
7 7
 #include "SBC.h"
8 8
 #include "RegisterDialog.h"
9
+#include "ReplacesMapper.h"
9 10
 
10 11
 /**
11 12
  * SimpleRelayDialog
... ...
@@ -41,7 +42,10 @@ SimpleRelayDialog::SimpleRelayDialog(SBCCallProfile &profile,
41 42
   : AmBasicSipDialog(this),
42 43
     AmEventQueue(this),
43 44
     parent_obj(parent_obj),
44
-    finished(false)
45
+    finished(false),
46
+    transparent_dlg_id(false),
47
+    keep_vias(false),
48
+    fix_replaces_ref(false)
45 49
 {
46 50
   if(parent_obj) {
47 51
     inc_ref(parent_obj);
... ...
@@ -53,7 +57,10 @@ SimpleRelayDialog::SimpleRelayDialog(atomic_ref_cnt* parent_obj)
53 57
   : AmBasicSipDialog(this),
54 58
     AmEventQueue(this),
55 59
     parent_obj(parent_obj),
56
-    finished(false)
60
+    finished(false),
61
+    transparent_dlg_id(false),
62
+    keep_vias(false),
63
+    fix_replaces_ref(false)
57 64
 {
58 65
   if(parent_obj) {
59 66
     inc_ref(parent_obj);
... ...
@@ -74,6 +81,11 @@ int SimpleRelayDialog::relayRequest(const AmSipRequest& req)
74 81
 
75 82
   string hdrs = req.hdrs;
76 83
   if(headerfilter.size()) inplaceHeaderFilter(hdrs, headerfilter);
84
+
85
+  if (fix_replaces_ref && req.method == SIP_METH_REFER) {
86
+    fixReplaces(hdrs, false);
87
+  }
88
+
77 89
   if(!append_headers.empty()) hdrs += append_headers;
78 90
 
79 91
   if(keep_vias)
... ...
@@ -405,6 +417,7 @@ int SimpleRelayDialog::initUAS(const AmSipRequest& req,
405 417
   append_headers = cp.aleg_append_headers_req;
406 418
   transparent_dlg_id = cp.transparent_dlg_id;
407 419
   keep_vias = cp.bleg_keep_vias;
420
+  fix_replaces_ref = cp.fix_replaces_ref=="yes";
408 421
 
409 422
   if(!cp.dlg_contact_params.empty())
410 423
     setContactParams(cp.dlg_contact_params);
... ...
@@ -54,6 +54,7 @@ class SimpleRelayDialog
54 54
   ReplyTranslationMap  reply_translations;
55 55
   bool                 transparent_dlg_id;
56 56
   bool                 keep_vias;
57
+  bool                 fix_replaces_ref;
57 58
 
58 59
   bool finished;
59 60
 
... ...
@@ -75,6 +75,10 @@
75 75
 # translate some 6xx class replies to 4xx class:
76 76
 #reply_translations="603=>488 Not acceptable here|600=>406 Not Acceptable"
77 77
 
78
+## fix replaces for call transfers
79
+# fix_replaces_inv=yes
80
+# fix_replaces_ref=yes
81
+
78 82
 ## authentication:
79 83
 #enable_auth=yes
80 84
 #auth_user=$P(u)
... ...
@@ -36,6 +36,7 @@
36 36
 #define SIP_HDR_P_ASSERTED_IDENTITY "P-Asserted-Identity"
37 37
 #define SIP_HDR_P_PREFERRED_IDENTITY "P-Preferred-Identity"
38 38
 #define SIP_HDR_REFER_TO        "Refer-To"
39
+#define SIP_HDR_REFER_TO_COMPACT "r"
39 40
 #define SIP_HDR_REFERRED_BY     "Referred-By"
40 41
 #define SIP_HDR_EXPIRES         "Expires"
41 42
 #define SIP_HDR_MIN_EXPIRES     "Min-Expires"
... ...
@@ -52,7 +53,7 @@
52 53
 #define SIP_HDR_ACCEPT           "Accept"
53 54
 #define SIP_HDR_EVENT            "Event"
54 55
 #define SIP_HDR_SUBSCRIPTION_STATE "Subscription-State"
55
-
56
+#define SIP_HDR_REPLACES          "Replaces"
56 57
 
57 58
 #define SIP_HDR_COL(_hdr)       _hdr ":"
58 59
 #define SIP_HDR_COLSP(_hdr)     SIP_HDR_COL(_hdr) " "
... ...
@@ -9,6 +9,12 @@ CORE_HDRS=$(CORE_SRCS:.cpp=.h)
9 9
 CORE_OBJS=$(CORE_SRCS:.cpp=.o)
10 10
 CORE_DEPS=$(subst ../,,$(CORE_SRCS:.cpp=.d))
11 11
 
12
+SBC_DIR=../../apps/sbc/
13
+SBC_SRCS=$(wildcard $(SBC_DIR)*.cpp)
14
+SBC_HDRS=$(SBC_SRCS:.cpp=.h)
15
+SBC_OBJS=$(SBC_SRCS:.cpp=.o)
16
+SBC_DEPS=$(subst $(SBC_DIR),,$(SBC_SRCS:.cpp=.d))
17
+
12 18
 AUTH_DIR=../plug-in/uac_auth
13 19
 AUTH_OBJS=$(AUTH_DIR)/UACAuth.o
14 20
 
... ...
@@ -23,7 +29,7 @@ EXTRA_LDFLAGS += -lresolv -levent -levent_pthreads
23 29
 
24 30
 .PHONY: all
25 31
 all: ../../Makefile.defs sip_stack libresample
26
-	-@$(MAKE) core_deps  && $(MAKE) deps    && \
32
+	-@$(MAKE) core_deps   && $(MAKE) sbc_deps  && $(MAKE) deps    && \
27 33
 	  $(MAKE) $(NAME) && \
28 34
 	./$(NAME)
29 35
 
... ...
@@ -51,6 +57,9 @@ deps: $(DEPS)
51 57
 .PHONY: core_deps
52 58
 core_deps: $(CORE_DEPS)
53 59
 
60
+.PHONY: sbc_deps
61
+sbc_deps: $(SBC_DEPS)
62
+
54 63
 AUTH_OBJS: $(AUTH_DIR)/UACAuth.cpp $(AUTH_DIR)/UACAuth.h
55 64
 	cd $(AUTH_DIR) ; $(MAKE) AUTH_OBJS
56 65
 
... ...
@@ -64,17 +73,19 @@ include ../../Makefile.defs
64 73
 %.d : %.cpp %.h ../../Makefile.defs
65 74
 	$(CXX) -MM $< $(CPPFLAGS) $(CXXFLAGS) > $@
66 75
 
76
+%.d : $(SBC_DIR)%.cpp $(SBC_DIR)%.h ../../Makefile.defs
77
+	$(CXX) -MM $< $(CPPFLAGS) $(CXXFLAGS) > $@
78
+
67 79
 %.d : ../%.cpp ../%.h ../../Makefile.defs
68 80
 	$(CXX) -MM $< $(CPPFLAGS) $(CXXFLAGS) > $@
69 81
 
70
-$(NAME): $(OBJS) $(CORE_OBJS) $(SIP_STACK) $(LIBRESAMPLE) ../../Makefile.defs
82
+$(NAME): $(OBJS) $(CORE_OBJS) $(SBC_OBJS) $(SIP_STACK) $(LIBRESAMPLE) ../../Makefile.defs
71 83
 	-@echo ""
72 84
 	-@echo "making $(NAME)"
73
-	$(LD) -o $(NAME) $(OBJS) $(CORE_OBJS) $(SIP_STACK) $(LIBRESAMPLE) $(LDFLAGS) $(EXTRA_LDFLAGS) $(AUTH_OBJS)
74
-
85
+	$(LD) -o $(NAME) $(OBJS) $(CORE_OBJS) $(SBC_OBJS) $(SIP_STACK) $(LIBRESAMPLE) $(LDFLAGS) $(EXTRA_LDFLAGS) $(AUTH_OBJS)
75 86
 
76 87
 ifeq '$(NAME)' '$(MAKECMDGOALS)'
77
-include $(DEPS) $(CORE_DEPS)
88
+include $(DEPS) $(CORE_DEPS) $(SBC_DEPS)
78 89
 endif
79 90
 
80 91
 
... ...
@@ -26,6 +26,7 @@ FCT_BGN() {
26 26
   FCTMF_SUITE_CALL(test_headers);
27 27
   FCTMF_SUITE_CALL(test_uriparser);
28 28
   FCTMF_SUITE_CALL(test_jsonarg);
29
+  FCTMF_SUITE_CALL(test_replaces);
29 30
 } FCT_END();
30 31
 
31 32
 
32 33
new file mode 100644
... ...
@@ -0,0 +1,103 @@
1
+#include "fct.h"
2
+
3
+#include "log.h"
4
+
5
+#include "AmSipHeaders.h"
6
+#include "AmSipMsg.h"
7
+#include "AmUtils.h"
8
+#include "AmUriParser.h"
9
+
10
+#include "../../apps/sbc/ReplacesMapper.h"
11
+#include "../../apps/sbc/SBCCallRegistry.h"
12
+
13
+
14
+FCTMF_SUITE_BGN(test_replaces) {
15
+
16
+    FCT_TEST_BGN(registry_simple) {
17
+      SBCCallRegistryEntry e = SBCCallRegistryEntry("callid2", "ltag2", "rtag2");
18
+      SBCCallRegistry::addCall("ltag", e);
19
+      fct_chk(SBCCallRegistry::lookupCall("ltag",e));
20
+      fct_chk(!SBCCallRegistry::lookupCall("ltag3",e));
21
+      SBCCallRegistry::removeCall("ltag");
22
+    } FCT_TEST_END();
23
+
24
+    FCT_TEST_BGN(replaces_fixup_invite) {
25
+      SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
26
+      SBCCallRegistry::addCall("Ct", e);
27
+      SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
28
+      SBCCallRegistry::addCall("C2f", e2);
29
+
30
+      AmSipRequest r;
31
+      r.hdrs="Replaces: C;from-tag=Cf;to-tag=Ct\r\n";
32
+      fixReplaces(r, true);
33
+      DBG("r.hdrs='%s'\n", r.hdrs.c_str());
34
+      fct_chk(r.hdrs=="Replaces: C2;from-tag=C2f;to-tag=C2t\r\n");
35
+
36
+      SBCCallRegistry::removeCall("Ct");
37
+      SBCCallRegistry::removeCall("C2f");
38
+    } FCT_TEST_END();
39
+
40
+    FCT_TEST_BGN(replaces_fixup_refer) {
41
+      SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
42
+      SBCCallRegistry::addCall("Ct", e);
43
+      SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
44
+      SBCCallRegistry::addCall("C2f", e2);
45
+
46
+      AmSipRequest r;
47
+      string orig_str = "Refer-To: \"Mr. Watson\" <sip:watson@bell-telephone.com?Replaces=C%3Bto-tag%3DCt%3Bfrom-tag%3DCf>;q=0.1";
48
+      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";
49
+
50
+      r.hdrs=orig_str+"\r\n";
51
+      fixReplaces(r, false);
52
+      DBG("r.hdrs='%s'\n", r.hdrs.c_str());
53
+      DBG("new  s='%s'\n", new_str.c_str());
54
+
55
+      fct_chk(r.hdrs==new_str);
56
+
57
+      SBCCallRegistry::removeCall("Ct");
58
+      SBCCallRegistry::removeCall("C2f");
59
+    } FCT_TEST_END();
60
+
61
+    FCT_TEST_BGN(replaces_fixup_refer2) {
62
+      SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
63
+      SBCCallRegistry::addCall("Ct", e);
64
+      SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
65
+      SBCCallRegistry::addCall("C2f", e2);
66
+
67
+      AmSipRequest r;
68
+      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";
69
+      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";
70
+
71
+      r.hdrs=orig_str;
72
+      fixReplaces(r, false);
73
+      DBG("r.hdrs='%s'\n", r.hdrs.c_str());
74
+      DBG("new  s='%s'\n", new_str.c_str());
75
+
76
+      fct_chk(r.hdrs==new_str);
77
+
78
+      SBCCallRegistry::removeCall("Ct");
79
+      SBCCallRegistry::removeCall("C2f");
80
+    } FCT_TEST_END();
81
+
82
+    FCT_TEST_BGN(replaces_fixup_refer3) {
83
+      SBCCallRegistryEntry e = SBCCallRegistryEntry("C2", "C2f", "C2t");
84
+      SBCCallRegistry::addCall("Ct", e);
85
+      SBCCallRegistryEntry e2 = SBCCallRegistryEntry("C", "Ct", "Cf");
86
+      SBCCallRegistry::addCall("C2f", e2);
87
+
88
+      AmSipRequest r;
89
+      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";
90
+      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";
91
+
92
+      r.hdrs=orig_str;
93
+      fixReplaces(r, false);
94
+      DBG("r.hdrs='%s'\n", r.hdrs.c_str());
95
+      DBG("new  s='%s'\n", new_str.c_str());
96
+
97
+      fct_chk(r.hdrs==new_str);
98
+
99
+      SBCCallRegistry::removeCall("Ct");
100
+      SBCCallRegistry::removeCall("C2f");
101
+    } FCT_TEST_END();
102
+
103
+} FCTMF_SUITE_END();
0 104
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+
... ...
@@ -124,5 +124,13 @@ FCTMF_SUITE_BGN(test_uriparser) {
124 124
       fct_chk(orig_str == a_str);
125 125
     } FCT_TEST_END();
126 126
 
127
+    FCT_TEST_BGN(uriparser_url_escape) {
128
+      string src = "Replaces: CSADFSD;from-tag=31241231abc;to-tag=235123";
129
+      string dst = "Replaces%3A%20CSADFSD%3Bfrom-tag%3D31241231abc%3Bto-tag%3D235123";
130
+      fct_chk ( URL_decode(dst)==src  );
131
+      fct_chk ( URL_encode(src)==dst  );
132
+      fct_chk ( URL_decode(URL_encode(src))==src  );
133
+
134
+    } FCT_TEST_END();
127 135
 
128 136
 } FCTMF_SUITE_END();
... ...
@@ -24,6 +24,7 @@ Features
24 24
  o reply code translation
25 25
  o SIP authentication
26 26
  o SIP Session Timers
27
+ o Fixing Call Transfers (Replaces in REFER target and INVITE)
27 28
  o call timer
28 29
  o prepaid accounting
29 30
  o CDR generation
... ...
@@ -590,6 +591,20 @@ Warning: Changing response codes, especially between different response
590 591
          code classes, can seriously mess up everything. Use with caution
591 592
          and only if you know what you are doing!
592 593
 
594
+Fixing Call Transfers (Replaces in REFER target and INVITE)
595
+-----------------------------------------------------------
596
+Using the profile options fix_replaces_inv and fix_replaces_ref Replaces
597
+can be fixed for call transfers going through the SBC.
598
+
599
+  fix_replaces_inv=yes
600
+  fix_replaces_ref=yes
601
+
602
+For situations where the call transfer is handled by the UAs (phone handles
603
+the REFER), the Replaces should be fixed in the INVITE message (fix_replaces_inv=yes).
604
+
605
+For situations where a PBX handles the call transfer (handles the REFER),
606
+the Replaces should be fixed in the REFER message (fix_replaces_ref=yes).
607
+
593 608
 Reliable 1xx (PRACK)
594 609
 --------------------
595 610