reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
InviteSession.cxx
Go to the documentation of this file.
20#include "rutil/Inserter.hxx"
21#include "rutil/Logger.hxx"
22#include "rutil/Timer.hxx"
23#include "rutil/Random.hxx"
24#include "rutil/compat.hxx"
26
27#include <utility>
28
29// Remove warning about 'this' use in initiator list - pointer is only stored
30#if defined(WIN32) && !defined(__GNUC__)
31#pragma warning( disable : 4355 ) // using this in base member initializer list
32#pragma warning( disable : 4800 ) // forcing value to bool (performance warning)
33#endif
34
35#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
36#define THROW(msg) throw DialogUsage::Exception(msg, __FILE__,__LINE__);
37
38using namespace resip;
39using namespace std;
40
42{
43 "Not Specified",
44 "User Hung Up",
45 "Application Rejected Sdp(usually no common codec)",
46 "Illegal Sdp Negotiation",
47 "ACK not received",
48 "Session Timer Expired",
49 "Stale re-Invite"
50};
51
53
55{
57 {
59 return EndReasons[reason];
60 }
61 else
62 {
63 return mUserEndReason;
64 }
65}
66
68 : DialogUsage(dum, dialog),
69 mPeerSupportsSessionTimer(false),
70 mState(Undefined),
71 mNitState(NitComplete),
72 mServerNitState(NitComplete),
73 mLastLocalSessionModification(new SipMessage),
74 mLastRemoteSessionModification(new SipMessage),
75 mInvite200(new SipMessage),
76 mLastNitResponse(new SipMessage),
77 mCurrentRetransmit200(0),
78 mStaleReInviteTimerSeq(1),
79 mSessionInterval(0),
80 mMinSE(90),
81 mSessionRefresher(false),
82 mSessionRefreshTimerSeq(0),
83 mSessionExpirationTimerSeq(0),
84 mSessionRefreshReInvite(false),
85 mSessionExpirationPending(false),
86 mReferSub(true),
87 mCurrentEncryptionLevel(DialogUsageManager::None),
88 mProposedEncryptionLevel(DialogUsageManager::None),
89 mEndReason(NotSpecified)
90{
91 DebugLog ( << "^^^ InviteSession::InviteSession " << this);
92 resip_assert(mDum.mInviteSessionHandler);
93}
94
96{
97 DebugLog ( << "^^^ InviteSession::~InviteSession " << this);
98 mDialog.mInviteSession = 0;
99 while(!mNITQueue.empty())
100 {
101 delete mNITQueue.front();
102 mNITQueue.pop();
103 }
104}
105
106bool
111
112const Contents&
114{
116 {
118 }
119 else
120 {
121 return SdpContents::Empty;
122 }
123}
124
125bool
130
131const Contents&
133{
135 {
137 }
138 else
139 {
140 return SdpContents::Empty;
141 }
142}
143
144bool
149
150const Contents&
152{
154 {
156 }
157 else
158 {
159 return SdpContents::Empty;
160 }
161}
162
163bool
165{
166 resip_assert(!mDum.mInviteSessionHandler->isGenericOfferAnswer());
167 return (mCurrentLocalOfferAnswer.get());
168}
169
170const SdpContents&
172{
173 resip_assert(!mDum.mInviteSessionHandler->isGenericOfferAnswer());
175 {
176 const SdpContents* sdp = dynamic_cast<const SdpContents*>(mCurrentLocalOfferAnswer.get());
177 resip_assert(sdp);
178 return *sdp;
179 }
180 else
181 {
182 return SdpContents::Empty;
183 }
184}
185
186bool
188{
189 resip_assert(!mDum.mInviteSessionHandler->isGenericOfferAnswer());
190 return (mCurrentRemoteOfferAnswer.get());
191}
192
193const SdpContents&
195{
196 resip_assert(!mDum.mInviteSessionHandler->isGenericOfferAnswer());
198 {
199 const SdpContents* sdp = dynamic_cast<const SdpContents*>(mCurrentRemoteOfferAnswer.get());
200 resip_assert(sdp);
201 return *sdp;
202 }
203 else
204 {
205 return SdpContents::Empty;
206 }
207}
208
209bool
211{
212 resip_assert(!mDum.mInviteSessionHandler->isGenericOfferAnswer());
213 return (mProposedRemoteOfferAnswer.get());
214}
215
216const SdpContents&
218{
219 resip_assert(!mDum.mInviteSessionHandler->isGenericOfferAnswer());
221 {
222 const SdpContents* sdp = dynamic_cast<const SdpContents*>(mProposedRemoteOfferAnswer.get());
223 resip_assert(sdp);
224 return *sdp;
225 }
226 else
227 {
228 return SdpContents::Empty;
229 }
230}
231
237
239{
240 // Policy: INVITE messages are treated as the authoritative capability-
241 // announcement boundary. UPDATE messages often omit capability headers
242 // (Allow, Supported, etc.) because implementations send lean UPDATEs for
243 // session timer refresh or SDP tweaks. Harvesting capabilities from UPDATE
244 // risks erasing richer data advertised on INVITE. Headers with per-message
245 // semantics (P-Asserted-Identity, User-Agent) are refreshed from any message.
246
247 // Always safe to refresh
248 if (msg.exists(h_UserAgent))
249 {
250 mPeerUserAgent = msg.header(h_UserAgent).value();
251 }
253 {
255 }
256
257 // Peer Session timer support
258 if (msg.method() == INVITE) // Request or response
259 {
260 mPeerSupportsSessionTimer = (msg.exists(h_Supporteds) && msg.header(h_Supporteds).find(SessionTimerToken)) ||
261 (msg.exists(h_Requires) && msg.header(h_Requires).find(SessionTimerToken));
262 }
263 else if(msg.method() == UPDATE) // Request or response
264 {
265 // Allow UPDATE to turn on session timer support only - we don't want to allow UPDATE to turn off support if
266 // it doesn't include the headers, but we do want to allow UPDATE to turn on support if it includes the headers.
268 {
269 mPeerSupportsSessionTimer = (msg.exists(h_Supporteds) && msg.header(h_Supporteds).find(SessionTimerToken)) ||
270 (msg.exists(h_Requires) && msg.header(h_Requires).find(SessionTimerToken));
271 }
272 }
273
274 // Capability headers: harvest only from INVITE family
275 // Rationale: UPDATE messages are often lean and may omit capability
276 // headers that were advertised on the initial INVITE. Overwriting
277 // with a shorter set (or failing to overwrite) creates inconsistency.
278 // We treat INVITE as the authoritative capability-announcement boundary.
279 if (msg.method() == INVITE) // Request or response
280 {
281 if (msg.exists(h_Allows))
282 {
283 mPeerSupportedMethods = msg.header(h_Allows);
284 }
285 if (msg.exists(h_Supporteds))
286 {
287 mPeerSupportedOptionTags = msg.header(h_Supporteds);
288 }
289 if (msg.exists(h_Requires))
290 {
291 mPeerRequiresOptionTags = msg.header(h_Requires);
292 }
293 if (msg.exists(h_AcceptEncodings))
294 {
295 mPeerSupportedEncodings = msg.header(h_AcceptEncodings);
296 }
297 if (msg.exists(h_AcceptLanguages))
298 {
299 mPeerSupportedLanguages = msg.header(h_AcceptLanguages);
300 }
301 if (msg.exists(h_AllowEvents))
302 {
304 }
305 if (msg.exists(h_Accepts))
306 {
307 mPeerSupportedMimeTypes = msg.header(h_Accepts);
308 }
309 }
310}
311
312bool
314{
315 // Check if Update is supported locally
316 if(mDum.getMasterProfile()->isMethodSupported(UPDATE))
317 {
318 // Check if peer supports UPDATE
319 return mPeerSupportedMethods.find(Token("UPDATE"));
320 }
321 return false;
322}
323
324bool
326{
327 // Check if session timer extension (RFC4028) is supported locally
328 // and by peer (either supported or requires headers)
329 return mDum.getMasterProfile()->getSupportedOptionTags().find(SessionTimerToken) && mPeerSupportsSessionTimer;
330}
331
332bool
334{
335 switch (mState)
336 {
337 case Connected:
338 case SentUpdate:
339 case SentUpdateGlare:
340 case SentReinvite:
345 case SentOptions:
346 case SentOptionsGlare:
347 case ReceivedUpdate:
348 case ReceivedReinvite:
351 case Answered:
352 case WaitingToOffer:
354 return true;
355
356 default:
357 return false;
358 }
359}
360
361bool
363{
364 switch (mState)
365 {
366 case UAC_Early:
372 case UAC_SentAnswer:
373 case UAC_QueuedUpdate:
374 return true;
375 default:
376 return false;
377 }
378}
379
380bool
382{
383 switch (mState)
384 {
385 case UAS_Start:
386 case UAS_Offer:
388 case UAS_EarlyOffer:
390
391 case UAS_NoOffer:
393 case UAS_EarlyNoOffer:
395 //case UAS_Accepted: // Obvious
396 //case UAS_WaitingToOffer: // We have accepted here and are waiting for ACK to Offer
397 //case UAS_WaitingToRequestOffer: // We have accepted here and are waiting for ACK to request an offer
398
399 //case UAS_AcceptedWaitingAnswer: // Obvious
409 case UAS_SentUpdate:
410 //case UAS_SentUpdateAccepted: // we have accepted here
413 //case UAS_ReceivedUpdateWaitingAnswer: // happens only after accept is called
414 //case UAS_WaitingToHangup: // always from an accepted state
415 return false;
416
417 default:
418 return true;
419 }
420}
421
422bool
424{
425 // Note: the Glare states are deliberately not included - in those we are waiting on the 491
426 // timer and have no transaction outstanding in the stack.
427 switch (mState)
428 {
429 case SentUpdate:
430 case SentReinvite:
432 case SentOptions:
433 return true;
434
435 default:
436 return false;
437 }
438}
439
440bool
442{
443 switch (mState)
444 {
445 case Terminated:
447 case WaitingToHangup:
448 case UAC_Cancelled:
450 return true;
451 default:
452 return false;
453 }
454}
455
457InviteSession::dump(EncodeStream& strm) const
458{
459 strm << "INVITE: " << mId
460 << " " << toData(mState)
461 << " ADDR=" << myAddr()
462 << " PEER=" << peerAddr();
463 return strm;
464}
465
466void
468{
469 switch (mState)
470 {
471 case Connected:
477 mLastLocalSessionModification->setContents(0); // Clear the contents from the INVITE
479
480 InfoLog (<< "Sending " << mLastLocalSessionModification->brief());
481
482 // call send to give app an chance to adorn the message.
484 break;
485
486 case Answered:
487 // queue the offer to be sent after the ACK is received
489 break;
490
491 // ?slg? Can we handle all of the states listed in isConnected() ???
492 default:
493 WarningLog (<< "Can't requestOffer when not in Connected state");
494 throw DialogUsage::Exception("Can't request an offer", __FILE__,__LINE__);
495 }
496}
497
498void
501 const Contents* alternative)
502{
503 switch (mState)
504 {
505 case Connected:
506 case WaitingToOffer:
511
513
514 InfoLog (<< "Sending " << mLastLocalSessionModification->brief());
519
520 // call send to give app an chance to adorn the message.
522 break;
523
524 case Answered:
525 // queue the offer to be sent after the ACK is received
529 break;
530
538
539 InfoLog (<< "Sending " << mInvite200->brief());
543 break;
544
545 default:
546 WarningLog (<< "Incorrect state to provideOffer: " << toData(mState));
547 throw DialogUsage::Exception("Can't provide an offer", __FILE__,__LINE__);
548 }
549}
550
552{
553public:
555 const Contents& offer,
557 const Contents* alternative)
558 : mInviteSessionHandle(inviteSessionHandle),
559 mOffer(offer.clone()),
560 mLevel(level),
561 mAlternative(alternative ? alternative->clone() : 0)
562 {
563 }
564
565 void executeCommand() override
566 {
567 if(mInviteSessionHandle.isValid())
568 {
569 mInviteSessionHandle->provideOffer(*mOffer, mLevel, mAlternative.get());
570 }
571 }
572
573 EncodeStream& encodeBrief(EncodeStream& strm) const override
574 {
575 return strm << "InviteSessionProvideOfferExCommand";
576 }
577private:
578 InviteSessionHandle mInviteSessionHandle;
579 std::unique_ptr<const Contents> mOffer;
581 std::unique_ptr<const Contents> mAlternative;
582};
583
584void
586{
587 mDum.post(new InviteSessionProvideOfferExCommand(getSessionHandle(), offer, level, alternative));
588}
589
590void
592{
593 return provideOffer(offer, mCurrentEncryptionLevel, 0);
594}
595
597{
598public:
599 InviteSessionProvideOfferCommand(const InviteSessionHandle& inviteSessionHandle, const Contents& offer)
600 : mInviteSessionHandle(inviteSessionHandle),
601 mOffer(offer.clone())
602 {
603 }
604
605 void executeCommand() override
606 {
607 if(mInviteSessionHandle.isValid())
608 {
609 mInviteSessionHandle->provideOffer(*mOffer);
610 }
611 }
612
613 EncodeStream& encodeBrief(EncodeStream& strm) const override
614 {
615 return strm << "InviteSessionProvideOfferCommand";
616 }
617private:
618 InviteSessionHandle mInviteSessionHandle;
619 std::unique_ptr<const Contents> mOffer;
620};
621
622void
627
628void
630{
631 switch (mState)
632 {
633 case ReceivedReinvite:
640 InfoLog (<< "Sending " << mInvite200->brief());
644 if (mDum.mDialogEventStateManager)
645 {
646 // New Offer/Answer - generate a new confirmed callback with updated SDP
647 mDum.mDialogEventStateManager->onConfirmed(mDialog, getSessionHandle());
648 }
649 break;
650
651 case ReceivedUpdate: // same as ReceivedReinvite case.
652 {
654
655 auto response = std::make_shared<SipMessage>();
658 InviteSession::setOfferAnswer(*response, answer, 0);
661 InfoLog (<< "Sending " << response->brief());
663 send(response);
664 if (mDum.mDialogEventStateManager)
665 {
666 // New Offer/Answer - generate a new confirmed callback with updated SDP
667 mDum.mDialogEventStateManager->onConfirmed(mDialog, getSessionHandle());
668 }
669 break;
670 }
671
674 sendAck(&answer);
675
678 if (mDum.mDialogEventStateManager)
679 {
680 // New Offer/Answer - generate a new confirmed callback with updated SDP
681 mDum.mDialogEventStateManager->onConfirmed(mDialog, getSessionHandle());
682 }
683 break;
684
685 default:
686 WarningLog (<< "Incorrect state to provideAnswer: " << toData(mState));
687 throw DialogUsage::Exception("Can't provide an answer", __FILE__,__LINE__);
688 }
689}
690
692{
693public:
694 InviteSessionProvideAnswerCommand(const InviteSessionHandle& inviteSessionHandle, const Contents& answer)
695 : mInviteSessionHandle(inviteSessionHandle),
696 mAnswer(answer.clone())
697 {
698 }
699
700 void executeCommand() override
701 {
702 if(mInviteSessionHandle.isValid())
703 {
704 mInviteSessionHandle->provideAnswer(*mAnswer);
705 }
706 }
707
708 EncodeStream& encodeBrief(EncodeStream& strm) const override
709 {
710 return strm << "InviteSessionProvideAnswerCommand";
711 }
712private:
713 InviteSessionHandle mInviteSessionHandle;
714 std::unique_ptr<const Contents> mAnswer;
715};
716
717void
722
723void
728
729void
730InviteSession::end(const Data& userReason)
731{
732 mUserEndReason = userReason;
734}
735
736void
738{
739 mUserEndReasons = endReasons;
741}
742
743void
745{
747 {
748 mEndReason = reason;
749 }
750
751 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
752
753 switch (mState)
754 {
755 case Connected:
756 case SentUpdate:
757 case SentUpdateGlare:
761 case SentOptions:
762 case SentOptionsGlare:
763 {
764 // !jf! do we need to store the BYE somewhere?
765 // .dw. BYE message handled
766 const auto msg = sendBye();
769 break;
770 }
771
772 case SentReinvite:
775 break;
776
777 case Answered:
778 case WaitingToOffer:
781 if(mCurrentRetransmit200) // If retransmit200 timer is active then ACK is not received yet - wait for it
782 {
784 }
785 else
786 {
787 // ACK has likely timedout - hangup immediately
788 const auto msg = sendBye();
790 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::LocalBye, msg.get());
791 }
792 break;
793
794 case ReceivedUpdate:
795 case ReceivedReinvite:
797 {
798 auto response = std::make_shared<SipMessage>();
800 InfoLog (<< "Sending " << response->brief());
801 send(response);
802
803 const auto msg = sendBye();
806 break;
807 }
808
809 case WaitingToTerminate: // ?slg? Why is this here?
810 {
811 const auto msg = sendBye();
814 break;
815 }
816
817 case Terminated:
818 // no-op.
819 break;
820
821 default:
822 resip_assert(0);
823 break;
824 }
825}
826
828{
829public:
830 InviteSessionEndCommand(const InviteSessionHandle& inviteSessionHandle, InviteSession::EndReason reason, const Data& userEndReason = Data::Empty, ParserContainer<Token> userEndReasons = ParserContainer<Token>())
831 : mInviteSessionHandle(inviteSessionHandle),
832 mReason(reason),
833 mUserEndReason(userEndReason),
834 mUserEndReasons(userEndReasons)
835 {
836 }
837
838 void executeCommand() override
839 {
840 if(mInviteSessionHandle.isValid())
841 {
842 if (mReason == InviteSession::UserSpecified)
843 {
844 if (mUserEndReasons.size() > 0)
845 {
846 mInviteSessionHandle->end(mUserEndReasons);
847 }
848 else
849 {
850 mInviteSessionHandle->end(mUserEndReason);
851 }
852 }
853 else
854 {
855 mInviteSessionHandle->end(mReason);
856 }
857 }
858 }
859
860 EncodeStream& encodeBrief(EncodeStream& strm) const override
861 {
862 return strm << "InviteSessionEndCommand";
863 }
864private:
865 InviteSessionHandle mInviteSessionHandle;
867 Data mUserEndReason;
868 ParserContainer<Token> mUserEndReasons;
869};
870
871void
876
877void
882
883void
888
889void
890InviteSession::reject(int statusCode, WarningCategory *warning)
891{
892 reject(statusCode, nullptr, warning);
893}
894
895void
896InviteSession::reject(int statusCode, const Contents* contents, WarningCategory *warning)
897{
898 mProposedRemoteOfferAnswer.reset(); // Clear out any potential ProposedRemoteOfferAnswer since we are rejecting
899 switch (mState)
900 {
901 case ReceivedUpdate:
902 case ReceivedReinvite:
904 {
906
907 auto response = std::make_shared<SipMessage>();
908 mDialog.makeResponse(*response, *mLastRemoteSessionModification, statusCode);
909 // In case we wanted to reject the request by including content
910 if (contents)
911 {
912 response->setContents(contents);
913 }
914 if(warning)
915 {
916 response->header(h_Warnings).push_back(*warning);
917 }
918 InfoLog (<< "Sending " << response->brief());
919 send(response);
920 break;
921 }
922 // Sent a reINVITE no offer and received a 200-offer.
923 // Simply send an ACK without an answer and stay in Connected.
925 {
926 InfoLog (<< "Not sending " << statusCode << " error since transaction"
927 "already completed, sending answer-less ACK");
929 sendAck();
930 break;
931 }
932 default:
933 resip_assert(0);
934 break;
935 }
936}
937
939{
940public:
941 InviteSessionRejectCommand(const InviteSessionHandle& inviteSessionHandle, int code, WarningCategory* warning)
942 : mInviteSessionHandle(inviteSessionHandle),
943 mCode(code),
944 mWarning(warning?new WarningCategory(*warning):0)
945 {
946 }
947
948 InviteSessionRejectCommand(const InviteSessionHandle& inviteSessionHandle, int code, const Contents* contents, WarningCategory* warning)
949 : mInviteSessionHandle(inviteSessionHandle),
950 mCode(code),
951 mContents(contents?contents->clone():nullptr),
952 mWarning(warning?new WarningCategory(*warning):0)
953 {
954 }
955
956 void executeCommand() override
957 {
958 if(mInviteSessionHandle.isValid())
959 {
960 mInviteSessionHandle->reject(mCode, mContents.get(), mWarning.get());
961 }
962 }
963
964 EncodeStream& encodeBrief(EncodeStream& strm) const override
965 {
966 return strm << "InviteSessionRejectCommand";
967 }
968private:
969 InviteSessionHandle mInviteSessionHandle;
970 int mCode;
971 std::unique_ptr<Contents> mContents;
972 std::unique_ptr<WarningCategory> mWarning;
973};
974
975void
980
981void
982InviteSession::rejectCommand(int code, const Contents* contents, WarningCategory *warning)
983{
984 mDum.post(new InviteSessionRejectCommand(getSessionHandle(), code, contents, warning));
985}
986
987void
989{
990 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
991 {
992 mDialog.mLocalContact = localUri;
994 }
995 else
996 {
997 WarningLog (<< "Can't targetRefresh before Connected");
998 throw UsageUseException("targetRefresh not allowed in this context", __FILE__, __LINE__);
999 }
1000}
1001
1002void
1003InviteSession::refer(const NameAddr& referTo, bool referSub)
1004{
1005 refer(referTo, myAddr(), nullptr, referSub);
1006}
1007void
1008InviteSession::refer(const NameAddr& referTo, const NameAddr& referredBy, bool referSub)
1009{
1010 refer(referTo, referredBy, nullptr, referSub);
1011}
1012void
1013InviteSession::refer(const NameAddr& referTo, std::unique_ptr<resip::Contents> contents, bool referSub)
1014{
1015 refer(referTo, myAddr(), std::move(contents), referSub);
1016}
1017void
1018InviteSession::refer(const NameAddr& referTo, const NameAddr& referredBy, std::unique_ptr<resip::Contents> contents, bool referSub)
1019{
1020 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
1021 {
1022 auto refer = std::make_shared<SipMessage>();
1023 mDialog.makeRequest(*refer, REFER, mNitState == NitComplete); // only increment CSeq if not going to queue NIT
1024 refer->header(h_ReferTo) = referTo;
1025 refer->header(h_ReferredBy) = referredBy;
1026 refer->header(h_ReferredBy).remove(p_tag); // tag-param not permitted in rfc3892; not the same as generic-param
1027 refer->setContents(std::move(contents));
1028 if (!referSub)
1029 {
1030 refer->header(h_ReferSub).value() = "false";
1031 refer->header(h_Supporteds).push_back(Token(Symbols::NoReferSub));
1032 }
1033
1034 if(mNitState == NitComplete)
1035 {
1037 mReferSub = referSub;
1039 send(refer);
1040 return;
1041 }
1042 mNITQueue.push(new QueuedNIT(refer,referSub));
1043 InfoLog(<< "refer - queuing NIT:" << refer->brief());
1044 return;
1045 }
1046 else
1047 {
1048 WarningLog (<< "Can't refer before Connected");
1049 throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__);
1050 }
1051}
1052
1053std::shared_ptr<SipMessage>
1058
1059void
1061{
1063 if (mNITQueue.size())
1064 {
1065 QueuedNIT *qn=mNITQueue.front();
1066 mNITQueue.pop();
1071 InfoLog(<< "checkNITQueue - sending queued NIT:" << mLastSentNITRequest->brief());
1073 delete qn;
1074 }
1075}
1076
1078{
1079public:
1080 InviteSessionReferCommand(const InviteSessionHandle& inviteSessionHandle, const NameAddr& referTo, bool referSub)
1081 : mInviteSessionHandle(inviteSessionHandle),
1082 mReferTo(referTo),
1083 mReferSub(referSub)
1084 {
1085
1086 }
1087
1088 void executeCommand() override
1089 {
1090 if(mInviteSessionHandle.isValid())
1091 {
1092 mInviteSessionHandle->refer(mReferTo, mReferSub);
1093 }
1094 }
1095
1097 {
1098 return strm << "InviteSessionReferCommand";
1099 }
1100
1101private:
1102 InviteSessionHandle mInviteSessionHandle;
1103 NameAddr mReferTo;
1104 bool mReferSub;
1105};
1106
1107void
1108InviteSession::referCommand(const NameAddr& referTo, bool referSub)
1109{
1110 mDum.post(new InviteSessionReferCommand(getSessionHandle(), referTo, referSub));
1111}
1112
1113void
1114InviteSession::refer(const NameAddr& referTo, InviteSessionHandle sessionToReplace, bool referSub)
1115{
1116 refer(referTo, sessionToReplace, nullptr, referSub);
1117}
1118
1119void
1120InviteSession::refer(const NameAddr& referTo, InviteSessionHandle sessionToReplace, std::unique_ptr<resip::Contents> contents, bool referSub)
1121{
1122 if (!sessionToReplace.isValid())
1123 {
1124 throw UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__);
1125 }
1126
1127 CallId replaces;
1128 DialogId id = sessionToReplace->mDialog.getId();
1129 replaces.value() = id.getCallId();
1130 replaces.param(p_toTag) = id.getRemoteTag();
1131 replaces.param(p_fromTag) = id.getLocalTag();
1132
1133 refer(referTo, replaces, std::move(contents), referSub);
1134}
1135
1136void
1137InviteSession::refer(const NameAddr& referTo, const CallId& replaces, bool referSub)
1138{
1139 refer(referTo, replaces, nullptr, referSub);
1140}
1141
1142void
1143InviteSession::refer(const NameAddr& referTo, const CallId& replaces, std::unique_ptr<resip::Contents> contents, bool referSub)
1144{
1145 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
1146 {
1147 auto refer = std::make_shared<SipMessage>();
1148 mDialog.makeRequest(*refer, REFER, mNitState == NitComplete); // only increment CSeq if not going to queue NIT
1149 refer->setContents(std::move(contents));
1150 refer->header(h_ReferTo) = referTo;
1151 refer->header(h_ReferredBy) = myAddr();
1152 refer->header(h_ReferredBy).remove(p_tag);
1153
1154 refer->header(h_ReferTo).uri().embedded().header(h_Replaces) = replaces;
1155
1156 if (!referSub)
1157 {
1158 refer->header(h_ReferSub).value() = "false";
1159 refer->header(h_Supporteds).push_back(Token(Symbols::NoReferSub));
1160 }
1161
1162 if(mNitState == NitComplete)
1163 {
1165 mReferSub = referSub;
1167 send(refer);
1168 return;
1169 }
1170 mNITQueue.push(new QueuedNIT(refer,referSub));
1171 InfoLog(<< "refer/replace - queuing NIT:" << refer->brief());
1172 return;
1173 }
1174 else
1175 {
1176 WarningLog (<< "Can't refer before Connected");
1177 resip_assert(0);
1178 throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__);
1179 }
1180}
1181
1183{
1184public:
1185 InviteSessionReferExCommand(const InviteSessionHandle& inviteSessionHandle, const NameAddr& referTo, InviteSessionHandle sessionToReplace, bool referSub)
1186 : mInviteSessionHandle(inviteSessionHandle),
1187 mSessionToReplace(sessionToReplace),
1188 mReferTo(referTo),
1189 mReferSub(referSub)
1190 {
1191 }
1192
1193 void executeCommand() override
1194 {
1195 if(mInviteSessionHandle.isValid())
1196 {
1197 mInviteSessionHandle->refer(mReferTo, mSessionToReplace, mReferSub);
1198 }
1199 }
1200
1202 {
1203 return strm << "InviteSessionReferExCommand";
1204 }
1205
1206private:
1207 InviteSessionHandle mInviteSessionHandle;
1208 InviteSessionHandle mSessionToReplace;
1209 NameAddr mReferTo;
1210 bool mReferSub;
1211};
1212
1213void
1214InviteSession::referCommand(const NameAddr& referTo, InviteSessionHandle sessionToReplace, bool referSub)
1215{
1216 mDum.post(new InviteSessionReferExCommand(getSessionHandle(), referTo, sessionToReplace, referSub));
1217}
1218
1219void
1221{
1222 auto info = std::make_shared<SipMessage>();
1223 mDialog.makeRequest(*info, INFO, mNitState == NitComplete); // only increment CSeq if not going to queue NIT
1224 // !jf! handle multipart here
1225 info->setContents(&contents);
1227 if (mNitState == NitComplete)
1228 {
1231 send(info);
1232 return;
1233 }
1234 mNITQueue.push(new QueuedNIT(info));
1235 InfoLog(<< "info - queuing NIT:" << info->brief());
1236 return;
1237}
1238
1240{
1241public:
1242 InviteSessionInfoCommand(const InviteSessionHandle& inviteSessionHandle, const Contents& contents)
1243 : mInviteSessionHandle(inviteSessionHandle),
1244 mContents(contents.clone())
1245 {
1246 }
1247
1248 void executeCommand() override
1249 {
1250 if(mInviteSessionHandle.isValid())
1251 {
1252 mInviteSessionHandle->info(*mContents);
1253 }
1254 }
1255
1257 {
1258 return strm << "InviteSessionInfoCommand";
1259 }
1260private:
1261 InviteSessionHandle mInviteSessionHandle;
1262 std::unique_ptr<Contents> mContents;
1263};
1264
1265void
1267{
1269}
1270
1271void
1273{
1274 auto message = std::make_shared<SipMessage>();
1275 mDialog.makeRequest(*message, MESSAGE, mNitState == NitComplete); // only increment CSeq if not going to queue NIT
1276 // !jf! handle multipart here
1277 message->setContents(&contents);
1279 InfoLog (<< "Trying to send MESSAGE: " << message);
1280 if (mNitState == NitComplete)
1281 {
1284 send(message);
1285 return;
1286 }
1287 mNITQueue.push(new QueuedNIT(message));
1288 InfoLog(<< "message - queuing NIT:" << message->brief());
1289 return;
1290}
1291
1293{
1294public:
1295 InviteSessionMessageCommand(const InviteSessionHandle& inviteSessionHandle, const Contents& contents)
1296 : mInviteSessionHandle(inviteSessionHandle),
1297 mContents(contents.clone())
1298 {
1299 }
1300
1301 void executeCommand() override
1302 {
1303 if(mInviteSessionHandle.isValid())
1304 {
1305 mInviteSessionHandle->message(*mContents);
1306 }
1307 }
1308
1310 {
1311 return strm << "InviteSessionMessageCommand";
1312 }
1313private:
1314 InviteSessionHandle mInviteSessionHandle;
1315 std::unique_ptr<Contents> mContents;
1316};
1317
1318
1319void
1324
1325void
1327{
1328 // Look for 2xx retransmissions - resend ACK and filter out of state machine
1329 if(msg.header(h_CSeq).method() == INVITE && msg.isResponse() && msg.header(h_StatusLine).statusCode() / 100 == 2)
1330 {
1331 AckMap::iterator i = mAcks.find(msg.getTransactionId());
1332 if (i != mAcks.end())
1333 {
1334 send(i->second); // resend ACK
1335 return;
1336 }
1337 }
1338
1339 // !jf! do we need to handle 3xx here or is it handled elsewhere?
1340 switch (mState)
1341 {
1342 case Connected:
1343 dispatchConnected(msg);
1344 break;
1345 case SentUpdate:
1346 dispatchSentUpdate(msg);
1347 break;
1348 case SentReinvite:
1350 break;
1353 break;
1356 break;
1357 case SentUpdateGlare:
1358 case SentReinviteGlare:
1359 case SentOptionsGlare:
1360 // The behavior is the same except for timer which is handled in dispatch(Timer)
1361 dispatchGlare(msg);
1362 break;
1365 break;
1366 case SentOptions:
1368 break;
1369 case ReceivedUpdate:
1370 case ReceivedReinvite:
1373 break;
1376 break;
1377 case Answered:
1378 dispatchAnswered(msg);
1379 break;
1380 case WaitingToOffer:
1382 break;
1385 break;
1386 case WaitingToTerminate:
1388 break;
1389 case WaitingToHangup:
1391 break;
1392 case Terminated:
1393 dispatchTerminated(msg);
1394 break;
1395 case Undefined:
1396 default:
1397 resip_assert(0);
1398 break;
1399 }
1400
1402}
1403
1404void
1406{
1407 if (timeout.type() == DumTimeout::Retransmit200)
1408 {
1410 mInvite200 &&
1411 timeout.seq() == mInvite200->header(h_CSeq).sequence())
1412 {
1413 InfoLog(<< "Retransmitting: " << endl << mInvite200->brief());
1414 //DumHelper::setOutgoingEncryptionLevel(*mInvite200, mCurrentEncryptionLevel);
1418 }
1419 else if (mCurrentRetransmit200) // we ARE retransmitting, but this timer is for a superseded 2xx
1420 {
1421 InfoLog(<< "Ignoring stale Retransmit200 timer: seq=" << timeout.seq()
1422 << " current=" << (mInvite200 ? mInvite200->header(h_CSeq).sequence() : 0u));
1423 }
1424 // else: mCurrentRetransmit200 == 0 -> ACK already received; nothing to do (matches prior silent behaviour)
1425 }
1426 else if (timeout.type() == DumTimeout::WaitForAck)
1427 {
1428 if (mCurrentRetransmit200) // If retransmit200 timer is active then ACK is not received yet
1429 {
1430 if (mInvite200 && timeout.seq() == mInvite200->header(h_CSeq).sequence())
1431 {
1432 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1433
1434 // If we are waiting for an Ack and it times out, then end with a BYE
1435 if (mState == UAS_WaitingToHangup ||
1437 {
1438 const auto msg = sendBye();
1440 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::LocalBye, msg.get());
1441 }
1443 {
1448 mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), 0);
1449 }
1450 else if (mState == WaitingToOffer ||
1452 {
1454 mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle());
1455 if (!isTerminated())
1456 {
1458 }
1459 }
1460 else if (mState == WaitingToRequestOffer ||
1462 {
1463 mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle());
1464 if (!isTerminated())
1465 {
1466 requestOffer();
1467 }
1468 }
1469 else
1470 {
1471 // this is so the app can decided to ignore this. default implementation
1472 // will call end next
1473 mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle());
1474 }
1475 }
1476 }
1477 }
1478 else if (timeout.type() == DumTimeout::CanDiscardAck)
1479 {
1480 AckMap::iterator i = mAcks.find(timeout.transactionId());
1481 if (i != mAcks.end())
1482 {
1483 mAcks.erase(i);
1484 }
1485 }
1486 else if (timeout.type() == DumTimeout::Glare)
1487 {
1488 if (mState == SentUpdateGlare)
1489 {
1491
1492 InfoLog(<< "Retransmitting the UPDATE (glare condition timer)");
1493 mDialog.makeRequest(*mLastLocalSessionModification, UPDATE); // increments CSeq
1495 }
1496 else if (mState == SentOptionsGlare)
1497 {
1499
1500 InfoLog(<< "Retransmitting the OPTIONS (glare condition timer)");
1501 mDialog.makeRequest(*mLastLocalSessionModification, OPTIONS); // increments CSeq
1503 }
1504 else if (mState == SentReinviteGlare)
1505 {
1507
1508 InfoLog(<< "Retransmitting the reINVITE (glare condition timer)");
1509 mDialog.makeRequest(*mLastLocalSessionModification, INVITE); // increments CSeq
1512 }
1513 else if (mState == SentReinviteNoOfferGlare)
1514 {
1516
1517 InfoLog(<< "Retransmitting the reINVITE-nooffer (glare condition timer)");
1518 mDialog.makeRequest(*mLastLocalSessionModification, INVITE); // increments CSeq
1521 }
1522 }
1523 else if (timeout.type() == DumTimeout::StaleReInvite)
1524 {
1525 if (timeout.seq() == mStaleReInviteTimerSeq)
1526 {
1528 {
1529 const auto msg = sendBye();
1531 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::LocalBye, msg.get());
1532 }
1534 {
1538
1539 // this is so the app can decide to ignore this. default implementation
1540 // will call end next - which will send a BYE
1541 mDum.mInviteSessionHandler->onStaleReInviteTimeout(getSessionHandle());
1542 }
1543 }
1544 }
1545 else if (timeout.type() == DumTimeout::SessionExpiration)
1546 {
1547 // Note: a session timer can still be pending when the session ends - the timer sequence numbers
1548 // are only bumped when the timers are restarted - and DUM will deliver it as long as this
1549 // usage hasn't been destroyed yet. There is nothing left to expire at that point, and the
1550 // application has already been told the session is over (or has asked to end it -
1551 // isTerminated() covers WaitingToTerminate and friends too).
1552 if (timeout.seq() == mSessionExpirationTimerSeq && !isTerminated())
1553 {
1555 {
1556 // Don't expire the session while we still have a request outstanding in the stack. The
1557 // application will destroy this session as soon as it's told the session expired, and the
1558 // stack would then be left retransmitting a request that belongs to a session that no
1559 // longer exists. Wait for the modification to complete instead - if it succeeds these
1560 // timers are restarted and this expiry is dropped, and if it fails the application is told
1561 // the session expired at that point. Note: mSessionInterval is at least 90 seconds and
1562 // refreshes are sent at mSessionInterval/2, so even waiting out TimerF still leaves us
1563 // expiring the session before the negotiated expiry time.
1564 InfoLog(<< "Session expired while a session modification is in flight - deferring expiry until it completes");
1566 }
1567 else
1568 {
1569 // this is so the app can decide to ignore this. default implementation
1570 // will call end next - which will send a BYE
1571 mDum.mInviteSessionHandler->onSessionExpired(getSessionHandle());
1572 }
1573 }
1574 }
1575 else if (timeout.type() == DumTimeout::SessionRefresh)
1576 {
1577 if (timeout.seq() == mSessionRefreshTimerSeq)
1578 {
1579 // Note: If not connected then we must be issueing a reinvite/update or
1580 // receiving one - in either case the session timer stuff will get
1581 // reset/renegotiated - thus just ignore this referesh
1582 if (mState == Connected)
1583 {
1585 // Set a timer for another refresh attempt that will get used if we don't get a success response to this refresh attempt
1586 // Retry at half the previous refresh interval, but no less than 32 seconds
1587 unsigned long refreshRetryDuration = timeout.duration() / 2;
1588 if (refreshRetryDuration > 32)
1589 {
1590 mDum.addTimer(DumTimeout::SessionRefresh, refreshRetryDuration, getBaseHandle(), ++mSessionRefreshTimerSeq);
1591 InfoLog(<< "Started Session Refresh Retry Timer for " << refreshRetryDuration << " seconds");
1592 }
1593 }
1594 }
1595 }
1596
1598}
1599
1600void
1602{
1603 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1604 std::unique_ptr<Contents> offerAnswer = InviteSession::getOfferAnswer(msg);
1605
1606 switch (toEvent(msg, offerAnswer.get()))
1607 {
1608 case OnInvite:
1609 case OnInviteReliable:
1612 handler->onOfferRequired(getSessionHandle(), msg);
1613 break;
1614
1615 case OnInviteOffer:
1620 mProposedRemoteOfferAnswer = std::move(offerAnswer);
1621
1623 break;
1624
1625 case On2xx:
1626 case On2xxOffer:
1627 case On2xxAnswer:
1628 // retransmission of 200I
1629 // !jf! Need to include the answer here.
1630 sendAck();
1631 break;
1632
1633 case OnUpdateOffer:
1635
1636 // !kh!
1637 // Find out if it's an UPDATE requiring state change.
1638 // See rfc3311 5.2, 4th paragraph.
1641 mProposedRemoteOfferAnswer = std::move(offerAnswer);
1643 break;
1644
1645 case OnUpdate:
1646 {
1647 // ?slg? no offerAnswer in update - just respond immediately (likely session timer) - do we need a callback?
1648 auto response = std::make_shared<SipMessage>();
1649 mDialog.makeResponse(*response, msg, 200);
1650 handleSessionTimerRequest(*response, msg);
1651 send(response);
1652 break;
1653 }
1654
1655 case OnUpdateRejected:
1656 case On200Update:
1657 WarningLog (<< "DUM delivered an UPDATE response in an incorrect state " << endl << msg);
1658 resip_assert(0);
1659 break;
1660
1661 case OnAck:
1662 case OnAckAnswer:
1663 // Stop retransmitting our 2xx once its ACK arrives; correlate by CSeq sequence (RFC 3261 13.2.2.4) - see isAckForCurrent200()
1664 if (!isAckForCurrent200(msg))
1665 {
1666 InfoLog(<< "dropped stale or duplicate ACK");
1667 }
1668 else
1669 {
1670 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1671 handler->onAckReceived(getSessionHandle(), msg);
1672 }
1673 break;
1674
1675 default:
1676 dispatchOthers(msg);
1677 break;
1678 }
1679}
1680
1681void
1683{
1684 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1685 std::unique_ptr<Contents> offerAnswer = InviteSession::getOfferAnswer(msg);
1686
1687 switch (toEvent(msg, offerAnswer.get()))
1688 {
1689 case OnInvite:
1690 case OnInviteReliable:
1691 case OnInviteOffer:
1693 case OnUpdate:
1694 case OnUpdateOffer:
1695 {
1696 // glare
1697 auto response = std::make_shared<SipMessage>();
1698 mDialog.makeResponse(*response, msg, 491);
1699 send(response);
1700 break;
1701 }
1702
1703 case On200Update:
1706 if (offerAnswer && mProposedLocalOfferAnswer)
1707 {
1710
1711 mCurrentRemoteOfferAnswer = std::move(offerAnswer);
1712 if (mDum.mDialogEventStateManager)
1713 {
1714 // New Offer/Answer - generate a new confirmed callback with updated SDP
1715 mDum.mDialogEventStateManager->onConfirmed(mDialog, getSessionHandle());
1716 }
1718 }
1720 {
1721 // If we sent an offer in the Update Request and no answer is received
1722 handler->onIllegalNegotiation(getSessionHandle(), msg);
1725 }
1726 break;
1727
1728 case On491Update:
1730 start491Timer();
1731 break;
1732
1733 case On422Update: // session timer
1734 if(msg.exists(h_MinSE))
1735 {
1736 // Change interval to min from 422 response
1737 mSessionInterval = msg.header(h_MinSE).value();
1740 }
1741 else
1742 {
1743 // Response must contain Min_SE - if not - just ignore
1744 // ?slg? callback?
1748 }
1749 break;
1750
1751 case OnUpdateRejected:
1754 handler->onOfferRejected(getSessionHandle(), &msg);
1755 break;
1756
1757 case OnGeneralFailure:
1759 {
1760 break;
1761 }
1762 sendBye();
1765 break;
1766
1767 default:
1768 dispatchOthers(msg);
1769 break;
1770 }
1771}
1772
1773void
1775{
1776 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1777 std::unique_ptr<Contents> offerAnswer = InviteSession::getOfferAnswer(msg);
1778
1779 switch (toEvent(msg, offerAnswer.get()))
1780 {
1781 case OnInvite:
1782 case OnInviteReliable:
1783 case OnInviteOffer:
1785 case OnUpdate:
1786 case OnUpdateOffer:
1787 {
1788 auto response = std::make_shared<SipMessage>();
1789 mDialog.makeResponse(*response, msg, 491);
1790 send(response);
1791 break;
1792 }
1793
1794 case On1xx:
1795 case On1xxEarly:
1796 // Some UA's send a 100 response to a ReInvite - just ignore it
1797 break;
1798
1799 case On2xxAnswer:
1800 case On2xxOffer: // .slg. doesn't really make sense - should be in SentReinviteNoOffer to get this
1801 {
1806
1807 // !jf! I need to potentially include an answer in the ACK here
1808 sendAck();
1810
1812 {
1814
1815 if (*mCurrentRemoteOfferAnswer != *offerAnswer)
1816 {
1817 mCurrentRemoteOfferAnswer = std::move(offerAnswer);
1818 if (mDum.mDialogEventStateManager)
1819 {
1820 // New Offer/Answer - generate a new confirmed callback with updated SDP
1821 mDum.mDialogEventStateManager->onConfirmed(mDialog, getSessionHandle());
1822 }
1824 }
1825 }
1826 else
1827 {
1828 mCurrentRemoteOfferAnswer = std::move(offerAnswer);
1829 if (mDum.mDialogEventStateManager)
1830 {
1831 // New Offer/Answer - generate a new confirmed callback with updated SDP
1832 mDum.mDialogEventStateManager->onConfirmed(mDialog, getSessionHandle());
1833 }
1835 }
1836
1837 // !jf! do I need to allow a reINVITE overlapping the retransmission of
1838 // the ACK when a 200I is received? If yes, then I need to store all
1839 // ACK messages for 64*T1
1840 break;
1841 }
1842 case On2xx:
1844 sendAck();
1847 handler->onIllegalNegotiation(getSessionHandle(), msg);
1850 break;
1851
1852 case On422Invite:
1854 if(msg.exists(h_MinSE))
1855 {
1856 // Change interval to min from 422 response
1857 mSessionInterval = msg.header(h_MinSE).value();
1860 }
1861 else
1862 {
1863 // Response must contact Min_SE - if not - just ignore
1864 // ?slg? callback?
1868 }
1869 break;
1870
1871 case On491Invite:
1874 start491Timer();
1875 break;
1876
1877 case OnGeneralFailure:
1880 {
1881 break;
1882 }
1883 sendBye();
1886 break;
1887
1888 case OnInviteFailure:
1889 case On487Invite:
1893 handler->onOfferRejected(getSessionHandle(), &msg);
1894 break;
1895
1896 case OnAck:
1897 case OnAckAnswer:
1898 // Stop retransmitting our 2xx once its ACK arrives; correlate by CSeq sequence (RFC 3261 13.2.2.4) - see isAckForCurrent200()
1899 if (!isAckForCurrent200(msg))
1900 {
1901 InfoLog(<< "dropped stale or duplicate ACK");
1902 }
1903 else
1904 {
1905 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1906 handler->onAckReceived(getSessionHandle(), msg);
1907 }
1908 break;
1909
1910 default:
1911 dispatchOthers(msg);
1912 break;
1913 }
1914}
1915
1916void
1918{
1919 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1920 std::unique_ptr<Contents> offerAnswer = InviteSession::getOfferAnswer(msg);
1921
1922 switch (toEvent(msg, offerAnswer.get()))
1923 {
1924 case OnInvite:
1925 case OnInviteReliable:
1926 case OnInviteOffer:
1928 case OnUpdate:
1929 case OnUpdateOffer:
1930 {
1931 auto response = std::make_shared<SipMessage>();
1932 mDialog.makeResponse(*response, msg, 491);
1933 send(response);
1934 break;
1935 }
1936
1937 case On1xx:
1938 case On1xxEarly:
1939 // Some UA's send a 100 response to a ReInvite - just ignore it
1940 break;
1941
1942 case On2xxAnswer: // .slg. doesn't really make sense - should be in SentReinvite to get this
1943 case On2xxOffer:
1944 {
1949 mProposedRemoteOfferAnswer = std::move(offerAnswer);
1951 break;
1952 }
1953
1954 case On2xx:
1956 sendAck();
1959 handler->onIllegalNegotiation(getSessionHandle(), msg);
1962 break;
1963
1964 case On422Invite:
1966 if(msg.exists(h_MinSE))
1967 {
1968 // Change interval to min from 422 response
1969 mSessionInterval = msg.header(h_MinSE).value();
1972 }
1973 else
1974 {
1975 // Response must contact Min_SE - if not - just ignore
1976 // ?slg? callback?
1980 }
1981 break;
1982
1983 case On491Invite:
1986 start491Timer();
1987 break;
1988
1989 case OnGeneralFailure:
1992 {
1993 break;
1994 }
1995 sendBye();
1998 break;
1999
2000 case OnInviteFailure:
2001 case On487Invite:
2005 handler->onOfferRejected(getSessionHandle(), &msg);
2006 break;
2007
2008 default:
2009 dispatchOthers(msg);
2010 break;
2011 }
2012}
2013
2014void
2016{
2017 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2018 std::unique_ptr<Contents> offerAnswer = InviteSession::getOfferAnswer(msg);
2019
2020 switch (toEvent(msg, offerAnswer.get()))
2021 {
2022 case OnInvite:
2023 case OnInviteReliable:
2024 case OnInviteOffer:
2026 case OnUpdate:
2027 case OnUpdateOffer:
2028 {
2029 // glare
2030 auto response = std::make_shared<SipMessage>();
2031 mDialog.makeResponse(*response, msg, 491);
2032 send(response);
2033 break;
2034 }
2035
2036 case On200Options:
2039 break;
2040
2041 case On491Options:
2043 start491Timer();
2044 break;
2045
2046 case OnGeneralFailure:
2048 {
2049 break;
2050 }
2051 sendBye();
2054 break;
2055
2056 default:
2057 dispatchOthers(msg);
2058 break;
2059 }
2060}
2061
2062void
2064{
2065 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2066 std::unique_ptr<Contents> offerAnswer = InviteSession::getOfferAnswer(msg);
2067
2068 switch (toEvent(msg, offerAnswer.get()))
2069 {
2070 case OnInvite:
2071 case OnInviteReliable:
2072 case OnInviteOffer:
2074 case OnUpdate:
2075 case OnUpdateOffer:
2076 {
2077 auto response = std::make_shared<SipMessage>();
2078 mDialog.makeResponse(*response, msg, 491);
2079 send(response);
2080 break;
2081 }
2082 case OnAckAnswer:
2083 // Stop retransmitting our 2xx once its ACK arrives; correlate by CSeq sequence (RFC 3261 13.2.2.4) - see isAckForCurrent200()
2084 if (!isAckForCurrent200(msg))
2085 {
2086 InfoLog(<< "dropped stale or duplicate ACK");
2087 }
2088 else
2089 {
2092 mCurrentRemoteOfferAnswer = std::move(offerAnswer);
2094 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
2095
2096 if (mDum.mDialogEventStateManager)
2097 {
2098 // New Offer/Answer - generate a new confirmed callback with updated SDP
2099 mDum.mDialogEventStateManager->onConfirmed(mDialog, getSessionHandle());
2100 }
2102 }
2103 break;
2104 case OnAck:
2105 // Stop retransmitting our 2xx once its ACK arrives; correlate by CSeq sequence (RFC 3261 13.2.2.4) - see isAckForCurrent200()
2106 if (!isAckForCurrent200(msg))
2107 {
2108 InfoLog(<< "dropped stale or duplicate ACK");
2109 }
2110 else
2111 {
2112 InfoLog(<< "Got Ack with no answer");
2116 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
2118 handler->onOfferRejected(getSessionHandle(), &msg);
2119 }
2120 break;
2121 default:
2122 dispatchOthers(msg);
2123 break;
2124 }
2125}
2126
2127void
2129{
2130 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2131 MethodTypes method = msg.header(h_CSeq).method();
2132 if (msg.isRequest() && (method == INVITE || method == UPDATE))
2133 {
2134 DebugLog(<< "Re-INVITE or UPDATE received when in SentReinviteGlare or SentUpdateGlare" << endl);
2135 // Received inbound reinvite or update, when waiting to resend outbound reinvite or update
2136 handler->onOfferRejected(getSessionHandle(), &msg);
2137 if(!isTerminated()) // make sure application didn't call end()
2138 {
2139 dispatchConnected(msg); // act as if we received message in Connected state
2140 }
2141 else
2142 {
2143 dispatchTerminated(msg);
2144 }
2145 }
2146 else
2147 {
2148 dispatchOthers(msg);
2149 }
2150}
2151
2152void
2154{
2155 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2156 MethodTypes method = msg.header(h_CSeq).method();
2157 if (msg.isRequest() && (method == INVITE || method == UPDATE))
2158 {
2159 // Received inbound reinvite or update, when waiting to resend outbound reinvite or update
2161 if(!isTerminated()) // make sure application didn't call end()
2162 {
2163 dispatchConnected(msg); // act as if we received message in Connected state
2164 }
2165 else
2166 {
2167 dispatchTerminated(msg);
2168 }
2169 }
2170 else
2171 {
2172 dispatchOthers(msg);
2173 }
2174}
2175
2176void
2178{
2179 // InviteSessionHandler* handler = mDum.mInviteSessionHandler; // unused
2180 std::unique_ptr<Contents> offerAnswer = InviteSession::getOfferAnswer(msg);
2181
2182 switch (toEvent(msg, offerAnswer.get()))
2183 {
2184 case OnInvite:
2185 case OnInviteReliable:
2186 case OnInviteOffer:
2188 case OnUpdate:
2189 case OnUpdateOffer:
2190 {
2191 // Means that the UAC has sent us a second reINVITE or UPDATE before we
2192 // responded to the first one. Bastard!
2193 auto response = std::make_shared<SipMessage>();
2194 mDialog.makeResponse(*response, msg, 500);
2195 response->header(h_RetryAfter).value() = Random::getRandom() % 10;
2196 send(response);
2197 break;
2198 }
2199 case OnBye:
2200 {
2201 // BYE received after a reINVITE, terminate the reINVITE transaction.
2202 auto response = std::make_shared<SipMessage>();
2203 mDialog.makeResponse(*response, *mLastRemoteSessionModification, 487); // Request Terminated
2205 send(response);
2206
2207 dispatchBye(msg);
2208 break;
2209 }
2210 default:
2211 dispatchOthers(msg);
2212 break;
2213 }
2214}
2215
2216
2217void
2219{
2220 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
2221 {
2222 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
2224 }
2225 else
2226 {
2227 dispatchOthers(msg);
2228 }
2229}
2230
2231void
2233{
2234 if (msg.isResponse() &&
2235 msg.header(h_CSeq).method() == INVITE &&
2236 msg.header(h_StatusLine).statusCode() / 200 == 1)
2237 {
2238 // Receiving a 200 retransmission is possible - but we don't have an ACK response yet - we are still waiting for provideAnswer to be
2239 // called by the app - so just drop the retransmission
2240 return;
2241 }
2242 dispatchOthers(msg);
2243}
2244
2245void
2247{
2248 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
2249 {
2251 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
2253 }
2254 else
2255 {
2256 dispatchOthers(msg);
2257 }
2258}
2259
2260void
2262{
2263 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
2264 {
2265 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
2266 requestOffer();
2267 }
2268 else
2269 {
2270 dispatchOthers(msg);
2271 }
2272}
2273
2274void
2276{
2277 if (msg.isResponse() &&
2278 msg.header(h_CSeq).method() == INVITE)
2279 {
2280 if(msg.header(h_StatusLine).statusCode() / 200 == 1) // Note: stack ACK's non-2xx final responses only
2281 {
2282 // !jf! Need to include the answer here.
2283 sendAck();
2284 }
2285 const auto bye = sendBye();
2287 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::LocalBye, bye.get());
2288 }
2289 else if(msg.isRequest())
2290 {
2291 if(msg.method() == BYE)
2292 {
2293 dispatchBye(msg);
2294 }
2295 else
2296 {
2297 auto response = std::make_shared<SipMessage>();
2298 mDialog.makeResponse(*response, msg, 400 /* Bad Request */);
2299 send(response);
2300 }
2301 }
2302}
2303
2304void
2306{
2307 const auto offerAnswer = InviteSession::getOfferAnswer(msg);
2308
2309 switch (toEvent(msg, offerAnswer.get()))
2310 {
2311 case OnAck:
2312 case OnAckAnswer:
2313 // Stop retransmitting our 2xx once its ACK arrives; correlate by CSeq sequence (RFC 3261 13.2.2.4) - see isAckForCurrent200()
2314 if (!isAckForCurrent200(msg))
2315 {
2316 InfoLog(<< "dropped stale or duplicate ACK");
2317 }
2318 else
2319 {
2320 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
2321
2322 const auto bye = sendBye();
2324 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::LocalBye, bye.get());
2325 }
2326 break;
2327
2328 default:
2329 break;
2330 }
2331}
2332
2333void
2335{
2336 InfoLog (<< "InviteSession::dispatchTerminated " << msg.brief());
2337
2338 if (msg.isRequest())
2339 {
2340 if (BYE == msg.header(h_CSeq).method())
2341 {
2342 auto response = std::make_shared<SipMessage>();
2343 mDialog.makeResponse(*response, msg, 200);
2344 send(response);
2345 }
2346 else if(ACK != msg.header(h_CSeq).method()) // Shouldn't 481 an ACK
2347 {
2348 auto response = std::make_shared<SipMessage>();
2349 mDialog.makeResponse(*response, msg, 481);
2350 send(response);
2351 }
2352
2353 // !jf! means the peer sent BYE while we are waiting for response to BYE
2354 //mDum.destroy(this);
2355 }
2356 else
2357 {
2358 mDum.destroy(this);
2359 }
2360}
2361
2362void
2363InviteSession::dispatchOthers(const SipMessage& msg)
2364{
2365 // handle OnGeneralFailure
2366 // handle OnRedirect
2367 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2368
2369 switch (msg.header(h_CSeq).method())
2370 {
2371 case INVITE:
2372 case UPDATE:
2373 if (msg.isRequest())
2374 {
2375 auto response = std::make_shared<SipMessage>();
2376 mDialog.makeResponse(*response, msg, 491);
2377 send(response);
2378 }
2379 break;
2380 case PRACK:
2381 dispatchPrack(msg);
2382 break;
2383 case CANCEL:
2384 dispatchCancel(msg);
2385 break;
2386 case BYE:
2387 dispatchBye(msg);
2388 break;
2389 case INFO:
2390 dispatchInfo(msg);
2391 break;
2392 case MESSAGE:
2393 dispatchMessage(msg);
2394 break;
2395 case ACK:
2396 // Stop retransmitting our 2xx once its ACK arrives; correlate by CSeq sequence (RFC 3261 13.2.2.4) - see isAckForCurrent200()
2397 if (!isAckForCurrent200(msg))
2398 {
2399 InfoLog(<< "dropped stale or duplicate ACK");
2400 }
2401 else
2402 {
2403 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
2404 handler->onAckReceived(getSessionHandle(), msg);
2405 }
2406 break;
2407 case OPTIONS:
2408 dispatchOptions(msg);
2409 break;
2410 default:
2411 // handled in Dialog
2412 WarningLog (<< "DUM delivered a "
2413 << msg.header(h_CSeq).unknownMethodName()
2414 << " to the InviteSession in state: " << toData(mState)
2415 << endl
2416 << msg);
2417 resip_assert(0);
2418 break;
2419 }
2420}
2421
2422void
2424{
2425 if(msg.isRequest())
2426 {
2427 auto response = std::make_shared<SipMessage>();
2428 mDialog.makeResponse(*response, msg, 200);
2429 handleSessionTimerRequest(*response, msg);
2430 send(response);
2431 }
2432}
2433
2434void
2435InviteSession::dispatchUnhandledInvite(const SipMessage& msg)
2436{
2437 resip_assert(msg.isRequest());
2438 resip_assert(msg.header(h_CSeq).method() == INVITE);
2439
2440 // If we get an INVITE request from the wire and we are not in
2441 // Connected state, reject the request and send a BYE
2442 auto response = std::make_shared<SipMessage>();
2443 mDialog.makeResponse(*response, msg, 400); // !jf! what code to use?
2444 InfoLog (<< "Sending " << response->brief());
2445 send(response);
2446
2447 sendBye();
2449 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Error, &msg);
2450}
2451
2452void
2453InviteSession::dispatchPrack(const SipMessage& msg)
2454{
2455 resip_assert(msg.header(h_CSeq).method() == PRACK);
2456 if(msg.isRequest())
2457 {
2458 auto rsp = std::make_shared<SipMessage>();
2459 mDialog.makeResponse(*rsp, msg, 481);
2460 send(rsp);
2461
2462 sendBye();
2463 // !jf! should we make some other callback here
2465 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Error, &msg);
2466 }
2467 else
2468 {
2469 // ignore. could be PRACK/200
2470 }
2471}
2472
2473void
2474InviteSession::dispatchCancel(const SipMessage& msg)
2475{
2476 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2477 resip_assert(msg.header(h_CSeq).method() == CANCEL);
2478 if(msg.isRequest())
2479 {
2480 auto rsp = std::make_shared<SipMessage>();
2481 mDialog.makeResponse(*rsp, msg, 200);
2482 send(rsp);
2483
2484 sendBye();
2485 // !jf! should we make some other callback here
2487
2489 }
2490 else
2491 {
2492 WarningLog (<< "DUM let me send a CANCEL at an incorrect state " << endl << msg);
2493 resip_assert(0);
2494 }
2495}
2496
2497void
2499{
2500 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2501
2502 if (msg.isRequest())
2503 {
2504 // Check for any non-invite server transactions (e.g. INFO)
2505 // that have not been responded yet and terminate them.
2507 {
2508 mLastNitResponse->header(h_StatusLine).statusCode() = 487;
2509 mLastNitResponse->setContents(0);
2513 }
2514
2515 auto rsp = std::make_shared<SipMessage>();
2516 InfoLog (<< "Received " << msg.brief());
2517 mDialog.makeResponse(*rsp, msg, 200);
2518 send(rsp);
2519
2520 // !jf! should we make some other callback here
2522
2523 if (mDum.mDialogEventStateManager)
2524 {
2525 mDum.mDialogEventStateManager->onTerminated(mDialog, msg, InviteSessionHandler::RemoteBye);
2526 }
2527
2529 mDum.destroy(this);
2530 }
2531 else
2532 {
2533 WarningLog (<< "DUM let me send a BYE at an incorrect state " << endl << msg);
2534 resip_assert(0);
2535 }
2536}
2537
2538void
2540{
2541 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2542 if (msg.isRequest())
2543 {
2545 {
2546 // Means that the UAC has sent us a second INFO before we
2547 // responded to the first one.
2548 auto response = std::make_shared<SipMessage>();
2549 mDialog.makeResponse(*response, msg, 500);
2550 response->header(h_RetryAfter).value() = Random::getRandom() % 10;
2551 send(response);
2552 WarningLog(<<"an INFO message was received before the application called acceptNIT() for the previous INFO message");
2553 }
2554 else
2555 {
2556 InfoLog (<< "Received " << msg.brief());
2559 handler->onInfo(getSessionHandle(), msg);
2560 }
2561 }
2562 else
2563 {
2566 if (msg.header(h_StatusLine).statusCode() >= 300)
2567 {
2568 handler->onInfoFailure(getSessionHandle(), msg);
2569 }
2570 else if (msg.header(h_StatusLine).statusCode() >= 200)
2571 {
2572 handler->onInfoSuccess(getSessionHandle(), msg);
2573 }
2574 nitComplete();
2575 }
2576}
2577
2578void
2579InviteSession::acceptNIT(int statusCode, const Contents * contents)
2580{
2581 if (statusCode / 100 != 2)
2582 {
2583 throw UsageUseException("Must accept with a 2xx", __FILE__, __LINE__);
2584 }
2585
2587 {
2588 throw UsageUseException("No transaction to accept", __FILE__, __LINE__);
2589 }
2590
2591 mLastNitResponse->header(h_StatusLine).statusCode() = statusCode;
2592 mLastNitResponse->setContents(contents);
2593 Helper::getResponseCodeReason(statusCode, mLastNitResponse->header(h_StatusLine).reason());
2596}
2597
2599{
2600public:
2601 InviteSessionAcceptNITCommand(const InviteSessionHandle& inviteSessionHandle, int statusCode, const Contents* contents)
2602 : mInviteSessionHandle(inviteSessionHandle),
2603 mStatusCode(statusCode),
2604 mContents(contents?contents->clone():0)
2605 {
2606
2607 }
2608
2609 void executeCommand() override
2610 {
2611 if(mInviteSessionHandle.isValid())
2612 {
2613 mInviteSessionHandle->acceptNIT(mStatusCode, mContents.get());
2614 }
2615 }
2616
2618 {
2619 return strm << "InviteSessionAcceptNITCommand";
2620 }
2621private:
2622 InviteSessionHandle mInviteSessionHandle;
2623 int mStatusCode;
2624 std::unique_ptr<Contents> mContents;
2625};
2626
2627void
2628InviteSession::acceptNITCommand(int statusCode, const Contents* contents)
2629{
2630 mDum.post(new InviteSessionAcceptNITCommand(getSessionHandle(), statusCode, contents));
2631}
2632
2633void
2635{
2636 if (statusCode < 400)
2637 {
2638 throw UsageUseException("Must reject with a >= 4xx", __FILE__, __LINE__);
2639 }
2640
2642 {
2643 throw UsageUseException("No transaction to reject", __FILE__, __LINE__);
2644 }
2645
2646 mLastNitResponse->header(h_StatusLine).statusCode() = statusCode;
2647 mLastNitResponse->setContents(0);
2648 Helper::getResponseCodeReason(statusCode, mLastNitResponse->header(h_StatusLine).reason());
2651}
2652
2654{
2655public:
2656 InviteSessionRejectNITCommand(const InviteSessionHandle& inviteSessionHandle, int statusCode)
2657 : mInviteSessionHandle(inviteSessionHandle),
2658 mStatusCode(statusCode)
2659 {
2660 }
2661
2662 void executeCommand() override
2663 {
2664 if(mInviteSessionHandle.isValid())
2665 {
2666 mInviteSessionHandle->rejectNIT(mStatusCode);
2667 }
2668 }
2669
2671 {
2672 return strm << "InviteSessionRejectNITCommand";
2673 }
2674private:
2675 InviteSessionHandle mInviteSessionHandle;
2676 int mStatusCode;
2677};
2678
2679void
2684
2685void
2687{
2688 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2689 if (msg.isRequest())
2690 {
2692 {
2693 // Means that the UAC has sent us a second NIT message before we
2694 // responded to the first one.
2695 auto response = std::make_shared<SipMessage>();
2696 mDialog.makeResponse(*response, msg, 500);
2697 response->header(h_RetryAfter).value() = Random::getRandom() % 10;
2698 send(response);
2699 }
2700 else
2701 {
2702 InfoLog (<< "Received " << msg.brief());
2705 mLastNitResponse->header(h_Contacts).clear();
2706 handler->onMessage(getSessionHandle(), msg);
2707 }
2708 }
2709 else
2710 {
2713 if (msg.header(h_StatusLine).statusCode() >= 300)
2714 {
2715 handler->onMessageFailure(getSessionHandle(), msg);
2716 }
2717 else if (msg.header(h_StatusLine).statusCode() >= 200)
2718 {
2719 handler->onMessageSuccess(getSessionHandle(), msg);
2720 }
2721 nitComplete();
2722 }
2723}
2724
2725// RFC 3261 13.2.2.4: the ACK for a 2xx INVITE response carries the same CSeq
2726// sequence number as the INVITE/2xx it acknowledges. mInvite200 holds the 2xx
2727// we are currently retransmitting, so its CSeq sequence number is the correct
2728// (and only reliable) key for deciding whether an inbound ACK terminates that
2729// retransmission. Keying off this - rather than mLastRemoteSessionModification -
2730// is robust under re-INVITE glare (where the session has already moved on to
2731// SentReinvite/Glare for a crossing re-INVITE) and under a peer that re-uses or
2732// does not strictly increase the in-dialog CSeq.
2733bool
2735{
2736 if (!ack.isRequest() || ack.header(h_RequestLine).method() != ACK)
2737 {
2738 return false;
2739 }
2740
2741 if (mCurrentRetransmit200 == 0 || !mInvite200)
2742 {
2743 return false;
2744 }
2745
2746 return ack.header(h_CSeq).sequence() == mInvite200->header(h_CSeq).sequence();
2747}
2748
2749void
2751{
2753 unsigned int seq = mInvite200->header(h_CSeq).sequence();
2756}
2757
2758// RFC3261 section 14.1
2759// If a UAC receives a 491 response to a re-INVITE, it SHOULD start a timer with
2760// a value T chosen as follows:
2761// 1. If the UAC is the owner of the Call-ID of the dialog ID, T has a randomly
2762// chosen value between 2.1 and 4 seconds in units of 10 ms.
2763// 2. If the UAC is not the owner of the Call-ID of the dialog ID, T has a
2764// randomly chosen value of between 0 and 2 seconds in units of 10 ms.
2765void
2767{
2768 unsigned int seq = mLastLocalSessionModification->header(h_CSeq).sequence();
2769
2770 if (dynamic_cast<ClientInviteSession*>(this))
2771 {
2772 int timer = Random::getRandom() % (4000 - 2100);
2773 timer += 2100;
2774 timer -= timer % 10;
2775
2776 DebugLog(<< "491 timer value: " << timer << "ms" << endl);
2777 mDum.addTimerMs(DumTimeout::Glare, timer, getBaseHandle(), seq);
2778 }
2779 else
2780 {
2781 int timer = Random::getRandom() % 2000;
2782 timer -= timer % 10;
2783 DebugLog(<< "491 timer value: " << timer << "ms" << endl);
2784 mDum.addTimerMs(DumTimeout::Glare, timer, getBaseHandle(), seq);
2785 }
2786}
2787
2788void
2790{
2791 InfoLog (<< toData(mState) << ": startStaleReInviteTimer");
2792 unsigned long when = mDialog.mDialogSet.getUserProfile()->getDefaultStaleReInviteTime();
2793
2795 when,
2796 getBaseHandle(),
2798}
2799
2800void
2802{
2803 if(mSessionInterval >= 90) // If mSessionInterval is 0 then SessionTimers are considered disabled
2804 {
2805 msg.header(h_SessionExpires).value() = mSessionInterval;
2806 if(msg.isRequest())
2807 {
2808 msg.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresher ? "uac" : "uas");
2809 }
2810 else
2811 {
2812 msg.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresher ? "uas" : "uac");
2813 }
2814 if(msg.isRequest() ||
2815 (msg.isResponse() && msg.header(h_StatusLine).responseCode() == 422))
2816 {
2817 msg.header(h_MinSE).value() = mMinSE;
2818 }
2819 }
2820 else
2821 {
2822 msg.remove(h_SessionExpires);
2823 msg.remove(h_MinSE);
2824 }
2825}
2826
2827void
2829{
2830 // Note: We could get a 422 to a provideOffer reINVITE, and sessionRefresh can get called.
2831 // In this case we want to make sure we resend the reINVITE and not an UPDATE. The
2832 // mState != SentReinvite check handles this.
2835 {
2838 mLastLocalSessionModification->setContents(0); // Don't send SDP
2839 }
2841 {
2843 auto options = std::make_shared<SipMessage>();
2844 mDialog.makeRequest(*options, OPTIONS);
2845 options->header(h_Accepts) = mDum.getMasterProfile()->getSupportedMimeTypes(OPTIONS);
2846 InfoLog (<< "sessionRefresh: Sending " << options->brief());
2848 send(options);
2849 return;
2850 }
2851 else
2852 {
2856 Contents* sdpToSend;
2858 {
2859 sdpToSend = mProposedLocalOfferAnswer.get();
2860 encryptionLevel = mProposedEncryptionLevel;
2861 }
2862 else
2863 {
2864 sdpToSend = mCurrentLocalOfferAnswer.get();
2866 }
2869 }
2871
2872 InfoLog (<< "sessionRefresh: Sending " << mLastLocalSessionModification->brief());
2875}
2876
2877void
2879{
2880 mSessionInterval = mDialog.mDialogSet.getUserProfile()->getDefaultSessionTime(); // Used only if remote doesn't request a time
2881 if(mSessionInterval != 0)
2882 {
2883 // If session timers are not disabled then ensure interval is greater than or equal to MinSE
2885 }
2886 switch(mDialog.mDialogSet.getUserProfile()->getDefaultSessionTimerMode())
2887 {
2889 mSessionRefresher = true; // Default refresher is Local
2890 break;
2892 mSessionRefresher = false; // Default refresher is Remote
2893 break;
2895 mSessionRefresher = dynamic_cast<ServerInviteSession*>(this) != NULL; // Default refresher is callee
2896 break;
2898 mSessionRefresher = dynamic_cast<ClientInviteSession*>(this) != NULL; // Default refresher is caller
2899 break;
2900 }
2901}
2902
2903void
2905{
2906 // The session expired while a session modification was in flight. If that modification has now
2907 // completed without ending the session, then it's safe to expire the session - the stack is no
2908 // longer retransmitting anything on our behalf.
2910 {
2912 InfoLog(<< "Session modification complete - applying deferred session expiry");
2913 mDum.mInviteSessionHandler->onSessionExpired(getSessionHandle());
2914 }
2915}
2916
2917bool
2919{
2920 // A session modification that was holding off a session expiry has failed outright (a 408 from
2921 // TimerF, for instance). The session is ending because it expired, not because the request
2922 // failed, so report the expiry - the application then sees the same callback it would have seen
2923 // had the modification not been in flight. Returns true if the application ended the session as
2924 // a result; if it chose to ignore the expiry then the caller carries on and ends the session
2925 // anyway, since the modification failed (via standard OnGeneralFailure handling in sent
2926 // update/reinvite states)
2928 {
2929 return false;
2930 }
2931
2932 transition(Connected); // the modification transaction is over
2935 return isTerminated();
2936}
2937
2938void
2940{
2941 // Any previously deferred session expiry belongs to the timers we are about to replace
2943
2944 if(mSessionInterval >= 90) // 90 is the absolute minimum - RFC4028
2945 {
2946 // For session expiration - BYE should be sent a minimum of 32 and one third of the SessionInterval, seconds
2947 // before the session expires (recommended by RFC4028 - section 10):
2948 // Firewalls and NAT ALGs may be very unforgiving about allowing SIP traffic to pass after the expiration
2949 // time of the session. This is why the BYE should be sent before the expiration.
2950 unsigned long aBitBeforeExpiration = mSessionInterval - resipMin((uint32_t)32, mSessionInterval / 3);
2951
2952 // Check if we are the refresher
2954 {
2955 // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by RFC4028)
2957 // Start Session-Expiration Timer to a bit before mSessionInterval
2958 mDum.addTimer(DumTimeout::SessionExpiration, aBitBeforeExpiration, getBaseHandle(), ++mSessionExpirationTimerSeq);
2959 InfoLog(<< "Started Session Refresh Timer for " << mSessionInterval / 2 << " seconds and Session Expiration Timer for " << aBitBeforeExpiration << " seconds (Session Interval is " << mSessionInterval << " seconds)");
2960 }
2961 else
2962 {
2963 // Start Session-Expiration Timer to a bit before mSessionInterval
2964 mDum.addTimer(DumTimeout::SessionExpiration, aBitBeforeExpiration, getBaseHandle(), ++mSessionExpirationTimerSeq);
2965 // increment refresh seq, incase old timers are running and we've switched roles to non refresher
2967 InfoLog(<< "Started Session Expiration Timer for " << aBitBeforeExpiration << " seconds (Session Interval is " << mSessionInterval << " seconds)");
2968 }
2969 }
2970 else // Session Interval less than 90 - consider timers disabled
2971 {
2972 // increment seq, incase old timers are running and now session timers are disabled
2975 }
2976}
2977
2978void
2980{
2981 resip_assert(msg.header(h_CSeq).method() == INVITE || msg.header(h_CSeq).method() == UPDATE || msg.header(h_CSeq).method() == OPTIONS);
2982
2983 bool isOptionsRequest = msg.header(h_CSeq).method() == OPTIONS;
2984
2985 // Allow Re-Invites and Updates (not Options) to update the stored peer capabilities
2986 if (!isOptionsRequest)
2987 {
2989 }
2991 {
2992 // This is an OPTIONS and Session timer is properly supported by both sides, don't let OPTIONS act as a refresh
2993 return;
2994 }
2995
2996 // If session timers are locally supported then handle response
2997 if(mDum.getMasterProfile()->getSupportedOptionTags().find(SessionTimerToken))
2998 {
3000
3001 if(msg.exists(h_Requires) && msg.header(h_Requires).find(SessionTimerToken)
3002 && !msg.exists(h_SessionExpires))
3003 {
3004 // If no Session Expires in response and Requires header is present then session timer is to be 'turned off'
3005 mSessionInterval = 0;
3006 }
3007 // Process Session Timer headers
3008 else if(msg.exists(h_SessionExpires))
3009 {
3010 mSessionInterval = msg.header(h_SessionExpires).value();
3011 if(msg.header(h_SessionExpires).exists(p_refresher))
3012 {
3013 // Remote end specified refresher preference
3014 mSessionRefresher = (msg.header(h_SessionExpires).param(p_refresher) == Data("uac"));
3015 }
3016 }
3017 else
3018 {
3019 // Note: If no Requires or Session-Expires, then UAS does not support Session Timers
3020 // - we are free to use our SessionInterval settings (set above as a default)
3021 // If far end doesn't support then refresher must be local
3022 mSessionRefresher = true;
3023 }
3024
3025 // Update MinSE if specified and longer than current value
3026 if(msg.exists(h_MinSE))
3027 {
3028 mMinSE = resipMax(mMinSE, msg.header(h_MinSE).value());
3029 }
3030
3032 }
3033}
3034
3035void
3037{
3038 resip_assert(request.header(h_CSeq).method() == INVITE || request.header(h_CSeq).method() == UPDATE || request.header(h_CSeq).method() == OPTIONS);
3039
3040 bool isOptionsRequest = request.header(h_CSeq).method() == OPTIONS;
3041
3042 // Allow Re-Invites and Updates (not Options) to update the stored peer capabilities
3043 if (!isOptionsRequest)
3044 {
3045 storePeerCapabilities(request);
3046 }
3048 {
3049 // This is an OPTIONS and Session timer is properly supported by both sides, don't let OPTIONS act as a refresh
3050 return;
3051 }
3052
3053 // If session timers are locally supported then add necessary headers to response
3054 if(mDum.getMasterProfile()->getSupportedOptionTags().find(SessionTimerToken))
3055 {
3056 // Update MinSE if specified and longer than current value
3057 if(request.exists(h_MinSE))
3058 {
3059 mMinSE = resipMax(mMinSE, request.header(h_MinSE).value());
3060 }
3062
3063 // Check if far-end supports
3064 bool farEndSupportsTimer = false;
3065 if(mPeerSupportsSessionTimer) // set in storePeerCapabilities above
3066 {
3067 farEndSupportsTimer = true;
3068 if(request.exists(h_SessionExpires))
3069 {
3070 // Use Session Interval requested by remote - if none then use local settings
3071 mSessionInterval = request.header(h_SessionExpires).value();
3072 if(request.header(h_SessionExpires).exists(p_refresher))
3073 {
3074 mSessionRefresher = (request.header(h_SessionExpires).param(p_refresher) == Data("uas"));
3075 }
3076 }
3077 }
3078 else
3079 {
3080 // If far end doesn't support then refresher must be local
3081 mSessionRefresher = true;
3082 }
3083
3084 // Add Session-Expires to response if needed
3085 if(mSessionInterval >= 90)
3086 {
3087 if(farEndSupportsTimer)
3088 {
3089 // If far end supports session-timer then require it, if not already present
3090 if(!response.header(h_Requires).find(SessionTimerToken))
3091 {
3092 response.header(h_Requires).push_back(SessionTimerToken);
3093 }
3094 }
3095 if (!isOptionsRequest)
3096 {
3097 setSessionTimerHeaders(response);
3098 }
3099 }
3100
3102 }
3103}
3104
3105Data
3107{
3108 switch (state)
3109 {
3110 case Undefined:
3111 return "InviteSession::Undefined";
3112 case Connected:
3113 return "InviteSession::Connected";
3114 case SentUpdate:
3115 return "InviteSession::SentUpdate";
3116 case SentUpdateGlare:
3117 return "InviteSession::SentUpdateGlare";
3118 case SentReinvite:
3119 return "InviteSession::SentReinvite";
3120 case SentReinviteGlare:
3121 return "InviteSession::SentReinviteGlare";
3123 return "InviteSession::SentReinviteNoOffer";
3125 return "InviteSession::SentReinviteAnswered";
3127 return "InviteSession::SentReinviteNoOfferGlare";
3128 case SentOptions:
3129 return "InviteSession::SentOptions";
3130 case SentOptionsGlare:
3131 return "InviteSession::SentOptionsGlare";
3132 case ReceivedUpdate:
3133 return "InviteSession::ReceivedUpdate";
3134 case ReceivedReinvite:
3135 return "InviteSession::ReceivedReinvite";
3137 return "InviteSession::ReceivedReinviteNoOffer";
3139 return "InviteSession::ReceivedReinviteSentOffer";
3140 case Answered:
3141 return "InviteSession::Answered";
3142 case WaitingToOffer:
3143 return "InviteSession::WaitingToOffer";
3145 return "InviteSession::WaitingToRequestOffer";
3146 case WaitingToTerminate:
3147 return "InviteSession::WaitingToTerminate";
3148 case WaitingToHangup:
3149 return "InviteSession::WaitingToHangup";
3150 case Terminated:
3151 return "InviteSession::Terminated";
3152
3153 case UAC_Start:
3154 return "UAC_Start";
3155 case UAS_Offer:
3156 return "UAS_Offer";
3158 return "UAS_OfferProvidedAnswer";
3159 case UAS_EarlyOffer:
3160 return "UAS_EarlyOffer";
3162 return "UAS_EarlyProvidedAnswer";
3163 case UAS_NoOffer:
3164 return "UAS_NoOffer";
3165 case UAS_ProvidedOffer:
3166 return "UAS_ProvidedOffer";
3167 case UAS_EarlyNoOffer:
3168 return "UAS_EarlyNoOffer";
3170 return "UAS_EarlyProvidedOffer";
3171 case UAS_Accepted:
3172 return "UAS_Accepted";
3173 case UAS_WaitingToOffer:
3174 return "UAS_WaitingToOffer";
3176 return "UAS_AcceptedWaitingAnswer";
3177 case UAC_Early:
3178 return "UAC_Early";
3179 case UAC_EarlyWithOffer:
3180 return "UAC_EarlyWithOffer";
3182 return "UAC_EarlyWithAnswer";
3183 case UAC_Answered:
3184 return "UAC_Answered";
3186 return "UAC_SentUpdateEarly";
3188 return "UAC_SentUpdateEarlyGlare";
3190 return "UAC_ReceivedUpdateEarly";
3191 case UAC_SentAnswer:
3192 return "UAC_SentAnswer";
3193 case UAC_QueuedUpdate:
3194 return "UAC_QueuedUpdate";
3195 case UAC_Cancelled:
3196 return "UAC_Cancelled";
3197
3198 case UAS_Start:
3199 return "UAS_Start";
3200 case UAS_OfferReliable:
3201 return "UAS_OfferReliable";
3203 return "UAS_OfferReliableProvidedAnswer";
3205 return "UAS_NoOfferReliable";
3207 return "UAS_ProvidedOfferReliable";
3209 return "UAS_FirstSentOfferReliable";
3211 return "UAS_FirstSentAnswerReliable";
3213 return "UAS_NoAnswerReliableWaitingPrack";
3215 return "UAS_NoAnswerReliable";
3217 return "UAS_NegotiatedReliable";
3218 case UAS_SentUpdate:
3219 return "UAS_SentUpdate";
3221 return "UAS_SentUpdateAccepted";
3223 return "UAS_SentUpdateGlare";
3224 case UAS_ReceivedUpdate:
3225 return "UAS_ReceivedUpdate";
3227 return "UAS_ReceivedUpdateWaitingAnswer";
3229 return "UAS_WaitingToHangup";
3231 return "UAS_WaitingToRequestOffer";
3232 }
3233 resip_assert(0);
3234 return "Undefined";
3235}
3236
3237
3238void
3240{
3241 InfoLog (<< "Transition " << toData(mState) << " -> " << toData(target));
3242 mState = target;
3243}
3244
3245bool
3247{
3248 if(msg.method() != INVITE)
3249 {
3250 return false;
3251 }
3252 if(msg.isRequest())
3253 {
3254 return mDum.getMasterProfile()->getUasReliableProvisionalMode() > MasterProfile::Never &&
3255 ((msg.exists(h_Supporteds) && msg.header(h_Supporteds).find(Token(Symbols::C100rel))) ||
3256 (msg.exists(h_Requires) && msg.header(h_Requires).find(Token(Symbols::C100rel))));
3257 }
3258 else
3259 {
3260 // RFC3262 says reliable provisionals MUST have a Require: 100rel and an RSeq
3261 return mDum.getMasterProfile()->getUacReliableProvisionalMode() > MasterProfile::Never &&
3262 msg.exists(h_Requires) && msg.header(h_Requires).find(Token(Symbols::C100rel)) &&
3263 msg.exists(h_RSeq);
3264 }
3265}
3266
3267//static std::unique_ptr<SdpContents> emptySdp;
3268std::unique_ptr<Contents>
3270{
3271 if(mDum.mInviteSessionHandler->isGenericOfferAnswer())
3272 {
3273 if(msg.getContents())
3274 {
3275 return std::unique_ptr<Contents>(msg.getContents()->clone());
3276 }
3277
3278 return nullptr;
3279 }
3280
3281 return std::unique_ptr<Contents>(Helper::getSdp(msg.getContents()));
3282}
3283
3284std::unique_ptr<Contents>
3286{
3287 return std::unique_ptr<Contents>(static_cast<Contents*>(offerAnswer.clone()));
3288}
3289
3290std::unique_ptr<Contents>
3292 const Contents* alternative)
3293{
3294 if (alternative)
3295 {
3297 mac->parts().push_back(alternative->clone());
3298 mac->parts().push_back(offerAnswer.clone());
3299 return std::unique_ptr<Contents>(mac);
3300 }
3301 else
3302 {
3303 return std::unique_ptr<Contents>(offerAnswer.clone());
3304 }
3305}
3306
3307void
3308InviteSession::setOfferAnswer(SipMessage& msg, const Contents& offerAnswer, const Contents* alternative)
3309{
3310 // !jf! should deal with multipart here
3311
3312 // This will clone the offerAnswer since the InviteSession also wants to keep its own
3313 // copy of the offerAnswer around for the application to access
3314 if (alternative)
3315 {
3317 mac->parts().push_back(alternative->clone());
3318 mac->parts().push_back(offerAnswer.clone());
3319 msg.setContents(unique_ptr<Contents>(mac));
3320 }
3321 else
3322 {
3323 msg.setContents(&offerAnswer);
3324 }
3325}
3326
3327void
3329{
3330 resip_assert(offerAnswer);
3331 msg.setContents(offerAnswer);
3332}
3333
3334void
3336{
3339 if (mp_ans)
3340 {
3341 // .kw. can cast below ever be NULL? Need assert/throw?
3342 provideOffer( *(dynamic_cast<Contents*>((mp_ans)->parts().back())),
3344 dynamic_cast<Contents*>((mp_ans)->parts().front()));
3345 }
3346 else
3347 {
3348 // .kw. can cast below ever be NULL? Need assert/throw?
3350 }
3351}
3352
3354InviteSession::toEvent(const SipMessage& msg, const Contents* offerAnswer)
3355{
3356 MethodTypes method = msg.header(h_CSeq).method();
3357 int code = msg.isResponse() ? msg.header(h_StatusLine).statusCode() : 0;
3358
3359 //.dcm. Treat an invite as reliable if UAS 100rel support is enabled. For
3360 //responses, reiable provisionals should only be received if the invite was
3361 //sent reliably. Spurious reliable provisional respnoses are dropped outside
3362 //the state machine.
3363 bool reliable = isReliable(msg);
3364 bool sentOffer = mProposedLocalOfferAnswer.get();
3365
3366 if (code == 481 || code == 408)
3367 {
3368 return OnGeneralFailure;
3369 }
3370 else if (code >= 300 && code <= 399)
3371 {
3372 return OnRedirect;
3373 }
3374 else if (method == INVITE && code == 0)
3375 {
3376 if (offerAnswer)
3377 {
3378 if (reliable)
3379 {
3380 return OnInviteReliableOffer;
3381 }
3382 else
3383 {
3384 return OnInviteOffer;
3385 }
3386 }
3387 else
3388 {
3389 if (reliable)
3390 {
3391 return OnInviteReliable;
3392 }
3393 else
3394 {
3395 return OnInvite;
3396 }
3397 }
3398 }
3399 else if (method == INVITE && code > 100 && code < 200)
3400 {
3401 if (reliable)
3402 {
3403 if (offerAnswer)
3404 {
3405 if (sentOffer)
3406 {
3407 return On1xxAnswer;
3408 }
3409 else
3410 {
3411 return On1xxOffer;
3412 }
3413 }
3414 else
3415 {
3416 return On1xx;
3417 }
3418 }
3419 else
3420 {
3421 if (offerAnswer)
3422 {
3423 return On1xxEarly;
3424 }
3425 else
3426 {
3427 return On1xx;
3428 }
3429 }
3430 }
3431 else if (method == INVITE && code >= 200 && code < 300)
3432 {
3433 if (offerAnswer)
3434 {
3435 if (sentOffer)
3436 {
3437 return On2xxAnswer;
3438 }
3439 else
3440 {
3441 return On2xxOffer;
3442 }
3443 }
3444 else
3445 {
3446 return On2xx;
3447 }
3448 }
3449 else if (method == INVITE && code == 422)
3450 {
3451 return On422Invite;
3452 }
3453 else if (method == INVITE && code == 487)
3454 {
3455 return On487Invite;
3456 }
3457 else if (method == INVITE && code == 491 &&
3458 !mDialog.mDialogSet.getUserProfile()->getHandleInviteSession491AsGeneralFailureEnabled())
3459 {
3460 return On491Invite;
3461 }
3462 else if (method == INVITE && code >= 400)
3463 {
3464 return OnInviteFailure;
3465 }
3466 else if (method == ACK)
3467 {
3468 if (offerAnswer)
3469 {
3470 return OnAckAnswer;
3471 }
3472 else
3473 {
3474 return OnAck;
3475 }
3476 }
3477 else if (method == CANCEL && code == 0)
3478 {
3479 return OnCancel;
3480 }
3481 else if (method == CANCEL && code / 200 == 1)
3482 {
3483 return On200Cancel;
3484 }
3485 else if (method == CANCEL && code >= 400)
3486 {
3487 return OnCancelFailure;
3488 }
3489 else if (method == BYE && code == 0)
3490 {
3491 return OnBye;
3492 }
3493 else if (method == BYE && code / 200 == 1)
3494 {
3495 return On200Bye;
3496 }
3497 else if (method == PRACK && code == 0)
3498 {
3499 return OnPrack;
3500 }
3501 else if (method == PRACK && code / 200 == 1)
3502 {
3503 return On200Prack;
3504 }
3505 else if (method == UPDATE && code == 0)
3506 {
3507 if (offerAnswer)
3508 {
3509 return OnUpdateOffer;
3510 }
3511 else
3512 {
3513 return OnUpdate;
3514 }
3515 }
3516 else if (method == UPDATE && code / 200 == 1)
3517 {
3518 return On200Update;
3519 }
3520 else if (method == UPDATE && code == 422)
3521 {
3522 return On422Update;
3523 }
3524 else if (method == UPDATE && code == 491 &&
3525 !mDialog.mDialogSet.getUserProfile()->getHandleInviteSession491AsGeneralFailureEnabled())
3526 {
3527 return On491Update;
3528 }
3529 else if (method == UPDATE && code >= 400)
3530 {
3531 return OnUpdateRejected;
3532 }
3533 else if (method == OPTIONS && code == 0)
3534 {
3535 return OnOptions;
3536 }
3537 else if (method == OPTIONS && code / 100 == 2)
3538 {
3539 return On200Options;
3540 }
3541 else if (method == OPTIONS && code == 491)
3542 {
3543 return On491Options;
3544 }
3545 else
3546 {
3547 //assert(0); // dispatchOthers will throw if the message type is really unknown
3548 return Unknown;
3549 }
3550}
3551
3552std::shared_ptr<SipMessage>
3554{
3555 auto ack = std::make_shared<SipMessage>();
3556
3557 std::shared_ptr<SipMessage> source;
3558
3559 if (mLastLocalSessionModification->method() == UPDATE)
3560 {
3561 //.dcm. scary--we could make a special ClientInviteSession variable/sendAck
3562 source = mDialog.mDialogSet.getCreator()->getLastRequest();
3563 }
3564 else
3565 {
3567 }
3568
3569 resip_assert(mAcks.find(source->getTransactionId()) == mAcks.end());
3570
3571 mDialog.makeRequest(*ack, ACK);
3572
3573 // Copy Authorization and Proxy Authorization headers from
3574 // mLastLocalSessionModification; regardless of whether this was the original
3575 // INVITE or not, this is the correct place to go for auth headers.
3576 if(mLastLocalSessionModification->exists(h_Authorizations))
3577 {
3578 ack->header(h_Authorizations) = mLastLocalSessionModification->header(h_Authorizations);
3579 }
3580 if(mLastLocalSessionModification->exists(h_ProxyAuthorizations))
3581 {
3582 ack->header(h_ProxyAuthorizations) = mLastLocalSessionModification->header(h_ProxyAuthorizations);
3583 }
3584
3585 // Copy CSeq from original INVITE
3586 ack->header(h_CSeq).sequence() = source->header(h_CSeq).sequence();
3587
3588 if(answer != 0)
3589 {
3590 setOfferAnswer(*ack, *answer);
3591 }
3592 mAcks[source->getTransactionId()] = ack;
3593 mDum.addTimerMs(DumTimeout::CanDiscardAck, Timer::TH, getBaseHandle(), ack->header(h_CSeq).sequence(), 0, source->getTransactionId());
3594
3595 InfoLog (<< "Sending " << ack->brief());
3596 send(ack);
3597 return ack;
3598}
3599
3600std::shared_ptr<SipMessage>
3602{
3603 auto bye = std::make_shared<SipMessage>();
3604 mDialog.makeRequest(*bye, BYE);
3605 Data txt;
3606 if (mEndReason != NotSpecified)
3607 {
3609 {
3610 bye->header(h_Reasons) = mUserEndReasons;
3611 }
3612 else
3613 {
3614 Token reason("SIP");
3616 reason.param(p_text) = txt;
3617 // RFC 3326 reason-text is a quoted-string
3618 reason.getParameterByEnum(ParameterTypes::text)->setQuoted(true);
3619 bye->header(h_Reasons).push_back(reason);
3620 }
3621 }
3622
3623 if (mDum.mDialogEventStateManager)
3624 {
3625 mDum.mDialogEventStateManager->onTerminated(mDialog, *bye, InviteSessionHandler::LocalBye);
3626 }
3627
3628 InfoLog (<< myAddr() << " Sending BYE " << txt);
3629 send(bye);
3630 return bye;
3631}
3632
3635{
3637 const SecurityAttributes* secAttr = msg.getSecurityAttributes();
3638 if (secAttr)
3639 {
3640 SignatureStatus sig = secAttr->getSignatureStatus();
3641 bool sign = (SignatureTrusted == sig || SignatureCATrusted == sig || SignatureSelfSigned == sig);
3642 bool encrypted = secAttr->isEncrypted();
3643 if (encrypted && sign ) level = DialogUsageManager::SignAndEncrypt;
3644 else if (encrypted) level = DialogUsageManager::Encrypt;
3645 else if (sign) level = DialogUsageManager::Sign;
3646 }
3647 return level;
3648}
3649
3650void
3652{
3655 {
3657 {
3658 mCurrentLocalOfferAnswer = unique_ptr<Contents>(static_cast<Contents*>((dynamic_cast<MultipartAlternativeContents*>(mProposedLocalOfferAnswer.get()))->parts().back()->clone()));
3659 }
3660 else
3661 {
3662 mCurrentLocalOfferAnswer = unique_ptr<Contents>(static_cast<Contents*>((dynamic_cast<MultipartAlternativeContents*>(mProposedLocalOfferAnswer.get()))->parts().front()->clone()));
3663 }
3664 }
3665 else
3666 {
3667 mCurrentLocalOfferAnswer = unique_ptr<Contents>(static_cast<Contents*>(mProposedLocalOfferAnswer->clone()));
3668 }
3670}
3671
3672void
3674{
3675 mDum.mInviteSessionHandler->onReadyToSend(getSessionHandle(), msg);
3676}
3677
3678void
3680{
3681 // notify handler
3682 mDum.mInviteSessionHandler->onFlowTerminated(getSessionHandle());
3683}
3684
3685void
3687{
3688 resip_assert(msg.isRequest() && msg.header(h_CSeq).method()==REFER);
3690 mDum.mInviteSessionHandler->onReferNoSub(getSessionHandle(), mLastReferNoSubRequest);
3691}
3692
3693void
3695{
3696 if (statusCode / 100 != 2)
3697 {
3698 throw UsageUseException("Must accept with a 2xx", __FILE__, __LINE__);
3699 }
3700
3701 auto response = std::make_shared<SipMessage>();
3702 mDialog.makeResponse(*response, mLastReferNoSubRequest, statusCode);
3703 response->header(h_ReferSub).value() = "false";
3704 //response->header(h_Supporteds).push_back(Token(Symbols::NoReferSub));
3705
3706 send(response);
3707}
3708
3709void
3711{
3712 if (responseCode < 400)
3713 {
3714 throw UsageUseException("Must reject with a >= 4xx", __FILE__, __LINE__);
3715 }
3716
3717 auto response = std::make_shared<SipMessage>();
3718 mDialog.makeResponse(*response, mLastReferNoSubRequest, responseCode);
3719 send(response);
3720}
3721
3722/* ====================================================================
3723 * The Vovida Software License, Version 1.0
3724 *
3725 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
3726 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
3727 *
3728 * Redistribution and use in source and binary forms, with or without
3729 * modification, are permitted provided that the following conditions
3730 * are met:
3731 *
3732 * 1. Redistributions of source code must retain the above copyright
3733 * notice, this list of conditions and the following disclaimer.
3734 *
3735 * 2. Redistributions in binary form must reproduce the above copyright
3736 * notice, this list of conditions and the following disclaimer in
3737 * the documentation and/or other materials provided with the
3738
3739 * distribution.
3740 *
3741 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
3742 * and "Vovida Open Communication Application Library (VOCAL)" must
3743 * not be used to endorse or promote products derived from this
3744 * software without prior written permission. For written
3745 * permission, please contact vocal@vovida.org.
3746 *
3747 * 4. Products derived from this software may not be called "VOCAL", nor
3748 * may "VOCAL" appear in their name, without prior written
3749 * permission of Vovida Networks, Inc.
3750 *
3751 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
3752 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
3753 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
3754 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
3755 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
3756 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
3757 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3758 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
3759 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
3760 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3761 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3762 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
3763 * DAMAGE.
3764 *
3765 * ====================================================================
3766 *
3767 * This software consists of voluntary contributions made by Vovida
3768 * Networks, Inc. and many individuals on behalf of Vovida Networks,
3769 * Inc. For more information on Vovida Networks, Inc., please see
3770 * <http://www.vovida.org/>.
3771 *
3772 */
3773
3774
3775
Data EndReasons[]
const Token SessionTimerToken(Symbols::Timer)
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define resip_assert(x)
This file needs to work for both C and C++ code.
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
InviteSessionAcceptNITCommand(const InviteSessionHandle &inviteSessionHandle, int statusCode, const Contents *contents)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
void executeCommand() override
InviteSessionEndCommand(const InviteSessionHandle &inviteSessionHandle, InviteSession::EndReason reason, const Data &userEndReason=Data::Empty, ParserContainer< Token > userEndReasons=ParserContainer< Token >())
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
InviteSessionInfoCommand(const InviteSessionHandle &inviteSessionHandle, const Contents &contents)
InviteSessionMessageCommand(const InviteSessionHandle &inviteSessionHandle, const Contents &contents)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
InviteSessionProvideAnswerCommand(const InviteSessionHandle &inviteSessionHandle, const Contents &answer)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
InviteSessionProvideOfferCommand(const InviteSessionHandle &inviteSessionHandle, const Contents &offer)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
InviteSessionProvideOfferExCommand(const InviteSessionHandle &inviteSessionHandle, const Contents &offer, DialogUsageManager::EncryptionLevel level, const Contents *alternative)
InviteSessionReferCommand(const InviteSessionHandle &inviteSessionHandle, const NameAddr &referTo, bool referSub)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
InviteSessionReferExCommand(const InviteSessionHandle &inviteSessionHandle, const NameAddr &referTo, InviteSessionHandle sessionToReplace, bool referSub)
InviteSessionRejectCommand(const InviteSessionHandle &inviteSessionHandle, int code, WarningCategory *warning)
InviteSessionRejectCommand(const InviteSessionHandle &inviteSessionHandle, int code, const Contents *contents, WarningCategory *warning)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
InviteSessionRejectNITCommand(const InviteSessionHandle &inviteSessionHandle, int statusCode)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
std::shared_ptr< SipMessage > getLastRequest() const noexcept
BaseUsageHandle getBaseHandle() const
Definition BaseUsage.cxx:36
DialogUsageManager & mDum
Definition BaseUsage.hxx:43
Represents the "callid" element in the SIP grammar.
Definition CallId.hxx:18
Data & value()
Definition CallId.cxx:75
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
Base class for all SIP body types.
Definition Contents.hxx:51
virtual Contents * clone() const =0
returns a copy of a Contents object
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
BaseCreator * getCreator() const noexcept
std::shared_ptr< UserProfile > getUserProfile() const
std::shared_ptr< MasterProfile > & getMasterProfile()
const NameAddr & myAddr() const noexcept
virtual void send(std::shared_ptr< SipMessage > msg)
const NameAddr & peerAddr() const noexcept
dcm! – kill typedef std::list<DialogId> DialogIdSet;
Definition Dialog.hxx:29
void makeRequest(SipMessage &request, MethodTypes method, bool incrementCSeq=true)
Definition Dialog.cxx:1033
const DialogId & getId() const noexcept
Definition Dialog.cxx:325
void setRequestNextCSeq(SipMessage &request)
Definition Dialog.cxx:1153
void makeResponse(SipMessage &response, const SipMessage &request, int responseCode)
Definition Dialog.cxx:1108
static void setOutgoingEncryptionLevel(SipMessage &message, DialogUsageManager::EncryptionLevel level)
Definition DumHelper.cxx:11
Type type() const
const Data & transactionId() const
unsigned long duration() const
unsigned int seq() const
bool isValid() const
Definition Handle.hxx:25
Handled::Id mId
Definition Handled.hxx:25
static void getResponseCodeReason(int responseCode, Data &reason)
Returns the default reason string for a particular response code.
Definition Helper.cxx:610
static std::unique_ptr< SdpContents > getSdp(Contents *tree)
Definition Helper.cxx:2339
virtual void onStaleReInviteTimeout(InviteSessionHandle h)
UAC gets no final response within the stale re-invite timeout (default is 40 seconds).
virtual void onInfo(InviteSessionHandle, const SipMessage &msg)=0
called when INFO message is received the application must call acceptNIT() or rejectNIT() once it is ...
virtual void onOfferRejected(InviteSessionHandle, const SipMessage *msg)=0
called if an offer in a UPDATE or re-INVITE was rejected - not real useful.
virtual void onIllegalNegotiation(InviteSessionHandle, const SipMessage &msg)
will be called if reINVITE or UPDATE in dialog fails
virtual void onAckNotReceived(InviteSessionHandle)
default behaviour is to send a BYE to end the dialog
virtual void onAckReceived(InviteSessionHandle, const SipMessage &msg)
called when ACK is received
virtual void onAnswer(InviteSessionHandle, const SipMessage &msg, const SdpContents &)=0
called when an answer is received - has nothing to do with user answering the call
virtual void onSessionExpired(InviteSessionHandle)
will be called if Session-Timers are used and Session Timer expires default behaviour is to send a BY...
virtual void onReferNoSub(InviteSessionHandle, const SipMessage &msg)=0
virtual void onOfferRequired(InviteSessionHandle, const SipMessage &msg)=0
called when an Invite w/out offer is sent, or any other context which requires an offer from the user
virtual void onMessage(InviteSessionHandle, const SipMessage &msg)=0
called when MESSAGE message is received
virtual void onInfoFailure(InviteSessionHandle, const SipMessage &msg)=0
virtual void onInfoSuccess(InviteSessionHandle, const SipMessage &msg)=0
called when response to INFO message is received
virtual void onTerminated(InviteSessionHandle, InviteSessionHandler::TerminatedReason reason, const SipMessage *related=0)=0
virtual void onOffer(InviteSessionHandle, const SipMessage &msg, const SdpContents &)=0
called when an offer is received - must send an answer soon after this
virtual void onReadyToSend(InviteSessionHandle, SipMessage &msg)
called to allow app to adorn a message. default is to send immediately
virtual void onMessageFailure(InviteSessionHandle, const SipMessage &msg)=0
virtual void onMessageSuccess(InviteSessionHandle, const SipMessage &msg)=0
called when response to MESSAGE message is received
virtual void onRemoteAnswerChanged(InviteSessionHandle, const SipMessage &msg, const Contents &)
virtual void onFlowTerminated(InviteSessionHandle)
Called when a TCP or TLS flow to the server has terminated.
virtual void onOfferRequestRejected(InviteSessionHandle, const SipMessage &msg)
Called when an error response is received for a reinvite-nobody request (via requestOffer)
std::shared_ptr< SipMessage > & getNIT()
virtual void flowTerminated()
std::unique_ptr< Contents > getOfferAnswer(const SipMessage &msg)
virtual void message(const Contents &contents)
sends a message request
const SdpContents & getLocalSdp() const
bool hasLocalOfferAnswer() const
virtual void infoCommand(const Contents &contents)
std::shared_ptr< SipMessage > mLastNitResponse
bool hasProposedRemoteOfferAnswer() const
void dispatchOptions(const SipMessage &msg)
void dispatchSentReinvite(const SipMessage &msg)
void dispatchSentOptions(const SipMessage &msg)
virtual void referCommand(const NameAddr &referTo, bool referSub=true)
const SdpContents & getRemoteSdp() const
bool hasProposedRemoteSdp() const
void transition(State target)
bool isSessionModificationInFlight() const
void dispatchTerminated(const SipMessage &msg)
void setCurrentLocalOfferAnswer(const SipMessage &msg)
virtual void rejectReferNoSub(int responseCode)
std::shared_ptr< SipMessage > mLastSentNITRequest
virtual void endCommand(EndReason reason=NotSpecified)
Asynchronously makes the specific dialog end.
bool expireSessionAfterFailedModification()
EncodeStream & dump(EncodeStream &strm) const override
std::shared_ptr< SipMessage > sendBye()
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.
void dispatchSentUpdate(const SipMessage &msg)
virtual void provideAnswerCommand(const Contents &answer)
std::shared_ptr< SipMessage > getLastSentNITRequest() const
gets the last NIT request that was sent by the session
const Contents & getProposedRemoteOfferAnswer() const
NameAddrs mPeerPAssertedIdentities
void dispatchReceivedUpdateOrReinvite(const SipMessage &msg)
const Data & getEndReasonString(InviteSession::EndReason reason)
std::shared_ptr< SipMessage > mInvite200
void dispatchSentReinviteNoOffer(const SipMessage &msg)
unsigned int mSessionExpirationTimerSeq
std::unique_ptr< Contents > mProposedRemoteOfferAnswer
std::unique_ptr< Contents > mProposedLocalOfferAnswer
std::unique_ptr< Contents > mCurrentLocalOfferAnswer
std::shared_ptr< SipMessage > mLastRemoteSessionModification
std::shared_ptr< SipMessage > mLastLocalSessionModification
bool sessionTimerSupportedLocalAndPeer() const
SipMessage mLastReferNoSubRequest
unsigned int mStaleReInviteTimerSeq
virtual void requestOffer()
Called to request that the far end provide an offer.
virtual void end() override
static std::unique_ptr< Contents > makeOfferAnswer(const Contents &offerAnswer)
DialogUsageManager::EncryptionLevel mProposedEncryptionLevel
unsigned long mCurrentRetransmit200
bool hasRemoteOfferAnswer() const
std::queue< QueuedNIT * > mNITQueue
virtual void refer(const NameAddr &referTo, bool referSub=true)
sends a refer request
void setSessionTimerHeaders(SipMessage &msg)
void dispatchSentReinviteAnswered(const SipMessage &msg)
void storePeerCapabilities(const SipMessage &msg)
void dispatchGlare(const SipMessage &msg)
virtual void provideOfferCommand(const Contents &offer)
Provide asynchronous method access by using command.
void dispatchConnected(const SipMessage &msg)
void dispatchReceivedReinviteSentOffer(const SipMessage &msg)
virtual void acceptNIT(int statusCode=200, const Contents *contents=0)
accepts an INFO or MESSAGE request with a 2xx and an optional contents
InviteSession(const InviteSession &)=delete
virtual void onReadyToSend(SipMessage &msg) override
virtual void rejectNITCommand(int statusCode=488)
Asynchronously rejects an INFO or MESSAGE request with an error status code.
InviteSessionHandle getSessionHandle()
virtual void provideAnswer(const Contents &answer)
Similar to provideOffer - called to set the answer to be signaled to the peer.
void dispatchWaitingToOffer(const SipMessage &msg)
virtual void rejectCommand(int statusCode, WarningCategory *warning=0)
Asynchronously rejects an offer at the SIP level.
void handleSessionTimerResponse(const SipMessage &msg)
virtual void targetRefresh(const NameAddr &localUri)
will send a reINVITE (current offerAnswer) or UPDATE with new Contact header
virtual void info(const Contents &contents)
sends an info request
void dispatchMessage(const SipMessage &msg)
void dispatchWaitingToRequestOffer(const SipMessage &msg)
unsigned int mSessionRefreshTimerSeq
bool updateMethodSupported() const
const SdpContents & getProposedRemoteSdp() const
void referNoSub(const SipMessage &msg)
const Contents & getRemoteOfferAnswer() const
static void setOfferAnswer(SipMessage &msg, const Contents &offerAnswer, const Contents *alternative=0)
void dispatchWaitingToHangup(const SipMessage &msg)
std::unique_ptr< Contents > mCurrentRemoteOfferAnswer
void dispatchInfo(const SipMessage &msg)
void dispatch(const SipMessage &msg) override
Event toEvent(const SipMessage &msg, const Contents *offeranswer)
bool isAckForCurrent200(const SipMessage &ack) const
virtual void acceptNITCommand(int statusCode=200, const Contents *contents=0)
Asynchronously accepts an INFO or MESSAGE request with a 2xx and an optional contents.
virtual void acceptReferNoSub(int statusCode=200)
void dispatchBye(const SipMessage &msg)
void handleSessionTimerRequest(SipMessage &response, const SipMessage &request)
const Contents & getLocalOfferAnswer() const
void dispatchWaitingToTerminate(const SipMessage &msg)
DialogUsageManager::EncryptionLevel getEncryptionLevel(const SipMessage &msg)
bool isReliable(const SipMessage &msg) const
virtual void messageCommand(const Contents &contents)
void dispatchReinviteNoOfferGlare(const SipMessage &msg)
virtual void provideOffer(const Contents &offer)
Called to set the offer that will be used in the next message that sends an offer.
static Data toData(State state)
std::shared_ptr< SipMessage > sendAck(const Contents *answer=0)
ParserContainer< Token > mUserEndReasons
void dispatchAnswered(const SipMessage &msg)
DialogUsageManager::EncryptionLevel mCurrentEncryptionLevel
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
SIP body type for holding Multipart-Alternative body contents (MIME content-type multipart/alternativ...
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
void remove(const Param< NameAddr > &paramType)
Definition NameAddr.cxx:485
virtual void setQuoted(bool)
Definition Parameter.hxx:27
Parameter * getParameterByEnum(ParameterTypes::Type type) const
size_t size() const
return the size of the mParsers vector
Container class for ParserCategory, used by SipMessage to represent multi-valued headers (Contact,...
bool find(const T &rhs) const
Finds the first header field value that matches rhs.
static int getRandom()
Returns a postive integer (31 bits) of randomness.
Definition Random.cxx:261
MethodTypes & method()
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
static const SdpContents Empty
SignatureStatus getSignatureStatus() const
Represents a SIP message.
void setContents(const Contents *contents)
Set the contents of the 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.
virtual const Data & getTransactionId() const
Returns the transaction id from the branch or if 2543, the computed hash.
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.
void remove(const HeaderBase &headerType)
Prevents a header field from being present when the message is prepared for sending to a transport.
const SecurityAttributes * getSecurityAttributes() const noexcept
resip::MethodTypes method() const
returns the method type of the message
static const char * C100rel
Definition Symbols.hxx:107
static const char * Timer
Definition Symbols.hxx:109
static const char * NoReferSub
Definition Symbols.hxx:110
static unsigned long T1
Definition Timer.hxx:114
static unsigned long TH
Definition Timer.hxx:122
static unsigned long T2
Definition Timer.hxx:115
Represents the "token" element in the RFC 3261 grammar.
Definition Token.hxx:18
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
void post(Message *msg)
Posts a Message to this TransactionUser's fifo.
Represents the "warning-value" element in the RFC 3261 grammar.
This file is used to handle compatibility fixes/tweaks so reSIProcate can function on multiple platfo...
dcm! – add UnusedChecking(_enum) below;
const _Tp & resipMin(const _Tp &__a, const _Tp &__b)
Definition compat.hxx:182
StatusLineType h_StatusLine
Definition Headers.cxx:348
unsigned int uint32_t
Definition Stun.hxx:34
H_PAssertedIdentitys h_PAssertedIdentities
Definition Headers.cxx:215
RequestLineType h_RequestLine
Definition Headers.cxx:347
const _Tp & resipMax(const _Tp &__a, const _Tp &__b)
Definition compat.hxx:194
Handle< InviteSession > InviteSessionHandle
Definition Handles.hxx:38
H_AllowEventss h_AllowEvents
Definition Headers.cxx:143
#define EncodeStream