1 /**
2  * multiroute.cpp
3  * This file is part of the YATE Project http://YATE.null.ro
4  *
5  * Multiple routing implementation
6  *
7  * Yet Another Telephony Engine - a fully featured software PBX and IVR
8  * Copyright (C) 2004-2014 Null Team
9  *
10  * This software is distributed under multiple licenses;
11  * see the COPYING file in the main directory for licensing
12  * information for this specific distribution.
13  *
14  * This use of this software may be subject to additional restrictions.
15  * See the LEGAL file in the main directory for details.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20  */
21 
22 #include <yatepbx.h>
23 
24 #include <stdlib.h>
25 #include <stdarg.h>
26 
27 using namespace TelEngine;
28 
29 
copyParam(const NamedList & original,const String & name,bool clear)30 bool CallInfo::copyParam(const NamedList& original, const String& name, bool clear)
31 {
32     NamedString* param = original.getParam(name);
33     if (param) {
34 	setParam(name,param->c_str());
35 	return true;
36     }
37     else if (clear)
38 	clearParam(name);
39     return false;
40 }
41 
copyParams(const NamedList & original,bool clear,...)42 void CallInfo::copyParams(const NamedList& original, bool clear, ...)
43 {
44     va_list va;
45     va_start(va,clear);
46     while (const char* name = va_arg(va,const char*))
47 	copyParam(original,name,clear);
48     va_end(va);
49 }
50 
fillParam(NamedList & target,const String & name,bool clear)51 void CallInfo::fillParam(NamedList& target, const String& name, bool clear)
52 {
53     NamedString* param = getParam(name);
54     if (param)
55 	target.setParam(name,param->c_str());
56     else if (clear)
57 	target.clearParam(name);
58 }
59 
fillParams(NamedList & target)60 void CallInfo::fillParams(NamedList& target)
61 {
62     unsigned int n = length();
63     for (unsigned int i = 0; i < n; i++) {
64 	NamedString* param = getParam(i);
65 	if (param)
66 	    target.setParam(param->name(),param->c_str());
67     }
68 }
69 
70 
find(const String & id)71 CallInfo* CallList::find(const String& id)
72 {
73     if (id.null())
74 	return 0;
75     return static_cast<CallInfo*>(m_calls[id]);
76 }
77 
find(const CallEndpoint * call)78 CallInfo* CallList::find(const CallEndpoint* call)
79 {
80     ObjList* l = m_calls.skipNull();
81     for (; l; l = l->skipNext()) {
82 	CallInfo* info = static_cast<CallInfo*>(l->get());
83 	if (info->call() == call)
84 	    return info;
85     }
86     return 0;
87 }
88 
89 
MultiRouter(const char * trackName)90 MultiRouter::MultiRouter(const char* trackName)
91     : Mutex(true,"MultiRouter"),
92       m_trackName(trackName),
93       m_relRoute(0), m_relExecute(0),
94       m_relHangup(0), m_relDisconnected(0)
95 {
96 }
97 
~MultiRouter()98 MultiRouter::~MultiRouter()
99 {
100     Engine::uninstall(m_relRoute);
101     Engine::uninstall(m_relExecute);
102     Engine::uninstall(m_relDisconnected);
103     Engine::uninstall(m_relHangup);
104 }
105 
setup(int priority)106 void MultiRouter::setup(int priority)
107 {
108     if (priority <= 0)
109 	priority = 20;
110     if (!m_relHangup)
111 	Engine::install(m_relHangup =
112 	    new MessageRelay("chan.hangup",this,Hangup,priority,m_trackName));
113     if (!m_relDisconnected)
114 	Engine::install(m_relDisconnected =
115 	    new MessageRelay("chan.disconnected",this,Disconnected,priority,m_trackName));
116     if (!m_relExecute)
117 	Engine::install(m_relExecute =
118 	    new MessageRelay("call.execute",this,Execute,priority,m_trackName));
119     if (!m_relRoute)
120 	Engine::install(m_relRoute =
121 	    new MessageRelay("call.route",this,Route,priority,m_trackName));
122 }
123 
received(Message & msg,int id)124 bool MultiRouter::received(Message& msg, int id)
125 {
126     CallEndpoint* call = static_cast<CallEndpoint*>(msg.userObject(YATOM("CallEndpoint")));
127     bool first = false;
128     CallInfo* info = 0;
129     String chanid(msg.getValue("id"));
130     Lock lock(this);
131     if (call)
132 	info = m_list.find(call);
133     if (info) {
134 	if (*info != chanid) {
135 	    Debug(DebugCrit,"Channel mismatch! call=%p id='%s' stored='%s'",
136 		call,chanid.c_str(),info->c_str());
137 	    return false;
138 	}
139     }
140     else
141 	info = m_list.find(chanid);
142     if (info) {
143 	if (!call)
144 	    call = info->call();
145 	else if (!info->call())
146 	    info->setCall(call);
147 	else if (info->call() != call) {
148 	    Debug(DebugCrit,"Channel mismatch! id='%s' call=%p stored=%p",
149 		chanid.c_str(),call,info->call());
150 	    return false;
151 	}
152     }
153     else if ((id == Route) || (id == Execute)) {
154 	info = new CallInfo(chanid,call);
155 	info->copyParams(msg,false,"module","address","billid","caller","called","callername",0);
156 	m_list.append(info);
157 	first = true;
158 	DDebug(DebugInfo,"MultiRouter built '%s' @ %p for %p",
159 	    chanid.c_str(),info,call);
160     }
161     else
162 	return false;
163     DDebug(DebugAll,"MultiRouter::received '%s' for '%s' info=%p call=%p",
164 	msg.c_str(),chanid.c_str(),info,call);
165     switch (id) {
166 	case Route:
167 	    return msgRoute(msg,*info,first);
168 	case Execute:
169 	    if (!call)
170 		return false;
171 	    return msgExecute(msg,*info,first);
172 	case Disconnected:
173 	    return msgDisconnected(msg,*info);
174 	case Hangup:
175 	    info->clearCall();
176 	    msgHangup(msg,*info);
177 	    m_list.remove(info);
178 	    DDebug(DebugInfo,"MultiRouter destroyed '%s' @ %p",info->c_str(),info);
179 	    info->destruct();
180 	    break;
181 	default:
182 	    Debug(DebugFail,"Invalid id %d in MultiRouter::received()",id);
183     }
184     return false;
185 }
186 
msgRoute(Message & msg,CallInfo & info,bool first)187 bool MultiRouter::msgRoute(Message& msg, CallInfo& info, bool first)
188 {
189     return false;
190 }
191 
msgExecute(Message & msg,CallInfo & info,bool first)192 bool MultiRouter::msgExecute(Message& msg, CallInfo& info, bool first)
193 {
194     return false;
195 }
196 
msgDisconnected(Message & msg,CallInfo & info)197 bool MultiRouter::msgDisconnected(Message& msg, CallInfo& info)
198 {
199     info.copyParams(msg,true,"reason","error",0);
200     Message* m = buildExecute(info,msg.getBoolValue("reroute"));
201     if (m) {
202 	m->userData(info.call());
203 	Engine::enqueue(m);
204 	return true;
205     }
206     return false;
207 }
208 
msgHangup(Message & msg,CallInfo & info)209 void MultiRouter::msgHangup(Message& msg, CallInfo& info)
210 {
211 }
212 
defaultExecute(CallInfo & info,const char * route)213 Message* MultiRouter::defaultExecute(CallInfo& info, const char* route)
214 {
215     Message* m = new Message("call.execute");
216     m->addParam("id",info);
217     info.fillParams(*m);
218     if (!null(route))
219 	m->setParam("callto",route);
220     return m;
221 }
222 
223 /* vi: set ts=8 sw=4 sts=4 noet: */
224