1 /*
2 * manager.cxx
3 *
4 * Media channels abstraction
5 *
6 * Open Phone Abstraction Library (OPAL)
7 * Formally known as the Open H323 project.
8 *
9 * Copyright (c) 2001 Equivalence Pty. Ltd.
10 *
11 * The contents of this file are subject to the Mozilla Public License
12 * Version 1.0 (the "License"); you may not use this file except in
13 * compliance with the License. You may obtain a copy of the License at
14 * http://www.mozilla.org/MPL/
15 *
16 * Software distributed under the License is distributed on an "AS IS"
17 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
18 * the License for the specific language governing rights and limitations
19 * under the License.
20 *
21 * The Original Code is Open Phone Abstraction Library.
22 *
23 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 *
25 * Contributor(s): ______________________________________.
26 *
27 * $Revision: 27984 $
28 * $Author: rjongbloed $
29 * $Date: 2012-07-10 03:24:24 -0500 (Tue, 10 Jul 2012) $
30 */
31
32 #include <ptlib.h>
33
34 #ifdef __GNUC__
35 #pragma implementation "manager.h"
36 #endif
37
38 #include <opal/buildopts.h>
39
40 #include <opal/manager.h>
41 #include <opal/endpoint.h>
42 #include <opal/call.h>
43 #include <opal/patch.h>
44 #include <opal/mediastrm.h>
45 #include <codec/g711codec.h>
46 #include <codec/vidcodec.h>
47 #include <codec/rfc4175.h>
48 #include <codec/rfc2435.h>
49 #include <codec/opalpluginmgr.h>
50
51 #if OPAL_HAS_H224
52 #include <h224/h224.h>
53 #endif
54
55 #include <ptclib/random.h>
56 #include <ptclib/url.h>
57
58 #include "../../version.h"
59 #include "../../revision.h"
60
61
62 static const char * const DefaultMediaFormatOrder[] = {
63 OPAL_G7222,
64 OPAL_G7221,
65 OPAL_G722,
66 OPAL_GSMAMR,
67 OPAL_G7231_6k3,
68 OPAL_G729B,
69 OPAL_G729AB,
70 OPAL_G729,
71 OPAL_G729A,
72 OPAL_iLBC,
73 OPAL_GSM0610,
74 OPAL_G728,
75 OPAL_G726_40K,
76 OPAL_G726_32K,
77 OPAL_G726_24K,
78 OPAL_G726_16K,
79 OPAL_G711_ULAW_64K,
80 OPAL_G711_ALAW_64K,
81 OPAL_H264, // H.323 version
82 OPAL_H264_MODE1, // SIP version, packetisation mode 1
83 OPAL_H264_MODE0, // SIP version, packetisation mode 0
84 OPAL_MPEG4,
85 OPAL_H263 "*",
86 OPAL_H261
87 };
88
89 // G.711 is *always* available
90 // Yes, it would make more sense for this to be in g711codec.cxx, but on
91 // Linux it would not get loaded due to static initialisation optimisation
92 OPAL_REGISTER_G711();
93
94 // Same deal for RC4175 video
95 #if OPAL_RFC4175
96 OPAL_REGISTER_RFC4175();
97 #endif
98
99 // Same deal for RC2435 video
100 #if OPAL_RFC2435
101 OPAL_REGISTER_RFC2435_JPEG();
102 #endif
103
104
105 #define new PNEW
106
107
108 /////////////////////////////////////////////////////////////////////////////
109
OpalGetVersion()110 PString OpalGetVersion()
111 {
112 #define AlphaCode "alpha"
113 #define BetaCode "beta"
114 #define ReleaseCode "."
115
116 return psprintf("%u.%u%s%u (svn:%u)", MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER, SVN_REVISION);
117 }
118
119
OpalGetMajorVersion()120 unsigned OpalGetMajorVersion()
121 {
122 return MAJOR_VERSION;
123 }
124
OpalGetMinorVersion()125 unsigned OpalGetMinorVersion()
126 {
127 return MINOR_VERSION;
128 }
129
OpalGetBuildNumber()130 unsigned OpalGetBuildNumber()
131 {
132 return BUILD_NUMBER;
133 }
134
135
OpalProductInfo()136 OpalProductInfo::OpalProductInfo()
137 : t35CountryCode(0)
138 , t35Extension(0)
139 , manufacturerCode(0)
140 {
141 }
142
143
OpalProductInfo(bool)144 OpalProductInfo::OpalProductInfo(bool)
145 : vendor(PProcess::Current().GetManufacturer())
146 , name(PProcess::Current().GetName())
147 , version(PProcess::Current().GetVersion())
148 , t35CountryCode(9) // Country code for Australia
149 , t35Extension(0) // No extension code for Australia
150 , manufacturerCode(61) // Allocated by Australian Communications Authority, Oct 2000;
151 {
152 // Sanitise the product name to be compatible with SIP User-Agent rules
153 name.Replace(' ', '-', true);
154 PINDEX pos;
155 while ((pos = name.FindSpan("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.!%*_+`'~")) != P_MAX_INDEX)
156 name.Delete(pos, 1);
157 }
158
159
Default()160 OpalProductInfo & OpalProductInfo::Default()
161 {
162 static OpalProductInfo instance(true);
163 return instance;
164 }
165
166
AsString() const167 PCaselessString OpalProductInfo::AsString() const
168 {
169 PStringStream str;
170 str << *this;
171 return str;
172 }
173
174
operator <<(ostream & strm,const OpalProductInfo & info)175 ostream & operator<<(ostream & strm, const OpalProductInfo & info)
176 {
177 if (info.name.IsEmpty() &&
178 info.version.IsEmpty() &&
179 info.vendor.IsEmpty() &&
180 info.t35CountryCode == 0 &&
181 info.manufacturerCode == 0)
182 return strm;
183
184 strm << info.name << '\t' << info.version << '\t';
185
186 if (info.t35CountryCode != 0 && info.manufacturerCode != 0) {
187 strm << (unsigned)info.t35CountryCode;
188 if (info.t35Extension != 0)
189 strm << '.' << (unsigned)info.t35Extension;
190 strm << '/' << info.manufacturerCode;
191 }
192
193 strm << '\t' << info.vendor;
194
195 return strm;
196 }
197
198
199 /////////////////////////////////////////////////////////////////////////////
200
201 #ifdef _MSC_VER
202 #pragma warning(disable:4355)
203 #endif
204
OpalManager()205 OpalManager::OpalManager()
206 : productInfo(OpalProductInfo::Default())
207 , defaultUserName(PProcess::Current().GetUserName())
208 , defaultDisplayName(defaultUserName)
209 , m_defaultMediaTypeOfService(0xb8) // New DiffServ value for Expidited Forwarding as per RFC3246
210 , rtpPayloadSizeMax(1400) // RFC879 recommends 576 bytes, but that is ancient history, 99.999% of the time 1400+ bytes is used.
211 , rtpPacketSizeMax(2048)
212 , minAudioJitterDelay(50) // milliseconds
213 , maxAudioJitterDelay(250) // milliseconds
214 , mediaFormatOrder(PARRAYSIZE(DefaultMediaFormatOrder), DefaultMediaFormatOrder)
215 , disableDetectInBandDTMF(false)
216 , noMediaTimeout(0, 0, 5) // Minutes
217 , translationAddress(0) // Invalid address to disable
218 , stun(NULL)
219 , interfaceMonitor(NULL)
220 , activeCalls(*this)
221 #ifdef OPAL_ZRTP
222 , zrtpEnabled(false)
223 #endif
224 , garbageCollectSkip(false)
225 #ifdef OPAL_HAS_IM
226 , m_imManager(NULL)
227 #endif
228 {
229 rtpIpPorts.current = rtpIpPorts.base = 5000;
230 rtpIpPorts.max = 5999;
231
232 // use dynamic port allocation by default
233 tcpPorts.current = tcpPorts.base = tcpPorts.max = 0;
234 udpPorts.current = udpPorts.base = udpPorts.max = 0;
235
236 #if OPAL_VIDEO
237 PStringArray devices = PVideoInputDevice::GetDriversDeviceNames("*"); // Get all devices on all drivers
238 PINDEX i;
239 for (i = 0; i < devices.GetSize(); ++i) {
240 if ((devices[i] *= "*.yuv") || (devices[i] *= "fake"))
241 continue;
242 videoInputDevice.deviceName = devices[i];
243 break;
244 }
245 SetAutoStartTransmitVideo(!videoInputDevice.deviceName.IsEmpty());
246
247 devices = PVideoOutputDevice::GetDriversDeviceNames("*"); // Get all devices on all drivers
248 for (i = 0; i < devices.GetSize(); ++i) {
249 if ((devices[i] *= "*.yuv") || (devices[i] *= "null"))
250 continue;
251 videoOutputDevice.deviceName = devices[i];
252 videoPreviewDevice = videoOutputDevice;
253 break;
254 }
255 SetAutoStartReceiveVideo(!videoOutputDevice.deviceName.IsEmpty());
256 #endif
257
258 m_imManager = new OpalIMManager(*this);
259
260 garbageCollector = PThread::Create(PCREATE_NOTIFIER(GarbageMain), "Opal Garbage");
261
262 PTRACE(4, "OpalMan\tCreated manager.");
263 }
264
265 #ifdef _MSC_VER
266 #pragma warning(default:4355)
267 #endif
268
269
~OpalManager()270 OpalManager::~OpalManager()
271 {
272 ShutDownEndpoints();
273
274 // Shut down the cleaner thread
275 garbageCollectExit.Signal();
276 garbageCollector->WaitForTermination();
277
278 // Clean up any calls that the cleaner thread missed on the way out
279 GarbageCollection();
280
281 delete garbageCollector;
282
283 delete stun;
284 delete interfaceMonitor;
285 delete m_imManager;
286
287 PTRACE(4, "OpalMan\tDeleted manager.");
288 }
289
290
GetEndPoints() const291 PList<OpalEndPoint> OpalManager::GetEndPoints() const
292 {
293 PList<OpalEndPoint> list;
294 list.AllowDeleteObjects(false);
295
296 PReadWaitAndSignal mutex(endpointsMutex);
297
298 for (PList<OpalEndPoint>::const_iterator it = endpointList.begin(); it != endpointList.end(); ++it)
299 list.Append((OpalEndPoint *)&*it);
300
301 return list;
302 }
303
304
ShutDownEndpoints()305 void OpalManager::ShutDownEndpoints()
306 {
307 PTRACE(4, "OpalMan\tShutting down endpoints.");
308
309 // Clear any pending calls, set flag so no calls can be received before endpoints removed
310 InternalClearAllCalls(OpalConnection::EndedByLocalUser, true, m_clearingAllCallsCount++ == 0);
311
312 // Remove (and unsubscribe) all the presentities
313 m_presentities.RemoveAll();
314 m_presentities.DeleteObjectsToBeRemoved();
315
316 // Deregister the endpoints
317 endpointsMutex.StartRead();
318 for (PList<OpalEndPoint>::iterator ep = endpointList.begin(); ep != endpointList.end(); ++ep)
319 ep->ShutDown();
320 endpointsMutex.EndRead();
321
322 endpointsMutex.StartWrite();
323 endpointMap.clear();
324 endpointList.RemoveAll();
325 endpointsMutex.EndWrite();
326
327 --m_clearingAllCallsCount; // Allow for endpoints to be added again.
328 }
329
330
AttachEndPoint(OpalEndPoint * endpoint,const PString & prefix)331 void OpalManager::AttachEndPoint(OpalEndPoint * endpoint, const PString & prefix)
332 {
333 if (PAssertNULL(endpoint) == NULL)
334 return;
335
336 PString thePrefix = prefix.IsEmpty() ? endpoint->GetPrefixName() : prefix;
337
338 PWriteWaitAndSignal mutex(endpointsMutex);
339
340 if (endpointMap.find(thePrefix) != endpointMap.end()) {
341 PTRACE(1, "OpalMan\tCannot re-attach endpoint prefix " << thePrefix);
342 return;
343 }
344
345 if (endpointList.GetObjectsIndex(endpoint) == P_MAX_INDEX)
346 endpointList.Append(endpoint);
347 endpointMap[thePrefix] = endpoint;
348
349 /* Avoid strange race condition caused when garbage collection occurs
350 on the endpoint instqance which has not completed cosntruction. This
351 is an ulgly hack andrelies on the ctors taking less than one second. */
352 garbageCollectSkip = true;
353
354 PTRACE(3, "OpalMan\tAttached endpoint with prefix " << thePrefix);
355 }
356
357
DetachEndPoint(OpalEndPoint * endpoint)358 void OpalManager::DetachEndPoint(OpalEndPoint * endpoint)
359 {
360 if (PAssertNULL(endpoint) == NULL)
361 return;
362
363 endpoint->ShutDown();
364
365 endpointsMutex.StartWrite();
366
367 if (endpointList.Remove(endpoint)) {
368 // Was in list, remove from map too
369 std::map<PString, OpalEndPoint *>::iterator it = endpointMap.begin();
370 while (it != endpointMap.end()) {
371 if (it->second != endpoint)
372 ++it;
373 else {
374 endpointMap.erase(it);
375 it = endpointMap.begin();
376 }
377 }
378 }
379
380 endpointsMutex.EndWrite();
381 }
382
383
DetachEndPoint(const PString & prefix)384 void OpalManager::DetachEndPoint(const PString & prefix)
385 {
386 PReadWaitAndSignal mutex(endpointsMutex);
387
388 std::map<PString, OpalEndPoint *>::iterator it = endpointMap.find(prefix);
389 if (it == endpointMap.end())
390 return;
391
392 OpalEndPoint * endpoint = it->second;
393
394 endpointsMutex.StartWrite();
395 endpointMap.erase(it);
396 endpointsMutex.EndWrite();
397
398 // See if other references
399 for (it = endpointMap.begin(); it != endpointMap.end(); ++it) {
400 if (it->second == endpoint)
401 return; // Still a reference to it
402 }
403
404 // Last copy, delete it now
405 DetachEndPoint(endpoint);
406 }
407
408
FindEndPoint(const PString & prefix)409 OpalEndPoint * OpalManager::FindEndPoint(const PString & prefix)
410 {
411 PReadWaitAndSignal mutex(endpointsMutex);
412 std::map<PString, OpalEndPoint *>::iterator it = endpointMap.find(prefix);
413 return it != endpointMap.end() ? it->second : NULL;
414 }
415
416
SetUpCall(const PString & partyA,const PString & partyB,PString & token,void * userData,unsigned int options,OpalConnection::StringOptions * stringOptions)417 PBoolean OpalManager::SetUpCall(const PString & partyA,
418 const PString & partyB,
419 PString & token,
420 void * userData,
421 unsigned int options,
422 OpalConnection::StringOptions * stringOptions)
423 {
424 token.MakeEmpty();
425
426 PSafePtr<OpalCall> call = SetUpCall(partyA, partyB, userData, options, stringOptions);
427 if (call == NULL)
428 return false;
429
430 token = call->GetToken();
431 return true;
432 }
433
434
SetUpCall(const PString & partyA,const PString & partyB,void * userData,unsigned int options,OpalConnection::StringOptions * stringOptions)435 PSafePtr<OpalCall> OpalManager::SetUpCall(const PString & partyA,
436 const PString & partyB,
437 void * userData,
438 unsigned int options,
439 OpalConnection::StringOptions * stringOptions)
440 {
441 PTRACE(3, "OpalMan\tSet up call from " << partyA << " to " << partyB);
442
443 OpalCall * call = CreateCall(userData);
444 if (call == NULL)
445 return NULL;
446
447 call->SetPartyB(partyB);
448
449 // If we are the A-party then need to initiate a call now in this thread and
450 // go through the routing engine via OnIncomingConnection. If we were the
451 // B-Party then SetUpConnection() gets called in the context of the A-party
452 // thread.
453 PSafePtr<OpalConnection> connection = MakeConnection(*call, partyA, userData, options, stringOptions);
454 if (connection != NULL && connection->SetUpConnection()) {
455 PTRACE(4, "OpalMan\tSetUpCall succeeded, call=" << *call);
456 return call;
457 }
458
459 PTRACE_IF(2, connection == NULL, "OpalMan\tCould not create connection for \"" << partyA << '"');
460
461 OpalConnection::CallEndReason endReason = call->GetCallEndReason();
462 if (endReason == OpalConnection::NumCallEndReasons)
463 endReason = OpalConnection::EndedByTemporaryFailure;
464 call->Clear(endReason);
465
466 return NULL;
467 }
468
469
OnEstablishedCall(OpalCall &)470 void OpalManager::OnEstablishedCall(OpalCall & /*call*/)
471 {
472 }
473
474
IsCallEstablished(const PString & token)475 PBoolean OpalManager::IsCallEstablished(const PString & token)
476 {
477 PSafePtr<OpalCall> call = activeCalls.FindWithLock(token, PSafeReadOnly);
478 if (call == NULL)
479 return false;
480
481 return call->IsEstablished();
482 }
483
484
ClearCall(const PString & token,OpalConnection::CallEndReason reason,PSyncPoint * sync)485 PBoolean OpalManager::ClearCall(const PString & token,
486 OpalConnection::CallEndReason reason,
487 PSyncPoint * sync)
488 {
489 /*The hugely multi-threaded nature of the OpalCall objects means that
490 to avoid many forms of race condition, a call is cleared by moving it from
491 the "active" call dictionary to a list of calls to be cleared that will be
492 processed by a background thread specifically for the purpose of cleaning
493 up cleared calls. So that is all that this function actually does.
494 The real work is done in the OpalGarbageCollector thread.
495 */
496
497 // Find the call by token, callid or conferenceid
498 PSafePtr<OpalCall> call = activeCalls.FindWithLock(token, PSafeReference);
499 if (call == NULL) {
500 PTRACE(2, "OpalMan\tCould not find/lock call token \"" << token << '"');
501 return false;
502 }
503
504 call->Clear(reason, sync);
505 return true;
506 }
507
508
ClearCallSynchronous(const PString & token,OpalConnection::CallEndReason reason)509 PBoolean OpalManager::ClearCallSynchronous(const PString & token,
510 OpalConnection::CallEndReason reason)
511 {
512 PSyncPoint wait;
513 if (!ClearCall(token, reason, &wait))
514 return false;
515
516 wait.Wait();
517 return false;
518 }
519
520
ClearAllCalls(OpalConnection::CallEndReason reason,PBoolean wait)521 void OpalManager::ClearAllCalls(OpalConnection::CallEndReason reason, PBoolean wait)
522 {
523 InternalClearAllCalls(reason, wait, m_clearingAllCallsCount++ == 0);
524 --m_clearingAllCallsCount;
525 }
526
527
InternalClearAllCalls(OpalConnection::CallEndReason reason,bool wait,bool firstThread)528 void OpalManager::InternalClearAllCalls(OpalConnection::CallEndReason reason, bool wait, bool firstThread)
529 {
530 PTRACE(3, "OpalMan\tClearing all calls " << (wait ? "and waiting" : "asynchronously")
531 << ", " << (firstThread ? "primary" : "secondary") << " thread.");
532
533 if (firstThread) {
534 // Clear all the currentyl active calls
535 for (PSafePtr<OpalCall> call = activeCalls; call != NULL; ++call)
536 call->Clear(reason);
537 }
538
539 if (wait) {
540 /* This is done this way as PSyncPoint only works for one thread at a time,
541 all subsequent threads will wait on the mutex for the first one to be
542 released from the PSyncPoint wait. */
543 m_clearingAllCallsMutex.Wait();
544 if (firstThread)
545 PAssert(m_allCallsCleared.Wait(120000), "All calls not cleared in a timely manner");
546 m_clearingAllCallsMutex.Signal();
547 }
548
549 PTRACE(3, "OpalMan\tAll calls cleared.");
550 }
551
552
OnClearedCall(OpalCall & PTRACE_PARAM (call))553 void OpalManager::OnClearedCall(OpalCall & PTRACE_PARAM(call))
554 {
555 PTRACE(3, "OpalMan\tOnClearedCall " << call << " from \"" << call.GetPartyA() << "\" to \"" << call.GetPartyB() << '"');
556 }
557
558
InternalCreateCall()559 OpalCall * OpalManager::InternalCreateCall()
560 {
561 if (m_clearingAllCallsCount != 0) {
562 PTRACE(2, "OpalMan\tCreate call not performed as currently clearing all calls.");
563 return NULL;
564 }
565
566 return CreateCall(NULL);
567 }
568
569
CreateCall(void *)570 OpalCall * OpalManager::CreateCall(void * /*userData*/)
571 {
572 return new OpalCall(*this);
573 }
574
575
DestroyCall(OpalCall * call)576 void OpalManager::DestroyCall(OpalCall * call)
577 {
578 delete call;
579 }
580
581
GetNextToken(char prefix)582 PString OpalManager::GetNextToken(char prefix)
583 {
584 return psprintf("%c%08x%u", prefix, PRandom::Number(), ++lastCallTokenID);
585 }
586
MakeConnection(OpalCall & call,const PString & remoteParty,void * userData,unsigned int options,OpalConnection::StringOptions * stringOptions)587 PSafePtr<OpalConnection> OpalManager::MakeConnection(OpalCall & call,
588 const PString & remoteParty,
589 void * userData,
590 unsigned int options,
591 OpalConnection::StringOptions * stringOptions)
592 {
593 PTRACE(3, "OpalMan\tSet up connection to \"" << remoteParty << '"');
594
595 if (remoteParty.IsEmpty())
596 return NULL;
597
598 PCaselessString epname = remoteParty.Left(remoteParty.Find(':'));
599
600 PReadWaitAndSignal mutex(endpointsMutex);
601
602 OpalEndPoint * ep = NULL;
603 if (epname.IsEmpty()) {
604 if (endpointMap.size() > 0)
605 ep = endpointMap.begin()->second;
606 }
607 else
608 ep = FindEndPoint(epname);
609
610 if (ep != NULL)
611 return ep->MakeConnection(call, remoteParty, userData, options, stringOptions);
612
613 PTRACE(1, "OpalMan\tCould not find endpoint to handle protocol \"" << epname << '"');
614 return NULL;
615 }
616
617
OnIncomingConnection(OpalConnection & connection,unsigned options,OpalConnection::StringOptions * stringOptions)618 PBoolean OpalManager::OnIncomingConnection(OpalConnection & connection, unsigned options, OpalConnection::StringOptions * stringOptions)
619 {
620 PTRACE(3, "OpalMan\tOnIncoming connection " << connection);
621
622 connection.OnApplyStringOptions();
623
624 // See if we already have a B-Party in the call. If not, make one.
625 if (connection.GetOtherPartyConnection() != NULL)
626 return true;
627
628 OpalCall & call = connection.GetCall();
629
630 // See if have pre-allocated B party address, otherwise
631 // get destination from incoming connection
632 PString destination = call.GetPartyB();
633 if (destination.IsEmpty()) {
634 destination = connection.GetDestinationAddress();
635 if (destination.IsEmpty()) {
636 PTRACE(3, "OpalMan\tCannot complete call, no destination address from connection " << connection);
637 return false;
638 }
639 }
640
641 OpalConnection::StringOptions mergedOptions = connection.GetStringOptions();
642 if (stringOptions != NULL) {
643 for (PINDEX i = 0; i < stringOptions->GetSize(); ++i)
644 mergedOptions.SetAt(stringOptions->GetKeyAt(i), stringOptions->GetDataAt(i));
645 }
646
647 // Use a routing algorithm to figure out who the B-Party is, and make second connection
648 PStringSet routesTried;
649 return OnRouteConnection(routesTried, connection.GetLocalPartyURL(), destination, call, options, &mergedOptions);
650 }
651
652
OnRouteConnection(PStringSet & routesTried,const PString & a_party,const PString & b_party,OpalCall & call,unsigned options,OpalConnection::StringOptions * stringOptions)653 bool OpalManager::OnRouteConnection(PStringSet & routesTried,
654 const PString & a_party,
655 const PString & b_party,
656 OpalCall & call,
657 unsigned options,
658 OpalConnection::StringOptions * stringOptions)
659 {
660 PINDEX tableEntry = 0;
661 for (;;) {
662 PString route = ApplyRouteTable(a_party, b_party, tableEntry);
663 if (route.IsEmpty()) {
664 // Check for if B-Party is an explicit address
665 if (FindEndPoint(b_party.Left(b_party.Find(':'))) != NULL)
666 return MakeConnection(call, b_party, NULL, options, stringOptions) != NULL;
667
668 PTRACE(3, "OpalMan\tCould not route a=\"" << a_party << "\", b=\"" << b_party << ", call=" << call);
669 return false;
670 }
671
672 // See if already tried, keep searching if this route has already failed
673 if (routesTried[route])
674 continue;
675 routesTried += route;
676
677 // See if this route can be connected
678 if (MakeConnection(call, route, NULL, options, stringOptions) != NULL)
679 return true;
680
681 // Recursively call with translated route
682 if (OnRouteConnection(routesTried, a_party, route, call, options, stringOptions))
683 return true;
684 }
685 }
686
687
OnProceeding(OpalConnection & connection)688 void OpalManager::OnProceeding(OpalConnection & connection)
689 {
690 PTRACE(3, "OpalMan\tOnProceeding " << connection);
691
692 connection.GetCall().OnProceeding(connection);
693 }
694
695
OnAlerting(OpalConnection & connection)696 void OpalManager::OnAlerting(OpalConnection & connection)
697 {
698 PTRACE(3, "OpalMan\tOnAlerting " << connection);
699
700 connection.GetCall().OnAlerting(connection);
701 }
702
703
OnAnswerCall(OpalConnection & connection,const PString & caller)704 OpalConnection::AnswerCallResponse OpalManager::OnAnswerCall(OpalConnection & connection,
705 const PString & caller)
706 {
707 return connection.GetCall().OnAnswerCall(connection, caller);
708 }
709
710
OnConnected(OpalConnection & connection)711 void OpalManager::OnConnected(OpalConnection & connection)
712 {
713 PTRACE(3, "OpalMan\tOnConnected " << connection);
714
715 connection.GetCall().OnConnected(connection);
716 }
717
718
OnEstablished(OpalConnection & connection)719 void OpalManager::OnEstablished(OpalConnection & connection)
720 {
721 PTRACE(3, "OpalMan\tOnEstablished " << connection);
722
723 connection.GetCall().OnEstablished(connection);
724 }
725
726
OnReleased(OpalConnection & connection)727 void OpalManager::OnReleased(OpalConnection & connection)
728 {
729 PTRACE(3, "OpalMan\tOnReleased " << connection);
730
731 connection.GetCall().OnReleased(connection);
732 }
733
734
OnHold(OpalConnection & connection,bool fromRemote,bool onHold)735 void OpalManager::OnHold(OpalConnection & connection, bool fromRemote, bool onHold)
736 {
737 PTRACE(3, "OpalMan\t" << (onHold ? "On" : "Off") << " Hold "
738 << (fromRemote ? "from remote" : "request succeeded") << " on " << connection);
739 connection.GetEndPoint().OnHold(connection);
740 connection.GetCall().OnHold(connection, fromRemote, onHold);
741 }
742
743
OnHold(OpalConnection &)744 void OpalManager::OnHold(OpalConnection & /*connection*/)
745 {
746 }
747
748
OnForwarded(OpalConnection & PTRACE_PARAM (connection),const PString &)749 PBoolean OpalManager::OnForwarded(OpalConnection & PTRACE_PARAM(connection),
750 const PString & /*forwardParty*/)
751 {
752 PTRACE(4, "OpalEP\tOnForwarded " << connection);
753 return true;
754 }
755
756
OnTransferNotify(OpalConnection & PTRACE_PARAM (connection),const PStringToString & info)757 bool OpalManager::OnTransferNotify(OpalConnection & PTRACE_PARAM(connection), const PStringToString & info)
758 {
759 PTRACE(4, "OpalManager\tOnTransferNotify for " << connection << '\n' << info);
760 return info["result"] != "success";
761 }
762
763
GetCommonMediaFormats(bool transportable,bool pcmAudio) const764 OpalMediaFormatList OpalManager::GetCommonMediaFormats(bool transportable, bool pcmAudio) const
765 {
766 OpalMediaFormatList formats;
767
768 if (transportable) {
769 OpalMediaFormatList allFormats = OpalMediaFormat::GetAllRegisteredMediaFormats();
770 for (OpalMediaFormatList::iterator iter = allFormats.begin(); iter != allFormats.end(); ++iter) {
771 if (iter->IsTransportable())
772 formats += *iter;
773 }
774 }
775
776 if (pcmAudio) {
777 // Sound cards can only do 16 bit PCM, but at various sample rates
778 // The following will be in order of preference, so lets do wideband first
779 formats += OpalPCM16S_48KHZ;
780 formats += OpalPCM16S_32KHZ;
781 formats += OpalPCM16S_16KHZ;
782 formats += OpalPCM16_48KHZ;
783 formats += OpalPCM16_32KHZ;
784 formats += OpalPCM16_16KHZ;
785 formats += OpalPCM16;
786 formats += OpalRFC2833;
787 #if OPAL_T38_CAPABILITY
788 formats += OpalCiscoNSE;
789 #endif
790 }
791
792 #if OPAL_VIDEO
793 if (!videoInputDevice.deviceName.IsEmpty())
794 formats += OpalYUV420P;
795 #endif
796
797 #if OPAL_HAS_MSRP
798 formats += OpalMSRP;
799 #endif
800
801 #if OPAL_HAS_SIPIM
802 formats += OpalSIPIM;
803 #endif
804
805 #if OPAL_HAS_RFC4103
806 formats += OpalT140;
807 #endif
808
809 #if OPAL_HAS_H224
810 formats += OpalH224AnnexQ;
811 formats += OpalH224Tunnelled;
812 #endif
813
814 return formats;
815 }
816
817
AdjustMediaFormats(bool local,const OpalConnection & connection,OpalMediaFormatList & mediaFormats) const818 void OpalManager::AdjustMediaFormats(bool local,
819 const OpalConnection & connection,
820 OpalMediaFormatList & mediaFormats) const
821 {
822 mediaFormats.Remove(mediaFormatMask);
823 if (local)
824 mediaFormats.Reorder(mediaFormatOrder);
825 connection.GetCall().AdjustMediaFormats(local, connection, mediaFormats);
826 }
827
828
IsMediaBypassPossible(const OpalConnection & source,const OpalConnection & destination,unsigned sessionID) const829 PBoolean OpalManager::IsMediaBypassPossible(const OpalConnection & source,
830 const OpalConnection & destination,
831 unsigned sessionID) const
832 {
833 PTRACE(3, "OpalMan\tIsMediaBypassPossible: session " << sessionID);
834
835 return source.IsMediaBypassPossible(sessionID) &&
836 destination.IsMediaBypassPossible(sessionID);
837 }
838
839
OnOpenMediaStream(OpalConnection & PTRACE_PARAM (connection),OpalMediaStream & PTRACE_PARAM (stream))840 PBoolean OpalManager::OnOpenMediaStream(OpalConnection & PTRACE_PARAM(connection),
841 OpalMediaStream & PTRACE_PARAM(stream))
842 {
843 PTRACE(3, "OpalMan\tOnOpenMediaStream " << connection << ',' << stream);
844 return true;
845 }
846
847
CreateRTPSession(const RTP_Session::Params & params)848 RTP_UDP * OpalManager::CreateRTPSession (const RTP_Session::Params & params)
849 {
850 return new RTP_UDP(params);
851 }
852
853
OnRTPStatistics(const OpalConnection & connection,const RTP_Session & session)854 void OpalManager::OnRTPStatistics(const OpalConnection & connection, const RTP_Session & session)
855 {
856 connection.GetCall().OnRTPStatistics(connection, session);
857 }
858
859
OnLocalRTP(OpalConnection & PTRACE_PARAM (connection1),OpalConnection & PTRACE_PARAM (connection2),unsigned PTRACE_PARAM (sessionID),bool PTRACE_PARAM (started)) const860 bool OpalManager::OnLocalRTP(OpalConnection & PTRACE_PARAM(connection1),
861 OpalConnection & PTRACE_PARAM(connection2),
862 unsigned PTRACE_PARAM(sessionID),
863 bool PTRACE_PARAM(started)) const
864 {
865 PTRACE(3, "OpalMan\tOnLocalRTP(" << connection1 << ',' << connection2 << ',' << sessionID << ',' << started);
866 return false;
867 }
868
869
PassOneThrough(OpalMediaStreamPtr source,OpalMediaStreamPtr sink,bool bypass)870 static bool PassOneThrough(OpalMediaStreamPtr source,
871 OpalMediaStreamPtr sink,
872 bool bypass)
873 {
874 if (source == NULL) {
875 PTRACE(2, "OpalMan\tSetMediaPassThrough could not complete as source stream does not exist");
876 return false;
877 }
878
879 if (sink == NULL) {
880 PTRACE(2, "OpalMan\tSetMediaPassThrough could not complete as sink stream does not exist");
881 return false;
882 }
883
884 OpalMediaPatch * sourcePatch = source->GetPatch();
885 if (sourcePatch == NULL) {
886 PTRACE(2, "OpalMan\tSetMediaPassThrough could not complete as source patch does not exist");
887 return false;
888 }
889
890 OpalMediaPatch * sinkPatch = sink->GetPatch();
891 if (sinkPatch == NULL) {
892 PTRACE(2, "OpalMan\tSetMediaPassThrough could not complete as sink patch does not exist");
893 return false;
894 }
895
896 if (source->GetMediaFormat() != sink->GetMediaFormat()) {
897 PTRACE(3, "OpalMan\tSetMediaPassThrough could not complete as different formats: "
898 << source->GetMediaFormat() << "!=" << sink->GetMediaFormat());
899 return false;
900 }
901
902 // Note SetBypassPatch() will do PTRACE() on status.
903 return sourcePatch->SetBypassPatch(bypass ? sinkPatch : NULL);
904 }
905
906
SetMediaPassThrough(OpalConnection & connection1,OpalConnection & connection2,bool bypass,unsigned sessionID)907 bool OpalManager::SetMediaPassThrough(OpalConnection & connection1,
908 OpalConnection & connection2,
909 bool bypass,
910 unsigned sessionID)
911 {
912 bool gotOne = false;
913
914 if (sessionID != 0) {
915 // Do not use || as McCarthy will not execute the second bypass
916 if (PassOneThrough(connection1.GetMediaStream(sessionID, true), connection2.GetMediaStream(sessionID, false), bypass))
917 gotOne = true;
918 if (PassOneThrough(connection2.GetMediaStream(sessionID, true), connection1.GetMediaStream(sessionID, false), bypass))
919 gotOne = true;
920 }
921 else {
922 OpalMediaStreamPtr stream;
923 while ((stream = connection1.GetMediaStream(OpalMediaType(), true , stream)) != NULL) {
924 if (PassOneThrough(stream, connection2.GetMediaStream(stream->GetSessionID(), false), bypass))
925 gotOne = true;
926 }
927 while ((stream = connection2.GetMediaStream(OpalMediaType(), true, stream)) != NULL) {
928 if (PassOneThrough(stream, connection1.GetMediaStream(stream->GetSessionID(), false), bypass))
929 gotOne = true;
930 }
931 }
932
933 return gotOne;
934 }
935
936
SetMediaPassThrough(const PString & token1,const PString & token2,bool bypass,unsigned sessionID,bool network)937 bool OpalManager::SetMediaPassThrough(const PString & token1,
938 const PString & token2,
939 bool bypass,
940 unsigned sessionID,
941 bool network)
942 {
943 PSafePtr<OpalCall> call1 = FindCallWithLock(token1);
944 PSafePtr<OpalCall> call2 = FindCallWithLock(token2);
945
946 if (call1 == NULL || call2 == NULL) {
947 PTRACE(2, "OpalMan\tSetMediaPassThrough could not complete as one call does not exist");
948 return false;
949 }
950
951 PSafePtr<OpalConnection> connection1 = call1->GetConnection(0, PSafeReadOnly);
952 while (connection1 != NULL && connection1->IsNetworkConnection() == network)
953 ++connection1;
954
955 PSafePtr<OpalConnection> connection2 = call2->GetConnection(0, PSafeReadOnly);
956 while (connection2 != NULL && connection2->IsNetworkConnection() == network)
957 ++connection2;
958
959 if (connection1 == NULL || connection2 == NULL) {
960 PTRACE(2, "OpalMan\tSetMediaPassThrough could not complete as network connection not present in calls");
961 return false;
962 }
963
964 return OpalManager::SetMediaPassThrough(*connection1, *connection2, sessionID, bypass);
965 }
966
967
OnClosedMediaStream(const OpalMediaStream &)968 void OpalManager::OnClosedMediaStream(const OpalMediaStream & /*channel*/)
969 {
970 }
971
972 #if OPAL_VIDEO
973
CreateVideoInputDevice(const OpalConnection &,const OpalMediaFormat & mediaFormat,PVideoInputDevice * & device,PBoolean & autoDelete)974 PBoolean OpalManager::CreateVideoInputDevice(const OpalConnection & /*connection*/,
975 const OpalMediaFormat & mediaFormat,
976 PVideoInputDevice * & device,
977 PBoolean & autoDelete)
978 {
979 // Make copy so we can adjust the size
980 PVideoDevice::OpenArgs args = videoInputDevice;
981 mediaFormat.AdjustVideoArgs(args);
982
983 autoDelete = true;
984 device = PVideoInputDevice::CreateOpenedDevice(args, false);
985 PTRACE_IF(2, device == NULL, "OpalCon\tCould not open video device \"" << args.deviceName << '"');
986 return device != NULL;
987 }
988
989
CreateVideoOutputDevice(const OpalConnection & connection,const OpalMediaFormat & mediaFormat,PBoolean preview,PVideoOutputDevice * & device,PBoolean & autoDelete)990 PBoolean OpalManager::CreateVideoOutputDevice(const OpalConnection & connection,
991 const OpalMediaFormat & mediaFormat,
992 PBoolean preview,
993 PVideoOutputDevice * & device,
994 PBoolean & autoDelete)
995 {
996 // Donot use our one and only SDL window, if we need it for the video output.
997 if (preview && (
998 (videoPreviewDevice.driverName == "SDL" && videoOutputDevice.driverName == "SDL") ||
999 (videoPreviewDevice.deviceName == "SDL" && videoOutputDevice.deviceName == "SDL")
1000 ))
1001 return false;
1002
1003 // Make copy so we can adjust the size
1004 PVideoDevice::OpenArgs args = preview ? videoPreviewDevice : videoOutputDevice;
1005 mediaFormat.AdjustVideoArgs(args);
1006
1007 PINDEX start = args.deviceName.Find("TITLE=\"");
1008 if (start != P_MAX_INDEX) {
1009 start += 7;
1010 static PConstString const LocalPreview("Local Preview");
1011 args.deviceName.Splice(preview ? LocalPreview : connection.GetRemotePartyName(), start, args.deviceName.Find('"', start)-start);
1012 }
1013
1014 autoDelete = true;
1015 device = PVideoOutputDevice::CreateOpenedDevice(args, false);
1016 return device != NULL;
1017 }
1018
1019 #endif // OPAL_VIDEO
1020
1021
CreateMediaPatch(OpalMediaStream & source,PBoolean requiresPatchThread)1022 OpalMediaPatch * OpalManager::CreateMediaPatch(OpalMediaStream & source,
1023 PBoolean requiresPatchThread)
1024 {
1025 if (requiresPatchThread)
1026 return new OpalMediaPatch(source);
1027 else
1028 return new OpalPassiveMediaPatch(source);
1029 }
1030
1031
OnStartMediaPatch(OpalConnection &,OpalMediaPatch &)1032 void OpalManager::OnStartMediaPatch(OpalConnection & /*connection*/, OpalMediaPatch & /*patch*/)
1033 {
1034 }
1035
1036
OnStopMediaPatch(OpalConnection &,OpalMediaPatch &)1037 void OpalManager::OnStopMediaPatch(OpalConnection & /*connection*/, OpalMediaPatch & /*patch*/)
1038 {
1039 }
1040
1041
OnUserInputString(OpalConnection & connection,const PString & value)1042 void OpalManager::OnUserInputString(OpalConnection & connection,
1043 const PString & value)
1044 {
1045 connection.GetCall().OnUserInputString(connection, value);
1046 }
1047
1048
OnUserInputTone(OpalConnection & connection,char tone,int duration)1049 void OpalManager::OnUserInputTone(OpalConnection & connection,
1050 char tone,
1051 int duration)
1052 {
1053 connection.GetCall().OnUserInputTone(connection, tone, duration);
1054 }
1055
1056
ReadUserInput(OpalConnection & connection,const char * terminators,unsigned lastDigitTimeout,unsigned firstDigitTimeout)1057 PString OpalManager::ReadUserInput(OpalConnection & connection,
1058 const char * terminators,
1059 unsigned lastDigitTimeout,
1060 unsigned firstDigitTimeout)
1061 {
1062 PTRACE(3, "OpalMan\tReadUserInput from " << connection);
1063
1064 connection.PromptUserInput(true);
1065 PString digit = connection.GetUserInput(firstDigitTimeout);
1066 connection.PromptUserInput(false);
1067
1068 if (digit.IsEmpty()) {
1069 PTRACE(2, "OpalMan\tReadUserInput first character timeout (" << firstDigitTimeout << " seconds) on " << *this);
1070 return PString::Empty();
1071 }
1072
1073 PString input;
1074 while (digit.FindOneOf(terminators) == P_MAX_INDEX) {
1075 input += digit;
1076
1077 digit = connection.GetUserInput(lastDigitTimeout);
1078 if (digit.IsEmpty()) {
1079 PTRACE(2, "OpalMan\tReadUserInput last character timeout (" << lastDigitTimeout << " seconds) on " << *this);
1080 return input; // Input so far will have to do
1081 }
1082 }
1083
1084 return input.IsEmpty() ? digit : input;
1085 }
1086
1087
OnMWIReceived(const PString & PTRACE_PARAM (party),MessageWaitingType PTRACE_PARAM (type),const PString & PTRACE_PARAM (extraInfo))1088 void OpalManager::OnMWIReceived(const PString & PTRACE_PARAM(party),
1089 MessageWaitingType PTRACE_PARAM(type),
1090 const PString & PTRACE_PARAM(extraInfo))
1091 {
1092 PTRACE(3, "OpalMan\tOnMWIReceived(" << party << ',' << type << ',' << extraInfo << ')');
1093 }
1094
1095
RouteEntry(const PString & pat,const PString & dest)1096 OpalManager::RouteEntry::RouteEntry(const PString & pat, const PString & dest)
1097 : pattern(pat),
1098 destination(dest)
1099 {
1100 PString adjustedPattern = '^' + pattern;
1101
1102 // The regular expression makes a \t a 't', but we want a tab character.
1103 PINDEX tab = 0;
1104 while ((tab = adjustedPattern.Find("\\t", tab)) != P_MAX_INDEX) {
1105 if (adjustedPattern[tab-1] != '\\')
1106 adjustedPattern.Splice("\t", tab, 2);
1107 ++tab;
1108 }
1109
1110 // Test for backward compatibility format
1111 PINDEX colon = adjustedPattern.Find(':');
1112 if (colon != P_MAX_INDEX && adjustedPattern.Find('\t', colon) == P_MAX_INDEX)
1113 adjustedPattern.Splice(".*\t", colon+1);
1114
1115 adjustedPattern += '$';
1116
1117 if (!regex.Compile(adjustedPattern, PRegularExpression::IgnoreCase|PRegularExpression::Extended)) {
1118 PTRACE(1, "OpalMan\tCould not compile route regular expression \"" << adjustedPattern << '"');
1119 }
1120 }
1121
1122
PrintOn(ostream & strm) const1123 void OpalManager::RouteEntry::PrintOn(ostream & strm) const
1124 {
1125 strm << pattern << '=' << destination;
1126 }
1127
1128
AddRouteEntry(const PString & spec)1129 PBoolean OpalManager::AddRouteEntry(const PString & spec)
1130 {
1131 if (spec[0] == '#') // Comment
1132 return false;
1133
1134 if (spec[0] == '@') { // Load from file
1135 PTextFile file;
1136 if (!file.Open(spec.Mid(1), PFile::ReadOnly)) {
1137 PTRACE(1, "OpalMan\tCould not open route file \"" << file.GetFilePath() << '"');
1138 return false;
1139 }
1140 PTRACE(4, "OpalMan\tAdding routes from file \"" << file.GetFilePath() << '"');
1141 PBoolean ok = false;
1142 PString line;
1143 while (file.good()) {
1144 file >> line;
1145 if (AddRouteEntry(line))
1146 ok = true;
1147 }
1148 return ok;
1149 }
1150
1151 PINDEX equal = spec.Find('=');
1152 if (equal == P_MAX_INDEX) {
1153 PTRACE(2, "OpalMan\tInvalid route table entry: \"" << spec << '"');
1154 return false;
1155 }
1156
1157 RouteEntry * entry = new RouteEntry(spec.Left(equal).Trim(), spec.Mid(equal+1).Trim());
1158 if (entry->regex.GetErrorCode() != PRegularExpression::NoError) {
1159 PTRACE(2, "OpalMan\tIllegal regular expression in route table entry: \"" << spec << '"');
1160 delete entry;
1161 return false;
1162 }
1163
1164 PTRACE(4, "OpalMan\tAdded route \"" << *entry << '"');
1165 m_routeMutex.Wait();
1166 m_routeTable.Append(entry);
1167 m_routeMutex.Signal();
1168 return true;
1169 }
1170
1171
SetRouteTable(const PStringArray & specs)1172 PBoolean OpalManager::SetRouteTable(const PStringArray & specs)
1173 {
1174 PBoolean ok = false;
1175
1176 m_routeMutex.Wait();
1177 m_routeTable.RemoveAll();
1178
1179 for (PINDEX i = 0; i < specs.GetSize(); i++) {
1180 if (AddRouteEntry(specs[i].Trim()))
1181 ok = true;
1182 }
1183
1184 m_routeMutex.Signal();
1185
1186 return ok;
1187 }
1188
1189
SetRouteTable(const RouteTable & table)1190 void OpalManager::SetRouteTable(const RouteTable & table)
1191 {
1192 m_routeMutex.Wait();
1193 m_routeTable = table;
1194 m_routeTable.MakeUnique();
1195 m_routeMutex.Signal();
1196 }
1197
1198
ReplaceNDU(PString & destination,const PString & subst)1199 static void ReplaceNDU(PString & destination, const PString & subst)
1200 {
1201 if (subst.Find('@') != P_MAX_INDEX) {
1202 PINDEX at = destination.Find('@');
1203 if (at != P_MAX_INDEX) {
1204 PINDEX du = destination.Find("<!du>", at);
1205 if (du != P_MAX_INDEX)
1206 destination.Delete(at, du-at);
1207 }
1208 }
1209 destination.Replace("<!du>", subst, true);
1210 }
1211
1212
ApplyRouteTable(const PString & a_party,const PString & b_party,PINDEX & routeIndex)1213 PString OpalManager::ApplyRouteTable(const PString & a_party, const PString & b_party, PINDEX & routeIndex)
1214 {
1215 PWaitAndSignal mutex(m_routeMutex);
1216
1217 if (m_routeTable.IsEmpty())
1218 return routeIndex++ == 0 ? b_party : PString::Empty();
1219
1220 PString search = a_party + '\t' + b_party;
1221 PTRACE(4, "OpalMan\tSearching for route \"" << search << '"');
1222
1223 /* Examples:
1224 Call from UI pc:USB Audio Device\USB Audio Device sip:fred@boggs.com
1225 pc:USB Audio Device\USB Audio Device h323:fred@boggs.com
1226 pc:USB Audio Device\USB Audio Device fred
1227 Call from handset pots:TigerJet:USB Audio Device 123
1228 Call from SIP sip:me@here.net sip:you@there.com
1229 sip:me@here.net:5061 sip:you@there.com
1230 Call from H.323 h323:me@here.net h323:there.com
1231 h323:me@here.net:1721 h323:fred
1232
1233 Table:
1234 .*:# = ivr:
1235 pots:.*\\*.*\\*.* = sip:<dn2ip>
1236 pots:.* = sip:<da>
1237 pc:.* = sip:<da>
1238 h323:.* = pots:<dn>
1239 sip:.* = pots:<dn>
1240 h323:.* = pc:
1241 sip:.* = pc:
1242 */
1243
1244 PString destination;
1245 while (routeIndex < m_routeTable.GetSize()) {
1246 RouteEntry & entry = m_routeTable[routeIndex++];
1247 PINDEX pos;
1248 if (entry.regex.Execute(search, pos)) {
1249 PTRACE(4, "OpalMan\tMatched regex \"" << entry.regex.GetPattern() << "\" (\"" << entry.pattern << "\")");
1250 if (entry.destination.NumCompare("label:") != EqualTo) {
1251 destination = entry.destination;
1252 break;
1253 }
1254
1255 // restart search in table using label.
1256 search = entry.destination;
1257 routeIndex = 0;
1258 }
1259 else {
1260 PTRACE(4, "OpalMan\tDid not match regex \"" << entry.regex.GetPattern() << "\" (\"" << entry.pattern << "\")");
1261 }
1262 }
1263
1264 // No route found
1265 if (destination.IsEmpty())
1266 return PString::Empty();
1267
1268 // We are backward compatibility mode and the supplied address can be called
1269 PINDEX colon = b_party.Find(':');
1270 if (colon == P_MAX_INDEX)
1271 colon = 0;
1272 else if (FindEndPoint(b_party.Left(colon)) != NULL) {
1273 // Hack to make some modes work
1274 if (destination.Find("<da>") != P_MAX_INDEX)
1275 return b_party;
1276 colon++;
1277 }
1278 else if (b_party.NumCompare("tel", colon) == EqualTo) // Small cheat for tel: URI (RFC3966)
1279 colon++;
1280 else
1281 colon = 0;
1282
1283 PINDEX nonDigitPos = b_party.FindSpan("0123456789*#-.()", colon + (b_party[colon] == '+'));
1284 PString digits = b_party(colon, nonDigitPos-1);
1285
1286 PINDEX at = b_party.Find('@', colon);
1287
1288 // Another tel: URI hack
1289 static const char PhoneContext[] = ";phone-context=";
1290 PINDEX pos = b_party.Find(PhoneContext);
1291 if (pos != P_MAX_INDEX) {
1292 pos += sizeof(PhoneContext)-1;
1293 PINDEX end = b_party.Find(';', pos)-1;
1294 if (b_party[pos] == '+') // Phone context is a prefix
1295 digits.Splice(b_party(pos+1, end), 0);
1296 else // Otherwise phone context is a domain name
1297 ReplaceNDU(destination, '@'+b_party(pos, end));
1298 }
1299
1300 // Filter out the non E.164 digits, mainly for tel: URI support.
1301 while ((pos = digits.FindOneOf("+-.()")) != P_MAX_INDEX)
1302 digits.Delete(pos, 1);
1303
1304 PString user = b_party(colon, at-1);
1305
1306 destination.Replace("<da>", b_party, true);
1307 destination.Replace("<db>", user, true);
1308
1309 if (at != P_MAX_INDEX) {
1310 destination.Replace("<du>", user, true);
1311 ReplaceNDU(destination, b_party.Mid(at));
1312 }
1313 else if (PIPSocket::IsLocalHost(user.Left(user.Find(':')))) {
1314 destination.Replace("<du>", "", true);
1315 ReplaceNDU(destination, user);
1316 }
1317 else {
1318 destination.Replace("<du>", user, true);
1319 ReplaceNDU(destination, "");
1320 }
1321
1322 destination.Replace("<dn>", digits, true);
1323 destination.Replace("<!dn>", b_party.Mid(nonDigitPos), true);
1324
1325 while ((pos = destination.FindRegEx("<dn[1-9]>")) != P_MAX_INDEX)
1326 destination.Splice(digits.Mid(destination[pos+3]-'0'), pos, 5);
1327
1328 // Do meta character substitutions
1329 while ((pos = destination.Find("<dn2ip>")) != P_MAX_INDEX) {
1330 PStringStream route;
1331 PStringArray stars = digits.Tokenise('*');
1332 switch (stars.GetSize()) {
1333 case 0 :
1334 case 1 :
1335 case 2 :
1336 case 3 :
1337 route << digits;
1338 break;
1339
1340 case 4 :
1341 route << stars[0] << '.' << stars[1] << '.'<< stars[2] << '.'<< stars[3];
1342 break;
1343
1344 case 5 :
1345 route << stars[0] << '@'
1346 << stars[1] << '.' << stars[2] << '.'<< stars[3] << '.'<< stars[4];
1347 break;
1348
1349 default :
1350 route << stars[0] << '@'
1351 << stars[1] << '.' << stars[2] << '.'<< stars[3] << '.'<< stars[4]
1352 << ':' << stars[5];
1353 break;
1354 }
1355 destination.Splice(route, pos, 7);
1356 }
1357
1358 return destination;
1359 }
1360
1361
SetProductInfo(const OpalProductInfo & info,bool updateAll)1362 void OpalManager::SetProductInfo(const OpalProductInfo & info, bool updateAll)
1363 {
1364 productInfo = info;
1365
1366 if (updateAll) {
1367 endpointsMutex.StartWrite();
1368 for (PList<OpalEndPoint>::iterator ep = endpointList.begin(); ep != endpointList.end(); ++ep)
1369 ep->SetProductInfo(info);
1370 endpointsMutex.EndWrite();
1371 }
1372 }
1373
1374
SetDefaultUserName(const PString & name,bool updateAll)1375 void OpalManager::SetDefaultUserName(const PString & name, bool updateAll)
1376 {
1377 defaultUserName = name;
1378
1379 if (updateAll) {
1380 endpointsMutex.StartWrite();
1381 for (PList<OpalEndPoint>::iterator ep = endpointList.begin(); ep != endpointList.end(); ++ep)
1382 ep->SetDefaultLocalPartyName(name);
1383 endpointsMutex.EndWrite();
1384 }
1385 }
1386
1387
SetDefaultDisplayName(const PString & name,bool updateAll)1388 void OpalManager::SetDefaultDisplayName(const PString & name, bool updateAll)
1389 {
1390 defaultDisplayName = name;
1391
1392 if (updateAll) {
1393 endpointsMutex.StartWrite();
1394 for (PList<OpalEndPoint>::iterator ep = endpointList.begin(); ep != endpointList.end(); ++ep)
1395 ep->SetDefaultDisplayName(name);
1396 endpointsMutex.EndWrite();
1397 }
1398 }
1399
1400
IsLocalAddress(const PIPSocket::Address & ip) const1401 PBoolean OpalManager::IsLocalAddress(const PIPSocket::Address & ip) const
1402 {
1403 /* Check if the remote address is a private IP, broadcast, or us */
1404 return ip.IsAny() || ip.IsBroadcast() || ip.IsRFC1918() || PIPSocket::IsLocalHost(ip);
1405 }
1406
1407
IsRTPNATEnabled(OpalConnection &,const PIPSocket::Address & localAddr,const PIPSocket::Address & peerAddr,const PIPSocket::Address & sigAddr,PBoolean PTRACE_PARAM (incoming))1408 PBoolean OpalManager::IsRTPNATEnabled(OpalConnection & /*conn*/,
1409 const PIPSocket::Address & localAddr,
1410 const PIPSocket::Address & peerAddr,
1411 const PIPSocket::Address & sigAddr,
1412 PBoolean PTRACE_PARAM(incoming))
1413 {
1414 PTRACE(4, "OPAL\tChecking " << (incoming ? "incoming" : "outgoing") << " call for NAT: local=" << localAddr << ", peer=" << peerAddr << ", sig=" << sigAddr);
1415
1416 /* The peer endpoint may be on a public address, the local network (LAN) or
1417 NATed. If the last, it either knows it is behind the NAT or is blissfully
1418 unaware of it.
1419
1420 If the remote is public or on a LAN/VPN then no special treatment of RTP
1421 is needed. We do make the assumption that the remote will indicate correct
1422 addresses everywhere, in SETUP/OLC and INVITE/SDP.
1423
1424 Now if the endpoint is NATed and knows it is behind a NAT, and is doing
1425 it correctly, it is indistinguishable from being on a public address.
1426
1427 If the remote endpoint is unaware of it's NAT status then there will be a
1428 discrepency between the physical address of the connection and the
1429 signaling adddress indicated in the protocol, the H.323 SETUP
1430 sourceCallSignalAddress or SIP "Contact" field.
1431
1432 So this is the first test to make: if those addresses the same, we will
1433 assume the other guy is public or LAN/VPN and either no NAT is involved,
1434 or we leave them in charge of any NAT traversal as he has the ability to
1435 do it. In either case we don't do anything.
1436 */
1437
1438 if (peerAddr == sigAddr)
1439 return false;
1440
1441 /* Next test is to see if BOTH addresses are "public", non RFC1918. There are
1442 some cases with proxies, particularly with SIP, where this is possible. We
1443 will assume that NAT never occurs between two public addresses though it
1444 could occur between two private addresses */
1445
1446 if (!peerAddr.IsRFC1918() && !sigAddr.IsRFC1918())
1447 return false;
1448
1449 /* So now we have a remote that is confused in some way, so needs help. Our
1450 next test is for cases of where we are on a multi-homed machine and we
1451 ended up with a call from interface to another. No NAT needed.
1452 */
1453 if (PIPSocket::IsLocalHost(peerAddr))
1454 return false;
1455
1456 /* So, call is from a remote host somewhere and is still confused. We now
1457 need to check if we are actually ABLE to help. We test if the local end
1458 of the connection is public, i.e. no NAT at this end so we can help.
1459 */
1460 if (!localAddr.IsRFC1918())
1461 return true;
1462
1463 /* Another test for if we can help, we are behind a NAT too, but the user has
1464 provided information so we can compensate for it, i.e. we "know" about the
1465 NAT. We determine this by translating the localAddr and seing if it gets
1466 changed to the NAT router address. If so, we can help.
1467 */
1468 PIPSocket::Address natAddr = localAddr;
1469 if (TranslateIPAddress(natAddr, peerAddr))
1470 return true;
1471
1472 /* If we get here, we appear to be in a situation which, if we tried to do the
1473 NAT translation, we could end up in a staring match as the NAT traversal
1474 technique does not send anything till it receives something. If both side
1475 do that then .....
1476
1477 So, we do nothing and hope for the best. This means that either the call
1478 gets no media, or there is some other magic entity (smart router, proxy,
1479 etc) between the two endpoints we know nothing about that will do NAT
1480 translations for us.
1481
1482 Are there any other cases?
1483 */
1484 return false;
1485 }
1486
1487
TranslateIPAddress(PIPSocket::Address & localAddress,const PIPSocket::Address & remoteAddress)1488 PBoolean OpalManager::TranslateIPAddress(PIPSocket::Address & localAddress,
1489 const PIPSocket::Address & remoteAddress)
1490 {
1491 if (!IsLocalAddress(localAddress))
1492 return false; // Is already translated
1493
1494 if (IsLocalAddress(remoteAddress))
1495 return false; // Does not need to be translated
1496
1497 if (translationAddress.IsValid()) {
1498 localAddress = translationAddress; // Translate it!
1499 return true;
1500 }
1501
1502 PIPSocket::Address stunInterface;
1503 if (stun != NULL &&
1504 stun->GetNatType() != PSTUNClient::BlockedNat &&
1505 stun->GetInterfaceAddress(stunInterface) &&
1506 stunInterface == localAddress)
1507 return stun->GetExternalAddress(localAddress); // Translate it!
1508
1509 return false; // Have nothing to translate it to
1510 }
1511
1512
SetTranslationHost(const PString & host)1513 bool OpalManager::SetTranslationHost(const PString & host)
1514 {
1515 if (PIPSocket::GetHostAddress(host, translationAddress)) {
1516 translationHost = host;
1517 return true;
1518 }
1519
1520 translationHost = PString::Empty();
1521 translationAddress = PIPSocket::GetDefaultIpAny();
1522 return false;
1523 }
1524
1525
SetTranslationAddress(const PIPSocket::Address & address)1526 void OpalManager::SetTranslationAddress(const PIPSocket::Address & address)
1527 {
1528 translationAddress = address;
1529 translationHost = PIPSocket::GetHostName(address);
1530 }
1531
1532
GetNatMethod(const PIPSocket::Address & ip) const1533 PNatMethod * OpalManager::GetNatMethod(const PIPSocket::Address & ip) const
1534 {
1535 if (ip.IsValid() && IsLocalAddress(ip))
1536 return NULL;
1537
1538 return stun;
1539 }
1540
1541
SetSTUNServer(const PString & server)1542 PSTUNClient::NatTypes OpalManager::SetSTUNServer(const PString & server)
1543 {
1544 stunServer = server;
1545
1546 if (server.IsEmpty()) {
1547 if (stun)
1548 PInterfaceMonitor::GetInstance().OnRemoveNatMethod(stun);
1549
1550 delete stun;
1551 delete interfaceMonitor;
1552 stun = NULL;
1553 interfaceMonitor = NULL;
1554 return PSTUNClient::UnknownNat;
1555 }
1556
1557 if (stun != NULL)
1558 stun->SetServer(server);
1559 else {
1560 stun = new PSTUNClient(server,
1561 GetUDPPortBase(), GetUDPPortMax(),
1562 GetRtpIpPortBase(), GetRtpIpPortMax());
1563 interfaceMonitor = new InterfaceMonitor(*this);
1564 }
1565
1566 PSTUNClient::NatTypes type = stun->GetNatType();
1567 PIPSocket::Address stunExternalAddress;
1568 if (type != PSTUNClient::BlockedNat)
1569 stun->GetExternalAddress(stunExternalAddress);
1570
1571 PTRACE(3, "OPAL\tSTUN server \"" << server << "\" replies " << type << ", external IP " << stunExternalAddress);
1572
1573 return type;
1574 }
1575
1576
Set(unsigned newBase,unsigned newMax,unsigned range,unsigned dflt)1577 void OpalManager::PortInfo::Set(unsigned newBase,
1578 unsigned newMax,
1579 unsigned range,
1580 unsigned dflt)
1581 {
1582 if (newBase == 0) {
1583 newBase = dflt;
1584 newMax = dflt;
1585 if (dflt > 0)
1586 newMax += range;
1587 }
1588 else {
1589 if (newBase < 1024)
1590 newBase = 1024;
1591 else if (newBase > 65500)
1592 newBase = 65500;
1593
1594 if (newMax <= newBase)
1595 newMax = newBase + range;
1596 if (newMax > 65535)
1597 newMax = 65535;
1598 }
1599
1600 mutex.Wait();
1601
1602 current = base = (WORD)newBase;
1603 max = (WORD)newMax;
1604
1605 mutex.Signal();
1606 }
1607
1608
GetNext(unsigned increment)1609 WORD OpalManager::PortInfo::GetNext(unsigned increment)
1610 {
1611 PWaitAndSignal m(mutex);
1612
1613 if (current < base || current >= (max-increment))
1614 current = base;
1615
1616 if (current == 0)
1617 return 0;
1618
1619 WORD p = current;
1620 current = (WORD)(current + increment);
1621 return p;
1622 }
1623
1624
SetTCPPorts(unsigned tcpBase,unsigned tcpMax)1625 void OpalManager::SetTCPPorts(unsigned tcpBase, unsigned tcpMax)
1626 {
1627 tcpPorts.Set(tcpBase, tcpMax, 49, 0);
1628 }
1629
1630
GetNextTCPPort()1631 WORD OpalManager::GetNextTCPPort()
1632 {
1633 return tcpPorts.GetNext(1);
1634 }
1635
1636
SetUDPPorts(unsigned udpBase,unsigned udpMax)1637 void OpalManager::SetUDPPorts(unsigned udpBase, unsigned udpMax)
1638 {
1639 udpPorts.Set(udpBase, udpMax, 99, 0);
1640
1641 if (stun != NULL)
1642 stun->SetPortRanges(GetUDPPortBase(), GetUDPPortMax(), GetRtpIpPortBase(), GetRtpIpPortMax());
1643 }
1644
1645
GetNextUDPPort()1646 WORD OpalManager::GetNextUDPPort()
1647 {
1648 return udpPorts.GetNext(1);
1649 }
1650
1651
SetRtpIpPorts(unsigned rtpIpBase,unsigned rtpIpMax)1652 void OpalManager::SetRtpIpPorts(unsigned rtpIpBase, unsigned rtpIpMax)
1653 {
1654 rtpIpPorts.Set((rtpIpBase+1)&0xfffe, rtpIpMax&0xfffe, 199, 5000);
1655
1656 if (stun != NULL)
1657 stun->SetPortRanges(GetUDPPortBase(), GetUDPPortMax(), GetRtpIpPortBase(), GetRtpIpPortMax());
1658 }
1659
1660
GetRtpIpPortPair()1661 WORD OpalManager::GetRtpIpPortPair()
1662 {
1663 return rtpIpPorts.GetNext(2);
1664 }
1665
1666
GetMediaTypeOfService(const OpalMediaType & type) const1667 BYTE OpalManager::GetMediaTypeOfService(const OpalMediaType & type) const
1668 {
1669 map<OpalMediaType, BYTE>::const_iterator it = m_mediaTypeOfService.find(type);
1670 return it != m_mediaTypeOfService.end() ? it->second : m_defaultMediaTypeOfService;
1671 }
1672
1673
SetMediaTypeOfService(const OpalMediaType & type,unsigned tos)1674 void OpalManager::SetMediaTypeOfService(const OpalMediaType & type, unsigned tos)
1675 {
1676 m_mediaTypeOfService[type] = (BYTE)tos;
1677 }
1678
1679
SetAudioJitterDelay(unsigned minDelay,unsigned maxDelay)1680 void OpalManager::SetAudioJitterDelay(unsigned minDelay, unsigned maxDelay)
1681 {
1682 if (minDelay == 0) {
1683 // Disable jitter buffer completely if minimum is zero.
1684 minAudioJitterDelay = maxAudioJitterDelay = 0;
1685 return;
1686 }
1687
1688 PAssert(minDelay <= 10000 && maxDelay <= 10000, PInvalidParameter);
1689
1690 if (minDelay < 10)
1691 minDelay = 10;
1692 minAudioJitterDelay = minDelay;
1693
1694 if (maxDelay < minDelay)
1695 maxDelay = minDelay;
1696 maxAudioJitterDelay = maxDelay;
1697 }
1698
1699
SetMediaFormatOrder(const PStringArray & order)1700 void OpalManager::SetMediaFormatOrder(const PStringArray & order)
1701 {
1702 mediaFormatOrder = order;
1703 PTRACE(3, "OPAL\tSetMediaFormatOrder(" << setfill(',') << order << ')');
1704 }
1705
1706
SetMediaFormatMask(const PStringArray & mask)1707 void OpalManager::SetMediaFormatMask(const PStringArray & mask)
1708 {
1709 mediaFormatMask = mask;
1710 PTRACE(3, "OPAL\tSetMediaFormatMask(" << setfill(',') << mask << ')');
1711 }
1712
1713
1714 #if OPAL_VIDEO
1715 template<class PVideoXxxDevice>
SetVideoDevice(const PVideoDevice::OpenArgs & args,PVideoDevice::OpenArgs & member)1716 static PBoolean SetVideoDevice(const PVideoDevice::OpenArgs & args, PVideoDevice::OpenArgs & member)
1717 {
1718 // Check that the input device is legal
1719 PVideoXxxDevice * pDevice = PVideoXxxDevice::CreateDeviceByName(args.deviceName, args.driverName, args.pluginMgr);
1720 if (pDevice != NULL) {
1721 delete pDevice;
1722 member = args;
1723 return true;
1724 }
1725
1726 if (args.deviceName[0] != '#')
1727 return false;
1728
1729 // Selected device by ordinal
1730 PStringArray devices = PVideoXxxDevice::GetDriversDeviceNames(args.driverName, args.pluginMgr);
1731 if (devices.IsEmpty())
1732 return false;
1733
1734 PINDEX id = args.deviceName.Mid(1).AsUnsigned();
1735 if (id <= 0 || id > devices.GetSize())
1736 return false;
1737
1738 member = args;
1739 member.deviceName = devices[id-1];
1740 return true;
1741 }
1742
1743
SetVideoInputDevice(const PVideoDevice::OpenArgs & args)1744 PBoolean OpalManager::SetVideoInputDevice(const PVideoDevice::OpenArgs & args)
1745 {
1746 return SetVideoDevice<PVideoInputDevice>(args, videoInputDevice);
1747 }
1748
1749
SetVideoPreviewDevice(const PVideoDevice::OpenArgs & args)1750 PBoolean OpalManager::SetVideoPreviewDevice(const PVideoDevice::OpenArgs & args)
1751 {
1752 return SetVideoDevice<PVideoOutputDevice>(args, videoPreviewDevice);
1753 }
1754
1755
SetVideoOutputDevice(const PVideoDevice::OpenArgs & args)1756 PBoolean OpalManager::SetVideoOutputDevice(const PVideoDevice::OpenArgs & args)
1757 {
1758 return SetVideoDevice<PVideoOutputDevice>(args, videoOutputDevice);
1759 }
1760
1761 #endif
1762
SetNoMediaTimeout(const PTimeInterval & newInterval)1763 PBoolean OpalManager::SetNoMediaTimeout(const PTimeInterval & newInterval)
1764 {
1765 if (newInterval < 10)
1766 return false;
1767
1768 noMediaTimeout = newInterval;
1769 return true;
1770 }
1771
1772
GarbageCollection()1773 void OpalManager::GarbageCollection()
1774 {
1775 m_presentities.DeleteObjectsToBeRemoved();
1776 m_imManager->GarbageCollection();
1777
1778 bool allCleared = activeCalls.DeleteObjectsToBeRemoved();
1779
1780 endpointsMutex.StartRead();
1781
1782 for (PList<OpalEndPoint>::iterator ep = endpointList.begin(); ep != endpointList.end(); ++ep) {
1783 if (!ep->GarbageCollection())
1784 allCleared = false;
1785 }
1786
1787 endpointsMutex.EndRead();
1788
1789 if (allCleared && m_clearingAllCallsCount != 0)
1790 m_allCallsCleared.Signal();
1791 }
1792
1793
DeleteObject(PObject * object) const1794 void OpalManager::CallDict::DeleteObject(PObject * object) const
1795 {
1796 manager.DestroyCall(PDownCast(OpalCall, object));
1797 }
1798
1799
GarbageMain(PThread &,INT)1800 void OpalManager::GarbageMain(PThread &, INT)
1801 {
1802 while (!garbageCollectExit.Wait(1000)) {
1803 if (garbageCollectSkip)
1804 garbageCollectSkip = false;
1805 else
1806 GarbageCollection();
1807 }
1808 }
1809
OnNewConnection(OpalConnection &)1810 void OpalManager::OnNewConnection(OpalConnection & /*conn*/)
1811 {
1812 }
1813
1814 #if OPAL_HAS_MIXER
1815
StartRecording(const PString & callToken,const PFilePath & fn,const OpalRecordManager::Options & options)1816 bool OpalManager::StartRecording(const PString & callToken,
1817 const PFilePath & fn,
1818 const OpalRecordManager::Options & options)
1819 {
1820 PSafePtr<OpalCall> call = activeCalls.FindWithLock(callToken, PSafeReadWrite);
1821 if (call == NULL)
1822 return false;
1823
1824 return call->StartRecording(fn, options);
1825 }
1826
1827
IsRecording(const PString & callToken)1828 bool OpalManager::IsRecording(const PString & callToken)
1829 {
1830 PSafePtr<OpalCall> call = FindCallWithLock(callToken, PSafeReadWrite);
1831 return call != NULL && call->IsRecording();
1832 }
1833
1834
StopRecording(const PString & callToken)1835 bool OpalManager::StopRecording(const PString & callToken)
1836 {
1837 PSafePtr<OpalCall> call = activeCalls.FindWithLock(callToken, PSafeReadWrite);
1838 if (call == NULL)
1839 return false;
1840
1841 call->StopRecording();
1842 return true;
1843 }
1844
1845 #endif
1846
1847
OnApplyStringOptions(OpalConnection &,OpalConnection::StringOptions &)1848 void OpalManager::OnApplyStringOptions(OpalConnection &, OpalConnection::StringOptions &)
1849 {
1850 }
1851
1852
AddPresentity(const PString & presentity)1853 PSafePtr<OpalPresentity> OpalManager::AddPresentity(const PString & presentity)
1854 {
1855 if (presentity.IsEmpty())
1856 return NULL;
1857
1858 PSafePtr<OpalPresentity> oldPresentity = m_presentities.FindWithLock(presentity, PSafeReadWrite);
1859 if (oldPresentity != NULL)
1860 return oldPresentity;
1861
1862 OpalPresentity * newPresentity = OpalPresentity::Create(*this, presentity);
1863 if (newPresentity == NULL)
1864 return NULL;
1865
1866 PTRACE(4, "OpalMan\tAdded presentity for " << *newPresentity);
1867 m_presentities.SetAt(presentity, newPresentity);
1868 return PSafePtr<OpalPresentity>(newPresentity, PSafeReadWrite);
1869 }
1870
1871
GetPresentity(const PString & presentity,PSafetyMode mode)1872 PSafePtr<OpalPresentity> OpalManager::GetPresentity(const PString & presentity, PSafetyMode mode)
1873 {
1874 return m_presentities.FindWithLock(presentity, mode);
1875 }
1876
1877
GetPresentities() const1878 PStringList OpalManager::GetPresentities() const
1879 {
1880 PStringList presentities;
1881
1882 for (PSafePtr<OpalPresentity> presentity(m_presentities, PSafeReference); presentity != NULL; ++presentity)
1883 presentities += presentity->GetAOR().AsString();
1884
1885 return presentities;
1886 }
1887
1888
RemovePresentity(const PString & presentity)1889 bool OpalManager::RemovePresentity(const PString & presentity)
1890 {
1891 PTRACE(4, "OpalMan\tRemoving presentity for " << presentity);
1892 return m_presentities.RemoveAt(presentity);
1893 }
1894
1895
Message(const PString & to,const PString & body)1896 PBoolean OpalManager::Message(const PString & to, const PString & body)
1897 {
1898 OpalIM message;
1899 message.m_to = to;
1900 message.m_body = body;
1901 return Message(message);
1902 }
1903
1904
Message(const PURL & to,const PString & type,const PString & body,PURL & from,PString & conversationId)1905 PBoolean OpalManager::Message(const PURL & to, const PString & type, const PString & body, PURL & from, PString & conversationId)
1906 {
1907 OpalIM message;
1908 message.m_to = to;
1909 message.m_mimeType = type;
1910 message.m_body = body;
1911 message.m_from = from;
1912 message.m_conversationId = conversationId;
1913
1914 bool stat = Message(message);
1915
1916 from = message.m_from;
1917 conversationId = message.m_conversationId;
1918
1919 return stat;
1920 }
1921
1922
Message(OpalIM & message)1923 bool OpalManager::Message(OpalIM & message)
1924 {
1925 PSafePtr<OpalIMContext> context = m_imManager->FindContextForMessageWithLock(message);
1926 if (context == NULL)
1927 context = OpalIMContext::Create(*this, message.m_from, message.m_to);
1928 if (context == NULL)
1929 return false;
1930
1931 OpalIMContext::SentStatus stat = context->Send(new OpalIM(message));
1932
1933 return (stat == OpalIMContext::SentOK) || (stat == OpalIMContext::SentPending);
1934 }
1935
1936
OnMessageReceived(const OpalIM & message)1937 void OpalManager::OnMessageReceived(const OpalIM & message)
1938 {
1939 // find a presentity to give the message to
1940 for (PSafePtr<OpalPresentity> presentity(m_presentities, PSafeReference); presentity != NULL; ++presentity) {
1941 if (message.m_to == presentity->GetAOR()) {
1942 presentity->OnReceivedMessage(message);
1943 break;
1944 }
1945 }
1946 }
1947
1948
1949 /////////////////////////////////////////////////////////////////////////////
1950
InterfaceMonitor(OpalManager & manager)1951 OpalManager::InterfaceMonitor::InterfaceMonitor(OpalManager & manager)
1952 : PInterfaceMonitorClient(OpalManagerInterfaceMonitorClientPriority)
1953 , m_manager(manager)
1954 {
1955 }
1956
OnAddInterface(const PIPSocket::InterfaceEntry &)1957 void OpalManager::InterfaceMonitor::OnAddInterface(const PIPSocket::InterfaceEntry & /*entry*/)
1958 {
1959 m_manager.SetSTUNServer(m_manager.GetSTUNServer());
1960 }
1961
OnRemoveInterface(const PIPSocket::InterfaceEntry & entry)1962 void OpalManager::InterfaceMonitor::OnRemoveInterface(const PIPSocket::InterfaceEntry & entry)
1963 {
1964 PSTUNClient * stun = m_manager.GetSTUNClient();
1965 PIPSocket::Address addr;
1966 if (stun != NULL && stun->GetInterfaceAddress(addr) && entry.GetAddress() == addr)
1967 stun->InvalidateCache();
1968 }
1969
1970 #ifdef OPAL_ZRTP
GetZRTPEnabled() const1971 bool OpalManager::GetZRTPEnabled() const
1972 {
1973 return zrtpEnabled;
1974 }
1975 #endif
1976
1977
1978 /////////////////////////////////////////////////////////////////////////////
1979