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