apps/sbc/SBCCallLeg.cpp
26a81fa5
 /*
baf99dc9
  * Copyright (C) 2010-2013 Stefan Sayer
26a81fa5
  * Copyright (C) 2012-2013 FRAFOS GmbH
  *
  * 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
  */
 
2cfe4c66
 #include "SBCCallLeg.h"
 
846ccbba
 #include "SBCCallControlAPI.h"
2cfe4c66
 
 #include "log.h"
 #include "AmUtils.h"
 #include "AmAudio.h"
 #include "AmPlugIn.h"
 #include "AmMediaProcessor.h"
 #include "AmConfigReader.h"
 #include "AmSessionContainer.h"
 #include "AmSipHeaders.h"
4735758f
 #include "SBCSimpleRelay.h"
 #include "RegisterDialog.h"
 #include "SubscriptionDialog.h"
2cfe4c66
 
7ed026c3
 #include "sip/pcap_logger.h"
6f1c3543
 #include "sip/sip_parser.h"
 #include "sip/sip_trans.h"
 
2cfe4c66
 #include "HeaderFilter.h"
 #include "ParamReplacer.h"
 #include "SDPFilter.h"
a5ba4c93
 #include "SBCEventLog.h"
baf99dc9
 #include "SBC.h"
a5ba4c93
 
2cfe4c66
 #include <algorithm>
 
 using namespace std;
 
0cb9ad89
 #define TRACE DBG
d8a37473
 
2cfe4c66
 // helper functions
 
4642da71
 static const SdpPayload *findPayload(const std::vector<SdpPayload>& payloads, const SdpPayload &payload, int transport)
2cfe4c66
 {
   string pname = payload.encoding_name;
   transform(pname.begin(), pname.end(), pname.begin(), ::tolower);
 
   for (vector<SdpPayload>::const_iterator p = payloads.begin(); p != payloads.end(); ++p) {
4642da71
     // fix for clients using non-standard names for static payload type (SPA504g: G729a)
     if (transport == TP_RTPAVP && payload.payload_type < 20) {
       if (payload.payload_type != p->payload_type) continue;
     }
     else {
       string s = p->encoding_name;
       transform(s.begin(), s.end(), s.begin(), ::tolower);
       if (s != pname) continue;
     }
 
2cfe4c66
     if (p->clock_rate != payload.clock_rate) continue;
     if ((p->encoding_param >= 0) && (payload.encoding_param >= 0) && 
         (p->encoding_param != payload.encoding_param)) continue;
     return &(*p);
   }
   return NULL;
 }
 
4642da71
 static bool containsPayload(const std::vector<SdpPayload>& payloads, const SdpPayload &payload, int transport)
2cfe4c66
 {
4642da71
   return findPayload(payloads, payload, transport) != NULL;
2cfe4c66
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////
 
 // map stream index and transcoder payload index (two dimensions) into one under
 // presumption that there will be less than 128 payloads for transcoding
 // (might be handy to remember mapping only for dynamic ones (96-127)
 #define MAP_INDEXES(stream_idx, payload_idx) ((stream_idx) * 128 + payload_idx)
 
 void PayloadIdMapping::map(int stream_index, int payload_index, int payload_id)
 {
   mapping[MAP_INDEXES(stream_index, payload_index)] = payload_id;
 }
 
 int PayloadIdMapping::get(int stream_index, int payload_index)
 {
   std::map<int, int>::iterator i = mapping.find(MAP_INDEXES(stream_index, payload_index));
   if (i != mapping.end()) return i->second;
   else return -1;
 }
 
 void PayloadIdMapping::reset()
 {
   mapping.clear();
 }
 
 #undef MAP_INDEXES
 
 ///////////////////////////////////////////////////////////////////////////////////////////
 
 // A leg constructor (from SBCDialog)
62cdc239
 SBCCallLeg::SBCCallLeg(const SBCCallProfile& call_profile, AmSipDialog* p_dlg,
 		       AmSipSubscription* p_subs)
   : CallLeg(p_dlg,p_subs),
9fce821e
     m_state(BB_Init),
2cfe4c66
     auth(NULL),
     call_profile(call_profile),
5fd073dc
     cc_timer_id(SBC_TIMER_ID_CALL_TIMERS_START),
ab5e06fb
     ext_cc_timer_id(SBC_TIMER_ID_CALL_TIMERS_END + 1),
ea9de070
     cc_started(false),
     logger(NULL)
2cfe4c66
 {
   set_sip_relay_only(false);
62fc8342
   dlg->setRel100State(Am100rel::REL100_IGNORED);
2cfe4c66
 
4e8f4ed9
   memset(&call_start_ts, 0, sizeof(struct timeval));
2cfe4c66
   memset(&call_connect_ts, 0, sizeof(struct timeval));
   memset(&call_end_ts, 0, sizeof(struct timeval));
f9701cec
 
   if(call_profile.rtprelay_bw_limit_rate > 0 &&
      call_profile.rtprelay_bw_limit_peak > 0) {
 
     RateLimit* limit = new RateLimit(call_profile.rtprelay_bw_limit_rate,
f9564977
 				     call_profile.rtprelay_bw_limit_peak,
 				     1000);
f9701cec
     rtp_relay_rate_limit.reset(limit);
   }
d90fd9ee
 
   subs->allowUnsolicitedNotify(call_profile.allow_subless_notify);
2cfe4c66
 }
 
 // B leg constructor (from SBCCalleeSession)
62cdc239
 SBCCallLeg::SBCCallLeg(SBCCallLeg* caller, AmSipDialog* p_dlg,
 		       AmSipSubscription* p_subs)
2cfe4c66
   : auth(NULL),
0cb9ad89
     call_profile(caller->getCallProfile()),
62cdc239
     CallLeg(caller,p_dlg,p_subs),
d2f3c249
     ext_cc_timer_id(SBC_TIMER_ID_CALL_TIMERS_END + 1),
ea9de070
     cc_started(false),
     logger(NULL)
2cfe4c66
 {
0cb9ad89
   // FIXME: do we want to inherit cc_vars from caller?
   // Can be pretty dangerous when caller stored pointer to object - we should
   // not probably operate on it! But on other hand it could be handy for
   // something, so just take care when using stored objects...
   // call_profile.cc_vars.clear();
0d003afc
 
62fc8342
   dlg->setRel100State(Am100rel::REL100_IGNORED);
0cb9ad89
 
4735758f
   // we need to apply it here instead of in applyBProfile because we have caller
   // here (FIXME: do it on better place and better way than accessing internals
   // of caller->dlg directly)
   if (call_profile.transparent_dlg_id && caller) {
db3a4483
     dlg->setCallid(caller->dlg->getCallid());
     dlg->setExtLocalTag(caller->dlg->getRemoteTag());
444cf6d6
     dlg->cseq = caller->dlg->r_cseq;
4735758f
   }
 
0bd4fcc2
   // copy RTP rate limit from caller leg
   if(caller->rtp_relay_rate_limit.get()) {
     rtp_relay_rate_limit.reset(new RateLimit(*caller->rtp_relay_rate_limit.get()));
f9701cec
   }
 
0cb9ad89
   // CC interfaces and variables should be already "evaluated" by A leg, we just
   // need to load the DI interfaces for us (later they will be initialized with
   // original INVITE so it must be done in A leg's thread!)
   if (!getCCInterfaces()) {
     throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
   }
 
baf99dc9
   if (!initCCExtModules(call_profile.cc_interfaces, cc_modules)) {
891a8e0e
     ERROR("initializing extended call control modules\n");
     throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
   }
ea9de070
 
   setLogger(caller->getLogger());
45860010
 
   subs->allowUnsolicitedNotify(call_profile.allow_subless_notify);
9cb13c6a
 }
 
62cdc239
 SBCCallLeg::SBCCallLeg(AmSipDialog* p_dlg, AmSipSubscription* p_subs)
   : CallLeg(p_dlg,p_subs),
c50294f1
     m_state(BB_Init),
     auth(NULL),
     cc_timer_id(SBC_TIMER_ID_CALL_TIMERS_START),
887d0b10
     cc_started(false),
     logger(NULL)
c50294f1
 {
4e8f4ed9
   memset(&call_start_ts, 0, sizeof(struct timeval));
   memset(&call_connect_ts, 0, sizeof(struct timeval));
   memset(&call_end_ts, 0, sizeof(struct timeval));
c50294f1
 }
 
9cb13c6a
 void SBCCallLeg::onStart()
 {
   // this should be the first thing called in session's thread
   CallLeg::onStart();
   if (!a_leg) applyBProfile(); // A leg needs to evaluate profile first
acb9bc22
   else if (!getOtherId().empty()) {
     // A leg but we already have a peer, what means that this call leg was
     // created as an A leg for already existing B leg (for example call
     // transfer)
     // we need to apply a profile, we use B profile and understand it as an
     // "outbound" profile though we are in A leg
     applyBProfile();
   }
9cb13c6a
 }
 
 void SBCCallLeg::applyAProfile()
 {
   // apply A leg configuration (but most of the configuration is applied in
   // SBCFactory::onInvite)
 
   if (call_profile.rtprelay_enabled || call_profile.transcoder.isActive()) {
     DBG("Enabling RTP relay mode for SBC call\n");
 
eca4a1df
     setRtpRelayForceSymmetricRtp(call_profile.aleg_force_symmetric_rtp_value);
     DBG("%s\n",getRtpRelayForceSymmetricRtp() ?
9f20a80e
 	"forcing symmetric RTP (passive mode)":
 	"disabled symmetric RTP (normal mode)");
9cb13c6a
 
     if (call_profile.aleg_rtprelay_interface_value >= 0) {
bce5fa74
       setRtpInterface(call_profile.aleg_rtprelay_interface_value);
9cb13c6a
       DBG("using RTP interface %i for A leg\n", rtp_interface);
     }
 
     setRtpRelayTransparentSeqno(call_profile.rtprelay_transparent_seqno);
     setRtpRelayTransparentSSRC(call_profile.rtprelay_transparent_ssrc);
a99c7a2b
     setEnableDtmfRtpFiltering(call_profile.rtprelay_dtmf_filtering);
     setEnableDtmfRtpDetection(call_profile.rtprelay_dtmf_detection);
9cb13c6a
 
     if(call_profile.transcoder.isActive()) {
4735758f
       setRtpRelayMode(RTP_Transcoding);
9cb13c6a
       switch(call_profile.transcoder.dtmf_mode) {
       case SBCCallProfile::TranscoderSettings::DTMFAlways:
         enable_dtmf_transcoding = true; break;
       case SBCCallProfile::TranscoderSettings::DTMFNever:
         enable_dtmf_transcoding = false; break;
       case SBCCallProfile::TranscoderSettings::DTMFLowFiCodecs:
         enable_dtmf_transcoding = false;
         lowfi_payloads = call_profile.transcoder.lowfi_codecs;
         break;
       };
     }
4735758f
     else {
       setRtpRelayMode(RTP_Relay);
     }
f9701cec
 
     // copy stats counters
     rtp_pegs = call_profile.aleg_rtp_counters;
9cb13c6a
   }
60195bba
 
   if(!call_profile.dlg_contact_params.empty())
     dlg->setContactParams(call_profile.dlg_contact_params);
9cb13c6a
 }
 
4735758f
 int SBCCallLeg::applySSTCfg(AmConfigReader& sst_cfg, 
 			   const AmSipRequest* p_req)
9cb13c6a
 {
4735758f
   DBG("Enabling SIP Session Timers\n");  
51b27fb9
   if (NULL == SBCFactory::instance()->session_timer_fact) {
4735758f
     ERROR("session_timer module not loaded - "
 	  "unable to create call with SST\n");
     return -1;
   }
     
51b27fb9
   if (p_req && !SBCFactory::instance()->session_timer_fact->
       onInvite(*p_req, sst_cfg)) {
4735758f
     return -1;
   }
 
51b27fb9
   AmSessionEventHandler* h = SBCFactory::instance()->session_timer_fact->
     getHandler(this);
 
4735758f
   if (!h) {
     ERROR("could not get a session timer event handler\n");
     return -1;
2cfe4c66
   }
 
4735758f
   if (h->configure(sst_cfg)) {
     ERROR("Could not configure the session timer: "
 	  "disabling session timers.\n");
     delete h;
   }
   else {
     addHandler(h);
     // hack: repeat calling the handler again to start timers because
     // it was called before SST was applied
     if(p_req) h->onSipRequest(*p_req);
   }
 
   return 0;
 }
 
 void SBCCallLeg::applyBProfile()
 {
   // TODO: fix this!!! (see d85ed5c7e6b8d4c24e7e5b61c732c2e1ddd31784)
   // if (!call_profile.contact.empty()) {
62fc8342
   //   dlg->contact_uri = SIP_HDR_COLSP(SIP_HDR_CONTACT) + call_profile.contact + CRLF;
4735758f
   // }
 
2cfe4c66
   if (call_profile.auth_enabled) {
     // adding auth handler
9cb13c6a
     AmSessionEventHandlerFactory* uac_auth_f =
2cfe4c66
       AmPlugIn::instance()->getFactory4Seh("uac_auth");
     if (NULL == uac_auth_f)  {
       INFO("uac_auth module not loaded. uac auth NOT enabled.\n");
     } else {
       AmSessionEventHandler* h = uac_auth_f->getHandler(this);
9cb13c6a
 
       // we cannot use the generic AmSessionEventHandler hooks,
2cfe4c66
       // because the hooks don't work in AmB2BSession
       setAuthHandler(h);
       DBG("uac auth enabled for callee session.\n");
     }
   }
 
   if (call_profile.sst_enabled_value) {
4735758f
     if(applySSTCfg(call_profile.sst_b_cfg,NULL) < 0) {
        throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
2cfe4c66
     }
   }
 
   if (!call_profile.outbound_proxy.empty()) {
62fc8342
     dlg->outbound_proxy = call_profile.outbound_proxy;
     dlg->force_outbound_proxy = call_profile.force_outbound_proxy;
2cfe4c66
   }
9cb13c6a
 
2cfe4c66
   if (!call_profile.next_hop.empty()) {
b5d21df8
     DBG("set next hop to '%s' (1st_req=%s,fixed=%s)\n",
 	call_profile.next_hop.c_str(), call_profile.next_hop_1st_req?"true":"false",
 	call_profile.next_hop_fixed?"true":"false");
db3a4483
     dlg->setNextHop(call_profile.next_hop);
     dlg->setNextHop1stReq(call_profile.next_hop_1st_req);
b5d21df8
     dlg->setNextHopFixed(call_profile.next_hop_fixed);
2cfe4c66
   }
 
7f3ab30a
   DBG("patch_ruri_next_hop = %i",call_profile.patch_ruri_next_hop);
   dlg->setPatchRURINextHop(call_profile.patch_ruri_next_hop);
 
2cfe4c66
   // was read from caller but reading directly from profile now
   if (call_profile.outbound_interface_value >= 0)
c9fbdb34
     dlg->setOutboundInterface(call_profile.outbound_interface_value);
2cfe4c66
 
   // was read from caller but reading directly from profile now
   if (call_profile.rtprelay_enabled || call_profile.transcoder.isActive()) {
fc50f80a
 
2cfe4c66
     if (call_profile.rtprelay_interface_value >= 0)
bce5fa74
       setRtpInterface(call_profile.rtprelay_interface_value);
fc50f80a
 
eca4a1df
     setRtpRelayForceSymmetricRtp(call_profile.force_symmetric_rtp_value);
     DBG("%s\n",getRtpRelayForceSymmetricRtp() ?
9f20a80e
 	"forcing symmetric RTP (passive mode)":
 	"disabled symmetric RTP (normal mode)");
2cfe4c66
 
f9701cec
     setRtpRelayTransparentSeqno(call_profile.rtprelay_transparent_seqno);
     setRtpRelayTransparentSSRC(call_profile.rtprelay_transparent_ssrc);
fb477196
     setEnableDtmfRtpFiltering(call_profile.rtprelay_dtmf_filtering);
     setEnableDtmfRtpDetection(call_profile.rtprelay_dtmf_detection);
f9701cec
 
     // copy stats counters
     rtp_pegs = call_profile.bleg_rtp_counters;
   }
2cfe4c66
 
   // was read from caller but reading directly from profile now
db3a4483
   if (!call_profile.callid.empty()) 
     dlg->setCallid(call_profile.callid);
60195bba
 
   if(!call_profile.bleg_dlg_contact_params.empty())
     dlg->setContactParams(call_profile.bleg_dlg_contact_params);
2cfe4c66
 }
 
 int SBCCallLeg::relayEvent(AmEvent* ev)
 {
b7e8dae9
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
     int res = (*i)->relayEvent(this, ev);
     if (res > 0) return 0;
     if (res < 0) return res;
   }
 
2cfe4c66
     switch (ev->event_id) {
       case B2BSipRequest:
         {
           B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
4735758f
           assert(req_ev);
2cfe4c66
 
4735758f
           if (call_profile.headerfilter.size()) {
2cfe4c66
             //B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
             // header filter
4735758f
             inplaceHeaderFilter(req_ev->req.hdrs, call_profile.headerfilter);
2cfe4c66
           }
 
           DBG("filtering body for request '%s' (c/t '%s')\n",
               req_ev->req.method.c_str(), req_ev->req.body.getCTStr().c_str());
           // todo: handle filtering errors
 
d8a37473
           int res = filterSdp(req_ev->req.body, req_ev->req.method);
2eb971c5
           if (res < 0) {
             delete ev; // failed relayEvent should destroy the event
             return res;
           }
7c5de74b
 
956fc951
 	  if((a_leg && call_profile.keep_vias)
 	     || (!a_leg && call_profile.bleg_keep_vias)) {
7c5de74b
 	    req_ev->req.hdrs = req_ev->req.vias + req_ev->req.hdrs;
 	  }
2cfe4c66
         }
         break;
 
       case B2BSipReply:
         {
           B2BSipReplyEvent* reply_ev = dynamic_cast<B2BSipReplyEvent*>(ev);
4735758f
           assert(reply_ev);
2cfe4c66
 
4735758f
           if(call_profile.transparent_dlg_id &&
db3a4483
 	     (reply_ev->reply.from_tag == dlg->getExtLocalTag()))
             reply_ev->reply.from_tag = dlg->getLocalTag();
4735758f
 
           if (call_profile.headerfilter.size() ||
2cfe4c66
               call_profile.reply_translations.size()) {
             // header filter
4735758f
             if (call_profile.headerfilter.size()) {
               inplaceHeaderFilter(reply_ev->reply.hdrs, call_profile.headerfilter);
2cfe4c66
             }
 
             // reply translations
             map<unsigned int, pair<unsigned int, string> >::iterator it =
               call_profile.reply_translations.find(reply_ev->reply.code);
4735758f
 
2cfe4c66
             if (it != call_profile.reply_translations.end()) {
               DBG("translating reply %u %s => %u %s\n",
                   reply_ev->reply.code, reply_ev->reply.reason.c_str(),
                   it->second.first, it->second.second.c_str());
               reply_ev->reply.code = it->second.first;
               reply_ev->reply.reason = it->second.second;
             }
           }
 
           DBG("filtering body for reply '%s' (c/t '%s')\n",
               reply_ev->trans_method.c_str(), reply_ev->reply.body.getCTStr().c_str());
 
d8a37473
           filterSdp(reply_ev->reply.body, reply_ev->reply.cseq_method);
2cfe4c66
         }
 
         break;
     }
 
   return CallLeg::relayEvent(ev);
 }
 
 SBCCallLeg::~SBCCallLeg()
 {
   if (auth)
     delete auth;
ea9de070
   if (logger) dec_ref(logger);
d8200c89
 }
8e79bbbb
 
d8200c89
 void SBCCallLeg::onBeforeDestroy()
 {
8e79bbbb
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
     (*i)->onDestroyLeg(this);
   }
2cfe4c66
 }
 
 UACAuthCred* SBCCallLeg::getCredentials() {
   if (a_leg) return &call_profile.auth_aleg_credentials;
   else return &call_profile.auth_credentials;
 }
 
 void SBCCallLeg::onSipRequest(const AmSipRequest& req) {
   // AmB2BSession does not call AmSession::onSipRequest for 
   // forwarded requests - so lets call event handlers here
   // todo: this is a hack, replace this by calling proper session 
   // event handler in AmB2BSession
4735758f
   bool fwd = sip_relay_only && (req.method != SIP_METH_CANCEL);
2cfe4c66
   if (fwd) {
4735758f
     CALL_EVENT_H(onSipRequest,req);
2cfe4c66
   }
 
4735758f
   if (fwd && call_profile.messagefilter.size()) {
     for (vector<FilterEntry>::iterator it=
 	   call_profile.messagefilter.begin(); 
 	 it != call_profile.messagefilter.end(); it++) {
 
       if (isActiveFilter(it->filter_type)) {
 	bool is_filtered = (it->filter_type == Whitelist) ^ 
 	  (it->filter_list.find(req.method) != it->filter_list.end());
 	if (is_filtered) {
 	  DBG("replying 405 to filtered message '%s'\n", req.method.c_str());
62fc8342
 	  dlg->reply(req, 405, "Method Not Allowed", NULL, "", SIP_FLAGS_VERBATIM);
4735758f
 	  return;
 	}
       }
2cfe4c66
     }
   }
 
8e79bbbb
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
     if ((*i)->onInDialogRequest(this, req) == StopProcessing) return;
   }
 
f643a6d5
   if (fwd && req.method == SIP_METH_INVITE) {
     DBG("replying 100 Trying to INVITE to be fwd'ed\n");
     dlg->reply(req, 100, SIP_REPLY_TRYING);
   }
 
2cfe4c66
   CallLeg::onSipRequest(req);
 }
 
b0fb142d
 void SBCCallLeg::setOtherId(const AmSipReply& reply)
 {
   DBG("setting other_id to '%s'",reply.from_tag.c_str());
6c634b3c
   setOtherId(reply.from_tag);
b0fb142d
   if(call_profile.transparent_dlg_id && !reply.to_tag.empty()) {
db3a4483
     dlg->setExtLocalTag(reply.to_tag);
b0fb142d
   }
 }
2cfe4c66
 
b58c566e
 void SBCCallLeg::onInitialReply(B2BSipReplyEvent *e)
 {
6e0c7d60
   if (call_profile.transparent_dlg_id && !e->reply.to_tag.empty()
       && dlg->getStatus() != AmBasicSipDialog::Connected) {
b58c566e
     dlg->setExtLocalTag(e->reply.to_tag);
   }
   CallLeg::onInitialReply(e);
 }
 
4735758f
 void SBCCallLeg::onSipReply(const AmSipRequest& req, const AmSipReply& reply,
 			   AmBasicSipDialog::Status old_dlg_status)
2cfe4c66
 {
   TransMap::iterator t = relayed_req.find(reply.cseq);
   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.body.getCTStr().c_str());
   if (fwd) {
6b4b3cc7
     CALL_EVENT_H(onSipReply, req, reply, old_dlg_status);
2cfe4c66
   }
 
8e79bbbb
   if (NULL != auth) {
     // only for SIP authenticated
62fc8342
     unsigned int cseq_before = dlg->cseq;
6b4b3cc7
     if (auth->onSipReply(req, reply, old_dlg_status)) {
62fc8342
       if (cseq_before != dlg->cseq) {
8e79bbbb
         DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; "
             "updating relayed_req map\n", reply.cseq, cseq_before);
         updateUACTransCSeq(reply.cseq, cseq_before);
7ace3e38
 
 	// don't relay to other leg, process in AmSession
 	AmSession::onSipReply(req, reply, old_dlg_status);
 	// skip presenting reply to ext_cc modules, too
 	return;
8e79bbbb
       }
     }
2cfe4c66
   }
 
8e79bbbb
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
7ace3e38
     if ((*i)->onInDialogReply(this, reply) == StopProcessing) {
       return;
     }
2cfe4c66
   }
8e79bbbb
 
4735758f
   CallLeg::onSipReply(req, reply, old_dlg_status);
2cfe4c66
 }
 
4735758f
 void SBCCallLeg::onSendRequest(AmSipRequest& req, int &flags) {
08442caa
 
   if(a_leg) {
     if (!call_profile.aleg_append_headers_req.empty()) {
       DBG("appending '%s' to outbound request (A leg)\n",
 	  call_profile.aleg_append_headers_req.c_str());
       req.hdrs+=call_profile.aleg_append_headers_req;
     }
   }
   else {
     if (!call_profile.append_headers_req.empty()) {
       DBG("appending '%s' to outbound request (B leg)\n", 
 	  call_profile.append_headers_req.c_str());
       req.hdrs+=call_profile.append_headers_req;
     }
   }
 
2cfe4c66
   if (NULL != auth) {
     DBG("auth->onSendRequest cseq = %d\n", req.cseq);
     auth->onSendRequest(req, flags);
   }
 
   CallLeg::onSendRequest(req, flags);
 }
 
a5ba4c93
 void SBCCallLeg::onRemoteDisappeared(const AmSipReply& reply)
 {
   CallLeg::onRemoteDisappeared(reply);
   if(a_leg)
41acf8dc
     SBCEventLog::instance()->logCallEnd(dlg,"reply",&call_connect_ts);
a5ba4c93
 }
 
 void SBCCallLeg::onBye(const AmSipRequest& req)
 {
   CallLeg::onBye(req);
   if(a_leg)
41acf8dc
     SBCEventLog::instance()->logCallEnd(req,getLocalTag(),"bye",&call_connect_ts);
a5ba4c93
 }
 
3352e36c
 void SBCCallLeg::onOtherBye(const AmSipRequest& req)
 {
   CallLeg::onOtherBye(req);
   if(a_leg)
41acf8dc
     SBCEventLog::instance()->logCallEnd(req,getLocalTag(),"bye",&call_connect_ts);
3352e36c
 }
 
2cfe4c66
 void SBCCallLeg::onDtmf(int event, int duration)
 {
a99c7a2b
   DBG("received DTMF on %c-leg (%i;%i)\n", a_leg ? 'A': 'B', event, duration);
 
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
4bbfb7ad
     if ((*i)->onDtmf(this, event, duration)  == StopProcessing)
       return;
a99c7a2b
   }
 
178c859e
   AmB2BMedia *ms = getMediaSession();
   if(ms) {
a99c7a2b
     DBG("sending DTMF (%i;%i)\n", event, duration);
178c859e
     ms->sendDtmf(!a_leg,event,duration);
2cfe4c66
   }
 }
 
39697707
 void SBCCallLeg::updateLocalSdp(AmSdp &sdp)
2cfe4c66
 {
d09715cf
   // anonymize SDP if configured to do so (we need to have our local media IP,
   // not the media IP of our peer leg there)
   if (call_profile.anonymize_sdp) normalizeSDP(sdp, call_profile.anonymize_sdp, advertisedIP());
 
2cfe4c66
   // remember transcodable payload IDs
70b8d101
   if (call_profile.transcoder.isActive()) savePayloadIDs(sdp);
39697707
   CallLeg::updateLocalSdp(sdp);
2cfe4c66
 }
 
 void SBCCallLeg::onControlCmd(string& cmd, AmArg& params) {
   if (cmd == "teardown") {
     if (a_leg) {
       // was for caller:
       DBG("teardown requested from control cmd\n");
6bbb7c8c
       stopCall("ctrl-cmd");
41acf8dc
       SBCEventLog::instance()->logCallEnd(dlg,"ctrl-cmd",&call_connect_ts);
2cfe4c66
       // FIXME: don't we want to relay the controll event as well?
     }
     else {
       // was for callee:
       DBG("relaying teardown control cmd to A leg\n");
       relayEvent(new SBCControlEvent(cmd, params));
       // FIXME: don't we want to stopCall as well?
     }
     return;
   }
   DBG("ignoring unknown control cmd : '%s'\n", cmd.c_str());
 }
 
 
 void SBCCallLeg::process(AmEvent* ev) {
8e79bbbb
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
     if ((*i)->onEvent(this, ev) == StopProcessing) return;
   }
 
2cfe4c66
   if (a_leg) {
     // was for caller (SBCDialog):
     AmPluginEvent* plugin_event = dynamic_cast<AmPluginEvent*>(ev);
     if(plugin_event && plugin_event->name == "timer_timeout") {
       int timer_id = plugin_event->data.get(0).asInt();
       if (timer_id >= SBC_TIMER_ID_CALL_TIMERS_START &&
           timer_id <= SBC_TIMER_ID_CALL_TIMERS_END) {
         DBG("timer %d timeout, stopping call\n", timer_id);
6bbb7c8c
         stopCall("timer");
41acf8dc
 	SBCEventLog::instance()->logCallEnd(dlg,"timeout",&call_connect_ts);
2cfe4c66
         ev->processed = true;
       }
     }
 
     SBCCallTimerEvent* ct_event;
     if (ev->event_id == SBCCallTimerEvent_ID &&
         (ct_event = dynamic_cast<SBCCallTimerEvent*>(ev)) != NULL) {
       switch (m_state) {
         case BB_Connected: 
           switch (ct_event->timer_action) {
             case SBCCallTimerEvent::Remove:
               DBG("removing timer %d on call timer request\n", ct_event->timer_id);
               removeTimer(ct_event->timer_id); return;
             case SBCCallTimerEvent::Set:
               DBG("setting timer %d to %f on call timer request\n",
                   ct_event->timer_id, ct_event->timeout);
               setTimer(ct_event->timer_id, ct_event->timeout); return;
             case SBCCallTimerEvent::Reset:
               DBG("resetting timer %d to %f on call timer request\n",
                   ct_event->timer_id, ct_event->timeout);
               removeTimer(ct_event->timer_id);
               setTimer(ct_event->timer_id, ct_event->timeout);
               return;
             default: ERROR("unknown timer_action in sbc call timer event\n"); return;
           }
 
         case BB_Init:
         case BB_Dialing:
 
           switch (ct_event->timer_action) {
             case SBCCallTimerEvent::Remove: 
               clearCallTimer(ct_event->timer_id); 
               return;
 
             case SBCCallTimerEvent::Set:
             case SBCCallTimerEvent::Reset:
               saveCallTimer(ct_event->timer_id, ct_event->timeout); 
               return;
 
             default: ERROR("unknown timer_action in sbc call timer event\n"); return;
           }
           break;
 
         default: break;
       }
     }
   }
 
   SBCControlEvent* ctl_event;
   if (ev->event_id == SBCControlEvent_ID &&
       (ctl_event = dynamic_cast<SBCControlEvent*>(ev)) != NULL) {
     onControlCmd(ctl_event->cmd, ctl_event->params);
     return;
   }
 
   CallLeg::process(ev);
 }
 
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // was for caller only (SBCDialog)
 // FIXME: move the stuff related to CC interface outside of this class?
 
 
 #define REPLACE_VALS req, app_param, ruri_parser, from_parser, to_parser
 
 void SBCCallLeg::onInvite(const AmSipRequest& req)
 {
   DBG("processing initial INVITE %s\n", req.r_uri.c_str());
 
0dae3311
   ParamReplacerCtx ctx(&call_profile);
4735758f
   ctx.app_param = getHeader(req.hdrs, PARAM_HDR, true);
2cfe4c66
 
4735758f
   // process call control
2cfe4c66
   if (call_profile.cc_interfaces.size()) {
     gettimeofday(&call_start_ts, NULL);
 
4735758f
     call_profile.eval_cc_list(ctx,req);
2cfe4c66
 
     // fix up module names
     for (CCInterfaceListIteratorT cc_it=call_profile.cc_interfaces.begin();
 	 cc_it != call_profile.cc_interfaces.end(); cc_it++) {
4735758f
 
2cfe4c66
       cc_it->cc_module =
4735758f
 	ctx.replaceParameters(cc_it->cc_module, "cc_module", req);
2cfe4c66
     }
 
     if (!getCCInterfaces()) {
       throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
     }
 
     // fix up variables
4735758f
     call_profile.replace_cc_values(ctx,req,NULL);
2cfe4c66
 
     if (!CCStart(req)) {
       setStopped();
39d24ba2
       oodHandlingTerminated(req, cc_modules, call_profile);
2cfe4c66
       return;
     }
   }
 
4735758f
   call_profile.sst_aleg_enabled = 
     ctx.replaceParameters(call_profile.sst_aleg_enabled,
 			  "enable_aleg_session_timer", req);
 
   call_profile.sst_enabled = ctx.replaceParameters(call_profile.sst_enabled, 
 						   "enable_session_timer", req);
 
   if ((call_profile.sst_aleg_enabled == "yes") ||
       (call_profile.sst_enabled == "yes")) {
 
     call_profile.eval_sst_config(ctx,req,call_profile.sst_a_cfg);
     if(applySSTCfg(call_profile.sst_a_cfg,&req) < 0) {
       throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
     }
   }
 
62fc8342
   if(dlg->reply(req, 100, "Connecting") != 0) {
2cfe4c66
     throw AmSession::Exception(500,"Failed to reply 100");
   }
 
4735758f
   if (!call_profile.evaluate(ctx, req)) {
2cfe4c66
     ERROR("call profile evaluation failed\n");
     throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);
   }
 
baf99dc9
   if (!initCCExtModules(call_profile.cc_interfaces, cc_modules)) {
891a8e0e
     ERROR("initializing extended call control modules\n");
     throw AmSession::Exception(500, SIP_REPLY_SERVER_INTERNAL_ERROR);    
   }
0cb9ad89
 
9cb13c6a
   string ruri, to, from;
4735758f
   AmSipRequest uac_req(req);
   AmUriParser uac_ruri;
   uac_ruri.uri = uac_req.r_uri;
   if(!uac_ruri.parse_uri()) {
     DBG("Error parsing R-URI '%s'\n",uac_ruri.uri.c_str());
     throw AmSession::Exception(400,"Failed to parse R-URI");
   }
 
7f0912e2
   if(call_profile.contact_hiding) { 
     if(RegisterDialog::decodeUsername(req.user,uac_ruri)) {
       uac_req.r_uri = uac_ruri.uri_str();
     }
   }
   else if(call_profile.reg_caching) {
     // REG-Cache lookup
7dff600f
     uac_req.r_uri = call_profile.retarget(req.user);
4735758f
   }
 
   ruri = call_profile.ruri.empty() ? uac_req.r_uri : call_profile.ruri;
2cfe4c66
   if(!call_profile.ruri_host.empty()){
4735758f
     ctx.ruri_parser.uri = ruri;
     if(!ctx.ruri_parser.parse_uri()) {
2cfe4c66
       WARN("Error parsing R-URI '%s'\n", ruri.c_str());
     }
     else {
4735758f
       ctx.ruri_parser.uri_port.clear();
       ctx.ruri_parser.uri_host = call_profile.ruri_host;
       ruri = ctx.ruri_parser.uri_str();
2cfe4c66
     }
   }
   from = call_profile.from.empty() ? req.from : call_profile.from;
   to = call_profile.to.empty() ? req.to : call_profile.to;
 
9cb13c6a
   applyAProfile();
a7e799d5
   call_profile.apply_a_routing(ctx,req,*dlg);
2cfe4c66
 
   m_state = BB_Dialing;
 
   // prepare request to relay to the B leg(s)
 
   AmSipRequest invite_req(req);
   est_invite_cseq = req.cseq;
 
   removeHeader(invite_req.hdrs,PARAM_HDR);
   removeHeader(invite_req.hdrs,"P-App-Name");
 
   if (call_profile.sst_enabled_value) {
     removeHeader(invite_req.hdrs,SIP_HDR_SESSION_EXPIRES);
     removeHeader(invite_req.hdrs,SIP_HDR_MIN_SE);
   }
 
4735758f
   inplaceHeaderFilter(invite_req.hdrs, call_profile.headerfilter);
2cfe4c66
 
   if (call_profile.append_headers.size() > 2) {
     string append_headers = call_profile.append_headers;
     assertEndCRLF(append_headers);
     invite_req.hdrs+=append_headers;
   }
   
d8a37473
   int res = filterSdp(invite_req.body, invite_req.method);
2cfe4c66
   if (res < 0) {
     // FIXME: quick hack, throw the exception from the filtering function for
     // requests
     throw AmSession::Exception(488, SIP_REPLY_NOT_ACCEPTABLE_HERE);
   }
 
 #undef REPLACE_VALS
 
   DBG("SBC: connecting to '%s'\n",ruri.c_str());
   DBG("     From:  '%s'\n",from.c_str());
   DBG("     To:  '%s'\n",to.c_str());
 
   // we evaluated the settings, now we can initialize internals (like RTP relay)
   // we have to use original request (not the altered one) because for example
   // codecs filtered out might be used in direction to caller
0d003afc
   CallLeg::onInvite(req);
 
956fc951
   if(a_leg && call_profile.keep_vias)
7c5de74b
     invite_req.hdrs = invite_req.vias + invite_req.hdrs;
 
7db20ac7
   subs->allowUnsolicitedNotify(call_profile.allow_subless_notify);
 
0d003afc
   // call extend call controls
   InitialInviteHandlerParams params(to, ruri, from, &req, &invite_req);
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
8e79bbbb
     (*i)->onInitialInvite(this, params);
baf99dc9
     // initialize possibily added modules
     initPendingCCExtModules();
0d003afc
   }
2cfe4c66
 
b61ff363
   if (getCallStatus() == Disconnected) {
0d003afc
     // no CC module connected a callee yet
0cb9ad89
     connectCallee(to, ruri, from, req, invite_req); // connect to the B leg(s) using modified request
0d003afc
   }
2cfe4c66
 }
 
9fce821e
 void SBCCallLeg::connectCallee(const string& remote_party, 
 			       const string& remote_uri,
 			       const string &from, 
 			       const AmSipRequest &original_invite, 
 			       const AmSipRequest &invite)
2cfe4c66
 {
   // FIXME: no fork for now
 
9fce821e
   SBCCallLeg* callee_session = SBCFactory::instance()->
     getCallLegCreator()->create(this);
 
2cfe4c66
   callee_session->setLocalParty(from, from);
   callee_session->setRemoteParty(remote_party, remote_uri);
 
   DBG("Created B2BUA callee leg, From: %s\n", from.c_str());
 
   // FIXME: inconsistent with other filtering stuff - here goes the INVITE
   // already filtered so need not to be catched (can not) in relayEvent because
   // it is sent other way
   addCallee(callee_session, invite);
   
   // we could start in SIP relay mode from the beginning if only one B leg, but
   // serial fork might mess it
   // set_sip_relay_only(true);
 }
 
 bool SBCCallLeg::getCCInterfaces() {
4735758f
   return ::getCCInterfaces(call_profile.cc_interfaces, cc_modules);
2cfe4c66
 }
 
 void SBCCallLeg::onCallConnected(const AmSipReply& reply) {
5e842e47
   if (a_leg) { // FIXME: really?
     m_state = BB_Connected;
2cfe4c66
 
5e842e47
     if (!startCallTimers())
       return;
2cfe4c66
 
5e842e47
     if (call_profile.cc_interfaces.size()) {
       gettimeofday(&call_connect_ts, NULL);
     }
2cfe4c66
 
a5ba4c93
     logCallStart(reply);
5e842e47
     CCConnect(reply);
   }
2cfe4c66
 }
 
5fd073dc
 void SBCCallLeg::onStop() {
2cfe4c66
   if (call_profile.cc_interfaces.size()) {
     gettimeofday(&call_end_ts, NULL);
   }
 
581f6b81
   if (a_leg && m_state == BB_Connected) { // m_state might be valid for A leg only
2cfe4c66
     stopCallTimers();
   }
 
   m_state = BB_Teardown;
 
5fd073dc
   // call only if really started (on CCStart failure CCEnd will be called
   // explicitly)
   // Note that role may change, so testing for a_leg need not to be correct.
   if (cc_started) CCEnd();
2cfe4c66
 }
 
 void SBCCallLeg::saveCallTimer(int timer, double timeout) {
   call_timers[timer] = timeout;
 }
 
 void SBCCallLeg::clearCallTimer(int timer) {
   call_timers.erase(timer);
 }
 
 void SBCCallLeg::clearCallTimers() {
   call_timers.clear();
 }
 
 /** @return whether successful */
 bool SBCCallLeg::startCallTimers() {
   for (map<int, double>::iterator it=
 	 call_timers.begin(); it != call_timers.end(); it++) {
     DBG("SBC: starting call timer %i of %f seconds\n", it->first, it->second);
     setTimer(it->first, it->second);
   }
 
   return true;
 }
 
 void SBCCallLeg::stopCallTimers() {
   for (map<int, double>::iterator it=
 	 call_timers.begin(); it != call_timers.end(); it++) {
     DBG("SBC: removing call timer %i\n", it->first);
     removeTimer(it->first);
   }
 }
 
 bool SBCCallLeg::CCStart(const AmSipRequest& req) {
9867e1c1
   if (!a_leg) return true; // preserve original behavior of the CC interface
 
2cfe4c66
   vector<AmDynInvoke*>::iterator cc_mod=cc_modules.begin();
 
   for (CCInterfaceListIteratorT cc_it=call_profile.cc_interfaces.begin();
        cc_it != call_profile.cc_interfaces.end(); cc_it++) {
     CCInterface& cc_if = *cc_it;
 
     AmArg di_args,ret;
     di_args.push(cc_if.cc_name);
     di_args.push(getLocalTag());
     di_args.push((AmObject*)&call_profile);
4735758f
     di_args.push((AmObject*)&req); // INVITE request
2cfe4c66
     di_args.push(AmArg());
     di_args.back().push((int)call_start_ts.tv_sec);
     di_args.back().push((int)call_start_ts.tv_usec);
     for (int i=0;i<4;i++)
       di_args.back().push((int)0);
 
     di_args.push(AmArg());
     AmArg& vals = di_args.back();
     vals.assertStruct();
     for (map<string, string>::iterator it = cc_if.cc_values.begin();
 	 it != cc_if.cc_values.end(); it++) {
       vals[it->first] = it->second;
     }
 
     di_args.push(cc_timer_id); // current timer ID
 
ef4f7164
     bool exception_occured = false;
2cfe4c66
     try {
       (*cc_mod)->invoke("start", di_args, ret);
     } catch (const AmArg::OutOfBoundsException& e) {
       ERROR("OutOfBoundsException executing call control interface start "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
ef4f7164
       exception_occured = true;
2cfe4c66
     } catch (const AmArg::TypeMismatchException& e) {
       ERROR("TypeMismatchException executing call control interface start "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
ef4f7164
       exception_occured = true;
     }
a5ba4c93
 
ef4f7164
     if(exception_occured) {
       SBCEventLog::instance()->
c2c6a82a
 	logCallStart(req, getLocalTag(), dlg->getRemoteUA(), "",
 		     500, SIP_REPLY_SERVER_INTERNAL_ERROR);
ef4f7164
       AmBasicSipDialog::reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR);
2cfe4c66
 
       // call 'end' of call control modules up to here
       call_end_ts.tv_sec = call_start_ts.tv_sec;
       call_end_ts.tv_usec = call_start_ts.tv_usec;
       CCEnd(cc_it);
 
       return false;
     }
 
c5a996bc
     if (!logger) {
ea9de070
       // open the logger if not already opened
c5a996bc
       msg_logger *l = call_profile.get_logger(req);
       if (l) setLogger(l);
6f1c3543
     }
 
2cfe4c66
     // evaluate ret
     if (isArgArray(ret)) {
       for (size_t i=0;i<ret.size();i++) {
 	if (!isArgArray(ret[i]) || !ret[i].size())
 	  continue;
 	if (!isArgInt(ret[i][SBC_CC_ACTION])) {
 	  ERROR("in call control module '%s' - action type not int\n",
 		cc_if.cc_name.c_str());
 	  continue;
 	}
 	switch (ret[i][SBC_CC_ACTION].asInt()) {
 	case SBC_CC_DROP_ACTION: {
 	  DBG("dropping call on call control action DROP from '%s'\n",
 	      cc_if.cc_name.c_str());
62fc8342
 	  dlg->setStatus(AmSipDialog::Disconnected);
2cfe4c66
 
 	  // call 'end' of call control modules up to here
 	  call_end_ts.tv_sec = call_start_ts.tv_sec;
 	  call_end_ts.tv_usec = call_start_ts.tv_usec;
 	  CCEnd(cc_it);
 
 	  return false;
 	}
 
 	case SBC_CC_REFUSE_ACTION: {
 	  if (ret[i].size() < 3 ||
 	      !isArgInt(ret[i][SBC_CC_REFUSE_CODE]) ||
 	      !isArgCStr(ret[i][SBC_CC_REFUSE_REASON])) {
 	    ERROR("in call control module '%s' - REFUSE action parameters missing/wrong: '%s'\n",
 		  cc_if.cc_name.c_str(), AmArg::print(ret[i]).c_str());
 	    continue;
 	  }
 	  string headers;
 	  if (ret[i].size() > SBC_CC_REFUSE_HEADERS) {
 	    for (size_t h=0;h<ret[i][SBC_CC_REFUSE_HEADERS].size();h++)
 	      headers += string(ret[i][SBC_CC_REFUSE_HEADERS][h].asCStr()) + CRLF;
 	  }
 
 	  DBG("replying with %d %s on call control action REFUSE from '%s' headers='%s'\n",
 	      ret[i][SBC_CC_REFUSE_CODE].asInt(), ret[i][SBC_CC_REFUSE_REASON].asCStr(),
 	      cc_if.cc_name.c_str(), headers.c_str());
 
ef4f7164
 	  SBCEventLog::instance()->
c2c6a82a
 	    logCallStart(req, getLocalTag(), dlg->getRemoteUA(), "",
ef4f7164
 			 ret[i][SBC_CC_REFUSE_CODE].asInt(),
 			 ret[i][SBC_CC_REFUSE_REASON].asCStr());
a5ba4c93
 
62fc8342
 	  dlg->reply(req,
a5ba4c93
 		     ret[i][SBC_CC_REFUSE_CODE].asInt(),
6f1c3543
 		     ret[i][SBC_CC_REFUSE_REASON].asCStr(),
 		     NULL, headers);
2cfe4c66
 
 	  // call 'end' of call control modules up to here
 	  call_end_ts.tv_sec = call_start_ts.tv_sec;
 	  call_end_ts.tv_usec = call_start_ts.tv_usec;
 	  CCEnd(cc_it);
 	  return false;
 	}
 
 	case SBC_CC_SET_CALL_TIMER_ACTION: {
 	  if (cc_timer_id > SBC_TIMER_ID_CALL_TIMERS_END) {
 	    ERROR("too many call timers - ignoring timer\n");
 	    continue;
 	  }
 
 	  if (ret[i].size() < 2 ||
 	      (!(isArgInt(ret[i][SBC_CC_TIMER_TIMEOUT]) ||
 		 isArgDouble(ret[i][SBC_CC_TIMER_TIMEOUT])))) {
 	    ERROR("in call control module '%s' - SET_CALL_TIMER action parameters missing: '%s'\n",
 		  cc_if.cc_name.c_str(), AmArg::print(ret[i]).c_str());
 	    continue;
 	  }
 
 	  double timeout;
 	  if (isArgInt(ret[i][SBC_CC_TIMER_TIMEOUT]))
 	    timeout = ret[i][SBC_CC_TIMER_TIMEOUT].asInt();
 	  else
 	    timeout = ret[i][SBC_CC_TIMER_TIMEOUT].asDouble();
 
 	  DBG("saving call timer %i: timeout %f\n", cc_timer_id, timeout);
 	  saveCallTimer(cc_timer_id, timeout);
 	  cc_timer_id++;
 	} break;
 	default: {
 	  ERROR("unknown call control action: '%s'\n", AmArg::print(ret[i]).c_str());
 	  continue;
 	}
 
 	}
 
       }
     }
 
     cc_mod++;
   }
5fd073dc
   cc_started = true;
2cfe4c66
   return true;
 }
 
 void SBCCallLeg::CCConnect(const AmSipReply& reply) {
5fd073dc
   if (!cc_started) return; // preserve original behavior of the CC interface
9867e1c1
 
2cfe4c66
   vector<AmDynInvoke*>::iterator cc_mod=cc_modules.begin();
 
   for (CCInterfaceListIteratorT cc_it=call_profile.cc_interfaces.begin();
        cc_it != call_profile.cc_interfaces.end(); cc_it++) {
     CCInterface& cc_if = *cc_it;
 
     AmArg di_args,ret;
     di_args.push(cc_if.cc_name);                // cc name
e1a8cf02
     di_args.push(getLocalTag());                // call ltag
2cfe4c66
     di_args.push((AmObject*)&call_profile);     // call profile
e1a8cf02
     di_args.push((AmObject*)NULL);              // there is no sip msg
     di_args.push(AmArg());                      // timestamps
2cfe4c66
     di_args.back().push((int)call_start_ts.tv_sec);
     di_args.back().push((int)call_start_ts.tv_usec);
     di_args.back().push((int)call_connect_ts.tv_sec);
     di_args.back().push((int)call_connect_ts.tv_usec);
     for (int i=0;i<2;i++)
       di_args.back().push((int)0);
6c634b3c
     di_args.push(getOtherId());                      // other leg ltag
2cfe4c66
 
 
     try {
       (*cc_mod)->invoke("connect", di_args, ret);
     } catch (const AmArg::OutOfBoundsException& e) {
       ERROR("OutOfBoundsException executing call control interface connect "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
6bbb7c8c
       stopCall(StatusChangeCause::InternalError);
2cfe4c66
       return;
     } catch (const AmArg::TypeMismatchException& e) {
       ERROR("TypeMismatchException executing call control interface connect "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
6bbb7c8c
       stopCall(StatusChangeCause::InternalError);
2cfe4c66
       return;
     }
 
     cc_mod++;
   }
 }
 
 void SBCCallLeg::CCEnd() {
   CCEnd(call_profile.cc_interfaces.end());
 }
 
 void SBCCallLeg::CCEnd(const CCInterfaceListIteratorT& end_interface) {
   vector<AmDynInvoke*>::iterator cc_mod=cc_modules.begin();
 
   for (CCInterfaceListIteratorT cc_it=call_profile.cc_interfaces.begin();
        cc_it != end_interface; cc_it++) {
     CCInterface& cc_if = *cc_it;
 
     AmArg di_args,ret;
     di_args.push(cc_if.cc_name);
     di_args.push(getLocalTag());                 // call ltag
     di_args.push((AmObject*)&call_profile);
e1a8cf02
     di_args.push((AmObject*)NULL);               // there is no sip msg
2cfe4c66
     di_args.push(AmArg());                       // timestamps
     di_args.back().push((int)call_start_ts.tv_sec);
     di_args.back().push((int)call_start_ts.tv_usec);
     di_args.back().push((int)call_connect_ts.tv_sec);
     di_args.back().push((int)call_connect_ts.tv_usec);
     di_args.back().push((int)call_end_ts.tv_sec);
     di_args.back().push((int)call_end_ts.tv_usec);
 
     try {
       (*cc_mod)->invoke("end", di_args, ret);
     } catch (const AmArg::OutOfBoundsException& e) {
       ERROR("OutOfBoundsException executing call control interface end "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
     } catch (const AmArg::TypeMismatchException& e) {
       ERROR("TypeMismatchException executing call control interface end "
 	    "module '%s' named '%s', parameters '%s'\n",
 	    cc_if.cc_module.c_str(), cc_if.cc_name.c_str(),
 	    AmArg::print(di_args).c_str());
     }
 
     cc_mod++;
   }
 }
 
e3f4e1ba
 void SBCCallLeg::onCallStatusChange(const StatusChangeCause &cause)
95973f10
 {
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
e3f4e1ba
     (*i)->onStateChange(this, cause);
95973f10
   }
 }
 
b7432bbe
 void SBCCallLeg::onBLegRefused(const AmSipReply& reply)
 {
   for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) {
     if ((*i)->onBLegRefused(this, reply) == StopProcessing) return;
   }
 }
 
33c0fb92
 void SBCCallLeg::onCallFailed(CallFailureReason reason, const AmSipReply *reply)
a5ba4c93
 {
33c0fb92
   switch (reason) {
     case CallRefused:
       if (reply) logCallStart(*reply);
       break;
 
     case CallCanceled:
       logCanceledCall();
       break;
   }
a5ba4c93
 }
f9701cec
 
 bool SBCCallLeg::onBeforeRTPRelay(AmRtpPacket* p, sockaddr_storage* remote_addr)
 {
   if(rtp_relay_rate_limit.get() &&
      rtp_relay_rate_limit->limit(p->getBufferSize()))
     return false; // drop
 
   return true; // relay
 }
 
 void SBCCallLeg::onAfterRTPRelay(AmRtpPacket* p, sockaddr_storage* remote_addr)
 {
   for(list<atomic_int*>::iterator it = rtp_pegs.begin();
       it != rtp_pegs.end(); ++it) {
     (*it)->inc(p->getBufferSize());
   }
 }
 
a5ba4c93
 void SBCCallLeg::logCallStart(const AmSipReply& reply)
 {
   std::map<int,AmSipRequest>::iterator t_req = recvd_req.find(reply.cseq);
   if (t_req != recvd_req.end()) {
9331eadf
     string b_leg_ua = getHeader(reply.hdrs,"Server");
ef4f7164
     SBCEventLog::instance()->logCallStart(t_req->second,getLocalTag(),
9331eadf
 					  dlg->getRemoteUA(),b_leg_ua,
ef4f7164
 					  (int)reply.code,reply.reason);
a5ba4c93
   }
   else {
34bd34a2
     DBG("could not log call-start/call-attempt (ci='%s';lt='%s')",
c2c6a82a
 	  getCallID().c_str(),getLocalTag().c_str());
a5ba4c93
   }
 }
 
33c0fb92
 void SBCCallLeg::logCanceledCall()
 {
   std::map<int,AmSipRequest>::iterator t_req = recvd_req.find(est_invite_cseq);
   if (t_req != recvd_req.end()) {
     SBCEventLog::instance()->logCallStart(t_req->second,getLocalTag(),
 					  "","",
 					  0,"canceled");
   }
   else {
     ERROR("could not log call-attempt (canceled, ci='%s';lt='%s')",
 	  getCallID().c_str(),getLocalTag().c_str());
   }
 }
 
d8a37473
 //////////////////////////////////////////////////////////////////////////////////////////
 // body filtering
 
ee5a6cea
 int SBCCallLeg::filterSdp(AmMimeBody &body, const string &method)
d8a37473
 {
ee5a6cea
   DBG("filtering body\n");
 
   AmMimeBody* sdp_body = body.hasContentType(SIP_APPLICATION_SDP);
   if (!sdp_body) return 0;
 
   // filter body for given methods only
   if (!(method == SIP_METH_INVITE ||
        method == SIP_METH_UPDATE ||
        method == SIP_METH_PRACK ||
        method == SIP_METH_ACK)) return 0;
d8a37473
 
ee5a6cea
   AmSdp sdp;
   int res = sdp.parse((const char *)sdp_body->getPayload());
   if (0 != res) {
     DBG("SDP parsing failed during body filtering!\n");
     return res;
   }
 
   bool changed = false;
d8a37473
   bool prefer_existing_codecs = call_profile.codec_prefs.preferExistingCodecs(a_leg);
 
d09715cf
   bool needs_normalization =
           call_profile.codec_prefs.shouldOrderPayloads(a_leg) ||
           call_profile.transcoder.isActive() ||
           !call_profile.sdpfilter.empty();
 
   if (needs_normalization) {
     normalizeSDP(sdp, false, ""); // anonymization is done in the other leg to use correct IP address
     changed = true;
   }
 
2eb971c5
   if (!call_profile.mediafilter.empty()) {
     res = filterMedia(sdp, call_profile.mediafilter);
     if (res < 0) {
       // result may be ignored, we need to set the SDP
       string n_body;
       sdp.print(n_body);
       sdp_body->setPayload((const unsigned char*)n_body.c_str(), n_body.length());
       return res;
     }
     changed = true;
   }
 
d8a37473
   if (prefer_existing_codecs) {
     // We have to order payloads before adding transcoder codecs to leave
     // transcoding as the last chance (existing codecs are preferred thus
     // relaying will be used if possible).
     if (call_profile.codec_prefs.shouldOrderPayloads(a_leg)) {
       call_profile.codec_prefs.orderSDP(sdp, a_leg);
       changed = true;
     }
   }
 
   // Add transcoder codecs before filtering because otherwise SDP filter could
   // inactivate some media lines which shouldn't be inactivated.
 
   if (call_profile.transcoder.isActive()) {
     appendTranscoderCodecs(sdp);
     changed = true;
   }
 
   if (!prefer_existing_codecs) {
     // existing codecs are not preferred - reorder AFTER adding transcoder
     // codecs so it might happen that transcoding will be forced though relaying
     // would be possible
     if (call_profile.codec_prefs.shouldOrderPayloads(a_leg)) {
       call_profile.codec_prefs.orderSDP(sdp, a_leg);
       changed = true;
     }
   }
 
   // It doesn't make sense to filter out codecs allowed for transcoding and thus
   // if the filter filters them out it can be considered as configuration
   // problem, right?
   // => So we wouldn't try to avoid filtering out transcoder codecs what would
   // just complicate things.
 
4735758f
   if (call_profile.sdpfilter.size()) {
     res = filterSDP(sdp, call_profile.sdpfilter);
d8a37473
     changed = true;
   }
4735758f
   if (call_profile.sdpalinesfilter.size()) {
d8a37473
     // filter SDP "a=lines"
4735758f
     filterSDPalines(sdp, call_profile.sdpalinesfilter);
d8a37473
     changed = true;
   }
 
ee5a6cea
   if (changed) {
     string n_body;
     sdp.print(n_body);
     sdp_body->setPayload((const unsigned char*)n_body.c_str(), n_body.length());
d8a37473
   }
ee5a6cea
 
   return res;
d8a37473
 }
 
 void SBCCallLeg::appendTranscoderCodecs(AmSdp &sdp)
 {
   // append codecs for transcoding, remember the added ones to be able to filter
   // them out from relayed reply!
 
   // important: normalized SDP should get here
 
   TRACE("going to append transcoder codecs into SDP\n");
   const std::vector<SdpPayload> &transcoder_codecs = call_profile.transcoder.audio_codecs;
 
   unsigned stream_idx = 0;
   vector<SdpPayload>::const_iterator p;
   for (vector<SdpMedia>::iterator m = sdp.media.begin(); m != sdp.media.end(); ++m) {
 
     // handle audio transcoder codecs
     if (m->type == MT_AUDIO) {
       // transcoder codecs can be added only if there are common payloads with
       // the remote (only those allowed for transcoder)
       // if there are no such common payloads adding transcoder codecs can't help
       // because we won't be able to transcode later on!
       // (we have to check for each media stream independently)
 
       // find first unused dynamic payload number & detect transcodable codecs
       // in original SDP
       int id = 96;
       bool transcodable = false;
       PayloadMask used_payloads;
       for (p = m->payloads.begin(); p != m->payloads.end(); ++p) {
         if (p->payload_type >= id) id = p->payload_type + 1;
4642da71
         if (containsPayload(transcoder_codecs, *p, m->transport)) transcodable = true;
d8a37473
         used_payloads.set(p->payload_type);
       }
 
       if (transcodable) {
         // there are some transcodable codecs present in the SDP, we can safely
         // add the other transcoder codecs to the SDP
         unsigned idx = 0;
         for (p = transcoder_codecs.begin(); p != transcoder_codecs.end(); ++p, ++idx) {
           // add all payloads which are not already there
4642da71
           if (!containsPayload(m->payloads, *p, m->transport)) {
d8a37473
             m->payloads.push_back(*p);
             int &pid = m->payloads.back().payload_type;
             if (pid < 0) {
               // try to use remembered ID
               pid = transcoder_payload_mapping.get(stream_idx, idx);
             }
 
             if ((pid < 0) || used_payloads.get(pid)) {
               // payload ID is not set or is already used in current SDP, we
               // need to assign a new one
               pid = id++;
             }
           }
         }
         if (id > 128) ERROR("assigned too high payload type number (%d), see RFC 3551\n", id);
       }
       else {
         // no compatible codecs found
         TRACE("can not transcode stream %d - no compatible codecs with transcoder_codecs found\n", stream_idx + 1);
       }
 
       stream_idx++; // count chosen media type only
     }
   }
 
   // remembered payload IDs should be used just once, in SDP answer
   // unfortunatelly the SDP answer might be present in 1xx and in 2xx as well so
   // we can't clear it here
   // on other hand it might be useful to use the same payload ID if offer/answer
   // is repeated in the other direction next time
 }
2cfe4c66
 
70b8d101
 void SBCCallLeg::savePayloadIDs(AmSdp &sdp)
 {
   unsigned stream_idx = 0;
   std::vector<SdpPayload> &transcoder_codecs = call_profile.transcoder.audio_codecs;
   for (vector<SdpMedia>::iterator m = sdp.media.begin(); m != sdp.media.end(); ++m) {
     if (m->type != MT_AUDIO) continue;
 
     unsigned idx = 0;
     for (vector<SdpPayload>::iterator p = transcoder_codecs.begin();
         p != transcoder_codecs.end(); ++p, ++idx)
     {
       if (p->payload_type < 0) {
4642da71
         const SdpPayload *pp = findPayload(m->payloads, *p, m->transport);
70b8d101
         if (pp && (pp->payload_type >= 0))
           transcoder_payload_mapping.map(stream_idx, idx, pp->payload_type);
       }
     }
 
     stream_idx++; // count chosen media type only
   }
 }
 
8e79bbbb
 bool SBCCallLeg::reinvite(const AmSdp &sdp, unsigned &request_cseq)
 {
   request_cseq = 0;
 
   AmMimeBody body;
   AmMimeBody *sdp_body = body.addPart(SIP_APPLICATION_SDP);
   if (!sdp_body) return false;
 
   string body_str;
   sdp.print(body_str);
   sdp_body->parse(SIP_APPLICATION_SDP, (const unsigned char*)body_str.c_str(), body_str.length());
 
62fc8342
   if (dlg->reinvite("", &body, SIP_FLAGS_VERBATIM) != 0) return false;
   request_cseq = dlg->cseq - 1;
8e79bbbb
   return true;
 }
 
baf99dc9
 /**
  * initialize modules from @arg cc_module_list with DI interface instances from @arg cc_module_di
  * add sucessfull ext-API call control instances to cc_ext
  * @return true on success (all modules properly initialized)
 */
 bool SBCCallLeg::initCCExtModules(const CCInterfaceListT& cc_module_list, const vector<AmDynInvoke*>& cc_module_di)
0cb9ad89
 {
63681f5c
   // init extended call control modules
baf99dc9
   vector<AmDynInvoke*>::const_iterator cc_mod = cc_module_di.begin();
   for (CCInterfaceListConstIteratorT cc_it = cc_module_list.begin(); cc_it != cc_module_list.end(); cc_it++)
63681f5c
   {
baf99dc9
     const CCInterface& cc_if = *cc_it;
     const string& cc_module = cc_it->cc_module;
63681f5c
 
     // get extended CC interface
     try {
       AmArg args, ret;
       (*cc_mod)->invoke("getExtendedInterfaceHandler", args, ret);
       ExtendedCCInterface *iface = dynamic_cast<ExtendedCCInterface*>(ret[0].asObject());
       if (iface) {
         DBG("extended CC interface offered by cc_module '%s'\n", cc_module.c_str());
         // module initialization
891a8e0e
         if (!iface->init(this, cc_if.cc_values)) {
baf99dc9
 	  ERROR("initializing extended call control interface '%s'\n", cc_module.c_str());
891a8e0e
 	  return false;
 	}
 
         cc_ext.push_back(iface);
63681f5c
       }
       else WARN("BUG: returned invalid extended CC interface by cc_module '%s'\n", cc_module.c_str());
     }
891a8e0e
     catch (const string& s) {
       DBG("initialization error '%s' or extended CC interface "
 	  "not supported by cc_module '%s'\n", s.c_str(), cc_module.c_str());
     }
63681f5c
     catch (...) {
891a8e0e
       DBG("initialization error or extended CC interface not "
 	  "supported by cc_module '%s'\n", cc_module.c_str());
63681f5c
     }
 
     ++cc_mod;
0cb9ad89
   }
baf99dc9
 
   if (!initPendingCCExtModules()) {
     return false;
   }
 
891a8e0e
   return true; // success
0cb9ad89
 }
9f0cf7a3
 
baf99dc9
 /** init pending modules until queue is empty */
 bool SBCCallLeg::initPendingCCExtModules() {
   while (cc_module_queue.size()) {
     // local copy
     CCInterfaceListT _cc_mod_queue = cc_module_queue;
     cc_module_queue.clear();
     vector<AmDynInvoke*> _cc_mod_ifs;
 
     // get corresponding DI interfaces
     if (!::getCCInterfaces(_cc_mod_queue, _cc_mod_ifs))
       return false;
 
     // add to call leg
     if (!initCCExtModules(_cc_mod_queue, _cc_mod_ifs)) {
       return false;
     }
   }
   return true;
 }
 
 void SBCCallLeg::addPendingCCExtModule(const string& cc_name, const string& cc_module, const map<string, string>& cc_values) {
   cc_module_queue.push_back(CCInterface(cc_name));
   cc_module_queue.back().cc_module = cc_module;
   cc_module_queue.back().cc_values = cc_values;
   DBG("added module '%s' from module '%s' to pending CC Ext modules\n",
       cc_name.c_str(), cc_module.c_str());
 }
 
b8f4e37a
 #define CALL_EXT_CC_MODULES(method) \
   do { \
     for (vector<ExtendedCCInterface*>::iterator i = cc_ext.begin(); i != cc_ext.end(); ++i) { \
       (*i)->method(this); \
     } \
   } while (0)
 
 void SBCCallLeg::holdRequested()
11b66546
 {
b8f4e37a
   TRACE("%s: hold requested\n", getLocalTag().c_str());
   CALL_EXT_CC_MODULES(holdRequested);
   CallLeg::holdRequested();
11b66546
 }
 
b8f4e37a
 void SBCCallLeg::holdAccepted()
11b66546
 {
b8f4e37a
   TRACE("%s: hold accepted\n", getLocalTag().c_str());
   CALL_EXT_CC_MODULES(holdAccepted);
   CallLeg::holdAccepted();
 }
 
 void SBCCallLeg::holdRejected()
 {
   TRACE("%s: hold rejected\n", getLocalTag().c_str());
   CALL_EXT_CC_MODULES(holdRejected);
   CallLeg::holdRejected();
 }
 
 void SBCCallLeg::resumeRequested()
 {
   TRACE("%s: resume requested\n", getLocalTag().c_str());
   CALL_EXT_CC_MODULES(resumeRequested);
   CallLeg::resumeRequested();
 }
 
 void SBCCallLeg::resumeAccepted()
 {
   TRACE("%s: resume accepted\n", getLocalTag().c_str());
   CALL_EXT_CC_MODULES(resumeAccepted);
   CallLeg::resumeAccepted();
 }
 
 void SBCCallLeg::resumeRejected()
 {
   TRACE("%s: resume rejected\n", getLocalTag().c_str());
   CALL_EXT_CC_MODULES(resumeRejected);
   CallLeg::resumeRejected();
 }
 
da3ea6b5
 static void replace_address(SdpConnection &c, const string &ip)
b8f4e37a
 {
   if (!c.address.empty()) {
da3ea6b5
     if (c.addrType == AT_V4) {
       c.address = ip;
       return;
b8f4e37a
     }
da3ea6b5
     // TODO: IPv6?
     DBG("unsupported address type for replacing IP");
11b66546
   }
 }
 
da3ea6b5
 static void alterHoldRequest(AmSdp &sdp, SBCCallProfile::HoldSettings::Activity a, const string &ip)
11b66546
 {
da3ea6b5
   if (!ip.empty()) replace_address(sdp.conn, ip);
f4fe7176
   for (vector<SdpMedia>::iterator m = sdp.media.begin(); m != sdp.media.end(); ++m) {
da3ea6b5
     if (!ip.empty()) replace_address(m->conn, ip);
     m->recv = (a == SBCCallProfile::HoldSettings::sendrecv || a == SBCCallProfile::HoldSettings::recvonly);
     m->send = (a == SBCCallProfile::HoldSettings::sendrecv || a == SBCCallProfile::HoldSettings::sendonly);
   }
 }
 
 void SBCCallLeg::alterHoldRequestImpl(AmSdp &sdp)
 {
   if (call_profile.hold_settings.mark_zero_connection(a_leg)) {
     static const string zero("0.0.0.0");
     ::alterHoldRequest(sdp, call_profile.hold_settings.activity(a_leg), zero);
   }
   else {
     if (getRtpRelayMode() == RTP_Direct) {
       // we can not put our IP there if not relaying, using empty not to
       // overwrite existing addresses
       static const string empty;
       ::alterHoldRequest(sdp, call_profile.hold_settings.activity(a_leg), empty);
     }
     else {
       // use public IP to be put into connection addresses (overwrite 0.0.0.0
       // there)
       ::alterHoldRequest(sdp, call_profile.hold_settings.activity(a_leg), advertisedIP());
     }
11b66546
   }
f4fe7176
 }
 
b8f4e37a
 void SBCCallLeg::alterHoldRequest(AmSdp &sdp)
11b66546
 {
da3ea6b5
   TRACE("altering B2B hold request(%s, %s, %s)\n",
       call_profile.hold_settings.alter_b2b(a_leg) ? "alter B2B" : "do not alter B2B",
       call_profile.hold_settings.mark_zero_connection(a_leg) ? "0.0.0.0" : "own IP",
       call_profile.hold_settings.activity_str(a_leg).c_str()
       );
b8f4e37a
 
   if (!call_profile.hold_settings.alter_b2b(a_leg)) return;
 
da3ea6b5
   alterHoldRequestImpl(sdp);
11b66546
 }
 
 void SBCCallLeg::createHoldRequest(AmSdp &sdp)
 {
b8f4e37a
   // hack: we need to have other side SDP (if the stream is hold already
   // it should be marked as inactive)
f4fe7176
   // FIXME: fix SDP versioning! (remember generated versions and increase the
   // version number in every SDP passing through?)
b8f4e37a
 
f4fe7176
   AmMimeBody *s = established_body.hasContentType(SIP_APPLICATION_SDP);
   if (s) sdp.parse((const char*)s->getPayload());
   if (sdp.media.empty()) {
     // established SDP is not valid! generate complete fake
b8f4e37a
     sdp.version = 0;
     sdp.origin.user = "sems";
     sdp.sessionName = "sems";
     sdp.conn.network = NT_IN;
     sdp.conn.addrType = AT_V4;
f4fe7176
     sdp.conn.address = "0.0.0.0";
b8f4e37a
 
     sdp.media.push_back(SdpMedia());
     SdpMedia &m = sdp.media.back();
     m.type = MT_AUDIO;
     m.transport = TP_RTPAVP;
     m.send = false;
     m.recv = false;
     m.payloads.push_back(SdpPayload(0));
11b66546
   }
f4fe7176
 
   AmB2BMedia *ms = getMediaSession();
   if (ms) ms->replaceOffer(sdp, a_leg);
64a5bee7
 
   alterHoldRequestImpl(sdp);
11b66546
 }
ea9de070
 
 void SBCCallLeg::setMediaSession(AmB2BMedia *new_session)
 {
a88c6890
   if (new_session) {
     if (call_profile.log_rtp) new_session->setRtpLogger(logger);
     else new_session->setRtpLogger(NULL);
   }
ea9de070
   CallLeg::setMediaSession(new_session);
 }
 
 bool SBCCallLeg::openLogger(const std::string &path)
 {
   file_msg_logger *log = new pcap_logger();
 
   if(log->open(path.c_str()) != 0) {
     // open error
     delete log;
     return false;
   }
 
   // opened successfully
   setLogger(log);
   return true;
 }
 
 void SBCCallLeg::setLogger(msg_logger *_logger)
 {
   if (logger) dec_ref(logger); // release the old one
 
   logger = _logger;
4b82acd3
   if (logger) inc_ref(logger);
ea9de070
   if (call_profile.log_sip) dlg->setMsgLogger(logger);
a88c6890
   else dlg->setMsgLogger(NULL);
ea9de070
 
   AmB2BMedia *m = getMediaSession();
a88c6890
   if (m) {
     if (call_profile.log_rtp) m->setRtpLogger(logger);
     else m->setRtpLogger(NULL);
   }
ea9de070
 }
68d1d42c
 
 void SBCCallLeg::computeRelayMask(const SdpMedia &m, bool &enable, PayloadMask &mask)
 {
   if (call_profile.transcoder.isActive()) {
     TRACE("entering transcoder's computeRelayMask(%s)\n", a_leg ? "A leg" : "B leg");
 
     SBCCallProfile::TranscoderSettings &transcoder_settings = call_profile.transcoder;
     PayloadMask m1, m2;
     bool use_m1 = false;
 
     /* if "m" contains only "norelay" codecs, relay is enabled for them (main idea
      * of these codecs is to limit network bandwidth and it makes not much sense
      * to transcode between codecs 'which are better to avoid', right?)
      *
      * if "m" contains other codecs, relay is enabled as well
      *
      * => if m contains at least some codecs, relay is enabled */
     enable = !m.payloads.empty();
 
     vector<SdpPayload> &norelay_payloads =
       a_leg ? transcoder_settings.audio_codecs_norelay_aleg : transcoder_settings.audio_codecs_norelay;
 
     vector<SdpPayload>::const_iterator p;
     for (p = m.payloads.begin(); p != m.payloads.end(); ++p) {
 
       // do not mark telephone-event payload for relay (and do not use it for
       // transcoding as well)
       if(strcasecmp("telephone-event",p->encoding_name.c_str()) == 0) continue;
 
       // mark every codec for relay in m2
       TRACE("m2: marking payload %d for relay\n", p->payload_type);
       m2.set(p->payload_type);
 
       if (!containsPayload(norelay_payloads, *p, m.transport)) {
         // this payload can be relayed
 
         TRACE("m1: marking payload %d for relay\n", p->payload_type);
         m1.set(p->payload_type);
 
         if (!use_m1 && containsPayload(transcoder_settings.audio_codecs, *p, m.transport)) {
           // the remote SDP contains transcodable codec which can be relayed (i.e.
           // the one with higher "priority" so we want to disable relaying of the
           // payloads which should not be ralyed if possible)
           use_m1 = true;
         }
       }
     }
 
     TRACE("using %s\n", use_m1 ? "m1" : "m2");
     if (use_m1) mask = m1;
     else mask = m2;
   }
   else {
     // for non-transcoding modes use default
     CallLeg::computeRelayMask(m, enable, mask);
   }
 }