reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
RemoteParticipant.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
8#include "Conversation.hxx"
9#include "UserAgent.hxx"
10#include "DtmfEvent.hxx"
11#include "ReconSubsystem.hxx"
12#include "MediaStackAdapter.hxx"
13
14#include <rutil/ResipAssert.h>
15#include <rutil/Log.hxx>
16#include <rutil/Logger.hxx>
17#include <rutil/DnsUtil.hxx>
18#include <rutil/Random.hxx>
29
31
32#include <utility>
33
34using namespace recon;
35using namespace resip;
36using namespace std;
37
38#define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
39
40/* Technically, there are a range of features that need to be implemented
41 to be fully (S)AVPF compliant.
42 However, it is speculated that (S)AVPF peers will communicate with legacy
43 systems that just fudge the RTP/SAVPF protocol in their SDP. Enabling
44 this define allows such behavior to be tested.
45
46 http://www.ietf.org/mail-archive/web/rtcweb/current/msg01145.html
47 "1) RTCWEB end-point will always signal AVPF or SAVPF. I signalling
48 gateway to legacy will change that by removing the F to AVP or SAVP."
49
50 http://www.ietf.org/mail-archive/web/rtcweb/current/msg04380.html
51*/
52//#define RTP_SAVPF_FUDGE
53
54// UAC / Outbound
56 ConversationManager& conversationManager,
58 RemoteParticipantDialogSet& remoteParticipantDialogSet) :
59 Participant(partHandle, ConversationManager::ParticipantType_Remote, conversationManager),
60 AppDialog(dum),
61 IMParticipantBase(true /* prependSenderInfoToIMs? */),
62 mDum(dum),
63 mDialogSet(remoteParticipantDialogSet),
64 mDialogId(Data::Empty, Data::Empty, Data::Empty),
65 mState(Connecting),
66 mOfferRequired(false),
67 mLocalHold(conversationManager.remoteParticipantInitialHold()),
68 mRemoteHold(false),
69 mTrickleIce(false),
70 mRedirectSuccessCondition(ConversationManager::RedirectSuccessOnConnected),
71 mLocalSdp(0),
72 mRemoteSdp(0),
73 mKeyframeIntervals(conversationManager.keyframeIntervals())
74{
75 InfoLog(<< "RemoteParticipant created (UAC), handle=" << mHandle);
76}
77
78// UAS / Inbound - or forked leg
81 RemoteParticipantDialogSet& remoteParticipantDialogSet) :
82 Participant(ConversationManager::ParticipantType_Remote, conversationManager),
83 AppDialog(dum),
84 IMParticipantBase(false /* prependSenderInfoToIMs? */),
85 mDum(dum),
86 mDialogSet(remoteParticipantDialogSet),
87 mDialogId(Data::Empty, Data::Empty, Data::Empty),
88 mState(Connecting),
89 mOfferRequired(false),
90 mLocalHold(conversationManager.remoteParticipantInitialHold()),
91 mRemoteHold(false),
92 mTrickleIce(false),
93 mRedirectSuccessCondition(ConversationManager::RedirectSuccessOnConnected),
94 mLocalSdp(0),
95 mRemoteSdp(0),
96 mKeyframeIntervals(conversationManager.keyframeIntervals())
97{
98 InfoLog(<< "RemoteParticipant created (UAS or forked leg), handle=" << mHandle);
99}
100
102{
103 if(!mDialogId.getCallId().empty())
104 {
105 mDialogSet.removeDialog(mDialogId);
106 }
107
108 InfoLog(<< "RemoteParticipant destroyed, handle=" << mHandle);
109}
110
111//static const resip::ExtensionHeader h_AlertInfo("Alert-Info");
112
113void
115{
116 initiateRemoteCall(destination, nullptr, std::multimap<resip::Data,resip::Data>());
117}
118
119void
120RemoteParticipant::initiateRemoteCall(const NameAddr& destination, const std::shared_ptr<ConversationProfile>& callingProfile, const std::multimap<resip::Data,resip::Data>& extraHeaders)
121{
122 ParticipantHandle handleId = mHandle;
124
125 auto profile = callingProfile;
126 if (!profile)
127 {
128 DebugLog(<<"initiateRemoteCall: no callingProfile supplied, calling mDialogSet.getConversationProfile()");
129 profile = mDialogSet.getConversationProfile();
130 resip_assert(profile);
131 }
132
133 CallbackSdpReady createAndSendInvite = [this, handleId, &cm, destination, profile, extraHeaders](bool success, std::unique_ptr<SdpContents> offer)
134 {
135 if(!cm.getParticipant(handleId))
136 {
137 WarningLog(<<"handle no longer valid");
138 return;
139 }
140 if(!success)
141 {
142 ErrLog(<<"failed to create an SDP offer");
143 mConversationManager.onParticipantTerminated(mHandle, 500, "Failed to create SDP offer");
144 delete this;
145 return;
146 }
147 auto invitemsg = mDum.makeInviteSession(
148 destination,
149 profile,
150 offer.get(),
151 &mDialogSet);
152
153 std::multimap<resip::Data,resip::Data>::const_iterator it = extraHeaders.begin();
154 for (; it != extraHeaders.end(); it++)
155 {
156 addExtraHeader(invitemsg, it->first, it->second);
157 }
158
159 mDialogSet.sendInvite(std::move(invitemsg));
160
161 // Clear any pending hold/unhold requests since our offer/answer here will handle it
162 if(mPendingRequest.mType == Hold ||
163 mPendingRequest.mType == Unhold)
164 {
165 mPendingRequest.mType = None;
166 }
167
168 // If we are not in delayed media mode, we can start the media setup here
169 if(offer)
170 {
171 // Adjust RTP streams
172 adjustRTPStreams(true);
173
174 // Special case of this call - since call in addToConversation will not work, since we didn't know our bridge port at that time
176 }
177 };
178
179 if(profile && profile->delayedMediaOutboundMode())
180 {
181 createAndSendInvite(true, std::unique_ptr<SdpContents>(nullptr));
182 }
183 else
184 {
185 buildSdpOffer(mLocalHold, createAndSendInvite);
186 }
187}
188
189void
190RemoteParticipant::addExtraHeader(const std::shared_ptr<SipMessage>& invitemsg, const Data& headerName, const Data& headerValue)
191{
192 try
193 {
194 Headers::Type headerType = Headers::getType(headerName.data(), headerName.size());
195 if (headerType == Headers::UNKNOWN)
196 {
197 ExtensionHeader h_Tmp(headerName.c_str());
198 StringCategories& scs = invitemsg->header(h_Tmp);
199 bool alreadyExists = false;
200 for (StringCategories::iterator it = scs.begin(); it != scs.end(); it++)
201 {
202 if (isEqualNoCase(it->value(), headerValue))
203 {
204 alreadyExists = true;
205 break;
206 }
207 }
208 // Only add if not already present
209 if(!alreadyExists)
210 {
211 StringCategory sc(headerValue);
212 scs.push_back(sc);
213 }
214 }
215 else
216 {
217 HeaderFieldValueList newHfvl;
218
219 // If header is a multi header, we will append to back, looking to see if the value we are
220 // about to add is present already or not, if present, it won't add twice.
221 if (Headers::isMulti(headerType))
222 {
223 // Ensure the message appears as an external so HeaderFieldValue inspection will work as expected
224 const HeaderFieldValueList* existingHfvl = invitemsg->getRawHeader(headerType);
225 if (existingHfvl)
226 {
227 Data existingHeaderValue;
228 for (size_t i = 0; i < existingHfvl->getNumHeaderValues(); i++)
229 {
230 existingHfvl->getHeaderValueByIndex(i, existingHeaderValue);
231
232 // If this instance of the header value is different from what we are going to set, then make
233 // sure the header ends up in the final message
234 if (!isEqualNoCase(existingHeaderValue, headerValue))
235 {
236 // Copy header data into new buffer and pass true to push_back to have SipMessage cleanup memory when done
237 char* buffer = new char[existingHeaderValue.size()];
238 memcpy(buffer, existingHeaderValue.data(), existingHeaderValue.size());
239 newHfvl.push_back(buffer, existingHeaderValue.size(), true /* let HeaderFieldValueList own new memory */);
240 }
241 }
242 }
243 }
244
245 // Copy header data into new buffer and pass true to push_back to have SipMessage cleanup memory when done
246 char* buffer = new char[headerValue.size()];
247 memcpy(buffer, headerValue.data(), headerValue.size());
248 newHfvl.push_back(buffer, headerValue.size(), true /* let HeaderFieldValueList own new memory */);
249
250 invitemsg->setRawHeader(&newHfvl, headerType);
251 }
252 }
253 catch (BaseException& ex)
254 {
255 WarningLog(<< "Discarding header '" << headerName << "', invalid value format '" << headerValue << "': " << ex);
256 }
257}
258
259void
261{
262 try
263 {
264 if(mState != Terminating)
265 {
266 stateTransition(Terminating);
268 {
269 // This will send a CANCEL if unanswered, in ForkSelectAutomaticEx mode we don't expect to
270 // manage destruction of forked endpoints manually or individually. This will also end
271 // all the other related conversations.
272 mDialogSet.endIncludeRelated(mHandle);
273 }
274 else
275 {
276 if (mInviteSessionHandle.isValid())
277 {
278 // Make sure any active Refer subscriptions are ended
279 if (mReferSubscriptionHandle.isValid())
280 {
281 mReferSubscriptionHandle->end();
282 }
283 // This will send a BYE.
284 mInviteSessionHandle->end();
285 }
286 else
287 {
288 mDialogSet.end();
289 }
290 }
291 }
292 }
293 catch(BaseException &e)
294 {
295 WarningLog(<< "RemoteParticipant::destroyParticipant exception: " << e);
296 }
297 catch(...)
298 {
299 WarningLog(<< "RemoteParticipant::destroyParticipant unknown exception");
300 }
301}
302
303void
304RemoteParticipant::addToConversation(Conversation* conversation, unsigned int inputGain, unsigned int outputGain)
305{
306 Participant::addToConversation(conversation, inputGain, outputGain);
307 if(mLocalHold && !conversation->shouldHold()) // If we are on hold and we now shouldn't be, then unhold
308 {
309 unhold();
310 }
311}
312
313void
319
320void
322{
323 // Return to Offer a hold sdp if we are not in any conversations, or all the conversations we are in have conditions such that a hold is required
324 bool shouldHold = true;
325 ConversationMap::iterator it;
326 for(it = mConversations.begin(); it != mConversations.end(); it++)
327 {
328 if(!it->second->shouldHold())
329 {
330 shouldHold = false;
331 break;
332 }
333 }
334 setLocalHold(shouldHold);
335}
336
337void
339{
340 if(mLocalHold != _hold)
341 {
342 if(_hold)
343 {
344 hold();
345 }
346 else
347 {
348 unhold();
349 }
350 }
351}
352
353void
354RemoteParticipant::sendInstantMessage(std::unique_ptr<resip::Contents> contents)
355{
356 try
357 {
358 if (mState != Terminating)
359 {
360 if (mInviteSessionHandle.isValid())
361 {
362 mInviteSessionHandle->message(*contents.get());
363 }
364 }
365 }
366 catch (BaseException& e)
367 {
368 WarningLog(<< "RemoteParticipant::sendInstantMessage exception: " << e);
369 }
370 catch (...)
371 {
372 WarningLog(<< "RemoteParticipant::sendInstantMessage unknown exception");
373 }
374}
375
376void
377RemoteParticipant::stateTransition(State state)
378{
379 Data stateName;
380
381 switch(state)
382 {
383 case Connecting:
384 stateName = "Connecting"; break;
385 case Accepted:
386 stateName = "Accepted"; break;
387 case Connected:
388 stateName = "Connected"; break;
389 case Redirecting:
390 stateName = "Redirecting"; break;
391 case Holding:
392 stateName = "Holding"; break;
393 case Unholding:
394 stateName = "Unholding"; break;
395 case Replacing:
396 stateName = "Replacing"; break;
397 case PendingOODRefer:
398 stateName = "PendingOODRefer"; break;
399 case Terminating:
400 stateName = "Terminating"; break;
401 default:
402 stateName = "Unknown: " + Data(state); break;
403 }
404 InfoLog( << "RemoteParticipant::stateTransition of handle=" << mHandle << " to state=" << stateName );
405 mState = state;
406
407 if(mState == Connected && mPendingRequest.mType != None)
408 {
409 PendingRequestType type = mPendingRequest.mType;
410 mPendingRequest.mType = None;
411 switch(type)
412 {
413 case Hold:
414 hold();
415 break;
416 case Unhold:
417 unhold();
418 break;
419 case Redirect:
420 redirect(mPendingRequest.mDestination, mPendingRequest.mRedirectSuccessCondition, mPendingRequest.mRedirectSuccessCondition);
421 break;
422 case RedirectTo:
423 redirectToParticipant(mPendingRequest.mDestInviteSessionHandle, mPendingRequest.mRedirectSuccessCondition);
424 break;
425 case None:
426 break;
427 }
428 }
429}
430
431void
433{
434 mTrickleIce = true;
435}
436
437void
439{
440 try
441 {
442 // Accept SIP call if required
443 if(mState == Connecting && mInviteSessionHandle.isValid())
444 {
445 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
446 if(sis && !sis->isAccepted())
447 {
449 {
450 WarningLog(<< "RemoteParticipant::accept cannot accept call, since no free RTP ports, rejecting instead.");
451 sis->reject(480); // Temporarily Unavailable - no free RTP ports
452 return;
453 }
454 // Clear any pending hold/unhold requests since our offer/answer here will handle it
455 if(mPendingRequest.mType == Hold ||
456 mPendingRequest.mType == Unhold)
457 {
458 mPendingRequest.mType = None;
459 }
460 if(mOfferRequired)
461 {
462 provideOffer(true /* postOfferAccept */);
463 }
464 else if(mPendingOffer)
465 {
466 provideAnswer(*mPendingOffer, true /* postAnswerAccept */, false /* postAnswerAlert */);
467 }
468 else
469 {
470 // It is possible to get here if the app calls alert with early true. There is special logic in
471 // RemoteParticipantDialogSet::accept to handle the case then an alert call followed immediately by
472 // accept. In this case the answer from the alert will be queued waiting on the flow to be ready, and
473 // we need to ensure the accept call is also delayed until the answer completes.
474 mDialogSet.accept(mInviteSessionHandle);
475 stateTransition(Accepted);
476 }
477 }
478 }
479 // Accept Pending OOD Refer if required
480 else if(mState == PendingOODRefer)
481 {
483 }
484 else
485 {
486 WarningLog(<< "RemoteParticipant::accept called in invalid state: " << mState);
487 }
488 }
489 catch(BaseException &e)
490 {
491 WarningLog(<< "RemoteParticipant::accept exception: " << e);
492 }
493 catch(...)
494 {
495 WarningLog(<< "RemoteParticipant::accept unknown exception");
496 }
497}
498
499void
501{
502 try
503 {
504 if(mState == Connecting && mInviteSessionHandle.isValid())
505 {
506 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
507 if(sis && !sis->isAccepted())
508 {
509 if(earlyFlag && mPendingOffer)
510 {
512 {
513 WarningLog(<< "RemoteParticipant::alert cannot alert call with early media, since no free RTP ports, rejecting instead.");
514 sis->reject(480); // Temporarily Unavailable - no free RTP ports
515 return;
516 }
517
518 provideAnswer(*mPendingOffer, false /* postAnswerAccept */, true /* postAnswerAlert */);
519 }
520 else
521 {
522 sis->provisional(180, earlyFlag);
523 }
524 }
525 }
526 else
527 {
528 WarningLog(<< "RemoteParticipant::alert called in invalid state: " << mState);
529 }
530 }
531 catch(BaseException &e)
532 {
533 WarningLog(<< "RemoteParticipant::alert exception: " << e);
534 }
535 catch(...)
536 {
537 WarningLog(<< "RemoteParticipant::alert unknown exception");
538 }
539}
540
541void
542RemoteParticipant::reject(unsigned int rejectCode)
543{
544 try
545 {
546 // Reject UAS Invite Session if required
547 if(mState == Connecting && mInviteSessionHandle.isValid())
548 {
549 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
550 if(sis && !sis->isAccepted())
551 {
552 sis->reject(rejectCode);
553 }
554 }
555 // Reject Pending OOD Refer request if required
556 else if(mState == PendingOODRefer)
557 {
558 rejectPendingOODRefer(rejectCode);
559 }
560 else
561 {
562 WarningLog(<< "RemoteParticipant::reject called in invalid state: " << mState);
563 }
564 }
565 catch(BaseException &e)
566 {
567 WarningLog(<< "RemoteParticipant::reject exception: " << e);
568 }
569 catch(...)
570 {
571 WarningLog(<< "RemoteParticipant::reject unknown exception");
572 }
573}
574
575void
576RemoteParticipant::redirect(NameAddr& destination, unsigned int redirectCode, ConversationManager::RedirectSuccessCondition successCondition)
577{
578 try
579 {
580 // First look for redirect conditions, ie: UAS call that isn't answered yet
581 // In this case we don't care about any pending requests, since we are rejecting the call
582 if (mState == Connecting && mInviteSessionHandle.isValid())
583 {
584 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
585 // If this is a UAS session and we haven't sent a final response yet - then redirect via 302 response
586 if (sis && !sis->isAccepted())
587 {
588 NameAddrs destinations;
589 destinations.push_back(destination);
591 // If provided redirect code is not valid, then set it to the commonly used 302 code
592 if (redirectCode < 300 || redirectCode > 399)
593 {
594 WarningLog(<< "RemoteParticipant::redirect: invalid redirect code of " << redirectCode << " provided, using 302 instead");
595 redirectCode = 302;
596 }
597 sis->redirect(destinations, redirectCode);
598 return;
599 }
600 }
601 // Next look for Refer conditions
602 if(mPendingRequest.mType == None)
603 {
604 if((mState == Connecting || mState == Accepted || mState == Connected) && mInviteSessionHandle.isValid())
605 {
606 if(mInviteSessionHandle->isConnected()) // redirect via blind transfer
607 {
608 mRedirectSuccessCondition = successCondition;
609 mInviteSessionHandle->refer(NameAddr(destination.uri()) /* remove tags */, true /* refersub */);
610 stateTransition(Redirecting);
611 }
612 else
613 {
614 mPendingRequest.mType = Redirect;
615 mPendingRequest.mDestination = destination;
616 mPendingRequest.mRedirectCode = redirectCode;
617 mPendingRequest.mRedirectSuccessCondition = successCondition;
618 }
619 }
620 else if(mState == PendingOODRefer)
621 {
622 redirectPendingOODRefer(destination);
623 }
624 else
625 {
626 mPendingRequest.mType = Redirect;
627 mPendingRequest.mDestination = destination;
628 mPendingRequest.mRedirectCode = redirectCode;
629 mPendingRequest.mRedirectSuccessCondition = successCondition;
630 }
631 }
632 else
633 {
634 WarningLog(<< "RemoteParticipant::redirect error: request pending");
636 }
637 }
638 catch(BaseException &e)
639 {
640 WarningLog(<< "RemoteParticipant::redirect exception: " << e);
641 }
642 catch(...)
643 {
644 WarningLog(<< "RemoteParticipant::redirect unknown exception");
645 }
646}
647
648void
650{
651 try
652 {
653 if(destParticipantInviteSessionHandle.isValid())
654 {
655 if(mPendingRequest.mType == None)
656 {
657 if((mState == Connecting || mState == Accepted || mState == Connected) && mInviteSessionHandle.isValid())
658 {
659 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
660 // If this is a UAS session and we haven't sent a final response yet - then redirect via 302 response
661 if(sis && !sis->isAccepted() && mState == Connecting)
662 {
663 NameAddrs destinations;
664 destinations.push_back(NameAddr(destParticipantInviteSessionHandle->peerAddr().uri())); // ensure we don't get to or from tag by only using the inner uri()
666 sis->redirect(destinations);
667 }
668 else if(mInviteSessionHandle->isConnected()) // redirect via attended transfer (with replaces)
669 {
670 mRedirectSuccessCondition = successCondition;
671 mInviteSessionHandle->refer(NameAddr(destParticipantInviteSessionHandle->peerAddr().uri()) /* remove tags */, destParticipantInviteSessionHandle /* session to replace) */, true /* refersub */);
672 stateTransition(Redirecting);
673 }
674 else
675 {
676 mPendingRequest.mType = RedirectTo;
677 mPendingRequest.mDestInviteSessionHandle = destParticipantInviteSessionHandle;
678 mPendingRequest.mRedirectSuccessCondition = successCondition;
679 }
680 }
681 else
682 {
683 mPendingRequest.mType = RedirectTo;
684 mPendingRequest.mDestInviteSessionHandle = destParticipantInviteSessionHandle;
685 mPendingRequest.mRedirectSuccessCondition = successCondition;
686 }
687 }
688 else
689 {
690 WarningLog(<< "RemoteParticipant::redirectToParticipant error: request pending");
692 }
693 }
694 else
695 {
696 WarningLog(<< "RemoteParticipant::redirectToParticipant error: destParticipant has no valid InviteSession");
698 }
699 }
700 catch(BaseException &e)
701 {
702 WarningLog(<< "RemoteParticipant::redirectToParticipant exception: " << e);
703 }
704 catch(...)
705 {
706 WarningLog(<< "RemoteParticipant::redirectToParticipant unknown exception");
707 }
708}
709
710void
712{
713 try
714 {
715 if(mPendingRequest.mType == None)
716 {
717 bool readyForInfo = (mState == Connected);
718 if((mState == Connecting || mState == Accepted) && contents.getType() == TrickleIceContents::getStaticType())
719 {
720 // we can send INFO in the early media stage, subject to
721 // the conditions in RFC 8840 s4.1
722 readyForInfo = isTrickleIce();
723 }
724 if(readyForInfo)
725 {
726 if(mInviteSessionHandle.isValid())
727 {
728 DebugLog(<<"sending an INFO message");
729 mInviteSessionHandle->info(contents);
730 }
731 else
732 {
733 WarningLog(<< "RemoteParticipant::info error: mInviteSessionHandle not valid");
734 }
735 }
736 else
737 {
738 WarningLog(<< "RemoteParticipant::info error: mState not connected");
739 }
740 }
741 else
742 {
743 WarningLog(<< "RemoteParticipant::info error: request pending");
744 }
745 }
746 catch(BaseException &e)
747 {
748 WarningLog(<< "RemoteParticipant::info exception: " << e);
749 }
750 catch(...)
751 {
752 WarningLog(<< "RemoteParticipant::info unknown exception");
753 }
754}
755
756void
758{
759 mLocalHold=true;
760
761 InfoLog(<< "RemoteParticipant::hold request: handle=" << mHandle);
762
763 try
764 {
765 if(mPendingRequest.mType == None)
766 {
767 if(mState == Connected && mInviteSessionHandle.isValid())
768 {
769 provideOffer(false /* postOfferAccept */, holdPreferExistingSdp());
770 stateTransition(Holding);
771 }
772 else
773 {
774 mPendingRequest.mType = Hold;
775 }
776 }
777 else if(mPendingRequest.mType == Unhold)
778 {
779 mPendingRequest.mType = None; // Unhold pending, so move to do nothing
780 return;
781 }
782 else if(mPendingRequest.mType == Hold)
783 {
784 return; // Hold already pending
785 }
786 else
787 {
788 WarningLog(<< "RemoteParticipant::hold error: request already pending");
789 }
790 }
791 catch(BaseException &e)
792 {
793 WarningLog(<< "RemoteParticipant::hold exception: " << e);
794 }
795 catch(...)
796 {
797 WarningLog(<< "RemoteParticipant::hold unknown exception");
798 }
799}
800
801void
803{
804 mLocalHold=false;
805
806 InfoLog(<< "RemoteParticipant::unhold request: handle=" << mHandle);
807
808 try
809 {
810 if(mPendingRequest.mType == None)
811 {
812 if(mState == Connected && mInviteSessionHandle.isValid())
813 {
814 provideOffer(false /* postOfferAccept */, holdPreferExistingSdp());
815 stateTransition(Unholding);
816 }
817 else
818 {
819 mPendingRequest.mType = Unhold;
820 }
821 }
822 else if(mPendingRequest.mType == Hold)
823 {
824 mPendingRequest.mType = None; // Hold pending, so move do nothing
825 return;
826 }
827 else if(mPendingRequest.mType == Unhold)
828 {
829 return; // Unhold already pending
830 }
831 else
832 {
833 WarningLog(<< "RemoteParticipant::unhold error: request already pending");
834 }
835 }
836 catch(BaseException &e)
837 {
838 WarningLog(<< "RemoteParticipant::unhold exception: " << e);
839 }
840 catch(...)
841 {
842 WarningLog(<< "RemoteParticipant::unhold unknown exception");
843 }
844}
845
846void
848{
849 auto now = std::chrono::steady_clock::now();
850 if(now < (mLastRemoteKeyframeRequest + mKeyframeRequestInterval))
851 {
852 DebugLog(<<"keyframe request ignored, too soon");
853 return;
854 }
855 mLastRemoteKeyframeRequest = now;
856 std::shared_ptr<resip::SdpContents> _sdp = getRemoteSdp();
857 std::set<Data> streamIDs;
858 if(!_sdp)
859 {
860 WarningLog(<<"we need to request a keyframe but there is no peer SDP");
861 }
862 else
863 {
864 streamIDs = _sdp->session().getMediaStreamLabels();
865 if(streamIDs.empty())
866 {
867 WarningLog(<<"peer SDP does not contain a stream ID (RFC 4574)");
868 }
869 }
870 DebugLog(<<"requesting a keyframe for stream(s)");
872 mcc.mediaControl() = MediaControlContents::MediaControl(streamIDs, true);
873 info(mcc);
874}
875
876void
878{
879 if(mKeyframeIntervals.empty())
880 {
881 StackLog(<<"no intervals defined");
882 return;
883 }
884 if(reset)
885 {
886 mCurrentIntervalIndex = 0;
887 }
888 auto now = std::chrono::steady_clock::now();
889 std::chrono::seconds t = std::chrono::seconds(mKeyframeIntervals[mCurrentIntervalIndex]);
890 if(now < (mLastRemoteKeyframeRequest + t))
891 {
892 DebugLog(<<"keyframe timeout ignored, too soon");
893 return;
894 }
896 if(mCurrentIntervalIndex < mKeyframeIntervals.size()-1)
897 {
898 mCurrentIntervalIndex++;
899 }
900}
901
902void
904{
905 // calling RemoteParticipant::unhold() here forces a reINVITE,
906 // even if not currently on hold.
907 // After experimenting with EX90, discovered that both hold()
908 // and unhold() required to establish video in both directions.
909 hold();
910 unhold();
911}
912
913void
915{
916 bool stateChanged = (remoteHold != mRemoteHold);
917 mRemoteHold = remoteHold;
918 if(stateChanged)
919 {
921 }
922}
923
924void
926{
927 stateTransition(PendingOODRefer);
928 mPendingOODReferMsg = referMsg;
929 mPendingOODReferNoSubHandle = ood;
930}
931
932void
934{
935 stateTransition(PendingOODRefer);
936 mPendingOODReferMsg = referMsg;
937 mPendingOODReferSubHandle = ss;
938}
939
940void
942{
943 if(mState == PendingOODRefer)
944 {
945 std::shared_ptr<UserProfile> profile;
946 bool accepted = false;
947 if(mPendingOODReferNoSubHandle.isValid())
948 {
949 mPendingOODReferNoSubHandle->send(mPendingOODReferNoSubHandle->accept(202)); // Accept OOD Refer
950 profile = mPendingOODReferNoSubHandle->getUserProfile();
951 accepted = true;
952 }
953 else if(mPendingOODReferSubHandle.isValid())
954 {
955 mPendingOODReferSubHandle->send(mPendingOODReferSubHandle->accept(202)); // Accept OOD Refer
956 profile = mPendingOODReferSubHandle->getUserProfile();
957 accepted = true;
958 }
959 if(accepted)
960 {
961 // Create offer
962 ParticipantHandle handleId = mHandle;
964 buildSdpOffer(mLocalHold, [this, handleId, &cm, profile](bool success, std::unique_ptr<SdpContents> _offer)
965 {
966 if(!cm.getParticipant(handleId))
967 {
968 WarningLog(<<"handle no longer valid");
969 return;
970 }
971 if(!success)
972 {
973 ErrLog(<<"failed to create an SDP offer");
974 mConversationManager.onParticipantTerminated(mHandle, 500, "Failed to create SDP offer");
975 delete this;
976 return;
977 }
978 SdpContents& offer = *_offer;
979 // Build the Invite
980 auto invitemsg = mDum.makeInviteSessionFromRefer(mPendingOODReferMsg,
981 profile,
982 mPendingOODReferSubHandle, // Note will be invalid if refer no-sub, which is fine
983 &offer,
984 DialogUsageManager::None, //EncryptionLevel
985 0, // Alternative Contents
986 &mDialogSet);
987 mDialogSet.sendInvite(invitemsg);
988
989 adjustRTPStreams(true);
990
991 stateTransition(Connecting);
992 });
993 }
994 else
995 {
996 WarningLog(<< "acceptPendingOODRefer - no valid handles");
997 mConversationManager.onParticipantTerminated(mHandle, 500, "Accepting OOD Refer failed, no valid handles");
998 delete this;
999 }
1000 }
1001}
1002
1003void
1005{
1006 if(mState == PendingOODRefer)
1007 {
1008 if(mPendingOODReferNoSubHandle.isValid())
1009 {
1010 mPendingOODReferNoSubHandle->send(mPendingOODReferNoSubHandle->reject(statusCode));
1011 mConversationManager.onParticipantTerminated(mHandle, statusCode, "OOD Refer NoSub rejected");
1012 }
1013 else if(mPendingOODReferSubHandle.isValid())
1014 {
1015 mPendingOODReferSubHandle->send(mPendingOODReferSubHandle->reject(statusCode));
1016 mConversationManager.onParticipantTerminated(mHandle, statusCode, "OOD Refer rejected");
1017 }
1018 else
1019 {
1020 WarningLog(<< "rejectPendingOODRefer - no valid handles");
1021 mConversationManager.onParticipantTerminated(mHandle, 500, "Rejecting OOD Refer failed, no valid handles");
1022 }
1023 mDialogSet.destroy(); // Will also cause "this" to be deleted
1024 }
1025}
1026
1027void
1029{
1030 if(mState == PendingOODRefer)
1031 {
1032 if(mPendingOODReferNoSubHandle.isValid())
1033 {
1034 auto redirect = mPendingOODReferNoSubHandle->reject(302 /* Moved Temporarily */);
1035 redirect->header(h_Contacts).clear();
1036 redirect->header(h_Contacts).push_back(destination);
1037 mPendingOODReferNoSubHandle->send(redirect);
1038 mConversationManager.onParticipantTerminated(mHandle, 302 /* Moved Temporarily */, "OOD Refer NoSub redirected");
1039 }
1040 else if(mPendingOODReferSubHandle.isValid())
1041 {
1042 auto redirect = mPendingOODReferSubHandle->reject(302 /* Moved Temporarily */);
1043 redirect->header(h_Contacts).clear();
1044 redirect->header(h_Contacts).push_back(destination);
1045 mPendingOODReferSubHandle->send(redirect);
1046 mConversationManager.onParticipantTerminated(mHandle, 302 /* Moved Temporarily */, "OOD Refer redirectred");
1047 }
1048 else
1049 {
1050 WarningLog(<< "redirectPendingOODRefer - no valid handles");
1051 mConversationManager.onParticipantTerminated(mHandle, 500, "Redirecting OOD Refer failed, no valid handles");
1052 }
1053 mDialogSet.destroy(); // Will also cause "this" to be deleted
1054 }
1055}
1056
1057void
1059{
1060 unsigned int code = 400; // Bad Request - default if for some reason a valid sipfrag is not present
1061
1062 mReferSubscriptionHandle = h;
1063
1064 SipFrag* frag = dynamic_cast<SipFrag*>(notify.getContents());
1065 if (frag)
1066 {
1067 // Get StatusCode from SipFrag
1068 if (frag->message().isResponse())
1069 {
1070 code = frag->message().header(h_StatusLine).statusCode();
1071 }
1072 }
1073
1074 // Check if response code in SipFrag meets our completion conditions
1075 if (mRedirectSuccessCondition == ConversationManager::RedirectSuccessOnTrying && code == 100)
1076 {
1077 if (mState == Redirecting)
1078 {
1080 h->end(); // We don't care about any more updates, end the implied subscription
1081 stateTransition(Connected);
1082 }
1083 }
1084 else if ((mRedirectSuccessCondition == ConversationManager::RedirectSuccessOnTrying ||
1085 mRedirectSuccessCondition == ConversationManager::RedirectSuccessOnRinging) &&
1086 code >= 180 && code <= 189)
1087 {
1088 if (mState == Redirecting)
1089 {
1091 h->end(); // We don't care about any more updates, end the implied subscription
1092 stateTransition(Connected);
1093 }
1094 }
1095 else if(code >= 200 && code < 300)
1096 {
1097 if(mState == Redirecting)
1098 {
1100 h->end(); // We don't care about any more updates, end the implied subscription - shouldn't be required here, since 200's are normally sent with subscription state terminated
1101 stateTransition(Connected);
1102 }
1103 }
1104 else if(code >= 300)
1105 {
1106 if(mState == Redirecting)
1107 {
1109 stateTransition(Connected);
1110 }
1111 }
1112}
1113
1114void
1115RemoteParticipant::onLocalIceCandidate(const resip::Data& candidate, unsigned int lineIndex, const resip::Data& mid)
1116{
1117 // FIXME - ensure we do this on correct thread
1118
1119 shared_ptr<SdpContents> localSdp = getLocalSdp();
1120 if(!localSdp)
1121 {
1122 localSdp = mLocalSdpGathering;
1123 }
1124 if(!localSdp)
1125 {
1126 WarningLog(<<"localSdp not available, can't create ICE SDP fragment");
1127 return;
1128 }
1129
1130 // FIXME - if we are waiting for a previous INFO to be confirmed,
1131 // aggregate the candidates into a vector and send them in bulk
1132
1133 auto ice = localSdp->session().makeIceFragment(Data(candidate),
1134 lineIndex, mid);
1135 if(ice.get())
1136 {
1137 StackLog(<<"about to send " << *ice);
1138 info(*ice);
1139 }
1140 else
1141 {
1142 WarningLog(<<"failed to create ICE fragment for mid: " << mid);
1143 }
1144}
1145
1146void
1147RemoteParticipant::provideOffer(bool postOfferAccept, bool preferExistingSdp)
1148{
1149 resip_assert(mInviteSessionHandle.isValid());
1150
1151 ParticipantHandle handleId = mHandle;
1153 buildSdpOffer(mLocalHold,[this, handleId, &cm, postOfferAccept](bool success, std::unique_ptr<SdpContents> offer)
1154 {
1155 if(!cm.getParticipant(handleId))
1156 {
1157 WarningLog(<<"handle no longer valid");
1158 return;
1159 }
1160 if(!success)
1161 {
1162 ErrLog(<<"buildSdpOffer failed");
1163 mConversationManager.onParticipantTerminated(mHandle, 500, "Failed to create SDP offer");
1164 delete this;
1165 return;
1166 }
1167 mDialogSet.provideOffer(std::move(offer), mInviteSessionHandle, postOfferAccept);
1168 mOfferRequired = false;
1169 }, preferExistingSdp);
1170}
1171
1172void
1173RemoteParticipant::provideAnswer(const SdpContents& offer, bool postAnswerAccept, bool postAnswerAlert)
1174{
1175 resip_assert(mInviteSessionHandle.isValid());
1177 buildSdpAnswer(offer, [this, h, postAnswerAccept, postAnswerAlert](bool answerOk, std::unique_ptr<SdpContents> answer)
1178 {
1179 if(!h.isValid())
1180 {
1181 WarningLog(<<"handle no longer valid");
1182 return;
1183 }
1184 if(answerOk)
1185 {
1186 mDialogSet.provideAnswer(std::move(answer), mInviteSessionHandle, postAnswerAccept, postAnswerAlert);
1187 if(postAnswerAccept && mState == Replacing)
1188 {
1189 stateTransition(Connecting);
1190 }
1191 }
1192 else
1193 {
1194 ErrLog(<<"buildSdpAnswer failed");
1195 mInviteSessionHandle->reject(488);
1196 }
1197 mPendingOffer.reset();
1198 });
1199}
1200
1201void
1203{
1204 ConversationMap temp = mConversations; // Copy since we may end up being destroyed
1205 ConversationMap::iterator it;
1206 for(it = temp.begin(); it != temp.end(); it++)
1207 {
1208 it->second->destroy();
1209 }
1210}
1211
1213{
1214 // The dialogset is being cancelled, we are now terminating
1215 stateTransition(Terminating);
1216
1217 ConversationMap::iterator it;
1218 for (it = mConversations.begin(); it != mConversations.end(); it++)
1219 {
1220 it->second->unregisterParticipant(this);
1221 }
1222 mConversations.clear();
1223
1224 // It can take up to 32 seconds for a cancelled leg Dialog/DialogSet to actually get destroyed, we
1225 // don't want to make recon users wait this time, so signal the participant as destroyed immediately
1226 // and let DUM handle the rest in the background.
1227 if (mHandle != 0)
1228 {
1231 setHandle(0); // unregister from Conversation Manager
1232 }
1233}
1234
1235void
1240
1241void
1243{
1244 InfoLog(<< "setLocalSdp: handle=" << mHandle << ", localSdp=" << sdp);
1245 mLocalSdp.reset(new SdpContents(sdp));
1246 mLocalSdpGathering.reset();
1247}
1248
1249void
1251{
1252 InfoLog(<< "setRemoteSdp: handle=" << mHandle << ", remoteSdp=" << sdp);
1253 mRemoteSdp.reset(new SdpContents(sdp));
1254 if(answer && mDialogSet.getProposedSdp())
1255 {
1256 mLocalSdp = mDialogSet.getProposedSdp();
1257 }
1258}
1259
1260void
1262{
1263 InfoLog(<< "setLocalSdpGathering: handle=" << mHandle << ", localSdpGathering=" << sdp);
1264 mLocalSdpGathering.reset(new SdpContents(sdp));
1265}
1266
1267void
1268RemoteParticipant::replaceWithParticipant(Participant* _replacingParticipant)
1269{
1270 RemoteParticipant* replacingParticipant = dynamic_cast<RemoteParticipant*>(_replacingParticipant);
1271 // Copy our local hold setting to the replacing participant to replace us
1272 replacingParticipant->mLocalHold = mLocalHold;
1273
1274 // We are about to adjust the participant handle of the replacing participant to ours
1275 // ensure that the mapping is also adjusted in the replacing participants dialog set
1276 if(replacingParticipant->mHandle == replacingParticipant->mDialogSet.getActiveRemoteParticipantHandle())
1277 {
1278 replacingParticipant->mDialogSet.setActiveRemoteParticipantHandle(mHandle);
1279 }
1280
1281 Participant::replaceWithParticipant(replacingParticipant);
1282}
1283
1284void
1286{
1287 InfoLog(<< "onNewSession(Client): handle=" << mHandle << ", " << msg.brief());
1288 mInviteSessionHandle = h->getSessionHandle();
1289 mDialogId = getDialogId();
1290}
1291
1292void
1294{
1295 InfoLog(<< "onNewSession(Server): handle=" << mHandle << ", " << msg.brief());
1296
1297 mInviteSessionHandle = h->getSessionHandle();
1298 mDialogId = getDialogId();
1299
1300 // First check if this INVITE is to replace an existing session
1301 if(msg.exists(h_Replaces))
1302 {
1303 pair<InviteSessionHandle, int> presult;
1304 presult = mDum.findInviteSession(msg.header(h_Replaces));
1305 if(!(presult.first == InviteSessionHandle::NotValid()))
1306 {
1307 RemoteParticipant* participantToReplace = dynamic_cast<RemoteParticipant *>(presult.first->getAppDialog().get());
1308 InfoLog(<< "onNewSession(Server): handle=" << mHandle << ", to replace handle=" << participantToReplace->getParticipantHandle() << ", " << msg.brief());
1309
1310 // Assume Participant Handle of old call
1311 participantToReplace->replaceWithParticipant(this); // adjust conversation mappings
1312
1313 // Session to replace was found - end old session and flag to auto-answer this session after SDP offer-answer is complete
1314 participantToReplace->destroyParticipant();
1315 stateTransition(Replacing);
1316 return;
1317 }
1318 }
1319
1320 // Check for Auto-Answer indication - support draft-ietf-answer-mode-01
1321 // and Answer-After parameter of Call-Info header
1322 ConversationProfile* profile = dynamic_cast<ConversationProfile*>(h->getUserProfile().get());
1323 bool autoAnswer = false;
1324 if(profile)
1325 {
1326 bool autoAnswerRequired;
1327 autoAnswer = profile->shouldAutoAnswer(msg, &autoAnswerRequired);
1328 if(!autoAnswer && autoAnswerRequired) // If we can't autoAnswer but it was required, we must reject the call
1329 {
1330 WarningCategory warning;
1331 warning.hostname() = DnsUtil::getLocalHostName();
1332 warning.code() = 399; /* Misc. */
1333 warning.text() = "automatic answer forbidden";
1334 setHandle(0); // Don't generate any callbacks for this rejected invite
1335 h->reject(403 /* Forbidden */, &warning);
1336 return;
1337 }
1338 }
1339 else
1340 {
1341 WarningLog(<<"bypassing logic for Auto-Answer");
1342 }
1343
1344 // notify of new participant
1345 notifyIncomingParticipant(msg, autoAnswer, *profile);
1346}
1347
1348void
1350{
1351 if(mHandle) mConversationManager.onIncomingParticipant(mHandle, msg, autoAnswer, conversationProfile);
1352}
1353
1354void
1356{
1357 stateTransition(Terminating);
1358 InfoLog(<< "onFailure: handle=" << mHandle << ", " << msg.brief());
1359 // If ForkSelectMode is automatic, then ensure we destory any conversations, except the original
1363 {
1365 }
1366}
1367
1368void
1370{
1371 InfoLog(<< "onEarlyMedia: handle=" << mHandle << ", " << msg.brief());
1372 if(!mDialogSet.isStaleFork(getDialogId()))
1373 {
1374 setRemoteSdp(sdp, true);
1376 if(sdp.session().isTrickleIceSupported())
1377 {
1379 }
1380 }
1381}
1382
1383void
1385{
1386 InfoLog(<< "onProvisional: handle=" << mHandle << ", " << msg.brief());
1387 resip_assert(msg.header(h_StatusLine).responseCode() != 100);
1388
1389 if(!mDialogSet.isStaleFork(getDialogId()))
1390 {
1392 }
1393}
1394
1395void
1397{
1398 if(getLocalSdp() == 0)
1399 {
1400 WarningLog(<<"no local SDP yet");
1401 }
1402 if(getRemoteSdp() == 0)
1403 {
1404 WarningLog(<<"no remote SDP yet");
1405 }
1406 ConversationMap::const_iterator it;
1407 for (it = mConversations.begin(); it != mConversations.end(); it++)
1408 {
1409 Conversation& conversation = *it->second;
1410 conversation.confirmParticipant(this);
1411 DebugLog(<<"confirmed participant " << mHandle << " conversation " << conversation.getHandle());
1412 }
1413}
1414
1415void
1417{
1418 InfoLog(<< "onConnected(Client): handle=" << mHandle << ", " << msg.brief());
1419
1420 // Check if this is the first leg in a potentially forked call to send a 200
1421 if(!mDialogSet.isUACConnected())
1422 {
1424
1425 mDialogSet.setUACConnected(getDialogId(), mHandle);
1426 stateTransition(Connected);
1428 }
1429 else
1430 {
1431 DebugLog(<<"ending forked call leg, another leg already answered");
1432 // We already have a 200 response - send a BYE to this leg
1433 h->end();
1434 }
1435}
1436
1437void
1439{
1440 InfoLog(<< "onConnected: handle=" << mHandle << ", " << msg.brief());
1441 stateTransition(Connected);
1442 if(msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
1443 {
1445 }
1446}
1447
1448void
1450{
1451 InfoLog(<< "onConnectedConfirmed: handle=" << mHandle << ", " << msg.brief());
1453 stateTransition(Connected);
1455}
1456
1457void
1459{
1460 WarningLog(<< "onStaleCallTimeout: handle=" << mHandle);
1461}
1462
1463void
1465{
1466 stateTransition(Terminating);
1467 Data callbackReason;
1468 switch(reason)
1469 {
1471 callbackReason = "RemoteBye";
1472 InfoLog(<< "onTerminated: handle=" << mHandle << ", received a BYE from peer");
1473 break;
1475 callbackReason = "RemoteCancel";
1476 InfoLog(<< "onTerminated: handle=" << mHandle << ", received a CANCEL from peer");
1477 break;
1479 callbackReason = "Rejected";
1480 InfoLog(<< "onTerminated: handle=" << mHandle << ", received a rejection from peer");
1481 break;
1483 callbackReason = "LocalBye";
1484 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended locally via BYE");
1485 break;
1487 callbackReason = "LocalCancel";
1488 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended locally via CANCEL");
1489 break;
1491 callbackReason = "Replaced";
1492 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to being replaced");
1493 break;
1495 callbackReason = "Referred";
1496 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to being reffered");
1497 break;
1499 callbackReason = "Error";
1500 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to an error");
1501 break;
1503 callbackReason = "Timeout";
1504 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to a timeout");
1505 break;
1506 default:
1507 resip_assert(false);
1508 break;
1509 }
1510 unsigned int statusCode = 0;
1511 if(msg)
1512 {
1513 if(msg->isResponse())
1514 {
1515 statusCode = msg->header(h_StatusLine).responseCode();
1516 }
1517 if (msg->exists(h_Reasons) && msg->header(h_Reasons).size() > 0)
1518 {
1519 // Grab first reason only
1520 callbackReason += ": ";
1521 callbackReason += Data::from(msg->header(h_Reasons).front());
1522 }
1523 }
1524
1525 // If this is a referred call and the refer is still around - then switch back to referrer (ie. failed transfer recovery)
1526 if(mHandle && mReferringAppDialog.isValid())
1527 {
1528 RemoteParticipant* participant = (RemoteParticipant*)mReferringAppDialog.get();
1529
1530 replaceWithParticipant(participant); // adjust conversation mappings
1531 if(participant->getParticipantHandle())
1532 {
1533 participant->adjustRTPStreams();
1534 return;
1535 }
1536 }
1537
1538 // Ensure terminating party is from answered fork before generating event
1539 if(!mDialogSet.isStaleFork(getDialogId()))
1540 {
1541 if(mHandle) mConversationManager.onParticipantTerminated(mHandle, statusCode, callbackReason);
1542 }
1543}
1544
1545void
1547{
1548 InfoLog(<< "onRedirected: handle=" << mHandle << ", " << msg.brief());
1549}
1550
1551void
1553{
1554 InfoLog(<< "onAnswer: handle=" << mHandle << ", " << msg.brief());
1555
1556 // Ensure answering party is from answered fork before generating event
1557 if(!mDialogSet.isStaleFork(getDialogId()))
1558 {
1559 setRemoteSdp(sdp, true);
1561 }
1562 stateTransition(Connected); // This is valid until PRACK is implemented
1563}
1564
1565void
1567{
1568 InfoLog(<< "onOffer: handle=" << mHandle << ", " << msg.brief());
1569 if(mState == Connecting && mInviteSessionHandle.isValid())
1570 {
1571 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
1572 if(sis && !sis->isAccepted())
1573 {
1574 // Don't set answer now - store offer and set when needed - so that sendHoldSdp() can be calculated at the right instant
1575 // we need to allow time for app to add to a conversation before alerting(with early flag) or answering
1576 mPendingOffer = std::unique_ptr<SdpContents>(static_cast<SdpContents*>(offer.clone()));
1577 return;
1578 }
1579 }
1580
1582 {
1583 WarningLog(<< "RemoteParticipant::onOffer cannot continue due to no free RTP ports, rejecting offer.");
1584 h->reject(480); // Temporarily Unavailable
1585 }
1586 else
1587 {
1588 provideAnswer(offer, mState==Replacing /* postAnswerAccept */, false /* postAnswerAlert */);
1589 }
1590}
1591
1592void
1594{
1595 InfoLog(<< "onOfferRequired: handle=" << mHandle << ", " << msg.brief());
1596 // We are being asked to provide SDP to the remote end - we should no longer be considering that
1597 // remote end wants us to be on hold
1598 setRemoteHold(false);
1599
1600 if(mState == Connecting && !h->isAccepted())
1601 {
1602 // If we haven't accepted yet - delay providing the offer until accept is called (this allows time
1603 // for a localParticipant to be added before generating the offer)
1604 mOfferRequired = true;
1605 }
1606 else
1607 {
1609 {
1610 WarningLog(<< "RemoteParticipant::onOfferRequired cannot continue due to no free RTP ports, rejecting offer request.");
1611 h->reject(480); // Temporarily Unavailable
1612 }
1613 else
1614 {
1615 provideOffer(mState == Replacing /* postOfferAccept */);
1616 if(mState == Replacing)
1617 {
1618 stateTransition(Connecting);
1619 }
1620 }
1621 }
1622}
1623
1624void
1626{
1627 if(msg)
1628 {
1629 InfoLog(<< "onOfferRejected: handle=" << mHandle << ", " << msg->brief());
1630 }
1631 else
1632 {
1633 InfoLog(<< "onOfferRejected: handle=" << mHandle);
1634 }
1635}
1636
1637void
1639{
1640 InfoLog(<< "onOfferRequestRejected: handle=" << mHandle << ", " << msg.brief());
1641 resip_assert(0); // We never send a request for an offer (ie. Invite with no SDP)
1642}
1643
1644void
1646{
1647 InfoLog(<< "onRemoteSdpChanged: handle=" << mHandle << ", " << msg.brief());
1648 setRemoteSdp(sdp);
1650}
1651
1652bool
1654{
1655 InfoLog(<<"onMediaControlEvent: not implemented by this ConversationManager");
1656 return false;
1657}
1658
1659bool
1661{
1662 InfoLog(<<"onTrickleIce: not implemented by this ConversationManager");
1663 return false;
1664}
1665
1666void
1668{
1669 InfoLog(<< "onInfo: handle=" << mHandle << ", " << msg.brief());
1670 if(mHandle)
1671 {
1672 bool accepted = false;
1673
1674 DtmfPayloadContents* dtmfContents = dynamic_cast<DtmfPayloadContents*>(msg.getContents());
1675 if(dtmfContents)
1676 {
1677 DtmfPayloadContents::DtmfPayload& payload = dtmfContents->dtmfPayload();
1679 session->acceptNIT();
1680 accepted = true;
1681 }
1682
1683 MediaControlContents* mediaControlContents = dynamic_cast<MediaControlContents*>(msg.getContents());
1684 if(mediaControlContents)
1685 {
1686 MediaControlContents::MediaControl& payload = mediaControlContents->mediaControl();
1687 if(onMediaControlEvent(payload))
1688 {
1689 session->acceptNIT();
1690 accepted = true;
1691 }
1692 }
1693
1694 TrickleIceContents* trickleIceContents = dynamic_cast<TrickleIceContents*>(msg.getContents());
1695 if(trickleIceContents)
1696 {
1697 if(onTrickleIce(*trickleIceContents))
1698 {
1699 session->acceptNIT();
1700 accepted = true;
1701 }
1702 }
1703
1704 if(!accepted)
1705 {
1706 WarningLog(<<"INFO message without recognized payload content-type, rejecting");
1707 session->rejectNIT();
1708 }
1709 }
1710 else
1711 {
1712 WarningLog(<<"INFO message received, but mHandle not set, rejecting");
1713 session->rejectNIT();
1714 }
1715}
1716
1717void
1719{
1720 WarningLog(<< "(unhandled) onInfoSuccess: handle=" << mHandle << ", " << msg.brief());
1721}
1722
1723void
1725{
1726 WarningLog(<< "(unhandled) onInfoFailure: handle=" << mHandle << ", " << msg.brief());
1727}
1728
1729void
1731{
1732 InfoLog(<< "onRefer: handle=" << mHandle << ", " << msg.brief());
1733
1734 try
1735 {
1736 // Accept the Refer
1737 ss->send(ss->accept(202 /* Refer Accepted */));
1738
1739 // Figure out hold SDP before removing ourselves from the conversation
1740 bool holdSdp = mLocalHold;
1741
1742 // Use same ConversationProfile as this participant
1743 auto profile = mDialogSet.getConversationProfile();
1744
1745 // Create new Participant - but use same participant handle
1746 RemoteParticipantDialogSet* participantDialogSet;
1747 if (dynamic_cast<RemoteIMSessionParticipant*>(this) != nullptr)
1748 {
1749 // If current participant is an IMSession participant then create new IM Session participant to handle refer
1751 }
1752 else
1753 {
1754 // otherwise, create a normal media based RemoteParticipant
1756 }
1757 RemoteParticipant *participant = participantDialogSet->createUACOriginalRemoteParticipant(mHandle); // This will replace old participant in ConversationManager map
1758 participant->mReferringAppDialog = getHandle();
1759
1760 replaceWithParticipant(participant); // adjust conversation mappings
1761
1762 // Create offer
1764 participant->buildSdpOffer(holdSdp, [this, h, msg, profile, ss, participantDialogSet, participant](bool success, unique_ptr<SdpContents> _offer)
1765 {
1766 if(!h.isValid())
1767 {
1768 WarningLog(<<"handle no longer valid");
1769 return;
1770 }
1771 if(!success)
1772 {
1773 ErrLog(<<"failed to create an SDP offer");
1774 mConversationManager.onParticipantTerminated(mHandle, 500, "Failed to create SDP offer");
1775 delete this;
1776 return;
1777 }
1778 SdpContents& offer = *_offer;
1779 // Build the Invite
1780 auto NewInviteMsg = mDum.makeInviteSessionFromRefer(msg, profile, ss->getHandle(), &offer, DialogUsageManager::None, 0, participantDialogSet);
1781 participantDialogSet->sendInvite(NewInviteMsg);
1782
1783 // Set RTP stack to listen
1784 participant->adjustRTPStreams(true);
1785 });
1786
1787 }
1788 catch(BaseException &e)
1789 {
1790 WarningLog(<< "onRefer exception: " << e);
1791 }
1792 catch(...)
1793 {
1794 WarningLog(<< "onRefer unknown exception");
1795 }
1796}
1797
1798void
1800{
1801 // Figure out hold SDP before removing ourselves from the conversation
1802 bool holdSdp = mLocalHold;
1803
1804 // Use same ConversationProfile as this participant
1805 auto profile = mDialogSet.getConversationProfile();
1806
1807 // Create new Participant - but use same participant handle
1808 RemoteParticipantDialogSet* participantDialogSet;
1809 if (dynamic_cast<RemoteIMSessionParticipant*>(this) != nullptr)
1810 {
1811 // If current participant is an IMSession participant then create new IM Session participant to handle refer
1813 }
1814 else
1815 {
1816 // otherwise, create a normal media based RemoteParticipant
1818 }
1819 RemoteParticipant *participant = participantDialogSet->createUACOriginalRemoteParticipant(mHandle); // This will replace old participant in ConversationManager map
1820 participant->mReferringAppDialog = getHandle();
1821
1822 replaceWithParticipant(participant); // adjust conversation mappings
1823
1824 // Create offer
1825 ParticipantHandle handleId = mHandle;
1827 participant->buildSdpOffer(holdSdp, [this, handleId, &cm, msg, profile, participantDialogSet, participant](bool success, unique_ptr<SdpContents> _offer)
1828 {
1829 if(!cm.getParticipant(handleId))
1830 {
1831 WarningLog(<<"handle no longer valid");
1832 return;
1833 }
1834 if(!success)
1835 {
1836 ErrLog(<<"failed to create SDP offer");
1837 mConversationManager.onParticipantTerminated(mHandle, 500, "Failed to create SDP offer");
1838 delete this;
1839 return;
1840 }
1841 SdpContents& offer = *_offer;
1842 // Build the Invite
1843 auto NewInviteMsg = mDum.makeInviteSessionFromRefer(msg, profile, &offer, participantDialogSet);
1844 participantDialogSet->sendInvite(NewInviteMsg);
1845
1846 // Set RTP stack to listen
1847 participant->adjustRTPStreams(true);
1848 });
1849}
1850
1851void
1853{
1854 InfoLog(<< "onReferNoSub: handle=" << mHandle << ", " << msg.brief());
1855
1856 try
1857 {
1858 // Accept the Refer
1859 is->acceptReferNoSub(202 /* Refer Accepted */);
1860
1861 doReferNoSub(msg);
1862 }
1863 catch(BaseException &e)
1864 {
1865 WarningLog(<< "onReferNoSub exception: " << e);
1866 }
1867 catch(...)
1868 {
1869 WarningLog(<< "onReferNoSub unknown exception");
1870 }
1871}
1872
1873void
1875{
1876 InfoLog(<< "onReferAccepted: handle=" << mHandle << ", " << msg.brief());
1877}
1878
1879void
1881{
1882 InfoLog(<< "onReferRejected: handle=" << mHandle << ", " << msg.brief());
1883 if(msg.isResponse() && mState == Redirecting)
1884 {
1886 stateTransition(Connected);
1887 }
1888}
1889
1890void
1892{
1893 InfoLog(<< "onMessage: handle=" << mHandle << ", " << msg.brief());
1894
1895 h->acceptNIT();
1896
1897 bool relay = false;
1899
1900 // Don't relay if sending the message to ourselves, this could end up in an infinite loop if both clients are using recon
1901 if (h->peerAddr().uri().getAOR(false) == h->myAddr().uri().getAOR(false))
1902 {
1903 relay = false;
1904 }
1905
1906 // Relay to others in our conversations, if requested to do so
1907 if (relay)
1908 {
1909 Data remoteDisplayName = h->peerAddr().displayName();
1910 if (remoteDisplayName.empty())
1911 {
1912 remoteDisplayName = h->peerAddr().uri().user();
1913 }
1914
1915 ConversationMap::iterator it;
1916 for (it = mConversations.begin(); it != mConversations.end(); it++)
1917 {
1918 it->second->relayInstantMessageToRemoteParticipants(mHandle, remoteDisplayName, msg);
1919 }
1920 }
1921}
1922
1923void
1925{
1926 InfoLog(<< "onMessageSuccess: handle=" << mHandle << ", " << msg.brief());
1927}
1928
1929void
1931{
1932 InfoLog(<< "onMessageFailure: handle=" << mHandle << ", " << msg.brief());
1933 auto failedMessage = h->getLastSentNITRequest();
1934 resip_assert(failedMessage->getContents() != nullptr);
1935
1936 std::unique_ptr<Contents> contents(failedMessage->getContents()->clone());
1937 if (mHandle) mConversationManager.onParticipantSendIMFailure(mHandle, msg, std::move(contents));
1938}
1939
1940void
1942{
1943 InfoLog(<< "onForkDestroyed: handle=" << mHandle);
1944}
1945
1946
1948// ClientSubscriptionHandler ///////////////////////////////////////////////////
1950void
1952{
1953 InfoLog(<< "onUpdatePending(ClientSub): handle=" << mHandle << ", " << notify.brief());
1954 if (notify.exists(h_Event) && notify.header(h_Event).value() == "refer")
1955 {
1956 h->acceptUpdate();
1957 processReferNotify(h, notify);
1958 }
1959 else
1960 {
1961 h->rejectUpdate(400, Data("Only notifies for refers are allowed."));
1962 }
1963}
1964
1965void
1967{
1968 InfoLog(<< "onUpdateActive(ClientSub): handle=" << mHandle << ", " << notify.brief());
1969 if (notify.exists(h_Event) && notify.header(h_Event).value() == "refer")
1970 {
1971 h->acceptUpdate();
1972 processReferNotify(h, notify);
1973 }
1974 else
1975 {
1976 h->rejectUpdate(400, Data("Only notifies for refers are allowed."));
1977 }
1978}
1979
1980void
1982{
1983 InfoLog(<< "onUpdateExtension(ClientSub): handle=" << mHandle << ", " << notify.brief());
1984 if (notify.exists(h_Event) && notify.header(h_Event).value() == "refer")
1985 {
1986 h->acceptUpdate();
1987 processReferNotify(h, notify);
1988 }
1989 else
1990 {
1991 h->rejectUpdate(400, Data("Only notifies for refers are allowed."));
1992 }
1993}
1994
1995void
1997{
1998 if(notify)
1999 {
2000 InfoLog(<< "onTerminated(ClientSub): handle=" << mHandle << ", " << notify->brief());
2001 if (notify->isRequest() && notify->exists(h_Event) && notify->header(h_Event).value() == "refer")
2002 {
2003 // Note: Final notify is sometimes only passed in the onTerminated callback
2004 processReferNotify(h, *notify);
2005 }
2006 else if(notify->isResponse() && mState == Redirecting)
2007 {
2009 stateTransition(Connected);
2010 }
2011 }
2012 else
2013 {
2014 // Timed out waiting for notify
2015 InfoLog(<< "onTerminated(ClientSub): handle=" << mHandle);
2016 if(mState == Redirecting)
2017 {
2019 stateTransition(Connected);
2020 }
2021 }
2022}
2023
2024void
2026{
2027 InfoLog(<< "onNewSubscription(ClientSub): handle=" << mHandle << ", " << notify.brief());
2028}
2029
2030int
2032{
2033 InfoLog(<< "onRequestRetry(ClientSub): handle=" << mHandle << ", " << notify.brief());
2034 return -1;
2035}
2036
2037
2038/* ====================================================================
2039
2040 Copyright (c) 2021-2023, SIP Spectrum, Inc. www.sipspectrum.com
2041 Copyright (c) 2022, Software Freedom Institute https://softwarefreedom.institute
2042 Copyright (c) 2021-2022, Daniel Pocock https://danielpocock.com
2043 Copyright (c) 2007-2008, Plantronics, Inc.
2044 All rights reserved.
2045
2046 Redistribution and use in source and binary forms, with or without
2047 modification, are permitted provided that the following conditions are
2048 met:
2049
2050 1. Redistributions of source code must retain the above copyright
2051 notice, this list of conditions and the following disclaimer.
2052
2053 2. Redistributions in binary form must reproduce the above copyright
2054 notice, this list of conditions and the following disclaimer in the
2055 documentation and/or other materials provided with the distribution.
2056
2057 3. Neither the name of Plantronics nor the names of its contributors
2058 may be used to endorse or promote products derived from this
2059 software without specific prior written permission.
2060
2061 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2062 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2063 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2064 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2065 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2066 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2067 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2068 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2069 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2070 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2071 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2072
2073 ==================================================================== */
ParserContainer< StringCategory > StringCategories
Definition Headers.cxx:228
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
This class is one of two main classes of concern to an application using the UserAgent library.
virtual void onParticipantSendIMFailure(ParticipantHandle partHandle, const resip::SipMessage &status, std::unique_ptr< resip::Contents > contents)
Notifies an application that a send IM call has failed.
virtual void onParticipantConnectedConfirmed(ParticipantHandle partHandle, const resip::SipMessage &msg)
Notifies an application that an inbound remote participant call now fully connected after answering (...
virtual void onParticipantConnected(ParticipantHandle partHandle, const resip::SipMessage &msg)=0
Notifies an application that a remote participant call attempt is now connected.
MediaStackAdapter & getMediaStackAdapter()
virtual void onParticipantAlerting(ParticipantHandle partHandle, const resip::SipMessage &msg)=0
Notifies an application that a remote participant call attempt is alerting the remote party.
virtual void onParticipantRequestedHold(ParticipantHandle partHandle, bool held)=0
virtual void onDtmfEvent(ParticipantHandle partHandle, int dtmf, int duration, bool up)=0
Notifies an application when an RFC2833 DTMF event is received from a particular remote participant.
virtual void onParticipantRedirectFailure(ParticipantHandle partHandle, unsigned int statusCode)=0
Notifies an application that a redirect request has failed.
virtual void onIncomingParticipant(ParticipantHandle partHandle, const resip::SipMessage &msg, bool autoAnswer, ConversationProfile &conversationProfile)=0
Notifies an application about a new remote participant that is attempting to contact it.
virtual bool onReceiveIMFromParticipant(ParticipantHandle partHandle, const resip::SipMessage &msg)
Notifies an application about a subsequent message from remote and existing IM participant.
virtual void onParticipantTerminated(ParticipantHandle partHandle, unsigned int statusCode)=0
Notifies an application about a disconnect by a remote participant.
virtual void onParticipantRedirectSuccess(ParticipantHandle partHandle)=0
Notifies an application that a redirect request has succeeded.
virtual void onParticipantDestroyed(ParticipantHandle partHandle)=0
Notifies an application when a Participant has been destroyed.
virtual RemoteParticipantDialogSet * createRemoteIMSessionParticipantDialogSetInstance(ConversationManager::ParticipantForkSelectMode forkSelectMode=ConversationManager::ForkSelectAutomatic, std::shared_ptr< ConversationProfile > conversationProfile=nullptr)
This class extends the resip UserProfile to include Conversation Manager specific settings.
virtual bool shouldAutoAnswer(const resip::SipMessage &inviteRequest, bool *required=0)
Checks an Invite message for auto-answer headers against the current allow settings.
This class is used to manage membership of participants.
ConversationHandle getHandle()
virtual void confirmParticipant(Participant *participant)
This class represents base functionality for a participant that supports IMs.
virtual RemoteParticipantDialogSet * createRemoteParticipantDialogSetInstance(ConversationManager::ParticipantForkSelectMode forkSelectMode=ConversationManager::ForkSelectAutomatic, std::shared_ptr< ConversationProfile > conversationProfile=nullptr)=0
This is the base class for a RemoteParticipant, LocalParticipant and a MediaResourceParticipant.
virtual void removeFromConversation(Conversation *conversation)
virtual void applyBridgeMixWeights()
ConversationMap mConversations
virtual void setHandle(ParticipantHandle partHandle)
std::map< ConversationHandle, Conversation * > ConversationMap
virtual void addToConversation(Conversation *conversation, unsigned int inputGain=100, unsigned int outputGain=100)
virtual ParticipantHandle getParticipantHandle()
ParticipantHandle mHandle
virtual void replaceWithParticipant(Participant *replacingParticipant)
ConversationManager & mConversationManager
This class represents an IM Session remote participant.
This class is used by Invite DialogSets.
virtual void endIncludeRelated(ParticipantHandle requestingParticipant)
virtual std::shared_ptr< resip::SdpContents > getProposedSdp()
virtual void removeDialog(const resip::DialogId &dialogId)
std::shared_ptr< ConversationProfile > getConversationProfile()
void provideOffer(std::unique_ptr< resip::SdpContents > offer, resip::InviteSessionHandle &inviteSessionHandle, bool postOfferAccept)
virtual void setActiveRemoteParticipantHandle(ParticipantHandle handle)
virtual void setProposedSdp(ParticipantHandle handle, const resip::SdpContents &sdp)
virtual RemoteParticipant * createUACOriginalRemoteParticipant(ParticipantHandle handle)
virtual ParticipantHandle getActiveRemoteParticipantHandle()
void sendInvite(std::shared_ptr< resip::SipMessage > invite)
void accept(resip::InviteSessionHandle &inviteSessionHandle)
virtual bool isStaleFork(const resip::DialogId &dialogId)
virtual void setUACConnected(const resip::DialogId &dialogId, ParticipantHandle partHandle)
virtual ConversationManager::ParticipantForkSelectMode getForkSelectMode()
This class represent a remote participant.
virtual void onConnectedConfirmed(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onNewSession(resip::ClientInviteSessionHandle h, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage &msg)
virtual void onFailure(resip::ClientInviteSessionHandle h, const resip::SipMessage &msg)
virtual void processReferNotify(resip::ClientSubscriptionHandle h, const resip::SipMessage &notify)
virtual void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage &notify, bool outOfOrder)
void setProposedSdp(const resip::SdpContents &sdp)
void setRemoteSdp(const resip::SdpContents &sdp, bool answer=false)
virtual void onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage &msg)
virtual void requestKeyframeFromPeerTimeout(bool reset)
virtual void redirectToParticipant(resip::InviteSessionHandle &destParticipantInviteSessionHandle, ConversationManager::RedirectSuccessCondition successCondition=ConversationManager::RedirectSuccessOnConnected)
virtual int onRequestRetry(resip::ClientSubscriptionHandle h, int retryMinimum, const resip::SipMessage &notify)
virtual void onRemoteSdpChanged(resip::InviteSessionHandle, const resip::SipMessage &msg, const resip::SdpContents &sdp)
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage &msg)
virtual void onLocalIceCandidate(const resip::Data &candidate, unsigned int lineIndex, const resip::Data &mid)
virtual void redirect(resip::NameAddr &destination, unsigned int redirectCode=302, ConversationManager::RedirectSuccessCondition successCondition=ConversationManager::RedirectSuccessOnConnected)
virtual void rejectPendingOODRefer(unsigned int statusCode)
virtual void onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onOfferRequestRejected(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onUpdatePending(resip::ClientSubscriptionHandle h, const resip::SipMessage &notify, bool outOfOrder)
virtual bool onMediaControlEvent(resip::MediaControlContents::MediaControl &mediaControl)
virtual void initiateRemoteCall(const resip::NameAddr &destination)
virtual bool mediaStackPortAvailable()=0
virtual void setPendingOODReferInfo(resip::ServerOutOfDialogReqHandle ood, const resip::SipMessage &referMsg)
virtual void onOffer(resip::InviteSessionHandle handle, const resip::SipMessage &msg, const resip::SdpContents &offer)
virtual void onInfo(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void setLocalHold(bool hold)
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage &msg)
std::function< void(bool sdpOk, std::unique_ptr< resip::SdpContents > sdp)> CallbackSdpReady
void addExtraHeader(const std::shared_ptr< resip::SipMessage > &invitemsg, const resip::Data &headerName, const resip::Data &headerValue)
virtual void sendInstantMessage(std::unique_ptr< resip::Contents > contents) override
virtual void onUpdateActive(resip::ClientSubscriptionHandle h, const resip::SipMessage &notify, bool outOfOrder)
virtual void onNewSubscription(resip::ClientSubscriptionHandle h, const resip::SipMessage &notify)
void setLocalSdp(const resip::SdpContents &sdp)
virtual void onConnected(resip::ClientInviteSessionHandle h, const resip::SipMessage &msg)
void setLocalSdpGathering(const resip::SdpContents &sdp)
virtual void onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void addToConversation(Conversation *conversation, unsigned int inputGain=100, unsigned int outputGain=100) override
std::shared_ptr< resip::SdpContents > getLocalSdp()
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage &msg)
virtual void notifyIncomingParticipant(const resip::SipMessage &msg, bool autoAnswer, ConversationProfile &conversationProfile)
virtual void onForkDestroyed(resip::ClientInviteSessionHandle)
virtual void info(const resip::Contents &contents)
virtual void onMessage(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onStaleCallTimeout(resip::ClientInviteSessionHandle)
virtual void onAnswer(resip::InviteSessionHandle, const resip::SipMessage &msg, const resip::SdpContents &)
virtual void removeFromConversation(Conversation *conversation) override
std::shared_ptr< resip::SdpContents > getRemoteSdp()
virtual void buildSdpOffer(bool holdSdp, CallbackSdpReady sdpReady, bool preferExistingSdp=false)=0
virtual bool onTrickleIce(resip::TrickleIceContents &trickleIce)
virtual void destroyParticipant() override
virtual void onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage &msg)
virtual void doReferNoSub(const resip::SipMessage &msg)
virtual void adjustRTPStreams(bool sendingOffer=false)=0
virtual void reject(unsigned int rejectCode)
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage &, const resip::SdpContents &)
virtual void onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual resip::InviteSessionHandle & getInviteSessionHandle()
virtual void alert(bool earlyFlag)
void setRemoteHold(bool remoteHold)
RemoteParticipant(ParticipantHandle partHandle, ConversationManager &conversationManager, resip::DialogUsageManager &dum, RemoteParticipantDialogSet &remoteParticipantDialogSet)
virtual void onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage *msg)
virtual void onTerminated(resip::InviteSessionHandle h, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage *msg)
virtual void redirectPendingOODRefer(resip::NameAddr &destination)
virtual void end()
virtual void destroy()
AppDialogHandle getHandle()
Definition AppDialog.cxx:18
DialogId getDialogId() const
Definition AppDialog.cxx:55
The abstract base-class for all exceptions thrown by resip code.
Base class for all SIP body types.
Definition Contents.hxx:51
const Mime & getType() const
getter for mime type of message
Definition Contents.hxx:118
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
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
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
static Data from(const T &x)
Converts from arbitrary other type to Data.
Definition Data.hxx:398
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
const Data & getCallId() const
Definition DialogId.cxx:99
std::shared_ptr< SipMessage > makeInviteSession(const NameAddr &target, const std::shared_ptr< UserProfile > &userProfile, const Contents *initialOffer, AppDialogSet *ads=nullptr)
InviteSessionHandle findInviteSession(const DialogId &id)
std::shared_ptr< SipMessage > makeInviteSessionFromRefer(const SipMessage &refer, ServerSubscriptionHandle, const Contents *initialOffer, AppDialogSet *=nullptr)
std::shared_ptr< UserProfile > getUserProfile() const
const NameAddr & myAddr() const noexcept
const NameAddr & peerAddr() const noexcept
static const Data & getLocalHostName()
Definition DnsUtil.cxx:118
int getDuration() const
obtain duration in milliseconds
unsigned short getEventCode() const
obtain representation of the button as event code
DtmfPayload & dtmfPayload()
get the parsed payload
bool isValid() const
Definition Handle.hxx:25
static Handle< InviteSession > NotValid()
Definition Handle.hxx:84
bool getHeaderValueByIndex(size_t index, Data &headerValue) const
void push_back(const char *buffer, size_t length, bool own)
READ THIS CAREFULLY BEFORE USING THIS FUNCTION.
static Type getType(const char *name, size_t len)
Definition Headers.cxx:351
static bool isMulti(Type type)
Definition Headers.cxx:50
TerminatedReason
called when an dialog enters the terminated state - this can happen after getting a BYE,...
@ Referred
slg! - This is really Redirected - not sure why it is called Referred. Only gets used when we send a ...
virtual void message(const Contents &contents)
sends a message request
virtual void rejectNIT(int statusCode=488)
rejects an INFO or MESSAGE request with an error status code
virtual void reject(int statusCode, WarningCategory *warning=0)
Rejects an offer at the SIP level.
std::shared_ptr< SipMessage > getLastSentNITRequest() const
gets the last NIT request that was sent by the session
virtual void refer(const NameAddr &referTo, bool referSub=true)
sends a refer request
virtual void end(const Data &userReason)
Makes the specific dialog end.
virtual void acceptNIT(int statusCode=200, const Contents *contents=0)
accepts an INFO or MESSAGE request with a 2xx and an optional contents
virtual void info(const Contents &contents)
sends an info request
virtual void acceptReferNoSub(int statusCode=200)
MediaControl & mediaControl()
get the parsed payload
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
Data & displayName()
Definition NameAddr.cxx:144
void push_back(const T &t)
Inserts a header field value at the back of this container.
MethodTypes & method()
bool isTrickleIceSupported() const
determine if ice-options:trickle is present
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
virtual Contents * clone() const
duplicate an SdpContents object
Session & session()
get the parsed SDP
void redirect(const NameAddrs &contacts, int code=302)
send a 3xx
virtual void reject(int statusCode, WarningCategory *warning=0)
Rejects an offer at the SIP level.
void provisional(int code=180, bool earlyFlag=true)
send a 1xx - provisional response.
ServerSubscriptionHandle getHandle() const
std::shared_ptr< SipMessage > accept(int statusCode=202)
void send(std::shared_ptr< SipMessage > msg) override
std::shared_ptr< SipMessage > reject(int responseCode)
SIP body type for holding SipFrag contents (MIME content-type message/sipfrag).
Definition SipFrag.hxx:22
SipMessage & message()
Definition SipFrag.cxx:82
Represents a SIP message.
bool isRequest() const
Returns true if message is a request, false otherwise.
Contents * getContents() const
Retrieves the body of a SIP message.
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.
Represents the "header-value" element in the RFC 3261 grammar (ie, an unknown header field value).
static const Mime & getStaticType()
Data getAOR(bool addPort) const
Definition Uri.cxx:999
Data & user()
Definition Uri.hxx:42
Represents the "warning-value" element in the RFC 3261 grammar.
unsigned int ParticipantHandle
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
RequestLineType h_RequestLine
Definition Headers.cxx:347