69e0103bc0
git-svn-id: http://yate.null.ro/svn/yate/trunk@2898 acf43c95-373e-0410-b603-e72c3f656dc1
328 lines
9.1 KiB
C++
328 lines
9.1 KiB
C++
/**
|
|
* jgengine.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>
|
|
#include <stdlib.h>
|
|
|
|
using namespace TelEngine;
|
|
|
|
const TokenDict JGEvent::s_typeName[] = {
|
|
{"Jingle", Jingle},
|
|
{"ResultOk", ResultOk},
|
|
{"ResultError", ResultError},
|
|
{"ResultTimeout", ResultTimeout},
|
|
{"Terminated", Terminated},
|
|
{"Destroy", Destroy},
|
|
{0,0}
|
|
};
|
|
|
|
|
|
/*
|
|
* JGEngine
|
|
*/
|
|
// Constructor
|
|
JGEngine::JGEngine(const char* name)
|
|
: Mutex(true,"JGEngine"),
|
|
m_sessionId(1), m_stanzaTimeout(20000), m_pingInterval(300000)
|
|
{
|
|
debugName(name);
|
|
}
|
|
|
|
JGEngine::~JGEngine()
|
|
{
|
|
}
|
|
|
|
// Create private stream(s) to get events from sessions
|
|
void JGEngine::initialize(const NamedList& params)
|
|
{
|
|
int lvl = params.getIntValue("debug_level",-1);
|
|
if (lvl != -1)
|
|
debugLevel(lvl);
|
|
|
|
int timeout = params.getIntValue("stanza_timeout",(int)m_stanzaTimeout);
|
|
m_stanzaTimeout = timeout > 10000 ? timeout : 10000;
|
|
int ping = params.getIntValue("ping_interval",(int)m_pingInterval);
|
|
if (ping == 0)
|
|
m_pingInterval = 0;
|
|
else if (ping < 60000)
|
|
m_pingInterval = 60000;
|
|
else
|
|
m_pingInterval = ping;
|
|
// Make sure we don't ping before a ping times out
|
|
if (m_pingInterval && m_stanzaTimeout && m_pingInterval <= m_stanzaTimeout)
|
|
m_pingInterval = m_stanzaTimeout + 100;
|
|
|
|
if (debugAt(DebugInfo)) {
|
|
String s;
|
|
s << " stanza_timeout=" << (unsigned int)m_stanzaTimeout;
|
|
s << " ping_interval=" << (unsigned int)m_pingInterval;
|
|
Debug(this,DebugInfo,"Jabber Jingle service initialized:%s [%p]",
|
|
s.c_str(),this);
|
|
}
|
|
}
|
|
|
|
// Make an outgoing call
|
|
JGSession* JGEngine::call(JGSession::Version ver, const JabberID& caller,
|
|
const JabberID& called, const ObjList& contents, XmlElement* extra,
|
|
const char* msg, const char* subject, const char* line)
|
|
{
|
|
DDebug(this,DebugAll,"call() from '%s' to '%s'",caller.c_str(),called.c_str());
|
|
JGSession* session = 0;
|
|
switch (ver) {
|
|
case JGSession::Version1:
|
|
session = new JGSession1(this,caller,called);
|
|
break;
|
|
case JGSession::Version0:
|
|
session = new JGSession0(this,caller,called);
|
|
break;
|
|
case JGSession::VersionUnknown:
|
|
Debug(this,DebugNote,"Unhandled session version %d in call()",ver);
|
|
return 0;
|
|
}
|
|
if (session) {
|
|
session->line(line);
|
|
if (!TelEngine::null(msg))
|
|
sendMessage(session,msg);
|
|
if (session->initiate(contents,extra,subject)) {
|
|
Lock lock(this);
|
|
m_sessions.append(session);
|
|
return (session && session->ref()) ? session : 0;
|
|
}
|
|
}
|
|
TelEngine::destruct(session);
|
|
Debug(this,DebugNote,"Outgoing call from '%s' to '%s' failed to initiate",
|
|
caller.c_str(),called.c_str());
|
|
return 0;
|
|
}
|
|
|
|
// Send a session's stanza.
|
|
bool JGEngine::sendStanza(JGSession* session, XmlElement*& stanza)
|
|
{
|
|
Debug(this,DebugStub,"JGEngine::sendStanza() not implemented!");
|
|
TelEngine::destruct(stanza);
|
|
return false;
|
|
}
|
|
|
|
// Send a chat message on behalf of a session
|
|
bool JGEngine::sendMessage(JGSession* session, const char* body)
|
|
{
|
|
XmlElement* x = XMPPUtils::createMessage(XMPPUtils::Chat,0,0,0,body);
|
|
return sendStanza(session,x);
|
|
}
|
|
|
|
// Get events from sessions
|
|
JGEvent* JGEngine::getEvent(u_int64_t time)
|
|
{
|
|
JGEvent* event = 0;
|
|
lock();
|
|
ListIterator iter(m_sessions);
|
|
for (;;) {
|
|
JGSession* session = static_cast<JGSession*>(iter.get());
|
|
// End of iteration?
|
|
if (!session)
|
|
break;
|
|
RefPointer<JGSession> s = session;
|
|
// Dead pointer?
|
|
if (!s)
|
|
continue;
|
|
unlock();
|
|
if (0 != (event = s->getEvent(time))) {
|
|
if (event->type() == JGEvent::Destroy) {
|
|
DDebug(this,DebugAll,"Deleting internal event (%p,Destroy)",event);
|
|
delete event;
|
|
}
|
|
else
|
|
return event;
|
|
}
|
|
lock();
|
|
}
|
|
unlock();
|
|
return 0;
|
|
}
|
|
|
|
// Ask this engine to accept an incoming xml 'iq' element
|
|
bool JGEngine::acceptIq(XMPPUtils::IqType type, const JabberID& from, const JabberID& to,
|
|
const String& id, XmlElement* xml, const char* line, XMPPError::Type& error,
|
|
String& text)
|
|
{
|
|
error = XMPPError::NoError;
|
|
if (!xml)
|
|
return false;
|
|
if (type == XMPPUtils::IqResult || type == XMPPUtils::IqError) {
|
|
for (ObjList* o = m_sessions.skipNull(); o; o = o->skipNext()) {
|
|
JGSession* session = static_cast<JGSession*>(o->get());
|
|
if (session->acceptIq(type,from,to,id,xml))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (type != XMPPUtils::IqGet && type != XMPPUtils::IqSet)
|
|
return false;
|
|
// Handle set/get iq
|
|
XmlElement* child = xml->findFirstChild();
|
|
if (!child)
|
|
return false;
|
|
String sid;
|
|
JGSession::Version ver = JGSession::VersionUnknown;
|
|
int ns = XMPPUtils::xmlns(*child);
|
|
bool fileTransfer = false;
|
|
// Jingle or file transfer stanzas (jingle stanzas can only have type='set')
|
|
// Set version and session id
|
|
if (ns == XMPPNamespace::Jingle) {
|
|
if (type == XMPPUtils::IqSet) {
|
|
ver = JGSession::Version1;
|
|
sid = child->getAttribute("sid");
|
|
}
|
|
}
|
|
else if (ns == XMPPNamespace::JingleSession) {
|
|
if (type == XMPPUtils::IqSet) {
|
|
ver = JGSession::Version0;
|
|
sid = child->getAttribute("id");
|
|
}
|
|
}
|
|
else if (ns == XMPPNamespace::ByteStreams && XMPPUtils::isUnprefTag(*child,XmlTag::Query)) {
|
|
fileTransfer = true;
|
|
sid = child->getAttribute("sid");
|
|
}
|
|
else
|
|
return false;
|
|
if (!sid) {
|
|
if (!fileTransfer) {
|
|
error = XMPPError::BadRequest;
|
|
if (type == XMPPUtils::IqSet)
|
|
text = "Missing session id attribute";
|
|
}
|
|
return false;
|
|
}
|
|
Lock lock(this);
|
|
DDebug(this,DebugAll,"Accepting xml child=%s sid=%s version=%d filetransfer=%u",
|
|
child->tag(),sid.c_str(),ver,fileTransfer);
|
|
// Check for an existing session destination
|
|
for (ObjList* o = m_sessions.skipNull(); o; o = o->skipNext()) {
|
|
JGSession* session = static_cast<JGSession*>(o->get());
|
|
if (session->acceptIq(type,from,to,sid,xml))
|
|
return true;
|
|
}
|
|
// Check if this an incoming session request
|
|
JGSession* session = 0;
|
|
if (ver != JGSession::VersionUnknown) {
|
|
JGSession::Action action = JGSession::lookupAction(child->attribute("type"),ver);
|
|
if (action == JGSession::ActInitiate) {
|
|
switch (ver) {
|
|
case JGSession::Version1:
|
|
session = new JGSession1(this,to,from,xml,sid);
|
|
break;
|
|
case JGSession::Version0:
|
|
session = new JGSession0(this,to,from,xml,sid);
|
|
break;
|
|
default:
|
|
error = XMPPError::ServiceUnavailable;
|
|
Debug(this,DebugStub,"JGEngine::accept(): unhandled session version %d",ver);
|
|
}
|
|
}
|
|
else {
|
|
error = XMPPError::Request;
|
|
text = "Unknown session";
|
|
}
|
|
if (session) {
|
|
session->line(line);
|
|
m_sessions.append(session);
|
|
}
|
|
return error == XMPPError::NoError;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Default event processor
|
|
void JGEngine::defProcessEvent(JGEvent* event)
|
|
{
|
|
if (!event)
|
|
return;
|
|
DDebug(this,DebugAll,"JGEngine::defprocessEvent. Deleting event (%p,%u)",
|
|
event,event->type());
|
|
delete event;
|
|
}
|
|
|
|
// Process generated events
|
|
void JGEngine::processEvent(JGEvent* event)
|
|
{
|
|
Debug(this,DebugStub,"JGEngine::processEvent. Calling default processor");
|
|
defProcessEvent(event);
|
|
}
|
|
|
|
// Create a local session id
|
|
void JGEngine::createSessionId(String& id)
|
|
{
|
|
Lock lock(this);
|
|
id = "JG";
|
|
id << (unsigned int)m_sessionId << "_" << (int)random();
|
|
m_sessionId++;
|
|
}
|
|
|
|
|
|
/**
|
|
* JGEvent
|
|
*/
|
|
JGEvent::~JGEvent()
|
|
{
|
|
if (m_session) {
|
|
if (!m_confirmed)
|
|
confirmElement(XMPPError::UndefinedCondition,"Unhandled");
|
|
m_session->eventTerminated(this);
|
|
TelEngine::destruct(m_session);
|
|
}
|
|
TelEngine::destruct(releaseXml());
|
|
XDebug(DebugAll,"JGEvent::~JGEvent [%p]",this);
|
|
}
|
|
|
|
void JGEvent::init(JGSession* session)
|
|
{
|
|
XDebug(DebugAll,"JGEvent::JGEvent [%p]",this);
|
|
if (session && session->ref())
|
|
m_session = session;
|
|
if (m_element) {
|
|
m_id = m_element->getAttribute("id");
|
|
if (m_session)
|
|
switch (m_session->version()) {
|
|
case JGSession::Version1:
|
|
m_jingle = XMPPUtils::findFirstChild(*m_element,XmlTag::Jingle);
|
|
break;
|
|
case JGSession::Version0:
|
|
m_jingle = XMPPUtils::findFirstChild(*m_element,XmlTag::Session);
|
|
break;
|
|
case JGSession::VersionUnknown:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the jingle action as enumeration. Set confirmation flag if
|
|
// the element don't require it
|
|
void JGEvent::setAction(JGSession::Action act)
|
|
{
|
|
m_action = act;
|
|
m_confirmed = !(m_element && act != JGSession::ActCount);
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|