Ejemplo n.º 1
0
bool SessionTimer::onSendReply(const AmSipRequest& req,
			       unsigned int  code,const string& reason,
			       const string& content_type,const string& body,
			       string& hdrs,
			       int flags)
{
  //if (!session_timer_conf.getEnableSessionTimer())
  // 	return "";

  string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED)  "timer"  CRLF;
  if  ((req.method != "INVITE") && (req.method != "UPDATE")) 
    return false;
    
  // only in 2xx responses to INV/UPD
  m_hdrs  += "Session-Expires: " + int2str(session_interval) + ";refresher="+
    (session_refresher_role==UAC ? "uac":"uas")+CRLF;
    
  if (((session_refresher_role==UAC) && (session_refresher==refresh_remote)) 
      || ((session_refresher_role==UAS) && remote_timer_aware))
    m_hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRED)  "timer"  CRLF;
    
  hdrs += m_hdrs;

  return false;
}
bool AmSipSubscription::doUnsubscribe()
{
  if (sub_state == SipSubStateTerminated || sub_state == SipSubStateIdle) {
    DBG("not subscribed - not unsubscribing\n");
    return true;
  }

  dlg.remote_uri = req.r_uri;
    
  string hdrs;
  hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) + info.event;
  if (!info.id.empty())
    hdrs += ";id="+info.id;
  hdrs += CRLF;
  if (!info.accept.empty()) {
    hdrs += SIP_HDR_COLSP(SIP_HDR_ACCEPT) + info.accept + CRLF;
  }
  hdrs += SIP_HDR_COLSP(SIP_HDR_EXPIRES) "0" CRLF;

  if (dlg.sendRequest(SIP_METH_SUBSCRIBE, NULL, hdrs) < 0) {
    WARN("failed to send unsubscription to '%s' (proxy '%s').\n",
	 dlg.remote_uri.c_str(), dlg.outbound_proxy.c_str());
    return false;
  }

  return true;
}
string AmSipSubscription::getSubscribeHdrs() {
  string hdrs;
  hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) + info.event;
  if (!info.id.empty())
    hdrs += ";id="+info.id;
  hdrs += CRLF;
  if (!info.accept.empty()) {
    hdrs += SIP_HDR_COLSP(SIP_HDR_ACCEPT) + info.accept + CRLF;
  }
  if (wanted_expires) {
    hdrs += SIP_HDR_COLSP(SIP_HDR_EXPIRES) + int2str(wanted_expires)  + CRLF;
  }

  return hdrs;
}
Ejemplo n.º 4
0
/* static */
int AmBasicSipDialog::reply_error(const AmSipRequest& req, unsigned int code, 
				  const string& reason, const string& hdrs,
				  msg_logger* logger)
{
  AmSipReply reply;

  reply.code = code;
  reply.reason = reason;
  reply.tt = req.tt;
  reply.hdrs = hdrs;
  reply.to_tag = AmSession::getNewId();

  if (AmConfig::Signature.length())
    reply.hdrs += SIP_HDR_COLSP(SIP_HDR_SERVER) + AmConfig::Signature + CRLF;

  // add transcoder statistics into reply headers
  //addTranscoderStats(reply.hdrs);

  int ret = SipCtrlInterface::send(reply,string(""),logger);
  if(ret){
    ERROR("Could not send reply: code=%i; reason='%s';"
	  " method=%s; call-id=%s; cseq=%i\n",
	  reply.code,reply.reason.c_str(),
	  req.method.c_str(),req.callid.c_str(),req.cseq);
  }

  return ret;
}
Ejemplo n.º 5
0
bool AmSIPRegistration::doRegistration() 
{
  bool res = true;

  waiting_result = true;
  unregistering = false;

  req.to_tag     = "";
  req.r_uri    = "sip:"+info.domain;

  dlg.setRemoteTag(string());
  dlg.setRemoteUri(req.r_uri);
    
  // set outbound proxy as next hop 
  if (!info.proxy.empty()) {
    dlg.outbound_proxy = info.proxy;
  } else if (!AmConfig::OutboundProxy.empty()) {
    dlg.outbound_proxy = AmConfig::OutboundProxy;
  }

  string hdrs = SIP_HDR_COLSP(SIP_HDR_EXPIRES) +
    int2str(expires_interval) + CRLF;

  int flags=0;
  if(!info.contact.empty()) {
    hdrs += SIP_HDR_COLSP(SIP_HDR_CONTACT) "<"
      + info.contact + ">" + CRLF;
    flags = SIP_FLAGS_NOCONTACT;
  }
    
  if (dlg.sendRequest(req.method, NULL, hdrs, flags) < 0) {
    ERROR("failed to send registration.\n");
    res = false;
    waiting_result = false;
  }
    
  // save TS
  reg_send_begin  = time(NULL);
  return res;
}
Ejemplo n.º 6
0
bool SessionTimer::onSendRequest(const string& method, 
				 const string& content_type,
				 const string& body,
				 string& hdrs,
				 int flags,
				 unsigned int cseq)
{
  string m_hdrs = SIP_HDR_COLSP(SIP_HDR_SUPPORTED)  "timer"  CRLF;
  if  ((method != "INVITE") && (method != "UPDATE"))
    goto end;
  
  m_hdrs += "Session-Expires: "+ int2str(session_timer_conf.getSessionExpires()) +CRLF
    + "Min-SE: " + int2str(session_timer_conf.getMinimumTimer()) + CRLF;

 end:
  hdrs += m_hdrs;
  return false;
}
Ejemplo n.º 7
0
int AmB2BSession::relaySip(const AmSipRequest& orig, const AmSipReply& reply)
{
  const string* hdrs = &reply.hdrs;
  string m_hdrs;
  const string method(orig.method);

  if (reply.rseq != 0) {
    m_hdrs = reply.hdrs +
      SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(reply.rseq) + CRLF;
    hdrs = &m_hdrs;
  }

  AmMimeBody body(reply.body);
  if ((orig.method == SIP_METH_INVITE ||
       orig.method == SIP_METH_UPDATE ||
       orig.method == SIP_METH_ACK ||
       orig.method == SIP_METH_PRACK))
  {
    updateLocalBody(body);
  }

  DBG("relaying SIP reply %u %s\n", reply.code, reply.reason.c_str());

  int flags = SIP_FLAGS_VERBATIM;
  if(reply.to_tag.empty())
    flags |= SIP_FLAGS_NOTAG;

  int err = dlg->reply(orig,reply.code,reply.reason,
		       &body, *hdrs, flags);

  if(err < 0){
    ERROR("dlg->reply() failed\n");
    return err;
  }

  if ((method == SIP_METH_INVITE ||
       method == SIP_METH_UPDATE) &&
      !reply.body.empty()) {
    saveSessionDescription(reply.body);
  }

  return 0;
}
Ejemplo n.º 8
0
string AmBasicSipDialog::getRoute() 
{
  string res;

  if(!outbound_proxy.empty() && (force_outbound_proxy || remote_tag.empty())){
    res += "<" + outbound_proxy + ";lr>";

    if(!route.empty()) {
      res += ",";
    }
  }

  res += route;

  if(!res.empty()) {
    res = SIP_HDR_COLSP(SIP_HDR_ROUTE) + res + CRLF;
  }

  return res;
}
Ejemplo n.º 9
0
string AmBasicSipDialog::getContactHdr() {
  AmUriParser tmp_contact = contact;
  if(tmp_contact.uri_host.empty()) {
    int oif = getOutboundIf();
    assert(oif >= 0);
    assert(oif < (int)AmConfig::SIP_Ifs.size());
    tmp_contact.uri_host = AmConfig::SIP_Ifs[oif].getIP();
    tmp_contact.uri_port = int2str(AmConfig::SIP_Ifs[oif].LocalPort);
  }

  if(tmp_contact.uri_user.empty() && !ext_local_tag.empty()) {
    tmp_contact.uri_user = local_tag;
  }

  string contact_str = tmp_contact.print();

  DBG("[%s] resulting Contact header: %s",
    local_tag.c_str(),
    contact_str.c_str());

  return SIP_HDR_COLSP(SIP_HDR_CONTACT) + contact_str += CRLF;
}
Ejemplo n.º 10
0
bool AmBasicSipDialog::onRxReqSanity(const AmSipRequest& req)
{
  // Sanity checks
  if(!remote_tag.empty() && !req.from_tag.empty() &&
     (req.from_tag != remote_tag)){
    DBG("remote_tag = '%s'; req.from_tag = '%s'\n",
	remote_tag.c_str(), req.from_tag.c_str());
    reply_error(req, 481, SIP_REPLY_NOT_EXIST);
    return false;
  }

  if (r_cseq_i && req.cseq <= r_cseq){

    if (req.method == SIP_METH_NOTIFY) {
      if (!AmConfig::IgnoreNotifyLowerCSeq) {
	// clever trick to not break subscription dialog usage
	// for implementations which follow 3265 instead of 5057
	string hdrs = SIP_HDR_COLSP(SIP_HDR_RETRY_AFTER)  "0"  CRLF;

	INFO("remote cseq lower than previous ones - refusing request\n");
	// see 12.2.2
	reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR, hdrs);
	return false;
      }
    }
    else {
      INFO("remote cseq lower than previous ones - refusing request\n");
      // see 12.2.2
      reply_error(req, 500, SIP_REPLY_SERVER_INTERNAL_ERROR);
      return false;
    }
  }

  r_cseq = req.cseq;
  r_cseq_i = true;

  return true;
}
Ejemplo n.º 11
0
int AmBasicSipDialog::sendRequest(const string& method, 
				  const AmMimeBody* body,
				  const string& hdrs,
				  int flags)
{
  AmSipRequest req;

  req.method = method;
  req.r_uri = remote_uri;

  req.from = SIP_HDR_COLSP(SIP_HDR_FROM) + local_party;
  if(!ext_local_tag.empty())
    req.from += ";tag=" + ext_local_tag;
  else if(!local_tag.empty())
    req.from += ";tag=" + local_tag;
    
  req.to = SIP_HDR_COLSP(SIP_HDR_TO) + remote_party;
  if(!remote_tag.empty()) 
    req.to += ";tag=" + remote_tag;
    
  req.cseq = cseq;
  req.callid = callid;
    
  req.hdrs = hdrs;

  req.route = getRoute();

  if(body != NULL) {
    req.body = *body;
  }

  if(onTxRequest(req,flags) < 0)
    return -1;

  if (!(flags & SIP_FLAGS_NOCONTACT)) {
    req.contact = getContactHdr();
  }

  if (!(flags & SIP_FLAGS_VERBATIM)) {
    // add Signature
    if (AmConfig::Signature.length())
      req.hdrs += SIP_HDR_COLSP(SIP_HDR_USER_AGENT) + AmConfig::Signature + CRLF;
  }

  int send_flags = 0;
  if(patch_ruri_next_hop && remote_tag.empty()) {
    send_flags |= TR_FLAG_NEXT_HOP_RURI;
  }

  if((flags & SIP_FLAGS_NOBL) ||
     !remote_tag.empty()) {
    send_flags |= TR_FLAG_DISABLE_BL;
  }

  int res = SipCtrlInterface::send(req, local_tag,
				   remote_tag.empty() || !next_hop_1st_req ?
				   next_hop : "",
				   outbound_interface,
				   send_flags,logger);
  if(res) {
    ERROR("Could not send request: method=%s; call-id=%s; cseq=%i\n",
	  req.method.c_str(),req.callid.c_str(),req.cseq);
    return res;
  }

  onRequestTxed(req);
  return 0;
}
Ejemplo n.º 12
0
int AmBasicSipDialog::reply(const AmSipRequest& req,
			    unsigned int  code,
			    const string& reason,
			    const AmMimeBody* body,
			    const string& hdrs,
			    int flags)
{
  TransMap::const_iterator t_it = uas_trans.find(req.cseq);
  if(t_it == uas_trans.end()){
    ERROR("could not find any transaction matching request cseq\n");
    ERROR("request cseq=%i; reply code=%i; callid=%s; local_tag=%s; "
	  "remote_tag=%s\n",
	  req.cseq,code,callid.c_str(),
	  local_tag.c_str(),remote_tag.c_str());
    log_stacktrace(L_ERR);
    return -1;
  }
  DBG("reply: transaction found!\n");
    
  AmSipReply reply;

  reply.code = code;
  reply.reason = reason;
  reply.tt = req.tt;
  if((code > 100) && !(flags & SIP_FLAGS_NOTAG))
    reply.to_tag = ext_local_tag.empty() ? local_tag : ext_local_tag;
  reply.hdrs = hdrs;
  reply.cseq = req.cseq;
  reply.cseq_method = req.method;

  if(body != NULL)
    reply.body = *body;

  if(onTxReply(req,reply,flags)){
    DBG("onTxReply failed\n");
    return -1;
  }

  if (!(flags & SIP_FLAGS_VERBATIM)) {
    // add Signature
    if (AmConfig::Signature.length())
      reply.hdrs += SIP_HDR_COLSP(SIP_HDR_SERVER) + AmConfig::Signature + CRLF;
  }

  if ((code > 100 && code < 300) && !(flags & SIP_FLAGS_NOCONTACT)) {
    /* if 300<=code<400, explicit contact setting should be done */
    reply.contact = getContactHdr();
  }

  int ret = SipCtrlInterface::send(reply,local_tag,logger);
  if(ret){
    ERROR("Could not send reply: code=%i; reason='%s'; method=%s;"
	  " call-id=%s; cseq=%i\n",
	  reply.code,reply.reason.c_str(),reply.cseq_method.c_str(),
	  callid.c_str(),reply.cseq);

    return ret;
  }
  else {
    onReplyTxed(req,reply);
  }

  return ret;
}
Ejemplo n.º 13
0
string AmBasicSipDialog::getContactHdr() {
  return
    SIP_HDR_COLSP(SIP_HDR_CONTACT) "<"+ getContactUri() += ">" CRLF;
}
Ejemplo n.º 14
0
void fixReplaces(string& req_hdrs, bool is_invite) {

  string replaces;
  string refer_to;
  AmUriParser refer_target;
  vector<string> hdrs;                      // headers from Refer-To URI
  vector<string>::iterator replaces_hdr_it; // Replaces header from Refer-To URI

  DBG("Replaces handler: fixing %s request\n", is_invite?"INVITE":"REFER");

  if (is_invite) {
    replaces = getHeader(req_hdrs, SIP_HDR_REPLACES, true);
    if (replaces.empty()) {
      DBG("Replaces handler: no Replaces in INVITE, ignoring\n");
      return;
    }
  } else {
    refer_to = getHeader(req_hdrs, SIP_HDR_REFER_TO, SIP_HDR_REFER_TO_COMPACT, true);
    if (refer_to.empty()) {
      DBG("Replaces handler: empty Refer-To header, ignoring\n");
      return;
    }

    size_t pos=0; size_t end=0;
    if (!refer_target.parse_contact(refer_to, pos, end)) {
      DBG("Replaces handler: unable to parse Refer-To name-addr, ignoring\n");
      return;
    }

    if (refer_target.uri_headers.empty()) {
      DBG("Replaces handler: no headers in Refer-To target, ignoring\n");
      return;
    }

    hdrs = explode(refer_target.uri_headers, ";");
    for (replaces_hdr_it=hdrs.begin(); replaces_hdr_it != hdrs.end(); replaces_hdr_it++) {

      string s = URL_decode(*replaces_hdr_it);
      const char* Replaces_str = "Replaces";
      if ((s.length() >= 8) &&
	  !strncmp(Replaces_str, s.c_str(), 8)) {
	size_t pos = 8;
	while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
	if (s[pos] != '=')
	  continue;
	pos++;
	while (s.length()>pos && (s[pos] == ' ' || s[pos] == '\t')) pos++;
	replaces = s.substr(pos);
	break;
      }
    }
    
    if (replaces_hdr_it == hdrs.end()) {
      DBG("Replaces handler: no Replaces headers in Refer-To target, ignoring\n");
      return;
    }
  }

  DBG("Replaces found: '%s'\n", replaces.c_str());
  size_t ftag_begin; size_t ftag_len;
  size_t ttag_begin; size_t ttag_len;
  size_t cid_len=0;
 
  // todo: parse full replaces header and reconstruct including unknown params
  if (!findTag(replaces, "from-tag=", ftag_begin, ftag_len)) {
    WARN("Replaces missing 'from-tag', ignoring\n");
    return;
  }

  if (!findTag(replaces, "to-tag=", ttag_begin, ttag_len)) {
    WARN("Replaces missing 'to-tag', ignoring\n");
    return;
  }
  while (cid_len < replaces.size() && replaces[cid_len] != ';')
    cid_len++;

  string ftag = replaces.substr(ftag_begin, ftag_len);
  string ttag = replaces.substr(ttag_begin, ttag_len);
  string callid = replaces.substr(0, cid_len);
  bool early_only = replaces.find("early-only") != string::npos;

  DBG("Replaces handler: found callid='%s', ftag='%s', ttag='%s'\n",
      callid.c_str(), ftag.c_str(), ttag.c_str());

  SBCCallRegistryEntry other_dlg;
  if (SBCCallRegistry::lookupCall(ttag, other_dlg)) {
    replaces = other_dlg.callid+
      ";from-tag="+other_dlg.ltag+";to-tag="+other_dlg.rtag;
    if (early_only)
      replaces += ";early_only";
    DBG("Replaces handler: mapped Replaces to: '%s'\n", replaces.c_str());

    if (is_invite) {
      removeHeader(req_hdrs, SIP_HDR_REPLACES);
      req_hdrs+=SIP_HDR_COLSP(SIP_HDR_REPLACES)+replaces+CRLF;
    } else {
      string replaces_enc = SIP_HDR_REPLACES "="+URL_encode(replaces);
      string new_hdrs;
      for (vector<string>::iterator it = hdrs.begin(); it != hdrs.end(); it++) {
	if (it != hdrs.begin())
	  new_hdrs+=";";

	if (it != replaces_hdr_it) {
	  // different hdr, just add it
	  new_hdrs+=*it;
	} else {
	  //reconstructed replaces hdr
	  new_hdrs+=replaces_enc;
	}
      }
      refer_target.uri_headers=new_hdrs;
      removeHeader(req_hdrs, SIP_HDR_REFER_TO);
      removeHeader(req_hdrs, SIP_HDR_REFER_TO_COMPACT);
      req_hdrs+=SIP_HDR_COLSP(SIP_HDR_REFER_TO)+refer_target.nameaddr_str()+CRLF;
    }

  } else {
    DBG("Replaces handler: call with tag '%s' not found\n", ttag.c_str());
  }

 
}
Ejemplo n.º 15
0
int AmB2BSession::relaySip(const AmSipRequest& req)
{
  AmMimeBody body(req.body);

  if ((req.method == SIP_METH_INVITE ||
       req.method == SIP_METH_UPDATE ||
       req.method == SIP_METH_ACK ||
       req.method == SIP_METH_PRACK))
  {
    updateLocalBody(body);
  }

  if (req.method != "ACK") {
    relayed_req[dlg->cseq] = req;

    const string* hdrs = &req.hdrs;
    string m_hdrs;

    // translate RAck for PRACK
    if (req.method == SIP_METH_PRACK && req.rseq) {
      TransMap::iterator t;
      for (t=relayed_req.begin(); t != relayed_req.end(); t++) {
	if (t->second.cseq == req.rack_cseq) {
	  m_hdrs = req.hdrs +
	    SIP_HDR_COLSP(SIP_HDR_RACK) + int2str(req.rseq) +
	    " " + int2str(t->first) + " " + req.rack_method + CRLF;
	  hdrs = &m_hdrs;
	  break;
	}
      }
      if (t==relayed_req.end()) {
	WARN("Transaction with CSeq %d not found for translating RAck cseq\n",
	     req.rack_cseq);
      }
    }

    DBG("relaying SIP request %s %s\n", req.method.c_str(), req.r_uri.c_str());
    int err = dlg->sendRequest(req.method, &body, *hdrs, SIP_FLAGS_VERBATIM);
    if(err < 0){
      ERROR("dlg->sendRequest() failed\n");
      return err;
    }

    if ((req.method == SIP_METH_INVITE ||
	 req.method == SIP_METH_UPDATE) &&
	!req.body.empty()) {
      saveSessionDescription(req.body);
    }

  } else {
    //its a (200) ACK 
    TransMap::iterator t = relayed_req.begin(); 

    while (t != relayed_req.end()) {
      if (t->second.cseq == req.cseq)
	break;
      t++;
    } 
    if (t == relayed_req.end()) {
      ERROR("transaction for ACK not found in relayed requests\n");
      // FIXME: local body (if updated) should be discarded here
      return -1;
    }

    DBG("sending relayed 200 ACK\n");
    int err = dlg->send_200_ack(t->first, &body,
			       req.hdrs, SIP_FLAGS_VERBATIM);
    if(err < 0) {
      ERROR("dlg->send_200_ack() failed\n");
      return err;
    }

    if (!req.body.empty() &&
	(t->second.method == SIP_METH_INVITE)) {
    // delayed SDP negotiation - save SDP
      saveSessionDescription(req.body);
    }

    relayed_req.erase(t);
  }

  return 0;
}
Ejemplo n.º 16
0
void AmB2BSession::onSipRequest(const AmSipRequest& req)
{
  bool fwd = sip_relay_only &&
    (req.method != SIP_METH_CANCEL);

  if( ((req.method == SIP_METH_SUBSCRIBE) ||
       (req.method == SIP_METH_NOTIFY) ||
       (req.method == SIP_METH_REFER))
      && !subs->onRequestIn(req) ) {
    return;
  }

  if(!fwd)
    AmSession::onSipRequest(req);
  else {
    updateRefreshMethod(req.hdrs);

    if(req.method == SIP_METH_BYE)
      onBye(req);
  }

  B2BSipRequestEvent* r_ev = new B2BSipRequestEvent(req,fwd);

  if (fwd) {
    DBG("relaying B2B SIP request (fwd) %s %s\n", r_ev->req.method.c_str(), r_ev->req.r_uri.c_str());

    if(r_ev->req.method == SIP_METH_NOTIFY) {

      string event = getHeader(r_ev->req.hdrs,SIP_HDR_EVENT,true);
      string id = get_header_param(event,"id");
      event = strip_header_params(event);

      if(event == "refer" && !id.empty()) {

	int id_int=0;
	if(str2int(id,id_int)) {

	  unsigned int mapped_id=0;
	  if(getMappedReferID(id_int,mapped_id)) {

	    removeHeader(r_ev->req.hdrs,SIP_HDR_EVENT);
	    r_ev->req.hdrs += SIP_HDR_COLSP(SIP_HDR_EVENT) "refer;id=" 
	      + int2str(mapped_id) + CRLF;
	  }
	}
      }
    }

    int res = relayEvent(r_ev);
    if (res == 0) {
      // successfuly relayed, store the request
      if(req.method != SIP_METH_ACK)
        recvd_req.insert(std::make_pair(req.cseq,req));
    }
    else {
      // relay failed, generate error reply
      DBG("relay failed, replying error\n");
      AmSipReply n_reply;
      errCode2RelayedReply(n_reply, res, 500);
      dlg->reply(req, n_reply.code, n_reply.reason);
    }

    return;
  }

  DBG("relaying B2B SIP request %s %s\n", r_ev->req.method.c_str(), r_ev->req.r_uri.c_str());
  relayEvent(r_ev);
}
Ejemplo n.º 17
0
void AmB2BSession::onB2BEvent(B2BEvent* ev)
{
  DBG("AmB2BSession::onB2BEvent\n");
  switch(ev->event_id){

  case B2BSipRequest:
    {   
      B2BSipRequestEvent* req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
      assert(req_ev);

      DBG("B2BSipRequest: %s (fwd=%s)\n",
	  req_ev->req.method.c_str(),
	  req_ev->forward?"true":"false");

      if(req_ev->forward){

	// Check Max-Forwards first
	if(req_ev->req.max_forwards == 0) {
	  relayError(req_ev->req.method,req_ev->req.cseq,
		     true,483,SIP_REPLY_TOO_MANY_HOPS);
	  return;
	}

	if (req_ev->req.method == SIP_METH_INVITE &&
	    dlg->getUACInvTransPending()) {
	  // don't relay INVITE if INV trans pending
	  DBG("not sip-relaying INVITE with pending INV transaction, "
	      "b2b-relaying 491 pending\n");
          relayError(req_ev->req.method, req_ev->req.cseq,
		     true, 491, SIP_REPLY_PENDING);
	  return;
	}

	if (req_ev->req.method == SIP_METH_BYE &&
	    dlg->getStatus() != AmBasicSipDialog::Connected) {
	  DBG("not sip-relaying BYE in not connected dlg, b2b-relaying 200 OK\n");
          relayError(req_ev->req.method, req_ev->req.cseq,
		     true, 200, "OK");
	  return;
	}
      }

       if( (req_ev->req.method == SIP_METH_BYE)
	  // CANCEL is handled differently: other side has already 
	  // sent a terminate event.
	  //|| (req_ev->req.method == SIP_METH_CANCEL)
	  ) {
	
	 if (onOtherBye(req_ev->req))
	   req_ev->processed = true; // app should have relayed 200 to BYE
      }

      if(req_ev->forward && !req_ev->processed){
        int res = relaySip(req_ev->req);
	if(res < 0) {
	  // reply relayed request internally
          relayError(req_ev->req.method, req_ev->req.cseq, true, res);
	  return;
	}
      }
      
    }
    return;

  case B2BSipReply:
    {
      B2BSipReplyEvent* reply_ev = dynamic_cast<B2BSipReplyEvent*>(ev);
      assert(reply_ev);

      DBG("B2BSipReply: %i %s (fwd=%s)\n",reply_ev->reply.code,
	  reply_ev->reply.reason.c_str(),reply_ev->forward?"true":"false");
      DBG("B2BSipReply: content-type = %s\n",
	  reply_ev->reply.body.getCTStr().c_str());

      if(reply_ev->forward){

        std::map<int,AmSipRequest>::iterator t_req =
	  recvd_req.find(reply_ev->reply.cseq);

	if (t_req != recvd_req.end()) {
	  if ((reply_ev->reply.code >= 300) && (reply_ev->reply.code <= 305) &&
	      !reply_ev->reply.contact.empty()) {
	    // relay with Contact in 300 - 305 redirect messages
	    AmSipReply n_reply(reply_ev->reply);
	    n_reply.hdrs+=SIP_HDR_COLSP(SIP_HDR_CONTACT) +
	      reply_ev->reply.contact+ CRLF;

	    if(relaySip(t_req->second,n_reply) < 0) {
	      terminateOtherLeg();
	      terminateLeg();
	    }
	  } else {
	    // relay response
	    if(relaySip(t_req->second,reply_ev->reply) < 0) {
	      terminateOtherLeg();
	      terminateLeg();
	    }
	  }
		
	} else {
	  DBG("Cannot relay reply: request already replied"
	      " (code=%u;cseq=%u;call-id=%s)",
	      reply_ev->reply.code, reply_ev->reply.cseq,
	      reply_ev->reply.callid.c_str());
	}
      } else {
	// check whether not-forwarded (locally initiated)
	// INV/UPD transaction changed session in other leg
	if (SIP_IS_200_CLASS(reply_ev->reply.code) &&
	    (!reply_ev->reply.body.empty()) &&
	    (reply_ev->reply.cseq_method == SIP_METH_INVITE ||
	     reply_ev->reply.cseq_method == SIP_METH_UPDATE)) {
	  if (updateSessionDescription(reply_ev->reply.body)) {
	    if (reply_ev->reply.cseq != est_invite_cseq) {
	      if (dlg->getUACInvTransPending()) {
		DBG("changed session, but UAC INVITE trans pending\n");
		// todo(?): save until trans is finished?
		return;
	      }
	      DBG("session description changed - refreshing\n");
	      sendEstablishedReInvite();
	    } else {
	      DBG("reply to establishing INVITE request - not refreshing\n");
	    }
	  }
	}
      }
    }
    return;

  case B2BTerminateLeg:
    DBG("terminateLeg()\n");
    terminateLeg();
    break;
  }

  //ERROR("unknown event caught\n");
}