1 /*
2  * handlers.cxx
3  *
4  * Session Initiation Protocol endpoint.
5  *
6  * Open Phone Abstraction Library (OPAL)
7  *
8  * Copyright (c) 2000 Equivalence Pty. Ltd.
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Open Phone Abstraction Library.
21  *
22  * The Initial Developer of the Original Code is Damien Sandras.
23  *
24  * Contributor(s): ______________________________________.
25  *
26  * $Revision: 29080 $
27  * $Author: rjongbloed $
28  * $Date: 2013-02-12 18:52:05 -0600 (Tue, 12 Feb 2013) $
29  *
30  * This code implements all or part of the following RFCs
31  *
32  * RFC 4479 "A Data Model for Presence"
33  * RFC 4480 "RPID: Rich Presence Extensions to the Presence Information Data Format (PIDF)"
34  */
35 
36 #include <ptlib.h>
37 #include <opal/buildopts.h>
38 
39 #if OPAL_SIP
40 
41 #ifdef __GNUC__
42 #pragma implementation "handlers.h"
43 #endif
44 
45 #include <sip/handlers.h>
46 
47 #include <ptclib/pdns.h>
48 #include <ptclib/enum.h>
49 #include <sip/sipep.h>
50 #include <ptclib/pxml.h>
51 #include <ptclib/random.h>
52 
53 #include <algorithm>
54 
55 
56 #define new PNEW
57 
58 
59 #if PTRACING
operator <<(ostream & strm,SIPHandler::State state)60 ostream & operator<<(ostream & strm, SIPHandler::State state)
61 {
62   static const char * const StateNames[] = {
63     "Subscribed", "Subscribing", "Unavailable", "Refreshing", "Restoring", "Unsubscribing", "Unsubscribed"
64   };
65   if (state < PARRAYSIZE(StateNames))
66     strm << StateNames[state];
67   else
68     strm << (unsigned)state;
69   return strm;
70 }
71 #endif
72 
73 
74 ////////////////////////////////////////////////////////////////////////////
75 
SIPHandler(SIP_PDU::Methods method,SIPEndPoint & ep,const SIPParameters & params)76 SIPHandler::SIPHandler(SIP_PDU::Methods method, SIPEndPoint & ep, const SIPParameters & params)
77   : endpoint(ep)
78   , m_authentication(NULL)
79   , m_authenticateErrors(0)
80   , m_username(params.m_authID)
81   , m_password(params.m_password)
82   , m_realm(params.m_realm)
83   , m_transport(NULL)
84   , m_method(method)
85   , m_addressOfRecord(params.m_addressOfRecord)
86   , m_remoteAddress(params.m_remoteAddress)
87   , m_callID(SIPTransaction::GenerateCallID())
88   , m_lastCseq(0)
89   , m_currentExpireTime(params.m_expire)
90   , m_originalExpireTime(params.m_expire)
91   , m_offlineExpireTime(params.m_restoreTime)
92   , m_state(Unavailable)
93   , m_receivedResponse(false)
94   , m_proxy(params.m_proxyAddress)
95 {
96   m_transactions.DisallowDeleteObjects();
97   m_expireTimer.SetNotifier(PCREATE_NOTIFIER(OnExpireTimeout));
98 
99   if (m_proxy.IsEmpty())
100     m_proxy = ep.GetProxy();
101 
102   PTRACE(4, "SIP\tConstructed " << m_method << " handler for " << m_addressOfRecord);
103 }
104 
105 
~SIPHandler()106 SIPHandler::~SIPHandler()
107 {
108   m_expireTimer.Stop();
109 
110   if (m_transport) {
111     m_transport->CloseWait();
112     delete m_transport;
113   }
114 
115   delete m_authentication;
116 
117   PTRACE(4, "SIP\tDestroyed " << m_method << " handler for " << m_addressOfRecord);
118 }
119 
120 
Compare(const PObject & obj) const121 PObject::Comparison SIPHandler::Compare(const PObject & obj) const
122 {
123   PAssert(PIsDescendant(&obj, SIPHandler), PInvalidCast);
124   const SIPHandler * other = dynamic_cast<const SIPHandler *>(&obj);
125   return other != NULL ? GetCallID().Compare(other->GetCallID()) : GreaterThan;
126 }
127 
128 
ShutDown()129 bool SIPHandler::ShutDown()
130 {
131   PSafeList<SIPTransaction> transactions;
132 
133   {
134     PSafeLockReadWrite mutex(*this);
135     if (!mutex.IsLocked())
136     return true;
137 
138   while (!m_stateQueue.empty())
139     m_stateQueue.pop();
140 
141     switch (GetState()) {
142       case Subscribed :
143       case Unavailable :
144         SendRequest(Unsubscribing);
145       case Unsubscribing :
146         return m_transactions.IsEmpty();
147 
148       default :
149         break;
150     }
151 
152     transactions = m_transactions;
153   }
154 
155   for (PSafePtr<SIPTransaction> transaction(transactions, PSafeReference); transaction != NULL; ++transaction)
156     transaction->Abort();
157 
158   return true;
159 }
160 
161 
SetState(SIPHandler::State newState)162 void SIPHandler::SetState(SIPHandler::State newState)
163 {
164   if (m_state == newState)
165     return;
166 
167   PTRACE(4, "SIP\tChanging " << GetMethod() << " handler from " << GetState() << " to " << newState
168          << ", target=" << GetAddressOfRecord() << ", id=" << GetCallID());
169 
170   m_state = newState;
171 
172   switch (m_state) {
173     case Subscribing :
174     case Refreshing :
175     case Restoring :
176     case Unsubscribing :
177       return;
178 
179     default :
180       break;
181   }
182 
183   if (m_stateQueue.empty())
184     return;
185 
186   newState = m_stateQueue.front();
187   m_stateQueue.pop();
188   SendRequest(newState);
189 }
190 
191 
ActivateState(SIPHandler::State newState)192 bool SIPHandler::ActivateState(SIPHandler::State newState)
193 {
194   if (GetState() == Unsubscribed)
195     return false;
196 
197   // If subscribing with zero expiry time, is same as unsubscribe
198   if (newState == Subscribing && GetExpire() == 0)
199     newState = Unsubscribing;
200 
201   // If unsubscribing and never got a response from server, rather than trying
202   // to send more unsuccessful packets, abort transactions and mark Unsubscribed
203   if (newState == Unsubscribing && !m_receivedResponse) {
204     SetState(Unsubscribed); // Allow garbage collection thread to clean up
205     return true;
206   }
207 
208   static const enum {
209     e_Invalid,
210     e_NoChange,
211     e_Execute,
212     e_Queue
213   } StateChangeActions[NumStates][NumStates] =
214   {
215     /* old-state
216            V      new-state-> Subscribed  Subscribing Unavailable Refreshing  Restoring   Unsubscribing Unsubscribed */
217     /* Subscribed        */ { e_NoChange, e_Execute,  e_Invalid,  e_Execute,  e_Execute,  e_Execute,    e_Invalid  },
218     /* Subscribing       */ { e_Invalid,  e_Queue,    e_Invalid,  e_NoChange, e_NoChange, e_Queue,      e_Invalid  },
219     /* Unavailable       */ { e_Invalid,  e_Execute,  e_NoChange, e_Execute,  e_Execute,  e_Execute,    e_Invalid  },
220     /* Refreshing        */ { e_Invalid,  e_Queue,    e_Invalid,  e_NoChange, e_NoChange, e_Queue,      e_Invalid  },
221     /* Restoring         */ { e_Invalid,  e_Queue,    e_Invalid,  e_NoChange, e_NoChange, e_Queue,      e_Invalid  },
222     /* Unsubscribing     */ { e_Invalid,  e_Invalid,  e_Invalid,  e_Invalid,  e_NoChange, e_NoChange,   e_Invalid  },
223     /* Unsubscribed      */ { e_Invalid,  e_Invalid,  e_Invalid,  e_Invalid,  e_Invalid,  e_NoChange,   e_NoChange }
224   };
225 
226   PSafeLockReadWrite mutex(*this);
227   if (!mutex.IsLocked())
228     return true;
229 
230   switch (StateChangeActions[GetState()][newState]) {
231     case e_Invalid :
232       PTRACE(2, "SIP\tCannot change state to " << newState << " for " << GetMethod()
233              << " handler while in " << GetState() << " state, target="
234              << GetAddressOfRecord() << ", id=" << GetCallID());
235       return false;
236 
237     case e_NoChange :
238       PTRACE(4, "SIP\tAlready in state " << GetState() << " for " << GetMethod()
239              << " handler, target=" << GetAddressOfRecord() << ", id=" << GetCallID());
240       break;
241 
242     case e_Execute :
243       PTRACE(4, "SIP\tExecuting state change to " << newState << " for " << GetMethod()
244              << " handler, target=" << GetAddressOfRecord() << ", id=" << GetCallID());
245       return SendRequest(newState);
246 
247     case e_Queue :
248       PTRACE(3, "SIP\tQueueing state change to " << newState << " for " << GetMethod()
249              << " handler while in " << GetState() << " state, target="
250              << GetAddressOfRecord() << ", id=" << GetCallID());
251       m_stateQueue.push(newState);
252       break;
253   }
254 
255   return true;
256 }
257 
258 
SendRequest(SIPHandler::State newState)259 PBoolean SIPHandler::SendRequest(SIPHandler::State newState)
260 {
261   m_expireTimer.Stop(false); // Stop automatic retry
262 
263   SetState(newState);
264 
265   if (GetTransport() == NULL)
266     OnFailed(SIP_PDU::Local_BadTransportAddress);
267   else {
268     m_lastCseq = 0;
269 
270     // Restoring or first time, try every interface
271     if (newState == Restoring || m_transport->GetInterface().IsEmpty()) {
272       PWaitAndSignal mutex(m_transport->GetWriteMutex());
273       if (m_transport->WriteConnect(WriteSIPHandler, this))
274         return true;
275     }
276     else {
277       // We contacted the server on an interface last time, assume it still works!
278       if (WriteSIPHandler(*m_transport, false))
279         return true;
280     }
281 
282     OnFailed(SIP_PDU::Local_TransportError);
283   }
284 
285   if (newState == Unsubscribing) {
286     // Transport level error, probably never going to get the unsubscribe through
287     SetState(Unsubscribed);
288     return true;
289   }
290 
291   RetryLater(m_offlineExpireTime);
292   return true;
293 }
294 
295 
GetTransport()296 OpalTransport * SIPHandler::GetTransport()
297 {
298   if (m_transport != NULL) {
299     if (m_transport->IsOpen())
300       return m_transport;
301 
302     m_transport->CloseWait();
303     delete m_transport;
304     m_transport = NULL;
305   }
306 
307   const PStringToString & remoteParams = m_remoteAddress.GetParamVars();
308 
309   // Look for a "proxy" parameter to override default proxy
310   if (m_proxy.IsEmpty() && remoteParams.Contains(OPAL_PROXY_PARAM)) {
311     m_proxy.Parse(remoteParams(OPAL_PROXY_PARAM));
312     m_remoteAddress.SetParamVar(OPAL_PROXY_PARAM, PString::Empty());
313   }
314 
315   SIPURL url;
316   if (!m_proxy.IsEmpty())
317     url = m_proxy;
318   else {
319     url = m_remoteAddress;
320     url.AdjustToDNS();
321   }
322 
323   PString localInterface = remoteParams(OPAL_INTERFACE_PARAM);
324   if (localInterface.IsEmpty())
325     localInterface = "*"; // Must specify a network interface or get infinite recursion
326 
327   m_transport = endpoint.CreateTransport(url, localInterface);
328   return m_transport;
329 }
330 
331 
SetExpire(int e)332 void SIPHandler::SetExpire(int e)
333 {
334   m_currentExpireTime = e;
335   PTRACE(3, "SIP\tExpiry time for " << GetMethod() << " set to " << e << " seconds.");
336 
337   // Only modify the m_originalExpireTime for future requests if IntervalTooBrief gives
338   // a bigger expire time. expire itself will always reflect the proxy decision
339   // (bigger or lower), but m_originalExpireTime determines what is used in future
340   // requests and is only modified if interval too brief
341   if (m_originalExpireTime < e)
342     m_originalExpireTime = e;
343 
344   // retry before the expire time.
345   // if the expire time is more than 20 mins, retry 10mins before expiry
346   // if the expire time is less than 20 mins, retry after half of the expiry time
347   if (GetExpire() > 0 && GetState() < Unsubscribing)
348     m_expireTimer.SetInterval(0, (unsigned)(GetExpire() < 20*60 ? GetExpire()/2 : GetExpire()-10*60));
349 }
350 
351 
WriteSIPHandler(OpalTransport & transport,void * param)352 PBoolean SIPHandler::WriteSIPHandler(OpalTransport & transport, void * param)
353 {
354   return param != NULL && ((SIPHandler *)param)->WriteSIPHandler(transport, true);
355 }
356 
357 
WriteSIPHandler(OpalTransport & transport,bool forked)358 bool SIPHandler::WriteSIPHandler(OpalTransport & transport, bool forked)
359 {
360   SIPTransaction * transaction = CreateTransaction(transport);
361   if (transaction == NULL) {
362     PTRACE(2, "SIP\tCould not create transaction on " << transport);
363     return false;
364   }
365 
366   SIPMIMEInfo & mime = transaction->GetMIME();
367 
368   if (forked) {
369     if (m_lastCseq == 0)
370       m_lastCseq = mime.GetCSeqIndex();
371     else
372       transaction->SetCSeq(m_lastCseq);
373   }
374 
375   for (PINDEX i = 0; i < m_mime.GetSize(); ++i)
376     mime.SetAt(m_mime.GetKeyAt(i), PString(m_mime.GetDataAt(i)));
377 
378   if (GetState() == Unsubscribing)
379     mime.SetExpires(0);
380 
381   if (m_authentication != NULL) {
382     SIPAuthenticator auth(*transaction);
383     m_authentication->Authorise(auth); // If already have info from last time, use it!
384   }
385 
386   if (transaction->Start()) {
387     m_transactions.Append(transaction);
388     return true;
389   }
390 
391   PTRACE(2, "SIP\tDid not start transaction on " << transport);
392   return false;
393 }
394 
395 
OnReceivedNOTIFY(SIP_PDU &)396 PBoolean SIPHandler::OnReceivedNOTIFY(SIP_PDU & /*response*/)
397 {
398   return PFalse;
399 }
400 
401 
OnReceivedResponse(SIPTransaction & transaction,SIP_PDU & response)402 void SIPHandler::OnReceivedResponse(SIPTransaction & transaction, SIP_PDU & response)
403 {
404   unsigned responseClass = response.GetStatusCode()/100;
405   if (responseClass < 2)
406     return; // Don't do anything with pending responses.
407 
408   // Received a response, so indicate that and collapse the forking on multiple interfaces.
409   m_receivedResponse = true;
410 
411   m_transactions.Remove(&transaction); // Take this transaction out of list
412 
413   switch (response.GetStatusCode()) {
414     default :
415       if (responseClass != 2)
416         break;
417 
418     case SIP_PDU::Failure_UnAuthorised :
419     case SIP_PDU::Failure_ProxyAuthenticationRequired :
420     case SIP_PDU::Failure_IntervalTooBrief :
421     case SIP_PDU::Failure_TemporarilyUnavailable:
422       // End connect mode on the transport
423       PString iface = transaction.GetInterface();
424       PTRACE(4, "SIP\tFinalising handlers interface \"" << iface << '"');
425       m_transport->SetInterface(iface);
426 
427       // And kill all the rest
428       PSafePtr<SIPTransaction> transToGo;
429       while ((transToGo = m_transactions.GetAt(0)) != NULL) {
430         m_transactions.Remove(transToGo);
431         transToGo->Abort();
432       }
433   }
434 
435   switch (response.GetStatusCode()) {
436     case SIP_PDU::Failure_UnAuthorised :
437     case SIP_PDU::Failure_ProxyAuthenticationRequired :
438       OnReceivedAuthenticationRequired(transaction, response);
439       return;
440 
441     case SIP_PDU::Failure_IntervalTooBrief :
442       OnReceivedIntervalTooBrief(transaction, response);
443       break;
444 
445     case SIP_PDU::Failure_TemporarilyUnavailable:
446       OnReceivedTemporarilyUnavailable(transaction, response);
447       break;
448 
449     default :
450       if (responseClass == 2)
451         OnReceivedOK(transaction, response);
452       else
453         OnFailed(response);
454   }
455 
456   m_authenticateErrors = 0;
457 }
458 
459 
OnReceivedIntervalTooBrief(SIPTransaction &,SIP_PDU & response)460 void SIPHandler::OnReceivedIntervalTooBrief(SIPTransaction & /*transaction*/, SIP_PDU & response)
461 {
462   SetExpire(response.GetMIME().GetMinExpires());
463 
464   // Restart the transaction with new authentication handler
465   SendRequest(GetState());
466 }
467 
468 
OnReceivedTemporarilyUnavailable(SIPTransaction &,SIP_PDU & response)469 void SIPHandler::OnReceivedTemporarilyUnavailable(SIPTransaction & /*transaction*/, SIP_PDU & response)
470 {
471   OnFailed(SIP_PDU::Failure_TemporarilyUnavailable);
472   RetryLater(response.GetMIME().GetInteger("Retry-After", m_offlineExpireTime));
473 }
474 
475 
OnReceivedAuthenticationRequired(SIPTransaction & transaction,SIP_PDU & response)476 void SIPHandler::OnReceivedAuthenticationRequired(SIPTransaction & transaction, SIP_PDU & response)
477 {
478   // If either username or password blank, try and fine values from other
479   // handlers which might be logged into the realm, or the proxy, if one.
480   SIP_PDU::StatusCodes status = endpoint.HandleAuthentication(m_authentication,
481                                                               m_authenticateErrors,
482                                                               response,
483                                                               GetProxy(),
484                                                               m_username,
485                                                               m_password);
486   if (status != SIP_PDU::Successful_OK) {
487     OnFailed(status);
488     if (GetState() != Unsubscribing && !transaction.IsCanceled())
489       RetryLater(m_offlineExpireTime);
490     return;
491   }
492 
493   // If we changed realm (or hadn't got one yet) update the handler database
494   if (m_realm != m_authentication->GetAuthRealm()) {
495     m_realm = m_authentication->GetAuthRealm();
496     PTRACE(3, "SIP\tAuth realm set to " << m_realm);
497     endpoint.UpdateHandlerIndexes(this);
498   }
499 
500   // Restart the transaction with new authentication handler
501   SendRequest(GetState());
502 }
503 
504 
OnReceivedOK(SIPTransaction &,SIP_PDU & response)505 void SIPHandler::OnReceivedOK(SIPTransaction & /*transaction*/, SIP_PDU & response)
506 {
507   response.GetMIME().GetProductInfo(m_productInfo);
508 
509   switch (GetState()) {
510     case Unsubscribing :
511       SetState(Unsubscribed);
512       break;
513 
514     case Subscribing :
515     case Refreshing :
516     case Restoring :
517       if (GetExpire() == 0)
518         SetState(Unsubscribed);
519       else
520         SetState(Subscribed);
521       break;
522 
523     default :
524       PTRACE(2, "SIP\tUnexpected 200 OK in handler with state " << GetState());
525   }
526 }
527 
528 
OnTransactionFailed(SIPTransaction & transaction)529 void SIPHandler::OnTransactionFailed(SIPTransaction & transaction)
530 {
531   if (m_transactions.Remove(&transaction)) {
532     OnFailed(transaction.GetStatusCode());
533     if (!transaction.IsCanceled())
534       RetryLater(m_offlineExpireTime);
535   }
536 }
537 
538 
RetryLater(unsigned after)539 void SIPHandler::RetryLater(unsigned after)
540 {
541   if (after == 0 || GetExpire() == 0)
542     return;
543 
544   PTRACE(3, "SIP\tRetrying " << GetMethod() << " after " << after << " seconds.");
545   m_expireTimer.SetInterval(0, after); // Keep trying to get it back
546 }
547 
548 
OnFailed(const SIP_PDU & response)549 void SIPHandler::OnFailed(const SIP_PDU & response)
550 {
551   OnFailed(response.GetStatusCode());
552 }
553 
554 
OnFailed(SIP_PDU::StatusCodes code)555 void SIPHandler::OnFailed(SIP_PDU::StatusCodes code)
556 {
557   switch (code) {
558     case SIP_PDU::Local_TransportError :
559     case SIP_PDU::Local_Timeout :
560     case SIP_PDU::Failure_RequestTimeout :
561     case SIP_PDU::Local_BadTransportAddress :
562     case SIP_PDU::Failure_TemporarilyUnavailable:
563       if (GetState() != Unsubscribing) {
564         SetState(Unavailable);
565         break;
566       }
567       // Do next case to finalise Unsubscribe even though there was an error
568 
569     default :
570       PTRACE(4, "SIP\tNot retrying " << GetMethod() << " due to error response " << code);
571       m_currentExpireTime = 0; // OK, stop trying
572       m_expireTimer.Stop(false);
573       SetState(Unsubscribed); // Allow garbage collection thread to clean up
574   }
575 }
576 
577 
OnExpireTimeout(PTimer &,INT)578 void SIPHandler::OnExpireTimeout(PTimer &, INT)
579 {
580   PSafeLockReadWrite lock(*this);
581   if (!lock.IsLocked())
582     return;
583 
584   switch (GetState()) {
585     case Subscribed :
586       PTRACE(2, "SIP\tStarting " << GetMethod() << " for binding refresh");
587       if (SendRequest(Refreshing))
588         return;
589       break;
590 
591     case Unavailable :
592       PTRACE(2, "SIP\tStarting " << GetMethod() << " for offline retry");
593       if (SendRequest(Restoring))
594         return;
595       break;
596 
597     default :
598       return;
599   }
600 
601   SetState(Unavailable);
602 }
603 
604 
605 ///////////////////////////////////////////////////////////////////////////////
606 
SIPRegisterHandler(SIPEndPoint & endpoint,const SIPRegister::Params & params)607 SIPRegisterHandler::SIPRegisterHandler(SIPEndPoint & endpoint, const SIPRegister::Params & params)
608   : SIPHandler(SIP_PDU::Method_REGISTER, endpoint, params)
609   , m_parameters(params)
610   , m_sequenceNumber(0)
611 {
612   // Foer some bizarre reason, even though REGISTER does not create a dialog,
613   // some registrars insist that you have a from tag ...
614   SIPURL local = params.m_localAddress.IsEmpty() ? params.m_addressOfRecord : params.m_localAddress;
615   local.SetTag();
616   m_parameters.m_localAddress = local.AsQuotedString();
617   m_parameters.m_proxyAddress = m_proxy.AsString();
618 }
619 
620 
CreateTransaction(OpalTransport & trans)621 SIPTransaction * SIPRegisterHandler::CreateTransaction(OpalTransport & trans)
622 {
623   SIPRegister::Params params = m_parameters;
624 
625   if (GetState() == Unsubscribing) {
626     params.m_expire = 0;
627 
628     if (params.m_contactAddress.IsEmpty()) {
629       if (m_contactAddresses.empty())
630         params.m_contactAddress = "*";
631       else {
632         for (SIPURLList::iterator contact = m_contactAddresses.begin(); contact != m_contactAddresses.end(); ++contact)
633           contact->GetFieldParameters().Remove("expires");
634         params.m_contactAddress = m_contactAddresses.ToString();
635       }
636     }
637   }
638   else {
639     params.m_expire = GetExpire();
640 
641     if (params.m_contactAddress.IsEmpty()) {
642       if (GetState() != Refreshing || m_contactAddresses.empty()) {
643         PString userName = SIPURL(params.m_addressOfRecord).GetUserName();
644         OpalTransportAddressArray interfaces = endpoint.GetInterfaceAddresses(true, &trans);
645         if (params.m_compatibility == SIPRegister::e_CannotRegisterMultipleContacts) {
646           // If translated by STUN then only the external address of the interface is used.
647           SIPURL contact(userName, interfaces[0]);
648           contact.Sanitise(SIPURL::RegContactURI);
649           params.m_contactAddress += contact.AsQuotedString();
650         }
651         else {
652           OpalTransportAddress localAddress = trans.GetLocalAddress(params.m_compatibility != SIPRegister::e_HasApplicationLayerGateway);
653           unsigned qvalue = 1000;
654           for (PINDEX i = 0; i < interfaces.GetSize(); ++i) {
655             /* If fully compliant, put into the contact field all the bound
656                interfaces. If special then we only put into the contact
657                listeners that are on the same interface. If translated by STUN
658                then only the external address of the interface is used. */
659             if (params.m_compatibility == SIPRegister::e_FullyCompliant || localAddress.IsEquivalent(interfaces[i], true)) {
660               SIPURL contact(userName, interfaces[i]);
661               contact.Sanitise(SIPURL::RegContactURI);
662               contact.GetFieldParameters().Set("q", qvalue < 1000 ? psprintf("0.%03u", qvalue) : "1");
663 
664               if (!params.m_contactAddress.IsEmpty())
665                 params.m_contactAddress += ", ";
666               params.m_contactAddress += contact.AsQuotedString();
667 
668               qvalue -= 1000/interfaces.GetSize();
669             }
670           }
671         }
672       }
673       else
674         params.m_contactAddress = m_contactAddresses.ToString();
675     }
676   }
677 
678   return new SIPRegister(endpoint, trans, GetCallID(), m_sequenceNumber, params);
679 }
680 
681 
OnReceivedOK(SIPTransaction & transaction,SIP_PDU & response)682 void SIPRegisterHandler::OnReceivedOK(SIPTransaction & transaction, SIP_PDU & response)
683 {
684   State previousState = GetState();
685   switch (previousState) {
686     case Unsubscribing :
687       SetState(Unsubscribed);
688       SendStatus(SIP_PDU::Successful_OK, Unsubscribing);
689       return;
690 
691     case Subscribing :
692     case Refreshing :
693     case Restoring :
694       break;
695 
696     default :
697       PTRACE(2, "SIP\tUnexpected 200 OK in handler with state " << previousState);
698       return;
699   }
700 
701   SIPMIMEInfo & mime = response.GetMIME();
702 
703   mime.GetProductInfo(m_productInfo);
704 
705   m_serviceRoute.FromString(mime("Service-Route"));
706 
707   SIPURLList requestedContacts;
708   transaction.GetMIME().GetContacts(requestedContacts);
709 
710   SIPURLList replyContacts;
711   mime.GetContacts(replyContacts);
712 
713   /* See if we are behind NAT and the Via header rport was present. This is
714      a STUN free mechanism for working behind firewalls. Also, some servers
715      will refuse to work unless the Contact header agrees whith where the
716      packet is physically coming from. */
717   OpalTransportAddress externalAddress;
718   if (m_parameters.m_compatibility != SIPRegister::e_HasApplicationLayerGateway)
719     externalAddress = mime.GetViaReceivedAddress();
720 
721   /* RFC3261 does say the registrar must send back ALL registrations, however
722      we only deal with replies from the registrar that we actually requested.
723 
724      Except! We look for special condition where registrar (e.g. OpenSIPS)
725      tries to be smart and adjusts the Contact field to be the external
726      address as per the Via header rport. IMHO this is a bug as it does not follow
727      what is implied by RFC3261/10.2.4 and 10.3 step 7. It should include the
728      original as well as the extra Contact. But it doesn't, so we have to deal.
729      */
730   for (SIPURLList::iterator reply = replyContacts.begin(); reply != replyContacts.end(); ) {
731     if (reply->GetHostAddress() == externalAddress) {
732       externalAddress.MakeEmpty(); // Clear this so no further action taken
733       m_externalAddress.MakeEmpty();
734       ++reply;
735     }
736     else if (std::find(requestedContacts.begin(), requestedContacts.end(), *reply) != requestedContacts.end())
737       ++reply;
738     else
739       replyContacts.erase(reply++);
740   }
741 
742   if (replyContacts.empty() && GetExpire() != 0) {
743     // Huh? Nothing we tried to register, registered! How is that possible?
744     PTRACE(2, "SIP\tREGISTER returned no Contact addresses we requested, not really registered.");
745     SendRequest(Unsubscribing);
746     SendStatus(SIP_PDU::GlobalFailure_NotAcceptable, previousState);
747     return;
748   }
749 
750   // If this is the final (possibly one and only) REGISTER, process it
751   if (m_externalAddress == externalAddress) {
752     int minExpiry = INT_MAX;
753     for (SIPURLList::iterator contact = replyContacts.begin(); contact != replyContacts.end(); ++contact) {
754       long expires = contact->GetFieldParameters().GetInteger("expires",
755                           mime.GetExpires(endpoint.GetRegistrarTimeToLive().GetSeconds()));
756       if (expires > 0 && minExpiry > expires)
757         minExpiry = expires;
758     }
759     SetExpire(minExpiry);
760 
761     m_contactAddresses = replyContacts;
762     SetState(Subscribed);
763     SendStatus(SIP_PDU::Successful_OK, previousState);
764     return;
765   }
766 
767   if (GetExpire() == 0) {
768     // If we had discovered we are behind NAT and had unregistered, re-REGISTER with new addresses
769     PTRACE(2, "SIP\tRe-registering NAT address change (" << m_contactAddresses << ") to " << externalAddress);
770     for (SIPURLList::iterator contact = m_contactAddresses.begin(); contact != m_contactAddresses.end(); ++contact)
771       contact->SetHostAddress(externalAddress);
772     m_contactAddresses.unique();
773     SetExpire(m_originalExpireTime);
774   }
775   else {
776     /* If we got here then we have done a successful register, but registrar indicated
777        that we are behind firewall. Unregister what we just registered */
778     for (SIPURLList::iterator contact = replyContacts.begin(); contact != replyContacts.end(); ++contact)
779       contact->GetFieldParameters().Remove("expires");
780     PTRACE(2, "SIP\tRemote indicated change of REGISTER Contact address(s) (" << replyContacts
781            << ") required due to NAT address " << externalAddress << ", previous=" << m_externalAddress);
782     m_contactAddresses = replyContacts;
783     SetExpire(0);
784   }
785 
786   // Remember (possibly new) NAT address
787   m_externalAddress == externalAddress;
788 
789   SendRequest(Refreshing);
790   SendStatus(SIP_PDU::Information_Trying, previousState);
791 }
792 
793 
OnFailed(SIP_PDU::StatusCodes r)794 void SIPRegisterHandler::OnFailed(SIP_PDU::StatusCodes r)
795 {
796   SendStatus(r, GetState());
797   SIPHandler::OnFailed(r);
798 }
799 
800 
SendRequest(SIPHandler::State s)801 PBoolean SIPRegisterHandler::SendRequest(SIPHandler::State s)
802 {
803   SendStatus(SIP_PDU::Information_Trying, s);
804   m_sequenceNumber = endpoint.GetNextCSeq();
805   return SIPHandler::SendRequest(s);
806 }
807 
808 
SendStatus(SIP_PDU::StatusCodes code,State state)809 void SIPRegisterHandler::SendStatus(SIP_PDU::StatusCodes code, State state)
810 {
811   SIPEndPoint::RegistrationStatus status;
812   status.m_handler = this;
813   status.m_addressofRecord = GetAddressOfRecord().AsString();
814   status.m_productInfo = m_productInfo;
815   status.m_reason = code;
816   status.m_userData = m_parameters.m_userData;
817 
818   switch (state) {
819     case Subscribing :
820       status.m_wasRegistering = true;
821       status.m_reRegistering = false;
822       break;
823 
824     case Subscribed :
825     case Refreshing :
826       status.m_wasRegistering = true;
827       status.m_reRegistering = true;
828       break;
829 
830     case Unsubscribed :
831     case Unavailable :
832     case Restoring :
833       status.m_wasRegistering = true;
834       status.m_reRegistering = code/100 != 2;
835       break;
836 
837     case Unsubscribing :
838       status.m_wasRegistering = false;
839       status.m_reRegistering = false;
840       break;
841 
842     default :
843       PAssertAlways(PInvalidParameter);
844   }
845 
846   endpoint.OnRegistrationStatus(status);
847 }
848 
849 
UpdateParameters(const SIPRegister::Params & params)850 void SIPRegisterHandler::UpdateParameters(const SIPRegister::Params & params)
851 {
852   if (!params.m_authID.IsEmpty())
853     m_username = m_parameters.m_authID = params.m_authID;   // Adjust the authUser if required
854   if (!params.m_realm.IsEmpty())
855     m_realm = m_parameters.m_realm = params.m_realm;   // Adjust the realm if required
856   if (!params.m_password.IsEmpty())
857     m_password = m_parameters.m_password = params.m_password; // Adjust the password if required
858 
859   if (params.m_expire > 0)
860     SetExpire(m_parameters.m_expire = params.m_expire);
861 
862   m_parameters.m_compatibility = params.m_compatibility;
863   m_parameters.m_contactAddress = params.m_contactAddress;
864   m_contactAddresses.clear();
865 
866   PTRACE(4, "SIP\tREGISTER parameters updated.");
867 }
868 
869 
870 /////////////////////////////////////////////////////////////////////////
871 
SIPSubscribeHandler(SIPEndPoint & endpoint,const SIPSubscribe::Params & params)872 SIPSubscribeHandler::SIPSubscribeHandler(SIPEndPoint & endpoint, const SIPSubscribe::Params & params)
873   : SIPHandler(SIP_PDU::Method_SUBSCRIBE, endpoint, params)
874   , m_parameters(params)
875   , m_unconfirmed(true)
876   , m_packageHandler(SIPEventPackageFactory::CreateInstance(params.m_eventPackage))
877   , m_previousResponse(NULL)
878 {
879   m_callID = m_dialog.GetCallID();
880 
881   m_parameters.m_proxyAddress = m_proxy.AsString();
882 
883   if (m_parameters.m_contentType.IsEmpty() && (m_packageHandler != NULL))
884     m_parameters.m_contentType = m_packageHandler->GetContentType();
885 }
886 
887 
~SIPSubscribeHandler()888 SIPSubscribeHandler::~SIPSubscribeHandler()
889 {
890   delete m_packageHandler;
891   delete m_previousResponse;
892 }
893 
894 
CreateTransaction(OpalTransport & trans)895 SIPTransaction * SIPSubscribeHandler::CreateTransaction(OpalTransport & trans)
896 {
897   // Do all the following here as must be after we have called GetTransport()
898   // which sets various fields correctly for transmission
899   if (!m_dialog.IsEstablished()) {
900     m_dialog.SetRequestURI(GetAddressOfRecord());
901     if (m_parameters.m_eventPackage.IsWatcher())
902       m_parameters.m_localAddress = GetAddressOfRecord().AsString();
903 
904     m_dialog.SetRemoteURI(m_parameters.m_addressOfRecord);
905 
906     if (m_parameters.m_localAddress.IsEmpty())
907       m_dialog.SetLocalURI(endpoint.GetRegisteredPartyName(m_parameters.m_addressOfRecord, *m_transport));
908     else
909       m_dialog.SetLocalURI(m_parameters.m_localAddress);
910 
911     m_dialog.SetProxy(m_proxy, true);
912   }
913 
914   m_parameters.m_expire = GetState() != Unsubscribing ? GetExpire() : 0;
915   return new SIPSubscribe(endpoint, trans, m_dialog, m_parameters);
916 }
917 
918 
OnFailed(const SIP_PDU & response)919 void SIPSubscribeHandler::OnFailed(const SIP_PDU & response)
920 {
921   SIP_PDU::StatusCodes responseCode = response.GetStatusCode();
922 
923   SendStatus(responseCode, GetState());
924 
925   if (GetState() != Unsubscribing && responseCode == SIP_PDU::Failure_TransactionDoesNotExist) {
926     // Resubscribe as previous subscription totally lost, but dialog processing
927     // may have altered the target so restore the original target address
928     m_parameters.m_addressOfRecord = GetAddressOfRecord().AsString();
929     PString dummy;
930     endpoint.Subscribe(m_parameters, dummy);
931   }
932 
933   SIPHandler::OnFailed(responseCode);
934 }
935 
936 
SendRequest(SIPHandler::State s)937 PBoolean SIPSubscribeHandler::SendRequest(SIPHandler::State s)
938 {
939   SendStatus(SIP_PDU::Information_Trying, s);
940   return SIPHandler::SendRequest(s);
941 }
942 
943 
WriteSIPHandler(OpalTransport & transport,bool forked)944 bool SIPSubscribeHandler::WriteSIPHandler(OpalTransport & transport, bool forked)
945 {
946   m_dialog.SetForking(forked);
947   bool ok = SIPHandler::WriteSIPHandler(transport, false);
948   m_dialog.SetForking(false);
949   return ok;
950 }
951 
952 
SendStatus(SIP_PDU::StatusCodes code,State state)953 void SIPSubscribeHandler::SendStatus(SIP_PDU::StatusCodes code, State state)
954 {
955   SIPEndPoint::SubscriptionStatus status;
956   status.m_handler = this;
957   status.m_addressofRecord = GetAddressOfRecord().AsString();
958   status.m_productInfo = m_productInfo;
959   status.m_reason = code;
960   status.m_userData = m_parameters.m_userData;
961 
962   switch (state) {
963     case Subscribing :
964       status.m_wasSubscribing = true;
965       status.m_reSubscribing = false;
966       break;
967 
968     case Subscribed :
969       if (m_unconfirmed) {
970         status.m_wasSubscribing = true;
971         status.m_reSubscribing = false;
972         endpoint.OnSubscriptionStatus(status);
973       }
974       // Do next state
975 
976     case Refreshing :
977       status.m_wasSubscribing = true;
978       status.m_reSubscribing = true;
979       break;
980 
981     case Unsubscribed :
982     case Unavailable :
983     case Restoring :
984       status.m_wasSubscribing = true;
985       status.m_reSubscribing = code/100 != 2;
986       break;
987 
988     case Unsubscribing :
989       status.m_wasSubscribing = false;
990       status.m_reSubscribing = false;
991       break;
992 
993     default :
994       PAssertAlways(PInvalidParameter);
995   }
996 
997   if (!m_parameters.m_onSubcribeStatus.IsNULL())
998     m_parameters.m_onSubcribeStatus(*this, status);
999 
1000   endpoint.OnSubscriptionStatus(status);
1001 }
1002 
1003 
UpdateParameters(const SIPSubscribe::Params & params)1004 void SIPSubscribeHandler::UpdateParameters(const SIPSubscribe::Params & params)
1005 {
1006   if (!params.m_authID.IsEmpty())
1007     m_username = params.m_authID;   // Adjust the authUser if required
1008   if (!params.m_realm.IsEmpty())
1009     m_realm = params.m_realm;   // Adjust the realm if required
1010   if (!params.m_password.IsEmpty())
1011     m_password = params.m_password; // Adjust the password if required
1012 
1013   m_parameters.m_contactAddress = params.m_contactAddress;
1014   m_parameters.m_onSubcribeStatus = params.m_onSubcribeStatus;
1015   m_parameters.m_onNotify = params.m_onNotify;
1016 
1017   if (params.m_expire > 0)
1018     SetExpire(params.m_expire);
1019 }
1020 
1021 
OnReceivedOK(SIPTransaction & transaction,SIP_PDU & response)1022 void SIPSubscribeHandler::OnReceivedOK(SIPTransaction & transaction, SIP_PDU & response)
1023 {
1024   /* An "expire" parameter in the Contact header has no semantics
1025    * for SUBSCRIBE. RFC3265, 3.1.1.
1026    * An answer can only shorten the expires time.
1027    */
1028   SetExpire(response.GetMIME().GetExpires(m_originalExpireTime));
1029   m_dialog.Update(*m_transport, response);
1030 
1031   if (GetState() != Unsubscribing)
1032     SIPHandler::OnReceivedOK(transaction, response);
1033 }
1034 
1035 
OnReceivedNOTIFY(SIP_PDU & request)1036 PBoolean SIPSubscribeHandler::OnReceivedNOTIFY(SIP_PDU & request)
1037 {
1038   if (PAssertNULL(m_transport) == NULL)
1039     return false;
1040 
1041   if (m_unconfirmed) {
1042     SendStatus(SIP_PDU::Successful_OK, GetState());
1043     m_unconfirmed = false;
1044   }
1045 
1046   SIPMIMEInfo & requestMIME = request.GetMIME();
1047 
1048   requestMIME.GetProductInfo(m_productInfo);
1049 
1050   // If we received this NOTIFY before, send the previous response
1051   if (m_dialog.IsDuplicateCSeq(requestMIME.GetCSeqIndex())) {
1052 
1053     // duplicate CSEQ but no previous response? That's unpossible!
1054     if (m_previousResponse == NULL)
1055       return request.SendResponse(*m_transport, SIP_PDU::Failure_InternalServerError, &endpoint);
1056 
1057     /* Make sure our repeat response has the CSeq/Via etc of this request, due
1058        to retries at various protocol layers we can have old and even older
1059        NOTIFY packets still coming in requiring matching responses. */
1060     m_previousResponse->InitialiseHeaders(request);
1061     return request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1062   }
1063 
1064   // remove last response
1065   delete m_previousResponse;
1066   m_previousResponse = new SIP_PDU(request, SIP_PDU::Failure_BadRequest);
1067 
1068   PStringToString subscriptionStateInfo;
1069   PCaselessString subscriptionState = requestMIME.GetSubscriptionState(subscriptionStateInfo);
1070   if (subscriptionState.IsEmpty()) {
1071     PTRACE(2, "SIP\tNOTIFY received without Subscription-State, assuming 'active'");
1072     subscriptionState = "active";
1073   }
1074 
1075   // Check the susbscription state
1076   if (subscriptionState == "terminated") {
1077     PTRACE(3, "SIP\tSubscription is terminated, state=" << GetState());
1078     m_previousResponse->SetStatusCode(SIP_PDU::Successful_OK);
1079     request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1080 
1081     switch (GetState()) {
1082       case Unsubscribed :
1083         break;
1084 
1085       case Subscribed :
1086       case Unavailable :
1087         SendRequest(Unsubscribing);
1088         break;
1089 
1090       default :
1091         SetState(Unsubscribed); // Allow garbage collection thread to clean up
1092         SendStatus(SIP_PDU::Successful_OK, Unsubscribing);
1093         break;
1094     }
1095 
1096     return true;
1097   }
1098 
1099   PString requestEvent = requestMIME.GetEvent();
1100   if (m_parameters.m_eventPackage != requestEvent) {
1101     PTRACE(2, "SIP\tNOTIFY received for incorrect event \"" << requestEvent << "\", requires \"" << m_parameters.m_eventPackage << '"');
1102     m_previousResponse->SetStatusCode(SIP_PDU::Failure_BadEvent);
1103     m_previousResponse->GetMIME().SetAt("Allow-Events", m_parameters.m_eventPackage);
1104     return request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1105   }
1106 
1107   // Check any Requires field
1108   PStringSet require = requestMIME.GetRequire();
1109   if (m_parameters.m_eventList)
1110     require -= "eventlist";
1111   if (!require.IsEmpty()) {
1112     PTRACE(2, "SIPPres\tNOTIFY contains unsupported Require field \"" << setfill(',') << require << '"');
1113     m_previousResponse->SetStatusCode(SIP_PDU::Failure_BadExtension);
1114     m_previousResponse->GetMIME().SetUnsupported(require);
1115     m_previousResponse->SetInfo("Unsupported Require");
1116     return request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1117   }
1118 
1119   // check the ContentType
1120   if (!m_parameters.m_contentType.IsEmpty()) {
1121     PCaselessString requestContentType = requestMIME.GetContentType();
1122     if (
1123         (m_parameters.m_contentType.Find(requestContentType) == P_MAX_INDEX) &&
1124         !(m_parameters.m_eventList && (requestContentType == "multipart/related")) &&
1125         !((m_packageHandler != NULL) && m_packageHandler->ValidateContentType(requestContentType, requestMIME))
1126         ) {
1127       PTRACE(2, "SIPPres\tNOTIFY contains unsupported Content-Type \""
1128              << requestContentType << "\", expecting \"" << m_parameters.m_contentType << "\" or multipart/related or other validated type");
1129       m_previousResponse->SetStatusCode(SIP_PDU::Failure_UnsupportedMediaType);
1130       m_previousResponse->GetMIME().SetAt("Accept", m_parameters.m_contentType);
1131       m_previousResponse->SetInfo("Unsupported Content-Type");
1132       return request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1133     }
1134   }
1135 
1136   // Check if we know how to deal with this event
1137   if (m_packageHandler == NULL && m_parameters.m_onNotify.IsNULL()) {
1138     PTRACE(2, "SIP\tNo handler for NOTIFY received for event \"" << requestEvent << '"');
1139     m_previousResponse->SetStatusCode(SIP_PDU::Failure_InternalServerError);
1140     return request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1141   }
1142 
1143   // Adjust timeouts if state is a go
1144   if (subscriptionState == "active" || subscriptionState == "pending") {
1145     PTRACE(3, "SIP\tSubscription is " << GetState());
1146     PString expire = SIPMIMEInfo::ExtractFieldParameter(GetState(), "expire");
1147     if (!expire.IsEmpty())
1148       SetExpire(expire.AsUnsigned());
1149   }
1150 
1151   bool sendResponse = true;
1152 
1153   PMultiPartList parts;
1154   if (!m_parameters.m_eventList || !requestMIME.DecodeMultiPartList(parts, request.GetEntityBody()))
1155     sendResponse = DispatchNOTIFY(request, *m_previousResponse);
1156   else {
1157     // If GetMultiParts() returns true there as at least one part and that
1158     // part must be the meta list, guranteed by DecodeMultiPartList()
1159     PMultiPartList::iterator iter = parts.begin();
1160 
1161     // First part is always Meta Information
1162     if (iter->m_mime.GetString(PMIMEInfo::ContentTypeTag) != "application/rlmi+xml") {
1163       PTRACE(2, "SIP\tNOTIFY received without RLMI as first multipart body");
1164       m_previousResponse->SetInfo("No Resource List Meta-Information");
1165       return request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1166     }
1167 
1168 #if P_EXPAT
1169     PXML xml;
1170     if (!xml.Load(iter->m_textBody)) {
1171       PTRACE(2, "SIP\tNOTIFY received with illegal RLMI\n"
1172                 "Line " << xml.GetErrorLine() << ", Column " << xml.GetErrorColumn() << ": " << xml.GetErrorString());
1173       m_previousResponse->SetInfo("Bad Resource List Meta-Information");
1174       return request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1175     }
1176 
1177     if (parts.GetSize() == 1)
1178       m_previousResponse->SetStatusCode(SIP_PDU::Successful_OK);
1179     else {
1180       while (++iter != parts.end()) {
1181         SIP_PDU pdu(request.GetMethod());
1182         SIPMIMEInfo & pduMIME = pdu.GetMIME();
1183 
1184         pduMIME.AddMIME(iter->m_mime);
1185         pdu.SetEntityBody(iter->m_textBody);
1186 
1187         PStringToString cid;
1188         if (iter->m_mime.GetComplex(PMIMEInfo::ContentIdTag, cid)) {
1189           PINDEX index = 0;
1190           PXMLElement * resource;
1191           while ((resource = xml.GetElement("resource", index++)) != NULL) {
1192             SIPURL uri = resource->GetAttribute("uri");
1193             if (!uri.IsEmpty()) {
1194               PXMLElement * instance = resource->GetElement("instance");
1195               if (instance != NULL && instance->GetAttribute("cid") == cid[PString::Empty()]) {
1196                 pduMIME.SetSubscriptionState(instance->GetAttribute("state"));
1197                 PXMLElement * name = resource->GetElement("name");
1198                 if (name != NULL)
1199                   uri.SetDisplayName(name->GetData());
1200                 pduMIME.SetFrom(uri.AsQuotedString());
1201                 pduMIME.SetTo(uri.AsQuotedString());
1202                 break;
1203               }
1204             }
1205           }
1206         }
1207 
1208         if (DispatchNOTIFY(pdu, *m_previousResponse))
1209           sendResponse = false;
1210       }
1211     }
1212 #else
1213     for ( ; iter != parts.end(); ++iter) {
1214       SIP_PDU pdu(request.GetMethod());
1215       pdu.GetMIME().AddMIME(iter->m_mime);
1216       pdu.SetEntityBody(iter->m_textBody);
1217 
1218       if (DispatchNOTIFY(pdu, *m_previousResponse))
1219         sendResponse = false;
1220     }
1221 #endif
1222   }
1223 
1224   if (sendResponse)
1225     request.SendResponse(*m_transport, *m_previousResponse, &endpoint);
1226   return true;
1227 }
1228 
1229 
DispatchNOTIFY(SIP_PDU & request,SIP_PDU & response)1230 bool SIPSubscribeHandler::DispatchNOTIFY(SIP_PDU & request, SIP_PDU & response)
1231 {
1232   if (!m_parameters.m_onNotify.IsNULL()) {
1233     PTRACE(4, "SIP\tCalling NOTIFY callback for AOR \"" << m_addressOfRecord << "\"");
1234     SIPSubscribe::NotifyCallbackInfo status(endpoint, *m_transport, request, response);
1235     m_parameters.m_onNotify(*this, status);
1236     return status.m_sendResponse;
1237   }
1238 
1239   if (m_packageHandler != NULL) {
1240     PTRACE(4, "SIP\tCalling package NOTIFY handler for AOR \"" << m_addressOfRecord << "\"");
1241     if (m_packageHandler->OnReceivedNOTIFY(*this, request))
1242       response.SetStatusCode(SIP_PDU::Successful_OK);
1243     return true;
1244   }
1245 
1246   PTRACE(2, "SIP\tNo NOTIFY handler for AOR \"" << m_addressOfRecord << "\"");
1247   return true;
1248 }
1249 
ValidateContentType(const PString & type,const SIPMIMEInfo & mime)1250 bool SIPEventPackageHandler::ValidateContentType(const PString & type, const SIPMIMEInfo & mime)
1251 {
1252   return type.IsEmpty() && (mime.GetContentLength() == 0);
1253 }
1254 
1255 class SIPMwiEventPackageHandler : public SIPEventPackageHandler
1256 {
GetContentType() const1257   virtual PCaselessString GetContentType() const
1258   {
1259     return "application/simple-message-summary";
1260   }
1261 
OnReceivedNOTIFY(SIPHandler & handler,SIP_PDU & request)1262   virtual bool OnReceivedNOTIFY(SIPHandler & handler, SIP_PDU & request)
1263   {
1264     PString body = request.GetEntityBody();
1265     if (body.IsEmpty ())
1266       return true;
1267 
1268     // Extract the string describing the number of new messages
1269     static struct {
1270       const char * name;
1271       OpalManager::MessageWaitingType type;
1272     } const validMessageClasses[] = {
1273       { "voice-message",      OpalManager::VoiceMessageWaiting      },
1274       { "fax-message",        OpalManager::FaxMessageWaiting        },
1275       { "pager-message",      OpalManager::PagerMessageWaiting      },
1276       { "multimedia-message", OpalManager::MultimediaMessageWaiting },
1277       { "text-message",       OpalManager::TextMessageWaiting       },
1278       { "none",               OpalManager::NoMessageWaiting         }
1279     };
1280 
1281     PMIMEInfo info(request.GetEntityBody());
1282 
1283     const SIPURL & aor = handler.GetAddressOfRecord();
1284     PString account = info.Get("Message-Account");
1285     SIPURL accountURI(account);
1286     if (account.IsEmpty() || aor.GetUserName() == account ||
1287             (accountURI.GetUserName() == "asterisk" && accountURI.GetHostName() == aor.GetHostName()))
1288       account = aor.AsString();
1289 
1290     bool nothingSent = true;
1291     for (PINDEX z = 0 ; z < PARRAYSIZE(validMessageClasses); z++) {
1292       if (info.Contains(validMessageClasses[z].name)) {
1293         handler.GetEndPoint().OnMWIReceived(account, validMessageClasses[z].type, info[validMessageClasses[z].name]);
1294         nothingSent = false;
1295       }
1296     }
1297 
1298     // Received MWI, but not counts, must be old form. Also make sure we send a
1299     // lower case yes/no to call back function, regardless of what comes down the wire.
1300     if (nothingSent)
1301       handler.GetEndPoint().OnMWIReceived(account, OpalManager::NumMessageWaitingTypes,
1302                                  (info.Get("Messages-Waiting") *= "yes") ? "yes" : "no");
1303 
1304     return true;
1305   }
1306 };
1307 
1308 static SIPEventPackageFactory::Worker<SIPMwiEventPackageHandler> mwiEventPackageHandler(SIPSubscribe::MessageSummary);
1309 
1310 
1311 ///////////////////////////////////////////////////////////////////////////////
1312 
1313 #if P_EXPAT
1314 
1315 // This package is on for backward compatibility, presence should now use the
1316 // the OpalPresence classes to manage SIP presence.
1317 class SIPPresenceEventPackageHandler : public SIPEventPackageHandler
1318 {
GetContentType() const1319   virtual PCaselessString GetContentType() const
1320   {
1321     return "application/pidf+xml";
1322   }
1323 
OnReceivedNOTIFY(SIPHandler & handler,SIP_PDU & request)1324   virtual bool OnReceivedNOTIFY(SIPHandler & handler, SIP_PDU & request)
1325   {
1326     PTRACE(4, "SIP\tProcessing presence NOTIFY using old API");
1327 
1328     // support old API
1329     SIPURL from = request.GetMIME().GetFrom();
1330     from.Sanitise(SIPURL::ExternalURI);
1331 
1332     SIPURL to = request.GetMIME().GetTo();
1333     to.Sanitise(SIPURL::ExternalURI);
1334 
1335     list<SIPPresenceInfo> infoList;
1336 
1337     // Check for empty body, if so then is OK, just a ping ...
1338     if (request.GetEntityBody().IsEmpty())
1339       infoList.resize(1);
1340     else {
1341       PString error;
1342       PString body = request.GetEntityBody();
1343       if (handler.GetProductInfo().name.Find("Asterisk") != P_MAX_INDEX) {
1344         PTRACE(4, "SIP\tCompensating for " << handler.GetProductInfo().name << ","
1345                   " replacing " << to.AsString() << " with " << from.AsString());
1346         body.Replace(to.AsString(), from.AsString());
1347       }
1348       if (!SIPPresenceInfo::ParseXML(body, infoList, error))
1349         return false;
1350     }
1351 
1352     for (list<SIPPresenceInfo>::iterator it = infoList.begin(); it != infoList.end(); ++it) {
1353       it->m_entity = from; // Really should not do this, but put in for backward compatibility
1354       it->m_target = to;
1355       handler.GetEndPoint().OnPresenceInfoReceived(*it);
1356     }
1357     return true;
1358   }
1359 };
1360 
1361 static SIPEventPackageFactory::Worker<SIPPresenceEventPackageHandler> presenceEventPackageHandler(SIPSubscribe::Presence);
1362 
1363 
ParseParticipant(PXMLElement * participantElement,SIPDialogNotification::Participant & participant)1364 static void ParseParticipant(PXMLElement * participantElement, SIPDialogNotification::Participant & participant)
1365 {
1366   if (participantElement == NULL)
1367     return;
1368 
1369   PXMLElement * identityElement = participantElement->GetElement("identity");
1370   if (identityElement != NULL) {
1371     participant.m_identity = identityElement->GetData();
1372     participant.m_display = identityElement->GetAttribute("display");
1373   }
1374 
1375   PXMLElement * targetElement = participantElement->GetElement("target");
1376   if (targetElement == NULL)
1377     return;
1378 
1379   participant.m_URI = targetElement->GetAttribute("uri");
1380 
1381   PXMLElement * paramElement;
1382   PINDEX i = 0;
1383   while ((paramElement = targetElement->GetElement("param", i++)) != NULL) {
1384     PCaselessString name = paramElement->GetAttribute("pname");
1385     PCaselessString value = paramElement->GetAttribute("pvalue");
1386     if (name == "appearance" || // draft-anil-sipping-bla-04 version
1387         name == "x-line-id")    // draft-anil-sipping-bla-03 version
1388       participant.m_appearance = value.AsUnsigned();
1389     else if (name == "sip.byeless" || name == "+sip.byeless")
1390       participant.m_byeless = value == "true";
1391     else if (name == "sip.rendering" || name == "+sip.rendering") {
1392       if (value == "yes")
1393         participant.m_rendering = SIPDialogNotification::RenderingMedia;
1394       else if (value == "no")
1395         participant.m_rendering = SIPDialogNotification::NotRenderingMedia;
1396       else
1397         participant.m_rendering = SIPDialogNotification::RenderingUnknown;
1398     }
1399   }
1400 }
1401 
1402 
1403 class SIPDialogEventPackageHandler : public SIPEventPackageHandler
1404 {
1405 public:
SIPDialogEventPackageHandler()1406   SIPDialogEventPackageHandler()
1407     : m_dialogNotifyVersion(1)
1408   {
1409   }
1410 
GetContentType() const1411   virtual PCaselessString GetContentType() const
1412   {
1413     return "application/dialog-info+xml";
1414   }
1415 
OnReceivedNOTIFY(SIPHandler & handler,SIP_PDU & request)1416   virtual bool OnReceivedNOTIFY(SIPHandler & handler, SIP_PDU & request)
1417   {
1418     // Check for empty body, if so then is OK, just a ping ...
1419     if (request.GetEntityBody().IsEmpty())
1420       return true;
1421 
1422     PXML xml;
1423     if (!xml.Load(request.GetEntityBody()))
1424       return false;
1425 
1426     PXMLElement * rootElement = xml.GetRootElement();
1427     if (rootElement == NULL || rootElement->GetName() != "dialog-info")
1428       return false;
1429 
1430     SIPDialogNotification info(rootElement->GetAttribute("entity"));
1431     if (info.m_entity.IsEmpty())
1432       return false;
1433 
1434     PINDEX index = 0;
1435     PXMLElement * dialogElement;
1436     while ((dialogElement = rootElement->GetElement("dialog", index)) != NULL) {
1437       info.m_callId = dialogElement->GetAttribute("call-id");
1438       info.m_local.m_dialogTag = dialogElement->GetAttribute("local-tag");
1439       info.m_remote.m_dialogTag = dialogElement->GetAttribute("remote-tag");
1440 
1441       PXMLElement * stateElement = dialogElement->GetElement("state");
1442       if (stateElement == NULL)
1443         info.m_state = SIPDialogNotification::Terminated;
1444       else {
1445         PCaselessString str = stateElement->GetData();
1446         for (info.m_state = SIPDialogNotification::LastState; info.m_state > SIPDialogNotification::FirstState; --info.m_state) {
1447           if (str == info.GetStateName())
1448             break;
1449         }
1450 
1451         str = stateElement->GetAttribute("event");
1452         for (info.m_eventType = SIPDialogNotification::LastEvent; info.m_eventType >= SIPDialogNotification::FirstEvent; --info.m_eventType) {
1453           if (str == info.GetEventName())
1454             break;
1455         }
1456 
1457         info.m_eventCode = stateElement->GetAttribute("code").AsUnsigned();
1458       }
1459 
1460       ParseParticipant(dialogElement->GetElement("local"), info.m_local);
1461       ParseParticipant(dialogElement->GetElement("remote"), info.m_remote);
1462       handler.GetEndPoint().OnDialogInfoReceived(info);
1463       index++;
1464     }
1465 
1466     if (index == 0)
1467       handler.GetEndPoint().OnDialogInfoReceived(info);
1468     return true;
1469   }
1470 
OnSendNOTIFY(SIPHandler & handler,const PObject * data)1471   virtual PString OnSendNOTIFY(SIPHandler & handler, const PObject * data)
1472   {
1473     PStringStream body;
1474     body << "<?xml version=\"1.0\"?>\r\n"
1475             "<dialog-info xmlns=\"urn:ietf:params:xml:ns:dialog-info\" version=\""
1476          << m_dialogNotifyVersion++ << "\" state=\"partial\" entity=\""
1477          << handler.GetAddressOfRecord() << "\">\r\n";
1478 
1479     std::map<PString, SIPDialogNotification>::iterator iter;
1480 
1481     const SIPDialogNotification * info = dynamic_cast<const SIPDialogNotification *>(data);
1482     if (info != NULL) {
1483       if (info->m_state != SIPDialogNotification::Terminated)
1484         m_activeDialogs[info->m_callId] = *info;
1485       else {
1486         iter = m_activeDialogs.find(info->m_callId);
1487         if (iter != m_activeDialogs.end())
1488           m_activeDialogs.erase(iter);
1489 
1490         body << *info;
1491       }
1492     }
1493 
1494     for (iter = m_activeDialogs.begin(); iter != m_activeDialogs.end(); ++iter)
1495       body << iter->second;
1496 
1497     body << "</dialog-info>\r\n";
1498     return body;
1499   }
1500 
1501   unsigned m_dialogNotifyVersion;
1502   std::map<PString, SIPDialogNotification> m_activeDialogs;
1503 };
1504 
1505 static SIPEventPackageFactory::Worker<SIPDialogEventPackageHandler> dialogEventPackageHandler(SIPSubscribe::Dialog);
1506 
1507 #endif // P_EXPAT
1508 
1509 
1510 ///////////////////////////////////////////////////////////////////////////////
1511 
SIPDialogNotification(const PString & entity)1512 SIPDialogNotification::SIPDialogNotification(const PString & entity)
1513   : m_entity(entity)
1514   , m_initiator(false)
1515   , m_state(Terminated)
1516   , m_eventType(NoEvent)
1517   , m_eventCode(0)
1518 {
1519 }
1520 
1521 
GetStateName(States state)1522 PString SIPDialogNotification::GetStateName(States state)
1523 {
1524   static const char * const Names[] = {
1525     "terminated",
1526     "trying",
1527     "proceeding",
1528     "early",
1529     "confirmed"
1530   };
1531   if (state < PARRAYSIZE(Names) && Names[state] != NULL)
1532     return Names[state];
1533 
1534   return psprintf("<%u>", state);
1535 }
1536 
1537 
GetEventName(Events state)1538 PString SIPDialogNotification::GetEventName(Events state)
1539 {
1540   static const char * const Names[] = {
1541     "cancelled",
1542     "rejected",
1543     "replaced",
1544     "local-bye",
1545     "remote-bye",
1546     "error",
1547     "timeout"
1548   };
1549   if (state < PARRAYSIZE(Names) && Names[state] != NULL)
1550     return Names[state];
1551 
1552   return psprintf("<%u>", state);
1553 }
1554 
1555 
OutputParticipant(ostream & strm,const char * name,const SIPDialogNotification::Participant & participant)1556 static void OutputParticipant(ostream & strm, const char * name, const SIPDialogNotification::Participant & participant)
1557 {
1558   if (participant.m_URI.IsEmpty())
1559     return;
1560 
1561   strm << "    <" << name << ">\r\n";
1562 
1563   if (!participant.m_identity.IsEmpty()) {
1564     strm << "      <identity";
1565     if (!participant.m_display.IsEmpty())
1566       strm << " display=\"" << participant.m_display << '"';
1567     strm << '>' << participant.m_identity << "</identity>\r\n";
1568   }
1569 
1570   strm << "      <target uri=\"" << participant.m_URI << "\">\r\n";
1571 
1572   if (participant.m_appearance >= 0)
1573     strm << "        <param pname=\"appearance\" pval=\"" << participant.m_appearance << "\"/>\r\n"
1574             "        <param pname=\"x-line-id\" pval=\"" << participant.m_appearance << "\"/>\r\n";
1575 
1576   if (participant.m_byeless)
1577     strm << "        <param pname=\"sip.byeless\" pval=\"true\"/>\r\n";
1578 
1579   if (participant.m_rendering >= 0)
1580     strm << "        <param pname=\"sip.rendering\" pval=\"" << (participant.m_rendering > 0 ? "yes" : "no") << "\"/>\r\n";
1581 
1582   strm << "      </target>\r\n"
1583        << "    </" << name << ">\r\n";
1584 }
1585 
1586 
PrintOn(ostream & strm) const1587 void SIPDialogNotification::PrintOn(ostream & strm) const
1588 {
1589   if (m_dialogId.IsEmpty())
1590     return;
1591 
1592   // Start dialog XML tag
1593   strm << "  <dialog id=\"" << m_dialogId << '"';
1594   if (!m_callId)
1595     strm << " call-id=\"" << m_callId << '"';
1596   if (!m_local.m_dialogTag)
1597     strm << " local-tag=\"" << m_local.m_dialogTag << '"';
1598   if (!m_remote.m_dialogTag)
1599     strm << " remote-tag=\"" << m_remote.m_dialogTag << '"';
1600   strm << " direction=\"" << (m_initiator ? "initiator" : "receiver") << "\">\r\n";
1601 
1602   // State XML tag & value
1603   strm << "    <state";
1604   if (m_eventType > SIPDialogNotification::NoEvent) {
1605     strm << " event=\"" << GetEventName() << '"';
1606     if (m_eventCode > 0)
1607       strm << " code=\"" << m_eventCode << '"';
1608   }
1609   strm << '>' << GetStateName() << "</state>\r\n";
1610 
1611   // Participant XML tags (local/remopte)
1612   OutputParticipant(strm, "local", m_local);
1613   OutputParticipant(strm, "remote", m_remote);
1614 
1615   // Close out dialog tag
1616   strm << "  </dialog>\r\n";
1617 }
1618 
1619 /////////////////////////////////////////////////////////////////////////
1620 
SIPNotifyHandler(SIPEndPoint & endpoint,const PString & targetAddress,const SIPEventPackage & eventPackage,const SIPDialogContext & dialog)1621 SIPNotifyHandler::SIPNotifyHandler(SIPEndPoint & endpoint,
1622                                    const PString & targetAddress,
1623                                    const SIPEventPackage & eventPackage,
1624                                    const SIPDialogContext & dialog)
1625   : SIPHandler(SIP_PDU::Method_NOTIFY, endpoint, SIPParameters(targetAddress, dialog.GetRemoteURI().AsString()))
1626   , m_eventPackage(eventPackage)
1627   , m_dialog(dialog)
1628   , m_reason(Deactivated)
1629   , m_packageHandler(SIPEventPackageFactory::CreateInstance(eventPackage))
1630 {
1631   m_callID = m_dialog.GetCallID();
1632 }
1633 
1634 
~SIPNotifyHandler()1635 SIPNotifyHandler::~SIPNotifyHandler()
1636 {
1637   delete m_packageHandler;
1638 }
1639 
1640 
CreateTransaction(OpalTransport & trans)1641 SIPTransaction * SIPNotifyHandler::CreateTransaction(OpalTransport & trans)
1642 {
1643   PString state;
1644   if (GetExpire() > 0 && GetState() != Unsubscribing)
1645     state.sprintf("active;expires=%u", GetExpire());
1646   else {
1647     state = "terminated;reason=";
1648     static const char * const ReasonNames[] = {
1649       "deactivated",
1650       "probation",
1651       "rejected",
1652       "timeout",
1653       "giveup",
1654       "noresource"
1655     };
1656     state += ReasonNames[m_reason];
1657   }
1658 
1659   return new SIPNotify(endpoint, trans, m_dialog, m_eventPackage, state, m_body);
1660 }
1661 
1662 
SendRequest(SIPHandler::State state)1663 PBoolean SIPNotifyHandler::SendRequest(SIPHandler::State state)
1664 {
1665   // If times out, i.e. Refreshing, then this is actually a time out unsubscribe.
1666   if (state == Refreshing)
1667     m_reason = Timeout;
1668 
1669   return SIPHandler::SendRequest(state == Refreshing ? Unsubscribing : state);
1670 }
1671 
1672 
WriteSIPHandler(OpalTransport & transport,bool forked)1673 bool SIPNotifyHandler::WriteSIPHandler(OpalTransport & transport, bool forked)
1674 {
1675   m_dialog.SetForking(forked);
1676   bool ok = SIPHandler::WriteSIPHandler(transport, false);
1677   m_dialog.SetForking(false);
1678   return ok;
1679 }
1680 
1681 
SendNotify(const PObject * body)1682 bool SIPNotifyHandler::SendNotify(const PObject * body)
1683 {
1684   if (!LockReadWrite())
1685     return false;
1686 
1687   if (m_packageHandler != NULL)
1688     m_body = m_packageHandler->OnSendNOTIFY(*this, body);
1689   else if (body == NULL)
1690     m_body.MakeEmpty();
1691   else {
1692     PStringStream str;
1693     str << *body;
1694     m_body = str;
1695   }
1696 
1697   UnlockReadWrite();
1698 
1699   return ActivateState(Subscribing);
1700 }
1701 
1702 
1703 /////////////////////////////////////////////////////////////////////////
1704 
SIPPublishHandler(SIPEndPoint & endpoint,const SIPSubscribe::Params & params,const PString & body)1705 SIPPublishHandler::SIPPublishHandler(SIPEndPoint & endpoint,
1706                                      const SIPSubscribe::Params & params,
1707                                      const PString & body)
1708   : SIPHandler(SIP_PDU::Method_PUBLISH, endpoint, params)
1709   , m_parameters(params)
1710   , m_body(body)
1711 {
1712   m_parameters.m_proxyAddress = m_proxy.AsString();
1713 }
1714 
1715 
CreateTransaction(OpalTransport & transport)1716 SIPTransaction * SIPPublishHandler::CreateTransaction(OpalTransport & transport)
1717 {
1718   if (GetState() == Unsubscribing)
1719     return NULL;
1720 
1721   m_parameters.m_expire = m_originalExpireTime;
1722   return new SIPPublish(endpoint,
1723                         transport,
1724                         GetCallID(),
1725                         m_sipETag,
1726                         m_parameters,
1727                         (GetState() == Refreshing) ? PString::Empty() : m_body);
1728 }
1729 
1730 
OnReceivedOK(SIPTransaction & transaction,SIP_PDU & response)1731 void SIPPublishHandler::OnReceivedOK(SIPTransaction & transaction, SIP_PDU & response)
1732 {
1733   PString newETag = response.GetMIME().GetSIPETag();
1734 
1735   if (!newETag.IsEmpty())
1736     m_sipETag = newETag;
1737 
1738   SetExpire(response.GetMIME().GetExpires(m_originalExpireTime));
1739 
1740   SIPHandler::OnReceivedOK(transaction, response);
1741 }
1742 
1743 
1744 ///////////////////////////////////////////////////////////////////////
1745 
1746 static PAtomicInteger DefaultTupleIdentifier(PRandom::Number());
1747 
SIPPresenceInfo(State state)1748 SIPPresenceInfo::SIPPresenceInfo(State state)
1749   : OpalPresenceInfo(state)
1750   , m_tupleId(PString::Printf, "T%08X", ++DefaultTupleIdentifier)
1751 {
1752 }
1753 
1754 
PrintOn(ostream & strm) const1755 void SIPPresenceInfo::PrintOn(ostream & strm) const
1756 {
1757   if (m_entity.IsEmpty())
1758     return;
1759 
1760   if (m_activities.GetSize() > 0)
1761     strm << setfill(',') << m_activities << setfill(' ');
1762   else {
1763     switch (m_state) {
1764       case Unchanged :
1765         strm << "Unchanged";
1766         break;
1767 
1768       case NoPresence :
1769         strm << "Closed";
1770         break;
1771 
1772       default:
1773         if (m_note.IsEmpty())
1774           strm << "Open";
1775         else
1776           strm << m_note;
1777     }
1778   }
1779 }
1780 
1781 // defined in RFC 4480
1782 static const char * const ExtendedSIPActivities[] = {
1783       "appointment",
1784       "away",
1785       "breakfast",
1786       "busy",
1787       "dinner",
1788       "holiday",
1789       "in-transit",
1790       "looking-for-work",
1791       "lunch",
1792       "meal",
1793       "meeting",
1794       "on-the-phone",
1795       "other",
1796       "performance",
1797       "permanent-absence",
1798       "playing",
1799       "presentation",
1800       "shopping",
1801       "sleeping",
1802       "spectator",
1803       "steering",
1804       "travel",
1805       "tv",
1806       "vacation",
1807       "working",
1808       "worship"
1809 };
1810 
AsSIPActivityString(State state,PString & str)1811 bool SIPPresenceInfo::AsSIPActivityString(State state, PString & str)
1812 {
1813   if ((state >= Appointment) && (state <= Worship)) {
1814     str = PString(ExtendedSIPActivities[state - Appointment]);
1815     return true;
1816   }
1817 
1818   return false;
1819 }
1820 
AsSIPActivityString(PString & str) const1821 bool SIPPresenceInfo::AsSIPActivityString(PString & str) const
1822 {
1823   return AsSIPActivityString(m_state, str);
1824 }
1825 
FromSIPActivityString(const PString & str)1826 OpalPresenceInfo::State SIPPresenceInfo::FromSIPActivityString(const PString & str)
1827 {
1828   for (size_t i = 0; i < sizeof(ExtendedSIPActivities)/sizeof(ExtendedSIPActivities[0]);++i) {
1829     if (str == ExtendedSIPActivities[i])
1830       return (State)(Appointment + i);
1831   }
1832 
1833   return NoPresence;
1834 }
1835 
AsXML() const1836 PString SIPPresenceInfo::AsXML() const
1837 {
1838   if (m_entity.IsEmpty() || m_tupleId.IsEmpty()) {
1839     PTRACE(1, "SIP\tCannot encode Presence XML as no address or no id.");
1840     return PString::Empty();
1841   }
1842 
1843   PStringStream xml;
1844 
1845   xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1846          "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" "
1847                   " xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\""
1848                   " xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\""
1849                   " entity=\"" << m_entity << "\">\r\n"
1850          "  <tuple id=\"" << m_tupleId << "\">\r\n"
1851          "    <status>\r\n";
1852   if (m_state != Unchanged)
1853     xml << "      <basic>" << (m_state != NoPresence ? "open" : "closed") << "</basic>\r\n";
1854   xml << "    </status>\r\n"
1855          "    <contact priority=\"1\">";
1856   if (m_contact.IsEmpty())
1857     xml << m_entity;
1858   else
1859     xml << m_contact;
1860   xml << "</contact>\r\n";
1861 
1862   if (!m_note.IsEmpty()) {
1863     //xml << "    <note xml:lang=\"en\">" << PXML::EscapeSpecialChars(m_note) << "</note>\r\n";
1864     xml << "    <note>" << PXML::EscapeSpecialChars(m_note) << "</note>\r\n";
1865   }
1866 
1867   xml << "    <timestamp>" << PTime().AsString(PTime::RFC3339) << "</timestamp>\r\n"
1868          "  </tuple>\r\n";
1869   if (!m_personId.IsEmpty() && (((m_state >= Appointment) && (m_state <= Worship)) || (m_activities.GetSize() > 0))) {
1870     xml << "  <dm:person id=\"p" << m_personId << "\">\r\n"
1871            "    <rpid:activities>\r\n";
1872     bool doneState = false;
1873     for (PINDEX i = 0; i < m_activities.GetSize(); ++i) {
1874       State s = FromString(m_activities[i]);
1875       if (s >= Appointment) {
1876         if (s == m_state)
1877           doneState = true;
1878         xml << "      <rpid:" << ExtendedSIPActivities[s - Appointment] <<"/>\r\n";
1879       }
1880     }
1881     if (!doneState)
1882       xml << "      <rpid:" << ExtendedSIPActivities[m_state - Appointment] <<"/>\r\n";
1883 
1884     xml << "    </rpid:activities>\r\n"
1885            "  </dm:person>\r\n";
1886   }
1887 
1888   xml << "</presence>\r\n";
1889 
1890   return xml;
1891 }
1892 
1893 
1894 #if P_EXPAT
SetNoteFromElement(PXMLElement * element,PString & note)1895 static void SetNoteFromElement(PXMLElement * element, PString & note)
1896 {
1897   PXMLElement * noteElement = element->GetElement("note");
1898   if (noteElement != NULL)
1899     note = noteElement->GetData();
1900 }
1901 
ParseXML(const PString & body,list<SIPPresenceInfo> & infoList,PString & error)1902 bool SIPPresenceInfo::ParseXML(const PString & body,
1903                                list<SIPPresenceInfo> & infoList,
1904                                PString & error)
1905 {
1906   infoList.clear();
1907 
1908   // Check for empty body, if so then is OK, just a ping ...
1909   if (body.IsEmpty()) {
1910     PTRACE(4, "SIPPres\tEmpty body on presence NOTIFY, ignoring");
1911     return true;
1912   }
1913 
1914   static PXML::ValidationInfo const StatusValidation[] = {
1915     { PXML::RequiredElement,            "basic" },
1916     { PXML::EndOfValidationList }
1917   };
1918 
1919   static PXML::ValidationInfo const TupleValidation[] = {
1920     { PXML::RequiredNonEmptyAttribute,  "id" },
1921     { PXML::Subtree,                    "status", { StatusValidation }, 1 },
1922     { PXML::EndOfValidationList }
1923   };
1924 
1925   static PXML::ValidationInfo const ActivitiesValidation[] = {
1926     { PXML::OptionalElement,            "rpid:busy" },
1927     { PXML::EndOfValidationList }
1928   };
1929 
1930   static PXML::ValidationInfo const PersonValidation[] = {
1931     { PXML::Subtree,                    "rpid:activities", { ActivitiesValidation }, 0, 1 },
1932     { PXML::EndOfValidationList }
1933   };
1934 
1935   static PXML::ValidationInfo const PresenceValidation[] = {
1936     { PXML::SetDefaultNamespace,        "urn:ietf:params:xml:ns:pidf" },
1937     { PXML::SetNamespace,               "dm",   { "urn:ietf:params:xml:ns:pidf:data-model" } },
1938     { PXML::SetNamespace,               "rpid", { "urn:ietf:params:xml:ns:pidf:rpid" } },
1939     { PXML::ElementName,                "presence" },
1940     { PXML::RequiredNonEmptyAttribute,  "entity" },
1941     { PXML::Subtree,                    "tuple",  { TupleValidation }, 0 },
1942     { PXML::Subtree,                    "dm:person", { PersonValidation }, 0, 1 },
1943     { PXML::EndOfValidationList }
1944   };
1945 
1946   PXML xml;
1947   if (!xml.LoadAndValidate(body, PresenceValidation, error, PXML::WithNS))
1948     return false;
1949 
1950   // Common info to all tuples
1951   PURL entity;
1952   PXMLElement * rootElement = xml.GetRootElement();
1953   if (!entity.Parse(rootElement->GetAttribute("entity"), "pres")) {
1954     error = "Invalid/unsupported entity";
1955     PTRACE(1, "SIPPres\t" << error << " \"" << rootElement->GetAttribute("entity") << '"');
1956     return false;
1957   }
1958 
1959   SIPPresenceInfo info;
1960   info.m_tupleId.MakeEmpty();
1961 
1962   PTime defaultTimestamp; // Start with "now"
1963 
1964   for (PINDEX idx = 0; idx < rootElement->GetSize(); ++idx) {
1965     PXMLElement * element = dynamic_cast<PXMLElement *>(rootElement->GetElement(idx));
1966     if (element == NULL)
1967       continue;
1968 
1969     if (element->GetName() == "urn:ietf:params:xml:ns:pidf|tuple") {
1970       PXMLElement * tupleElement = element;
1971 
1972       if (!info.m_tupleId.IsEmpty()) {
1973         infoList.push_back(info);
1974         defaultTimestamp = info.m_when - PTimeInterval(0, 1); // One second older
1975         info = SIPPresenceInfo();
1976       }
1977 
1978       info.m_entity = entity;
1979       info.m_tupleId = tupleElement->GetAttribute("id");
1980 
1981       SetNoteFromElement(rootElement, info.m_note);
1982       SetNoteFromElement(tupleElement, info.m_note);
1983 
1984       if ((element = tupleElement->GetElement("status")) != NULL) {
1985         SetNoteFromElement(element, info.m_note);
1986         if ((element = element->GetElement("basic")) != NULL) {
1987           PCaselessString value = element->GetData();
1988           if (value == "open")
1989             info.m_state = Available;
1990           else if (value == "closed")
1991             info.m_state = NoPresence;
1992         }
1993       }
1994 
1995       if ((element = tupleElement->GetElement("contact")) != NULL)
1996         info.m_contact = element->GetData();
1997 
1998       if ((element = tupleElement->GetElement("timestamp")) == NULL || !info.m_when.Parse(element->GetData()))
1999         info.m_when = defaultTimestamp;
2000     }
2001     else if (element->GetName() == "urn:ietf:params:xml:ns:pidf:data-model|person") {
2002       static PConstCaselessString rpid("urn:ietf:params:xml:ns:pidf:rpid|");
2003       PXMLElement * activities = element->GetElement(rpid + "activities");
2004       if (activities == NULL)
2005         continue;
2006 
2007       for (PINDEX i = 0; i < activities->GetSize(); ++i) {
2008         PXMLElement * activity = dynamic_cast<PXMLElement *>(activities->GetElement(i));
2009         if (activity == NULL)
2010           continue;
2011 
2012         PCaselessString name(activity->GetName());
2013         if (name.NumCompare(rpid) != PObject::EqualTo)
2014           continue;
2015 
2016         name.Delete(0, rpid.GetLength());
2017         info.m_activities.AppendString(name);
2018 
2019         State newState = SIPPresenceInfo::FromSIPActivityString(name);
2020         if (newState != SIPPresenceInfo::NoPresence && info.m_state == Available)
2021           info.m_state = newState;
2022       }
2023     }
2024   }
2025 
2026   if (!info.m_tupleId.IsEmpty())
2027     infoList.push_back(info);
2028 
2029   infoList.sort();
2030 
2031   return true;
2032 }
2033 #endif
2034 
2035 /////////////////////////////////////////////////////////////////////////
2036 
SIPMessageHandler(SIPEndPoint & endpoint,const SIPMessage::Params & params)2037 SIPMessageHandler::SIPMessageHandler(SIPEndPoint & endpoint, const SIPMessage::Params & params)
2038   : SIPHandler(SIP_PDU::Method_MESSAGE, endpoint, params)
2039   , m_parameters(params)
2040 {
2041   m_parameters.m_proxyAddress = m_proxy.AsString();
2042 
2043   if (params.m_id.IsEmpty())
2044     m_parameters.m_id = GetCallID();
2045   else
2046     m_callID = params.m_id;   // make sure the transation uses the conversation ID, so we can track it
2047 
2048   m_offlineExpireTime = 0; // No retries for offline, just give up
2049 
2050   SetState(Subscribed);
2051 }
2052 
2053 
CreateTransaction(OpalTransport & transport)2054 SIPTransaction * SIPMessageHandler::CreateTransaction(OpalTransport & transport)
2055 {
2056   if (GetState() == Unsubscribing)
2057     return NULL;
2058 
2059   // If message ID is zero, then it was sent once, don't do it again.
2060   if (m_parameters.m_messageId == 0) {
2061     PTRACE(4, "SIP\tMessage was already sent, not sending again.");
2062     return NULL;
2063   }
2064 
2065   SetExpire(m_originalExpireTime);
2066 
2067   SIPMessage * msg = new SIPMessage(endpoint, transport, m_parameters);
2068   m_parameters.m_localAddress = msg->GetLocalAddress().AsString();
2069   return msg;
2070 }
2071 
2072 
OnFailed(SIP_PDU::StatusCodes reason)2073 void SIPMessageHandler::OnFailed(SIP_PDU::StatusCodes reason)
2074 {
2075   SIPHandler::OnFailed(reason);
2076 
2077   if (m_parameters.m_messageId != 0) {
2078     endpoint.OnMESSAGECompleted(m_parameters, reason);
2079     m_parameters.m_messageId = 0;
2080   }
2081 }
2082 
2083 
OnReceivedOK(SIPTransaction & transaction,SIP_PDU & response)2084 void SIPMessageHandler::OnReceivedOK(SIPTransaction & transaction, SIP_PDU & response)
2085 {
2086   SIPHandler::OnReceivedOK(transaction, response);
2087   endpoint.OnMESSAGECompleted(m_parameters, SIP_PDU::Successful_OK);
2088   m_parameters.m_messageId = 0;
2089 }
2090 
2091 
UpdateParameters(const SIPMessage::Params & params)2092 void SIPMessageHandler::UpdateParameters(const SIPMessage::Params & params)
2093 {
2094   if (params.m_messageId != 0)
2095     m_parameters.m_messageId = params.m_messageId;
2096 
2097   if (!params.m_body.IsEmpty()) {
2098     m_parameters.m_body = params.m_body;
2099     m_parameters.m_contentType = params.m_contentType;
2100   }
2101 }
2102 
2103 
2104 /////////////////////////////////////////////////////////////////////////
2105 
SIPOptionsHandler(SIPEndPoint & endpoint,const SIPOptions::Params & params)2106 SIPOptionsHandler::SIPOptionsHandler(SIPEndPoint & endpoint, const SIPOptions::Params & params)
2107   : SIPHandler(SIP_PDU::Method_OPTIONS, endpoint, params)
2108   , m_parameters(params)
2109 {
2110   m_parameters.m_proxyAddress = m_proxy.AsString();
2111 
2112   m_offlineExpireTime = 0; // No retries for offline, just give up
2113 
2114   /* The "singleton" operation of the OPTION command is executed as though we
2115      are unsubscribing from an existing subscription, so when completed the
2116      handler is disposed of. So, we need to fake the subscribe state. */
2117   SetState(Subscribed);
2118   m_receivedResponse = true;
2119 }
2120 
2121 
CreateTransaction(OpalTransport & transport)2122 SIPTransaction * SIPOptionsHandler::CreateTransaction(OpalTransport & transport)
2123 {
2124   return new SIPOptions(endpoint, transport, GetCallID(), m_parameters);
2125 }
2126 
2127 
OnFailed(SIP_PDU::StatusCodes reason)2128 void SIPOptionsHandler::OnFailed(SIP_PDU::StatusCodes reason)
2129 {
2130   SIP_PDU dummy;
2131   dummy.SetStatusCode(reason);
2132   endpoint.OnOptionsCompleted(m_parameters, dummy);
2133   SIPHandler::OnFailed(reason);
2134 }
2135 
2136 
OnFailed(const SIP_PDU & response)2137 void SIPOptionsHandler::OnFailed(const SIP_PDU & response)
2138 {
2139   endpoint.OnOptionsCompleted(m_parameters, response);
2140   SIPHandler::OnFailed(response.GetStatusCode());
2141 }
2142 
2143 
OnReceivedOK(SIPTransaction & transaction,SIP_PDU & response)2144 void SIPOptionsHandler::OnReceivedOK(SIPTransaction & transaction, SIP_PDU & response)
2145 {
2146   endpoint.OnOptionsCompleted(m_parameters, response);
2147   SIPHandler::OnReceivedOK(transaction, response);
2148 }
2149 
2150 
2151 /////////////////////////////////////////////////////////////////////////
2152 
SIPPingHandler(SIPEndPoint & endpoint,const PURL & to)2153 SIPPingHandler::SIPPingHandler(SIPEndPoint & endpoint, const PURL & to)
2154   : SIPHandler(SIP_PDU::Method_MESSAGE, endpoint, SIPParameters(to.AsString()))
2155 {
2156 }
2157 
2158 
CreateTransaction(OpalTransport & transport)2159 SIPTransaction * SIPPingHandler::CreateTransaction(OpalTransport & transport)
2160 {
2161   if (GetState() == Unsubscribing)
2162     return NULL;
2163 
2164   return new SIPPing(endpoint, transport, GetAddressOfRecord());
2165 }
2166 
2167 
2168 //////////////////////////////////////////////////////////////////
2169 
2170 /* All of the bwlow search loops run through the list with only
2171    PSafeReference rather than PSafeReadOnly, even though they are
2172    reading fields from the handler instances. We can get away with
2173    this becuase the information being tested, e.g. AOR, is constant
2174    for the life of the handler instance, once constructed.
2175 
2176    We need to use PSafeReference as there are some cases where
2177    deadlocks can occur when locked handlers look for information
2178    from other handlers.
2179  */
GetCount(SIP_PDU::Methods meth,const PString & eventPackage) const2180 unsigned SIPHandlersList::GetCount(SIP_PDU::Methods meth, const PString & eventPackage) const
2181 {
2182   unsigned count = 0;
2183   for (PSafePtr<SIPHandler> handler(m_handlersList, PSafeReference); handler != NULL; ++handler)
2184     if (handler->GetState () == SIPHandler::Subscribed &&
2185         handler->GetMethod() == meth &&
2186         (eventPackage.IsEmpty() || handler->GetEventPackage() == eventPackage))
2187       count++;
2188   return count;
2189 }
2190 
2191 
GetAddresses(bool includeOffline,SIP_PDU::Methods meth,const PString & eventPackage) const2192 PStringList SIPHandlersList::GetAddresses(bool includeOffline, SIP_PDU::Methods meth, const PString & eventPackage) const
2193 {
2194   PStringList addresses;
2195   for (PSafePtr<SIPHandler> handler(m_handlersList, PSafeReference); handler != NULL; ++handler)
2196     if ((includeOffline ? handler->GetState () != SIPHandler::Unsubscribed
2197                         : handler->GetState () == SIPHandler::Subscribed) &&
2198         handler->GetMethod() == meth &&
2199         (eventPackage.IsEmpty() || handler->GetEventPackage() == eventPackage))
2200       addresses.AppendString(handler->GetAddressOfRecord().AsString());
2201   return addresses;
2202 }
2203 
2204 
MakeUrlKey(const PURL & aor,SIP_PDU::Methods method,const PString & eventPackage=PString::Empty ())2205 static PString MakeUrlKey(const PURL & aor, SIP_PDU::Methods method, const PString & eventPackage = PString::Empty())
2206 {
2207   PStringStream key;
2208 
2209   key << method << '\n' << aor;
2210 
2211   if (!eventPackage.IsEmpty())
2212     key << '\n' << eventPackage;
2213 
2214   return key;
2215 }
2216 
2217 
2218 /**
2219   * called when a handler is added
2220   */
2221 
Append(SIPHandler * newHandler)2222 void SIPHandlersList::Append(SIPHandler * newHandler)
2223 {
2224   if (newHandler == NULL)
2225     return;
2226 
2227   PWaitAndSignal m(m_extraMutex);
2228 
2229   PSafePtr<SIPHandler> handler = m_handlersList.FindWithLock(*newHandler, PSafeReference);
2230   if (handler == NULL)
2231     handler = m_handlersList.Append(newHandler, PSafeReference);
2232 
2233   // add entry to call to handler map
2234   handler->m_byCallID = m_byCallID.insert(IndexMap::value_type(handler->GetCallID(), handler));
2235 
2236   // add entry to url and package map
2237   handler->m_byAorAndPackage = m_byAorAndPackage.insert(IndexMap::value_type(MakeUrlKey(handler->GetAddressOfRecord(), handler->GetMethod(), handler->GetEventPackage()), handler));
2238 
2239   // add entry to username/realm map
2240   PString realm = handler->GetRealm();
2241   if (realm.IsEmpty())
2242     return;
2243 
2244   PString username = handler->GetUsername();
2245   if (!username.IsEmpty()) {
2246     handler->m_byAuthIdAndRealm = m_byAuthIdAndRealm.insert(IndexMap::value_type(username + '\n' + realm, handler));
2247     PTRACE_IF(4, !handler->m_byAuthIdAndRealm.second, "Duplicate handler for authId=\"" << username << "\", realm=\"" << realm << '"');
2248   }
2249 
2250   username = handler->GetAddressOfRecord().GetUserName();
2251   if (!username.IsEmpty()) {
2252     handler->m_byAorUserAndRealm = m_byAorUserAndRealm.insert(IndexMap::value_type(username + '\n' + realm, handler));
2253     PTRACE_IF(4, !handler->m_byAuthIdAndRealm.second, "Duplicate handler for AOR user=\"" << username << "\", realm=\"" << realm << '"');
2254   }
2255 }
2256 
2257 
2258 /**
2259   * Called when a handler is removed
2260   */
Remove(SIPHandler * handler)2261 void SIPHandlersList::Remove(SIPHandler * handler)
2262 {
2263   if (handler == NULL)
2264     return;
2265 
2266   m_extraMutex.Wait();
2267 
2268   if (m_handlersList.Remove(handler))
2269     RemoveIndexes(handler);
2270 
2271   m_extraMutex.Signal();
2272 }
2273 
2274 
Update(SIPHandler * handler)2275 void SIPHandlersList::Update(SIPHandler * handler)
2276 {
2277   if (handler == NULL)
2278     return;
2279 
2280   m_extraMutex.Wait();
2281 
2282   RemoveIndexes(handler);
2283   Append(handler);
2284 
2285   m_extraMutex.Signal();
2286 }
2287 
2288 
RemoveIndexes(SIPHandler * handler)2289 void SIPHandlersList::RemoveIndexes(SIPHandler * handler)
2290 {
2291   if (handler->m_byAorUserAndRealm.second)
2292     m_byAorUserAndRealm.erase(handler->m_byAorUserAndRealm.first);
2293 
2294   if (handler->m_byAuthIdAndRealm.second)
2295     m_byAuthIdAndRealm.erase(handler->m_byAuthIdAndRealm.first);
2296 
2297   if (handler->m_byAorAndPackage.second)
2298     m_byAorAndPackage.erase(handler->m_byAorAndPackage.first);
2299 
2300   if (handler->m_byCallID.second)
2301     m_byCallID.erase(handler->m_byCallID.first);
2302 }
2303 
2304 
FindBy(IndexMap & by,const PString & key,PSafetyMode mode)2305 PSafePtr<SIPHandler> SIPHandlersList::FindBy(IndexMap & by, const PString & key, PSafetyMode mode)
2306 {
2307   PSafePtr<SIPHandler> ptr;
2308   {
2309     PWaitAndSignal m(m_extraMutex);
2310 
2311     IndexMap::iterator r = by.find(key);
2312     if (r == by.end())
2313       return NULL;
2314 
2315     ptr = r->second;
2316     // If above assignement ends up NULL, then entry in IndexMap was deleted
2317     if (ptr == NULL)
2318       return NULL;
2319   }
2320 
2321   if (ptr && ptr->GetState() != SIPHandler::Unsubscribed)
2322     return ptr.SetSafetyMode(mode) ? ptr : NULL;
2323 
2324   PTRACE(3, "SIP\tHandler " << *ptr << " unsubscribed, awaiting shutdown.");
2325   while (!ptr->ShutDown())
2326     PThread::Sleep(100);
2327 
2328   Remove(ptr);
2329   return NULL;
2330 }
2331 
2332 
FindSIPHandlerByCallID(const PString & callID,PSafetyMode mode)2333 PSafePtr<SIPHandler> SIPHandlersList::FindSIPHandlerByCallID(const PString & callID, PSafetyMode mode)
2334 {
2335   return FindBy(m_byCallID, callID, mode);
2336 }
2337 
2338 
FindSIPHandlerByUrl(const PURL & aor,SIP_PDU::Methods method,PSafetyMode mode)2339 PSafePtr<SIPHandler> SIPHandlersList::FindSIPHandlerByUrl(const PURL & aor, SIP_PDU::Methods method, PSafetyMode mode)
2340 {
2341   return FindBy(m_byAorAndPackage, MakeUrlKey(aor, method), mode);
2342 }
2343 
2344 
FindSIPHandlerByUrl(const PURL & aor,SIP_PDU::Methods method,const PString & eventPackage,PSafetyMode mode)2345 PSafePtr<SIPHandler> SIPHandlersList::FindSIPHandlerByUrl(const PURL & aor, SIP_PDU::Methods method, const PString & eventPackage, PSafetyMode mode)
2346 {
2347   return FindBy(m_byAorAndPackage, MakeUrlKey(aor, method, eventPackage), mode);
2348 }
2349 
2350 
FindSIPHandlerByAuthRealm(const PString & authRealm,PSafetyMode mode)2351 PSafePtr<SIPHandler> SIPHandlersList::FindSIPHandlerByAuthRealm(const PString & authRealm, PSafetyMode mode)
2352 {
2353   // look for a match to realm without users
2354   for (PSafePtr<SIPHandler> handler(m_handlersList, PSafeReference); handler != NULL; ++handler) {
2355     if (handler->GetRealm() == authRealm && handler.SetSafetyMode(mode)) {
2356       PTRACE(4, "SIP\tLocated existing credentials for realm \"" << authRealm << '"');
2357       return handler;
2358     }
2359   }
2360 
2361   return NULL;
2362 }
2363 
2364 
FindSIPHandlerByAuthRealm(const PString & authRealm,const PString & userName,PSafetyMode mode)2365 PSafePtr<SIPHandler> SIPHandlersList::FindSIPHandlerByAuthRealm(const PString & authRealm, const PString & userName, PSafetyMode mode)
2366 {
2367   PSafePtr<SIPHandler> ptr;
2368 
2369   // look for a match to exact user name and realm
2370   if ((ptr = FindBy(m_byAuthIdAndRealm, userName + '\n' + authRealm, mode)) != NULL) {
2371     PTRACE(4, "SIP\tLocated existing credentials for ID \"" << userName << "\" at realm \"" << authRealm << '"');
2372     return ptr;
2373   }
2374 
2375   // look for a match to exact user name and realm
2376   if ((ptr = FindBy(m_byAorUserAndRealm, userName + '\n' + authRealm, mode)) != NULL) {
2377     PTRACE(4, "SIP\tLocated existing credentials for ID \"" << userName << "\" at realm \"" << authRealm << '"');
2378     return ptr;
2379   }
2380 
2381   return NULL;
2382 }
2383 
2384 
2385 /**
2386  * Find the SIPHandler object with the specified registration host.
2387  * For example, in the above case, the name parameter
2388  * could be "sip.seconix.com" or "seconix.com".
2389  */
FindSIPHandlerByDomain(const PString & name,SIP_PDU::Methods meth,PSafetyMode mode)2390 PSafePtr<SIPHandler> SIPHandlersList::FindSIPHandlerByDomain(const PString & name, SIP_PDU::Methods meth, PSafetyMode mode)
2391 {
2392   for (PSafePtr<SIPHandler> handler(m_handlersList, PSafeReference); handler != NULL; ++handler) {
2393     if ( handler->GetMethod() == meth &&
2394          handler->GetState() != SIPHandler::Unsubscribed &&
2395         (handler->GetAddressOfRecord().GetHostName() == name ||
2396          handler->GetAddressOfRecord().GetHostAddress().IsEquivalent(name)) &&
2397          handler.SetSafetyMode(mode))
2398       return handler;
2399   }
2400   return NULL;
2401 }
2402 
2403 
2404 #endif // OPAL_SIP
2405