yate/libs/yjingle/session.cpp

822 lines
25 KiB
C++

/**
* session.cpp
* Yet Another Jingle Stack
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2006 Null Team
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <yatejingle.h>
using namespace TelEngine;
static XMPPNamespace s_ns;
static XMPPError s_err;
/**
* JGAudio
*/
XMLElement* JGAudio::toXML()
{
XMLElement* p = new XMLElement(XMLElement::PayloadType);
p->setAttribute("id",id);
p->setAttributeValid("name",name);
p->setAttributeValid("clockrate",clockrate);
p->setAttributeValid("bitrate",bitrate);
return p;
}
void JGAudio::fromXML(XMLElement* xml)
{
if (!xml) {
set("","","","","");
return;
}
xml->getAttribute("id",id);
xml->getAttribute("name",name);
xml->getAttribute("clockrate",clockrate);
xml->getAttribute("bitrate",bitrate);
}
/**
* JGAudioList
*/
// Find a data payload by its synonym
JGAudio* JGAudioList::findSynonym(const String& value)
{
for (ObjList* o = skipNull(); o; o = o->skipNext()) {
JGAudio* a = static_cast<JGAudio*>(o->get());
if (value == a->synonym)
return a;
}
return 0;
}
// Create a 'description' element and add payload children to it
XMLElement* JGAudioList::toXML(bool telEvent)
{
XMLElement* desc = XMPPUtils::createElement(XMLElement::Description,
XMPPNamespace::JingleAudio);
for (ObjList* o = skipNull(); o; o = o->skipNext()) {
JGAudio* a = static_cast<JGAudio*>(o->get());
desc->addChild(a->toXML());
}
if (telEvent) {
JGAudio* te = new JGAudio("106","telephone-event","8000","","");
desc->addChild(te->toXML());
TelEngine::destruct(te);
}
return desc;
}
// Fill this list from an XML element's children. Clear before attempting to fill
void JGAudioList::fromXML(XMLElement* xml)
{
clear();
XMLElement* m = xml ? xml->findFirstChild(XMLElement::PayloadType) : 0;
for (; m; m = xml->findNextChild(m,XMLElement::PayloadType))
ObjList::append(new JGAudio(m));
}
// Create a list from data payloads
bool JGAudioList::createList(String& dest, bool synonym, const char* sep)
{
dest = "";
for (ObjList* o = skipNull(); o; o = o->skipNext()) {
JGAudio* a = static_cast<JGAudio*>(o->get());
dest.append(synonym?a->synonym:a->name,sep);
}
return (0 != dest.length());
}
/**
* JGTransport
*/
JGTransport::JGTransport(const JGTransport& src)
{
name = src.name;
address = src.address;
port = src.port;
preference = src.preference;
username = src.username;
protocol = src.protocol;
generation = src.generation;
password = src.password;
type = src.type;
network = src.network;
}
XMLElement* JGTransport::createTransport()
{
return XMPPUtils::createElement(XMLElement::Transport,
XMPPNamespace::JingleTransport);
}
XMLElement* JGTransport::toXML()
{
XMLElement* p = new XMLElement(XMLElement::Candidate);
p->setAttribute("name",name);
p->setAttribute("address",address);
p->setAttribute("port",port);
p->setAttributeValid("preference",preference);
p->setAttributeValid("username",username);
p->setAttributeValid("protocol",protocol);
p->setAttributeValid("generation",generation);
p->setAttributeValid("password",password);
p->setAttributeValid("type",type);
p->setAttributeValid("network",network);
return p;
}
void JGTransport::fromXML(XMLElement* element)
{
element->getAttribute("name",name);
element->getAttribute("address",address);
element->getAttribute("port",port);
element->getAttribute("preference",preference);
element->getAttribute("username",username);
element->getAttribute("protocol",protocol);
element->getAttribute("generation",generation);
element->getAttribute("password",password);
element->getAttribute("type",type);
element->getAttribute("network",network);
}
/**
* JGSession
*/
TokenDict JGSession::s_states[] = {
{"Idle", Idle},
{"Pending", Pending},
{"Active", Active},
{"Ending", Ending},
{"Destroy", Destroy},
{0,0}
};
TokenDict JGSession::s_actions[] = {
{"accept", ActAccept},
{"initiate", ActInitiate},
{"reject", ActReject},
{"terminate", ActTerminate},
{"candidates", ActTransportCandidates},
{"transport-info", ActTransportInfo},
{"transport-accept", ActTransportAccept},
{"content-info", ActContentInfo},
{"Transport", ActTransport},
{"DTMF", ActDtmf},
{"DTMF method", ActDtmfMethod},
{0,0}
};
// Create an outgoing session
JGSession::JGSession(JGEngine* engine, JBStream* stream,
const String& callerJID, const String& calledJID,
XMLElement* media, XMLElement* transport,
bool sid, const char* msg)
: Mutex(true),
m_state(Idle),
m_transportType(TransportUnknown),
m_engine(engine),
m_stream(stream),
m_outgoing(true),
m_localJID(callerJID),
m_remoteJID(calledJID),
m_sidAttr(sid?"sid":"id"),
m_lastEvent(0),
m_private(0),
m_stanzaId(1)
{
m_engine->createSessionId(m_localSid);
m_sid = m_localSid;
Debug(m_engine,DebugAll,"Call(%s). Outgoing msg=%s [%p]",m_sid.c_str(),msg,this);
if (msg)
sendMessage(msg);
XMLElement* xml = createJingle(ActInitiate,media,transport);
if (sendStanza(xml))
changeState(Pending);
else
changeState(Destroy);
}
// Create an incoming session
JGSession::JGSession(JGEngine* engine, JBEvent* event, const String& id, bool sid)
: Mutex(true),
m_state(Idle),
m_transportType(TransportUnknown),
m_engine(engine),
m_stream(event->stream()),
m_outgoing(false),
m_sid(id),
m_sidAttr(sid?"sid":"id"),
m_lastEvent(0),
m_private(0),
m_stanzaId(1)
{
m_events.append(event);
m_engine->createSessionId(m_localSid);
Debug(m_engine,DebugAll,"Call(%s). Incoming [%p]",m_sid.c_str(),this);
}
// Destructor: hangup, cleanup, remove from engine's list
JGSession::~JGSession()
{
XDebug(m_engine,DebugAll,"JGSession::~JGSession() [%p]",this);
}
// Release this session and its memory
void JGSession::destroyed()
{
lock();
// Cancel pending outgoing. Hangup. Cleanup
if (m_stream) {
m_stream->removePending(m_localSid,false);
hangup();
TelEngine::destruct(m_stream);
}
m_events.clear();
unlock();
// Remove from engine
Lock lock(m_engine);
m_engine->m_sessions.remove(this,false);
lock.drop();
DDebug(m_engine,DebugAll,"Call(%s). Destroyed [%p]",m_sid.c_str(),this);
}
// Accept a Pending incoming session
bool JGSession::accept(XMLElement* description)
{
Lock lock(this);
if (outgoing() || state() != Pending)
return false;
XMLElement* xml = createJingle(ActAccept,description,JGTransport::createTransport());
if (!sendStanza(xml))
return false;
changeState(Active);
return true;
}
// Close a Pending or Active session
bool JGSession::hangup(bool reject, const char* msg)
{
Lock lock(this);
if (state() != Pending && state() != Active)
return false;
DDebug(m_engine,DebugAll,"Call(%s). %s('%s') [%p]",m_sid.c_str(),
reject?"Reject":"Hangup",msg,this);
if (msg)
sendMessage(msg);
// Clear sent stanzas list. We will wait for this element to be confirmed
m_sentStanza.clear();
XMLElement* xml = createJingle(reject ? ActReject : ActTerminate);
bool ok = sendStanza(xml);
changeState(Ending);
return ok;
}
// Confirm a received element. If the error is NoError a result stanza will be sent
// Otherwise, an error stanza will be created and sent
bool JGSession::confirm(XMLElement* xml, XMPPError::Type error,
const char* text, XMPPError::ErrorType type)
{
if (!xml)
return false;
String id = xml->getAttribute("id");
XMLElement* iq = 0;
if (error == XMPPError::NoError) {
iq = XMPPUtils::createIq(XMPPUtils::IqResult,m_localJID,m_remoteJID,id);
// The receiver will detect which stanza is confirmed by id
// If missing, make a copy of the received element and attach it to the error
if (!id) {
XMLElement* copy = new XMLElement(*xml);
iq->addChild(copy);
}
}
else {
iq = XMPPUtils::createIq(XMPPUtils::IqError,m_localJID,m_remoteJID,id);
iq->addChild(xml);
iq->addChild(XMPPUtils::createError(type,error,text));
}
return sendStanza(iq,false);
}
// Send a dtmf string to remote peer
bool JGSession::sendDtmf(const char* dtmf, bool buttonUp)
{
XMLElement* iq = createJingle(ActContentInfo);
XMLElement* sess = iq->findFirstChild();
if (!(dtmf && *dtmf && sess))
return sendStanza(iq);
char s[2] = {0,0};
const char* action = buttonUp ? "button-up" : "button-down";
while (*dtmf) {
s[0] = *dtmf++;
XMLElement* xml = XMPPUtils::createElement(XMLElement::Dtmf,XMPPNamespace::Dtmf);
xml->setAttribute("action",action);
xml->setAttribute("code",s);
sess->addChild(xml);
}
TelEngine::destruct(sess);
return sendStanza(iq);
}
// Send a dtmf method to remote peer
bool JGSession::sendDtmfMethod(const char* method)
{
XMLElement* xml = XMPPUtils::createElement(XMLElement::DtmfMethod,
XMPPNamespace::Dtmf);
xml->setAttribute("method",method);
return sendStanza(createJingle(ActContentInfo,xml));
}
// Deny a dtmf method request from remote peer
bool JGSession::denyDtmfMethod(XMLElement* element)
{
if (!element)
return false;
String id = element->getAttribute("id");
XMLElement* iq = XMPPUtils::createIq(XMPPUtils::IqError,m_localJID,m_remoteJID,id);
iq->addChild(element);
XMLElement* err = XMPPUtils::createError(XMPPError::TypeCancel,XMPPError::SFeatureNotImpl);
err->addChild(XMPPUtils::createElement(s_err[XMPPError::DtmfNoMethod],XMPPNamespace::DtmfError));
iq->addChild(err);
return sendStanza(iq,false);
}
// Enqueue a Jabber engine event
void JGSession::enqueue(JBEvent* event)
{
Lock lock(this);
if (event->type() == JBEvent::Terminated || event->type() == JBEvent::Destroy)
m_events.insert(event);
else
m_events.append(event);
DDebug(m_engine,DebugAll,"Call(%s). Accepted event (%p,%s) [%p]",
m_sid.c_str(),event,event->name(),this);
}
// Process received events. Generate Jingle events
JGEvent* JGSession::getEvent(u_int64_t time)
{
Lock lock(this);
if (m_lastEvent)
return 0;
if (state() == Destroy)
return 0;
// Deque and process event(s)
// Loop until a jingle event is generated or no more events in queue
JBEvent* jbev = 0;
while (true) {
TelEngine::destruct(jbev);
jbev = static_cast<JBEvent*>(m_events.remove(false));
if (!jbev)
break;
DDebug(m_engine,DebugAll,
"Call(%s). Dequeued Jabber event (%p,%s) in state %s [%p]",
m_sid.c_str(),jbev,jbev->name(),lookupState(state()),this);
// Process Jingle 'set' stanzas
if (jbev->type() == JBEvent::IqJingleSet) {
// Filter some conditions in which we can't accept any jingle stanza
// Incoming pending sessions are waiting for the user to accept/reject them
// Outgoing idle sessions are waiting for the user to initiate them
if ((state() == Pending && !outgoing()) ||
(state() == Idle && outgoing())) {
confirm(jbev->releaseXML(),XMPPError::SRequest);
continue;
}
m_lastEvent = decodeJingle(jbev);
if (!m_lastEvent) {
// Destroy incoming session if session initiate stanza contains errors
if (!outgoing() && state() == Idle) {
m_lastEvent = new JGEvent(JGEvent::Destroy,this,0,"failure");
break;
}
continue;
}
DDebug(m_engine,DebugInfo,
"Call(%s). Processing action (%u,'%s') state=%s [%p]",
m_sid.c_str(),m_lastEvent->action(),
lookup(m_lastEvent->action(),s_actions),lookupState(state()),this);
// Check for termination events
if (m_lastEvent->final())
break;
bool error = false;
bool fatal = false;
switch (state()) {
case Active:
if (m_lastEvent->action() == ActAccept ||
m_lastEvent->action() == ActInitiate)
error = true;
break;
case Pending:
// Accept session-accept or transport stanzas
switch (m_lastEvent->action()) {
case ActAccept:
changeState(Active);
break;
case ActTransportAccept:
case ActTransport:
case ActTransportInfo:
case ActTransportCandidates:
case ActContentInfo:
break;
default:
error = true;
}
break;
case Idle:
// Update data. Terminate if not a session initiating event
if (m_lastEvent->action() == ActInitiate) {
m_localJID.set(jbev->to());
m_remoteJID.set(jbev->from());
changeState(Pending);
}
else
error = fatal = true;
break;
default:
error = true;
}
if (!error) {
// Automatically confirm some actions
// Don't confirm actions that need session user's interaction:
// transport and dtmf method negotiation
if (m_lastEvent->action() != ActTransport &&
m_lastEvent->action() != ActDtmfMethod)
confirm(m_lastEvent->element());
}
else {
confirm(m_lastEvent->releaseXML(),XMPPError::SRequest);
delete m_lastEvent;
m_lastEvent = 0;
if (fatal)
m_lastEvent = new JGEvent(JGEvent::Destroy,this);
else
continue;
}
break;
}
// Check for responses or failures
bool response = jbev->type() == JBEvent::IqJingleRes ||
jbev->type() == JBEvent::IqJingleErr ||
jbev->type() == JBEvent::IqResult ||
jbev->type() == JBEvent::WriteFail;
while (response) {
bool notSent = true;
// Don't use iterator: we stop searching the list at first item removal
for (ObjList* o = m_sentStanza.skipNull(); o; o = o->skipNext()) {
JGSentStanza* sent = static_cast<JGSentStanza*>(o->get());
if (jbev->id() == *sent) {
m_sentStanza.remove(sent,true);
notSent = false;
if (state() == Ending)
m_lastEvent = new JGEvent(JGEvent::Destroy,this);
break;
}
}
// Ignore it if this event is not a result of a known sent stanza
// We didn't expect a result anyway
if (notSent)
break;
// Write fail: Terminate if failed stanza is a Jingle one
// Ignore all other write failures
if (jbev->type() == JBEvent::WriteFail) {
// Check if failed stanza is a jingle one
XMLElement* e = jbev->element() ? jbev->element()->findFirstChild() : 0;
if (e && e->hasAttribute("xmlns",s_ns[XMPPNamespace::Jingle])) {
Debug(m_engine,DebugInfo,
"Call(%s). Write stanza failure. Terminating [%p]",
m_sid.c_str(),this);
m_lastEvent = new JGEvent(JGEvent::Terminated,this,0,"noconn");
}
TelEngine::destruct(e);
break;
}
#ifdef DEBUG
String error;
if (jbev->text())
error << ". Error: " << jbev->text();
Debug(m_engine,DebugAll,
"Call(%s). Sent element with id '%s' confirmed%s [%p]",
m_sid.c_str(),jbev->id().c_str(),error.safe(),this);
#endif
// Terminate pending outgoing sessions if session initiate stanza received error
if (state() == Pending && outgoing() && jbev->type() == JBEvent::IqJingleErr)
m_lastEvent = new JGEvent(JGEvent::Terminated,this,jbev->releaseXML(),
jbev->text()?jbev->text().c_str():"failure");
break;
}
if (response)
if (!m_lastEvent)
continue;
else
break;
// Silently ignore temporary stream down
if (jbev->type() == JBEvent::Terminated) {
DDebug(m_engine,DebugInfo,
"Call(%s). Stream disconnected in state %s [%p]",
m_sid.c_str(),lookupState(state()),this);
continue;
}
// Terminate on stream destroy
if (jbev->type() == JBEvent::Destroy) {
Debug(m_engine,DebugInfo,
"Call(%s). Stream destroyed in state %s [%p]",
m_sid.c_str(),lookupState(state()),this);
m_lastEvent = new JGEvent(JGEvent::Terminated,this,0,"noconn");
break;
}
Debug(m_engine,DebugStub,"Call(%s). Unhandled event type %u '%s' [%p]",
m_sid.c_str(),jbev->type(),jbev->name(),this);
continue;
}
TelEngine::destruct(jbev);
// No event: check first sent stanza's timeout
if (!m_lastEvent) {
ObjList* o = m_sentStanza.skipNull();
if (o) {
JGSentStanza* tmp = static_cast<JGSentStanza*>(o->get());
if (tmp->timeout(time)) {
Debug(m_engine,DebugNote,"Call(%s). Sent stanza ('%s') timed out [%p]",
m_sid.c_str(),tmp->c_str(),this);
// Notify the peer anyway (something may be wrong)
hangup(false,"Timeout");
m_lastEvent = new JGEvent(JGEvent::Terminated,this,0,"timeout");
}
}
}
if (m_lastEvent) {
// Deref the session for final events
if (m_lastEvent->final()) {
changeState(Destroy);
deref();
}
DDebug(m_engine,DebugAll,
"Call(%s). Raising event (%p,%u) action=%u final=%s [%p]",
m_sid.c_str(),m_lastEvent,m_lastEvent->type(),
m_lastEvent->action(),String::boolText(m_lastEvent->final()),this);
return m_lastEvent;
}
return 0;
}
// Send a stanza to the remote peer
bool JGSession::sendStanza(XMLElement* stanza, bool confirmation)
{
Lock lock(this);
if (!(state() != Ending && state() != Destroy && stanza && m_stream)) {
Debug(m_engine,DebugNote,
"Call(%s). Can't send stanza (%p,'%s') in state %s [%p]",
m_sid.c_str(),stanza,stanza->name(),lookupState(m_state),this);
TelEngine::destruct(stanza);
return false;
}
DDebug(m_engine,DebugAll,"Call(%s). Sending stanza (%p,'%s') [%p]",
m_sid.c_str(),stanza,stanza->name(),this);
// Check if the stanza should be added to the list of stanzas requiring confirmation
if (confirmation && stanza->type() == XMLElement::Iq) {
// Create id
String id = m_localSid;
id << "_" << (unsigned int)m_stanzaId;
m_stanzaId++;
stanza->setAttribute("id",id);
// Append to sent stanzas
m_sentStanza.append(new JGSentStanza(id,m_engine->stanzaTimeout() + Time::msecNow()));
}
// Send. If it fails leave it in the sent items to timeout
JBStream::Error res = m_stream->sendStanza(stanza,m_localSid);
if (res == JBStream::ErrorNoSocket || res == JBStream::ErrorContext)
return false;
return true;
}
// Decode a jingle stanza
JGEvent* JGSession::decodeJingle(JBEvent* jbev)
{
XMLElement* jingle = jbev->child();
if (!jingle) {
confirm(jbev->releaseXML(),XMPPError::SBadRequest);
return 0;
}
Action act = (Action)lookup(jingle->getAttribute("type"),s_actions,ActCount);
if (act == ActCount) {
confirm(jbev->releaseXML(),XMPPError::SServiceUnavailable,
"Unknown jingle type");
return 0;
}
// *** ActTerminate or ActReject
if (act == ActTerminate || act == ActReject) {
confirm(jbev->element());
return new JGEvent(JGEvent::Terminated,this,jbev->releaseXML(),
act==ActTerminate?"hangup":"rejected");
}
// *** ActContentInfo: ActDtmf or ActDtmfMethod
if (act == ActContentInfo) {
// Check dtmf
XMLElement* tmp = jingle->findFirstChild(XMLElement::Dtmf);
if (tmp) {
String reason = tmp->getAttribute("action");
// Expect more then 1 'dtmf' child
String text;
for (; tmp; tmp = jingle->findNextChild(tmp,XMLElement::Dtmf))
text << tmp->getAttribute("code");
if (!text || (reason != "button-up" && reason != "button-down")) {
confirm(jbev->releaseXML(),XMPPError::SBadRequest,"Unknown action");
return 0;
}
return new JGEvent(ActDtmf,this,jbev->releaseXML(),reason,text);
}
// Check dtmf method
tmp = jingle->findFirstChild(XMLElement::DtmfMethod);
if (tmp) {
String text = tmp->getAttribute("method");
TelEngine::destruct(tmp);
if (text != "rtp" && text != "xmpp") {
confirm(jbev->releaseXML(),XMPPError::SBadRequest,"Unknown method");
return 0;
}
return new JGEvent(ActDtmfMethod,this,jbev->releaseXML(),0,text);
}
confirm(jbev->releaseXML(),XMPPError::SServiceUnavailable);
return 0;
}
// *** ActAccept ActInitiate ActModify
// *** ActTransport: ActTransportInfo/ActTransportCandidates
// *** ActTransportAccept
// Detect transport type
if (act == ActTransportCandidates) {
m_transportType = TransportCandidates;
act = ActTransport;
DDebug(m_engine,DebugInfo,"Call(%s). Set transport='candidates' [%p]",
m_sid.c_str(),this);
}
else if (act == ActTransportInfo || act == ActTransportAccept) {
m_transportType = TransportInfo;
// Don't set action for transport-accept. Use it only to get transport info if any
if (act == ActTransportInfo)
act = ActTransport;
DDebug(m_engine,DebugInfo,"Call(%s). Set transport='transport-info' [%p]",
m_sid.c_str(),this);
}
// Get transport candidates parent:
// transport-info: A 'transport' child element
// candidates: The 'session' element
// Get media description
// Create event, update transport and media
XMLElement* trans = jingle;
XMLElement* media = 0;
JGEvent* event = 0;
while (true) {
if (m_transportType == TransportInfo) {
trans = trans->findFirstChild(XMLElement::Transport);
if (trans && !trans->hasAttribute("xmlns",s_ns[XMPPNamespace::JingleTransport]))
break;
}
media = jingle->findFirstChild(XMLElement::Description);
if (media && !media->hasAttribute("xmlns",s_ns[XMPPNamespace::JingleAudio]))
break;
// Don't set the event's element yet: this would invalidate the 'jingle' variable
event = new JGEvent(act,this,0);
XMLElement* t = trans ? trans->findFirstChild(XMLElement::Candidate) : 0;
for (; t; t = trans->findNextChild(t,XMLElement::Candidate))
event->m_transport.append(new JGTransport(t));
event->m_audio.fromXML(media);
event->m_id = jbev->id();
event->m_element = jbev->releaseXML();
break;
}
if (trans != jingle)
TelEngine::destruct(trans);
TelEngine::destruct(media);
if (!event)
confirm(jbev->releaseXML(),XMPPError::SServiceUnavailable);
return event;
}
// Create an 'iq' stanza with a 'jingle' child
XMLElement* JGSession::createJingle(Action action, XMLElement* element1, XMLElement* element2)
{
XMLElement* iq = XMPPUtils::createIq(XMPPUtils::IqSet,m_localJID,m_remoteJID,0);
XMLElement* jingle = XMPPUtils::createElement(XMLElement::Jingle,
XMPPNamespace::Jingle);
if (action < ActCount)
jingle->setAttribute("type",lookup(action,s_actions));
jingle->setAttribute("initiator",outgoing()?m_localJID:m_remoteJID);
// jingle->setAttribute("responder",outgoing()?m_remoteJID:m_localJID);
jingle->setAttribute(m_sidAttr,m_sid);
jingle->addChild(element1);
jingle->addChild(element2);
iq->addChild(jingle);
return iq;
}
// Send a transport related element to the remote peer
bool JGSession::sendTransport(JGTransport* transport, Action act)
{
if (act != ActTransport && act != ActTransportAccept)
return false;
// Accept received transport
if (act == ActTransportAccept) {
TelEngine::destruct(transport);
// Clients negotiating transport as 'candidates' don't expect transport-accept
if (m_transportType == TransportCandidates)
return true;
XMLElement* child = JGTransport::createTransport();
return sendStanza(createJingle(ActTransportAccept,0,child));
}
// Sent transport
if (!transport)
return false;
// TransportUnknown: send both transport types
// TransportInfo: A 'transport' child element of the session element
// TransportCandidates: Transport candidates are direct children of the 'session' element
XMLElement* child = 0;
bool ok = false;
switch (m_transportType) {
case TransportUnknown:
// Fallthrough to send both transport types
case TransportInfo:
child = JGTransport::createTransport();
transport->addTo(child);
ok = sendStanza(createJingle(ActTransportInfo,0,child));
if (!ok || m_transportType == TransportInfo)
break;
// Fallthrough to send candidates if unknown and succedded
case TransportCandidates:
child = transport->toXML();
ok = sendStanza(createJingle(ActTransportCandidates,0,child));
}
TelEngine::destruct(transport);
return ok;
}
// Event termination notification
void JGSession::eventTerminated(JGEvent* event)
{
lock();
if (event == m_lastEvent) {
DDebug(m_engine,DebugAll,"Call(%s). Event (%p,%u) terminated [%p]",
m_sid.c_str(),event,event->type(),this);
m_lastEvent = 0;
}
else if (m_lastEvent)
Debug(m_engine,DebugNote,
"Call(%s). Event (%p,%u) replaced while processed [%p]",
m_sid.c_str(),event,event->type(),this);
unlock();
}
// Change session state
void JGSession::changeState(State newState)
{
if (m_state == newState)
return;
Debug(m_engine,DebugInfo,"Call(%s). Changing state from %s to %s [%p]",
m_sid.c_str(),lookup(m_state,s_states),lookup(newState,s_states),this);
m_state = newState;
}
/* vi: set ts=8 sw=4 sts=4 noet: */