2004-12-24 18:15:34 +00:00
|
|
|
/**
|
|
|
|
* transaction.cpp
|
|
|
|
* Yet Another SIP Stack
|
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
|
|
*
|
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2005-04-29 22:05:07 +00:00
|
|
|
* Copyright (C) 2004, 2005 Null Team
|
2004-12-24 18:15:34 +00:00
|
|
|
*
|
|
|
|
* This program 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.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
2005-04-28 22:46:59 +00:00
|
|
|
#include <yatesip.h>
|
2004-12-24 18:15:34 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
|
|
|
|
SIPTransaction::SIPTransaction(SIPMessage* message, SIPEngine* engine, bool outgoing)
|
2005-09-19 22:13:22 +00:00
|
|
|
: m_outgoing(outgoing), m_invite(false), m_transmit(false), m_state(Invalid), m_response(0), m_timeout(0),
|
2004-12-31 01:09:21 +00:00
|
|
|
m_firstMessage(message), m_lastMessage(0), m_pending(0), m_engine(engine), m_private(0)
|
2004-12-24 18:15:34 +00:00
|
|
|
{
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugAll,"SIPTransaction::SIPTransaction(%p,%p,%d) [%p]",
|
2005-01-02 04:52:02 +00:00
|
|
|
message,engine,outgoing,this);
|
2004-12-28 05:15:11 +00:00
|
|
|
if (m_firstMessage) {
|
2004-12-24 18:15:34 +00:00
|
|
|
m_firstMessage->ref();
|
2005-01-06 15:32:16 +00:00
|
|
|
|
2004-12-28 06:24:03 +00:00
|
|
|
const NamedString* ns = message->getParam("Via","branch");
|
|
|
|
if (ns)
|
|
|
|
m_branch = *ns;
|
2004-12-28 05:15:11 +00:00
|
|
|
if (!m_branch.startsWith("z9hG4bK"))
|
|
|
|
m_branch.clear();
|
2004-12-31 01:09:21 +00:00
|
|
|
ns = message->getParam("To","tag");
|
|
|
|
if (ns)
|
|
|
|
m_tag = *ns;
|
2005-01-06 15:32:16 +00:00
|
|
|
|
2005-05-02 18:31:05 +00:00
|
|
|
const SIPHeaderLine* hl = message->getHeader("Call-ID");
|
2004-12-28 06:24:03 +00:00
|
|
|
if (hl)
|
|
|
|
m_callid = *hl;
|
2005-01-06 15:32:16 +00:00
|
|
|
|
2005-01-02 04:52:02 +00:00
|
|
|
if (!m_outgoing && m_firstMessage->getParty()) {
|
2005-01-06 15:32:16 +00:00
|
|
|
// adjust the address where we send the answers
|
|
|
|
hl = message->getHeader("Via");
|
2004-12-29 17:01:39 +00:00
|
|
|
if (hl) {
|
|
|
|
URI uri(*hl);
|
2005-01-06 15:32:16 +00:00
|
|
|
// skip over protocol/version/transport
|
|
|
|
uri >> "/" >> "/" >> " ";
|
|
|
|
uri.trimBlanks();
|
|
|
|
uri = "sip:" + uri;
|
2004-12-29 17:01:39 +00:00
|
|
|
m_firstMessage->getParty()->setParty(uri);
|
|
|
|
}
|
|
|
|
}
|
2004-12-28 05:15:11 +00:00
|
|
|
}
|
2004-12-24 18:15:34 +00:00
|
|
|
m_invite = (getMethod() == "INVITE");
|
2004-12-28 05:15:11 +00:00
|
|
|
m_engine->TransList.append(this);
|
2004-12-24 18:15:34 +00:00
|
|
|
m_state = Initial;
|
|
|
|
}
|
|
|
|
|
|
|
|
SIPTransaction::~SIPTransaction()
|
|
|
|
{
|
2005-05-06 18:13:33 +00:00
|
|
|
#ifdef DEBUG
|
2004-12-24 18:15:34 +00:00
|
|
|
Debugger debug(DebugAll,"SIPTransaction::~SIPTransaction()"," [%p]",this);
|
2005-05-06 18:13:33 +00:00
|
|
|
#endif
|
2004-12-24 18:15:34 +00:00
|
|
|
m_state = Invalid;
|
2004-12-28 05:15:11 +00:00
|
|
|
m_engine->TransList.remove(this,false);
|
2004-12-29 17:01:39 +00:00
|
|
|
setPendingEvent();
|
2004-12-24 18:15:34 +00:00
|
|
|
if (m_lastMessage)
|
|
|
|
m_lastMessage->deref();
|
|
|
|
m_lastMessage = 0;
|
|
|
|
if (m_firstMessage)
|
|
|
|
m_firstMessage->deref();
|
|
|
|
m_firstMessage = 0;
|
|
|
|
}
|
|
|
|
|
2005-04-22 20:07:26 +00:00
|
|
|
Mutex* SIPTransaction::mutex()
|
|
|
|
{
|
|
|
|
return m_engine ? m_engine->mutex() : 0;
|
|
|
|
}
|
|
|
|
|
2004-12-29 17:01:39 +00:00
|
|
|
const char* SIPTransaction::stateName(int state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case Invalid:
|
|
|
|
return "Invalid";
|
|
|
|
case Initial:
|
|
|
|
return "Initial";
|
|
|
|
case Trying:
|
|
|
|
return "Trying";
|
|
|
|
case Process:
|
|
|
|
return "Process";
|
|
|
|
case Retrans:
|
|
|
|
return "Retrans";
|
|
|
|
case Finish:
|
|
|
|
return "Finish";
|
|
|
|
case Cleared:
|
|
|
|
return "Cleared";
|
|
|
|
default:
|
|
|
|
return "Undefined";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-24 18:15:34 +00:00
|
|
|
bool SIPTransaction::changeState(int newstate)
|
|
|
|
{
|
|
|
|
if ((newstate < 0) || (newstate == m_state))
|
|
|
|
return false;
|
2004-12-28 05:15:11 +00:00
|
|
|
if (m_state == Invalid) {
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(DebugGoOn,"SIPTransaction is already invalid [%p]",this);
|
2004-12-28 05:15:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugAll,"SIPTransaction state changed from %s to %s [%p]",
|
2004-12-29 17:01:39 +00:00
|
|
|
stateName(m_state),stateName(newstate),this);
|
2004-12-24 18:15:34 +00:00
|
|
|
m_state = newstate;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-09-13 16:11:30 +00:00
|
|
|
void SIPTransaction::setDialogTag(const char* tag)
|
|
|
|
{
|
|
|
|
if (null(tag)) {
|
|
|
|
if (m_tag.null())
|
|
|
|
m_tag = (int)::random();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_tag = tag;
|
|
|
|
}
|
|
|
|
|
2004-12-24 18:15:34 +00:00
|
|
|
void SIPTransaction::setLatestMessage(SIPMessage* message)
|
|
|
|
{
|
|
|
|
if (m_lastMessage == message)
|
|
|
|
return;
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugAll,"SIPTransaction latest message changing from %p %d to %p %d [%p]",
|
2005-01-12 13:26:08 +00:00
|
|
|
m_lastMessage, m_lastMessage ? m_lastMessage->code : 0,
|
|
|
|
message, message ? message->code : 0, this);
|
2004-12-24 18:15:34 +00:00
|
|
|
if (m_lastMessage)
|
|
|
|
m_lastMessage->deref();
|
|
|
|
m_lastMessage = message;
|
2004-12-28 05:47:43 +00:00
|
|
|
if (m_lastMessage) {
|
2004-12-24 18:15:34 +00:00
|
|
|
m_lastMessage->ref();
|
2005-09-19 22:13:22 +00:00
|
|
|
if (message->isAnswer()) {
|
|
|
|
m_response = message->code;
|
|
|
|
if (m_response > 100)
|
|
|
|
setDialogTag();
|
|
|
|
}
|
2004-12-31 01:09:21 +00:00
|
|
|
message->complete(m_engine,0,0,m_tag);
|
2004-12-28 05:47:43 +00:00
|
|
|
}
|
2004-12-24 18:15:34 +00:00
|
|
|
}
|
|
|
|
|
2004-12-29 17:01:39 +00:00
|
|
|
void SIPTransaction::setPendingEvent(SIPEvent* event, bool replace)
|
|
|
|
{
|
|
|
|
if (m_pending)
|
|
|
|
if (replace) {
|
|
|
|
delete m_pending;
|
|
|
|
m_pending = event;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
delete event;
|
|
|
|
else
|
|
|
|
m_pending = event;
|
|
|
|
}
|
|
|
|
|
2005-04-02 00:49:38 +00:00
|
|
|
void SIPTransaction::setTimeout(u_int64_t delay, unsigned int count)
|
2004-12-24 18:15:34 +00:00
|
|
|
{
|
|
|
|
m_timeouts = count;
|
|
|
|
m_delay = delay;
|
|
|
|
m_timeout = (count && delay) ? Time::now() + delay : 0;
|
2005-05-06 18:13:33 +00:00
|
|
|
#ifdef DEBUG
|
2005-01-17 12:46:24 +00:00
|
|
|
if (m_timeout)
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(DebugAll,"SIPTransaction new %d timeouts initially " FMT64U " usec apart [%p]",
|
2005-01-17 12:46:24 +00:00
|
|
|
m_timeouts,m_delay,this);
|
2005-05-06 18:13:33 +00:00
|
|
|
#endif
|
2004-12-24 18:15:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SIPEvent* SIPTransaction::getEvent()
|
|
|
|
{
|
2004-12-29 17:01:39 +00:00
|
|
|
SIPEvent *e = 0;
|
|
|
|
|
|
|
|
if (m_pending) {
|
|
|
|
e = m_pending;
|
|
|
|
m_pending = 0;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2004-12-28 05:15:11 +00:00
|
|
|
if (m_transmit) {
|
|
|
|
m_transmit = false;
|
2004-12-29 04:02:55 +00:00
|
|
|
return new SIPEvent(m_lastMessage ? m_lastMessage : m_firstMessage,this);
|
2004-12-28 05:15:11 +00:00
|
|
|
}
|
2004-12-29 04:02:55 +00:00
|
|
|
|
2004-12-24 18:15:34 +00:00
|
|
|
int timeout = -1;
|
2004-12-29 17:01:39 +00:00
|
|
|
if (m_timeout && (Time::now() >= m_timeout)) {
|
2004-12-24 18:15:34 +00:00
|
|
|
timeout = --m_timeouts;
|
|
|
|
m_timeout = (m_timeouts) ? Time::now() + m_delay : 0;
|
2005-01-01 22:21:32 +00:00
|
|
|
m_delay *= 2; // exponential back-off
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugAll,"SIPTransaction fired timer #%d [%p]",timeout,this);
|
2004-12-24 18:15:34 +00:00
|
|
|
}
|
2004-12-29 04:02:55 +00:00
|
|
|
|
2004-12-29 17:01:39 +00:00
|
|
|
e = isOutgoing() ? getClientEvent(m_state,timeout) : getServerEvent(m_state,timeout);
|
2004-12-24 18:15:34 +00:00
|
|
|
if (e)
|
|
|
|
return e;
|
2004-12-29 04:02:55 +00:00
|
|
|
|
2004-12-29 17:01:39 +00:00
|
|
|
// do some common default processing
|
2004-12-24 18:15:34 +00:00
|
|
|
switch (m_state) {
|
|
|
|
case Retrans:
|
2004-12-28 05:15:11 +00:00
|
|
|
if (timeout < 0)
|
2004-12-24 18:15:34 +00:00
|
|
|
break;
|
2004-12-28 05:15:11 +00:00
|
|
|
if (timeout && m_lastMessage)
|
|
|
|
e = new SIPEvent(m_lastMessage,this);
|
2004-12-29 17:01:39 +00:00
|
|
|
if (timeout)
|
|
|
|
break;
|
|
|
|
changeState(Cleared);
|
|
|
|
// fall trough so we don't wait another turn for processing
|
2004-12-24 18:15:34 +00:00
|
|
|
case Cleared:
|
2004-12-28 05:15:11 +00:00
|
|
|
setTimeout();
|
2004-12-24 18:15:34 +00:00
|
|
|
e = new SIPEvent(m_firstMessage,this);
|
|
|
|
// make sure we don't get trough this one again
|
|
|
|
changeState(Invalid);
|
|
|
|
// remove from list and dereference
|
2004-12-28 05:15:11 +00:00
|
|
|
m_engine->TransList.remove(this);
|
2004-12-24 18:15:34 +00:00
|
|
|
return e;
|
|
|
|
case Invalid:
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(DebugFail,"SIPTransaction::getEvent in invalid state [%p]",this);
|
2004-12-24 18:15:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
2004-12-28 05:15:11 +00:00
|
|
|
|
2004-12-28 05:47:43 +00:00
|
|
|
void SIPTransaction::setResponse(SIPMessage* message)
|
2004-12-28 05:15:11 +00:00
|
|
|
{
|
2004-12-29 17:01:39 +00:00
|
|
|
if (m_outgoing) {
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(DebugWarn,"SIPTransaction::setResponse(%p) in client mode [%p]",message,this);
|
2004-12-29 17:01:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-04-22 20:07:26 +00:00
|
|
|
Lock lock(mutex());
|
2004-12-28 05:47:43 +00:00
|
|
|
setLatestMessage(message);
|
2004-12-29 17:01:39 +00:00
|
|
|
setTransmit();
|
|
|
|
if (message && (message->code >= 200)) {
|
|
|
|
if (isInvite()) {
|
|
|
|
if (changeState(Finish))
|
|
|
|
setTimeout();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setTimeout();
|
|
|
|
changeState(Cleared);
|
|
|
|
}
|
|
|
|
}
|
2005-09-19 22:13:22 +00:00
|
|
|
// extend timeout for provisional messages, use proxy timeout (maximum)
|
2005-01-17 12:46:24 +00:00
|
|
|
else if (message && (message->code > 100))
|
2005-09-19 22:13:22 +00:00
|
|
|
setTimeout(m_engine->getTimer('C'));
|
2004-12-28 05:15:11 +00:00
|
|
|
}
|
2004-12-28 05:47:43 +00:00
|
|
|
|
2005-07-16 23:55:05 +00:00
|
|
|
bool SIPTransaction::setResponse(int code, const char* reason)
|
2004-12-28 05:47:43 +00:00
|
|
|
{
|
2004-12-29 17:01:39 +00:00
|
|
|
if (m_outgoing) {
|
2005-05-06 18:13:33 +00:00
|
|
|
Debug(DebugWarn,"SIPTransaction::setResponse(%d,'%s') in client mode [%p]",code,reason,this);
|
2005-07-16 23:55:05 +00:00
|
|
|
return false;
|
2004-12-29 17:01:39 +00:00
|
|
|
}
|
2004-12-31 01:09:21 +00:00
|
|
|
switch (m_state) {
|
|
|
|
case Invalid:
|
|
|
|
case Retrans:
|
|
|
|
case Finish:
|
|
|
|
case Cleared:
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugInfo,"SIPTransaction ignoring setResponse(%d) in state %s [%p]",
|
2004-12-31 01:09:21 +00:00
|
|
|
code,stateName(m_state),this);
|
2005-07-16 23:55:05 +00:00
|
|
|
return false;
|
2004-12-31 01:09:21 +00:00
|
|
|
}
|
2005-04-29 21:07:41 +00:00
|
|
|
if (!reason)
|
|
|
|
reason = lookup(code,SIPResponses,"Unknown Reason Code");
|
2004-12-28 14:50:10 +00:00
|
|
|
SIPMessage* msg = new SIPMessage(m_firstMessage, code, reason);
|
|
|
|
setResponse(msg);
|
|
|
|
msg->deref();
|
2005-07-16 23:55:05 +00:00
|
|
|
return true;
|
2005-06-03 12:23:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SIPTransaction::requestAuth(const String& realm, const String& domain, bool stale, bool proxy)
|
|
|
|
{
|
|
|
|
if (m_outgoing) {
|
|
|
|
Debug(DebugWarn,"SIPTransaction::requestAuth() in client mode [%p]",this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (m_state) {
|
|
|
|
case Invalid:
|
|
|
|
case Retrans:
|
|
|
|
case Finish:
|
|
|
|
case Cleared:
|
|
|
|
DDebug(DebugInfo,"SIPTransaction ignoring requestAuth() in state %s [%p]",
|
|
|
|
stateName(m_state),this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int code = proxy ? 407 : 401;
|
|
|
|
const char* hdr = proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
|
|
|
|
SIPMessage* msg = new SIPMessage(m_firstMessage, code, lookup(code,SIPResponses));
|
|
|
|
if (realm) {
|
|
|
|
String tmp;
|
|
|
|
tmp << "Digest realm=\"" << realm << "\"";
|
|
|
|
SIPHeaderLine* line = new SIPHeaderLine(hdr,tmp,',');
|
|
|
|
if (domain)
|
|
|
|
line->setParam(" domain","\"" + domain + "\"");
|
|
|
|
m_engine->nonceGet(tmp);
|
|
|
|
line->setParam(" nonce","\"" + tmp + "\"");
|
|
|
|
line->setParam(" stale",stale ? "TRUE" : "FALSE");
|
|
|
|
line->setParam(" algorithm","MD5");
|
|
|
|
msg->addHeader(line);
|
|
|
|
}
|
|
|
|
setResponse(msg);
|
|
|
|
msg->deref();
|
|
|
|
}
|
|
|
|
|
|
|
|
int SIPTransaction::authUser(String& user, bool proxy)
|
|
|
|
{
|
|
|
|
if (!(m_engine && m_firstMessage))
|
|
|
|
return -1;
|
|
|
|
return m_engine->authUser(m_firstMessage, user, proxy);
|
2004-12-28 05:47:43 +00:00
|
|
|
}
|
|
|
|
|
2004-12-28 05:15:11 +00:00
|
|
|
bool SIPTransaction::processMessage(SIPMessage* message, const String& branch)
|
2004-12-24 18:15:34 +00:00
|
|
|
{
|
2005-01-27 21:26:00 +00:00
|
|
|
if (!(message && m_firstMessage))
|
2005-01-02 04:52:02 +00:00
|
|
|
return false;
|
2004-12-31 01:09:21 +00:00
|
|
|
DDebug("SIPTransaction",DebugAll,"processMessage(%p,'%s') [%p]",
|
2004-12-28 05:15:11 +00:00
|
|
|
message,branch.c_str(),this);
|
|
|
|
if (branch) {
|
2005-01-01 22:21:32 +00:00
|
|
|
if (branch != m_branch) {
|
|
|
|
// different branch is allowed only for ACK in incoming INVITE...
|
|
|
|
if (!(isInvite() && isIncoming() && message->isACK()))
|
|
|
|
return false;
|
|
|
|
// ...and only if we sent a 200 response...
|
|
|
|
if (!m_lastMessage || ((m_lastMessage->code / 100) != 2))
|
|
|
|
return false;
|
|
|
|
// ...and if also matches the CSeq, Call-ID and To: tag
|
|
|
|
if ((m_firstMessage->getCSeq() != message->getCSeq()) ||
|
|
|
|
(getCallID() != message->getHeaderValue("Call-ID")) ||
|
2005-01-04 02:22:43 +00:00
|
|
|
(getDialogTag() != message->getParamValue("To","tag")))
|
2005-01-01 22:21:32 +00:00
|
|
|
return false;
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugAll,"SIPTransaction found non-branch ACK response to our 2xx");
|
2005-01-01 22:21:32 +00:00
|
|
|
}
|
|
|
|
else if (getMethod() != message->method) {
|
|
|
|
if (!(isIncoming() && isInvite() && message->isACK()))
|
2004-12-28 05:15:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2005-01-27 21:26:00 +00:00
|
|
|
if (getMethod() != message->method) {
|
|
|
|
if (!(isIncoming() && isInvite() && message->isACK()))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((m_firstMessage->getCSeq() != message->getCSeq()) ||
|
|
|
|
(getCallID() != message->getHeaderValue("Call-ID")) ||
|
|
|
|
(m_firstMessage->getHeaderValue("From") != message->getHeaderValue("From")) ||
|
2005-09-13 16:11:30 +00:00
|
|
|
(m_firstMessage->getHeaderValue("To") != message->getHeaderValue("To")))
|
2005-07-01 23:02:49 +00:00
|
|
|
return false;
|
2005-09-13 16:11:30 +00:00
|
|
|
// allow braindamaged UAs that send answers with no Via line
|
|
|
|
if (m_firstMessage->getHeader("Via") && message->getHeader("Via") &&
|
|
|
|
(m_firstMessage->getHeaderValue("Via") != message->getHeaderValue("Via")))
|
2005-01-27 21:26:00 +00:00
|
|
|
return false;
|
2005-09-13 16:11:30 +00:00
|
|
|
// extra checks are to be made for ACK only
|
|
|
|
if (message->isACK()) {
|
|
|
|
if (getURI() != message->uri)
|
|
|
|
return false;
|
|
|
|
if (getDialogTag() != message->getParamValue("To","tag"))
|
|
|
|
return false;
|
|
|
|
}
|
2004-12-28 05:15:11 +00:00
|
|
|
}
|
2005-01-12 13:58:52 +00:00
|
|
|
if (isOutgoing() != message->isAnswer()) {
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugAll,"SIPTransaction ignoring retransmitted %s %p '%s' in [%p]",
|
2005-01-12 13:58:52 +00:00
|
|
|
message->isAnswer() ? "answer" : "request",
|
|
|
|
message,message->method.c_str(),this);
|
|
|
|
return false;
|
|
|
|
}
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugAll,"SIPTransaction processing %s %p '%s' in [%p]",
|
2004-12-31 01:09:21 +00:00
|
|
|
message->isAnswer() ? "answer" : "request",
|
|
|
|
message,message->method.c_str(),this);
|
|
|
|
|
|
|
|
if (m_tag.null() && message->isAnswer()) {
|
|
|
|
const NamedString* ns = message->getParam("To","tag");
|
|
|
|
if (ns) {
|
|
|
|
m_tag = *ns;
|
2005-05-06 18:13:33 +00:00
|
|
|
DDebug(DebugInfo,"SIPTransaction found dialog tag '%s' [%p]",
|
2004-12-31 01:09:21 +00:00
|
|
|
m_tag.c_str(),this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-29 04:02:55 +00:00
|
|
|
if (isOutgoing())
|
|
|
|
processClientMessage(message,m_state);
|
|
|
|
else
|
|
|
|
processServerMessage(message,m_state);
|
2004-12-28 05:15:11 +00:00
|
|
|
return true;
|
2004-12-24 18:15:34 +00:00
|
|
|
}
|
|
|
|
|
2004-12-29 04:02:55 +00:00
|
|
|
void SIPTransaction::processClientMessage(SIPMessage* message, int state)
|
|
|
|
{
|
2004-12-29 17:01:39 +00:00
|
|
|
switch (state) {
|
|
|
|
case Trying:
|
2005-01-03 03:02:11 +00:00
|
|
|
setTimeout(m_engine->getTimer(isInvite() ? 'B' : 'F'));
|
|
|
|
changeState(Process);
|
2005-09-19 22:13:22 +00:00
|
|
|
m_response = message->code;
|
|
|
|
if (m_response == 100)
|
2005-01-03 03:02:11 +00:00
|
|
|
break;
|
|
|
|
// fall trough for non-100 answers
|
2004-12-29 17:01:39 +00:00
|
|
|
case Process:
|
2005-09-19 22:13:22 +00:00
|
|
|
if (message->code <= 100)
|
|
|
|
break;
|
|
|
|
if (m_invite && (m_response <= 100))
|
|
|
|
// use the human interaction timeout in INVITEs
|
|
|
|
setTimeout(m_engine->getUserTimeout());
|
|
|
|
m_response = message->code;
|
|
|
|
setPendingEvent(new SIPEvent(message,this));
|
|
|
|
if (m_response >= 200) {
|
2004-12-29 17:01:39 +00:00
|
|
|
setTimeout();
|
2005-01-03 03:02:11 +00:00
|
|
|
if (isInvite()) {
|
2005-07-01 20:31:43 +00:00
|
|
|
// build the ACK
|
|
|
|
setLatestMessage(new SIPMessage(m_firstMessage,message));
|
2005-01-03 03:02:11 +00:00
|
|
|
m_lastMessage->deref();
|
|
|
|
setTransmit();
|
|
|
|
if (changeState(Retrans))
|
|
|
|
setTimeout(m_engine->getTimer('I'));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
changeState(Cleared);
|
2004-12-29 17:01:39 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Retrans:
|
|
|
|
if (m_lastMessage && m_lastMessage->isACK())
|
|
|
|
setTransmit();
|
|
|
|
break;
|
|
|
|
}
|
2004-12-29 04:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SIPEvent* SIPTransaction::getClientEvent(int state, int timeout)
|
|
|
|
{
|
|
|
|
SIPEvent *e = 0;
|
|
|
|
switch (state) {
|
|
|
|
case Initial:
|
2004-12-29 17:01:39 +00:00
|
|
|
e = new SIPEvent(m_firstMessage,this);
|
|
|
|
if (changeState(Trying))
|
2005-01-01 22:21:32 +00:00
|
|
|
setTimeout(m_engine->getTimer(isInvite() ? 'A' : 'E'),5);
|
2004-12-29 17:01:39 +00:00
|
|
|
break;
|
|
|
|
case Trying:
|
|
|
|
if (timeout < 0)
|
|
|
|
break;
|
|
|
|
if (timeout)
|
|
|
|
setTransmit();
|
2005-09-19 22:13:22 +00:00
|
|
|
else {
|
|
|
|
m_response = 408;
|
2004-12-29 17:01:39 +00:00
|
|
|
changeState(Cleared);
|
2005-09-19 22:13:22 +00:00
|
|
|
}
|
2004-12-29 17:01:39 +00:00
|
|
|
break;
|
2005-01-03 03:02:11 +00:00
|
|
|
case Process:
|
2005-09-19 22:13:22 +00:00
|
|
|
if (timeout == 0) {
|
|
|
|
m_response = 408;
|
2004-12-29 17:01:39 +00:00
|
|
|
changeState(Cleared);
|
2005-09-19 22:13:22 +00:00
|
|
|
}
|
2005-01-03 03:02:11 +00:00
|
|
|
break;
|
|
|
|
case Finish:
|
|
|
|
setTimeout();
|
|
|
|
changeState(Cleared);
|
2004-12-29 04:02:55 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2004-12-29 17:01:39 +00:00
|
|
|
void SIPTransaction::processServerMessage(SIPMessage* message, int state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case Trying:
|
|
|
|
case Process:
|
|
|
|
setTransmit();
|
|
|
|
break;
|
|
|
|
case Finish:
|
|
|
|
case Retrans:
|
|
|
|
if (message->isACK()) {
|
|
|
|
setTimeout();
|
2005-01-01 22:21:32 +00:00
|
|
|
setPendingEvent(new SIPEvent(message,this));
|
2004-12-29 17:01:39 +00:00
|
|
|
changeState(Cleared);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
setTransmit();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-29 04:02:55 +00:00
|
|
|
SIPEvent* SIPTransaction::getServerEvent(int state, int timeout)
|
2004-12-24 18:15:34 +00:00
|
|
|
{
|
|
|
|
SIPEvent *e = 0;
|
2004-12-28 05:15:11 +00:00
|
|
|
switch (state) {
|
|
|
|
case Initial:
|
2004-12-29 17:01:39 +00:00
|
|
|
if (m_engine->isAllowed(m_firstMessage->method)) {
|
2005-04-29 21:07:41 +00:00
|
|
|
setResponse(100);
|
2004-12-28 05:15:11 +00:00
|
|
|
changeState(Trying);
|
|
|
|
}
|
2005-01-02 04:52:02 +00:00
|
|
|
else {
|
2005-09-02 16:39:00 +00:00
|
|
|
setResponse(501);
|
2005-01-02 04:52:02 +00:00
|
|
|
e = new SIPEvent(m_lastMessage,this);
|
|
|
|
m_transmit = false;
|
|
|
|
changeState(Invalid);
|
|
|
|
// remove from list and dereference
|
|
|
|
m_engine->TransList.remove(this);
|
|
|
|
}
|
2004-12-29 17:01:39 +00:00
|
|
|
break;
|
|
|
|
case Trying:
|
|
|
|
e = new SIPEvent(m_firstMessage,this);
|
|
|
|
changeState(Process);
|
2005-09-19 22:13:22 +00:00
|
|
|
// the absolute maximum timeout as we have to accomodate proxies
|
|
|
|
setTimeout(m_engine->getTimer('C'));
|
2004-12-29 17:01:39 +00:00
|
|
|
break;
|
|
|
|
case Process:
|
|
|
|
if (timeout < 0)
|
|
|
|
break;
|
|
|
|
if (timeout && m_lastMessage)
|
2004-12-28 06:24:03 +00:00
|
|
|
e = new SIPEvent(m_lastMessage,this);
|
2004-12-29 17:01:39 +00:00
|
|
|
if (timeout)
|
|
|
|
break;
|
2005-04-29 21:07:41 +00:00
|
|
|
setResponse(408);
|
2004-12-29 17:01:39 +00:00
|
|
|
break;
|
|
|
|
case Finish:
|
|
|
|
e = new SIPEvent(m_lastMessage,this);
|
2005-01-01 22:21:32 +00:00
|
|
|
setTimeout(m_engine->getTimer('G'),5);
|
2004-12-29 17:01:39 +00:00
|
|
|
changeState(Retrans);
|
2004-12-28 05:15:11 +00:00
|
|
|
break;
|
|
|
|
}
|
2004-12-24 18:15:34 +00:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|