From 16b6c1638756f38366a78cae2b7a1bb604307568 Mon Sep 17 00:00:00 2001 From: paulc Date: Tue, 4 Jan 2005 02:22:43 +0000 Subject: [PATCH] More assignment operators for String. RTP pass-trough for SIP. git-svn-id: http://yate.null.ro/svn/yate/trunk@155 acf43c95-373e-0410-b603-e72c3f656dc1 --- contrib/ysip/message.cpp | 95 +++++++++++++++++++++++++ contrib/ysip/transaction.cpp | 2 +- contrib/ysip/yatesip.h | 38 +++++++++- engine/String.cpp | 10 +++ modules/ysipchan.cpp | 133 +++++++++++++++++++++++++++++------ yatengine.h | 13 ++++ 6 files changed, 264 insertions(+), 27 deletions(-) diff --git a/contrib/ysip/message.cpp b/contrib/ysip/message.cpp index 9c86ada7..40243c7f 100644 --- a/contrib/ysip/message.cpp +++ b/contrib/ysip/message.cpp @@ -578,4 +578,99 @@ void SIPMessage::setParty(SIPParty* ep) m_ep->ref(); } +SIPDialog::SIPDialog() +{ +} + +SIPDialog::SIPDialog(const SIPDialog& original) + : String(original), + localURI(original.localURI), localTag(original.localTag), + remoteURI(original.remoteURI), remoteTag(original.remoteTag) +{ + Debug("SIPDialog",DebugAll,"callid '%s' local '%s;tag=%s' remote '%s;tag=%s' [%p]", + c_str(),localURI.c_str(),localTag.c_str(),remoteURI.c_str(),remoteTag.c_str(),this); +} + +SIPDialog& SIPDialog::operator=(const SIPDialog& original) +{ + String::operator=(original); + localURI = original.localURI; + localTag = original.localTag; + remoteURI = original.remoteURI; + remoteTag = original.remoteTag; + Debug("SIPDialog",DebugAll,"callid '%s' local '%s;tag=%s' remote '%s;tag=%s' [%p]", + c_str(),localURI.c_str(),localTag.c_str(),remoteURI.c_str(),remoteTag.c_str(),this); + return *this; +} + +SIPDialog& SIPDialog::operator=(const String& callid) +{ + String::operator=(callid); + localURI.clear(); + localTag.clear(); + remoteURI.clear(); + remoteTag.clear(); + Debug("SIPDialog",DebugAll,"callid '%s' local '%s;tag=%s' remote '%s;tag=%s' [%p]", + c_str(),localURI.c_str(),localTag.c_str(),remoteURI.c_str(),remoteTag.c_str(),this); + return *this; +} + +SIPDialog::SIPDialog(const SIPMessage& message) + : String(message.getHeaderValue("Call-ID")) +{ + Regexp r("<\\([^>]\\+\\)>"); + bool local = message.isOutgoing() ^ message.isAnswer(); + const HeaderLine* hl = message.getHeader(local ? "From" : "To"); + localURI = hl; + if (localURI.matches(r)) + localURI = localURI.matchString(1); + if (hl) + localTag = hl->getParam("tag"); + hl = message.getHeader(local ? "To" : "From"); + remoteURI = hl; + if (remoteURI.matches(r)) + remoteURI = remoteURI.matchString(1); + if (hl) + remoteTag = hl->getParam("tag"); + Debug("SIPDialog",DebugAll,"callid '%s' local '%s;tag=%s' remote '%s;tag=%s' [%p]", + c_str(),localURI.c_str(),localTag.c_str(),remoteURI.c_str(),remoteTag.c_str(),this); +} + +SIPDialog& SIPDialog::operator=(const SIPMessage& message) +{ + String::operator=(message.getHeaderValue("Call-ID")); + Regexp r("<\\([^>]\\+\\)>"); + bool local = message.isOutgoing() ^ message.isAnswer(); + const HeaderLine* hl = message.getHeader(local ? "From" : "To"); + localURI = hl; + if (localURI.matches(r)) + localURI = localURI.matchString(1); + if (hl) + localTag = hl->getParam("tag"); + hl = message.getHeader(local ? "To" : "From"); + remoteURI = hl; + if (remoteURI.matches(r)) + remoteURI = remoteURI.matchString(1); + if (hl) + remoteTag = hl->getParam("tag"); + Debug("SIPDialog",DebugAll,"callid '%s' local '%s;tag=%s' remote '%s;tag=%s' [%p]", + c_str(),localURI.c_str(),localTag.c_str(),remoteURI.c_str(),remoteTag.c_str(),this); + return *this; +} + +bool SIPDialog::operator==(const SIPDialog& other) const +{ + return + String::operator==(other) && + localURI == other.localURI && + localTag == other.localTag && + remoteURI == other.remoteURI && + remoteTag == other.remoteTag; +} + +bool SIPDialog::operator!=(const SIPDialog& other) const +{ + return !operator==(other); +} + /* vi: set ts=8 sw=4 sts=4 noet: */ diff --git a/contrib/ysip/transaction.cpp b/contrib/ysip/transaction.cpp index a1ba9b2f..85d45a0b 100644 --- a/contrib/ysip/transaction.cpp +++ b/contrib/ysip/transaction.cpp @@ -259,7 +259,7 @@ bool SIPTransaction::processMessage(SIPMessage* message, const String& branch) // ...and if also matches the CSeq, Call-ID and To: tag if ((m_firstMessage->getCSeq() != message->getCSeq()) || (getCallID() != message->getHeaderValue("Call-ID")) || - (getLocalTag() != message->getParamValue("To","tag"))) + (getDialogTag() != message->getParamValue("To","tag"))) return false; Debug("SIPTransaction",DebugWarn,"Found non-branch ACK response to our 2xx"); } diff --git a/contrib/ysip/yatesip.h b/contrib/ysip/yatesip.h index c5b0c5b5..fcaf6792 100644 --- a/contrib/ysip/yatesip.h +++ b/contrib/ysip/yatesip.h @@ -330,6 +330,12 @@ public: inline void addHeader(const char* name, const char* value = 0) { header.append(new HeaderLine(name,value)); } + /** + * Append an already constructed header line + */ + inline void addHeader(HeaderLine* line) + { header.append(line); } + /** * Creates a binary buffer from a SIPMessage. */ @@ -394,6 +400,26 @@ protected: mutable DataBlock m_data; }; +/** + * A class to store information required to identify a dialog + */ +class SIPDialog : public String +{ +public: + SIPDialog(); + SIPDialog(const SIPDialog& original); + SIPDialog(const SIPMessage& message); + SIPDialog& operator=(const SIPDialog& original); + SIPDialog& operator=(const SIPMessage& message); + SIPDialog& operator=(const String& callid); + bool operator==(const SIPDialog& other) const; + bool operator!=(const SIPDialog& other) const; + String localURI; + String localTag; + String remoteURI; + String remoteTag; +}; + /** * All informaton related to a SIP transaction, starting with 1st message */ @@ -473,6 +499,12 @@ public: inline const SIPMessage* latestMessage() const { return m_lastMessage; } + /** + * The most recent message handled by this transaction + */ + inline const SIPMessage* recentMessage() const + { return m_lastMessage ? m_lastMessage : m_firstMessage; } + /** * The SIPEngine this transaction belongs to */ @@ -534,10 +566,10 @@ public: { return m_callid; } /** - * The local tag that may identify this transaction - * @return The local tag parameter + * The dialog tag that may identify this transaction + * @return The dialog tag parameter */ - inline const String& getLocalTag() const + inline const String& getDialogTag() const { return m_tag; } /** diff --git a/engine/String.cpp b/engine/String.cpp index b56233ef..5e59b8e5 100644 --- a/engine/String.cpp +++ b/engine/String.cpp @@ -209,6 +209,16 @@ String::String(bool value) changed(); } +String::String(const String *value) + : m_string(0), m_length(0), m_hash(INIT_HASH), m_matches(0) +{ + DDebug(DebugAll,"String::String(%p) [%p]",&value,this); + if (value && !value->null()) { + m_string = ::strdup(value->c_str()); + changed(); + } +} + String::~String() { DDebug(DebugAll,"String::~String() [%p] (\"%s\")",this,m_string); diff --git a/modules/ysipchan.cpp b/modules/ysipchan.cpp index 4093603c..014a5018 100644 --- a/modules/ysipchan.cpp +++ b/modules/ysipchan.cpp @@ -144,6 +144,12 @@ private: class YateSIPConnection : public DataEndpoint { public: + enum { + Incoming, + Outgoing, + Established, + Cleared, + }; YateSIPConnection(Message& msg, SIPTransaction* tr); YateSIPConnection(Message& msg, const String& uri); ~YateSIPConnection(); @@ -160,8 +166,8 @@ public: { return "sip/" + m_id; } inline const String& status() const { return m_status; } - inline void setStatus(const char *status) - { m_status = status; } + inline void setStatus(const char *status, int state = -1) + { m_status = status; if (state >= 0) m_state = state; } inline void setTarget(const char *target = 0) { m_target = target; } inline const String& getTarget() const @@ -170,13 +176,17 @@ public: { return m_tr; } static YateSIPConnection* find(const String& id); private: + void clearTransaction(); SDPBody* createSDP(const char* addr, const char* port, const char* formats, const char* format = 0); SDPBody* createPasstroughSDP(Message &msg); SDPBody* createRtpSDP(SIPMessage* msg, const char* formats); SDPBody* createRtpSDP(bool start = false); bool startRtp(); SIPTransaction* m_tr; - String m_id; + bool m_hungup; + int m_state; + SIPDialog m_id; + String m_uri; String m_target; String m_status; String m_rtpid; @@ -557,18 +567,20 @@ int SipMsgThread::s_routed = 0; YateSIPConnection* YateSIPConnection::find(const String& id) { + Debug("YateSIPConnection",DebugAll,"finding '%s'",id.c_str()); ObjList* l = s_calls.find(id); return l ? static_cast(l->get()) : 0; } // Incoming call constructor - after call.route but before call.execute YateSIPConnection::YateSIPConnection(Message& msg, SIPTransaction* tr) - : m_tr(tr) + : m_tr(tr), m_hungup(false), m_state(Incoming) { Debug(DebugAll,"YateSIPConnection::YateSIPConnection(%p) [%p]",tr,this); s_mutex.lock(); m_tr->ref(); - m_id = m_tr->getCallID(); + m_id = *m_tr->initialMessage(); + m_uri = m_tr->initialMessage()->getHeader("From"); m_tr->setUserData(this); s_calls.append(this); s_mutex.unlock(); @@ -583,13 +595,12 @@ YateSIPConnection::YateSIPConnection(Message& msg, SIPTransaction* tr) // Outgoing call constructor - in call.execute handler YateSIPConnection::YateSIPConnection(Message& msg, const String& uri) - : m_tr(0) + : m_tr(0), m_hungup(false), m_state(Outgoing), m_uri(uri) { Debug(DebugAll,"YateSIPConnection::YateSIPConnection(%p,'%s') [%p]", &msg,uri.c_str(),this); SIPMessage* m = new SIPMessage("INVITE",uri); plugin.ep()->buildParty(m); -// m->complete(plugin.ep()->engine()); SDPBody* sdp = createPasstroughSDP(msg); if (!sdp) sdp = createRtpSDP(m,msg.getValue("formats")); @@ -598,7 +609,7 @@ YateSIPConnection::YateSIPConnection(Message& msg, const String& uri) m->deref(); if (m_tr) { m_tr->ref(); - m_id = m_tr->getCallID(); + m_id = *m_tr->initialMessage(); m_tr->setUserData(this); } Lock lock(s_mutex); @@ -611,23 +622,78 @@ YateSIPConnection::~YateSIPConnection() Lock lock(s_mutex); s_calls.remove(this,false); hangup(); + clearTransaction(); +} + +void YateSIPConnection::clearTransaction() +{ + if (m_tr) { + m_tr->setUserData(0); + if (m_tr->isIncoming()) + m_tr->setResponse(487,"Request Terminated"); + m_tr->deref(); + m_tr = 0; + } } void YateSIPConnection::hangup() { - Message *m = new Message("call.hangup"); - m->addParam("driver","sip"); - m->addParam("id",id()); + if (m_hungup) + return; + m_hungup = true; + Message *msg = new Message("call.hangup"); + msg->addParam("driver","sip"); + msg->addParam("id",id()); if (m_target) - m->addParam("targetid",m_target); - Engine::enqueue(m); - if (m_tr) { - m_tr->setUserData(0); - m_tr->setResponse(487,"Request Terminated"); - m_tr->deref(); + msg->addParam("targetid",m_target); + Engine::enqueue(msg); + msg = 0; + switch (m_state) { + case Cleared: + clearTransaction(); + return; + case Incoming: + if (m_tr) { + clearTransaction(); + return; + } + break; + case Outgoing: + if (m_tr) { + SIPMessage* m = new SIPMessage("CANCEL",m_uri); + const SIPMessage* i = m_tr->initialMessage(); + m->copyHeader(i,"Via"); + m->copyHeader(i,"From"); + m->copyHeader(i,"To"); + m->copyHeader(i,"Call-ID"); + String tmp; + tmp << i->getCSeq() << " CANCEL"; + m->addHeader("CSeq",tmp); + plugin.ep()->buildParty(m); + plugin.ep()->engine()->addMessage(m); + m->deref(); + } + break; } - else - disconnect(); + clearTransaction(); + m_state = Cleared; + + SIPMessage* m = new SIPMessage("BYE",m_uri); + m->addHeader("Call-ID",m_id); + String tmp; + tmp << "<" << m_id.localURI << ">"; + HeaderLine* hl = new HeaderLine("From",tmp); + hl->setParam("tag",m_id.localTag); + m->addHeader(hl); + tmp.clear(); + tmp << "<" << m_id.remoteURI << ">"; + hl = new HeaderLine("To",tmp); + hl->setParam("tag",m_id.remoteTag); + m->addHeader(hl); + plugin.ep()->buildParty(m); + plugin.ep()->engine()->addMessage(m); + m->deref(); + disconnect(); } // Creates a SDP from RTP address data present in message @@ -764,18 +830,21 @@ bool YateSIPConnection::process(SIPEvent* ev) { Debug(DebugInfo,"YateSIPConnection::process(%p) %s [%p]", ev,SIPTransaction::stateName(ev->getState()),this); + m_id = *ev->getTransaction()->recentMessage(); if (ev->getState() == SIPTransaction::Cleared) { - Lock lock(s_mutex); if (m_tr) { + Lock lock(s_mutex); Debug(DebugInfo,"YateSIPConnection clearing transaction %p [%p]", m_tr,this); m_tr->setUserData(0); m_tr->deref(); m_tr = 0; } + if (m_state != Established) + hangup(); return false; } - if (!ev->getMessage()) + if (!ev->getMessage() || ev->getMessage()->isOutgoing()) return false; if (ev->getMessage()->body && ev->getMessage()->body->isSDP()) { Debug(DebugInfo,"YateSIPConnection got SDP [%p]",this); @@ -786,6 +855,22 @@ bool YateSIPConnection::process(SIPEvent* ev) Debug(DebugAll,"RTP addr '%s' port %s formats '%s' format '%s'", m_rtpAddr.c_str(),m_rtpPort.c_str(),m_formats.c_str(),m_rtpFormat.c_str()); } + if (ev->getMessage()->isAnswer() && ((ev->getMessage()->code / 100) == 2)) { + setStatus("answered",Established); + Message *m = new Message("call.answered"); + m->addParam("driver","sip"); + m->addParam("id",id()); + if (m_target) + m->addParam("targetid",m_target); + m->addParam("status","answered"); + if (m_rtpPort) { + m->addParam("rtp_forward","yes"); + m->addParam("rtp_addr",m_rtpAddr); + m->addParam("rtp_port",m_rtpPort); + m->addParam("formats",m_formats); + } + Engine::enqueue(m); + } if (ev->getMessage()->isACK()) { Debug(DebugInfo,"YateSIPConnection got ACK [%p]",this); startRtp(); @@ -829,12 +914,14 @@ void YateSIPConnection::answered(Message* msg) { if (m_tr && (m_tr->getState() == SIPTransaction::Process)) { SIPMessage* m = new SIPMessage(m_tr->initialMessage(), 200, "OK"); - SDPBody* sdp = createRtpSDP(); + SDPBody* sdp = msg ? createPasstroughSDP(*msg) : 0; + if (!sdp) + sdp = createRtpSDP(); m->setBody(sdp); m_tr->setResponse(m); m->deref(); } - setStatus("answered"); + setStatus("answered",Established); } bool SipMsgThread::route() diff --git a/yatengine.h b/yatengine.h index 2144f82b..7a58148b 100644 --- a/yatengine.h +++ b/yatengine.h @@ -481,6 +481,12 @@ public: */ String(const String &value); + /** + * Constructor from String pointer. + * @param value Initial value of the string + */ + String(const String *value); + /** * Destroys the string, disposes the memory. */ @@ -625,6 +631,13 @@ public: inline String& operator=(const String &value) { return operator=(value.c_str()); } + /** + * Assignment from String* operator. + * @see TelEngine::strcpy + */ + inline String& operator=(const String *value) + { return operator=(value ? value->c_str() : 0); } + /** * Assignment from char* operator. * @see TelEngine::strcpy