291 lines
7.2 KiB
C++
291 lines
7.2 KiB
C++
/**
|
|
* park.cpp
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* Call parking module
|
|
*
|
|
* 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 <yatephone.h>
|
|
|
|
using namespace TelEngine;
|
|
namespace { // anonymous
|
|
|
|
// Parking endpoint
|
|
class ParkEndpoint : public CallEndpoint
|
|
{
|
|
public:
|
|
ParkEndpoint(const String &name);
|
|
~ParkEndpoint();
|
|
// Call parent's method. Dispatch a chan.hangup message
|
|
virtual void disconnected(bool final, const char *reason);
|
|
// Connect to peer. Create data source. Notify state changes
|
|
bool callExecute(Message& msg);
|
|
// Complete a message with common notification parameters
|
|
Message* complete(const char* message, const char* status = 0);
|
|
private:
|
|
bool m_hungup; // Allready hungup flag (used to avoid multiple notifications)
|
|
String m_peerId; // Peer's channel id
|
|
};
|
|
|
|
// Parking module
|
|
class ParkModule : public Module
|
|
{
|
|
friend class ParkEndpoint;
|
|
public:
|
|
ParkModule();
|
|
virtual ~ParkModule();
|
|
virtual void initialize();
|
|
// Find a parking by id
|
|
ParkEndpoint* findParking(const String& id);
|
|
private:
|
|
bool m_initialized; // True if already initialized
|
|
};
|
|
|
|
// call.execute handler. Park a call
|
|
class ParkHandler : public MessageHandler
|
|
{
|
|
public:
|
|
inline ParkHandler(int prio = 100) : MessageHandler("call.execute",prio) {}
|
|
virtual bool received(Message& msg);
|
|
};
|
|
|
|
// engine.halt handler. Disconnect all parked calls
|
|
class HaltHandler : public MessageHandler
|
|
{
|
|
public:
|
|
inline HaltHandler(int prio = 100) : MessageHandler("engine.halt",prio) {}
|
|
virtual bool received(Message& msg);
|
|
};
|
|
|
|
// chan.locate handler
|
|
class LocateHandler : public MessageHandler
|
|
{
|
|
public:
|
|
inline LocateHandler(int prio = 100) : MessageHandler("chan.locate",prio) {}
|
|
virtual bool received(Message& msg);
|
|
};
|
|
|
|
|
|
static ParkModule s_module;
|
|
static const char* s_prefix = "park/";
|
|
static unsigned int s_id = 1; // Channel id to use
|
|
static ObjList s_chans; // Channel list
|
|
static Mutex s_mutex(true); // Global mutex
|
|
|
|
|
|
// Find a parking by id
|
|
ParkEndpoint* findParking(const String& id)
|
|
{
|
|
Lock lock(s_mutex);
|
|
ObjList* obj = s_chans.find(id);
|
|
if (obj)
|
|
return static_cast<ParkEndpoint*>(obj->get());
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* ParkEndpoint
|
|
*/
|
|
ParkEndpoint::ParkEndpoint(const String& id)
|
|
: CallEndpoint(id),
|
|
m_hungup(false)
|
|
{
|
|
Lock lock(s_mutex);
|
|
s_chans.append(this);
|
|
}
|
|
|
|
ParkEndpoint::~ParkEndpoint()
|
|
{
|
|
disconnected(true,0);
|
|
Lock lock(s_mutex);
|
|
s_chans.remove(this,false);
|
|
}
|
|
|
|
// Call parent's method. Dispatch a chan.hangup message
|
|
void ParkEndpoint::disconnected(bool final, const char *reason)
|
|
{
|
|
CallEndpoint::disconnected(final,reason);
|
|
if (m_hungup)
|
|
return;
|
|
m_hungup = true;
|
|
setSource();
|
|
Message* m = complete("chan.hangup");
|
|
m->addParam("targetid",m_peerId);
|
|
if (reason && *reason)
|
|
m->addParam("reason",reason);
|
|
Engine::enqueue(m);
|
|
}
|
|
|
|
// Connect to peer. Create data source. Notify state changes
|
|
bool ParkEndpoint::callExecute(Message& msg)
|
|
{
|
|
CallEndpoint* peer = static_cast<CallEndpoint*>(msg.userData());
|
|
if (!peer) {
|
|
Debug(&s_module,DebugNote,"No channel to park on '%s'",id().c_str());
|
|
msg.setParam("error","failure");
|
|
return false;
|
|
}
|
|
m_peerId = peer->id();
|
|
if (!peer->connect(this,msg.getValue("reason"))) {
|
|
Debug(&s_module,DebugNote,"Failed to park '%s' on '%s'",m_peerId.c_str(),id().c_str());
|
|
return false;
|
|
}
|
|
msg.setParam("peerid",id());
|
|
msg.setParam("targetid",id());
|
|
|
|
Message* m = complete("chan.startup","outgoing");
|
|
m->addParam("cdrwrite","false");
|
|
m->addParam("caller",msg.getValue("caller"));
|
|
m->addParam("called",msg.getValue("called"));
|
|
Engine::enqueue(m);
|
|
|
|
// Set source
|
|
String source = msg.getValue("source");
|
|
if (source) {
|
|
Message src("chan.attach");
|
|
src.userData(this);
|
|
src.addParam("source",source);
|
|
Engine::dispatch(src);
|
|
}
|
|
|
|
m = complete("call.ringing","ringing");
|
|
m->addParam("targetid",m_peerId);
|
|
m->addParam("peerid",m_peerId);
|
|
Engine::enqueue(m);
|
|
|
|
DDebug(&s_module,DebugInfo,
|
|
"'%s' parked on '%s' (%ssource: '%s') [%p]",m_peerId.c_str(),
|
|
id().c_str(),getSource()?"":"Failed to set ",source.c_str(),this);
|
|
return true;
|
|
}
|
|
|
|
// Complete a message with common notification parameters
|
|
Message* ParkEndpoint::complete(const char* message, const char* status)
|
|
{
|
|
Message* m = new Message(message);
|
|
m->addParam("driver",s_module.name());
|
|
m->addParam("id",id());
|
|
if (status)
|
|
m->addParam("status",status);
|
|
return m;
|
|
}
|
|
|
|
|
|
/**
|
|
* ParkModule
|
|
*/
|
|
ParkModule::ParkModule()
|
|
: Module("park","misc"),
|
|
m_initialized(false)
|
|
{
|
|
Output("Loaded module Call Parking");
|
|
}
|
|
|
|
ParkModule::~ParkModule()
|
|
{
|
|
Output("Unloading module Call Parking");
|
|
}
|
|
|
|
void ParkModule::initialize()
|
|
{
|
|
Output("Initializing module Call Parking");
|
|
if (m_initialized)
|
|
return;
|
|
setup();
|
|
m_initialized = true;
|
|
Engine::install(new ParkHandler);
|
|
Engine::install(new HaltHandler);
|
|
Engine::install(new LocateHandler);
|
|
}
|
|
|
|
|
|
/**
|
|
* Message handlers
|
|
*/
|
|
|
|
// call.execute. Park a call
|
|
bool ParkHandler::received(Message& msg)
|
|
{
|
|
if (Engine::exiting())
|
|
return false;
|
|
|
|
String callto = msg.getValue("callto");
|
|
if (!(callto.startSkip(s_prefix,false) && callto))
|
|
return false;
|
|
|
|
String id;
|
|
if (callto == "any") {
|
|
s_mutex.lock();
|
|
// Try to re-use an old parking
|
|
for (unsigned int i = 1; i < s_id; i++) {
|
|
id << s_prefix << i;
|
|
if (!findParking(id))
|
|
break;
|
|
id = "";
|
|
}
|
|
if (!id)
|
|
id << s_prefix << s_id++;
|
|
s_mutex.unlock();
|
|
}
|
|
else {
|
|
id << s_prefix << callto;
|
|
if (findParking(id)) {
|
|
Debug(&s_module,DebugNote,"Park '%s' already taken",id.c_str());
|
|
msg.setParam("error","failure");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ParkEndpoint* park = new ParkEndpoint(id);
|
|
bool ok = park->callExecute(msg);
|
|
park->deref();
|
|
return ok;
|
|
}
|
|
|
|
// engine.halt. Disconnect all parked calls
|
|
bool HaltHandler::received(Message& msg)
|
|
{
|
|
Lock lock (s_mutex);
|
|
ListIterator iter(s_chans);
|
|
for (GenObject* o = 0; (o = iter.get());)
|
|
(static_cast<ParkEndpoint*>(o))->disconnect("shutdown");
|
|
return false;
|
|
}
|
|
|
|
// chan.locate
|
|
bool LocateHandler::received(Message& msg)
|
|
{
|
|
String chan = msg.getValue("id");
|
|
if (!chan.startsWith(s_prefix))
|
|
return false;
|
|
Lock lock(s_mutex);
|
|
ParkEndpoint* park = findParking(chan);
|
|
if (park)
|
|
msg.userData(park);
|
|
return true;
|
|
}
|
|
|
|
}; // anonymous namespace
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|
|
|
|
|