yate/modules/server/park.cpp

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: */