2006-02-14 17:58:41 +00:00
|
|
|
/**
|
|
|
|
* callfork.cpp
|
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
|
|
*
|
|
|
|
* Call Forker
|
|
|
|
*
|
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2023-05-23 14:01:06 +00:00
|
|
|
* Copyright (C) 2004-2023 Null Team
|
2006-02-14 17:58:41 +00:00
|
|
|
*
|
2013-08-06 13:38:10 +00:00
|
|
|
* 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.
|
2006-02-14 17:58:41 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2013-08-06 13:38:10 +00:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
2006-02-14 17:58:41 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <yatephone.h>
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
2006-05-27 14:53:18 +00:00
|
|
|
namespace { // anonymous
|
2006-02-14 17:58:41 +00:00
|
|
|
|
|
|
|
#define MOD_PREFIX "fork"
|
|
|
|
|
|
|
|
static ObjList s_calls;
|
|
|
|
static int s_current = 0;
|
|
|
|
|
|
|
|
class ForkSlave;
|
|
|
|
|
2021-05-28 11:50:18 +00:00
|
|
|
class ForkMaster : public CallEndpoint, public DebugEnabler
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
friend class ForkSlave;
|
2006-02-14 17:58:41 +00:00
|
|
|
public:
|
2021-05-28 11:50:18 +00:00
|
|
|
ForkMaster(ObjList* targets, int lvl);
|
2006-02-14 17:58:41 +00:00
|
|
|
virtual ~ForkMaster();
|
2011-02-28 17:58:26 +00:00
|
|
|
virtual void disconnected(bool final, const char* reason);
|
2006-02-21 19:57:44 +00:00
|
|
|
bool startCalling(Message& msg);
|
2009-11-04 17:14:56 +00:00
|
|
|
void checkTimer(const Time& tmr);
|
2006-02-14 17:58:41 +00:00
|
|
|
void lostSlave(ForkSlave* slave, const char* reason);
|
2009-05-20 13:08:01 +00:00
|
|
|
bool msgAnswered(Message& msg, const String& dest);
|
|
|
|
bool msgProgress(Message& msg, const String& dest);
|
2009-05-20 14:40:02 +00:00
|
|
|
const ObjList& slaves() const
|
|
|
|
{ return m_slaves; }
|
2011-04-07 21:00:09 +00:00
|
|
|
bool msgToSlaves(const Message& msg, const String& match);
|
2006-02-14 17:58:41 +00:00
|
|
|
protected:
|
2012-01-04 15:52:58 +00:00
|
|
|
bool clearRinging(const String& id);
|
2007-02-27 23:31:33 +00:00
|
|
|
void clear(bool softly);
|
2006-02-21 19:57:44 +00:00
|
|
|
String* getNextDest();
|
2011-07-12 14:18:39 +00:00
|
|
|
bool forkSlave(String* dest);
|
2006-02-21 19:57:44 +00:00
|
|
|
bool callContinue();
|
2011-11-15 18:47:43 +00:00
|
|
|
RefPointer<CallEndpoint> m_discPeer;
|
2006-02-14 17:58:41 +00:00
|
|
|
ObjList m_slaves;
|
|
|
|
String m_ringing;
|
2006-02-21 19:57:44 +00:00
|
|
|
Regexp m_failures;
|
|
|
|
int m_index;
|
2006-02-14 17:58:41 +00:00
|
|
|
bool m_answered;
|
2006-02-23 13:46:20 +00:00
|
|
|
bool m_rtpForward;
|
|
|
|
bool m_rtpStrict;
|
2007-04-30 22:39:03 +00:00
|
|
|
bool m_fake;
|
2006-02-21 19:57:44 +00:00
|
|
|
ObjList* m_targets;
|
|
|
|
Message* m_exec;
|
2009-11-04 17:14:56 +00:00
|
|
|
u_int64_t m_timer;
|
|
|
|
bool m_timerDrop;
|
2010-05-11 15:10:44 +00:00
|
|
|
bool m_execNext;
|
2011-02-28 17:58:26 +00:00
|
|
|
bool m_chanMsgs;
|
2011-07-28 11:15:55 +00:00
|
|
|
bool m_failuresRev;
|
2021-01-14 15:40:40 +00:00
|
|
|
bool m_setId;
|
2006-12-27 16:03:39 +00:00
|
|
|
String m_reason;
|
2007-04-30 22:39:03 +00:00
|
|
|
String m_media;
|
2021-05-28 11:50:18 +00:00
|
|
|
unsigned int m_targetIdx;
|
|
|
|
int m_level;
|
2006-02-14 17:58:41 +00:00
|
|
|
};
|
|
|
|
|
2021-05-28 11:50:18 +00:00
|
|
|
class ForkSlave : public CallEndpoint, public DebugEnabler
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
|
|
|
public:
|
2007-02-27 23:31:33 +00:00
|
|
|
enum Type {
|
2021-05-28 11:50:18 +00:00
|
|
|
Unknown = 0,
|
|
|
|
Regular,
|
2007-02-27 23:31:33 +00:00
|
|
|
Auxiliar,
|
|
|
|
Persistent
|
|
|
|
};
|
2006-02-14 17:58:41 +00:00
|
|
|
ForkSlave(ForkMaster* master, const char* id);
|
|
|
|
virtual ~ForkSlave();
|
2012-09-14 12:01:53 +00:00
|
|
|
virtual void destroyed();
|
2006-02-14 17:58:41 +00:00
|
|
|
virtual void disconnected(bool final, const char* reason);
|
2021-05-28 11:50:18 +00:00
|
|
|
void clearMaster(RefPointer<ForkMaster>* master = 0);
|
2006-02-14 17:58:41 +00:00
|
|
|
inline void lostMaster(const char* reason)
|
2021-05-28 11:50:18 +00:00
|
|
|
{ clearMaster(); disconnect(reason); }
|
|
|
|
inline bool isMaster(ForkMaster* master)
|
|
|
|
{ return master && m_master == master; }
|
2007-02-27 23:31:33 +00:00
|
|
|
inline Type type() const
|
|
|
|
{ return m_type; }
|
|
|
|
inline void setType(Type type)
|
|
|
|
{ m_type = type; }
|
2006-02-14 17:58:41 +00:00
|
|
|
protected:
|
|
|
|
ForkMaster* m_master;
|
2007-02-27 23:31:33 +00:00
|
|
|
Type m_type;
|
2021-05-28 11:50:18 +00:00
|
|
|
int m_level;
|
2006-02-14 17:58:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class ForkModule : public Module
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ForkModule();
|
|
|
|
virtual ~ForkModule();
|
2009-02-04 14:35:12 +00:00
|
|
|
bool unload();
|
2011-04-07 21:00:09 +00:00
|
|
|
bool msgToSlaves(const Message& msg, const String& match);
|
2006-02-14 17:58:41 +00:00
|
|
|
protected:
|
|
|
|
virtual void initialize();
|
|
|
|
virtual bool received(Message& msg, int id);
|
2006-12-21 22:36:32 +00:00
|
|
|
virtual void statusParams(String& str);
|
2006-02-14 17:58:41 +00:00
|
|
|
bool msgExecute(Message& msg);
|
2009-05-20 12:34:43 +00:00
|
|
|
bool msgLocate(Message& msg, bool masquerade);
|
2006-02-14 17:58:41 +00:00
|
|
|
bool msgToMaster(Message& msg, bool answer);
|
2011-04-07 21:00:09 +00:00
|
|
|
bool m_hasRelays;
|
|
|
|
};
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
|
2007-02-27 23:31:33 +00:00
|
|
|
static TokenDict s_calltypes[] = {
|
|
|
|
{ "regular", ForkSlave::Regular },
|
|
|
|
{ "auxiliar", ForkSlave::Auxiliar },
|
|
|
|
{ "persistent", ForkSlave::Persistent },
|
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
INIT_PLUGIN(ForkModule);
|
|
|
|
|
2009-02-04 14:35:12 +00:00
|
|
|
UNLOAD_PLUGIN(unloadNow)
|
|
|
|
{
|
|
|
|
if (unloadNow)
|
|
|
|
return __plugin.unload();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
|
2012-06-12 23:47:01 +00:00
|
|
|
class ForkRelay : public MessageHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline ForkRelay(const char* name, const char* match, int priority)
|
|
|
|
: MessageHandler(name,priority,__plugin.name()),
|
|
|
|
m_match(match)
|
|
|
|
{ }
|
|
|
|
virtual bool received(Message& msg)
|
|
|
|
{ return __plugin.msgToSlaves(msg,m_match); }
|
|
|
|
private:
|
|
|
|
String m_match;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2021-05-28 11:50:18 +00:00
|
|
|
ForkMaster::ForkMaster(ObjList* targets, int lvl)
|
2006-02-23 13:46:20 +00:00
|
|
|
: m_index(0), m_answered(false), m_rtpForward(false), m_rtpStrict(false),
|
2009-11-04 17:14:56 +00:00
|
|
|
m_fake(false), m_targets(targets), m_exec(0),
|
2011-02-28 17:58:26 +00:00
|
|
|
m_timer(0), m_timerDrop(false), m_execNext(false), m_chanMsgs(false),
|
2021-05-28 11:50:18 +00:00
|
|
|
m_failuresRev(false), m_setId(false), m_reason("hangup"),
|
|
|
|
m_targetIdx(0), m_level(lvl)
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
|
|
|
String tmp(MOD_PREFIX "/");
|
|
|
|
tmp << ++s_current;
|
|
|
|
setId(tmp);
|
2021-05-28 11:50:18 +00:00
|
|
|
debugName(id());
|
|
|
|
debugChain(&__plugin);
|
|
|
|
if (m_level > 0)
|
|
|
|
debugLevel(m_level);
|
2006-02-14 17:58:41 +00:00
|
|
|
s_calls.append(this);
|
2021-05-28 11:50:18 +00:00
|
|
|
DDebug(this,DebugAll,"ForkMaster::ForkMaster(%p) [%p]",targets,this);
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ForkMaster::~ForkMaster()
|
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
DDebug(this,DebugAll,"ForkMaster::~ForkMaster() [%p]",this);
|
2009-11-04 17:14:56 +00:00
|
|
|
m_timer = 0;
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2006-02-14 17:58:41 +00:00
|
|
|
s_calls.remove(this,false);
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2007-02-27 23:31:33 +00:00
|
|
|
clear(false);
|
2011-11-15 18:47:43 +00:00
|
|
|
if (m_discPeer && !m_answered) {
|
|
|
|
RefPointer<CallEndpoint> call = m_discPeer->getPeer();
|
|
|
|
if (call) {
|
|
|
|
Message* r = new Message("chan.replaced",0,true);
|
|
|
|
r->addParam("id",id());
|
|
|
|
r->addParam("newid",call->id());
|
|
|
|
r->addParam("peerid",m_discPeer->id());
|
|
|
|
r->userData(this);
|
|
|
|
Engine::enqueue(r);
|
|
|
|
call = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_discPeer = 0;
|
2011-02-28 17:58:26 +00:00
|
|
|
if (m_chanMsgs) {
|
|
|
|
Message* msg = new Message("chan.hangup");
|
|
|
|
msg->addParam("id",id());
|
|
|
|
msg->addParam("cdrtrack",String::boolText(false));
|
|
|
|
Engine::enqueue(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ForkMaster::disconnected(bool final, const char* reason)
|
|
|
|
{
|
|
|
|
CallEndpoint::disconnected(final,reason);
|
2011-11-15 18:47:43 +00:00
|
|
|
if (m_chanMsgs && !(final || m_answered || m_discPeer)) {
|
2011-02-28 17:58:26 +00:00
|
|
|
Message* msg = new Message("chan.disconnected");
|
|
|
|
msg->addParam("id",id());
|
|
|
|
if (m_exec)
|
|
|
|
msg->copyParams(*m_exec,"error,reason");
|
|
|
|
msg->userData(this);
|
|
|
|
Engine::enqueue(msg);
|
|
|
|
}
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2006-02-21 19:57:44 +00:00
|
|
|
String* ForkMaster::getNextDest()
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
2006-02-21 19:57:44 +00:00
|
|
|
String* ret = 0;
|
|
|
|
while (!ret && m_targets && m_targets->count())
|
|
|
|
ret = static_cast<String*>(m_targets->remove(false));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-07-12 14:18:39 +00:00
|
|
|
bool ForkMaster::forkSlave(String* dest)
|
2006-02-21 19:57:44 +00:00
|
|
|
{
|
|
|
|
if (null(dest))
|
2006-02-14 17:58:41 +00:00
|
|
|
return false;
|
2021-05-28 11:50:18 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
Debugger dbg(debugAt(DebugAll) ? DebugAll : 50,
|
|
|
|
"ForkMaster forkSlave"," '%s' dest='%s' [%p]",id().c_str(),dest->c_str(),this);
|
|
|
|
#endif
|
2006-02-21 19:57:44 +00:00
|
|
|
bool ok = false;
|
|
|
|
m_exec->clearParam("error");
|
|
|
|
m_exec->clearParam("reason");
|
2010-09-24 15:04:22 +00:00
|
|
|
Message msgCopy(*m_exec);
|
2011-07-12 14:18:39 +00:00
|
|
|
msgCopy.setParam("callto",*dest);
|
2010-09-24 15:04:22 +00:00
|
|
|
msgCopy.setParam("rtp_forward",String::boolText(m_rtpForward));
|
|
|
|
msgCopy.setParam("cdrtrack",String::boolText(false));
|
2011-07-12 14:18:39 +00:00
|
|
|
NamedList* params = YOBJECT(NamedList,dest);
|
|
|
|
if (params)
|
|
|
|
msgCopy.copyParams(*params);
|
2012-05-10 08:39:12 +00:00
|
|
|
const char* error = "failure";
|
2010-05-11 15:10:44 +00:00
|
|
|
if (m_execNext) {
|
|
|
|
RefPointer<CallEndpoint> peer = getPeer();
|
|
|
|
if (!peer) {
|
|
|
|
clear(false);
|
|
|
|
return false;
|
|
|
|
}
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugCall,"Call '%s' directly to target '%s' [%p]",
|
|
|
|
peer->id().c_str(),dest->c_str(),this);
|
2011-11-15 18:47:43 +00:00
|
|
|
m_discPeer = peer;
|
2010-09-24 15:04:22 +00:00
|
|
|
msgCopy.userData(peer);
|
|
|
|
msgCopy.setParam("id",peer->id());
|
|
|
|
msgCopy.clearParam("cdrtrack");
|
2012-05-10 08:39:12 +00:00
|
|
|
if (!Engine::dispatch(msgCopy)) {
|
|
|
|
error = msgCopy.getValue("error",error);
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugNote,"Call '%s' failed non-fork to target '%s', error '%s' [%p]",
|
|
|
|
getPeerId().c_str(),dest->c_str(),error,this);
|
2010-05-11 15:10:44 +00:00
|
|
|
return false;
|
2012-05-10 08:39:12 +00:00
|
|
|
}
|
2010-05-11 15:10:44 +00:00
|
|
|
clear(false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
String tmp(id());
|
|
|
|
tmp << "/" << ++m_index;
|
|
|
|
ForkSlave* slave = new ForkSlave(this,tmp);
|
2010-09-24 15:04:22 +00:00
|
|
|
msgCopy.setParam("id",tmp);
|
|
|
|
msgCopy.userData(slave);
|
2007-02-27 21:54:33 +00:00
|
|
|
bool autoring = false;
|
2010-09-24 15:04:22 +00:00
|
|
|
if (Engine::dispatch(msgCopy)) {
|
2006-02-23 13:46:20 +00:00
|
|
|
ok = true;
|
2010-09-24 15:04:22 +00:00
|
|
|
autoring = msgCopy.getBoolValue("fork.autoring");
|
|
|
|
if (m_ringing.null() && (autoring || msgCopy.getBoolValue("fork.ringer")))
|
2006-12-22 16:50:20 +00:00
|
|
|
m_ringing = tmp;
|
2007-02-27 21:54:33 +00:00
|
|
|
else
|
|
|
|
autoring = false;
|
2006-02-23 13:46:20 +00:00
|
|
|
if (m_rtpForward) {
|
2010-09-24 15:04:22 +00:00
|
|
|
String rtp(msgCopy.getValue("rtp_forward"));
|
2006-02-23 13:46:20 +00:00
|
|
|
if (rtp != "accepted") {
|
|
|
|
error = "nomedia";
|
|
|
|
int level = DebugWarn;
|
|
|
|
if (m_rtpStrict) {
|
|
|
|
ok = false;
|
|
|
|
level = DebugCall;
|
|
|
|
}
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,level,"Call '%s' did not get RTP forward from '%s' target '%s' [%p]",
|
|
|
|
getPeerId().c_str(),slave->getPeerId().c_str(),dest->c_str(),this);
|
2006-02-23 13:46:20 +00:00
|
|
|
}
|
|
|
|
}
|
2010-09-24 15:04:22 +00:00
|
|
|
m_exec->copyParams(msgCopy,"error,reason,rtp_forward");
|
2006-02-23 13:46:20 +00:00
|
|
|
}
|
|
|
|
else
|
2010-09-24 15:04:22 +00:00
|
|
|
error = msgCopy.getValue("error",error);
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"Executed slave (%p) '%s' refs=%u ok=%u [%p]",
|
|
|
|
slave,slave->id().c_str(),slave->refcount(),ok,this);
|
2010-09-24 15:04:22 +00:00
|
|
|
msgCopy.userData(0);
|
2021-05-28 11:50:18 +00:00
|
|
|
// Avoid adding slave to list if already terminated (master reset)
|
|
|
|
// Avoid adding a slave with refcount=1:this will trigger re-enter in callContinue() on slave destroy
|
|
|
|
bool master = slave->isMaster(this);
|
|
|
|
if (ok && master && slave->refcount() > 1) {
|
2010-09-24 15:04:22 +00:00
|
|
|
ForkSlave::Type type = static_cast<ForkSlave::Type>(msgCopy.getIntValue("fork.calltype",s_calltypes,ForkSlave::Regular));
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugCall,"Call '%s' calling on %s '%s' target '%s' [%p]",
|
|
|
|
getPeerId().c_str(),lookup(type,s_calltypes),tmp.c_str(),dest->c_str(),this);
|
2007-02-27 23:31:33 +00:00
|
|
|
slave->setType(type);
|
2006-02-21 19:57:44 +00:00
|
|
|
m_slaves.append(slave);
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugInfo,"Added slave (%p) '%s' refs=%u [%p]",
|
|
|
|
slave,slave->id().c_str(),slave->refcount(),this);
|
2007-02-27 21:54:33 +00:00
|
|
|
if (autoring) {
|
2010-09-24 15:04:22 +00:00
|
|
|
Message* ring = new Message(msgCopy.getValue("fork.automessage","call.ringing"));
|
2007-02-27 21:54:33 +00:00
|
|
|
ring->addParam("id",slave->getPeerId());
|
|
|
|
ring->addParam("peerid",tmp);
|
|
|
|
ring->addParam("targetid",tmp);
|
|
|
|
Engine::enqueue(ring);
|
|
|
|
}
|
2006-02-21 19:57:44 +00:00
|
|
|
}
|
2012-05-10 08:39:12 +00:00
|
|
|
else {
|
2021-05-28 11:50:18 +00:00
|
|
|
if (!ok)
|
|
|
|
Debug(this,DebugNote,"Call '%s' failed on '%s' target '%s', error '%s' [%p]",
|
|
|
|
getPeerId().c_str(),tmp.c_str(),dest->c_str(),error,this);
|
|
|
|
else if (!master)
|
|
|
|
Debug(this,DebugAll,"Call '%s' target '%s' slave '%s' lost master during execute [%p]",
|
|
|
|
getPeerId().c_str(),dest->c_str(),tmp.c_str(),this);
|
|
|
|
else
|
|
|
|
Debug(this,DebugAll,"Call '%s' target '%s' slave '%s' execute succeeded with no peer [%p]",
|
|
|
|
getPeerId().c_str(),dest->c_str(),tmp.c_str(),this);
|
|
|
|
ok = false;
|
2020-12-08 20:43:27 +00:00
|
|
|
// error propagation via lostMaster() does not work in this case. No channel was created, hence no messages are created
|
|
|
|
m_exec->setParam("error", error);
|
2006-02-23 13:46:20 +00:00
|
|
|
slave->lostMaster(error);
|
2012-05-10 08:39:12 +00:00
|
|
|
}
|
2006-02-21 19:57:44 +00:00
|
|
|
slave->deref();
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ForkMaster::startCalling(Message& msg)
|
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
Debugger dbg(debugAt(DebugInfo) ? DebugInfo : 50,
|
|
|
|
"ForkMaster startCalling"," '%s' [%p]",id().c_str(),this);
|
|
|
|
#endif
|
2006-02-21 19:57:44 +00:00
|
|
|
m_exec = new Message(msg);
|
2011-02-28 17:58:26 +00:00
|
|
|
m_chanMsgs = msg.getBoolValue("fork.chanmsgs",(msg.getParam("pbxoper") != 0));
|
|
|
|
if (m_chanMsgs) {
|
|
|
|
Message* m = new Message("chan.startup");
|
|
|
|
m->addParam("id",id());
|
2011-02-28 18:10:51 +00:00
|
|
|
m->addParam("module",__plugin.name());
|
2011-02-28 17:58:26 +00:00
|
|
|
m->addParam("status","outgoing");
|
|
|
|
m->addParam("cdrtrack",String::boolText(false));
|
|
|
|
m->addParam("pbxguest",String::boolText(true));
|
|
|
|
m->addParam("fork.origid",getPeerId());
|
2011-02-28 18:10:51 +00:00
|
|
|
m->copyParams(msg,"caller,callername,called,billid,username");
|
2011-02-28 17:58:26 +00:00
|
|
|
Engine::enqueue(m);
|
|
|
|
}
|
2006-12-27 16:03:39 +00:00
|
|
|
// stoperror is OBSOLETE
|
|
|
|
m_failures = msg.getValue("fork.stop",msg.getValue("stoperror"));
|
2011-07-28 11:15:55 +00:00
|
|
|
if (m_failures.endsWith("^")) {
|
|
|
|
m_failuresRev = true;
|
|
|
|
m_failures = m_failures.substr(0,m_failures.length()-1);
|
|
|
|
}
|
2021-01-14 15:40:40 +00:00
|
|
|
m_setId = msg.getBoolValue("fork.setid");
|
2006-02-23 13:46:20 +00:00
|
|
|
m_exec->clearParam("stoperror");
|
2006-12-27 16:03:39 +00:00
|
|
|
m_exec->clearParam("fork.stop");
|
2021-01-14 15:40:40 +00:00
|
|
|
m_exec->clearParam("fork.setid");
|
2011-02-28 16:18:21 +00:00
|
|
|
m_exec->clearParam("peerid");
|
2006-12-27 16:03:39 +00:00
|
|
|
m_exec->setParam("fork.master",id());
|
|
|
|
m_exec->setParam("fork.origid",getPeerId());
|
2006-02-23 13:46:20 +00:00
|
|
|
m_rtpForward = msg.getBoolValue("rtp_forward");
|
|
|
|
m_rtpStrict = msg.getBoolValue("rtpstrict");
|
2006-02-21 19:57:44 +00:00
|
|
|
if (!callContinue()) {
|
Callfork: Inconsistent group progression
Callforks support three types of call legs:
- regular
- auxiliar
- persistent
These different types change the behavior of how groups progress.
If no regular calls are left in a group, the fork progresses
immediately to the next group, drops all auxiliar children and keeps
the persistent ones. However, there is inconsistent behavior depending
on whether the creation of a call leg is successful or not.
Currently, the check if there are regular calls left in the current
group is only implemented in lostSlave(). So, if you have a group
with one persistent member (ringback) and one regular member, you can
observe different behavior in the case that routing for the member fails
and in the situation that the call is started but then rejected on a remote
end.
If the regular member is remote, forkSlave() will succeed and initiate a sip
call. The callContinue() function will return. Then, the sip call might fail
because the remote member is offline. Now lostSlave() is called, the module
detects that this was the last regular member and immediately progresses
to the next group.
If the regular member, however, is local, the routing knows that the member
is offline and fails. In this case forkSlave() will fail. Nonetheless, forks = 1
because creation of the persistent slave (ringback) was succesful. Consequently,
the call continues but it is ringing nowhere. In case that the next group is timed,
the caller will just hear the ringback for a while. If the next group isn't timed
but expected to progress if all calls in the current group have failed, the call
is actually stuck and the caller will hear the ringback indefinately while it is
nowhere ringing.
This patch fixes this behavior by checking if regular call legs are active before
returning from callContinue(). If this isn't the case, it will trigger progression
to the next group immediately.
2020-12-08 21:43:28 +00:00
|
|
|
Debug(&__plugin,DebugNote,"Fork '%s' startCalling() failed. No successful peers", id().c_str());
|
2009-11-04 17:14:56 +00:00
|
|
|
const char* err = m_exec->getValue("reason");
|
|
|
|
if (err)
|
|
|
|
msg.setParam("reason",err);
|
|
|
|
err = m_exec->getValue("error");
|
|
|
|
msg.setParam("error",err);
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"startCalling failed refs=%u [%p]",refcount(),this);
|
2009-11-04 17:14:56 +00:00
|
|
|
disconnect(err);
|
2006-02-21 19:57:44 +00:00
|
|
|
return false;
|
|
|
|
}
|
2006-02-23 13:46:20 +00:00
|
|
|
if (m_rtpForward) {
|
|
|
|
String tmp(m_exec->getValue("rtp_forward"));
|
|
|
|
if (tmp != "accepted") {
|
|
|
|
// no RTP forwarding from now on
|
|
|
|
m_rtpForward = false;
|
|
|
|
tmp = String::boolText(false);
|
|
|
|
}
|
|
|
|
msg.setParam("rtp_forward",tmp);
|
|
|
|
}
|
2006-02-21 19:57:44 +00:00
|
|
|
msg.setParam("peerid",id());
|
|
|
|
msg.setParam("targetid",id());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ForkMaster::callContinue()
|
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
Debugger dbg(debugAt(DebugInfo) ? DebugInfo : 50,
|
|
|
|
"ForkMaster callContinue"," '%s' [%p]",id().c_str(),this);
|
|
|
|
#endif
|
2009-11-04 17:14:56 +00:00
|
|
|
m_timer = 0;
|
|
|
|
m_timerDrop = false;
|
2006-02-14 17:58:41 +00:00
|
|
|
int forks = 0;
|
2006-02-21 19:57:44 +00:00
|
|
|
while (m_exec && !m_answered) {
|
2007-04-30 22:39:03 +00:00
|
|
|
// get the fake media source at start of each group
|
|
|
|
m_media = m_exec->getValue("fork.fake");
|
2006-02-21 19:57:44 +00:00
|
|
|
String* dest = getNextDest();
|
2006-02-14 17:58:41 +00:00
|
|
|
if (!dest)
|
2006-02-21 19:57:44 +00:00
|
|
|
break;
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"Handling target #%u '%s' [%p]",++m_targetIdx,dest->c_str(),this);
|
2009-11-04 17:14:56 +00:00
|
|
|
if (dest->startSkip("|",false)) {
|
2012-05-10 08:39:12 +00:00
|
|
|
m_execNext = false;
|
2009-11-04 17:14:56 +00:00
|
|
|
if (*dest) {
|
|
|
|
String tmp(*dest);
|
|
|
|
int tout = 0;
|
|
|
|
if (tmp.startSkip("next=",false) && ((tout = tmp.toInteger()) > 0)) {
|
|
|
|
m_timer = 1000 * tout + Time::now();
|
|
|
|
m_timerDrop = false;
|
|
|
|
}
|
|
|
|
else if (tmp.startSkip("drop=",false) && ((tout = tmp.toInteger()) > 0)) {
|
|
|
|
m_timer = 1000 * tout + Time::now();
|
|
|
|
m_timerDrop = true;
|
|
|
|
}
|
2010-05-11 15:10:44 +00:00
|
|
|
else if (tmp.startSkip("exec=",false) && ((tout = tmp.toInteger()) > 0)) {
|
|
|
|
m_timer = 1000 * tout + Time::now();
|
|
|
|
m_timerDrop = true;
|
|
|
|
m_execNext = true;
|
|
|
|
}
|
|
|
|
else if (tmp == "exec")
|
|
|
|
m_execNext = true;
|
2009-11-04 17:14:56 +00:00
|
|
|
else
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugMild,"Call '%s' ignoring modifier '%s' [%p]",
|
|
|
|
getPeerId().c_str(),dest->c_str(),this);
|
2009-11-04 17:14:56 +00:00
|
|
|
}
|
2006-02-21 19:57:44 +00:00
|
|
|
dest->destruct();
|
Callfork: Inconsistent group progression
Callforks support three types of call legs:
- regular
- auxiliar
- persistent
These different types change the behavior of how groups progress.
If no regular calls are left in a group, the fork progresses
immediately to the next group, drops all auxiliar children and keeps
the persistent ones. However, there is inconsistent behavior depending
on whether the creation of a call leg is successful or not.
Currently, the check if there are regular calls left in the current
group is only implemented in lostSlave(). So, if you have a group
with one persistent member (ringback) and one regular member, you can
observe different behavior in the case that routing for the member fails
and in the situation that the call is started but then rejected on a remote
end.
If the regular member is remote, forkSlave() will succeed and initiate a sip
call. The callContinue() function will return. Then, the sip call might fail
because the remote member is offline. Now lostSlave() is called, the module
detects that this was the last regular member and immediately progresses
to the next group.
If the regular member, however, is local, the routing knows that the member
is offline and fails. In this case forkSlave() will fail. Nonetheless, forks = 1
because creation of the persistent slave (ringback) was succesful. Consequently,
the call continues but it is ringing nowhere. In case that the next group is timed,
the caller will just hear the ringback for a while. If the next group isn't timed
but expected to progress if all calls in the current group have failed, the call
is actually stuck and the caller will hear the ringback indefinately while it is
nowhere ringing.
This patch fixes this behavior by checking if regular call legs are active before
returning from callContinue(). If this isn't the case, it will trigger progression
to the next group immediately.
2020-12-08 21:43:28 +00:00
|
|
|
if (forks) {
|
|
|
|
unsigned int regulars = 0;
|
|
|
|
unsigned int auxiliars = 0;
|
|
|
|
unsigned int persistents = 0;
|
|
|
|
for (ObjList* l = m_slaves.skipNull(); l; l = l->skipNext()) {
|
|
|
|
switch (static_cast<const ForkSlave*>(l->get())->type()) {
|
|
|
|
case ForkSlave::Auxiliar:
|
|
|
|
auxiliars++;
|
|
|
|
break;
|
|
|
|
case ForkSlave::Persistent:
|
|
|
|
persistents++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
regulars++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (regulars > 0) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
clear(true);
|
|
|
|
forks = persistents;
|
|
|
|
}
|
|
|
|
}
|
2012-05-10 08:39:12 +00:00
|
|
|
m_timer = 0;
|
|
|
|
m_timerDrop = false;
|
2006-02-14 17:58:41 +00:00
|
|
|
continue;
|
|
|
|
}
|
2011-07-12 14:18:39 +00:00
|
|
|
if (forkSlave(dest))
|
2006-02-21 19:57:44 +00:00
|
|
|
++forks;
|
|
|
|
dest->destruct();
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"Exiting callContinue forks=%u [%p]",forks,this);
|
2006-02-21 19:57:44 +00:00
|
|
|
return (forks > 0);
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2009-11-04 17:14:56 +00:00
|
|
|
void ForkMaster::checkTimer(const Time& tmr)
|
|
|
|
{
|
|
|
|
if (!m_timer || m_timer > tmr)
|
|
|
|
return;
|
|
|
|
m_timer = 0;
|
|
|
|
if (m_timerDrop) {
|
|
|
|
m_timerDrop = false;
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugNote,"Call '%s' dropping slaves on timer [%p]",
|
|
|
|
getPeerId().c_str(),this);
|
2009-11-04 17:14:56 +00:00
|
|
|
clear(true);
|
|
|
|
}
|
|
|
|
else
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugNote,"Call '%s' calling more on timer [%p]",
|
|
|
|
getPeerId().c_str(),this);
|
2009-11-04 17:14:56 +00:00
|
|
|
callContinue();
|
|
|
|
}
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
void ForkMaster::lostSlave(ForkSlave* slave, const char* reason)
|
|
|
|
{
|
2010-01-17 17:44:35 +00:00
|
|
|
Lock lock(CallEndpoint::commonMutex());
|
2012-01-04 15:52:58 +00:00
|
|
|
bool ringing = clearRinging(slave->id());
|
2021-05-28 11:50:18 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
GenObject* gen =
|
|
|
|
#endif
|
2006-02-14 17:58:41 +00:00
|
|
|
m_slaves.remove(slave,false);
|
2021-05-28 11:50:18 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
XDebug(this,gen ? DebugInfo : DebugMild,"Removed%s slave (%p) '%s' refs=%u [%p]",
|
|
|
|
(gen ? "":" MISSING"),slave,slave->id().c_str(),slave->refcount(),this);
|
|
|
|
#endif
|
2006-02-21 19:57:44 +00:00
|
|
|
if (m_answered)
|
|
|
|
return;
|
2011-12-21 16:06:17 +00:00
|
|
|
if (reason)
|
|
|
|
m_exec->setParam("fork.reason",reason);
|
2011-07-28 11:15:55 +00:00
|
|
|
if (reason && m_failures && (m_failures.matches(reason) != m_failuresRev)) {
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugCall,"Call '%s' terminating early on reason '%s' [%p]",
|
|
|
|
getPeerId().c_str(),reason,this);
|
2006-02-21 19:57:44 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-05-28 11:50:18 +00:00
|
|
|
// Slave have no type: we are still processing it, continue from processing point
|
|
|
|
if (!slave->type())
|
|
|
|
return;
|
2007-02-27 23:31:33 +00:00
|
|
|
unsigned int regulars = 0;
|
|
|
|
unsigned int auxiliars = 0;
|
|
|
|
unsigned int persistents = 0;
|
|
|
|
for (ObjList* l = m_slaves.skipNull(); l; l = l->skipNext()) {
|
|
|
|
switch (static_cast<const ForkSlave*>(l->get())->type()) {
|
|
|
|
case ForkSlave::Auxiliar:
|
|
|
|
auxiliars++;
|
|
|
|
break;
|
|
|
|
case ForkSlave::Persistent:
|
|
|
|
persistents++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
regulars++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugNote,
|
|
|
|
"Call '%s' lost%s slave '%s' reason '%s' remaining %u regulars, %u auxiliars, %u persistent [%p]",
|
2006-12-22 16:50:20 +00:00
|
|
|
getPeerId().c_str(),ringing ? " ringing" : "",
|
2007-02-27 23:31:33 +00:00
|
|
|
slave->id().c_str(),reason,
|
2021-05-28 11:50:18 +00:00
|
|
|
regulars,auxiliars,persistents,this);
|
2007-02-27 23:31:33 +00:00
|
|
|
if (auxiliars && !regulars) {
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugNote,"Dropping remaining %u auxiliars [%p]",auxiliars,this);
|
2007-02-27 23:31:33 +00:00
|
|
|
clear(true);
|
|
|
|
}
|
|
|
|
if (regulars || callContinue())
|
2006-02-21 19:57:44 +00:00
|
|
|
return;
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugCall,"Call '%s' failed after %d attempts with reason '%s' [%p]",
|
|
|
|
getPeerId().c_str(),m_index,reason,this);
|
2006-02-21 19:57:44 +00:00
|
|
|
}
|
2009-11-04 17:14:56 +00:00
|
|
|
m_timer = 0;
|
2006-02-21 19:57:44 +00:00
|
|
|
lock.drop();
|
|
|
|
disconnect(reason);
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2009-05-20 13:08:01 +00:00
|
|
|
bool ForkMaster::msgAnswered(Message& msg, const String& dest)
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
2010-01-17 17:44:35 +00:00
|
|
|
Lock lock(CallEndpoint::commonMutex());
|
2009-11-04 17:14:56 +00:00
|
|
|
m_timer = 0;
|
2006-02-14 17:58:41 +00:00
|
|
|
// make sure only the first succeeds
|
|
|
|
if (m_answered)
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2006-02-14 17:58:41 +00:00
|
|
|
RefPointer<CallEndpoint> peer = getPeer();
|
|
|
|
if (!peer)
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2006-02-14 17:58:41 +00:00
|
|
|
ForkSlave* slave = static_cast<ForkSlave*>(m_slaves[dest]);
|
|
|
|
if (!slave)
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2006-02-14 17:58:41 +00:00
|
|
|
RefPointer<CallEndpoint> call = slave->getPeer();
|
|
|
|
if (!call)
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2007-04-30 22:39:03 +00:00
|
|
|
m_media.clear();
|
|
|
|
m_fake = false;
|
2006-02-14 17:58:41 +00:00
|
|
|
m_answered = true;
|
2006-12-27 16:03:39 +00:00
|
|
|
m_reason = msg.getValue("reason","pickup");
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugCall,"Call '%s' answered on '%s' by '%s' [%p]",
|
|
|
|
peer->id().c_str(),dest.c_str(),call->id().c_str(),this);
|
2021-01-14 15:40:40 +00:00
|
|
|
if (m_setId) {
|
|
|
|
msg.setParam("fork.origid",msg.getValue("id"));
|
|
|
|
msg.setParam("id",id());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
msg.setParam("fork.master",id());
|
2006-02-14 17:58:41 +00:00
|
|
|
msg.setParam("peerid",peer->id());
|
|
|
|
msg.setParam("targetid",peer->id());
|
2011-03-24 11:57:47 +00:00
|
|
|
Message* r = new Message("chan.replaced",0,true);
|
|
|
|
r->addParam("id",id());
|
|
|
|
r->addParam("newid",call->id());
|
|
|
|
r->addParam("peerid",peer->id());
|
|
|
|
r->addParam("id.1",dest);
|
|
|
|
r->addParam("newid.1",peer->id());
|
|
|
|
r->addParam("peerid.1",call->id());
|
2006-02-14 17:58:41 +00:00
|
|
|
lock.drop();
|
2006-12-22 16:50:20 +00:00
|
|
|
clearEndpoint();
|
2006-02-14 17:58:41 +00:00
|
|
|
call->connect(peer);
|
2011-03-24 11:57:47 +00:00
|
|
|
Engine::enqueue(r);
|
2009-05-20 13:08:01 +00:00
|
|
|
return true;
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2009-05-20 13:08:01 +00:00
|
|
|
bool ForkMaster::msgProgress(Message& msg, const String& dest)
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
2010-01-17 17:44:35 +00:00
|
|
|
Lock lock(CallEndpoint::commonMutex());
|
2006-02-14 17:58:41 +00:00
|
|
|
if (m_answered)
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2006-02-14 17:58:41 +00:00
|
|
|
if (m_ringing && (m_ringing != dest))
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2009-11-04 17:14:56 +00:00
|
|
|
|
2007-02-23 20:14:25 +00:00
|
|
|
ForkSlave* slave = static_cast<ForkSlave*>(m_slaves[dest]);
|
|
|
|
if (!slave)
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2006-02-14 17:58:41 +00:00
|
|
|
RefPointer<CallEndpoint> peer = getPeer();
|
|
|
|
if (!peer)
|
2009-05-20 13:08:01 +00:00
|
|
|
return false;
|
2010-09-15 10:30:35 +00:00
|
|
|
RefPointer<DataEndpoint> dataEp = getEndpoint();
|
2006-02-14 17:58:41 +00:00
|
|
|
if (m_ringing.null())
|
|
|
|
m_ringing = dest;
|
2007-04-30 22:39:03 +00:00
|
|
|
if (m_fake || !dataEp) {
|
2007-02-23 20:14:25 +00:00
|
|
|
const CallEndpoint* call = slave->getPeer();
|
|
|
|
if (!call)
|
2013-04-12 13:19:14 +00:00
|
|
|
call = static_cast<const CallEndpoint*>(msg.userObject(YATOM("CallEndpoint")));
|
2006-12-22 16:50:20 +00:00
|
|
|
if (call) {
|
|
|
|
dataEp = call->getEndpoint();
|
2007-04-30 22:39:03 +00:00
|
|
|
if (dataEp) {
|
|
|
|
// don't use the media if it has no format and fake is possible
|
|
|
|
if ((m_fake || m_media) &&
|
|
|
|
!(dataEp->getSource() && dataEp->getSource()->getFormat()))
|
|
|
|
dataEp = 0;
|
|
|
|
else {
|
|
|
|
m_fake = false;
|
|
|
|
setEndpoint(dataEp);
|
|
|
|
m_media.clear();
|
|
|
|
}
|
|
|
|
}
|
2006-12-22 16:50:20 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-14 15:40:40 +00:00
|
|
|
if (m_setId) {
|
|
|
|
msg.setParam("fork.origid",msg.getValue("id"));
|
|
|
|
msg.setParam("id",id());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
msg.setParam("fork.master",id());
|
2006-02-14 17:58:41 +00:00
|
|
|
msg.setParam("peerid",peer->id());
|
|
|
|
msg.setParam("targetid",peer->id());
|
2007-04-30 22:39:03 +00:00
|
|
|
if (m_media) {
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugInfo,"Call '%s' faking media '%s'",
|
2007-04-30 22:39:03 +00:00
|
|
|
peer->id().c_str(),m_media.c_str());
|
|
|
|
String newMsg;
|
|
|
|
if (m_exec)
|
|
|
|
newMsg = m_exec->getValue("fork.fakemessage");
|
|
|
|
Message m("chan.attach");
|
|
|
|
m.userData(this);
|
|
|
|
m.addParam("id",id());
|
|
|
|
m.addParam("source",m_media);
|
|
|
|
m.addParam("single",String::boolText(true));
|
2007-05-04 12:41:58 +00:00
|
|
|
if (m_exec)
|
|
|
|
m.copyParam(*m_exec,"autorepeat");
|
2007-04-30 22:39:03 +00:00
|
|
|
m_media.clear();
|
|
|
|
lock.drop();
|
|
|
|
if (Engine::dispatch(m)) {
|
|
|
|
m_fake = true;
|
|
|
|
if (newMsg)
|
|
|
|
msg = newMsg;
|
|
|
|
}
|
|
|
|
}
|
2021-05-28 11:50:18 +00:00
|
|
|
Debug(this,DebugNote,"Call '%s' going on '%s' to '%s'%s%s [%p]",
|
2007-04-30 22:39:03 +00:00
|
|
|
peer->id().c_str(),dest.c_str(),msg.getValue("id"),
|
|
|
|
(dataEp || m_fake) ? " with audio data" : "",
|
2021-05-28 11:50:18 +00:00
|
|
|
m_fake ? " (fake)" : "",this);
|
2009-05-20 13:08:01 +00:00
|
|
|
return true;
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2011-04-07 21:00:09 +00:00
|
|
|
bool ForkMaster::msgToSlaves(const Message& msg, const String& match)
|
|
|
|
{
|
|
|
|
bool ok = false;
|
|
|
|
for (ObjList* l = m_slaves.skipNull(); l; l = l->skipNext()) {
|
|
|
|
ForkSlave* slave = static_cast<ForkSlave*>(l->get());
|
|
|
|
if (slave->type() == ForkSlave::Auxiliar)
|
|
|
|
continue;
|
|
|
|
Message* m = new Message(msg);
|
|
|
|
m->setParam(match,slave->getPeerId());
|
|
|
|
m->userData(msg.userData());
|
|
|
|
ok = Engine::enqueue(m) || ok;
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2012-01-04 15:52:58 +00:00
|
|
|
bool ForkMaster::clearRinging(const String& id)
|
|
|
|
{
|
|
|
|
if (m_ringing != id)
|
|
|
|
return false;
|
|
|
|
m_fake = false;
|
|
|
|
m_ringing.clear();
|
|
|
|
clearEndpoint();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-02-27 23:31:33 +00:00
|
|
|
void ForkMaster::clear(bool softly)
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"Clearing [%p]",this);
|
2006-04-01 11:04:03 +00:00
|
|
|
RefPointer<ForkSlave> slave;
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2006-02-14 17:58:41 +00:00
|
|
|
ListIterator iter(m_slaves);
|
2014-07-31 16:30:15 +00:00
|
|
|
while ((slave = static_cast<ForkSlave*>(iter.get()))) {
|
2007-02-27 23:31:33 +00:00
|
|
|
if (softly && (slave->type() == ForkSlave::Persistent))
|
|
|
|
continue;
|
2012-01-04 15:52:58 +00:00
|
|
|
clearRinging(slave->id());
|
2006-02-14 17:58:41 +00:00
|
|
|
m_slaves.remove(slave,false);
|
2009-05-20 12:34:43 +00:00
|
|
|
slave->clearMaster();
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2006-12-27 16:03:39 +00:00
|
|
|
slave->lostMaster(m_reason);
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2006-04-01 11:04:03 +00:00
|
|
|
slave = 0;
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
2007-02-27 23:31:33 +00:00
|
|
|
if (softly) {
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2007-02-27 23:31:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2007-05-15 15:40:50 +00:00
|
|
|
TelEngine::destruct(m_exec);
|
|
|
|
TelEngine::destruct(m_targets);
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ForkSlave::ForkSlave(ForkMaster* master, const char* id)
|
2021-05-28 11:50:18 +00:00
|
|
|
: CallEndpoint(id), m_master(master), m_type(Unknown), m_level(0)
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
debugName(CallEndpoint::id());
|
|
|
|
if (master) {
|
|
|
|
debugChain(master);
|
|
|
|
m_level = master->m_level;
|
|
|
|
if (m_level > 0)
|
|
|
|
debugLevel(m_level);
|
|
|
|
}
|
|
|
|
DDebug(this,DebugAll,"ForkSlave::ForkSlave(%s) [%p]",master->id().c_str(),this);
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ForkSlave::~ForkSlave()
|
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
DDebug(this,DebugAll,"ForkSlave::~ForkSlave() [%p]",this);
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2012-09-14 12:01:53 +00:00
|
|
|
void ForkSlave::destroyed()
|
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"Destroying [%p]",this);
|
2012-09-14 12:01:53 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2021-05-28 11:50:18 +00:00
|
|
|
RefPointer<ForkMaster> master;
|
|
|
|
clearMaster(&master);
|
2012-09-14 12:01:53 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
|
|
|
if (master)
|
|
|
|
master->lostSlave(this,0);
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"Destroyed [%p]",this);
|
2012-09-14 12:01:53 +00:00
|
|
|
CallEndpoint::destroyed();
|
|
|
|
}
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
void ForkSlave::disconnected(bool final, const char* reason)
|
|
|
|
{
|
2021-05-28 11:50:18 +00:00
|
|
|
XDebug(this,DebugAll,"Disconnected refs=%u [%p]",refcount(),this);
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2021-05-28 11:50:18 +00:00
|
|
|
RefPointer<ForkMaster> master;
|
|
|
|
clearMaster(&master);
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2006-02-14 17:58:41 +00:00
|
|
|
CallEndpoint::disconnected(final,reason);
|
2009-02-04 14:08:54 +00:00
|
|
|
if (master)
|
|
|
|
master->lostSlave(this,reason);
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2021-05-28 11:50:18 +00:00
|
|
|
void ForkSlave::clearMaster(RefPointer<ForkMaster>* master)
|
|
|
|
{
|
|
|
|
if (master)
|
|
|
|
*master = m_master;
|
|
|
|
m_master = 0;
|
|
|
|
debugChain(&__plugin);
|
|
|
|
if (m_level > 0)
|
|
|
|
debugLevel(m_level);
|
|
|
|
}
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
|
|
|
|
ForkModule::ForkModule()
|
2011-04-07 21:00:09 +00:00
|
|
|
: Module("callfork","misc"),
|
|
|
|
m_hasRelays(false)
|
2006-02-14 17:58:41 +00:00
|
|
|
{
|
|
|
|
Output("Loaded module Call Forker");
|
|
|
|
}
|
|
|
|
|
|
|
|
ForkModule::~ForkModule()
|
|
|
|
{
|
|
|
|
Output("Unloading module Call Forker");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ForkModule::initialize()
|
|
|
|
{
|
|
|
|
Output("Initializing module Call Forker");
|
|
|
|
setup();
|
2011-04-07 21:00:09 +00:00
|
|
|
if (!m_hasRelays) {
|
|
|
|
static const String s_prio("priorities");
|
|
|
|
Configuration cfg(Engine::configFile("callfork"));
|
|
|
|
installRelay(Execute,cfg.getIntValue(s_prio,messageName(Execute),100));
|
|
|
|
installRelay(Masquerade,cfg.getIntValue(s_prio,messageName(Masquerade),10));
|
|
|
|
installRelay(Locate,cfg.getIntValue(s_prio,messageName(Locate),40));
|
|
|
|
installRelay(Answered,cfg.getIntValue(s_prio,messageName(Answered),20));
|
|
|
|
installRelay(Ringing,cfg.getIntValue(s_prio,messageName(Ringing),20));
|
|
|
|
installRelay(Progress,cfg.getIntValue(s_prio,messageName(Progress),20));
|
|
|
|
int prio = cfg.getIntValue(s_prio,"generic",100);
|
|
|
|
const NamedList* generic = cfg.getSection("messages");
|
|
|
|
if (generic) {
|
|
|
|
NamedIterator iter(*generic);
|
|
|
|
while (!iter.eof()) {
|
|
|
|
const NamedString* item = iter.get();
|
|
|
|
if (TelEngine::null(item))
|
|
|
|
continue;
|
|
|
|
switch (relayId(item->name())) {
|
|
|
|
case 0:
|
|
|
|
case Tone:
|
|
|
|
case Text:
|
|
|
|
case Update:
|
|
|
|
case Control:
|
2013-08-13 07:54:00 +00:00
|
|
|
case MsgExecute:
|
2011-04-07 21:00:09 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Debug(this,DebugWarn,"Refusing to fork message '%s'",item->name().c_str());
|
|
|
|
continue;
|
|
|
|
}
|
2011-11-04 21:25:20 +00:00
|
|
|
int p = cfg.getIntValue(s_prio,item->name(),prio);
|
|
|
|
ForkRelay* r = new ForkRelay(item->name(),*item,p);
|
|
|
|
Debug(this,DebugInfo,"Will fork messages '%s' matching '%s' priority %d",
|
|
|
|
item->name().c_str(),item->c_str(),p);
|
2011-04-07 21:00:09 +00:00
|
|
|
Engine::install(r);
|
|
|
|
m_hasRelays = true;
|
|
|
|
}
|
|
|
|
}
|
2011-11-04 21:25:20 +00:00
|
|
|
else {
|
|
|
|
int p = cfg.getIntValue(s_prio,"chan.dtmf",prio);
|
|
|
|
Debug(this,DebugInfo,"Default fork for 'chan.dtmf' matching 'peerid' priority %d",p);
|
|
|
|
Engine::install(new ForkRelay("chan.dtmf","peerid",p));
|
|
|
|
m_hasRelays = true;
|
|
|
|
}
|
2011-04-07 21:00:09 +00:00
|
|
|
}
|
2006-02-14 17:58:41 +00:00
|
|
|
}
|
|
|
|
|
2009-02-04 14:35:12 +00:00
|
|
|
bool ForkModule::unload()
|
|
|
|
{
|
2011-04-07 21:00:09 +00:00
|
|
|
if (m_hasRelays)
|
|
|
|
return false;
|
2010-01-17 17:44:35 +00:00
|
|
|
Lock lock(CallEndpoint::commonMutex(),500000);
|
2009-07-22 12:57:14 +00:00
|
|
|
if (!lock.locked())
|
2009-02-04 14:35:12 +00:00
|
|
|
return false;
|
|
|
|
if (s_calls.count())
|
|
|
|
return false;
|
|
|
|
uninstallRelays();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-12-21 22:36:32 +00:00
|
|
|
void ForkModule::statusParams(String& str)
|
|
|
|
{
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2006-12-21 22:36:32 +00:00
|
|
|
str.append("total=",",") << s_current << ",forks=" << s_calls.count();
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2006-12-21 22:36:32 +00:00
|
|
|
}
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
bool ForkModule::msgExecute(Message& msg)
|
|
|
|
{
|
2012-10-01 11:00:30 +00:00
|
|
|
CallEndpoint* ch = YOBJECT(CallEndpoint,msg.userData());
|
2006-02-14 17:58:41 +00:00
|
|
|
if (!ch)
|
|
|
|
return false;
|
|
|
|
String dest(msg.getParam("callto"));
|
|
|
|
if (!dest.startSkip(MOD_PREFIX))
|
|
|
|
return false;
|
2011-07-12 14:18:39 +00:00
|
|
|
ObjList* targets = 0;
|
|
|
|
if (dest)
|
|
|
|
targets = dest.split(' ',false);
|
|
|
|
else {
|
|
|
|
for (int n = 1; true; n++) {
|
|
|
|
String prefix;
|
|
|
|
prefix << "callto." << n;
|
|
|
|
NamedString* ns = msg.getParam(prefix);
|
|
|
|
if (!ns)
|
|
|
|
break;
|
|
|
|
if (TelEngine::null(ns))
|
|
|
|
continue;
|
|
|
|
// Set target parameters from enclosed list
|
|
|
|
// Override/add new params from message sub-params
|
|
|
|
NamedPointer* np = YOBJECT(NamedPointer,ns);
|
|
|
|
NamedList* target = YOBJECT(NamedList,np);
|
|
|
|
if (target) {
|
|
|
|
np->takeData();
|
|
|
|
target->assign(*ns);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
target = new NamedList(*ns);
|
|
|
|
target->copySubParams(msg,prefix + ".");
|
|
|
|
if (!targets)
|
|
|
|
targets = new ObjList;
|
|
|
|
targets->append(target);
|
|
|
|
// Clear from initial message
|
|
|
|
msg.clearParam(prefix,'.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!(targets && targets->skipNull())) {
|
|
|
|
msg.setParam("error","failure");
|
|
|
|
TelEngine::destruct(targets);
|
2006-02-14 17:58:41 +00:00
|
|
|
return false;
|
2011-07-12 14:18:39 +00:00
|
|
|
}
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2021-05-28 11:50:18 +00:00
|
|
|
ForkMaster* master = new ForkMaster(targets,msg.getIntValue(YSTRING("fork.debug_level")));
|
2009-05-20 14:43:16 +00:00
|
|
|
bool ok = master->connect(ch,msg.getValue("reason")) && master->startCalling(msg);
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2006-02-14 17:58:41 +00:00
|
|
|
master->deref();
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2009-05-20 12:34:43 +00:00
|
|
|
bool ForkModule::msgLocate(Message& msg, bool masquerade)
|
|
|
|
{
|
|
|
|
String tmp(msg.getParam("id"));
|
|
|
|
if (!tmp.startsWith(MOD_PREFIX "/"))
|
|
|
|
return false;
|
2010-01-17 17:44:35 +00:00
|
|
|
Lock lock(CallEndpoint::commonMutex());
|
2009-05-20 12:34:43 +00:00
|
|
|
CallEndpoint* c = static_cast<CallEndpoint*>(s_calls[tmp]);
|
2009-05-20 14:40:02 +00:00
|
|
|
if (!c) {
|
|
|
|
ForkMaster* m = static_cast<ForkMaster*>(s_calls[tmp.substr(0,tmp.rfind('/'))]);
|
|
|
|
if (m)
|
|
|
|
c = static_cast<CallEndpoint*>(m->slaves()[tmp]);
|
|
|
|
}
|
2009-05-20 12:34:43 +00:00
|
|
|
if (!c)
|
|
|
|
return false;
|
|
|
|
if (masquerade) {
|
|
|
|
tmp = msg.getValue("message");
|
|
|
|
if (tmp.null())
|
|
|
|
return false;
|
|
|
|
msg.clearParam("message");
|
|
|
|
msg = tmp;
|
2009-05-20 17:30:08 +00:00
|
|
|
if (tmp == "call.answered")
|
|
|
|
msg.setParam("cdrcreate",String::boolText(false));
|
|
|
|
else if (tmp == "call.execute")
|
|
|
|
msg.setParam("cdrtrack",String::boolText(false));
|
2009-05-20 12:34:43 +00:00
|
|
|
if (c->getPeer())
|
|
|
|
msg.setParam("peerid",c->getPeerId());
|
|
|
|
}
|
|
|
|
msg.userData(c);
|
|
|
|
return !masquerade;
|
|
|
|
}
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
bool ForkModule::msgToMaster(Message& msg, bool answer)
|
|
|
|
{
|
2009-05-20 12:34:43 +00:00
|
|
|
String dest(msg.getParam("peerid"));
|
|
|
|
if (dest.null())
|
|
|
|
dest = msg.getParam("targetid");
|
2006-02-14 17:58:41 +00:00
|
|
|
if (!dest.startsWith(MOD_PREFIX "/"))
|
|
|
|
return false;
|
|
|
|
int slash = dest.rfind('/');
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2006-02-14 17:58:41 +00:00
|
|
|
// the fork master will be kept referenced until we finish the work
|
|
|
|
RefPointer<ForkMaster> m = static_cast<ForkMaster*>(s_calls[dest.substr(0,slash)]);
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2009-05-20 13:08:01 +00:00
|
|
|
if (m)
|
|
|
|
return answer ? m->msgAnswered(msg,dest) : m->msgProgress(msg,dest);
|
2006-02-14 17:58:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-04-07 21:00:09 +00:00
|
|
|
bool ForkModule::msgToSlaves(const Message& msg, const String& match)
|
|
|
|
{
|
|
|
|
if (match.null())
|
|
|
|
return false;
|
|
|
|
const String* param = msg.getParam(match);
|
|
|
|
if (TelEngine::null(param))
|
|
|
|
return false;
|
|
|
|
if (!param->startsWith(MOD_PREFIX "/"))
|
|
|
|
return false;
|
|
|
|
Lock lock(CallEndpoint::commonMutex());
|
|
|
|
ForkMaster* m = static_cast<ForkMaster*>(s_calls[*param]);
|
|
|
|
return m && m->msgToSlaves(msg,match);
|
|
|
|
}
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
bool ForkModule::received(Message& msg, int id)
|
|
|
|
{
|
|
|
|
switch (id) {
|
|
|
|
case Execute:
|
|
|
|
return msgExecute(msg);
|
2009-05-20 12:34:43 +00:00
|
|
|
case Locate:
|
|
|
|
return msgLocate(msg,false);
|
|
|
|
case Masquerade:
|
|
|
|
return msgLocate(msg,true);
|
2006-02-14 17:58:41 +00:00
|
|
|
case Answered:
|
2009-05-20 13:08:01 +00:00
|
|
|
while (msgToMaster(msg,true))
|
|
|
|
;
|
|
|
|
return false;
|
2006-02-14 17:58:41 +00:00
|
|
|
case Progress:
|
|
|
|
case Ringing:
|
2009-05-20 13:08:01 +00:00
|
|
|
while (msgToMaster(msg,false))
|
|
|
|
;
|
|
|
|
return false;
|
2009-11-04 17:14:56 +00:00
|
|
|
case Timer:
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().lock();
|
2009-11-04 17:14:56 +00:00
|
|
|
for (ObjList* l = s_calls.skipNull(); l; l = l->skipNext()) {
|
|
|
|
RefPointer<ForkMaster> m = static_cast<ForkMaster*>(l->get());
|
|
|
|
if (m)
|
|
|
|
m->checkTimer(msg.msgTime());
|
|
|
|
}
|
2010-01-17 17:44:35 +00:00
|
|
|
CallEndpoint::commonMutex().unlock();
|
2009-11-04 17:14:56 +00:00
|
|
|
// fall through
|
2006-02-14 17:58:41 +00:00
|
|
|
default:
|
|
|
|
return Module::received(msg,id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-27 14:53:18 +00:00
|
|
|
}; // anonymous namespace
|
|
|
|
|
2006-02-14 17:58:41 +00:00
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|