332 lines
8.0 KiB
C++
332 lines
8.0 KiB
C++
/**
|
|
* cdrcombine.cpp
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* Combined CDR builder
|
|
*
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
|
* Copyright (C) 2013-2014 Null Team
|
|
*
|
|
* This software is distributed under multiple licenses;
|
|
* see the COPYING file in the main directory for licensing
|
|
* information for this specific distribution.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <yatengine.h>
|
|
|
|
using namespace TelEngine;
|
|
namespace { // anonymous
|
|
|
|
#define PLUGIN "cdrcombine"
|
|
|
|
class CdrCombinePlugin : public Plugin
|
|
{
|
|
public:
|
|
CdrCombinePlugin();
|
|
virtual ~CdrCombinePlugin();
|
|
virtual void initialize();
|
|
private:
|
|
bool m_first;
|
|
};
|
|
|
|
INIT_PLUGIN(CdrCombinePlugin);
|
|
|
|
class CdrHandler : public MessageHandler
|
|
{
|
|
public:
|
|
CdrHandler()
|
|
: MessageHandler("call.cdr",100,__plugin.name())
|
|
{ }
|
|
virtual bool received(Message &msg);
|
|
};
|
|
|
|
class StatusHandler : public MessageHandler
|
|
{
|
|
public:
|
|
StatusHandler()
|
|
: MessageHandler("engine.status",100,__plugin.name())
|
|
{ }
|
|
virtual bool received(Message &msg);
|
|
};
|
|
|
|
class CommandHandler : public MessageHandler
|
|
{
|
|
public:
|
|
CommandHandler()
|
|
: MessageHandler("engine.command",100,__plugin.name())
|
|
{ }
|
|
virtual bool received(Message &msg);
|
|
};
|
|
|
|
class CdrParams : public NamedList
|
|
{
|
|
public:
|
|
explicit inline CdrParams(const char* name)
|
|
: NamedList(name), m_inuse(true)
|
|
{ }
|
|
inline bool inUse() const
|
|
{ return m_inuse; }
|
|
inline void finalize()
|
|
{ m_inuse = false; }
|
|
void setParams(const NamedList& src, bool outgoing);
|
|
private:
|
|
static bool isForbidden(const String& name, const String* list);
|
|
bool m_inuse;
|
|
};
|
|
|
|
class CdrCombiner : public CdrParams
|
|
{
|
|
public:
|
|
explicit inline CdrCombiner(const char* billid)
|
|
: CdrParams(billid)
|
|
{ }
|
|
void updateInit(const NamedList& params, const String& chan);
|
|
bool updateFini(const NamedList& params, const String& chan);
|
|
void getStatus(String& str) const;
|
|
private:
|
|
CdrParams* updateParams(const NamedList& params, const String& chan);
|
|
void emitMessage() const;
|
|
ObjList m_out;
|
|
};
|
|
|
|
// List of CDRs in progress
|
|
static ObjList s_cdrs;
|
|
|
|
// This mutex protects the CDR list
|
|
static Mutex s_mutex(false,"CdrCombine");
|
|
|
|
// Non-copiable parameters for all call legs
|
|
static const String s_forbidden[] = {
|
|
"operation",
|
|
"direction",
|
|
"billid",
|
|
"cdrtrack",
|
|
"cdrcreate",
|
|
"cdrid",
|
|
""
|
|
};
|
|
|
|
// Extra non-copiable parameters for outgoing call legs
|
|
static const String s_forbidden2[] = {
|
|
"nodename",
|
|
"nodeprefix",
|
|
"cdrwrite",
|
|
"runid",
|
|
""
|
|
};
|
|
|
|
|
|
void CdrParams::setParams(const NamedList& src, bool outgoing)
|
|
{
|
|
DDebug(PLUGIN,DebugAll,"Setting params of %s leg '%s'",(outgoing ? "outgoing" : "incoming"),c_str());
|
|
NamedIterator iter(src);
|
|
while (const NamedString* p = iter.get()) {
|
|
if (p->name() == Engine::trackParam())
|
|
continue;
|
|
if (isForbidden(p->name(),s_forbidden))
|
|
continue;
|
|
if (outgoing && isForbidden(p->name(),s_forbidden2))
|
|
continue;
|
|
setParam(p->name(),*p);
|
|
}
|
|
}
|
|
|
|
bool CdrParams::isForbidden(const String& name, const String* list)
|
|
{
|
|
for (; !TelEngine::null(list); list++) {
|
|
if (name == *list)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
CdrParams* CdrCombiner::updateParams(const NamedList& params, const String& chan)
|
|
{
|
|
const String* ch = getParam(YSTRING("chan"));
|
|
if (ch) {
|
|
if (chan == *ch) {
|
|
setParams(params,false);
|
|
return this;
|
|
}
|
|
}
|
|
else if (params[YSTRING("direction")] == YSTRING("incoming")) {
|
|
setParams(params,false);
|
|
return this;
|
|
}
|
|
CdrParams* c = static_cast<CdrParams*>(m_out[chan]);
|
|
if (!c) {
|
|
DDebug(PLUGIN,DebugAll,"Creating CdrParams for '%s' in '%s'",chan.c_str(),c_str());
|
|
c = new CdrParams(chan);
|
|
m_out.insert(c);
|
|
}
|
|
c->setParams(params,true);
|
|
return c;
|
|
}
|
|
|
|
void CdrCombiner::updateInit(const NamedList& params, const String& chan)
|
|
{
|
|
updateParams(params,chan);
|
|
}
|
|
|
|
bool CdrCombiner::updateFini(const NamedList& params, const String& chan)
|
|
{
|
|
updateParams(params,chan)->finalize();
|
|
|
|
// check if this CDR is finalized or not
|
|
if (inUse())
|
|
return false;
|
|
for (ObjList* l = m_out.skipNull(); l; l = l->skipNext()) {
|
|
if (static_cast<const CdrParams*>(l->get())->inUse())
|
|
return false;
|
|
}
|
|
// all legs are no longer in use - emit message and be destroyed
|
|
emitMessage();
|
|
return true;
|
|
}
|
|
|
|
void CdrCombiner::emitMessage() const
|
|
{
|
|
Message* m = new Message("call.cdr",0,true);
|
|
m->addParam("operation","combined");
|
|
m->addParam("billid",c_str());
|
|
m->copyParams(*this);
|
|
int count = 0;
|
|
for (ObjList* l = m_out.skipNull(); l; l = l->skipNext(), count++) {
|
|
String prefix("out_leg");
|
|
if (count)
|
|
prefix << "." << count;
|
|
prefix << ".";
|
|
NamedIterator iter(*static_cast<const CdrParams*>(l->get()));
|
|
while (const NamedString* p = iter.get())
|
|
m->addParam(prefix + p->name(),*p);
|
|
}
|
|
Engine::enqueue(m);
|
|
}
|
|
|
|
void CdrCombiner::getStatus(String& str) const
|
|
{
|
|
str << c_str()
|
|
<< "=" << getValue(YSTRING("chan")) << "|" << getValue(YSTRING("caller"))
|
|
<< "|" << getValue(YSTRING("called")) << "|" << getValue(YSTRING("address"))
|
|
<< "|" << m_out.count();
|
|
}
|
|
|
|
|
|
bool CdrHandler::received(Message &msg)
|
|
{
|
|
const String* op = msg.getParam(YSTRING("operation"));
|
|
if (TelEngine::null(op))
|
|
return false;
|
|
bool init = false;
|
|
if (*op == YSTRING("initialize"))
|
|
init = true;
|
|
else if (*op != YSTRING("finalize"))
|
|
return false;
|
|
|
|
const String& billid = msg[YSTRING("billid")];
|
|
if (billid.null())
|
|
return false;
|
|
const String& chan = msg[YSTRING("chan")];
|
|
if (chan.null())
|
|
return false;
|
|
|
|
s_mutex.lock();
|
|
if (init) {
|
|
CdrCombiner* c = static_cast<CdrCombiner*>(s_cdrs[billid]);
|
|
if (!c) {
|
|
DDebug(PLUGIN,DebugInfo,"Creating CdrCombiner for '%s'",billid.c_str());
|
|
c = new CdrCombiner(billid);
|
|
s_cdrs.append(c);
|
|
}
|
|
c->updateInit(msg,chan);
|
|
}
|
|
else {
|
|
CdrCombiner* c = static_cast<CdrCombiner*>(s_cdrs[billid]);
|
|
if (c) {
|
|
if (c->updateFini(msg,chan)) {
|
|
DDebug(PLUGIN,DebugInfo,"Removing CdrCombiner for '%s'",billid.c_str());
|
|
s_cdrs.remove(c);
|
|
}
|
|
}
|
|
else
|
|
Debug(chan.c_str(),DebugWarn,"CDR finalize without combiner for '%s'",billid.c_str());
|
|
}
|
|
s_mutex.unlock();
|
|
return false;
|
|
}
|
|
|
|
|
|
bool StatusHandler::received(Message &msg)
|
|
{
|
|
const String* sel = msg.getParam(YSTRING("module"));
|
|
if (!(TelEngine::null(sel) || (*sel == YSTRING("cdrcombine"))))
|
|
return false;
|
|
String st("name=cdrcombine,type=cdr,format=ChanId|Caller|Called|Address|OutLegs");
|
|
s_mutex.lock();
|
|
st << ";cdrs=" << s_cdrs.count();
|
|
if (msg.getBoolValue(YSTRING("details"),true)) {
|
|
st << ";";
|
|
bool first = true;
|
|
for (ObjList* l = s_cdrs.skipNull(); l; l = l->skipNext()) {
|
|
const CdrCombiner* c = static_cast<const CdrCombiner*>(l->get());
|
|
if (first)
|
|
first = false;
|
|
else
|
|
st << ",";
|
|
c->getStatus(st);
|
|
}
|
|
}
|
|
s_mutex.unlock();
|
|
msg.retValue() << st << "\r\n";
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CommandHandler::received(Message &msg)
|
|
{
|
|
static const String name("cdrcombine");
|
|
const String* partial = msg.getParam(YSTRING("partline"));
|
|
if (!partial || *partial != YSTRING("status"))
|
|
return false;
|
|
partial = msg.getParam(YSTRING("partword"));
|
|
if (TelEngine::null(partial) || name.startsWith(*partial))
|
|
msg.retValue().append(name,"\t");
|
|
return false;
|
|
}
|
|
|
|
|
|
CdrCombinePlugin::CdrCombinePlugin()
|
|
: Plugin("cdrcombine"),
|
|
m_first(true)
|
|
{
|
|
Output("Loaded module CdrCombine");
|
|
}
|
|
|
|
CdrCombinePlugin::~CdrCombinePlugin()
|
|
{
|
|
Output("Unloading module CdrCombine");
|
|
}
|
|
|
|
void CdrCombinePlugin::initialize()
|
|
{
|
|
Output("Initializing module CdrCombine");
|
|
if (m_first) {
|
|
m_first = false;
|
|
Engine::install(new CdrHandler);
|
|
Engine::install(new StatusHandler);
|
|
Engine::install(new CommandHandler);
|
|
}
|
|
}
|
|
|
|
}; // anonymous namespace
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|