1 /**
2  * engine.cpp
3  * This file is part of the YATE Project http://YATE.null.ro
4  *
5  * Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN
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 "yatesig.h"
23 #include <yateversn.h>
24 
25 #include <string.h>
26 
27 // Maximum wait for a non-critical mutex acquisition
28 #ifndef MAX_LOCK_WAIT
29 #define MAX_LOCK_WAIT 10000
30 #endif
31 
32 #define MIN_TICK_SLEEP 500
33 #define DEF_TICK_SLEEP 5000
34 #define MAX_TICK_SLEEP 50000
35 
36 namespace TelEngine {
37 
38 class SignallingThreadPrivate : public Thread
39 {
40 public:
SignallingThreadPrivate(SignallingEngine * engine,const char * name,Priority prio)41     inline SignallingThreadPrivate(SignallingEngine* engine, const char* name, Priority prio)
42 	: Thread(name,prio), m_engine(engine)
43 	{ }
44     virtual ~SignallingThreadPrivate();
45     virtual void run();
46 
47 private:
48     SignallingEngine* m_engine;
49 };
50 
51 };
52 
53 
54 using namespace TelEngine;
55 
56 static ObjList s_factories;
57 static Mutex s_mutex(true,"SignallingFactory");
58 
59 // Retrieve a value from a list
60 // Shift it if upper bits are set and mask is not set
61 // Mask it with a given mask
fixValue(const NamedList & list,const String & param,const TokenDict * dict,unsigned char mask,unsigned char upperMask,unsigned char shift)62 static inline unsigned char fixValue(const NamedList& list, const String& param,
63     const TokenDict* dict, unsigned char mask, unsigned char upperMask, unsigned char shift)
64 {
65     unsigned char val = (unsigned char)list.getIntValue(param,dict,0);
66     if (0 != (val & upperMask) && 0 == (val & mask))
67 	val >>= shift;
68     return val & mask;
69 }
70 
SignallingFactory(bool fallback)71 SignallingFactory::SignallingFactory(bool fallback)
72 {
73     s_mutex.lock();
74     if (!s_factories.find(this)) {
75 	ObjList* l = fallback ? s_factories.append(this) : s_factories.insert(this);
76 	l->setDelete(false);
77     }
78     s_mutex.unlock();
79 }
80 
~SignallingFactory()81 SignallingFactory::~SignallingFactory()
82 {
83     s_mutex.lock();
84     s_factories.remove(this,false);
85     s_mutex.unlock();
86 }
87 
build(const String & type,NamedList * name)88 SignallingComponent* SignallingFactory::build(const String& type, NamedList* name)
89 {
90     if (type.null())
91 	return 0;
92     NamedList dummy(type);
93     if (!name)
94 	name = &dummy;
95     Lock lock(s_mutex);
96     for (ObjList* l = &s_factories; l; l = l->next()) {
97 	SignallingFactory* f = static_cast<SignallingFactory*>(l->get());
98 	if (!f)
99 	    continue;
100 	DDebug(DebugAll,"Attempting to create a '%s' %s using factory %p",
101 	    name->c_str(),type.c_str(),f);
102 	SignallingComponent* obj = f->create(type,*name);
103 	if (obj)
104 	    return obj;
105     }
106     lock.drop();
107     DDebug(DebugInfo,"Factory creating default '%s' named '%s'",type.c_str(),name->c_str());
108     // now build some objects we know about
109     if (type == YSTRING("SS7MTP2"))
110 	return new SS7MTP2(*name);
111     else if (type == YSTRING("SS7M2PA"))
112 	return new SS7M2PA(*name);
113     else if (type == YSTRING("SS7MTP3"))
114 	return new SS7MTP3(*name);
115     else if (type == YSTRING("SS7Router"))
116 	return new SS7Router(*name);
117     else if (type == YSTRING("SS7Management"))
118 	return new SS7Management(*name);
119     else if (type == YSTRING("ISDNQ921"))
120 	return new ISDNQ921(*name,*name);
121     else if (type == YSTRING("ISDNQ931"))
122 	return new ISDNQ931(*name,*name);
123     else if (type == YSTRING("ISDNQ931Monitor"))
124 	return new ISDNQ931Monitor(*name,*name);
125     Debug(DebugMild,"Factory could not create '%s' named '%s'",type.c_str(),name->c_str());
126     return 0;
127 }
128 
buildInternal(const String & type,NamedList * name)129 void* SignallingFactory::buildInternal(const String& type, NamedList* name)
130 {
131     SignallingComponent* c = build(type,name);
132     if (!c)
133 	return 0;
134     void* raw = c->getObject(type);
135     if (!raw)
136 	Debug(DebugFail,"Built component %p could not be casted back to type '%s'",c,type.c_str());
137 #ifdef DEBUG
138     else
139 	Debug(DebugAll,"Built component %p type '%s' interface at %p",c,type.c_str(),raw);
140 #endif
141     return raw;
142 }
143 
144 
SignallingComponent(const char * name,const NamedList * params,const char * type)145 SignallingComponent::SignallingComponent(const char* name, const NamedList* params, const char* type)
146     : m_engine(0), m_compType(type)
147 {
148     if (params) {
149 	name = params->getValue(YSTRING("debugname"),name);
150 	m_compType = params->getValue(YSTRING("type"),m_compType);
151 	debugLevel(params->getIntValue(YSTRING("debuglevel"),-1));
152     }
153     DDebug(engine(),DebugAll,"Component '%s' created [%p]",name,this);
154     setName(name);
155 }
156 
setName(const char * name)157 void SignallingComponent::setName(const char* name)
158 {
159     debugName(0);
160     m_name = name;
161     debugName(m_name);
162 }
163 
~SignallingComponent()164 SignallingComponent::~SignallingComponent()
165 {
166     DDebug(engine(),DebugAll,"Component '%s' deleted [%p]",toString().c_str(),this);
167 }
168 
destroyed()169 void SignallingComponent::destroyed()
170 {
171     detach();
172 }
173 
toString() const174 const String& SignallingComponent::toString() const
175 {
176     return m_name;
177 }
178 
initialize(const NamedList * config)179 bool SignallingComponent::initialize(const NamedList* config)
180 {
181     return true;
182 }
183 
resolveConfig(const String & cmpName,NamedList & params,const NamedList * config)184 bool SignallingComponent::resolveConfig(const String& cmpName, NamedList& params, const NamedList* config)
185 {
186     if (!config)
187 	return false;
188     String name = config->getValue(cmpName,params);
189     if (!(name && !name.toBoolean(false)))
190 	return false;
191     static_cast<String&>(params) = name;
192     NamedString* param = config->getParam(params);
193     NamedPointer* ptr = YOBJECT(NamedPointer,param);
194     NamedList* ifConfig = ptr ? YOBJECT(NamedList,ptr->userData()) : 0;
195     if (ifConfig)
196 	params.copyParams(*ifConfig);
197     else {
198 	if (config->hasSubParams(params + "."))
199 	    params.copySubParams(*config,params + ".");
200 	else
201 	    params.addParam("local-config","true");
202     }
203     return true;
204 }
205 
control(NamedList & params)206 bool SignallingComponent::control(NamedList& params)
207 {
208     return false;
209 }
210 
controlCreate(const char * oper)211 NamedList* SignallingComponent::controlCreate(const char* oper)
212 {
213     if (m_name.null())
214 	return 0;
215     NamedList* params = new NamedList("chan.control");
216     params->addParam("component",m_name);
217     if (!TelEngine::null(oper))
218 	params->addParam("operation",oper);
219     return params;
220 }
221 
controlExecute(NamedList * params)222 bool SignallingComponent::controlExecute(NamedList* params)
223 {
224     bool ok = false;
225     if (params) {
226 	ok = control(*params);
227 	TelEngine::destruct(params);
228     }
229     return ok;
230 }
231 
232 
engine(SignallingEngine * eng)233 void SignallingComponent::engine(SignallingEngine* eng)
234 {
235     if (eng == m_engine)
236 	return;
237     if (eng)
238 	eng->insert(this);
239     else
240 	detach();
241 }
242 
insert(SignallingComponent * component)243 void SignallingComponent::insert(SignallingComponent* component)
244 {
245     if (!component)
246 	return;
247     if (m_engine) {
248 	// we have an engine - force the other component in the same
249 	m_engine->insert(component);
250 	return;
251     }
252     if (component->engine())
253 	// insert ourselves in the other's engine
254 	component->engine()->insert(this);
255 }
256 
detach()257 void SignallingComponent::detach()
258 {
259     debugChain();
260     if (m_engine) {
261 	m_engine->remove(this);
262 	m_engine = 0;
263     }
264 }
265 
timerTick(const Time & when)266 void SignallingComponent::timerTick(const Time& when)
267 {
268     XDebug(engine(),DebugAll,"Timer ticked for component '%s' [%p]",
269 	toString().c_str(),this);
270 }
271 
tickSleep(unsigned long usec) const272 unsigned long SignallingComponent::tickSleep(unsigned long usec) const
273 {
274     return m_engine ? m_engine->tickSleep(usec) : 0;
275 }
276 
notify(NamedList & notifs)277 void SignallingNotifier::notify(NamedList& notifs)
278 {
279     DDebug(DebugInfo,"SignallingNotifier::notify() [%p] stub",this);
280 }
281 
cleanup()282 void SignallingNotifier::cleanup()
283 {
284     DDebug(DebugInfo,"SignallingNotifier::cleanup() [%p] stub",this);
285 }
286 
287 
288 static SignallingEngine* s_self = 0;
289 long SignallingEngine::s_maxLockWait = MAX_LOCK_WAIT;
290 
SignallingEngine(const char * name)291 SignallingEngine::SignallingEngine(const char* name)
292     : Mutex(true,"SignallingEngine"),
293       m_thread(0),
294       m_usecSleep(DEF_TICK_SLEEP), m_tickSleep(0)
295 {
296     debugName(name);
297 }
298 
~SignallingEngine()299 SignallingEngine::~SignallingEngine()
300 {
301     if (m_thread) {
302 	Debug(this,DebugCrit,
303 	    "Engine destroyed with worker thread still running [%p]",this);
304 	stop();
305     }
306     lock();
307     if (s_self == this)
308 	s_self = 0;
309     unsigned int n = m_components.count();
310     if (n)
311 	Debug(this,DebugNote,"Cleaning up %u components [%p]",n,this);
312     m_components.clear();
313     unlock();
314 }
315 
self(bool create)316 SignallingEngine* SignallingEngine::self(bool create)
317 {
318     if (create && !s_self) {
319 	// if mutex debugging is in force don't limit the lock time
320 	if (Lockable::wait())
321 	    s_maxLockWait = -1;
322 	s_self = new SignallingEngine;
323     }
324     return s_self;
325 }
326 
find(const String & name)327 SignallingComponent* SignallingEngine::find(const String& name)
328 {
329     Lock mylock(this);
330     return static_cast<SignallingComponent*>(m_components[name]);
331 }
332 
find(const String & name,const String & type,const SignallingComponent * start)333 SignallingComponent* SignallingEngine::find(const String& name, const String& type, const SignallingComponent* start)
334 {
335     XDebug(this,DebugAll,"Engine finding '%s' of type %s from %p [%p]",
336 	name.c_str(),type.c_str(),start,this);
337     Lock mylock(this);
338     ObjList* l = m_components.skipNull();
339     if (start) {
340 	l = m_components.find(start);
341 	if (!l)
342 	    return 0;
343 	l = l->skipNext();
344     }
345     for (; l; l = l->skipNext()) {
346 	SignallingComponent* c = static_cast<SignallingComponent*>(l->get());
347 	if ((name.null() || (c->toString() == name)) &&
348 	    (type.null() || c->getObject(type)))
349 	    return c;
350     }
351     return 0;
352 }
353 
find(const SignallingComponent * component)354 bool SignallingEngine::find(const SignallingComponent* component)
355 {
356     if (!component)
357 	return false;
358     Lock mylock(this);
359     DDebug(this,DebugAll,"Engine finding component @%p [%p]",component,this);
360     return m_components.find(component) != 0;
361 }
362 
build(const String & type,NamedList & params,bool init,bool ref)363 SignallingComponent* SignallingEngine::build(const String& type, NamedList& params, bool init, bool ref)
364 {
365     XDebug(this,DebugAll,"Engine building '%s' of type %s [%p]",
366 	params.c_str(),type.c_str(),this);
367     Lock mylock(this);
368     SignallingComponent* c = find(params,type);
369     if (c && (ref ? c->ref() : c->alive())) {
370 	DDebug(this,DebugAll,"Engine returning existing component '%s' @%p (%d) [%p]",
371 	    c->toString().c_str(),c,c->refcount(),this);
372 	return c;
373     }
374     c = SignallingFactory::build(type,&params);
375     DDebug(this,DebugAll,"Created new component '%s' @%p [%p]",
376 	c->toString().c_str(),c,this);
377     insert(c);
378     if (init && c)
379 	c->initialize(&params);
380     return c;
381 }
382 
insert(SignallingComponent * component)383 void SignallingEngine::insert(SignallingComponent* component)
384 {
385     if (!component)
386 	return;
387     Lock mylock(this);
388     if (component->engine() == this)
389 	return;
390 #ifdef DEBUG
391     const char* dupl = m_components.find(component->toString()) ? " (duplicate)" : "";
392     Debug(this,DebugAll,"Engine inserting component '%s'%s @%p [%p]",
393 	component->toString().c_str(),dupl,component,this);
394 #endif
395     component->detach();
396     component->m_engine = this;
397     component->debugChain(this);
398     m_components.append(component);
399 }
400 
remove(SignallingComponent * component)401 void SignallingEngine::remove(SignallingComponent* component)
402 {
403     if (!component)
404 	return;
405     Lock mylock(this);
406     if (component->engine() != this)
407 	return;
408     DDebug(this,DebugAll,"Engine removing component @%p '%s' [%p]",
409 	component,component->toString().c_str(),this);
410     m_components.remove(component,false);
411     component->m_engine = 0;
412     component->detach();
413 }
414 
remove(const String & name)415 bool SignallingEngine::remove(const String& name)
416 {
417     if (name.null())
418 	return false;
419     Lock mylock(this);
420     SignallingComponent* component = find(name);
421     if (!component)
422 	return false;
423     DDebug(this,DebugAll,"Engine removing component '%s' @%p [%p]",
424 	component->toString().c_str(),component,this);
425     component->m_engine = 0;
426     component->detach();
427     m_components.remove(component);
428     return true;
429 }
430 
notify(SignallingComponent * component,NamedList notifs)431 void SignallingEngine::notify(SignallingComponent* component, NamedList notifs)
432 {
433     if (!(m_notifier && component))
434 	return;
435     Debug(this,DebugAll,"Engine [%p] sending notify from '%s' [%p]",this,component->toString().c_str(),component);
436     m_notifier->notify(notifs);
437 }
438 
control(NamedList & params)439 bool SignallingEngine::control(NamedList& params)
440 {
441     bool ok = false;
442     Lock mylock(this);
443     for (ObjList* l = m_components.skipNull(); l; l = l->skipNext())
444 	ok = static_cast<SignallingComponent*>(l->get())->control(params) || ok;
445     // Do not add operation-status here !!
446     // The handler should return false because the message wasn't processed
447     // by any component
448     return ok;
449 }
450 
start(const char * name,Thread::Priority prio,unsigned long usec)451 bool SignallingEngine::start(const char* name, Thread::Priority prio, unsigned long usec)
452 {
453     Lock mylock(this);
454     if (m_thread)
455 	return m_thread->running();
456     // defaults and sanity checks
457     if (usec == 0)
458 	usec = DEF_TICK_SLEEP;
459     else if (usec < MIN_TICK_SLEEP)
460 	usec = MIN_TICK_SLEEP;
461     else if (usec > MAX_TICK_SLEEP)
462 	usec = MAX_TICK_SLEEP;
463 
464     SignallingThreadPrivate* tmp = new SignallingThreadPrivate(this,name,prio);
465     if (tmp->startup()) {
466 	m_usecSleep = usec;
467 	m_thread = tmp;
468 	DDebug(this,DebugInfo,"Engine started worker thread [%p]",this);
469 	return true;
470     }
471     delete tmp;
472     Debug(this,DebugCrit,"Engine failed to start worker thread [%p]",this);
473     return false;
474 }
475 
stop()476 void SignallingEngine::stop()
477 {
478     // TODO: experimental: remove commented if it's working
479     if (!m_thread)
480 	return;
481     m_thread->cancel(false);
482     while (m_thread)
483 	Thread::yield(true);
484     Debug(this,DebugAll,"Engine stopped worker thread [%p]",this);
485 #if 0
486     lock();
487     SignallingThreadPrivate* tmp = m_thread;
488     m_thread = 0;
489     if (tmp) {
490 	delete tmp;
491 	DDebug(this,DebugInfo,"Engine stopped worker thread [%p]",this);
492     }
493     unlock();
494 #endif
495 }
496 
thread() const497 Thread* SignallingEngine::thread() const
498 {
499     return m_thread;
500 }
501 
tickSleep(unsigned long usec)502 unsigned long SignallingEngine::tickSleep(unsigned long usec)
503 {
504     if (m_tickSleep > usec)
505 	m_tickSleep = usec;
506     return m_tickSleep;
507 }
508 
timerTick(const Time & when)509 unsigned long SignallingEngine::timerTick(const Time& when)
510 {
511     RefPointer<SignallingComponent> c;
512     lock();
513     m_tickSleep = m_usecSleep;
514     ListIterator iter(m_components);
515     while ((c = static_cast<SignallingComponent*>(iter.get()))) {
516 	unlock();
517 	c->timerTick(when);
518 	c = 0;
519 	lock();
520     }
521     unsigned long rval = m_tickSleep;
522     m_tickSleep = m_usecSleep;
523     unlock();
524     return rval;
525 }
526 
maxLockWait(long maxWait)527 void SignallingEngine::maxLockWait(long maxWait)
528 {
529     if (maxWait < 0)
530 	maxWait = -1;
531     else if (maxWait < MIN_TICK_SLEEP)
532 	maxWait = MIN_TICK_SLEEP;
533     s_maxLockWait = maxWait;
534 }
535 
~SignallingThreadPrivate()536 SignallingThreadPrivate::~SignallingThreadPrivate()
537 {
538     if (m_engine)
539 	m_engine->m_thread = 0;
540 }
541 
run()542 void SignallingThreadPrivate::run()
543 {
544     for (;;) {
545 	if (m_engine) {
546 	    Time t;
547 	    unsigned long sleepTime = m_engine->timerTick(t);
548 	    if (sleepTime) {
549 		usleep(sleepTime,true);
550 		continue;
551 	    }
552 	}
553 	yield(true);
554     }
555 }
556 
557 
558 /*
559  * SignallingTimer
560  */
561 // Retrieve a timer interval from a list of parameters
getInterval(const NamedList & params,const char * param,unsigned int minVal,unsigned int defVal,unsigned int maxVal,bool allowDisable)562 unsigned int SignallingTimer::getInterval(const NamedList& params, const char* param,
563     unsigned int minVal, unsigned int defVal, unsigned int maxVal, bool allowDisable)
564 {
565     unsigned int val = (unsigned int)params.getIntValue(param,defVal);
566     if (!val)
567         return allowDisable ? 0 : minVal;
568     if (val < minVal)
569 	return minVal;
570     if (maxVal && val > maxVal)
571 	return maxVal;
572     return val;
573 }
574 
575 
576 /**
577  * SignallingUtils
578  */
579 
580 // Coding standard as defined in Q.931/Q.850
581 static const TokenDict s_dict_codingStandard[] = {
582 	{"CCITT",            0x00},
583 	{"ISO/IEC",          0x01},
584 	{"national",         0x02},
585 	{"network specific", 0x03},
586 	{0,0}
587 	};
588 
589 // Locations as defined in Q.850
590 static const TokenDict s_dict_location[] = {
591 	{"U",    0x00},                  // User
592 	{"LPN",  0x01},                  // Private network serving the local user
593 	{"LN",   0x02},                  // Public network serving the local user
594 	{"TN",   0x03},                  // Transit network
595 	{"RLN",  0x04},                  // Public network serving the remote user
596 	{"RPN",  0x05},                  // Private network serving the remote user
597 	{"INTL", 0x07},                  // International network
598 	{"BI",   0x0a},                  // Network beyond the interworking point
599 	{0,0}
600 	};
601 
602 // Q.850 2.2.5. Cause class: Bits 4-6
603 // Q.850 Table 1. Cause value: Bits 0-6
604 // Defined for CCITT coding standard
605 static const TokenDict s_dict_causeCCITT[] = {
606 	// normal-event class
607 	{"normal-event",                   0x00},
608 	{"unallocated",                    0x01}, // Unallocated (unassigned) number
609 	{"noroute-to-network",             0x02}, // No route to specified transit network
610 	{"noroute",                        0x03}, // No route to destination
611 	{"send-info-tone",                 0x04}, // Send special information tone
612 	{"misdialed-trunk-prefix",         0x05}, // Misdialed trunk prefix
613 	{"channel-unacceptable",           0x06}, // Channel unacceptable
614 	{"call-delivered",                 0x07}, // Call awarded and being delivered in an established channel
615 	{"preemption",                     0x08}, // Preemption
616 	{"preemption-circuit-reserved",    0x09}, // Preemption circuit reserved for re-use
617 	{"ported-number",                  0x0e}, // QoR: ported number Q.850 Addendum 1 (06/2000)
618 	{"excess-digits",                  0x0e}, // Excess digits received, call is proceeding
619 	{"normal-clearing",                0x10}, // Normal Clearing
620 	{"busy",                           0x11}, // User busy
621 	{"noresponse",                     0x12}, // No user responding
622 	{"noanswer",                       0x13}, // No answer from user (user alerted)
623 	{"offline",                        0x14}, // Subscriber absent
624 	{"rejected",                       0x15}, // Call Rejected
625 	{"moved",                          0x16}, // Number changed
626 	{"redirection",                    0x17}, // Redirection to new destination Q.850 05/98
627 	{"rejected-by-feature",            0x18}, // Call rejected due to feature at the destination Q.850 Amendment 1 (07/2001)
628 	{"looping",                        0x19}, // Exchange routing error (hop counter) Q.850 05/98
629 	{"answered",                       0x1a}, // Non-selected user clearing (answered elsewhere)
630 	{"out-of-order",                   0x1b}, // Destination out of order
631 	{"invalid-number",                 0x1c}, // Invalid number format
632 	{"facility-rejected",              0x1d}, // Facility rejected
633 	{"status-enquiry-rsp",             0x1e}, // Response to STATUS ENQUIRY
634 	{"normal",                         0x1f}, // Normal, unspecified
635 	// resource-unavailable class
636 	{"resource-unavailable",           0x20}, // Resource unavailable
637 	{"congestion",                     0x22}, // No circuit/channel available
638 	{"channel-congestion",             0x22},
639 	{"net-out-of-order",               0x26}, // Network out of order
640 	{"frame-mode-conn-down",           0x27}, // Permanent frame mode connection out of service
641 	{"frame-mode-conn-up",             0x28}, // Permanent frame mode connection operational
642 	{"noconn",                         0x29},
643 	{"temporary-failure",              0x29}, // Temporary failure
644 	{"congestion",                     0x2a}, // Switching equipment congestion
645 	{"switch-congestion",              0x2a},
646 	{"access-info-discarded",          0x2b}, // Access information discarded
647 	{"channel-unavailable",            0x2c}, // Requested channel not available
648 	{"preemption-congestion",          0x2e}, // Precedence call blocked
649 	{"noresource",                     0x2f}, // Resource unavailable, unspecified
650 	{"service-unavailable",            0x30}, // Service or option not available
651 	{"qos-unavailable",                0x31}, // Quality of service unavailable
652 	{"facility-not-subscribed",        0x32}, // Requested facility not subscribed
653 	{"forbidden-out",                  0x35}, // Outgoing call barred within CUG
654 	{"forbidden-in",                   0x37}, // Incoming call barred within CUG
655 	{"bearer-cap-not-auth",            0x39}, // Bearer capability not authorized
656 	{"bearer-cap-not-available",       0x3a}, // Bearer capability not presently available
657 	{"nomedia",                        0x3a},
658 	{"invalid-access-info-out",        0x3e}, // Inconsistency in designated outgoing access information and subscriber class
659 	{"service-unavailable",            0x3f}, // Service or option not available
660 	// service-not-implemented class
661 	{"bearer-cap-not-implemented",     0x41}, // Bearer capability not implemented
662 	{"channel-type-not-implemented",   0x42}, // Channel type not implemented
663 	{"facility-not-implemented",       0x45}, // Requested facility not implemented
664 	{"restrict-bearer-cap-avail",      0x46}, // Only restricted digital information bearer capability is available
665 	{"service-not-implemented",        0x4f}, // Service or option not implemented, unspecified
666 	// invalid-message class
667 	{"invalid-callref",                0x51}, // Invalid call reference value
668 	{"unknown-channel",                0x52}, // Identified channel does not exist
669 	{"unknown-callid",                 0x53}, // A suspended call exists, but this call identity does not
670 	{"duplicate-callid",               0x54}, // Call identity in use
671 	{"no-call-suspended",              0x55}, // No call suspended
672 	{"suspended-call-cleared",         0x56}, // Call having the requested call identity has been cleared
673 	{"not-subscribed",                 0x57}, // User not member of CUG
674 	{"incompatible-dest",              0x58}, // Incompatible destination
675 	{"unknown-group",                  0x5a}, // Non-existent CUG
676 	{"invalid-transit-net",            0x5b}, // Invalid transit network selection
677 	{"invalid-message",                0x5f}, // Invalid message, unspecified
678 	// protocol-error class
679 	{"missing-mandatory-ie",           0x60}, // Mandatory information element is missing
680 	{"unknown-message",                0x61}, // Message type non-existent or not implemented
681 	{"wrong-message",                  0x62}, // Message not compatible with call state, non-existent or not implemented
682 	{"unknown-ie",                     0x63}, // Information element non-existent or not implemented
683 	{"invalid-ie",                     0x64}, // Invalid information element contents
684 	{"wrong-state-message",            0x65}, // Message not compatible with call state
685 	{"timeout",                        0x66}, // Recovery on timer expiry
686 	{"unknown-param-passed-on",        0x67}, // Parameter non-existent or not implemented, passed on
687 	{"unknown-param-message-droppped", 0x6e}, // Message with unrecognized parameter, discarded
688 	{"protocol-error",                 0x6f}, // Protocol error, unspecified
689 	// interworking class
690 	{"interworking",                   0x7f}, // Interworking, unspecified
691 	{0,0}
692 	};
693 
694 // Q.931 4.5.5. Information transfer capability: Bits 0-4
695 // Defined for CCITT coding standard
696 static const TokenDict s_dict_transferCapCCITT[] = {
697 	{"speech",       0x00},          // Speech
698 	{"udi",          0x08},          // Unrestricted digital information
699 	{"rdi",          0x09},          // Restricted digital information
700 	{"3.1khz-audio", 0x10},          // 3.1 khz audio
701 	{"udi-ta",       0x11},          // Unrestricted digital information with tone/announcements
702 	{"video",        0x18},          // Video
703 	{0,0}
704 	};
705 
706 // Q.931 4.5.5. Transfer mode: Bits 5,6
707 // Defined for CCITT coding standard
708 static const TokenDict s_dict_transferModeCCITT[] = {
709 	{"circuit",      0x00},          // Circuit switch mode
710 	{"packet",       0x02},          // Packet mode
711 	{0,0}
712 	};
713 
714 // Q.931 4.5.5. Transfer rate: Bits 0-4
715 // Defined for CCITT coding standard
716 static const TokenDict s_dict_transferRateCCITT[] = {
717 	{"packet",        0x00},         // Packet mode only
718 	{"64kbit",        0x10},         // 64 kbit/s
719 	{"2x64kbit",      0x11},         // 2x64 kbit/s
720 	{"384kbit",       0x13},         // 384 kbit/s
721 	{"1536kbit",      0x15},         // 1536 kbit/s
722 	{"1920kbit",      0x17},         // 1920 kbit/s
723 	{"multirate",     0x18},         // Multirate (64 kbit/s base rate)
724 	{0,0}
725 	};
726 
727 // Q.931 4.5.5. User information Layer 1 protocol: Bits 0-4
728 // Defined for CCITT coding standard
729 static const TokenDict s_dict_formatCCITT[] = {
730 	{"v110",          0x01},         // Recomendation V.110 and X.30
731 	{"mulaw",         0x02},         // Recomendation G.711 mu-law
732 	{"alaw",          0x03},         // Recomendation G.711 A-law
733 	{"g721",          0x04},         // Recomendation G.721 32kbit/s ADPCM and I.460
734 	{"h221",          0x05},         // Recomendation H.221 and H.242
735 	{"h223",          0x06},         // Recomendation H.223 and H.245 videoconference
736 	{"non-CCITT",     0x07},         // Non CCITT standardized rate adaption
737 	{"v120",          0x08},         // Recomendation V.120
738 	{"x31",           0x09},         // Recomendation X.31 HDLC flag stuffing
739 	{0,0}
740 	};
741 
742 const TokenDict* SignallingUtils::s_dictCCITT[5] = {
743 	s_dict_causeCCITT,
744 	s_dict_formatCCITT,
745 	s_dict_transferCapCCITT,
746 	s_dict_transferModeCCITT,
747 	s_dict_transferRateCCITT
748 	};
749 
750 // Check if a comma separated list of flags has a given flag
hasFlag(const String & flags,const char * flag)751 bool SignallingUtils::hasFlag(const String& flags, const char* flag)
752 {
753     ObjList* obj = flags.split(',',false);
754     bool found = (obj->find(flag) != 0);
755     TelEngine::destruct(obj);
756     return found;
757 }
758 
759 // Append a flag to a comma separated list of flags
appendFlag(String & flags,const char * flag)760 bool SignallingUtils::appendFlag(String& flags, const char* flag)
761 {
762     if (TelEngine::null(flag) || hasFlag(flags,flag))
763 	return false;
764     flags.append(flag,",");
765     return true;
766 }
767 
768 // Remove a flag from a comma separated list of flags
removeFlag(String & flags,const char * flag)769 bool SignallingUtils::removeFlag(String& flags, const char* flag)
770 {
771     ObjList* obj = flags.split(',',false);
772     ObjList* found = obj->find(flag);
773     if (found) {
774 	obj->remove(found,true);
775 	flags = "";
776 	for (ObjList* o = obj->skipNull(); o; o = o->skipNext())
777 	    flags.append(*static_cast<String*>(o->get()),",");
778     }
779     TelEngine::destruct(obj);
780     return (found != 0);
781 }
782 
783 // Check if a list's parameter (comma separated list of flags) has a given flag
hasFlag(const NamedList & list,const char * param,const char * flag)784 bool SignallingUtils::hasFlag(const NamedList& list, const char* param, const char* flag)
785 {
786     const String* s = list.getParam(param);
787     return s && hasFlag(*s,flag);
788 }
789 
790 // Append a flag to a list parameter, create parameter if missing
appendFlag(NamedList & list,const char * param,const char * flag)791 bool SignallingUtils::appendFlag(NamedList& list, const char* param, const char* flag)
792 {
793     String* s = list.getParam(param);
794     if (s)
795 	return appendFlag(*s,flag);
796     list.addParam(param,flag);
797     return true;
798 }
799 
800 // Add string (keyword) if found or integer parameter to a named list
addKeyword(NamedList & list,const char * param,const TokenDict * tokens,unsigned int val)801 void SignallingUtils::addKeyword(NamedList& list, const char* param, const TokenDict* tokens, unsigned int val)
802 {
803     const char* value = lookup(val,tokens);
804     if (value)
805 	list.addParam(param,value);
806     else
807 	list.addParam(param,String(val));
808 }
809 
810 // Dump a buffer to a list of parameters
dumpData(const SignallingComponent * comp,NamedList & list,const char * param,const unsigned char * buf,unsigned int len,char sep)811 void SignallingUtils::dumpData(const SignallingComponent* comp, NamedList& list, const char* param,
812 	const unsigned char* buf, unsigned int len, char sep)
813 {
814     String raw;
815     raw.hexify((void*)buf,len,sep);
816     list.addParam(param,raw);
817     DDebug(comp,DebugAll,"Utils::dumpData dumped %s='%s'",param,raw.safe());
818 }
819 
820 // Dump data from a buffer to a list of parameters. The buffer is parsed until (and including)
821 //  the first byte with the extension bit (the most significant one) set
dumpDataExt(const SignallingComponent * comp,NamedList & list,const char * param,const unsigned char * buf,unsigned int len,char sep)822 unsigned int SignallingUtils::dumpDataExt(const SignallingComponent* comp, NamedList& list, const char* param,
823 	const unsigned char* buf, unsigned int len, char sep)
824 {
825     if (!(buf && len))
826 	return 0;
827     unsigned int count = 0;
828     for (; count < len && !(buf[count] & 0x80); count++) ;
829     if (count == len) {
830 	Debug(comp,DebugMild,"Utils::dumpDataExt invalid ext bits for %s (len=%u)",param,len);
831 	return 0;
832     }
833     count++;
834     dumpData(comp,list,param,buf,count,sep);
835     return count;
836 }
837 
838 // Decode a received buffer to a comma separated list of flags
decodeFlags(const SignallingComponent * comp,NamedList & list,const char * param,const SignallingFlags * flags,const unsigned char * buf,unsigned int len)839 bool SignallingUtils::decodeFlags(const SignallingComponent* comp, NamedList& list, const char* param,
840 	const SignallingFlags* flags, const unsigned char* buf, unsigned int len)
841 {
842     if (!(flags && buf && len <= sizeof(unsigned int)))
843 	return false;
844     unsigned int val = 0;
845     int shift = 0;
846     while (len--) {
847 	val |= ((unsigned int)*buf++) << shift;
848 	shift += 8;
849     }
850     String tmp;
851     for (; flags->mask; flags++)
852 	if ((val & flags->mask) == flags->value)
853 	    tmp.append(flags->name,",");
854     DDebug(comp,DebugAll,"Utils::decodeFlags. Decoded %s='%s' from %u",param,tmp.safe(),val);
855     list.addParam(param,tmp);
856     return true;
857 }
858 
codings()859 const TokenDict* SignallingUtils::codings()
860 {
861     return s_dict_codingStandard;
862 }
863 
locations()864 const TokenDict* SignallingUtils::locations()
865 {
866     return s_dict_location;
867 }
868 
869 #define Q850_MAX_CAUSE 32
870 
871 // Q.850 2.1
decodeCause(const SignallingComponent * comp,NamedList & list,const unsigned char * buf,unsigned int len,const char * prefix,bool isup)872 bool SignallingUtils::decodeCause(const SignallingComponent* comp, NamedList& list,
873 	const unsigned char* buf, unsigned int len, const char* prefix, bool isup)
874 {
875     if (!buf)
876 	return false;
877     if (len < 2) {
878 	Debug(comp,DebugNote,"Utils::decodeCause. Invalid length %u",len);
879 	return false;
880     }
881     String causeName = prefix;
882     // Byte 0: Coding standard (bit 5,6), location (bit 0-3)
883     unsigned char coding = (buf[0] & 0x60) >> 5;
884     addKeyword(list,causeName + ".coding",codings(),coding);
885     addKeyword(list,causeName + ".location",locations(),buf[0] & 0x0f);
886     unsigned int crt = 1;
887     // If bit 7 is 0, the next byte should contain the recomendation
888     unsigned char rec = 0;
889     if (!(buf[0] & 0x80)) {
890 	rec = buf[1] & 0x7f;
891 	// For ISUP there shouldn't be a recomendation byte
892 	if (isup)
893 	    Debug(comp,DebugMild,"Utils::decodeCause. Found recomendation %u for ISUP cause",rec);
894 	crt = 2;
895     }
896     if (rec)
897 	list.addParam(causeName + ".rec",String(rec));
898     if (crt >= len) {
899 	Debug(comp,DebugMild,"Utils::decodeCause. Invalid length %u. Cause value is missing",len);
900 	list.addParam(causeName,"");
901 	return false;
902     }
903     // Current byte: bits 0..6: cause, bits 5,6: cause class
904     addKeyword(list,causeName,dict(0,coding),buf[crt] & 0x7f);
905     // Rest of data: diagnostic
906     crt++;
907     if (crt < len)
908 	dumpData(comp,list,causeName + ".diagnostic",buf + crt,len - crt);
909     return true;
910 }
911 
912 // Decode bearer capabilities as defined in Q.931 (Bearer Capabilities) and Q.763 (User Service Information)
913 // Q.931 - 4.5.5 / Q.763 - 3.57
914 // The given sections in comments are from Q.931
decodeCaps(const SignallingComponent * comp,NamedList & list,const unsigned char * buf,unsigned int len,const char * prefix,bool isup)915 bool SignallingUtils::decodeCaps(const SignallingComponent* comp, NamedList& list, const unsigned char* buf,
916 	unsigned int len, const char* prefix, bool isup)
917 {
918     if (!buf)
919 	return false;
920     if (len < 2) {
921 	Debug(comp,DebugMild,"Utils::decodeCaps. Invalid length %u",len);
922 	return false;
923     }
924     String capsName = prefix;
925     // Byte 0: Coding standard (bit 5,6), Information transfer capability (bit 0-4)
926     // Byte 1: Transfer mode (bit 5,6), Transfer rate (bit 0-4)
927     unsigned char coding = (buf[0] & 0x60) >> 5;
928     addKeyword(list,capsName + ".coding",codings(),coding);
929     addKeyword(list,capsName + ".transfercap",dict(2,coding),buf[0] & 0x1f);
930     addKeyword(list,capsName + ".transfermode",dict(3,coding),(buf[1] & 0x60) >> 5);
931     u_int8_t rate = buf[1] & 0x1f;
932     addKeyword(list,capsName + ".transferrate",dict(4,coding),rate);
933     // Figure 4.11 Note 1: Next byte is the rate multiplier if the transfer rate is 'multirate' (0x18)
934     u_int8_t crt = 2;
935     if (rate == 0x18) {
936 	if (len < 3) {
937 	    Debug(comp,DebugMild,"Utils::decodeCaps. Invalid length %u. No rate multiplier",len);
938 	    return false;
939 	}
940 	addKeyword(list,capsName + ".multiplier",0,buf[2] & 0x7f);
941 	crt = 3;
942     }
943     // Get optional extra information
944     // Layer 1 data
945     if (len <= crt)
946 	return true;
947     u_int8_t ident = (buf[crt] & 0x60) >> 5;
948     if (ident != 1) {
949 	Debug(comp,DebugNote,"Utils::decodeCaps. Invalid layer 1 ident %u",ident);
950 	return true;
951     }
952     addKeyword(list,capsName,dict(1,coding),buf[crt] & 0x1f);
953     //TODO: Decode the rest of Layer 1, Layer 2 and Layer 3 data
954     return true;
955 }
956 
957 // Encode a comma separated list of flags. Flags can be prefixed with the '-'
958 //  character to be reset if previously set
encodeFlags(const SignallingComponent * comp,int & dest,const String & flags,const TokenDict * dict)959 void SignallingUtils::encodeFlags(const SignallingComponent* comp,
960 	int& dest, const String& flags, const TokenDict* dict)
961 {
962     if (flags.null() || !dict)
963 	return;
964     ObjList* list = flags.split(',',false);
965     DDebug(comp,DebugAll,"Utils::encodeFlags '%s' dest=0x%x",flags.c_str(),dest);
966     for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
967 	String* s = static_cast<String*>(o->get());
968 	bool set = !s->startSkip("-",false);
969 	const TokenDict* p = dict;
970 	for (; p->token && *s != p->token; p++) ;
971 	if (!p->token) {
972 	    DDebug(comp,DebugAll,"Utils::encodeFlags '%s' not found",s->c_str());
973 	    continue;
974 	}
975 	DDebug(comp,DebugAll,"Utils::encodeFlags %sset %s=0x%x",
976 	    set?"":"re",p->token,p->value);
977 	if (set)
978 	    dest |= p->value;
979 	else
980 	    dest &= ~p->value;
981     }
982     TelEngine::destruct(list);
983 }
984 
985 // Encode a comma separated list of signalling flags
encodeFlags(const SignallingComponent * comp,const String & flags,const SignallingFlags * dict,const char * paramName)986 unsigned int SignallingUtils::encodeFlags(const SignallingComponent* comp, const String& flags,
987     const SignallingFlags* dict, const char* paramName)
988 {
989     if (!dict)
990 	return 0;
991     unsigned int v = 0;
992     ObjList* l = flags.split(',',false);
993     for (ObjList* o = l->skipNull(); o; o = o->skipNext()) {
994 	const String* s = static_cast<const String*>(o->get());
995 	for (const SignallingFlags* d = dict; d->mask; d++) {
996 	    if (*s == d->name) {
997 		if (v & d->mask) {
998 		    Debug(comp,DebugMild,"Flag %s. %s overwriting bits 0x%x",
999 			paramName,d->name,v & d->mask);
1000 		    v &= d->mask;
1001 		}
1002 		v |= d->value;
1003 	    }
1004 	}
1005     }
1006     TelEngine::destruct(l);
1007     return v;
1008 }
1009 
1010 // Q.850 2.1
encodeCause(const SignallingComponent * comp,DataBlock & buf,const NamedList & params,const char * prefix,bool isup,bool fail)1011 bool SignallingUtils::encodeCause(const SignallingComponent* comp, DataBlock& buf,
1012 	const NamedList& params, const char* prefix, bool isup, bool fail)
1013 {
1014     u_int8_t data[4] = {2,0x80,0x80,0x80};
1015     String causeName = prefix;
1016     // Coding standard (0: CCITT) + location. If no location, set it to 0x0a: "BI"
1017     unsigned char coding = fixValue(params,causeName + ".coding",codings(),0x03,0x60,5);
1018     unsigned char loc = (unsigned char)params.getIntValue(causeName + ".location",locations(),0x0a);
1019     data[1] |= (coding << 5) | (loc & 0x0f);
1020     // Recommendation (only for Q.931)
1021     if (!isup) {
1022 	unsigned char rec = (unsigned char)params.getIntValue(causeName + ".rec",0,0);
1023 	// Add recommendation. Clear bit 7 of the first byte
1024 	data[1] &= 0x7f;
1025 	data[2] |= (rec & 0x7f);
1026 	data[0] = 3;
1027     }
1028     // Value. Set to normal-clearing if missing for CCITT encoding or
1029     //  to 0 for other encoding standards
1030     unsigned char val = (unsigned char)params.getIntValue(causeName,dict(0,coding),!coding ? 0x10 : 0);
1031     data[data[0]] |= (val & 0x7f);
1032     // Diagnostic
1033     DataBlock diagnostic;
1034     const char* tmp = params.getValue(causeName + ".diagnostic");
1035     if (tmp)
1036 	diagnostic.unHexify(tmp,strlen(tmp),' ');
1037     // Set data
1038     if (!isup && diagnostic.length() + data[0] + 1 > 32) {
1039 	Debug(comp,fail?DebugNote:DebugMild,
1040 	    "Utils::encodeCause. Cause length %u > 32. %s",
1041 	    diagnostic.length() + data[0] + 1,fail?"Fail":"Skipping diagnostic");
1042 	if (fail)
1043 	    return false;
1044 	diagnostic.clear();
1045     }
1046     u_int8_t len = data[0] + 1;
1047     data[0] += diagnostic.length();
1048     buf.assign(data,len);
1049     buf += diagnostic;
1050     return true;
1051 }
1052 
encodeCaps(const SignallingComponent * comp,DataBlock & buf,const NamedList & params,const char * prefix,bool isup)1053 bool SignallingUtils::encodeCaps(const SignallingComponent* comp, DataBlock& buf, const NamedList& params,
1054 	const char* prefix, bool isup)
1055 {
1056     u_int8_t data[5] = {2,0x80,0x80,0x80,0x80};
1057     String capsName = prefix;
1058     // Byte 1: Coding standard (bit 5,6), Information transfer capability (bit 0-4)
1059     // Byte 2: Transfer mode (bit 5,6), Transfer rate (bit 0-4)
1060     unsigned char coding = fixValue(params,capsName + ".coding",codings(),0x03,0x60,5);
1061     unsigned char cap = (unsigned char)params.getIntValue(capsName + ".transfercap",dict(2,coding),0);
1062     unsigned char mode = fixValue(params,capsName + ".transfermode",dict(3,coding),0x03,0x60,5);
1063     unsigned char rate = (unsigned char)params.getIntValue(capsName + ".transferrate",dict(4,coding),0x10);
1064     data[1] |= (coding << 5) | (cap & 0x1f);
1065     data[2] |= (mode << 5) | (rate & 0x1f);
1066     if (rate == 0x18) {
1067 	data[0] = 3;
1068 	rate = (unsigned char)params.getIntValue(capsName + ".multiplier",0,0);
1069 	data[3] |= rate & 0x7f;
1070     }
1071     // User information layer data
1072     // Bit 7 = 1, Bits 5,6 = layer (1), Bits 0-4: the value
1073     int format = params.getIntValue(capsName,dict(1,coding),-1);
1074     if (format != -1) {
1075 	data[data[0] + 1] |= 0x20 | (((unsigned char)format) & 0x1f);
1076 	data[0]++;
1077     }
1078     buf.assign(data,data[0] + 1);
1079     return true;
1080 }
1081 
1082 // Parse a list of integers or integer intervals. Source elements must be separated by a
1083 //   '.' or ',' character. Integer intervals must be separated by a '-' character.
1084 // Empty elements are silently discarded
parseUIntArray(const String & source,unsigned int min,unsigned int max,unsigned int & count,bool discardDup)1085 unsigned int* SignallingUtils::parseUIntArray(const String& source,
1086 	unsigned int min, unsigned int max,
1087 	unsigned int& count, bool discardDup)
1088 {
1089     count = 0;
1090     ObjList* list = source.split(((-1!=source.find(','))?',':'.'),false);
1091     if (!list->count()) {
1092 	TelEngine::destruct(list);
1093 	return 0;
1094     }
1095 
1096     unsigned int maxArray = 0;
1097     unsigned int* array = 0;
1098     bool ok = true;
1099     int first, last;
1100 
1101     for (ObjList* o = list->skipNull(); o; o = o->skipNext()) {
1102 	String* s = static_cast<String*>(o->get());
1103 	// Get the interval (may be a single value)
1104 	int sep = s->find('-');
1105 	if (sep == -1)
1106 	    first = last = s->toInteger(-1);
1107 	else {
1108 	    first = s->substr(0,sep).toInteger(-1);
1109 	    last = s->substr(sep + 1).toInteger(-2);
1110 	}
1111 	if (first < 0 || last < 0 || last < first) {
1112 	    ok = false;
1113 	    break;
1114 	}
1115 	// Resize and copy array if not enough room
1116 	unsigned int len = (unsigned int)(last - first + 1);
1117 	if (count + len > maxArray) {
1118 	    maxArray = count + len;
1119 	    unsigned int* tmp = new unsigned int[maxArray];
1120 	    if (array) {
1121 		::memcpy(tmp,array,sizeof(unsigned int) * count);
1122 		delete[] array;
1123 	    }
1124 	    array = tmp;
1125 	}
1126 	// Add to array code list
1127 	for (; first <= last; first++) {
1128 	    // Check interval
1129 	    if ((unsigned int)first < min || max < (unsigned int)first) {
1130 		ok = false;
1131 		break;
1132 	    }
1133 	    // Check duplicates
1134 	    if (discardDup) {
1135 		bool dup = false;
1136 		for (unsigned int i = 0; i < count; i++)
1137 		    if (array[i] == (unsigned int)first) {
1138 			dup = true;
1139 			break;
1140 		    }
1141 		if (dup)
1142 		    continue;
1143 	    }
1144 	    array[count++] = first;
1145 	}
1146 	if (!ok)
1147 	    break;
1148     }
1149     TelEngine::destruct(list);
1150 
1151     if (ok && count)
1152 	return array;
1153     count = 0;
1154     if (array)
1155 	delete[] array;
1156     return 0;
1157 }
1158 
1159 
1160 /*
1161  * SignallingMessageTimerList
1162  */
1163 // Add a pending operation to the list. Start its timer
add(SignallingMessageTimer * m,const Time & when)1164 SignallingMessageTimer* SignallingMessageTimerList::add(SignallingMessageTimer* m,
1165     const Time& when)
1166 {
1167     if (!m)
1168 	return 0;
1169     m->stop();
1170     m->start(when.msec());
1171     if (m->global().interval() && !m->global().started())
1172 	m->global().start(when.msec());
1173     ObjList* ins = skipNull();
1174     for (; ins; ins = ins->skipNext()) {
1175 	SignallingMessageTimer* crt = static_cast<SignallingMessageTimer*>(ins->get());
1176 	if (m->fireTime() < crt->fireTime())
1177 	    break;
1178     }
1179     if (!ins)
1180 	append(m);
1181     else
1182 	ins->insert(m);
1183     return m;
1184 }
1185 
1186 // Check if the first operation timed out
timeout(const Time & when)1187 SignallingMessageTimer* SignallingMessageTimerList::timeout(const Time& when)
1188 {
1189     ObjList* o = skipNull();
1190     if (!o)
1191 	return 0;
1192     SignallingMessageTimer* m = static_cast<SignallingMessageTimer*>(o->get());
1193     if (!(m->timeout(when.msec()) || m->global().timeout(when.msec())))
1194 	return 0;
1195     o->remove(false);
1196     return m;
1197 }
1198 
1199 /* vi: set ts=8 sw=4 sts=4 noet: */
1200