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