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