1 /**
2 * analog.cpp
3 * This file is part of the YATE Project http://YATE.null.ro
4 *
5 * Yet Another Analog Channel
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 <yatephone.h>
23 #include <yatesig.h>
24
25 using namespace TelEngine;
26 namespace { // anonymous
27
28 class ModuleLine; // Module's interface to an analog line or recorder
29 // Manages the call setup detector and sends call setup info
30 class ModuleGroup; // Module's interface to a group of lines
31 class AnalogChannel; // Channel associated with an analog line
32 class AnalogCallRec; // Recorder call endpoint associated with an analog line monitor
33 class AnalogDriver; // Analog driver
34 class AnalogWorkerThread; // Worker thread to get events from a group
35 class ChanNotifyHandler; // chan.notify handler (notify lines on detector events)
36 class EngineStartHandler; // engine.start handler (start detectors on lines expectind data before ring)
37
38 // Value for m_ringTimer interval. The timer is used to ignore some ring events
39 // Some ring patterns might raise multiple ring events for the same logical ring
40 // e.g. ring-ring....ring-ring...
41 #define RING_PATTERN_TIME 750
42
43 // Module's interface to an analog line or monitor
44 class ModuleLine : public AnalogLine
45 {
46 public:
47 ModuleLine(ModuleGroup* grp, unsigned int cic, const NamedList& params, const NamedList& groupParams);
48 // Get the module group representation of this line's owner
49 ModuleGroup* moduleGroup();
caller() const50 inline const String& caller() const
51 { return m_caller; }
callerName() const52 inline const String& callerName() const
53 { return m_callerName; }
called()54 inline String& called()
55 { return m_called; }
noRingTimer()56 inline SignallingTimer& noRingTimer()
57 { return m_noRingTimer; }
58 // Send call setup data
59 void sendCallSetup(bool privacy);
60 // Set call setup detector
61 void setCallSetupDetector();
62 // Remove call setup detector
63 void removeCallSetupDetector();
64 // Process notifications from detector
65 void processNotify(Message& msg);
66 // Set the caller, callername and called parameters
setCall(const char * caller=0,const char * callername=0,const char * called=0)67 inline void setCall(const char* caller = 0, const char* callername = 0,
68 const char* called = 0)
69 { m_caller = caller; m_callerName = callername; m_called = called; }
70 // Set the caller, callername and called parameters
71 void copyCall(NamedList& dest, bool privacy = false);
72 // Fill a string with line status parameters
73 void statusParams(String& str);
74 // Fill a string with line status detail parameters
75 void statusDetail(String& str);
76 protected:
77 virtual void checkTimeouts(const Time& when);
78 // Remove detector. Call parent's destructor
destroyed()79 virtual void destroyed() {
80 removeCallSetupDetector();
81 AnalogLine::destroyed();
82 }
83 // Set the FXO line. Start detector if waiting call setup before first ring
84 void setFXO(AnalogLine* fxo);
85
86 String m_called; // Called's extension
87 // Call setup (caller id)
88 String m_caller; // Caller's extension
89 String m_callerName; // Caller's name
90 String m_detector; // Call setup detector resource
91 DataConsumer* m_callSetupDetector; // The call setup detector
92 SignallingTimer m_noRingTimer; // No more rings detected on unanswered line
93 SignallingTimer m_callSetupTimer; // Timeout of call setup data received before the first ring
94 // Stop detector if started and timeout
95 };
96
97 // Module's interface to a group of lines
98 class ModuleGroup : public AnalogLineGroup
99 {
100 friend class AnalogWorkerThread; // Set worker thread variable
101 public:
102 // Create an FXS/FXO group of analog lines
ModuleGroup(AnalogLine::Type type,const char * name)103 inline ModuleGroup(AnalogLine::Type type, const char* name)
104 : AnalogLineGroup(type,name),
105 m_init(false), m_ringback(false), m_thread(0), m_callEndedPlayTime(0)
106 { m_prefix << name << "/"; }
107 // Create a group of analog lines used to record
ModuleGroup(const char * name,ModuleGroup * fxo)108 inline ModuleGroup(const char* name, ModuleGroup* fxo)
109 : AnalogLineGroup(name,fxo), m_init(false), m_thread(0), m_callEndedPlayTime(0)
110 { m_prefix << name << "/"; }
111 // Create an FXO group of analog lines to be attached to a group of recorders
ModuleGroup(const char * name)112 inline ModuleGroup(const char* name)
113 : AnalogLineGroup(AnalogLine::FXO,name), m_init(false), m_thread(0), m_callEndedPlayTime(0)
114 { m_prefix << name << "/"; }
~ModuleGroup()115 virtual ~ModuleGroup()
116 {}
fxoRec()117 inline ModuleGroup* fxoRec()
118 { return static_cast<ModuleGroup*>(fxo()); }
prefix()119 inline const String& prefix()
120 { return m_prefix; }
ringback() const121 inline bool ringback() const
122 { return m_ringback; }
123 // Remove all channels associated with this group and stop worker thread
124 virtual void destruct();
125 // Process an event geberated by a line
126 void handleEvent(ModuleLine& line, SignallingCircuitEvent& event);
127 // Process an event generated by a monitor
128 void handleRecEvent(ModuleLine& line, SignallingCircuitEvent& event);
129 // Apply debug level. Call create and create worker thread on first init
130 // Re(load) lines and calls specific group reload
131 // Return false on failure
132 bool initialize(const NamedList& params, const NamedList& defaults, String& error);
133 // Copy some data to a module's channel
134 void copyData(AnalogChannel* chan);
135 // Append/remove endpoints from list
136 void setEndpoint(CallEndpoint* ep, bool add);
137 // Find a recorder by its line
138 AnalogCallRec* findRecorder(ModuleLine* line);
139 // Check timers for endpoints owned by this group
140 void checkTimers(Time& when);
141 // Fill a string with group status parameters
142 void statusParams(String& str);
143 // Fill a string with group status detail parameters
144 void statusDetail(String& str);
145 protected:
146 // Disconnect all group's endpoints
147 void clearEndpoints(const char* reason = 0);
148 private:
149 // Create FXS/FXO group data: called by initialize() on first init
150 bool create(const NamedList& params, const NamedList& defaults,
151 String& error);
152 // Reload FXS/FXO data: called by initialize() (not called on first init if create failed)
153 bool reload(const NamedList& params, const NamedList& defaults,
154 String& error);
155 // Create recorder group data: called by initialize() on first init
156 bool createRecorder(const NamedList& params, const NamedList& defaults,
157 String& error);
158 // Reload recorder data: called by initialize() (not called on first init if create failed)
159 bool reloadRecorder(const NamedList& params, const NamedList& defaults,
160 String& error);
161 // Reload existing line's parameters
162 void reloadLine(ModuleLine* line, const NamedList& params);
163 // Build the group of circuits (spans)
164 void buildGroup(ModuleGroup* group, ObjList& spanList, String& error);
165 // Complete missing line parameters from other list of parameters
completeLineParams(NamedList & dest,const NamedList & src,const NamedList & defaults)166 inline void completeLineParams(NamedList& dest, const NamedList& src, const NamedList& defaults) {
167 for (unsigned int i = 0; lineParams[i]; i++)
168 if (!dest.getParam(lineParams[i]))
169 dest.addParam(lineParams[i],src.getValue(lineParams[i],
170 defaults.getValue(lineParams[i])));
171 }
172 // Line parameters that can be overridden
173 static const char* lineParams[];
174
175 bool m_init; // Init flag
176 bool m_ringback; // Lines need to provide ringback
177 String m_prefix; // Line prefix used to complete commands
178 AnalogWorkerThread* m_thread; // The worker thread
179 // FXS/FXO group data
180 String m_callEndedTarget; // callto when an FXS line was disconnected
181 String m_oooTarget; // callto when out-of-order (hook is off for a long time after call ended)
182 String m_lang; // Language for played tones
183 u_int64_t m_callEndedPlayTime; // Time to play call ended prompt
184 // Recorder group data
185 ObjList m_endpoints; // Record data endpoints
186 };
187
188 // Channel associated with an analog line
189 class AnalogChannel : public Channel
190 {
191 friend class ModuleGroup; // Copy data
192 public:
193 enum RecordTrigger {
194 None = 0,
195 FXO,
196 FXS
197 };
198 AnalogChannel(ModuleLine* line, Message* msg, RecordTrigger recorder = None);
199 virtual ~AnalogChannel();
line() const200 inline ModuleLine* line() const
201 { return m_line; }
202 // Start outgoing media and echo train if earlymedia or got peer with data source
203 virtual bool msgProgress(Message& msg);
204 // Start outgoing media and echo train if earlymedia or got peer with data source
205 virtual bool msgRinging(Message& msg);
206 // Terminate ringing on line. Start echo train. Open audio streams
207 virtual bool msgAnswered(Message& msg);
208 // Send tones or flash
209 virtual bool msgTone(Message& msg, const char* tone);
210 // Hangup line
211 virtual bool msgDrop(Message& msg, const char* reason);
212 // Update echo canceller and/or start echo training
213 virtual bool msgUpdate(Message& msg);
214 // Set tone detector
215 virtual bool callRouted(Message& msg);
216 // Open media if not answered
217 virtual void callAccept(Message& msg);
218 // Hangup
219 virtual void callRejected(const char* error, const char* reason = 0,
220 const Message* msg = 0);
221 // Disconnected notification
222 virtual void disconnected(bool final, const char* reason);
223 // Hangup
224 bool disconnect(const char* reason = 0);
225 // Hangup call
226 // Keep call alive to play announcements on FXS line not set on hook by the remote FXO
227 void hangup(bool local, const char* status = 0, const char* reason = 0);
228 // Enqueue chan.dtmf message
229 void evDigits(const char* text, bool tone);
230 // Line got off hook. Terminate ringing
231 // Outgoing: answer it (call outCallAnswered()). Incoming: start echo train
232 void evOffHook();
233 // Line ring on/off notification. Ring off is ignored
234 // Outgoing: enqueue call.ringing
235 // Incoming: FXO: Route the call if delayed. Remove line's detector and start ring timer
236 void evRing(bool on);
237 // Line started (initialized) notification
238 // Answer outgoing FXO calls on lines not expecting polarity changes to answer
239 // Send called number if any
240 void evLineStarted();
241 // Dial complete notification. Enqueue call.progress
242 // Answer outgoing FXO calls on lines not expecting polarity changes to answer
243 void evDialComplete();
244 // Line polarity change notification
245 // Terminate call if:
246 // - no line or line is not FXO,
247 // - Outgoing: don't answer on polarity or already answered and should hangup on polarity change
248 // - Incoming: don't answer on polarity or polarity already changed and should hangup on polarity change
249 // Outgoing: don't answer on polarity or already answered: call outCallAnswered()
250 void evPolarity();
251 // Line ok: stop alarm timer
252 // Terminate channel if not answered; otherwise: start timer if not already started
253 void evAlarm(bool alarm, const char* alarms);
254 // Check timers. Return false to terminate
255 bool checkTimeouts(const Time& when);
256 protected:
257 // Set reason if not already set
setReason(const char * reason)258 inline void setReason(const char* reason)
259 { if (!m_reason) m_reason = reason; }
260 // Route incoming. If first is false the router is started on second ring
261 void startRouter(bool first);
262 // Set data source and consumer
263 bool setAudio(bool in);
264 // Set call status. Return true
265 bool setStatus(const char* newStat = 0);
266 // Set tones to the remote end of the line
267 bool setAnnouncement(const char* status, const char* callto);
268 // Outgoing call answered: set call state, start echo train, open data source/consumer
269 void outCallAnswered(bool stopDial = true);
270 // Hangup. Release memory
271 virtual void destroyed();
272 // Detach the line from this channel and reset it
273 void detachLine();
274 // Send tones (DTMF or dial number)
275 bool sendTones(const char* tone, bool dial = true);
276 // Set line polarity
polarityControl(bool state)277 inline void polarityControl(bool state) {
278 if (!(m_line && m_line->type() == AnalogLine::FXS &&
279 m_line->polarityControl() && state != m_polarity))
280 return;
281 m_polarity = state;
282 m_line->setCircuitParam("polarity",String::boolText(m_polarity));
283 }
284 private:
285 ModuleLine* m_line; // The analog line associated with this channel
286 bool m_hungup; // Hang up flag
287 bool m_ringback; // Circuit ringback provider flag
288 bool m_routeOnSecondRing; // Delay router if waiting callerid
289 RecordTrigger m_recording; // Recording trigger source
290 String m_reason; // Hangup reason
291 SignallingTimer m_callEndedTimer; // Call ended notification to the FXO
292 SignallingTimer m_ringTimer; // Timer used to fix some ring patterns
293 SignallingTimer m_alarmTimer; // How much time a channel may stay with its line in alarm
294 SignallingTimer m_dialTimer; // FXO: delay dialing the number
295 // FXS: send call setup after first ring
296 String m_callEndedTarget; // callto when an FXS line was disconnected
297 String m_oooTarget; // callto when out-of-order (hook is off for a long time after call ended)
298 String m_lang; // Language for played tones
299 unsigned int m_polarityCount; // The number of polarity changes received
300 bool m_polarity; // The last value we've set for the line polarity
301 bool m_privacy; // Send caller identity
302 int m_callsetup; // Send callsetup before/after first ring
303 };
304
305 // Recorder call endpoint associated with an analog line monitor
306 class AnalogCallRec : public CallEndpoint, public DebugEnabler
307 {
308 public:
309 // Append to driver's list
310 AnalogCallRec(ModuleLine* m_line, bool fxsCaller, const char* id);
line()311 inline ModuleLine* line()
312 { return m_line; }
fxo() const313 inline ModuleLine* fxo() const
314 { return m_line ? static_cast<ModuleLine*>(m_line->getPeer()) : 0; }
startOnSecondRing()315 inline bool startOnSecondRing()
316 { return m_startOnSecondRing; }
317 void hangup(const char* reason = "normal");
318 bool disconnect(const char* reason);
319 virtual void* getObject(const String& name) const;
reason()320 inline const char* reason()
321 { return m_reason; }
322 // Create data source. Route and execute
323 // Return false to hangup
324 bool startRecording();
325 // Call answered: start recording
326 bool answered();
327 // Process rings: start recording if delayed
328 // Return false to hangup
329 bool ringing(bool fxsEvent);
330 // Enqueue chan.dtmf
331 void evDigits(bool fxsEvent, const char* text, bool tone);
332 // Process line polarity changes. Return false to hangup
333 bool evPolarity(bool fxsEvent);
334 // Line alarms changed
335 bool evAlarm(bool fxsEvent, bool alarm, const char* alarms);
336 // Check timers. Return false to terminate
337 bool checkTimeouts(const Time& when);
338 // Fill a string with recorder status parameters
339 void statusParams(String& str);
340 // Fill a string with recorder status detail parameters
341 void statusDetail(String& str);
342 protected:
343 // Remove from driver's list
344 virtual void destroyed();
345 virtual void disconnected(bool final, const char *reason);
346 // Create a message to be enqueued/dispatched to the engine
347 // @param peers True to add caller and called parameters
348 // @param userdata True to add this call endpoint as user data
349 Message* message(const char* name, bool peers = true, bool userdata = false);
350 private:
351 ModuleLine* m_line; // The monitored lines
352 bool m_fxsCaller; // True if the call originated from the FXS
353 bool m_answered; // True if answered
354 bool m_hungup; // Already hungup flag
355 unsigned int m_polarityCount; // The number of polarity changes received
356 bool m_startOnSecondRing; // Start recording on second ring if waiting callerid
357 SignallingTimer m_ringTimer; // Timer used to fix some ring patterns
358 String m_reason; // Hangup reason
359 String m_status; // Call status
360 String m_address; // Call enspoint's address
361 };
362
363 // The driver
364 class AnalogDriver : public Driver
365 {
366 public:
367 // Additional driver commands
368 enum Commands {
369 CmdCount = 0
370 };
371 // Additional driver status commands
372 enum StatusCommands {
373 Groups = 0, // Show all groups
374 Lines = 1, // Show all lines
375 Recorders = 2, // Show all active recorders
376 StatusCmdCount = 3
377 };
378 AnalogDriver();
379 ~AnalogDriver();
recPrefix()380 inline const String& recPrefix()
381 { return m_recPrefix; }
382 virtual void initialize();
383 virtual bool msgExecute(Message& msg, String& dest);
384 virtual void dropAll(Message& msg);
385 // Check timers for recorders owned by the given group
386 void checkTimers(Time& when, ModuleGroup* recGrp);
387 // Notification of line service state change or removal
388 // Return true if a channel or recorder was found
389 bool lineUnavailable(ModuleLine* line);
390 // Disconnect or deref a channel
391 void terminateChan(AnalogChannel* ch, const char* reason = "normal");
392 // Destroy a monitor endpoint
393 void terminateChan(AnalogCallRec* ch, const char* reason = "normal");
394 // Attach detectors after engine started
395 void engineStart(Message& msg);
396 // Notify lines on detector events
397 bool chanNotify(Message& msg);
398 // Get an id for a recorder
nextRecId()399 inline unsigned int nextRecId() {
400 Lock lock(this);
401 return ++m_recId;
402 }
403 // Append/remove recorders from list
404 void setRecorder(AnalogCallRec* rec, bool add);
405 // Find a group by its name
findGroup(const String & name)406 inline ModuleGroup* findGroup(const String& name) {
407 Lock lock(this);
408 ObjList* obj = m_groups.find(name);
409 return obj ? static_cast<ModuleGroup*>(obj->get()) : 0;
410 }
411 // Find a recorder by its id
findRecorder(const String & name)412 inline AnalogCallRec* findRecorder(const String& name) {
413 Lock lock(this);
414 ObjList* obj = m_recorders.find(name);
415 return obj ? static_cast<AnalogCallRec*>(obj->get()) : 0;
416 }
417 // Additional driver status commands
418 static String s_statusCmd[StatusCmdCount];
419 protected:
420 virtual bool received(Message& msg, int id);
421 // Handle command complete requests
422 virtual bool commandComplete(Message& msg, const String& partLine,
423 const String& partWord);
424 // Execute commands
425 virtual bool commandExecute(String& retVal, const String& line);
426 // Complete channels/recorders IDs from partial command word
completeChanRec(String & dest,const String & partWord,bool chans,bool all)427 inline void completeChanRec(String& dest, const String& partWord,
428 bool chans, bool all) {
429 ObjList* o = (chans ? channels().skipNull() : m_recorders.skipNull());
430 for (; o; o = o->skipNext()) {
431 CallEndpoint* c = static_cast<CallEndpoint*>(o->get());
432 if (all || c->id().startsWith(partWord))
433 dest.append(c->id(),"\t");
434 }
435 }
436 // Complete group names from partial command word
437 void completeGroups(String& dest, const String& partWord);
438 // Complete line names from partial command word
439 void completeLines(String& dest, const String& partWord);
440 // Remove a group from list
441 void removeGroup(ModuleGroup* group);
442 // Find a group or recorder by its name
443 // Set useFxo to true to find a recorder by its fxo's name
444 ModuleGroup* findGroup(const char* name, bool useFxo);
445 private:
446 bool m_init; // Init flag
447 String m_recPrefix; // Prefix used for recorders
448 unsigned int m_recId; // Next recorder id
449 ObjList m_groups; // Analog line groups
450 ObjList m_recorders; // Recorders created by monitor groups
451 String m_statusCmd; // Prefix for status commands
452 };
453
454 // Get events from a group. Check timers for lines
455 class AnalogWorkerThread : public Thread
456 {
457 public:
458 AnalogWorkerThread(ModuleGroup* group);
459 virtual ~AnalogWorkerThread();
460 virtual void run();
461 private:
462 ModuleGroup* m_client; // The module's group client
463 String m_groupName; // Group's name (saved to be printed in destructor)
464 };
465
466
467 /**
468 * Module data and functions
469 */
470 static AnalogDriver plugin;
471 static Configuration s_cfg;
472 static bool s_engineStarted = false; // Received engine.start message
473 static const char* s_lineSectPrefix = "line "; // Prefix for line sections in config
474 static const char* s_unk = "unknown"; // Used to set caller
475 // Status detail formats
476 static const char* s_lineStatusDetail = "format=State|UsedBy";
477 static const char* s_groupStatusDetail = "format=Type|Lines";
478 static const char* s_recStatusDetail = "format=Status|Address|Peer";
479
480
481 // engine.start handler (start detectors on lines expectind data before ring)
482 class EngineStartHandler : public MessageHandler
483 {
484 public:
EngineStartHandler()485 inline EngineStartHandler()
486 : MessageHandler("engine.start",100,plugin.name())
487 {}
488 virtual bool received(Message& msg);
489 };
490
491 // chan.notify handler (notify lines on detector events)
492 class ChanNotifyHandler : public MessageHandler
493 {
494 public:
ChanNotifyHandler()495 inline ChanNotifyHandler()
496 : MessageHandler("chan.notify",100,plugin.name())
497 {}
498 virtual bool received(Message& msg);
499 };
500
501
502 // Decode a line address into group name and circuit code
503 // Set first to decode group name until first '/'
504 // Return:
505 // -1 if src is the name of the group
506 // -2 if src contains invalid circuit code
507 // Otherwise: The integer part of the circuit code
decodeAddr(const String & src,String & group,bool first)508 inline int decodeAddr(const String& src, String& group, bool first)
509 {
510 int pos = first ? src.find("/") : src.rfind('/');
511 if (pos == -1) {
512 group = src;
513 return -1;
514 }
515 group = src.substr(0,pos);
516 return src.substr(pos+1).toInteger(-2);
517 }
518
519 // Get FXS/FXO type string
callertype(bool fxs)520 inline const char* callertype(bool fxs)
521 {
522 return fxs ? "fxs" : "fxo";
523 }
524
525 // Get privacy from message
526 // Return true if caller's identity is private (screened)
getPrivacy(const Message & msg)527 static inline bool getPrivacy(const Message& msg)
528 {
529 String tmp = msg.getValue("privacy");
530 if (!tmp)
531 return false;
532 if (!tmp.isBoolean())
533 return true;
534 return tmp.toBoolean();
535 }
536
537
538 /**
539 * ModuleLine
540 */
ModuleLine(ModuleGroup * grp,unsigned int cic,const NamedList & params,const NamedList & groupParams)541 ModuleLine::ModuleLine(ModuleGroup* grp, unsigned int cic, const NamedList& params, const NamedList& groupParams)
542 : AnalogLine(grp,cic,params),
543 m_callSetupDetector(0),
544 m_noRingTimer(0),
545 m_callSetupTimer(callSetupTimeout())
546 {
547 m_detector = groupParams.getValue("analogdetect","analogdetect/callsetup");
548 m_detector = params.getValue("analogdetect",m_detector);
549 if (type() == AnalogLine::FXO && callSetup() == AnalogLine::Before && s_engineStarted)
550 setCallSetupDetector();
551 }
552
moduleGroup()553 inline ModuleGroup* ModuleLine::moduleGroup()
554 {
555 return static_cast<ModuleGroup*>(group());
556 }
557
558 // Send call setup data through the FXS line
sendCallSetup(bool privacy)559 void ModuleLine::sendCallSetup(bool privacy)
560 {
561 if (type() != AnalogLine::FXS)
562 return;
563 Lock lock(this);
564 if (callSetup() == AnalogLine::NoCallSetup)
565 return;
566
567 Message msg("chan.attach");
568 if (userdata())
569 msg.userData(static_cast<RefObject*>(userdata()));
570 msg.addParam("source",m_detector);
571 msg.addParam("single",String::boolText(true));
572 String tmp;
573 tmp << plugin.prefix() << address();
574 msg.addParam("notify",tmp);
575 copyCall(msg,privacy);
576
577 if (Engine::dispatch(msg))
578 return;
579 Debug(group(),DebugNote,"%s: failed to send call setup reason='%s' [%p]",
580 address(),msg.getValue("reason"),this);
581 }
582
583 // Set the call setup detector
setCallSetupDetector()584 void ModuleLine::setCallSetupDetector()
585 {
586 removeCallSetupDetector();
587 m_callerName = "";
588
589 Lock lock(this);
590 if (callSetup() == AnalogLine::NoCallSetup)
591 return;
592
593 // Dispatch message
594 DataSource* src = 0;
595 if (circuit())
596 src = static_cast<DataSource*>(circuit()->getObject(YATOM("DataSource")));
597 Message msg("chan.attach");
598 msg.userData(src);
599 msg.addParam("consumer",m_detector);
600 msg.addParam("single",String::boolText(true));
601 String tmp;
602 tmp << plugin.prefix() << address();
603 msg.addParam("notify",tmp);
604
605 const char* error = 0;
606 while (true) {
607 if (!Engine::dispatch(msg)) {
608 error = msg.getValue("reason");
609 if (!error)
610 error = "chan.attach failed";
611 break;
612 }
613 DataConsumer* cons = 0;
614 if (msg.userData())
615 cons = static_cast<DataConsumer*>(msg.userData()->getObject(YATOM("DataConsumer")));
616 if (cons && cons->ref())
617 m_callSetupDetector = cons;
618 else
619 error = "chan.attach returned without consumer";
620 break;
621 }
622
623 if (!error)
624 DDebug(group(),DebugAll,"%s: attached detector (%p) [%p]",
625 address(),m_callSetupDetector,this);
626 else
627 Debug(group(),DebugNote,"%s: failed to attach detector error='%s' [%p]",
628 address(),error,this);
629 }
630
631 // Remove the call setup detector from FXO line
removeCallSetupDetector()632 void ModuleLine::removeCallSetupDetector()
633 {
634 Lock lock(this);
635 if (!m_callSetupDetector)
636 return;
637
638 m_callSetupTimer.stop();
639 DataSource* src = m_callSetupDetector->getConnSource();
640 if (src)
641 src->detach(m_callSetupDetector);
642 DDebug(group(),DebugAll,"%s: removed detector (%p) [%p]",
643 address(),m_callSetupDetector,this);
644 TelEngine::destruct(m_callSetupDetector);
645 }
646
647 // Process notifications from detector
processNotify(Message & msg)648 void ModuleLine::processNotify(Message& msg)
649 {
650 String operation = msg.getValue("operation");
651
652 Lock lock(this);
653
654 if (operation == "setup") {
655 DDebug(group(),DebugAll,
656 "%s: received setup info detector=%p caller=%s callername=%s called=%s [%p]",
657 address(),m_callSetupDetector,
658 msg.getValue("caller"),msg.getValue("callername"),
659 msg.getValue("called"),this);
660 if (!m_callSetupDetector)
661 return;
662 m_called = msg.getValue("called",m_called);
663 m_caller = msg.getValue("caller");
664 m_callerName = msg.getValue("callername");
665 }
666 else if (operation == "terminate") {
667 DDebug(group(),DebugAll,"%s: detector (%p) terminated reason=%s [%p]",
668 address(),m_callSetupDetector,msg.getValue("reason"),this);
669 removeCallSetupDetector();
670 }
671 else if (operation == "start") {
672 DDebug(group(),DebugAll,"%s: detector (%p) started [%p]",
673 address(),m_callSetupDetector,this);
674 if (callSetup() == AnalogLine::Before && m_callSetupDetector)
675 m_callSetupTimer.start();
676 }
677 else
678 DDebug(group(),DebugStub,
679 "%s: received notification with operation=%s [%p]",
680 address(),operation.c_str(),this);
681 }
682
683 // Set the caller, callername and called parameters
copyCall(NamedList & dest,bool privacy)684 void ModuleLine::copyCall(NamedList& dest, bool privacy)
685 {
686 if (privacy)
687 dest.addParam("callerpres","restricted");
688 else {
689 if (m_caller)
690 dest.addParam("caller",m_caller);
691 if (m_callerName)
692 dest.addParam("callername",m_callerName);
693 }
694 if (m_called)
695 dest.addParam("called",m_called);
696 }
697
698 // Fill a string with line status parameters
statusParams(String & str)699 void ModuleLine::statusParams(String& str)
700 {
701 str.append("module=",";") << plugin.name();
702 str << ",address=" << address();
703 str << ",type=" << lookup(type(),typeNames());
704 str << ",state=" << lookup(state(),stateNames());
705 str << ",usedby=";
706 if (userdata())
707 str << (static_cast<CallEndpoint*>(userdata()))->id();
708 str << ",polaritycontrol=" << polarityControl();
709 if (type() == AnalogLine::FXO) {
710 str << ",answer-on-polarity=" << answerOnPolarity();
711 str << ",hangup-on-polarity=" << hangupOnPolarity();
712 }
713 else
714 str << ",answer-on-polarity=not-defined,hangup-on-polarity=not-defined";
715 str << ",callsetup=" << lookup(callSetup(),AnalogLine::csNames());
716 // Lines with peer are used in recorders (don't send DTMFs)
717 if (!getPeer())
718 str << ",dtmf=" << (outbandDtmf() ? "outband" : "inband");
719 else
720 str << ",dtmf=not-defined";
721
722 // Fill peer status
723 bool master = (type() == AnalogLine::FXS && getPeer());
724 if (master)
725 (static_cast<ModuleLine*>(getPeer()))->statusParams(str);
726 }
727
728 // Fill a string with line status detail parameters
statusDetail(String & str)729 void ModuleLine::statusDetail(String& str)
730 {
731 // format=State|UsedBy
732 Lock lock(this);
733 str.append(address(),";") << "=";
734 str << lookup(state(),AnalogLine::stateNames()) << "|";
735 if (userdata())
736 str << (static_cast<CallEndpoint*>(userdata()))->id();
737 }
738
739 // Check detector timeout. Calls line's timeout check method
checkTimeouts(const Time & when)740 void ModuleLine::checkTimeouts(const Time& when)
741 {
742 if (m_callSetupTimer.timeout(when.msec())) {
743 m_callSetupTimer.stop();
744 DDebug(group(),DebugNote,"%s: call setup timed out [%p]",address(),this);
745 // Reset detector
746 setCallSetupDetector();
747 }
748 AnalogLine::checkTimeouts(when);
749 }
750
751
752 /**
753 * ModuleGroup
754 */
755 // Line parameters that can be overridden
756 const char* ModuleGroup::lineParams[] = {"echocancel","dtmfinband","answer-on-polarity",
757 "hangup-on-polarity","ring-timeout","callsetup","alarm-timeout","delaydial",
758 "polaritycontrol",0};
759
760 // Remove all channels associated with this group and stop worker thread
destruct()761 void ModuleGroup::destruct()
762 {
763 clearEndpoints(Engine::exiting()?"shutdown":"out-of-service");
764 if (m_thread) {
765 XDebug(this,DebugInfo,"Terminating worker thread [%p]",this);
766 m_thread->cancel(false);
767 while (m_thread)
768 Thread::yield(true);
769 Debug(this,DebugInfo,"Worker thread terminated [%p]",this);
770 }
771 AnalogLineGroup::destruct();
772 }
773
774 // Process an event generated by a line
handleEvent(ModuleLine & line,SignallingCircuitEvent & event)775 void ModuleGroup::handleEvent(ModuleLine& line, SignallingCircuitEvent& event)
776 {
777 Lock lock(&plugin);
778 AnalogChannel* ch = static_cast<AnalogChannel*>(line.userdata());
779 DDebug(this,DebugInfo,"Processing event %u '%s' line=%s channel=%s",
780 event.type(),event.c_str(),line.address(),ch?ch->id().c_str():"");
781
782 switch (event.type()) {
783 case SignallingCircuitEvent::OffHook:
784 case SignallingCircuitEvent::Wink:
785 // Line got offhook - clear the ring timer
786 line.noRingTimer().stop();
787 default: ;
788 }
789 if (ch) {
790 switch (event.type()) {
791 case SignallingCircuitEvent::Dtmf:
792 ch->evDigits(event.getValue("tone"),true);
793 break;
794 case SignallingCircuitEvent::PulseDigit:
795 ch->evDigits(event.getValue("pulse"),false);
796 break;
797 case SignallingCircuitEvent::OnHook:
798 ch->hangup(false);
799 plugin.terminateChan(ch);
800 break;
801 case SignallingCircuitEvent::OffHook:
802 case SignallingCircuitEvent::Wink:
803 ch->evOffHook();
804 break;
805 case SignallingCircuitEvent::RingBegin:
806 case SignallingCircuitEvent::RingerOn:
807 ch->evRing(true);
808 break;
809 case SignallingCircuitEvent::RingEnd:
810 case SignallingCircuitEvent::RingerOff:
811 ch->evRing(false);
812 break;
813 case SignallingCircuitEvent::LineStarted:
814 ch->evLineStarted();
815 break;
816 case SignallingCircuitEvent::DialComplete:
817 ch->evDialComplete();
818 break;
819 case SignallingCircuitEvent::Polarity:
820 ch->evPolarity();
821 break;
822 case SignallingCircuitEvent::Flash:
823 ch->evDigits("F",true);
824 break;
825 case SignallingCircuitEvent::PulseStart:
826 DDebug(ch,DebugAll,"Pulse dialing started [%p]",ch);
827 break;
828 case SignallingCircuitEvent::Alarm:
829 case SignallingCircuitEvent::NoAlarm:
830 ch->evAlarm(event.type() == SignallingCircuitEvent::Alarm,event.getValue("alarms"));
831 break;
832 default:
833 Debug(this,DebugStub,"handleEvent(%u,'%s') not implemented [%p]",
834 event.type(),event.c_str(),this);
835 }
836 }
837 else
838 if ((line.type() == AnalogLine::FXS &&
839 event.type() == SignallingCircuitEvent::OffHook) ||
840 (line.type() == AnalogLine::FXO &&
841 ((event.type() == SignallingCircuitEvent::RingBegin) ||
842 (type() == AnalogLine::Recorder && event.type() == SignallingCircuitEvent::Wink)))) {
843 if (!line.ref()) {
844 Debug(this,DebugWarn,"Incoming call on line '%s' failed [%p]",
845 line.address(),this);
846 return;
847 }
848 if (line.noRingTimer().started()) {
849 if (line.noRingTimer().timeout())
850 line.noRingTimer().stop();
851 else {
852 DDebug(this,DebugNote,
853 "Ring timer still active on line (%p,%s) without channel [%p]",
854 &line,line.address(),this);
855 // Restart the timer
856 line.noRingTimer().start();
857 return;
858 }
859 }
860 AnalogChannel::RecordTrigger rec =
861 (type() == AnalogLine::Recorder)
862 ? ((event.type() == SignallingCircuitEvent::RingBegin)
863 ? AnalogChannel::FXS : AnalogChannel::FXO)
864 : AnalogChannel::None;
865 ch = new AnalogChannel(&line,0,rec);
866 ch->initChan();
867 if (!ch->line())
868 plugin.terminateChan(ch);
869 }
870 else
871 DDebug(this,DebugNote,
872 "Event (%p,%u,%s) from line (%p,%s) without channel [%p]",
873 &event,event.type(),event.c_str(),&line,line.address(),this);
874 }
875
876 // Process an event generated by a recorder
handleRecEvent(ModuleLine & line,SignallingCircuitEvent & event)877 void ModuleGroup::handleRecEvent(ModuleLine& line, SignallingCircuitEvent& event)
878 {
879 Lock lock(&plugin);
880 AnalogCallRec* rec = static_cast<AnalogCallRec*>(line.userdata());
881 DDebug(this,DebugInfo,"Processing event %u '%s' line=%s recorder=%s",
882 event.type(),event.c_str(),line.address(),rec?rec->id().c_str():"");
883 if (event.type() == SignallingCircuitEvent::OffHook)
884 line.noRingTimer().stop();
885 if (rec) {
886 // FXS event: our FXO receiver is watching the FXS end of the monitored line
887 bool fxsEvent = (line.type() == AnalogLine::FXO);
888 bool terminate = false;
889 switch (event.type()) {
890 case SignallingCircuitEvent::Dtmf:
891 rec->evDigits(fxsEvent,event.getValue("tone"),true);
892 break;
893 case SignallingCircuitEvent::PulseDigit:
894 rec->evDigits(fxsEvent,event.getValue("pulse"),false);
895 break;
896 case SignallingCircuitEvent::OnHook:
897 terminate = true;
898 break;
899 case SignallingCircuitEvent::OffHook:
900 terminate = !rec->answered();
901 return;
902 case SignallingCircuitEvent::RingBegin:
903 case SignallingCircuitEvent::RingerOn:
904 terminate = !rec->ringing(fxsEvent);
905 break;
906 case SignallingCircuitEvent::Polarity:
907 terminate = !rec->evPolarity(fxsEvent);
908 break;
909 case SignallingCircuitEvent::Flash:
910 rec->evDigits(fxsEvent,"F",true);
911 break;
912 case SignallingCircuitEvent::Alarm:
913 case SignallingCircuitEvent::NoAlarm:
914 terminate = !rec->evAlarm(fxsEvent,event.type() == SignallingCircuitEvent::Alarm,
915 event.getValue("alarms"));
916 break;
917 case SignallingCircuitEvent::RingEnd:
918 case SignallingCircuitEvent::RingerOff:
919 case SignallingCircuitEvent::PulseStart:
920 case SignallingCircuitEvent::LineStarted:
921 case SignallingCircuitEvent::DialComplete:
922 case SignallingCircuitEvent::Wink:
923 DDebug(rec,DebugAll,"Ignoring '%s' event [%p]",event.c_str(),rec);
924 break;
925 default:
926 Debug(this,DebugStub,"handleRecEvent(%u,'%s') not implemented [%p]",
927 event.type(),event.c_str(),this);
928 }
929 if (terminate) {
930 rec->hangup();
931 plugin.terminateChan(rec);
932 }
933 return;
934 }
935
936 // Check for new call
937 bool fxsCaller = (line.type() == AnalogLine::FXO && event.type() == SignallingCircuitEvent::RingBegin);
938 bool fxoCaller = (line.type() == AnalogLine::FXS && event.type() == SignallingCircuitEvent::OffHook);
939
940 if (!(fxsCaller || fxoCaller)) {
941 DDebug(this,DebugNote,
942 "Event (%p,%u,%s) from line (%p,%s) without recorder [%p]",
943 &event,event.type(),event.c_str(),&line,line.address(),this);
944 return;
945 }
946
947 String id;
948 id << plugin.recPrefix() << plugin.nextRecId();
949 ModuleLine* fxs = (line.type() == AnalogLine::FXS ? &line : static_cast<ModuleLine*>(line.getPeer()));
950 rec = new AnalogCallRec(fxs,fxsCaller,id);
951 if (!(rec->line() && rec->fxo())) {
952 plugin.terminateChan(rec,rec->reason());
953 return;
954 }
955 if (rec->startOnSecondRing()) {
956 DDebug(rec,DebugAll,"Delaying start until next ring [%p]",rec);
957 return;
958 }
959 bool ok = true;
960 if (fxsCaller || rec->fxo()->answerOnPolarity())
961 ok = rec->startRecording();
962 else
963 ok = rec->answered();
964 if (!ok) {
965 rec->hangup();
966 plugin.terminateChan(rec,rec->reason());
967 }
968 }
969
970 // Apply debug level. Call create and create worker thread on first init
971 // Re(load) lines and calls specific group reload
initialize(const NamedList & params,const NamedList & defaults,String & error)972 bool ModuleGroup::initialize(const NamedList& params, const NamedList& defaults,
973 String& error)
974 {
975 if (!m_init)
976 debugChain(&plugin);
977
978 int level = params.getIntValue("debuglevel",m_init ? DebugEnabler::debugLevel() : plugin.debugLevel());
979 if (level >= 0) {
980 debugEnabled(0 != level);
981 debugLevel(level);
982 }
983
984 m_ringback = params.getBoolValue("ringback");
985
986 Lock2 lock(this,fxoRec());
987 bool ok = true;
988 if (!m_init) {
989 m_init = true;
990 if (!fxoRec())
991 ok = create(params,defaults,error);
992 else
993 ok = createRecorder(params,defaults,error);
994 if (!ok)
995 return false;
996 m_thread = new AnalogWorkerThread(this);
997 if (!m_thread->startup()) {
998 error = "Failed to start worker thread";
999 return false;
1000 }
1001 }
1002
1003 // (Re)load analog lines
1004 bool all = params.getBoolValue("useallcircuits",true);
1005
1006 unsigned int n = circuits().length();
1007 for (unsigned int i = 0; i < n; i++) {
1008 SignallingCircuit* cic = static_cast<SignallingCircuit*>(circuits()[i]);
1009 if (!cic)
1010 continue;
1011
1012 // Setup line parameter list
1013 NamedList dummy("");
1014 String sectName = s_lineSectPrefix + toString() + "/" + String(cic->code());
1015 NamedList* lineParams = s_cfg.getSection(sectName);
1016 if (!lineParams)
1017 lineParams = &dummy;
1018 bool remove = !lineParams->getBoolValue("enable",true);
1019
1020 ModuleLine* line = static_cast<ModuleLine*>(findLine(cic->code()));
1021
1022 // Remove existing line if required
1023 if (remove) {
1024 if (line) {
1025 XDebug(this,DebugAll,"Removing line=%s [%p]",line->address(),this);
1026 plugin.lineUnavailable(line);
1027 TelEngine::destruct(line);
1028 }
1029 continue;
1030 }
1031
1032 // Reload line if already created. Notify plugin if service state changed
1033 completeLineParams(*lineParams,params,defaults);
1034 if (line) {
1035 bool inService = (line->state() != AnalogLine::OutOfService);
1036 reloadLine(line,*lineParams);
1037 if (inService != (line->state() != AnalogLine::OutOfService))
1038 plugin.lineUnavailable(line);
1039 continue;
1040 }
1041
1042 // Don't create the line if useallcircuits is false and no section in config
1043 if (!all && lineParams == &dummy)
1044 continue;
1045
1046 DDebug(this,DebugAll,"Creating line for cic=%u [%p]",cic->code(),this);
1047 // Create a new line (create its peer if this is a monitor)
1048 line = new ModuleLine(this,cic->code(),*lineParams,params);
1049 while (fxoRec() && line->type() != AnalogLine::Unknown) {
1050 SignallingCircuit* fxoCic = static_cast<SignallingCircuit*>(fxoRec()->circuits()[i]);
1051 if (!fxoCic) {
1052 Debug(this,DebugNote,"FXO circuit is missing for %s/%u [%p]",
1053 debugName(),cic->code(),this);
1054 TelEngine::destruct(line);
1055 break;
1056 }
1057
1058 NamedList dummyFxo("");
1059 String fxoName = s_lineSectPrefix + fxoRec()->toString() + "/" + String(fxoCic->code());
1060 NamedList* fxoParams = s_cfg.getSection(fxoName);
1061 if (!fxoParams)
1062 fxoParams = &dummyFxo;
1063
1064 completeLineParams(*fxoParams,params,defaults);
1065
1066 ModuleLine* fxoLine = new ModuleLine(fxoRec(),fxoCic->code(),*fxoParams,params);
1067 if (fxoLine->type() == AnalogLine::Unknown) {
1068 TelEngine::destruct(fxoLine);
1069 TelEngine::destruct(line);
1070 break;
1071 }
1072 fxoRec()->appendLine(fxoLine);
1073 line->setPeer(fxoLine);
1074 break;
1075 }
1076
1077 // Append line to group: constructor may fail
1078 if (line && line->type() != AnalogLine::Unknown) {
1079 appendLine(line);
1080 // Disconnect the line if not expecting call setup
1081 if (line->callSetup() != AnalogLine::Before)
1082 line->disconnect(true);
1083 }
1084 else {
1085 Debug(this,DebugNote,"Failed to create line %s/%u [%p]",
1086 debugName(),cic->code(),this);
1087 TelEngine::destruct(line);
1088 }
1089 }
1090
1091 if (!fxoRec())
1092 ok = reload(params,defaults,error);
1093 else
1094 ok = reloadRecorder(params,defaults,error);
1095 return ok;
1096 }
1097
1098 // Copy some data to a channel
copyData(AnalogChannel * chan)1099 void ModuleGroup::copyData(AnalogChannel* chan)
1100 {
1101 if (!chan || fxoRec())
1102 return;
1103 chan->m_callEndedTarget = m_callEndedTarget;
1104 chan->m_oooTarget = m_oooTarget;
1105 if (!chan->m_lang)
1106 chan->m_lang = m_lang;
1107 chan->m_callEndedTimer.interval(m_callEndedPlayTime);
1108 }
1109
1110 // Append/remove endpoints from list
setEndpoint(CallEndpoint * ep,bool add)1111 void ModuleGroup::setEndpoint(CallEndpoint* ep, bool add)
1112 {
1113 if (!ep)
1114 return;
1115 Lock lock(this);
1116 if (add)
1117 m_endpoints.append(ep);
1118 else
1119 m_endpoints.remove(ep,false);
1120 }
1121
1122 // Find a recorder by its line
findRecorder(ModuleLine * line)1123 AnalogCallRec* ModuleGroup::findRecorder(ModuleLine* line)
1124 {
1125 if (!fxoRec())
1126 return 0;
1127 Lock lock(this);
1128 for (ObjList* o = m_endpoints.skipNull(); o; o = o->skipNull()) {
1129 AnalogCallRec* rec = static_cast<AnalogCallRec*>(o->get());
1130 if (rec->line() == line)
1131 return rec;
1132 }
1133 return 0;
1134 }
1135
1136 // Fill a string with group status parameters
statusParams(String & str)1137 void ModuleGroup::statusParams(String& str)
1138 {
1139 str.append("module=",";") << plugin.name();
1140 str << ",name=" << toString();
1141 str << ",type=" << lookup(!fxo()?type():AnalogLine::Monitor,AnalogLine::typeNames());
1142 str << ",lines=" << lines().count();
1143 str << "," << s_lineStatusDetail;
1144 for (ObjList* o = lines().skipNull(); o; o = o->skipNext())
1145 (static_cast<ModuleLine*>(o->get()))->statusDetail(str);
1146 }
1147
1148 // Fill a string with group status detail parameters
statusDetail(String & str)1149 void ModuleGroup::statusDetail(String& str)
1150 {
1151 // format=Type|Lines
1152 Lock lock(this);
1153 str.append(toString(),";") << "=";
1154 str << lookup(!fxo()?type():AnalogLine::Monitor,AnalogLine::typeNames());
1155 str << "|" << lines().count();
1156 }
1157
1158 // Disconnect all group's endpoints
clearEndpoints(const char * reason)1159 void ModuleGroup::clearEndpoints(const char* reason)
1160 {
1161 if (!reason)
1162 reason = "shutdown";
1163
1164 DDebug(this,DebugAll,"Clearing endpoints with reason=%s [%p]",reason,this);
1165 bool chans = !fxoRec();
1166 lock();
1167 ListIterator iter(m_endpoints);
1168 for (;;) {
1169 RefPointer<CallEndpoint> c = static_cast<CallEndpoint*>(iter.get());
1170 unlock();
1171 if (!c)
1172 break;
1173 if (chans)
1174 plugin.terminateChan(static_cast<AnalogChannel*>((CallEndpoint*)c),reason);
1175 else
1176 plugin.terminateChan(static_cast<AnalogCallRec*>((CallEndpoint*)c),reason);
1177 c = 0;
1178 lock();
1179 }
1180 }
1181
1182 // Check timers for recorders owned by this group
checkTimers(Time & when)1183 void ModuleGroup::checkTimers(Time& when)
1184 {
1185 bool chans = !fxoRec();
1186 lock();
1187 ListIterator iter(m_endpoints);
1188 for (;;) {
1189 RefPointer<CallEndpoint> c = static_cast<CallEndpoint*>(iter.get());
1190 unlock();
1191 if (!c)
1192 break;
1193 if (chans) {
1194 AnalogChannel* ch = static_cast<AnalogChannel*>((CallEndpoint*)c);
1195 if (!ch->checkTimeouts(when))
1196 plugin.terminateChan(ch,"timeout");
1197 }
1198 else {
1199 AnalogCallRec* ch = static_cast<AnalogCallRec*>((CallEndpoint*)c);
1200 if (!ch->checkTimeouts(when))
1201 plugin.terminateChan(ch,"timeout");
1202 }
1203 c = 0;
1204 lock();
1205 }
1206 }
1207
1208 // Create FXS/FXO group data: called by initialize() on first init
create(const NamedList & params,const NamedList & defaults,String & error)1209 bool ModuleGroup::create(const NamedList& params, const NamedList& defaults,
1210 String& error)
1211 {
1212 String device = params.getValue("spans");
1213 ObjList* voice = device.split(',',false);
1214 if (voice && voice->count())
1215 buildGroup(this,*voice,error);
1216 else
1217 error << "Missing or invalid spans=" << device;
1218 TelEngine::destruct(voice);
1219 if (error)
1220 return false;
1221 return true;
1222 }
1223
1224 // Reload FXS/FXO data: called by initialize() (not called on first init if create failed)
reload(const NamedList & params,const NamedList & defaults,String & error)1225 bool ModuleGroup::reload(const NamedList& params, const NamedList& defaults,
1226 String& error)
1227 {
1228 // (Re)load tone targets
1229 if (type() == AnalogLine::FXS) {
1230 int tmp = params.getIntValue("call-ended-playtime",
1231 defaults.getIntValue("call-ended-playtime",5));
1232 if (tmp < 0)
1233 tmp = 5;
1234 m_callEndedPlayTime = 1000 * (unsigned int)tmp;
1235 m_callEndedTarget = params.getValue("call-ended-target",
1236 defaults.getValue("call-ended-target"));
1237 if (!m_callEndedTarget)
1238 m_callEndedTarget = "tone/busy";
1239 m_oooTarget = params.getValue("outoforder-target",
1240 defaults.getValue("outoforder-target"));
1241 if (!m_oooTarget)
1242 m_oooTarget = "tone/outoforder";
1243 m_lang = params.getValue("lang",defaults.getValue("lang"));
1244 XDebug(this,DebugAll,"Targets: call-ended='%s' outoforder='%s' [%p]",
1245 m_callEndedTarget.c_str(),m_oooTarget.c_str(),this);
1246 }
1247 return true;
1248 }
1249
1250 // Create recorder group data: called by initialize() on first init
createRecorder(const NamedList & params,const NamedList & defaults,String & error)1251 bool ModuleGroup::createRecorder(const NamedList& params, const NamedList& defaults,
1252 String& error)
1253 {
1254 for (unsigned int i = 0; i < 2; i++) {
1255 String device = params.getValue(callertype(0 != i));
1256 ObjList* voice = device.split(',',false);
1257 if (voice && voice->count())
1258 if (i)
1259 buildGroup(this,*voice,error);
1260 else
1261 buildGroup(fxoRec(),*voice,error);
1262 else
1263 error << "Missing or invalid " << callertype(0 != i) << " spans=" << device;
1264 TelEngine::destruct(voice);
1265 if (error)
1266 return false;
1267 }
1268 return true;
1269 }
1270
1271 // Reload recorder data: called by initialize() (not called on first init if create failed)
reloadRecorder(const NamedList & params,const NamedList & defaults,String & error)1272 bool ModuleGroup::reloadRecorder(const NamedList& params, const NamedList& defaults,
1273 String& error)
1274 {
1275 return true;
1276 }
1277
1278 // Reload existing line's parameters
reloadLine(ModuleLine * line,const NamedList & params)1279 void ModuleGroup::reloadLine(ModuleLine* line, const NamedList& params)
1280 {
1281 if (!line)
1282 return;
1283 bool inService = !params.getBoolValue("out-of-service",false);
1284 if (inService == (line->state() != AnalogLine::OutOfService))
1285 return;
1286 Lock lock(line);
1287 Debug(this,DebugAll,"Reloading line %s in-service=%s [%p]",line->address(),String::boolText(inService),this);
1288 line->ref();
1289 line->enable(inService,true);
1290 line->deref();
1291 }
1292
1293 // Build the circuit list for a given group
buildGroup(ModuleGroup * group,ObjList & spanList,String & error)1294 void ModuleGroup::buildGroup(ModuleGroup* group, ObjList& spanList, String& error)
1295 {
1296 if (!group)
1297 return;
1298 unsigned int start = 0;
1299 for (ObjList* o = spanList.skipNull(); o; o = o->skipNext()) {
1300 String* s = static_cast<String*>(o->get());
1301 if (s->null())
1302 continue;
1303 SignallingCircuitSpan* span = buildSpan(*s,start);
1304 if (!span) {
1305 error << "Failed to build span '" << *s << "'";
1306 break;
1307 }
1308 start += span->increment();
1309 }
1310 }
1311
1312
1313 /**
1314 * AnalogChannel
1315 */
1316 // Incoming: msg=0. Outgoing: msg is the call execute message
AnalogChannel(ModuleLine * line,Message * msg,RecordTrigger recorder)1317 AnalogChannel::AnalogChannel(ModuleLine* line, Message* msg, RecordTrigger recorder)
1318 : Channel(&plugin,0,(msg != 0)),
1319 m_line(line),
1320 m_hungup(false),
1321 m_ringback(false),
1322 m_routeOnSecondRing(false),
1323 m_recording(recorder),
1324 m_callEndedTimer(0),
1325 m_ringTimer(RING_PATTERN_TIME),
1326 m_alarmTimer(line ? line->alarmTimeout() : 0),
1327 m_dialTimer(0),
1328 m_polarityCount(0),
1329 m_polarity(false),
1330 m_privacy(false),
1331 m_callsetup(AnalogLine::NoCallSetup)
1332 {
1333 if (msg)
1334 setChanParams(*msg);
1335 m_line->userdata(this);
1336 if (m_line->moduleGroup()) {
1337 m_line->moduleGroup()->setEndpoint(this,true);
1338 m_ringback = m_line->moduleGroup()->ringback();
1339 }
1340
1341 // Set caller/called from line
1342 if (isOutgoing()) {
1343 m_lang = msg->getValue("lang");
1344 m_line->setCall(msg->getValue("caller"),msg->getValue("callername"),msg->getValue("called"));
1345 }
1346 else
1347 if ((m_line->type() == AnalogLine::FXS) || (recorder == FXO))
1348 m_line->setCall("","","off-hook");
1349 else
1350 m_line->setCall("","","ringing");
1351
1352 const char* mode = 0;
1353 switch (recorder) {
1354 case FXO:
1355 mode = "Record FXO";
1356 break;
1357 case FXS:
1358 mode = "Record FXS";
1359 break;
1360 default:
1361 mode = isOutgoing() ? "Outgoing" : "Incoming";
1362 }
1363 Debug(this,DebugCall,"%s call on line %s caller=%s called=%s [%p]",
1364 mode,
1365 m_line->address(),
1366 m_line->caller().c_str(),m_line->called().c_str(),this);
1367
1368 m_line->connect(false);
1369 m_line->acceptPulseDigit(isIncoming());
1370
1371 // Incoming on FXO:
1372 // Caller id after first ring: delay router until the second ring and
1373 // set/remove call setup detector
1374 if (isIncoming() && m_line->type() == AnalogLine::FXO && recorder != FXO) {
1375 m_routeOnSecondRing = (m_line->callSetup() == AnalogLine::After);
1376 if (m_routeOnSecondRing)
1377 m_line->setCallSetupDetector();
1378 else
1379 m_line->removeCallSetupDetector();
1380 }
1381
1382 m_address = m_line->address();
1383 if (m_line->type() == AnalogLine::FXS && m_line->moduleGroup())
1384 m_line->moduleGroup()->copyData(this);
1385
1386 setMaxcall(msg);
1387 if (msg)
1388 setMaxPDD(*msg);
1389 // Startup
1390 Message* m = message("chan.startup");
1391 if (msg)
1392 m->copyParams(*msg,"caller,callername,called,billid,callto,username");
1393 m_line->copyCall(*m);
1394 if (isOutgoing())
1395 m_targetid = msg->getValue("id");
1396 Engine::enqueue(m);
1397
1398 // Init call
1399 setAudio(isIncoming());
1400 if (isOutgoing()) {
1401 // Check for parameters override
1402 m_dialTimer.interval(msg->getIntValue("delaydial",0));
1403 // FXO: send start line event
1404 // FXS: start ring and send call setup (caller id)
1405 // Return if failed to send events
1406 switch (line->type()) {
1407 case AnalogLine::FXO:
1408 m_line->sendEvent(SignallingCircuitEvent::StartLine,AnalogLine::Dialing);
1409 break;
1410 case AnalogLine::FXS:
1411 m_callsetup = m_line->callSetup();
1412 // Check call setup override
1413 {
1414 NamedString* ns = msg->getParam("callsetup");
1415 if (ns)
1416 m_callsetup = lookup(*ns,AnalogLine::csNames(),AnalogLine::NoCallSetup);
1417 }
1418 m_privacy = getPrivacy(*msg);
1419 if (m_callsetup == AnalogLine::Before)
1420 m_line->sendCallSetup(m_privacy);
1421 {
1422 NamedList* params = 0;
1423 NamedList callerId("");
1424 if (m_callsetup != AnalogLine::NoCallSetup) {
1425 params = &callerId;
1426 m_line->copyCall(callerId,m_privacy);
1427 }
1428 m_line->sendEvent(SignallingCircuitEvent::RingBegin,AnalogLine::Dialing,params);
1429 }
1430 if (m_callsetup == AnalogLine::After)
1431 m_dialTimer.interval(500);
1432 break;
1433 default: ;
1434 }
1435 if (line->state() == AnalogLine::Idle) {
1436 setReason("failure");
1437 msg->setParam("error",m_reason);
1438 return;
1439 }
1440 }
1441 else {
1442 m_line->changeState(AnalogLine::Dialing);
1443
1444 // FXO: start ring timer (check if the caller hangs up before answer)
1445 // FXS: do nothing
1446 switch (line->type()) {
1447 case AnalogLine::FXO:
1448 if (recorder == FXO) {
1449 m_line->noRingTimer().stop();
1450 break;
1451 }
1452 m_line->noRingTimer().interval(m_line->noRingTimeout());
1453 DDebug(this,DebugAll,"Starting ring timer for " FMT64 "ms [%p]",
1454 m_line->noRingTimer().interval(),this);
1455 m_line->noRingTimer().start();
1456 if (recorder == FXS) {
1457 // The FXS recorder will route only on off-hook
1458 m_routeOnSecondRing = false;
1459 return;
1460 }
1461 break;
1462 case AnalogLine::FXS:
1463 break;
1464 default: ;
1465 }
1466 if (!m_routeOnSecondRing)
1467 startRouter(true);
1468 else
1469 DDebug(this,DebugInfo,"Delaying route until next ring [%p]",this);
1470 }
1471 }
1472
~AnalogChannel()1473 AnalogChannel::~AnalogChannel()
1474 {
1475 XDebug(this,DebugCall,"AnalogChannel::~AnalogChannel() [%p]",this);
1476 }
1477
1478 // Start outgoing media and echo train if earlymedia or got peer with data source
msgProgress(Message & msg)1479 bool AnalogChannel::msgProgress(Message& msg)
1480 {
1481 Lock lock(m_mutex);
1482 if (isAnswered())
1483 return true;
1484
1485 Channel::msgProgress(msg);
1486 setStatus();
1487 if (m_line && m_line->type() != AnalogLine::FXS)
1488 m_line->acceptPulseDigit(false);
1489 if (msg.getBoolValue("earlymedia",getPeer() && getPeer()->getSource())) {
1490 setAudio(false);
1491 if (m_line)
1492 m_line->setCircuitParam("echotrain",msg.getValue("echotrain"));
1493 }
1494 return true;
1495 }
1496
1497 // Start outgoing media and echo train if earlymedia or got peer with data source
msgRinging(Message & msg)1498 bool AnalogChannel::msgRinging(Message& msg)
1499 {
1500 Lock lock(m_mutex);
1501 if (isAnswered())
1502 return true;
1503
1504 Channel::msgRinging(msg);
1505 setStatus();
1506 if (m_line) {
1507 if (m_line->type() != AnalogLine::FXS)
1508 m_line->acceptPulseDigit(false);
1509 m_line->changeState(AnalogLine::Ringing);
1510 }
1511 bool media = msg.getBoolValue("earlymedia",getPeer() && getPeer()->getSource());
1512 if (media) {
1513 setAudio(false);
1514 if (m_line)
1515 m_line->setCircuitParam("echotrain",msg.getValue("echotrain"));
1516 }
1517 else if (m_ringback && m_line) {
1518 // Provide ringback from circuit features if supported
1519 NamedList params("ringback");
1520 params.addParam("tone","ringback");
1521 media = m_line->sendEvent(SignallingCircuitEvent::GenericTone,¶ms);
1522 }
1523 if (media)
1524 m_ringback = false;
1525 return true;
1526 }
1527
1528 // Terminate ringing on line. Start echo train. Open audio streams
msgAnswered(Message & msg)1529 bool AnalogChannel::msgAnswered(Message& msg)
1530 {
1531 Lock lock(m_mutex);
1532 if (m_line) {
1533 m_line->noRingTimer().stop();
1534 m_line->removeCallSetupDetector();
1535 m_line->sendEvent(SignallingCircuitEvent::RingEnd);
1536 if (m_line->type() == AnalogLine::FXS)
1537 polarityControl(true);
1538 else {
1539 m_line->acceptPulseDigit(false);
1540 m_line->sendEvent(SignallingCircuitEvent::OffHook);
1541 }
1542 m_line->changeState(AnalogLine::Answered);
1543 m_line->setCircuitParam("echotrain",msg.getValue("echotrain"));
1544 }
1545 setAudio(true);
1546 setAudio(false);
1547 Channel::msgAnswered(msg);
1548 setStatus();
1549 return true;
1550 }
1551
1552 // Send tones or flash
msgTone(Message & msg,const char * tone)1553 bool AnalogChannel::msgTone(Message& msg, const char* tone)
1554 {
1555 Lock lock(m_mutex);
1556 if (!(tone && *tone && m_line))
1557 return false;
1558 if (*tone != 'F') {
1559 if (m_dialTimer.started()) {
1560 Debug(this,DebugAll,"msgTone(%s). Adding to called number [%p]",tone,this);
1561 m_line->called().append(tone);
1562 return true;
1563 }
1564 return sendTones(tone,false);
1565 }
1566 // Flash event: don't send if not FXO
1567 if (m_line->type() != AnalogLine::FXO) {
1568 Debug(this,DebugInfo,"Can't send line flash on non-FXO line (tones='%s') [%p]",tone,this);
1569 return false;
1570 }
1571 Debug(this,DebugAll,"Sending line flash (tones='%s') [%p]",tone,this);
1572 return m_line->sendEvent(SignallingCircuitEvent::Flash);
1573 }
1574
1575 // Hangup
msgDrop(Message & msg,const char * reason)1576 bool AnalogChannel::msgDrop(Message& msg, const char* reason)
1577 {
1578 Lock lock(m_mutex);
1579 setReason(reason ? reason : "dropped");
1580 if (Engine::exiting() || !m_line || m_line->type() != AnalogLine::FXS)
1581 Channel::msgDrop(msg,m_reason);
1582 hangup(true);
1583 return true;
1584 }
1585
1586 // Update echo canceller and/or start echo training
msgUpdate(Message & msg)1587 bool AnalogChannel::msgUpdate(Message& msg)
1588 {
1589 String tmp = msg.getValue("echocancel");
1590 Lock lock(m_mutex);
1591 if (!(tmp.isBoolean() && m_line))
1592 return false;
1593 bool ok = m_line->setCircuitParam("echocancel",tmp);
1594 if (tmp.toBoolean())
1595 m_line->setCircuitParam("echotrain",msg.getValue("echotrain"));
1596 return ok;
1597 }
1598
1599 // Call routed: set tone detector
callRouted(Message & msg)1600 bool AnalogChannel::callRouted(Message& msg)
1601 {
1602 Channel::callRouted(msg);
1603 setStatus();
1604 Lock lock(m_mutex);
1605 // Update tones language
1606 m_lang = msg.getValue("lang",m_lang);
1607 // Check if the circuit supports tone detection
1608 if (!m_line->circuit())
1609 return true;
1610 String value;
1611 if (m_line->circuit()->getParam("tonedetect",value) && value.toBoolean())
1612 return true;
1613 // Set tone detector
1614 setAudio(false);
1615 if (toneDetect())
1616 DDebug(this,DebugAll,"Loaded tone detector [%p]",this);
1617 else {
1618 setConsumer();
1619 DDebug(this,DebugNote,"Failed to set tone detector [%p]",this);
1620 }
1621 return true;
1622 }
1623
1624 // Call accepted: set line and open audio
callAccept(Message & msg)1625 void AnalogChannel::callAccept(Message& msg)
1626 {
1627 Lock lock(m_mutex);
1628 // Update tones language
1629 m_lang = msg.getValue("lang",m_lang);
1630 if (isAnswered())
1631 return;
1632 if (m_line) {
1633 if (m_line->type() != AnalogLine::FXS)
1634 m_line->acceptPulseDigit(false);
1635 m_line->changeState(AnalogLine::DialComplete);
1636 }
1637 m_ringback = msg.getBoolValue("ringback",m_ringback);
1638 setAudio(false);
1639 setAudio(true);
1640 Channel::callAccept(msg);
1641 }
1642
1643 // Call rejected: hangup
callRejected(const char * error,const char * reason,const Message * msg)1644 void AnalogChannel::callRejected(const char* error, const char* reason,
1645 const Message* msg)
1646 {
1647 if (msg) {
1648 Lock lock(m_mutex);
1649 m_lang = msg->getValue("lang",m_lang);
1650 }
1651 setReason(error ? error : reason);
1652 Channel::callRejected(error,m_reason,msg);
1653 setStatus();
1654 hangup(true);
1655 }
1656
disconnected(bool final,const char * reason)1657 void AnalogChannel::disconnected(bool final, const char* reason)
1658 {
1659 Lock lock(m_mutex);
1660 Channel::disconnected(final,m_reason);
1661 hangup(!final,"disconnected",reason);
1662 }
1663
1664 // Disconnect the channel
disconnect(const char * reason)1665 bool AnalogChannel::disconnect(const char* reason)
1666 {
1667 Lock lock(m_mutex);
1668 if (!m_hungup) {
1669 setReason(reason);
1670 setStatus("disconnecting");
1671 }
1672 return Channel::disconnect(m_reason,parameters());
1673 }
1674
1675 // Hangup call
1676 // Keep call alive to play announcements on FXS line not set on hook by the remote FXO
hangup(bool local,const char * status,const char * reason)1677 void AnalogChannel::hangup(bool local, const char* status, const char* reason)
1678 {
1679 // Sanity: reset dial timer and call setup flag if FXS
1680 m_dialTimer.stop();
1681 m_callsetup = AnalogLine::NoCallSetup;
1682
1683 Lock lock(m_mutex);
1684
1685 if (m_hungup)
1686 return;
1687 m_hungup = true;
1688 setReason(reason ? reason : (Engine::exiting() ? "shutdown" : "normal"));
1689 if (status)
1690 setStatus(status);
1691 setSource();
1692 setConsumer();
1693
1694 Message* m = message("chan.hangup",true);
1695 putStatus(*m);
1696 m->setParam("reason",m_reason);
1697 Engine::enqueue(m);
1698
1699 setStatus("hangup");
1700 if (m_line && m_line->state() != AnalogLine::Idle)
1701 m_line->sendEvent(SignallingCircuitEvent::RingEnd);
1702 polarityControl(false);
1703
1704 // Check some conditions to keep the channel
1705 if (!m_line || m_line->type() != AnalogLine::FXS ||
1706 !local || Engine::exiting() ||
1707 (isOutgoing() && m_line->state() < AnalogLine::Answered) ||
1708 (isIncoming() && m_line->state() == AnalogLine::Idle))
1709 return;
1710
1711 Debug(this,DebugAll,"Call ended. Keep channel alive [%p]",this);
1712 if (m_callEndedTimer.interval()) {
1713 m_callEndedTimer.start();
1714 m_line->changeState(AnalogLine::CallEnded);
1715 if (!setAnnouncement("call-ended",m_callEndedTarget))
1716 ref();
1717 }
1718 else {
1719 m_line->changeState(AnalogLine::OutOfOrder);
1720 if (!setAnnouncement("out-of-order",m_oooTarget))
1721 ref();
1722 }
1723 }
1724
1725 // Process incoming or outgoing digits
evDigits(const char * text,bool tone)1726 void AnalogChannel::evDigits(const char* text, bool tone)
1727 {
1728 if (!(text && *text))
1729 return;
1730 Debug(this,DebugAll,"Got %s digits=%s [%p]",tone?"tone":"pulse",text,this);
1731 Message* m = message("chan.dtmf",false,true);
1732 m->addParam("text",text);
1733 if (!tone)
1734 m->addParam("pulse",String::boolText(true));
1735 m->addParam("detected","analog");
1736 dtmfEnqueue(m);
1737 }
1738
1739 // Line got off hook. Terminate ringing
1740 // Outgoing: answer it (call outCallAnswered())
1741 // Incoming: start echo train
evOffHook()1742 void AnalogChannel::evOffHook()
1743 {
1744 Lock lock(m_mutex);
1745 if (isOutgoing()) {
1746 outCallAnswered();
1747 if (m_line)
1748 m_line->sendEvent(SignallingCircuitEvent::RingEnd,AnalogLine::Answered);
1749 }
1750 else if (m_line) {
1751 m_line->sendEvent(SignallingCircuitEvent::RingEnd,m_line->state());
1752 m_line->setCircuitParam("echotrain");
1753 if (m_recording == FXS)
1754 startRouter(true);
1755 }
1756 }
1757
1758 // Line ring on/off notification. Ring off is ignored
1759 // Outgoing: enqueue call.ringing
1760 // Incoming: FXO: Route the call if delayed. Remove line's detector and start ring timer
evRing(bool on)1761 void AnalogChannel::evRing(bool on)
1762 {
1763 Lock lock(m_mutex);
1764
1765 // Re(start) ring timer. Ignore ring events if timer was already started
1766 if (on) {
1767 bool ignore = m_ringTimer.started();
1768 m_ringTimer.start();
1769 if (ignore)
1770 return;
1771 }
1772
1773 // Check call setup
1774 if (m_callsetup == AnalogLine::After) {
1775 if (on)
1776 m_dialTimer.stop();
1777 else
1778 m_dialTimer.start();
1779 }
1780
1781 // Done if ringer is off
1782 if (!on)
1783 return;
1784
1785 // Outgoing: remote party is ringing
1786 if (isOutgoing()) {
1787 Engine::enqueue(message("call.ringing",false,true));
1788 if (m_line)
1789 m_line->changeState(AnalogLine::Ringing);
1790 return;
1791 }
1792 // Incoming: start ringing (restart FXO timer to check remote hangup)
1793 // Start router if delayed
1794 if (!m_line)
1795 return;
1796 if (m_line->type() == AnalogLine::FXO) {
1797 if (m_routeOnSecondRing) {
1798 m_routeOnSecondRing = false;
1799 startRouter(false);
1800 }
1801 m_line->removeCallSetupDetector();
1802 if (m_line->noRingTimer().interval()) {
1803 DDebug(this,DebugAll,"Restarting ring timer for " FMT64 "ms [%p]",
1804 m_line->noRingTimer().interval(),this);
1805 m_line->noRingTimer().start();
1806 }
1807 }
1808 }
1809
1810 // Line started (initialized) notification
1811 // Answer outgoing FXO calls on lines not expecting polarity changes to answer
1812 // Send called number if any
evLineStarted()1813 void AnalogChannel::evLineStarted()
1814 {
1815 Lock lock(m_mutex);
1816 if (!m_line)
1817 return;
1818 // Send number: delay it if interval is not 0
1819 bool stopDial = true;
1820 if (m_line->called()) {
1821 if (m_line->delayDial() || m_dialTimer.interval()) {
1822 if (!m_dialTimer.started()) {
1823 if (!m_dialTimer.interval())
1824 m_dialTimer.interval(m_line->delayDial());
1825 DDebug(this,DebugAll,"Delaying dial for " FMT64 "ms [%p]",
1826 m_dialTimer.interval(),this);
1827 m_dialTimer.start();
1828 }
1829 stopDial = false;
1830 }
1831 else
1832 sendTones(m_line->called());
1833 }
1834
1835 // Answer now outgoing FXO calls on lines not expecting polarity changes to answer
1836 if (isOutgoing() && m_line && m_line->type() == AnalogLine::FXO &&
1837 !m_line->answerOnPolarity())
1838 outCallAnswered(stopDial);
1839 }
1840
1841 // Dial complete notification. Enqueue call.progress
1842 // Answer outgoing FXO calls on lines not expecting polarity changes to answer
evDialComplete()1843 void AnalogChannel::evDialComplete()
1844 {
1845 DDebug(this,DebugAll,"Dial completed [%p]",this);
1846 Lock lock(m_mutex);
1847 if (m_line)
1848 m_line->changeState(AnalogLine::DialComplete);
1849 Engine::enqueue(message("call.progress",true,true));
1850 // Answer now outgoing FXO calls on lines not expecting polarity changes to answer
1851 if (isOutgoing() && m_line && m_line->type() == AnalogLine::FXO &&
1852 !m_line->answerOnPolarity())
1853 outCallAnswered();
1854 }
1855
1856 // Line polarity change notification
1857 // Terminate call if:
1858 // - no line or line is not FXO,
1859 // - Outgoing: don't answer on polarity or already answered and should hangup on polarity change
1860 // - Incoming: don't answer on polarity or polarity already changed and should hangup on polarity change
evPolarity()1861 void AnalogChannel::evPolarity()
1862 {
1863 Lock lock(m_mutex);
1864 m_polarityCount++;
1865 DDebug(this,DebugAll,"Line polarity changed %u time(s) [%p]",m_polarityCount,this);
1866 bool terminate = (!m_line || m_line->type() != AnalogLine::FXO);
1867 if (!terminate) {
1868 if (isOutgoing())
1869 if (!m_line->answerOnPolarity() || isAnswered())
1870 terminate = m_line->hangupOnPolarity();
1871 else
1872 outCallAnswered();
1873 else if (!m_line->answerOnPolarity() || m_polarityCount > 1)
1874 terminate = m_line->hangupOnPolarity();
1875 }
1876
1877 if (terminate) {
1878 DDebug(this,DebugAll,"Terminating on polarity change [%p]",this);
1879 hangup(false);
1880 plugin.terminateChan(this);
1881 }
1882 }
1883
1884 // Line ok: stop alarm timer
1885 // Terminate channel if not answered; otherwise: start timer if not already started
evAlarm(bool alarm,const char * alarms)1886 void AnalogChannel::evAlarm(bool alarm, const char* alarms)
1887 {
1888 Lock lock(m_mutex);
1889 if (!alarm) {
1890 Debug(this,DebugInfo,"No more alarms on line [%p]",this);
1891 if (m_line)
1892 m_line->setCircuitParam("echotrain");
1893 m_alarmTimer.stop();
1894 return;
1895 }
1896 // Terminate now if not answered
1897 if (!isAnswered()) {
1898 Debug(this,DebugNote,"Line is out of order alarms=%s. Terminating now [%p]",
1899 alarms,this);
1900 hangup(false,0,"net-out-of-order");
1901 plugin.terminateChan(this);
1902 return;
1903 }
1904 // Wait if answered
1905 if (!m_alarmTimer.started()) {
1906 Debug(this,DebugNote,
1907 "Line is out of order alarms=%s. Starting timer for " FMT64U " ms [%p]",
1908 alarms,m_alarmTimer.interval(),this);
1909 m_alarmTimer.start();
1910 }
1911 }
1912
1913 // Check timers. Return false to terminate
checkTimeouts(const Time & when)1914 bool AnalogChannel::checkTimeouts(const Time& when)
1915 {
1916 Lock lock(m_mutex);
1917 // Stop ring timer: we didn't received a ring event in the last interval
1918 if (m_ringTimer.timeout(when.msecNow()))
1919 m_ringTimer.stop();
1920 if (m_alarmTimer.timeout(when.msecNow())) {
1921 m_alarmTimer.stop();
1922 DDebug(this,DebugInfo,"Line was in alarm for " FMT64 " ms [%p]",
1923 m_alarmTimer.interval(),this);
1924 setReason("net-out-of-order");
1925 hangup(false);
1926 return false;
1927 }
1928 if (m_callEndedTimer.timeout(when.msecNow())) {
1929 m_callEndedTimer.stop();
1930 m_line->changeState(AnalogLine::OutOfOrder);
1931 disconnect();
1932 if (!setAnnouncement("out-of-order",m_oooTarget))
1933 ref();
1934 return true;
1935 }
1936 if (m_line->noRingTimer().timeout(when.msecNow())) {
1937 DDebug(this,DebugInfo,"No ring for " FMT64 " ms. Terminating [%p]",
1938 m_line->noRingTimer().interval(),this);
1939 m_line->noRingTimer().stop();
1940 setReason("cancelled");
1941 hangup(false);
1942 return false;
1943 }
1944 if (m_dialTimer.timeout(when.msecNow())) {
1945 m_dialTimer.stop();
1946 m_callsetup = AnalogLine::NoCallSetup;
1947 DDebug(this,DebugInfo,"Dial timer expired. %s [%p]",
1948 m_line?"Sending number/callsetup":"Line is missing",this);
1949 if (!m_line)
1950 return true;
1951 if (m_line->type() == AnalogLine::FXO)
1952 sendTones(m_line->called());
1953 else if (m_line->type() == AnalogLine::FXS)
1954 m_line->sendCallSetup(m_privacy);
1955 return true;
1956 }
1957 return true;
1958 }
1959
1960 // Route incoming
startRouter(bool first)1961 void AnalogChannel::startRouter(bool first)
1962 {
1963 m_routeOnSecondRing = false;
1964 Message* m = message("call.preroute",false,true);
1965 if (m_line) {
1966 m_line->copyCall(*m);
1967 const char* caller = m->getValue("caller");
1968 if (!(caller && *caller))
1969 m->setParam("caller",s_unk);
1970 switch (m_line->type()) {
1971 case AnalogLine::FXO:
1972 if (getSource())
1973 m->addParam("format",getSource()->getFormat());
1974 break;
1975 case AnalogLine::FXS:
1976 m->addParam("overlapped","true");
1977 m->addParam("lang",m_lang,false);
1978 break;
1979 default: ;
1980 }
1981 }
1982 switch (m_recording) {
1983 case FXO:
1984 m->addParam("callsource","fxo");
1985 break;
1986 case FXS:
1987 m->addParam("callsource","fxs");
1988 break;
1989 default: ;
1990 }
1991 DDebug(this,DebugInfo,"Starting router %scaller=%s callername=%s [%p]",
1992 first?"":"(delayed) ",
1993 m->getValue("caller"),m->getValue("callername"),this);
1994 Channel::startRouter(m);
1995 }
1996
1997 // Set data source and consumer
setAudio(bool in)1998 bool AnalogChannel::setAudio(bool in)
1999 {
2000 if ((in && getSource()) || (!in && getConsumer()))
2001 return true;
2002 if ((m_recording != None) && !in)
2003 return true;
2004
2005 SignallingCircuit* cic = m_line ? m_line->circuit() : 0;
2006 if (cic) {
2007 if (in)
2008 setSource(static_cast<DataSource*>(cic->getObject(YATOM("DataSource"))));
2009 else
2010 setConsumer(static_cast<DataConsumer*>(cic->getObject(YATOM("DataConsumer"))));
2011 }
2012
2013 DataNode* res = in ? (DataNode*)getSource() : (DataNode*)getConsumer();
2014 if (res)
2015 DDebug(this,DebugAll,"Data %s set to (%p): '%s' [%p]",
2016 in?"source":"consumer",res,res->getFormat().c_str(),this);
2017 else
2018 Debug(this,DebugNote,"Failed to set data %s%s [%p]",
2019 in?"source":"consumer",cic?"":". Circuit is missing",this);
2020 return res != 0;
2021 }
2022
2023 // Set call status
setStatus(const char * newStat)2024 bool AnalogChannel::setStatus(const char* newStat)
2025 {
2026 if (newStat)
2027 status(newStat);
2028 if (debugAt(DebugCall)) {
2029 String tmp;
2030 getStatus(tmp);
2031 if (m_reason)
2032 Debug(this,DebugCall,"status=%s reason=%s [%p]",
2033 tmp.c_str(),m_reason.c_str(),this);
2034 else
2035 Debug(this,DebugCall,"status=%s [%p]",tmp.c_str(),this);
2036 }
2037 return true;
2038 }
2039
2040 // Set tones to the remote end of the line
setAnnouncement(const char * status,const char * callto)2041 bool AnalogChannel::setAnnouncement(const char* status, const char* callto)
2042 {
2043 setStatus(status);
2044 // Don't set announcements for FXO
2045 if (!m_line || m_line->type() == AnalogLine::FXO)
2046 return false;
2047 Message* m = message("call.execute",false,true);
2048 m->addParam("callto",callto);
2049 m->addParam("lang",m_lang,false);
2050 bool ok = Engine::dispatch(*m);
2051 TelEngine::destruct(m);
2052 if (ok) {
2053 setAudio(false);
2054 Debug(this,DebugAll,"Announcement set to %s",callto);
2055 }
2056 else
2057 Debug(this,DebugMild,"Set announcement=%s failed",callto);
2058 return ok;
2059 }
2060
2061 // Outgoing call answered: set call state, start echo train, open data source/consumer
outCallAnswered(bool stopDial)2062 void AnalogChannel::outCallAnswered(bool stopDial)
2063 {
2064 // Sanity: reset dial timer and call setup flag if FXS
2065 if (m_line && m_line->type() == AnalogLine::FXS) {
2066 m_dialTimer.stop();
2067 m_callsetup = AnalogLine::NoCallSetup;
2068 }
2069
2070 if (isAnswered())
2071 return;
2072
2073 if (stopDial)
2074 m_dialTimer.stop();
2075 m_answered = true;
2076 m_ringback = false;
2077 setStatus("answered");
2078 if (m_line) {
2079 m_line->changeState(AnalogLine::Answered);
2080 polarityControl(true);
2081 m_line->setCircuitParam("echotrain");
2082 }
2083 setAudio(true);
2084 setAudio(false);
2085 Engine::enqueue(message("call.answered",false,true));
2086 }
2087
2088 // Hangup. Release memory
destroyed()2089 void AnalogChannel::destroyed()
2090 {
2091 detachLine();
2092 if (!m_hungup)
2093 hangup(true);
2094 else {
2095 setConsumer();
2096 setSource();
2097 }
2098 setStatus("destroyed");
2099 Channel::destroyed();
2100 }
2101
2102 // Detach the line from this channel
detachLine()2103 void AnalogChannel::detachLine()
2104 {
2105 Lock lock(m_mutex);
2106 if (!m_line)
2107 return;
2108
2109 if (m_line->moduleGroup())
2110 m_line->moduleGroup()->setEndpoint(this,false);
2111 m_line->userdata(0);
2112 m_line->acceptPulseDigit(true);
2113 if (m_line->state() != AnalogLine::Idle) {
2114 m_line->sendEvent(SignallingCircuitEvent::RingEnd);
2115 m_line->sendEvent(SignallingCircuitEvent::OnHook);
2116 m_line->changeState(AnalogLine::Idle);
2117 }
2118 m_line->removeCallSetupDetector();
2119 m_line->setCall();
2120 polarityControl(false);
2121
2122 // Don't disconnect the line if waiting for call setup (need audio)
2123 if (m_line->type() == AnalogLine::FXO && m_line->callSetup() == AnalogLine::Before)
2124 m_line->setCallSetupDetector();
2125 else
2126 m_line->disconnect(false);
2127 TelEngine::destruct(m_line);
2128 }
2129
2130 // Send tones (DTMF or dial number)
sendTones(const char * tone,bool dial)2131 bool AnalogChannel::sendTones(const char* tone, bool dial)
2132 {
2133 if (!(m_line && tone && *tone))
2134 return false;
2135 DDebug(this,DebugInfo,"Sending %sband tones='%s' dial=%u [%p]",
2136 m_line->outbandDtmf()?"out":"in",tone,dial,this);
2137 bool ok = false;
2138 if (m_line->outbandDtmf()) {
2139 NamedList p("");
2140 p.addParam("tone",tone);
2141 p.addParam("dial",String::boolText(dial));
2142 ok = m_line->sendEvent(SignallingCircuitEvent::Dtmf,&p);
2143 }
2144 if (!ok)
2145 ok = dtmfInband(tone);
2146 return ok;
2147 }
2148
2149
2150 /**
2151 * AnalogCallRec
2152 */
2153 // Append to driver's list
AnalogCallRec(ModuleLine * line,bool fxsCaller,const char * id)2154 AnalogCallRec::AnalogCallRec(ModuleLine* line, bool fxsCaller, const char* id)
2155 : CallEndpoint(id),
2156 m_line(line),
2157 m_fxsCaller(fxsCaller),
2158 m_answered(false),
2159 m_hungup(false),
2160 m_polarityCount(0),
2161 m_startOnSecondRing(false),
2162 m_ringTimer(RING_PATTERN_TIME),
2163 m_status("startup")
2164 {
2165 debugName(CallEndpoint::id());
2166 debugChain(&plugin);
2167
2168 ModuleLine* fxo = this->fxo();
2169 if (!(fxo && m_line->ref())) {
2170 m_line = 0;
2171 m_reason = "invalid-line";
2172 return;
2173 }
2174
2175 plugin.setRecorder(this,true);
2176 if (m_line->moduleGroup())
2177 m_line->moduleGroup()->setEndpoint(this,true);
2178 m_line->userdata(this);
2179
2180 m_line->connect(true);
2181 m_line->changeState(AnalogLine::Dialing,true);
2182 m_line->acceptPulseDigit(fxsCaller);
2183 fxo->acceptPulseDigit(!fxsCaller);
2184
2185 // FXS caller:
2186 // Caller id after first ring: delay router until the second ring and
2187 // set/remove call setup detector
2188 if (fxsCaller) {
2189 m_startOnSecondRing = (fxo->callSetup() == AnalogLine::After);
2190 if (m_startOnSecondRing)
2191 fxo->setCallSetupDetector();
2192 else
2193 fxo->removeCallSetupDetector();
2194 }
2195
2196 if (fxsCaller && m_line->getPeer())
2197 m_address = m_line->getPeer()->address();
2198 else
2199 m_address = m_line->address();
2200
2201 // Set caller/called
2202 if (fxsCaller) {
2203 if (m_startOnSecondRing && fxo->callSetup() == AnalogLine::Before)
2204 fxo->setCall(fxo->caller(),"",m_line->called());
2205 else
2206 fxo->setCall(s_unk,"",m_line->called());
2207 }
2208 else
2209 m_line->setCall(s_unk,"",fxo->called());
2210
2211 Debug(this,DebugCall,"Created addr=%s initiator=%s [%p]",
2212 m_address.c_str(),callertype(fxsCaller),this);
2213
2214 Engine::enqueue(message("chan.startup"));
2215
2216 if (fxsCaller) {
2217 fxo->noRingTimer().interval(fxo->noRingTimeout());
2218 DDebug(this,DebugAll,"Starting ring timer for " FMT64 "ms [%p]",
2219 fxo->noRingTimer().interval(),this);
2220 fxo->noRingTimer().start();
2221 }
2222 }
2223
2224 // Close recorder. Disconnect the line
hangup(const char * reason)2225 void AnalogCallRec::hangup(const char* reason)
2226 {
2227 Lock lock(m_mutex);
2228 if (m_hungup)
2229 return;
2230
2231 m_hungup = true;
2232 m_status = "hangup";
2233 if (!m_reason)
2234 m_reason = reason;
2235 if (!m_reason)
2236 m_reason = Engine::exiting() ? "shutdown" : "unknown";
2237
2238 Debug(this,DebugCall,"Hangup reason='%s' [%p]",m_reason.c_str(),this);
2239 setSource();
2240 Engine::enqueue(message("chan.hangup",false));
2241
2242 // Disconnect lines
2243 if (!m_line)
2244 return;
2245
2246 ModuleLine* peer = fxo();
2247 bool sync = !(peer && peer->callSetup() == AnalogLine::Before);
2248
2249 m_line->changeState(AnalogLine::Idle,true);
2250 m_line->disconnect(sync);
2251 m_line->acceptPulseDigit(true);
2252 m_line->setCall();
2253
2254 if (peer) {
2255 if (!sync)
2256 peer->setCallSetupDetector();
2257 peer->acceptPulseDigit(true);
2258 peer->setCall();
2259 }
2260 }
2261
disconnect(const char * reason)2262 bool AnalogCallRec::disconnect(const char* reason)
2263 {
2264 Debug(this,DebugCall,"Disconnecting reason='%s' [%p]",reason,this);
2265 hangup(reason);
2266 return CallEndpoint::disconnect(m_reason);
2267 }
2268
2269 // Get source(s) and other objects
2270 // DataSource0: caller's source
2271 // DataSource1: called's source
getObject(const String & name) const2272 void* AnalogCallRec::getObject(const String& name) const
2273 {
2274 int who = (name == YATOM("DataSource0")) ? 0 : (name == YATOM("DataSource1") ? 1 : -1);
2275 if (who == -1)
2276 return CallEndpoint::getObject(name);
2277
2278 ModuleLine* target = 0;
2279 if (who)
2280 target = m_fxsCaller ? m_line : fxo();
2281 else
2282 target = m_fxsCaller ? fxo() : m_line;
2283 return (target && target->circuit()) ? target->circuit()->getObject(YATOM("DataSource")) : 0;
2284 }
2285
2286 // Create data source. Route and execute
startRecording()2287 bool AnalogCallRec::startRecording()
2288 {
2289 m_line->setCircuitParam("echotrain");
2290 if (getSource())
2291 return true;
2292
2293 Debug(this,DebugCall,"Start recording [%p]",this);
2294
2295 Lock lock (m_mutex);
2296 String format = "2*";
2297 DataSource* src = 0;
2298 String buflen;
2299 if (m_line && m_line->circuit()) {
2300 src = static_cast<DataSource*>(m_line->circuit()->getObject(YATOM("DataSource")));
2301 m_line->circuit()->getParam("buflen",buflen);
2302 }
2303 if (src)
2304 format << src->getFormat();
2305
2306 // Create source
2307 Message* m = message("chan.attach",false,true);
2308 m->addParam("source","mux/");
2309 m->addParam("single",String::boolText(true));
2310 m->addParam("notify",id());
2311 if (buflen)
2312 m->addParam("chanbuffer",buflen);
2313 m->addParam("format",format);
2314 m->addParam("fail","true");
2315 m->addParam("failempty","true");
2316 if (!Engine::dispatch(m))
2317 Debug(this,DebugNote,"Error attaching data mux '%s' [%p]",m->getValue("error"),this);
2318 else if (m->userData())
2319 setSource(static_cast<DataSource*>(m->userData()->getObject(YATOM("DataSource"))));
2320 TelEngine::destruct(m);
2321 if (!getSource()) {
2322 m_reason = "nodata";
2323 return false;
2324 }
2325
2326 // Route and execute
2327 m = message("call.preroute");
2328 m->addParam("callsource",callertype(m_fxsCaller));
2329 const char* caller = m->getValue("caller");
2330 if (!(caller && *caller))
2331 m->setParam("caller",s_unk);
2332 bool ok = false;
2333 while (true) {
2334 if (Engine::dispatch(m) && (m->retValue() == "-" || m->retValue() == "error")) {
2335 m_reason = m->getValue("reason",m->getValue("error","failure"));
2336 break;
2337 }
2338 *m = "call.route";
2339 m->addParam("type","record");
2340 m->addParam("format",format);
2341 m->setParam("callsource",callertype(m_fxsCaller));
2342 if (!(Engine::dispatch(m) && m->retValue())) {
2343 m_reason = "noroute";
2344 break;
2345 }
2346 *m = "call.execute";
2347 m->userData(this);
2348 m->setParam("callto",m->retValue());
2349 m->retValue().clear();
2350 if (!Engine::dispatch(m)) {
2351 m_reason = "noconn";
2352 break;
2353 }
2354 ok = true;
2355 break;
2356 }
2357 TelEngine::destruct(m);
2358 if (getPeer()) {
2359 XDebug(this,DebugInfo,"Got connected: deref() [%p]",this);
2360 deref();
2361 }
2362 else
2363 setSource();
2364 return ok;
2365 }
2366
2367 // Call answered: start recording
answered()2368 bool AnalogCallRec::answered()
2369 {
2370 Lock lock(m_mutex);
2371 if (m_line)
2372 m_line->noRingTimer().stop();
2373 if (fxo())
2374 fxo()->noRingTimer().stop();
2375 m_startOnSecondRing = false;
2376 if (!(m_line && startRecording()))
2377 return false;
2378 if (m_answered)
2379 return true;
2380 Debug(this,DebugCall,"Answered [%p]",this);
2381 m_answered = true;
2382 m_status = "answered";
2383 m_line->changeState(AnalogLine::Answered,true);
2384 Engine::enqueue(message("call.answered"));
2385 return true;
2386 }
2387
2388 // Process rings: start recording if delayed
ringing(bool fxsEvent)2389 bool AnalogCallRec::ringing(bool fxsEvent)
2390 {
2391 Lock lock(m_mutex);
2392
2393 // Re(start) ring timer. Ignore ring events if timer was already started
2394 bool ignore = m_ringTimer.started();
2395 m_ringTimer.start();
2396 if (ignore)
2397 return true;
2398
2399 if (m_line)
2400 m_line->changeState(AnalogLine::Ringing,true);
2401
2402 // Ignore rings from caller party
2403 if (m_fxsCaller != fxsEvent) {
2404 DDebug(this,DebugAll,"Ignoring ring from caller [%p]",this);
2405 return true;
2406 }
2407
2408 if (!m_answered) {
2409 m_status = "ringing";
2410 Engine::enqueue(message("call.ringing",false,true));
2411 }
2412
2413 bool ok = true;
2414 if (m_fxsCaller) {
2415 if (m_startOnSecondRing) {
2416 m_startOnSecondRing = false;
2417 ok = startRecording();
2418 }
2419 if (m_line->getPeer())
2420 fxo()->removeCallSetupDetector();
2421 if (ok && !m_answered) {
2422 DDebug(this,DebugAll,"Restarting ring timer for " FMT64 "ms [%p]",
2423 fxo()->noRingTimer().interval(),this);
2424 fxo()->noRingTimer().start();
2425 }
2426 }
2427 return ok;
2428 }
2429
2430 // Enqueue chan.dtmf
evDigits(bool fxsEvent,const char * text,bool tone)2431 void AnalogCallRec::evDigits(bool fxsEvent, const char* text, bool tone)
2432 {
2433 if (!(text && *text))
2434 return;
2435 DDebug(this,DebugAll,"Got %s digits=%s from %s [%p]",
2436 tone?"tone":"pulse",text,callertype(fxsEvent),this);
2437 Message* m = message("chan.dtmf",false,true);
2438 m->addParam("text",text);
2439 if (!tone)
2440 m->addParam("pulse",String::boolText(true));
2441 m->addParam("sender",callertype(fxsEvent));
2442 m->addParam("detected","analog");
2443 Engine::enqueue(m);
2444 }
2445
2446 // Process line polarity changes
evPolarity(bool fxsEvent)2447 bool AnalogCallRec::evPolarity(bool fxsEvent)
2448 {
2449 if (fxsEvent)
2450 return true;
2451
2452 Lock lock(m_mutex);
2453 m_polarityCount++;
2454 DDebug(this,DebugAll,"Line polarity changed %u time(s) [%p]",m_polarityCount,this);
2455
2456 ModuleLine* fxo = this->fxo();
2457 if (!fxo)
2458 return false;
2459
2460 if (m_fxsCaller) {
2461 if (!fxo->answerOnPolarity() || m_polarityCount > 1)
2462 return !fxo->hangupOnPolarity();
2463 return true;
2464 }
2465 if (!fxo->answerOnPolarity() || m_answered)
2466 return !fxo->hangupOnPolarity();
2467 return answered();
2468 }
2469
2470 // Line alarms changed
evAlarm(bool fxsEvent,bool alarm,const char * alarms)2471 bool AnalogCallRec::evAlarm(bool fxsEvent, bool alarm, const char* alarms)
2472 {
2473 Lock lock(m_mutex);
2474 if (alarm) {
2475 Debug(this,DebugNote,"%s line is out of order alarms=%s. Terminating now [%p]",
2476 callertype(!fxsEvent),alarms,this);
2477 if (!m_reason) {
2478 m_reason = callertype(!fxsEvent);
2479 m_reason << "-out-of-order";
2480 }
2481 return false;
2482 }
2483 else {
2484 if (m_line)
2485 m_line->setCircuitParam("echotrain");
2486 Debug(this,DebugInfo,"No more alarms on %s line [%p]",callertype(!fxsEvent),this);
2487 }
2488 return true;
2489 }
2490
2491 // Check timers. Return false to terminate
checkTimeouts(const Time & when)2492 bool AnalogCallRec::checkTimeouts(const Time& when)
2493 {
2494 Lock lock(m_mutex);
2495
2496 if (m_ringTimer.timeout(when.msecNow()))
2497 m_ringTimer.stop();
2498
2499 if (!fxo()->noRingTimer().timeout(when.msecNow()))
2500 return true;
2501 DDebug(this,DebugInfo,"Ring timer expired [%p]",this);
2502 fxo()->noRingTimer().stop();
2503 hangup("cancelled");
2504 return false;
2505 }
2506
2507 // Fill a string with recorder status parameters
statusParams(String & str)2508 void AnalogCallRec::statusParams(String& str)
2509 {
2510 str.append("module=",",") << plugin.name();
2511 str << ",peerid=";
2512 if (getPeer())
2513 str << getPeer()->id();
2514 str << ",status=" << m_status;
2515 str << ",initiator=" << callertype(m_fxsCaller);
2516 str << ",answered=" << m_answered;
2517 str << ",address=" << m_address;
2518 }
2519
2520 // Fill a string with recorder status detail parameters
statusDetail(String & str)2521 void AnalogCallRec::statusDetail(String& str)
2522 {
2523 // format=Status|Address|Peer
2524 Lock lock(m_mutex);
2525 str.append(id(),";") << "=" << m_status;
2526 str << "|" << m_address << "|";
2527 if (getPeer())
2528 str << getPeer()->id();
2529 }
2530
2531 // Remove from driver's list
destroyed()2532 void AnalogCallRec::destroyed()
2533 {
2534 plugin.setRecorder(this,false);
2535 hangup();
2536 // Reset line
2537 if (m_line) {
2538 m_line->userdata(0,true);
2539 if (m_line->moduleGroup())
2540 m_line->moduleGroup()->setEndpoint(this,false);
2541 TelEngine::destruct(m_line);
2542 }
2543 Debug(this,DebugCall,"Destroyed reason='%s' [%p]",m_reason.c_str(),this);
2544 CallEndpoint::destroyed();
2545 }
2546
disconnected(bool final,const char * reason)2547 void AnalogCallRec::disconnected(bool final, const char *reason)
2548 {
2549 DDebug(this,DebugCall,"Disconnected final=%s reason='%s' [%p]",
2550 String::boolText(final),reason,this);
2551 hangup(reason);
2552 CallEndpoint::disconnected(final,m_reason);
2553 }
2554
message(const char * name,bool peers,bool userdata)2555 Message* AnalogCallRec::message(const char* name, bool peers, bool userdata)
2556 {
2557 Message* m = new Message(name);
2558 m->addParam("id",id());
2559 m->addParam("status",m_status);
2560 if (m_address)
2561 m->addParam("address",m_address);
2562 ModuleLine* fxo = peers ? this->fxo() : 0;
2563 if (fxo) {
2564 if (m_fxsCaller) {
2565 m->addParam("caller",fxo->caller());
2566 m->addParam("called",fxo->called());
2567 }
2568 else {
2569 m->addParam("caller",m_line->caller());
2570 m->addParam("called",m_line->called());
2571 }
2572 }
2573 if (m_reason)
2574 m->addParam("reason",m_reason);
2575 if (userdata)
2576 m->userData(this);
2577 return m;
2578 }
2579
2580
2581 /**
2582 * AnalogDriver
2583 */
2584 String AnalogDriver::s_statusCmd[StatusCmdCount] = {"groups","lines","recorders"};
2585
AnalogDriver()2586 AnalogDriver::AnalogDriver()
2587 : Driver("analog","varchans"),
2588 m_init(false),
2589 m_recId(0)
2590 {
2591 Output("Loaded module Analog Channel");
2592 m_statusCmd << "status " << name();
2593 m_recPrefix << prefix() << "rec/";
2594 }
2595
~AnalogDriver()2596 AnalogDriver::~AnalogDriver()
2597 {
2598 Output("Unloading module Analog Channel");
2599 m_groups.clear();
2600 }
2601
initialize()2602 void AnalogDriver::initialize()
2603 {
2604 Output("Initializing module Analog Channel");
2605 s_cfg = Engine::configFile("analog");
2606 s_cfg.load();
2607
2608 NamedList dummy("");
2609 NamedList* general = s_cfg.getSection("general");
2610 if (!general)
2611 general = &dummy;
2612
2613 // Startup
2614 setup();
2615 if (!m_init) {
2616 m_init = true;
2617 installRelay(Masquerade);
2618 installRelay(Halt);
2619 installRelay(Progress);
2620 installRelay(Update);
2621 installRelay(Route);
2622 Engine::install(new EngineStartHandler);
2623 Engine::install(new ChanNotifyHandler);
2624 }
2625
2626 // Build/initialize groups
2627 String tmpRec = m_recPrefix.substr(0,m_recPrefix.length()-1);
2628 unsigned int n = s_cfg.sections();
2629 for (unsigned int i = 0; i < n; i++) {
2630 NamedList* sect = s_cfg.getSection(i);
2631 if (!sect || sect->null() || *sect == "general" ||
2632 sect->startsWith(s_lineSectPrefix))
2633 continue;
2634
2635 // Check section name
2636 bool valid = true;
2637 if (*sect == name() || *sect == tmpRec)
2638 valid = false;
2639 else
2640 for (unsigned int i = 0; i < StatusCmdCount; i++)
2641 if (*sect == s_statusCmd[i]) {
2642 valid = false;
2643 break;
2644 }
2645 if (!valid) {
2646 Debug(this,DebugWarn,"Invalid use of reserved word in section name '%s'",sect->c_str());
2647 continue;
2648 }
2649
2650 ModuleGroup* group = findGroup(*sect);
2651 if (!sect->getBoolValue("enable",true)) {
2652 if (group)
2653 removeGroup(group);
2654 continue;
2655 }
2656
2657 // Create and/or initialize. Check for valid type if creating
2658 const char* stype = sect->getValue("type");
2659 int type = lookup(stype,AnalogLine::typeNames(),AnalogLine::Unknown);
2660 switch (type) {
2661 case AnalogLine::FXO:
2662 case AnalogLine::FXS:
2663 case AnalogLine::Recorder:
2664 case AnalogLine::Monitor:
2665 break;
2666 default:
2667 Debug(this,DebugWarn,"Unknown type '%s' for group '%s'",stype,sect->c_str());
2668 continue;
2669 }
2670
2671 bool create = (group == 0);
2672 Debug(this,DebugAll,"%sing group '%s' of type '%s'",create?"Creat":"Reload",sect->c_str(),stype);
2673
2674 if (create) {
2675 if (type != AnalogLine::Monitor)
2676 group = new ModuleGroup((AnalogLine::Type)type,*sect);
2677 else {
2678 String tmp = *sect;
2679 tmp << "/fxo";
2680 ModuleGroup* fxo = new ModuleGroup(tmp);
2681 group = new ModuleGroup(*sect,fxo);
2682 }
2683 lock();
2684 m_groups.append(group);
2685 unlock();
2686 XDebug(this,DebugAll,"Added group (%p,'%s')",group,group->debugName());
2687 }
2688
2689 String error;
2690 if (!group->initialize(*sect,*general,error)) {
2691 Debug(this,DebugWarn,"Failed to %s group '%s'. Error: '%s'",
2692 create?"create":"reload",sect->c_str(),error.safe());
2693 if (create)
2694 removeGroup(group);
2695 }
2696 }
2697 }
2698
msgExecute(Message & msg,String & dest)2699 bool AnalogDriver::msgExecute(Message& msg, String& dest)
2700 {
2701 CallEndpoint* peer = YOBJECT(CallEndpoint,msg.userData());
2702 ModuleLine* line = 0;
2703 String cause;
2704 const char* error = "failure";
2705
2706 // Check message parameters: peer channel, group, circuit, line
2707 while (true) {
2708 if (!peer) {
2709 cause = "No data channel";
2710 break;
2711 }
2712 String tmp;
2713 int cic = decodeAddr(dest,tmp,true);
2714 ModuleGroup* group = findGroup(tmp);
2715 if (group && !group->fxoRec()) {
2716 if (cic >= 0)
2717 line = static_cast<ModuleLine*>(group->findLine(cic));
2718 else if (cic == -1) {
2719 Lock lock(group);
2720 // Destination is a group: find the first free idle line
2721 for (ObjList* o = group->lines().skipNull(); o; o = o->skipNext()) {
2722 line = static_cast<ModuleLine*>(o->get());
2723 Lock lockLine(line);
2724 if (!line->userdata() && line->state() == AnalogLine::Idle)
2725 break;
2726 line = 0;
2727 }
2728 lock.drop();
2729 if (!line) {
2730 cause << "All lines in group '" << dest << "' are busy";
2731 error = "busy";
2732 break;
2733 }
2734 }
2735 }
2736
2737 if (!line) {
2738 cause << "No line with address '" << dest << "'";
2739 error = "noroute";
2740 break;
2741 }
2742 if (line->type() == AnalogLine::Unknown) {
2743 cause << "Line '" << line->address() << "' has unknown type";
2744 break;
2745 }
2746 if (line->userdata()) {
2747 cause << "Line '" << line->address() << "' is busy";
2748 error = "busy";
2749 break;
2750 }
2751 if (line->state() == AnalogLine::OutOfService) {
2752 cause << "Line '" << line->address() << "' is out of service";
2753 error = "noroute";
2754 break;
2755 }
2756 if (!line->ref())
2757 cause = "ref() failed";
2758 break;
2759 }
2760
2761 if (!line || cause) {
2762 Debug(this,DebugNote,"Analog call failed. %s",cause.c_str());
2763 msg.setParam("error",error);
2764 return false;
2765 }
2766
2767 Debug(this,DebugAll,"Executing call. caller=%s called=%s line=%s",
2768 msg.getValue("caller"),msg.getValue("called"),line->address());
2769
2770 msg.clearParam("error");
2771 // Create channel
2772 AnalogChannel* analogCh = new AnalogChannel(line,&msg);
2773 analogCh->initChan();
2774 error = msg.getValue("error");
2775 if (!error) {
2776 if (analogCh->connect(peer,msg.getValue("reason"))) {
2777 analogCh->callConnect(msg);
2778 msg.setParam("peerid",analogCh->id());
2779 msg.setParam("targetid",analogCh->id());
2780 if (analogCh->line() && (analogCh->line()->type() == AnalogLine::FXS))
2781 Engine::enqueue(analogCh->message("call.ringing",false,true));
2782 }
2783 }
2784 else
2785 Debug(this,DebugNote,"Analog call failed with reason '%s'",error);
2786 analogCh->deref();
2787 return !error;
2788 }
2789
dropAll(Message & msg)2790 void AnalogDriver::dropAll(Message& msg)
2791 {
2792 const char* reason = msg.getValue("reason");
2793 if (!(reason && *reason))
2794 reason = "dropped";
2795 DDebug(this,DebugInfo,"dropAll('%s')",reason);
2796 Driver::dropAll(msg);
2797 // Drop recorders
2798 lock();
2799 ListIterator iter(m_recorders);
2800 for (;;) {
2801 RefPointer<AnalogCallRec> c = static_cast<AnalogCallRec*>(iter.get());
2802 unlock();
2803 if (!c)
2804 break;
2805 terminateChan(c,reason);
2806 c = 0;
2807 lock();
2808 }
2809 }
2810
received(Message & msg,int id)2811 bool AnalogDriver::received(Message& msg, int id)
2812 {
2813 String target;
2814
2815 switch (id) {
2816 case Masquerade:
2817 // Masquerade a recorder message
2818 target = msg.getValue("id");
2819 if (target.startsWith(recPrefix())) {
2820 Lock lock(this);
2821 AnalogCallRec* rec = findRecorder(target);
2822 if (rec) {
2823 msg = msg.getValue("message");
2824 msg.clearParam("message");
2825 msg.userData(rec);
2826 return false;
2827 }
2828 }
2829 return Driver::received(msg,id);
2830 case Status:
2831 case Drop:
2832 target = msg.getValue("module");
2833 // Target is the driver or channel
2834 if (!target || target == name() || target.startsWith(prefix()))
2835 return Driver::received(msg,id);
2836 // Check if requested a recorder
2837 if (target.startsWith(recPrefix())) {
2838 Lock lock(this);
2839 AnalogCallRec* rec = findRecorder(target);
2840 if (!rec)
2841 return false;
2842 if (id == Status) {
2843 msg.retValue().clear();
2844 rec->statusParams(msg.retValue());
2845 msg.retValue() << "\r\n";
2846 }
2847 else
2848 terminateChan(rec,"dropped");
2849 return true;
2850 }
2851 // Done if the command is drop
2852 if (id == Drop)
2853 return Driver::received(msg,id);
2854 break;
2855 case Halt:
2856 lock();
2857 m_groups.clear();
2858 unlock();
2859 return Driver::received(msg,id);
2860 default:
2861 return Driver::received(msg,id);
2862 }
2863
2864 // Check for additional status commands or a specific group or line
2865 if (!target.startSkip(name(),false))
2866 return false;
2867 target.trimBlanks();
2868 int cmd = 0;
2869 for (; cmd < StatusCmdCount; cmd++)
2870 if (s_statusCmd[cmd] == target)
2871 break;
2872
2873 Lock lock(this);
2874 DDebug(this,DebugInfo,"Processing '%s' target=%s",msg.c_str(),target.c_str());
2875 // Specific group or line
2876 if (cmd == StatusCmdCount) {
2877 String group;
2878 int cic = decodeAddr(target,group,false);
2879 ModuleGroup* grp = findGroup(group);
2880 bool ok = true;
2881 while (grp) {
2882 Lock lock(grp);
2883 if (target == grp->toString()) {
2884 msg.retValue().clear();
2885 grp->statusParams(msg.retValue());
2886 break;
2887 }
2888 ModuleLine* line = static_cast<ModuleLine*>(grp->findLine(cic));
2889 if (!line) {
2890 ok = false;
2891 break;
2892 }
2893 msg.retValue().clear();
2894 Lock lockLine(line);
2895 line->statusParams(msg.retValue());
2896 break;
2897 }
2898 if (ok)
2899 msg.retValue() << "\r\n";
2900 return ok;
2901 }
2902
2903 // Additional command
2904 String detail;
2905 const char* format = 0;
2906 int count = 0;
2907 switch (cmd) {
2908 case Groups:
2909 format = s_groupStatusDetail;
2910 for (ObjList* o = m_groups.skipNull(); o; o = o->skipNext()) {
2911 count++;
2912 (static_cast<ModuleGroup*>(o->get()))->statusDetail(detail);
2913 }
2914 break;
2915 case Lines:
2916 format = s_lineStatusDetail;
2917 for (ObjList* o = m_groups.skipNull(); o; o = o->skipNext()) {
2918 ModuleGroup* grp = static_cast<ModuleGroup*>(o->get());
2919 Lock lockGrp(grp);
2920 for (ObjList* ol = grp->lines().skipNull(); ol; ol = ol->skipNext()) {
2921 count++;
2922 (static_cast<ModuleLine*>(ol->get()))->statusDetail(detail);
2923 }
2924 }
2925 break;
2926 case Recorders:
2927 format = s_recStatusDetail;
2928 for (ObjList* o = m_recorders.skipNull(); o; o = o->skipNext()) {
2929 count++;
2930 (static_cast<AnalogCallRec*>(o->get()))->statusDetail(detail);
2931 }
2932 break;
2933 default:
2934 count = -1;
2935 }
2936 // Just in case we've missed something
2937 if (count == -1)
2938 return false;
2939
2940 msg.retValue().clear();
2941 msg.retValue() << "module=" << name();
2942 msg.retValue() << "," << s_statusCmd[cmd] << "=" << count;
2943 msg.retValue() << "," << format;
2944 if (detail)
2945 msg.retValue() << ";" << detail;
2946 msg.retValue() << "\r\n";
2947 return true;
2948 }
2949
2950 // Handle command complete requests
commandComplete(Message & msg,const String & partLine,const String & partWord)2951 bool AnalogDriver::commandComplete(Message& msg, const String& partLine,
2952 const String& partWord)
2953 {
2954 bool status = partLine.startsWith("status");
2955 bool drop = !status && partLine.startsWith("drop");
2956 if (!(status || drop))
2957 return Driver::commandComplete(msg,partLine,partWord);
2958
2959 // 'status' command
2960 Lock lock(this);
2961 // line='status analog': add additional commands, groups and lines
2962 if (partLine == m_statusCmd) {
2963 DDebug(this,DebugInfo,"Processing '%s' partWord=%s",partLine.c_str(),partWord.c_str());
2964 for (unsigned int i = 0; i < StatusCmdCount; i++)
2965 itemComplete(msg.retValue(),s_statusCmd[i],partWord);
2966 completeGroups(msg.retValue(),partWord);
2967 completeLines(msg.retValue(),partWord);
2968 return true;
2969 }
2970
2971 if (partLine != "status" && partLine != "drop")
2972 return false;
2973
2974 // Empty partial word or name start with it: add name, prefix and recorder prefix
2975 if (itemComplete(msg.retValue(),name(),partWord)) {
2976 if (channels().skipNull())
2977 msg.retValue().append(prefix(),"\t");
2978 return false;
2979 }
2980 // Non empty partial word greater then module name: check if we have a prefix
2981 if (!partWord.startsWith(prefix()))
2982 return false;
2983 // Partial word is not empty and starts with module's prefix
2984 // Recorder prefix (greater then any channel ID): complete recorders
2985 // Between module and recorder prefix: complete recorder prefix and channels
2986 if (partWord.startsWith(recPrefix())) {
2987 bool all = (partWord == recPrefix());
2988 completeChanRec(msg.retValue(),partWord,false,all);
2989 }
2990 else {
2991 bool all = (partWord == prefix());
2992 completeChanRec(msg.retValue(),partWord,true,all);
2993 completeChanRec(msg.retValue(),partWord,false,all);
2994 }
2995 return true;
2996 }
2997
2998 // Execute commands
commandExecute(String & retVal,const String & line)2999 bool AnalogDriver::commandExecute(String& retVal, const String& line)
3000 {
3001 DDebug(this,DebugInfo,"commandExecute(%s)",line.c_str());
3002 return false;
3003 }
3004
3005 // Complete group names from partial command word
completeGroups(String & dest,const String & partWord)3006 void AnalogDriver::completeGroups(String& dest, const String& partWord)
3007 {
3008 for (ObjList* o = m_groups.skipNull(); o; o = o->skipNext())
3009 itemComplete(dest,static_cast<ModuleGroup*>(o->get())->toString(),partWord);
3010 }
3011
3012 // Complete line names from partial command word
completeLines(String & dest,const String & partWord)3013 void AnalogDriver::completeLines(String& dest, const String& partWord)
3014 {
3015 for (ObjList* o = m_groups.skipNull(); o; o = o->skipNext()) {
3016 ModuleGroup* grp = static_cast<ModuleGroup*>(o->get());
3017 Lock lock(grp);
3018 for (ObjList* ol = grp->lines().skipNull(); ol; ol = ol->skipNext())
3019 itemComplete(dest,static_cast<ModuleLine*>(ol->get())->toString(),partWord);
3020 }
3021 }
3022
3023 // Notification of line service state change or removal
3024 // Return true if a channel or recorder was found
lineUnavailable(ModuleLine * line)3025 bool AnalogDriver::lineUnavailable(ModuleLine* line)
3026 {
3027 if (!line)
3028 return false;
3029
3030 const char* reason = (line->state() == AnalogLine::OutOfService) ? "line-out-of-service" : "line-shutdown";
3031 Lock lock(this);
3032 for (ObjList* o = channels().skipNull(); o; o = o->skipNext()) {
3033 AnalogChannel* ch = static_cast<AnalogChannel*>(o->get());
3034 if (ch->line() != line)
3035 continue;
3036 terminateChan(ch,reason);
3037 return true;
3038 }
3039
3040 // Check for recorders
3041 if (!line->getPeer())
3042 return false;
3043 ModuleGroup* grp = line->moduleGroup();
3044 AnalogCallRec* rec = 0;
3045 if (grp && 0 != (rec = grp->findRecorder(line))) {
3046 terminateChan(rec,reason);
3047 return true;
3048 }
3049 return false;
3050 }
3051
3052 // Destroy a channel
terminateChan(AnalogChannel * ch,const char * reason)3053 void AnalogDriver::terminateChan(AnalogChannel* ch, const char* reason)
3054 {
3055 if (!ch)
3056 return;
3057 DDebug(this,DebugAll,"Terminating channel %s peer=%p reason=%s",
3058 ch->id().c_str(),ch->getPeer(),reason);
3059 if (ch->getPeer())
3060 ch->disconnect(reason);
3061 else
3062 ch->deref();
3063 }
3064
3065 // Destroy a monitor endpoint
terminateChan(AnalogCallRec * ch,const char * reason)3066 void AnalogDriver::terminateChan(AnalogCallRec* ch, const char* reason)
3067 {
3068 if (!ch)
3069 return;
3070 DDebug(this,DebugAll,"Terminating recorder %s peer=%p reason=%s",
3071 ch->id().c_str(),ch->getPeer(),reason);
3072 if (ch->getPeer())
3073 ch->disconnect(reason);
3074 else
3075 ch->deref();
3076 }
3077
3078 // Attach detectors after engine started
engineStart(Message & msg)3079 void AnalogDriver::engineStart(Message& msg)
3080 {
3081 s_engineStarted = true;
3082 Lock lock(this);
3083 for (ObjList* o = m_groups.skipNull(); o; o = o->skipNext()) {
3084 ModuleGroup* grp = static_cast<ModuleGroup*>(o->get());
3085 if (grp->type() != AnalogLine::FXO) {
3086 grp = grp->fxoRec();
3087 if (!grp || grp->type() != AnalogLine::FXO)
3088 grp = 0;
3089 }
3090 if (!grp)
3091 continue;
3092 Lock lock(grp);
3093 for (ObjList* ol = grp->lines().skipNull(); ol; ol = ol->skipNext()) {
3094 ModuleLine* line = static_cast<ModuleLine*>(ol->get());
3095 if (line->callSetup() == AnalogLine::Before)
3096 line->setCallSetupDetector();
3097 }
3098 }
3099 }
3100
3101 // Notify lines on detector events or channels
chanNotify(Message & msg)3102 bool AnalogDriver::chanNotify(Message& msg)
3103 {
3104 String target = msg.getValue("targetid");
3105 if (!target.startSkip(plugin.prefix(),false))
3106 return false;
3107
3108 // Check if the notification is for a channel
3109 if (-1 != target.toInteger(-1)) {
3110 Debug(this,DebugStub,"Ignoring chan.notify with target=%s",msg.getValue("targetid"));
3111 return true;
3112 }
3113
3114 // Notify lines
3115 String name;
3116 int cic = decodeAddr(target,name,false);
3117 ModuleLine* line = 0;
3118 Lock lockDrv(this);
3119 ModuleGroup* grp = findGroup(name);
3120 if (grp)
3121 line = static_cast<ModuleLine*>(grp->findLine(cic));
3122 else {
3123 // Find by recorder's fxo
3124 grp = findGroup(name,true);
3125 if (grp && grp->fxoRec())
3126 line = static_cast<ModuleLine*>(grp->fxoRec()->findLine(cic));
3127 }
3128
3129 Lock lockLine(line);
3130 if (!(line && line->ref())) {
3131 Debug(this,DebugNote,"Received chan.notify for unknown target=%s",target.c_str());
3132 return true;
3133 }
3134 lockDrv.drop();
3135 line->processNotify(msg);
3136 line->deref();
3137 return true;
3138 }
3139
3140 // Append/remove recorders from list
setRecorder(AnalogCallRec * rec,bool add)3141 void AnalogDriver::setRecorder(AnalogCallRec* rec, bool add)
3142 {
3143 if (!rec)
3144 return;
3145 Lock lock(this);
3146 if (add)
3147 m_recorders.append(rec);
3148 else
3149 m_recorders.remove(rec,false);
3150 }
3151
3152 // Remove a group from list
removeGroup(ModuleGroup * group)3153 void AnalogDriver::removeGroup(ModuleGroup* group)
3154 {
3155 if (!group)
3156 return;
3157 Lock lock(this);
3158 Debug(this,DebugAll,"Removing group (%p,'%s')",group,group->debugName());
3159 m_groups.remove(group);
3160 }
3161
3162 // Find a group or recorder by its name
3163 // Set useFxo to true to find a recorder by its fxo's name
findGroup(const char * name,bool useFxo)3164 ModuleGroup* AnalogDriver::findGroup(const char* name, bool useFxo)
3165 {
3166 if (!useFxo)
3167 return findGroup(name);
3168 if (!(name && *name))
3169 return 0;
3170 Lock lock(this);
3171 String tmp = name;
3172 for (ObjList* o = m_groups.skipNull(); o; o = o->skipNext()) {
3173 ModuleGroup* grp = static_cast<ModuleGroup*>(o->get());
3174 if (grp->fxoRec() && grp->fxoRec()->toString() == tmp)
3175 return grp;
3176 }
3177 return 0;
3178 }
3179
3180
3181 /**
3182 * AnalogWorkerThread
3183 */
AnalogWorkerThread(ModuleGroup * group)3184 AnalogWorkerThread::AnalogWorkerThread(ModuleGroup* group)
3185 : Thread("Analog Worker"),
3186 m_client(group),
3187 m_groupName(group ? group->debugName() : "")
3188 {
3189 }
3190
~AnalogWorkerThread()3191 AnalogWorkerThread::~AnalogWorkerThread()
3192 {
3193 DDebug(&plugin,DebugAll,"AnalogWorkerThread(%p,'%s') terminated [%p]",
3194 m_client,m_groupName.c_str(),this);
3195 if (m_client)
3196 m_client->m_thread = 0;
3197 }
3198
run()3199 void AnalogWorkerThread::run()
3200 {
3201 Debug(&plugin,DebugAll,"AnalogWorkerThread(%p,'%s') start running [%p]",
3202 m_client,m_groupName.c_str(),this);
3203 if (!m_client)
3204 return;
3205 while (true) {
3206 Time t = Time();
3207 AnalogLineEvent* event = m_client->getEvent(t);
3208 if (!event) {
3209 m_client->checkTimers(t);
3210 Thread::idle(true);
3211 continue;
3212 }
3213 ModuleLine* line = static_cast<ModuleLine*>(event->line());
3214 SignallingCircuitEvent* cicEv = event->event();
3215 if (line && cicEv)
3216 if (!m_client->fxoRec())
3217 m_client->handleEvent(*line,*cicEv);
3218 else
3219 m_client->handleRecEvent(*line,*cicEv);
3220 else
3221 Debug(m_client,DebugInfo,"Invalid event (%p) line=%p cic event=%p",
3222 event,line,event->event());
3223 TelEngine::destruct(event);
3224 if (Thread::check(true))
3225 break;
3226 }
3227 }
3228
3229
3230 /**
3231 * EngineStartHandler
3232 */
received(Message & msg)3233 bool EngineStartHandler::received(Message& msg)
3234 {
3235 plugin.engineStart(msg);
3236 return false;
3237 }
3238
3239
3240 /**
3241 * ChanNotifyHandler
3242 */
received(Message & msg)3243 bool ChanNotifyHandler::received(Message& msg)
3244 {
3245 return plugin.chanNotify(msg);
3246 }
3247
3248 }; // anonymous namespace
3249
3250 /* vi: set ts=8 sw=4 sts=4 noet: */
3251