reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
Dialog.cxx
Go to the documentation of this file.
18#include "rutil/ResipAssert.h"
19#include "rutil/Logger.hxx"
20#include "rutil/Inserter.hxx"
23
24#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
25
26using namespace resip;
27using namespace std;
28
30 : mDum(dum),
31 mDialogSet(ds),
32 mId("INVALID", "INVALID", "INVALID"),
33 mClientSubscriptions(),
34 mServerSubscriptions(),
35 mInviteSession(0),
36 mType(Fake),
37 mRouteSet(),
38 mLocalContact(),
39 mLocalCSeq(0),
40 mRemoteCSeq(0),
41 mPendingRemoteTargetCSeq(0),
42 mRemoteTarget(),
43 mLocalNameAddr(),
44 mRemoteNameAddr(),
45 mCallId(msg.header(h_CallID)),
46 mAppDialog(0),
47 mDestroying(false),
48 mReUseDialogSet(false)
49{
51
52 resip_assert(msg.header(h_CSeq).method() != MESSAGE);
53 resip_assert(msg.header(h_CSeq).method() != REGISTER);
54 resip_assert(msg.header(h_CSeq).method() != PUBLISH);
55
56 mNetworkAssociation.setDum(&dum);
57
58 if (msg.isRequest()) // UAS
59 {
60 const SipMessage& request = msg;
61
62 switch (request.header(h_CSeq).method())
63 {
64 case INVITE:
65 mType = Invitation;
66 break;
67
68 case SUBSCRIBE:
69 case REFER:
70 case NOTIFY:
72 mType = Subscription;
73 break;
74
75 default:
76 mType = Fake;
77 }
78 if (request.exists(h_RecordRoutes))
79 {
80 mRouteSet = request.header(h_RecordRoutes);
81 }
82
83 switch (request.header(h_CSeq).method())
84 {
85 case INVITE:
86 case SUBSCRIBE:
87 case REFER:
88 case NOTIFY:
89 DebugLog ( << "UAS dialog ID creation, DS: " << ds.getId());
90 mId = DialogId(ds.getId(),
91 request.header(h_From).exists(p_tag) ? request.header(h_From).param(p_tag) : Data::Empty);
92
93 mRemoteNameAddr = request.header(h_From);
94 mLocalNameAddr = request.header(h_To);
95 mLocalNameAddr.param(p_tag) = mId.getLocalTag();
96 if (request.exists(h_Contacts) && request.header(h_Contacts).size() == 1)
97 {
98 const NameAddr& contact = request.header(h_Contacts).front();
99 if (isEqualNoCase(contact.uri().scheme(), Symbols::Sips) ||
100 isEqualNoCase(contact.uri().scheme(), Symbols::Sip))
101 {
102 // Store Remote Target
103 mRemoteTarget = contact;
104
105 // Create Local Contact
106 if(mDialogSet.mUserProfile->hasUserAgentCapabilities())
107 {
108 mLocalContact = mDialogSet.mUserProfile->getUserAgentCapabilities();
109 }
110 if(!mDialogSet.mUserProfile->isAnonymous() && mDialogSet.mUserProfile->hasPublicGruu())
111 {
112 mLocalContact.uri() = mDialogSet.mUserProfile->getPublicGruu();
113 }
114 else if(mDialogSet.mUserProfile->isAnonymous() && mDialogSet.mUserProfile->hasTempGruu())
115 {
116 mLocalContact.uri() = mDialogSet.mUserProfile->getTempGruu();
117 }
118 else
119 {
120 if (mDialogSet.mUserProfile->hasOverrideHostAndPort())
121 {
122 mLocalContact.uri() = mDialogSet.mUserProfile->getOverrideHostAndPort();
123 }
124 if(request.header(h_RequestLine).uri().user().empty())
125 {
126 // Contact header will always have a sip: scheme so it's safe to copy
127 // the user part from sip:, sips: and tel: but not from others like urn:
128 // (RFC 8141's NID:NSS has nothing to do with a sip: user's ABNF).
129 // When skipped, mLocalContact.uri().user() keeps whatever
130 // getOverrideHostAndPort() set above (or stays empty) --
131 // e.g. for a urn: To (an incoming request targeting an
132 // emergency-services URN), the local Contact ends up with
133 // no user part at all: "sip:host:port". Legal per RFC 3261
134 // (Contact only needs a valid sip:/sips: URI), and arguably
135 // the only sane choice here, since there is no sip:-shaped
136 // user to carry over from a urn: To.
137 if (request.header(h_To).uri().isUserRelevant())
138 {
139 mLocalContact.uri().user() = request.header(h_To).uri().user();
140 }
141 }
142 else
143 {
144 // Same reasoning as above, for the case where the
145 // Request-URI (rather than To) carries the user to copy
146 // -- e.g. a urn: Request-URI (RFC 5031 emergency call)
147 // also leaves mLocalContact.uri().user() untouched.
148 if (request.header(h_RequestLine).uri().isUserRelevant())
149 {
150 mLocalContact.uri().user() = request.header(h_RequestLine).uri().user();
151 }
152 }
153 const Data& instanceId = mDialogSet.mUserProfile->getInstanceId();
154 if (!contact.uri().exists(p_gr) && !instanceId.empty())
155 {
156 mLocalContact.param(p_Instance) = instanceId;
157 }
158 }
159 if(mDialogSet.mUserProfile->clientOutboundEnabled())
160 {
161 // Add ;ob parm to non-register requests - RFC5626 pg17
162 mLocalContact.uri().param(p_ob);
163 }
164 }
165 else
166 {
167 InfoLog(<< "Got an INVITE or SUBSCRIBE with invalid scheme");
168 InfoLog(<< request);
169 throw Exception("Invalid scheme in request", __FILE__, __LINE__);
170 }
171 }
172 else
173 {
174 InfoLog (<< "Got an INVITE or SUBSCRIBE that doesn't have exactly one contact");
175 InfoLog (<< request);
176 throw Exception("Too many (or no contact) contacts in request", __FILE__, __LINE__);
177 }
178 break;
179 default:
180 break;
181 }
182
183 mRemoteCSeq = request.header(h_CSeq).sequence();
184
185 // This may actually be a UAC dialogset - ie. the case where the first NOTIFY creates the
186 // SUBSCRIPTION dialog, instead of the 200/SUB. If so, then we need to make sure the local
187 // CSeq is correct - it's value may be greator than 1, if the original request (SUBSCRIBE)
188 // got digest challenged.
189 BaseCreator* creator = mDialogSet.getCreator();
190 if(creator)
191 {
192 mLocalCSeq = creator->getLastRequest()->header(h_CSeq).sequence();
193 }
194 else
195 {
196 mLocalCSeq = 1;
197 }
198
199 DebugLog ( << "************** Created Dialog as UAS **************" );
200 DebugLog ( << "mRemoteNameAddr: " << mRemoteNameAddr );
201 DebugLog ( << "mLocalNameAddr: " << mLocalNameAddr );
202 DebugLog ( << "mLocalContact: " << mLocalContact );
203 DebugLog ( << "mRemoteTarget: " << mRemoteTarget );
204 }
205 else if (msg.isResponse())
206 {
207 mId = DialogId(msg);
208 const SipMessage& response = msg;
209 mRemoteNameAddr = response.header(h_To);
210 mLocalNameAddr = response.header(h_From);
211
212 switch (msg.header(h_CSeq).method())
213 {
214 case INVITE:
215 mType = Invitation;
216 break;
217
218 case SUBSCRIBE:
219 case REFER:
220 mType = Subscription;
221 break;
222
223 default:
224 mType = Fake;
225 }
226
227 if (response.exists(h_RecordRoutes))
228 {
229 mRouteSet = response.header(h_RecordRoutes).reverse();
230 }
231
232 switch (response.header(h_CSeq).method())
233 {
234 case INVITE:
235 case SUBSCRIBE:
236 case REFER:
237 if (response.header(h_StatusLine).statusCode() > 100 &&
238 response.header(h_StatusLine).statusCode() < 300)
239 {
240
241 if (response.exists(h_Contacts) && response.header(h_Contacts).size() == 1)
242 {
243 const NameAddr& contact = response.header(h_Contacts).front();
244 if (isEqualNoCase(contact.uri().scheme(), Symbols::Sips) ||
245 isEqualNoCase(contact.uri().scheme(), Symbols::Sip))
246 {
247 BaseCreator* creator = mDialogSet.getCreator();
248 if(0 == creator)
249 {
250 ErrLog(<< "BaseCreator is null for DialogSet");
251 ErrLog(<< response);
252 throw Exception("BaseCreator is null for DialogSet", __FILE__, __LINE__);
253 }
254
255 auto lastRequest = creator->getLastRequest();
256
257 if (!lastRequest ||
258 !lastRequest->exists(h_Contacts) ||
259 lastRequest->header(h_Contacts).empty())
260 {
261 InfoLog(<< "lastRequest does not contain a valid contact");
262 InfoLog(<< response);
263 throw Exception("lastRequest does not contain a valid contact.", __FILE__, __LINE__);
264 }
265 mLocalContact = lastRequest->header(h_Contacts).front();
266 mRemoteTarget = contact;
267 }
268 else
269 {
270 InfoLog (<< "Got an INVITE or SUBSCRIBE with invalid scheme");
271 DebugLog (<< response);
272 throw Exception("Bad scheme in contact in response", __FILE__, __LINE__);
273 }
274 }
275 else
276 {
277 InfoLog (<< "Got an INVITE or SUBSCRIBE that doesn't have exactly one contact");
278 DebugLog (<< response);
279 throw Exception("Too many contacts (or no contact) in response", __FILE__, __LINE__);
280 }
281 break;
282 default:
283 break;
284 }
285 }
286
287 mLocalCSeq = response.header(h_CSeq).sequence();
288 mRemoteCSeq = 0;
289 DebugLog ( << "************** Created Dialog as UAC **************" );
290 DebugLog ( << "mRemoteNameAddr: " << mRemoteNameAddr );
291 DebugLog ( << "mLocalNameAddr: " << mLocalNameAddr );
292 DebugLog ( << "mLocalContact: " << mLocalContact );
293 DebugLog ( << "mRemoteTarget: " << mRemoteTarget );
294 }
295 mDialogSet.addDialog(this);
296 DebugLog ( <<"Dialog::Dialog " << mId);
297}
298
299Dialog::~Dialog()
300{
301 DebugLog ( <<"Dialog::~Dialog() ");
302
303 mDestroying = true;
304
305 while (!mClientSubscriptions.empty())
306 {
307 delete *mClientSubscriptions.begin();
308 }
309
310 while (!mServerSubscriptions.empty())
311 {
312 delete *mServerSubscriptions.begin();
313 }
314
315 delete mInviteSession;
316 mDialogSet.mDialogs.erase(this->getId());
317 delete mAppDialog;
318 if(!mReUseDialogSet)
319 {
320 mDialogSet.possiblyDie();
321 }
322}
323
324const DialogId&
325Dialog::getId() const noexcept
326{
327 return mId;
328}
329
330const NameAddr&
332{
333 return mLocalNameAddr;
334}
335
336const NameAddr&
338{
339 return mLocalContact;
340}
341
342const NameAddr&
344{
345 return mRemoteNameAddr;
346}
347
348const NameAddr&
350{
351 return mRemoteTarget;
352}
353
354const NameAddr&
356{
357 return mPendingRemoteTarget;
358}
359
360const NameAddrs&
361Dialog::getRouteSet() const noexcept
362{
363 return mRouteSet;
364}
365
366void
368{
369 ClientInviteSession* uac = dynamic_cast<ClientInviteSession*>(mInviteSession);
370 if (uac)
371 {
372 uac->cancel();
373 }
374}
375
376void
377Dialog::end(const Data& endReason, const ParserContainer<Token>& endReasons)
378{
379 if (mInviteSession)
380 {
381 // Currently End reasons are only passed to Invite sessions
382 if (endReasons.size() > 0)
383 {
384 mInviteSession->end(endReasons);
385
386 }
387 else if (endReason.size() > 0)
388 {
389 mInviteSession->end(endReason);
390 }
391 else
392 {
393 mInviteSession->end();
394 }
395 }
396
397 // End Subscriptions
398 // !jrm! WARN ClientSubscription and ServerSubscription have access to this dialog and will remove themselves
399 // from the m<client|server>Subscriptions collections in the call to end().
400 for (list<ClientSubscription*>::iterator it(mClientSubscriptions.begin());
401 it != mClientSubscriptions.end();)
402 {
403 ClientSubscription* c = *it;
404 it++;
405 c->end();
406 }
407
408 for (list<ServerSubscription*>::iterator it2(mServerSubscriptions.begin());
409 it2 != mServerSubscriptions.end();)
410 {
411 ServerSubscription* s = *it2;
412 it2++;
413 s->end();
414 }
415}
416
417void
418Dialog::handleTargetRefresh(const SipMessage& msg)
419{
420 if (msg.exists(h_Contacts))
421 {
422 switch (msg.header(h_CSeq).method())
423 {
424 case INVITE:
425 case UPDATE:
426 case SUBSCRIBE: // RFC6665 - Note: target refreshes via NOTIFY requests are handled via
427 // ClientSubscription usage after NOTIFY ordering is confirmed
428 if (msg.isRequest())
429 {
430 // If the RemoteTarget is different, then pend changing it until a success response
431 // is generated by the application
432 // RFC3261 12.2.2 - "If a particular request is accepted by the UAS, all the state changes
433 // associated with it are performed.If the request is rejected, none
434 // of the state changes are performed."
435 if (!(mRemoteTarget == msg.header(h_Contacts).front()))
436 {
437 mPendingRemoteTarget = msg.header(h_Contacts).front();
438 mPendingRemoteTargetCSeq = msg.header(h_CSeq).sequence();
439 }
440 }
441 else if (msg.isResponse() && msg.header(h_StatusLine).statusCode() / 100 == 2)
442 {
443 mRemoteTarget = msg.header(h_Contacts).front();
444 }
445 break;
446
447 default:
448 return;
449 }
450 }
451}
452
453void
454Dialog::handlePendingTargetRefresh(const SipMessage& msg)
455{
456 // If we have a final response (ie: response code >= 200)
457 if (msg.isResponse() && msg.header(h_StatusLine).statusCode() / 100 >= 2)
458 {
459 // And our CSeq matches a pending remote target refresh
460 if (mPendingRemoteTargetCSeq == msg.header(h_CSeq).sequence())
461 {
462 // And the response is a success response, then promote the PendingRemoteTarget to be
463 // the RemoteTarget
464 if (msg.header(h_StatusLine).statusCode() / 100 == 2)
465 {
466 mRemoteTarget = mPendingRemoteTarget;
467 }
468
469 // We have received a final response, let's clear out the pending stuff now
470 mPendingRemoteTargetCSeq = 0;
471 mPendingRemoteTarget = NameAddr();
472 }
473 }
474}
475
476void
478{
479 // !jf! Should be checking for messages with out of order CSeq and rejecting
480
481 DebugLog ( << "Dialog::dispatch: " << msg.brief());
482
483 if(msg.isFromWire())
484 {
485 TransportType receivedTransport = toTransportType(
486 msg.header(h_Vias).front().transport());
487 int keepAliveTime = 0;
488 if(isReliable(receivedTransport))
489 {
490 keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForStream();
491 }
492 else
493 {
494 keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForDatagram();
495 }
496
497 if(keepAliveTime > 0)
498 {
499 mNetworkAssociation.update(msg, keepAliveTime, false /* targetSupportsOutbound */); // target supports outbound is detected in registration responses only
500 }
501 }
502
503 handleTargetRefresh(msg);
504 if (msg.isRequest())
505 {
506 const SipMessage& request = msg;
507 switch (request.header(h_CSeq).method())
508 {
509 case INVITE: // new INVITE
510 if (mInviteSession == 0)
511 {
512 DebugLog ( << "Dialog::dispatch -- Created new server invite session" << msg.brief());
513 mInviteSession = makeServerInviteSession(request);
514 }
515 mInviteSession->dispatch(request);
516 break;
517 //refactor, send bad request for BYE, INFO, CANCEL?
518 case BYE:
519 if (mInviteSession == 0)
520 {
521 InfoLog ( << "Spurious BYE" );
522 return;
523 }
524 else
525 {
526 mInviteSession->dispatch(request);
527 }
528 break;
529 case UPDATE:
530 if (mInviteSession == 0)
531 {
532 InfoLog ( << "Spurious UPDATE" );
533 return;
534 }
535 else
536 {
537 mInviteSession->dispatch(request);
538 }
539 break;
540 case INFO:
541 if (mInviteSession == 0)
542 {
543 InfoLog ( << "Spurious INFO" );
544 return;
545 }
546 else
547 {
548 mInviteSession->dispatch(request);
549 }
550 break;
551 case MESSAGE:
552 if (mInviteSession == 0)
553 {
554 InfoLog ( << "Spurious MESSAGE" );
555 return;
556 }
557 else
558 {
559 mInviteSession->dispatch(request);
560 }
561 break;
562 case OPTIONS:
563 if (mInviteSession == 0)
564 {
565 InfoLog ( << "Spurious OPTIONS" );
566 return;
567 }
568 else
569 {
570 mInviteSession->dispatch(request);
571 }
572 break;
573 case PRACK:
574 if (mInviteSession == 0)
575 {
576 InfoLog ( << "Spurious PRACK" );
577 return;
578 }
579 else
580 {
581 mInviteSession->dispatch(request);
582 }
583 break;
584 case ACK:
585 case CANCEL:
586 if (mInviteSession == 0)
587 {
588 InfoLog (<< "Drop stray ACK or CANCEL in dialog on the floor");
589 DebugLog (<< request);
590 }
591 else
592 {
593 mInviteSession->dispatch(request);
594 }
595 break;
596 case SUBSCRIBE:
597 {
598 ServerSubscription* server = findMatchingServerSub(request);
599 if (server)
600 {
601 server->dispatch(request);
602 }
603 else
604 {
605 if (request.exists(h_Event) && request.header(h_Event).value() == "refer")
606 {
607 InfoLog (<< "Received a subscribe to a non-existent refer subscription: " << request.brief());
608 SipMessage failure;
609 makeResponse(failure, request, 403);
610 mDum.sendResponse(failure);
611 return;
612 }
613 else
614 {
615 if (mDum.checkEventPackage(request))
616 {
617 server = makeServerSubscription(request);
618 mServerSubscriptions.push_back(server);
619 server->dispatch(request);
620 }
621 }
622 }
623 }
624 break;
625 case REFER:
626 {
627 if (!request.exists(h_ReferTo))
628 {
629 InfoLog (<< "Received refer w/out a Refer-To: " << request.brief());
630 SipMessage failure;
631 makeResponse(failure, request, 400);
632 mDum.sendResponse(failure);
633 return;
634 }
635 else
636 {
637 if ((request.exists(h_ReferSub) &&
638 request.header(h_ReferSub).isWellFormed() &&
639 request.header(h_ReferSub).value()=="false") ||
640 (request.exists(h_Requires) &&
641 request.header(h_Requires).find(Token("norefersub"))))
642 {
643 if (mInviteSession == 0)
644 {
645 InfoLog(<< "Received an in dialog refer with no subscription in a non-invite dialog: " << request.brief());
646 SipMessage failure;
647 makeResponse(failure, request, 603);
648 mDum.sendResponse(failure);
649 return;
650 }
651 mInviteSession->referNoSub(msg);
652 }
653 else // else we need a subscription
654 {
655 ServerSubscription* server = findMatchingServerSub(request);
656 ServerSubscriptionHandle serverHandle;
657 if (server)
658 {
659 serverHandle = server->getHandle();
660 server->dispatch(request);
661 }
662 else
663 {
664 server = makeServerSubscription(request);
665 mServerSubscriptions.push_back(server);
666 serverHandle = server->getHandle();
667 server->dispatch(request);
668 }
669
670 if (mInviteSession)
671 {
672 mDum.mInviteSessionHandler->onRefer(mInviteSession->getSessionHandle(), serverHandle, msg);
673 }
674
675 }
676 }
677 }
678 break;
679 case NOTIFY:
680 {
681 ClientSubscription* client = findMatchingClientSub(request);
682 if (client)
683 {
684 client->dispatch(request);
685 }
686 else
687 {
688 BaseCreator* creator = mDialogSet.getCreator();
689 if (creator && (creator->getLastRequest()->header(h_RequestLine).method() == SUBSCRIBE ||
690 creator->getLastRequest()->header(h_RequestLine).method() == REFER))
691 {
692 DebugLog (<< "Making subscription (from creator) request: " << *creator->getLastRequest());
693 ClientSubscription* sub = makeClientSubscription(*creator->getLastRequest());
694 mClientSubscriptions.push_back(sub);
695 sub->dispatch(request);
696 }
697 else
698 {
699 if (mInviteSession != 0 && (!msg.exists(h_Event) || msg.header(h_Event).value() == "refer") &&
700 mDum.getClientSubscriptionHandler("refer")!=0)
701 {
702 DebugLog (<< "Making subscription from NOTIFY: " << msg);
703 ClientSubscription* sub = makeClientSubscription(msg);
704 mClientSubscriptions.push_back(sub);
706 mDum.mInviteSessionHandler->onReferAccepted(mInviteSession->getSessionHandle(), client, msg);
707 sub->dispatch(request);
708 }
709 else
710 {
711 auto response = std::make_shared<SipMessage>();
712 makeResponse(*response, msg, 406);
713 send(response);
714 }
715 }
716 }
717 }
718 break;
719 default:
720 resip_assert(0);
721 return;
722 }
723 }
724 else if (msg.isResponse())
725 {
726 // !jf! There is a substantial change in how this works in teltel-branch
727 // from how it worked in main branch pre merge.
728 // If the response doesn't match a cseq for a request I've sent, ignore
729 // the response
730 {//scope 'r' as it is invalidated below
731 RequestMap::iterator r = mRequests.find(msg.header(h_CSeq).sequence());
732 if (r != mRequests.end())
733 {
734 bool handledByAuth = false;
735 if (mDum.mClientAuthManager.get() &&
736 mDum.mClientAuthManager->handle(*mDialogSet.mUserProfile, *r->second, msg))
737 {
738 InfoLog( << "about to re-send request with digest credentials" << r->second->brief());
739
740 resip_assert (r->second->isRequest());
741
742 mLocalCSeq++;
743 send(r->second);
744 handledByAuth = true;
745 }
746 mRequests.erase(r);
747 if (handledByAuth) return;
748 }
749 }
750
751 const SipMessage& response = msg;
752 int code = response.header(h_StatusLine).statusCode();
753 // If this is a 200 response to the initial request, then store the routeset (if present)
754 BaseCreator* creator = mDialogSet.getCreator();
755 if (creator && (creator->getLastRequest()->header(h_CSeq) == response.header(h_CSeq)) && code >=200 && code < 300)
756 {
757 if (response.exists(h_RecordRoutes))
758 {
759 mRouteSet = response.header(h_RecordRoutes).reverse();
760 }
761 else
762 {
763 // Note: This is code to facilitate Dialog recovery, and works in conjunction with the
764 // makeInviteSession API that takes a DialogSetId parameter.
765 // If our original outbound request contained a to tag, then we are attempting to restore a previously
766 // existing (outside this process) dialog. Some clients will send the routeset to use as Record-Routes
767 // in the response, in which case the code above will store them. However clients are not mandated to
768 // do so, so if our outbound INVITE had routes on it, then store these as the routeset for the dialog.
769 if (creator->getLastRequest()->header(h_To).exists(p_tag) && creator->getLastRequest()->exists(h_Routes))
770 {
771 mRouteSet = creator->getLastRequest()->header(h_Routes);
772 }
773 else
774 {
775 // Ensure that if the route-set in the 200 is empty, then we overwrite any existing route-sets
776 mRouteSet.clear();
777 }
778 }
779 }
780
781 // !jf! should this only be for 2xx responses? !jf! Propose no as an
782 // answer !dcm! what is he on?
783 switch (response.header(h_CSeq).method())
784 {
785 case INVITE:
786 if (mInviteSession == 0)
787 {
788 DebugLog ( << "Dialog::dispatch -- Created new client invite session" << msg.brief());
789
790 mInviteSession = makeClientInviteSession(response);
791 if (mInviteSession)
792 {
793 mInviteSession->dispatch(response);
794 }
795 else
796 {
797 ErrLog( << "Dialog::dispatch -- Unable to create invite session from response" << msg.brief());
798 }
799 }
800 else
801 {
802 mInviteSession->dispatch(response);
803 }
804 break;
805 case BYE:
806 case ACK:
807 case CANCEL:
808 case INFO:
809 case MESSAGE:
810 case UPDATE:
811 case PRACK:
812 case OPTIONS:
813 if (mInviteSession)
814 {
815 mInviteSession->dispatch(response);
816 }
817 // else drop on the floor
818 break;
819
820 case REFER:
821 if(mInviteSession)
822 {
823 if (code >= 300)
824 {
825 mDum.mInviteSessionHandler->onReferRejected(mInviteSession->getSessionHandle(), msg);
826 }
827 else
828 {
830 if (!mInviteSession->mReferSub &&
831 ((msg.exists(h_ReferSub) && msg.header(h_ReferSub).value()=="false") ||
832 !msg.exists(h_ReferSub)))
833 {
834 DebugLog(<< "refer accepted with norefersub");
835 mDum.mInviteSessionHandler->onReferAccepted(mInviteSession->getSessionHandle(), ClientSubscriptionHandle::NotValid(), msg);
836 }
837 // else no need for action - first Notify will cause onReferAccepted to be called
838 }
839 mInviteSession->nitComplete();
840 break;
841 }
842 // fall through, out of dialog refer was sent.
843
844 case SUBSCRIBE:
845 {
846 ClientSubscription* client = findMatchingClientSub(response);
847 if (client)
848 {
849 client->dispatch(response);
850 }
851 else
852 {
854 //a bit of a hack; currently, spurious failure messages may cause callbacks
855 BaseCreator* creator = mDialogSet.getCreator();
856 if (!creator || !creator->getLastRequest()->exists(h_Event))
857 {
858 return;
859 }
860 else
861 {
863 mDum.getClientSubscriptionHandler(creator->getLastRequest()->header(h_Event).value());
864 if (handler)
865 {
866 ClientSubscription* sub = makeClientSubscription(*creator->getLastRequest());
867 mClientSubscriptions.push_back(sub);
868 sub->dispatch(response);
869 }
870 }
871 }
872
873 }
874 break;
875 case NOTIFY:
876 {
877 //Failures are dispatched to all ServerSubsscriptions,
878 //which may not be correct as per RFC 5057.
879
880 int code = msg.header(h_StatusLine).statusCode();
881 if (code >= 300)
882 {
884 mDestroying = true;
885 for (list<ServerSubscription*>::iterator it = mServerSubscriptions.begin();
886 it != mServerSubscriptions.end(); )
887 {
888 ServerSubscription* s = *it;
889 it++;
890 s->dispatch(msg);
891 }
892 mDestroying = false;
893 possiblyDie();
894 }
895 else if (code >= 200)
896 {
897 ServerSubscription* server = findMatchingServerSub(response);
898 if (server)
899 {
900 server->dispatch(response);
901 }
902 }
903 }
904 break;
905 default:
906 resip_assert(0);
907 return;
908 }
909 }
910}
911
913Dialog::findMatchingServerSub(const SipMessage& msg)
914{
915 for (std::list<ServerSubscription*>::iterator i=mServerSubscriptions.begin();
916 i != mServerSubscriptions.end(); ++i)
917 {
918 if ((*i)->matches(msg))
919 {
920 return *i;
921 }
922 }
923 return 0;
924}
925
927Dialog::findMatchingClientSub(const SipMessage& msg)
928{
929 for (std::list<ClientSubscription*>::iterator i=mClientSubscriptions.begin();
930 i != mClientSubscriptions.end(); ++i)
931 {
932 if ((*i)->matches(msg))
933 {
934 return *i;
935 }
936 }
937 return 0;
938}
939
942{
943 if (mInviteSession)
944 {
945 return mInviteSession->getSessionHandle();
946 }
947 else
948 {
950 }
951}
952
953std::vector<ClientSubscriptionHandle>
955{
956 std::vector<ClientSubscriptionHandle> handles;
957
958 for (std::list<ClientSubscription*>::const_iterator i = mClientSubscriptions.begin();
959 i != mClientSubscriptions.end(); ++i)
960 {
961 if ( (*i)->getEventType() == event)
962 {
963 handles.push_back((*i)->getHandle());
964 }
965 }
966 return handles;
967}
968
969std::vector<ServerSubscriptionHandle>
971{
972 std::vector<ServerSubscriptionHandle> handles;
973
974 for (std::list<ServerSubscription*>::const_iterator i = mServerSubscriptions.begin();
975 i != mServerSubscriptions.end(); ++i)
976 {
977 if ( (*i)->getEventType() == event)
978 {
979 handles.push_back((*i)->getHandle());
980 }
981 }
982 return handles;
983}
984
985std::vector<ClientSubscriptionHandle>
987{
988 std::vector<ClientSubscriptionHandle> handles;
989
990 for (std::list<ClientSubscription*>::const_iterator i = mClientSubscriptions.begin();
991 i != mClientSubscriptions.end(); ++i)
992 {
993 handles.push_back((*i)->getHandle());
994 }
995
996 return handles;
997}
998
999std::vector<ServerSubscriptionHandle>
1001{
1002 std::vector<ServerSubscriptionHandle> handles;
1003
1004 for (std::list<ServerSubscription*>::const_iterator i = mServerSubscriptions.begin();
1005 i != mServerSubscriptions.end(); ++i)
1006 {
1007 handles.push_back((*i)->getHandle());
1008 }
1009
1010 return handles;
1011}
1012
1013void
1015{
1016 //Established dialogs are not destroyed by a redirect
1017 if (!mClientSubscriptions.empty() || !mServerSubscriptions.empty())
1018 {
1019 return;
1020 }
1021 if (mInviteSession)
1022 {
1023 ClientInviteSession* cInv = dynamic_cast<ClientInviteSession*>(mInviteSession);
1024 if (cInv)
1025 {
1026 cInv->handleRedirect(msg);
1027 mReUseDialogSet = true; // Set flag so that DialogSet will not be destroyed and new Request can use it
1028 }
1029 }
1030}
1031
1032void
1033Dialog::makeRequest(SipMessage& request, MethodTypes method, bool incrementCSeq)
1034{
1035 RequestLine rLine(method);
1036
1037 rLine.uri() = mRemoteTarget.uri();
1038
1039 request.header(h_RequestLine) = rLine;
1040 request.header(h_To) = mRemoteNameAddr;
1041// request.header(h_To).param(p_tag) = mId.getRemoteTag();
1042 request.header(h_From) = mLocalNameAddr;
1043// request.header(h_From).param(p_tag) = mId.getLocalTag();
1044
1045 request.header(h_CallId) = mCallId;
1046
1047 request.remove(h_RecordRoutes);
1048 request.remove(h_Replaces);
1049
1050 request.remove(h_Contacts);
1051 request.header(h_Contacts).push_front(mLocalContact);
1052
1053 request.header(h_CSeq).method() = method;
1054 request.header(h_MaxForwards).value() = 70;
1055
1056 //must keep old via for cancel
1057 if (method != CANCEL)
1058 {
1059 request.header(h_Routes) = mRouteSet;
1060 request.remove(h_Vias);
1061 Via via;
1062 via.param(p_branch); // will create the branch
1063 request.header(h_Vias).push_front(via);
1064 }
1065 else
1066 {
1067 resip_assert(request.exists(h_Vias));
1068 }
1069
1070 //don't increment CSeq for ACK or CANCEL
1071 if (method != ACK && method != CANCEL)
1072 {
1073 if(incrementCSeq)
1074 {
1075 setRequestNextCSeq(request);
1076 }
1077 }
1078 else
1079 {
1080 // ACK and cancel have a minimal header set
1081 request.remove(h_Accepts);
1082 request.remove(h_AcceptEncodings);
1083 request.remove(h_AcceptLanguages);
1084 request.remove(h_Allows);
1085 request.remove(h_Requires);
1086 request.remove(h_ProxyRequires);
1087 request.remove(h_Supporteds);
1088 // request.header(h_CSeq).sequence() = ?; // Caller should provide original request, or modify CSeq to proper value after calling this method
1089 }
1090
1091 // If method is INVITE then advertise required headers
1092 if(method == INVITE || method == UPDATE)
1093 {
1094 // Add Advertised Capabilities
1095 mDum.setAdvertisedCapabilities(request, mDialogSet.mUserProfile);
1096 }
1097
1098 if (mDialogSet.mUserProfile->isAnonymous())
1099 {
1100 request.remove(h_Privacys);
1101 request.header(h_Privacys).push_back(PrivacyCategory(Symbols::id));
1102 }
1103
1104 DebugLog ( << "Dialog::makeRequest: " << std::endl << std::endl << request );
1105}
1106
1107void
1108Dialog::makeResponse(SipMessage& response, const SipMessage& request, int code)
1109{
1110 resip_assert( code >= 100 );
1111 response.remove(h_Contacts);
1112 if (code < 300 && code > 100)
1113 {
1114 resip_assert(request.isRequest());
1115 resip_assert(request.header(h_RequestLine).getMethod() == INVITE ||
1116 request.header(h_RequestLine).getMethod() == SUBSCRIBE ||
1117 request.header(h_RequestLine).getMethod() == BYE ||
1118 request.header(h_RequestLine).getMethod() == CANCEL ||
1119 request.header(h_RequestLine).getMethod() == REFER ||
1120 request.header(h_RequestLine).getMethod() == MESSAGE ||
1121 request.header(h_RequestLine).getMethod() == NOTIFY ||
1122 request.header(h_RequestLine).getMethod() == INFO ||
1123 request.header(h_RequestLine).getMethod() == OPTIONS ||
1124 request.header(h_RequestLine).getMethod() == PRACK ||
1125 request.header(h_RequestLine).getMethod() == UPDATE
1126 );
1127
1128// assert (request.header(h_RequestLine).getMethod() == CANCEL || // Contact header is not required for Requests that do not form a dialog
1129// request.header(h_RequestLine).getMethod() == BYE ||
1130// request.header(h_Contacts).size() == 1);
1131 Helper::makeResponse(response, request, code, mLocalContact);
1132 response.header(h_To).param(p_tag) = mId.getLocalTag();
1133
1134 if((request.header(h_RequestLine).getMethod() == INVITE ||
1135 request.header(h_RequestLine).getMethod() == PRACK ||
1136 request.header(h_RequestLine).getMethod() == UPDATE)
1137 && code >= 200 && code < 300)
1138 {
1139 // Add Advertised Capabilities
1140 mDum.setAdvertisedCapabilities(response, mDialogSet.mUserProfile);
1141 }
1142 }
1143 else
1144 {
1145 Helper::makeResponse(response, request, code);
1146 response.header(h_To).param(p_tag) = mId.getLocalTag();
1147 }
1148
1149 DebugLog ( << "Dialog::makeResponse: " << std::endl << std::endl << response);
1150}
1151
1152void
1154{
1155 resip_assert(request.isRequest() && request.method() != ACK && request.method() != CANCEL);
1156 request.header(h_CSeq).sequence() = ++mLocalCSeq;
1157}
1158
1160Dialog::makeClientInviteSession(const SipMessage& response)
1161{
1162 InviteSessionCreator* creator = dynamic_cast<InviteSessionCreator*>(mDialogSet.getCreator());
1163 if (!creator)
1164 {
1165 resip_assert(0); // !jf! this maybe can assert by evil UAS
1166 return 0;
1167 }
1168 //return mDum.createAppClientInviteSession(*this, *creator);
1169 return new ClientInviteSession(mDum, *this, creator->getLastRequest(),
1170 creator->getInitialOffer(), creator->getEncryptionLevel(), creator->getServerSubscription());
1171}
1172
1174Dialog::makeClientSubscription(const SipMessage& request)
1175{
1176 return new ClientSubscription(mDum, *this, request);
1177}
1178
1180Dialog::makeServerInviteSession(const SipMessage& request)
1181{
1182 return new ServerInviteSession(mDum, *this, request);
1183}
1184
1186Dialog::makeServerSubscription(const SipMessage& request)
1187{
1188 return new ServerSubscription(mDum, *this, request);
1189}
1190
1191Dialog::Exception::Exception(const Data& msg, const Data& file, int line)
1192 : BaseException(msg, file, line)
1193{
1194}
1195
1196
1197void
1198Dialog::send(std::shared_ptr<SipMessage> msg)
1199{
1200 if (msg->isRequest() && msg->header(h_CSeq).method() != ACK)
1201 {
1202 mRequests[msg->header(h_CSeq).sequence()] = msg;
1203 }
1204 handlePendingTargetRefresh(*msg);
1205 mDum.send(msg);
1206}
1207
1208void
1210{
1211 ClientInviteSession* uac = dynamic_cast<ClientInviteSession*>(mInviteSession);
1212 if (uac)
1213 {
1214 uac->onForkAccepted();
1215 }
1216}
1217
1218void
1219Dialog::possiblyDie()
1220{
1221 if (!mDestroying)
1222 {
1223 if (mClientSubscriptions.empty() &&
1224 mServerSubscriptions.empty() &&
1225 !mInviteSession)
1226 {
1227 mDestroying = true;
1228 mDum.destroy(this);
1229 }
1230 }
1231}
1232
1233void
1234Dialog::flowTerminated()
1235{
1236 // Clear the network association
1237 mNetworkAssociation.clear();
1238
1239 // notify server subscirption dialogs
1240 std::list<ServerSubscription*> tempServerList = mServerSubscriptions; // Create copy since subscription can be deleted
1241 for (std::list<ServerSubscription*>::iterator is=tempServerList.begin();
1242 is != tempServerList.end(); ++is)
1243 {
1244 (*is)->flowTerminated();
1245 }
1246
1247 // notify client subscription dialogs
1248 std::list<ClientSubscription*> tempClientList = mClientSubscriptions; // Create copy since subscription can be deleted
1249 for (std::list<ClientSubscription*>::iterator ic=tempClientList.begin();
1250 ic != tempClientList.end(); ++ic)
1251 {
1252 (*ic)->flowTerminated();
1253 }
1254
1255 // notify invite session dialog
1256 if (mInviteSession)
1257 {
1258 mInviteSession->flowTerminated();
1259 }
1260}
1261
1263resip::operator<<(EncodeStream& strm, const Dialog& dialog)
1264{
1265 strm
1266 << "mClientSubscriptions("
1267 << dialog.mClientSubscriptions.size()
1268 << "), "
1269 << "mServerSubscriptions("
1270 << dialog.mServerSubscriptions.size()
1271 << ")";
1272 return strm;
1273}
1274
1275
1276/* ====================================================================
1277 * The Vovida Software License, Version 1.0
1278 *
1279 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1280 *
1281 * Redistribution and use in source and binary forms, with or without
1282 * modification, are permitted provided that the following conditions
1283 * are met:
1284 *
1285 * 1. Redistributions of source code must retain the above copyright
1286 * notice, this list of conditions and the following disclaimer.
1287 *
1288 * 2. Redistributions in binary form must reproduce the above copyright
1289 * notice, this list of conditions and the following disclaimer in
1290 * the documentation and/or other materials provided with the
1291 * distribution.
1292 *
1293 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1294 * and "Vovida Open Communication Application Library (VOCAL)" must
1295 * not be used to endorse or promote products derived from this
1296 * software without prior written permission. For written
1297 * permission, please contact vocal@vovida.org.
1298 *
1299 * 4. Products derived from this software may not be called "VOCAL", nor
1300 * may "VOCAL" appear in their name, without prior written
1301 * permission of Vovida Networks, Inc.
1302 *
1303 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1304 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1305 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1306 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1307 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1308 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1309 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1310 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1311 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1312 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1313 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1314 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1315 * DAMAGE.
1316 *
1317 * ====================================================================
1318 *
1319 * This software consists of voluntary contributions made by Vovida
1320 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1321 * Inc. For more information on Vovida Networks, Inc., please see
1322 * <http://www.vovida.org/>.
1323 *
1324 */
1325
1326
1327
websocketpp::client< websocketpp::config::asio_client > client
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
std::shared_ptr< SipMessage > getLastRequest() const noexcept
The abstract base-class for all exceptions thrown by resip code.
dcm! – update contact in dialog if required
ClientSubscriptionHandle getHandle()
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
static const Data Empty
Constant that represents a zero-length data.
Definition Data.hxx:882
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
const Data & getLocalTag() const
Definition DialogId.cxx:105
void addDialog(Dialog *)
BaseCreator * getCreator() const noexcept
DialogSetId getId() const
void send(std::shared_ptr< SipMessage > request)
ClientSubscriptionHandler * getClientSubscriptionHandler(const Data &eventType)
void setAdvertisedCapabilities(SipMessage &msg, const std::shared_ptr< UserProfile > &userProfile)
Exception(const Data &msg, const Data &file, int line)
Definition Dialog.cxx:1191
dcm! – kill typedef std::list<DialogId> DialogIdSet;
Definition Dialog.hxx:29
Dialog(DialogUsageManager &dum, const SipMessage &msg, DialogSet &ds)
Definition Dialog.cxx:29
friend class ClientSubscription
Definition Dialog.hxx:90
const NameAddr & getRemoteNameAddr() const noexcept
Definition Dialog.cxx:343
std::vector< ClientSubscriptionHandle > findClientSubscriptions(const Data &event)
Definition Dialog.cxx:954
const NameAddr & getRemoteTarget() const noexcept
Definition Dialog.cxx:349
const NameAddr & getLocalNameAddr() const noexcept
Definition Dialog.cxx:331
void cancel()
Definition Dialog.cxx:367
void redirected(const SipMessage &msg)
Definition Dialog.cxx:1014
const NameAddr & getLocalContact() const noexcept
Definition Dialog.cxx:337
void makeRequest(SipMessage &request, MethodTypes method, bool incrementCSeq=true)
Definition Dialog.cxx:1033
void end(const Data &endReason, const ParserContainer< Token > &endReasons)
Definition Dialog.cxx:377
const DialogId & getId() const noexcept
Definition Dialog.cxx:325
const NameAddrs & getRouteSet() const noexcept
Definition Dialog.cxx:361
void setRequestNextCSeq(SipMessage &request)
Definition Dialog.cxx:1153
friend class ServerSubscription
Definition Dialog.hxx:94
void onForkAccepted()
Definition Dialog.cxx:1209
InviteSessionHandle getInviteSession()
Definition Dialog.cxx:941
std::vector< ServerSubscriptionHandle > getServerSubscriptions()
Definition Dialog.cxx:1000
std::vector< ServerSubscriptionHandle > findServerSubscriptions(const Data &event)
Definition Dialog.cxx:970
void dispatch(const SipMessage &msg)
Definition Dialog.cxx:477
void makeResponse(SipMessage &response, const SipMessage &request, int responseCode)
Definition Dialog.cxx:1108
friend class ClientInviteSession
Definition Dialog.hxx:92
const NameAddr & getPendingRemoteTarget() const
Definition Dialog.cxx:355
std::vector< ClientSubscriptionHandle > getClientSubscriptions()
Definition Dialog.cxx:986
friend class ServerInviteSession
Definition Dialog.hxx:93
void send(std::shared_ptr< SipMessage > msg)
Definition Dialog.cxx:1198
static Handle< T > NotValid()
Definition Handle.hxx:84
static void makeResponse(SipMessage &response, const SipMessage &request, int responseCode, const Data &reason=Data::Empty, const Data &hostname=Data::Empty, const Data &warning=Data::Empty)
Make a response to a provided request.
Definition Helper.cxx:412
ServerSubscriptionHandle getServerSubscription()
const Contents * getInitialOffer() const
DialogUsageManager::EncryptionLevel getEncryptionLevel() const
virtual void onReferRejected(InviteSessionHandle, const SipMessage &msg)=0
called when an REFER message receives a failure response
virtual void onRefer(InviteSessionHandle, ServerSubscriptionHandle, const SipMessage &msg)=0
called when an REFER message is received.
virtual void onReferAccepted(InviteSessionHandle, ClientSubscriptionHandle, const SipMessage &msg)=0
called when an REFER message receives an accepted response
virtual void flowTerminated()
virtual void end(const Data &userReason)
Makes the specific dialog end.
InviteSessionHandle getSessionHandle()
void referNoSub(const SipMessage &msg)
void dispatch(const SipMessage &msg) override
bool isWellFormed() const
Returns true iff this element was parsed successfully, according to the implementation of parse().
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
void setDum(DialogUsageManager *dum)
bool update(const SipMessage &msg, int keepAliveInterval, bool targetSupportsOutbound)
void clear()
clear the mParsers vector
size_t size() const
return the size of the mParsers vector
Represents the "token" element in the RFC 3261 grammar.
Represents the "Request-Line" element in the RFC 3261 grammar.
MethodTypes getMethod() const
const Uri & uri() const
MethodTypes & method()
dcm! – no Subscription State expires parameter generation yet.
void dispatch(const SipMessage &msg) override
void end(TerminateReason reason, const Contents *document=0, int retryAfter=0)
ServerSubscriptionHandle getHandle() const
Represents a SIP message.
bool isRequest() const
Returns true if message is a request, false otherwise.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
bool isResponse() const
Returns true if message is a response, false otherwise.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
bool isFromWire() const
Check if SipMessage came off the wire.
void remove(const HeaderBase &headerType)
Prevents a header field from being present when the message is prepared for sending to a transport.
bool isExternal() const
Check if SipMessage is to be treated as it came off the wire.
resip::MethodTypes method() const
returns the method type of the message
static const char * id
Definition Symbols.hxx:127
static const char * Sips
Definition Symbols.hxx:63
static const char * Sip
Definition Symbols.hxx:62
Represents the "token" element in the RFC 3261 grammar.
Definition Token.hxx:18
bool isUserRelevant() const
Checks if this URI's scheme has sip:/sips:/tel: user-part semantics, i.e.
Definition Uri.cxx:528
Data & scheme()
Definition Uri.hxx:143
bool exists(const Param< Uri > &paramType) const
Definition Uri.cxx:1701
Data & user()
Definition Uri.hxx:42
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
Represents the "via-parm" element in the RFC 3261 grammar.
Definition Via.hxx:18
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
TransportType toTransportType(const resip::Data &transportName)
TransportType
An enumeration of transport protocols.
RequestLineType h_RequestLine
Definition Headers.cxx:347
EncodeStream & operator<<(EncodeStream &strm, const BaseException &e)
bool isReliable(TransportType type)
Returns true if passed in transport type is a reliable transport protocol.
H_CallId h_CallId
Definition Headers.cxx:266
#define EncodeStream