/[resiprocate]/main/resip/dum/InviteSession.cxx
ViewVC logotype

Contents of /main/resip/dum/InviteSession.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7084 - (show annotations) (download)
Thu Apr 19 08:50:33 2007 UTC (12 years, 7 months ago) by nash
File MIME type: text/plain
File size: 84187 byte(s)
Security.cxx/hxx:
 allow to disable server authentication

InviteSession.cxx:
 public getProposedRemoteSdp() method
 when assigning mProposedRemoteSdp, don't make clone it simply take over ownership as sdp is not used later part of the codes
 

1 #include "resip/stack/MultipartMixedContents.hxx"
2 #include "resip/stack/MultipartAlternativeContents.hxx"
3 #include "resip/stack/SdpContents.hxx"
4 #include "resip/stack/SipMessage.hxx"
5 #include "resip/stack/Helper.hxx"
6 #include "resip/dum/AppDialogSet.hxx"
7 #include "resip/dum/Dialog.hxx"
8 #include "resip/dum/DialogUsageManager.hxx"
9 #include "resip/dum/InviteSession.hxx"
10 #include "resip/dum/ServerInviteSession.hxx"
11 #include "resip/dum/ClientSubscription.hxx"
12 #include "resip/dum/ServerSubscription.hxx"
13 #include "resip/dum/ClientInviteSession.hxx"
14 #include "resip/dum/InviteSessionHandler.hxx"
15 #include "resip/dum/MasterProfile.hxx"
16 #include "resip/dum/UsageUseException.hxx"
17 #include "resip/dum/DumHelper.hxx"
18 #include "rutil/Inserter.hxx"
19 #include "rutil/Logger.hxx"
20 #include "rutil/MD5Stream.hxx"
21 #include "rutil/Timer.hxx"
22 #include "rutil/Random.hxx"
23 #include "rutil/compat.hxx"
24 #include "rutil/WinLeakCheck.hxx"
25 #include "rutil/SharedPtr.hxx"
26
27 // Remove warning about 'this' use in initiator list - pointer is only stored
28 #if defined(WIN32) && !defined(__GNUC__)
29 #pragma warning( disable : 4355 ) // using this in base member initializer list
30 #pragma warning( disable : 4800 ) // forcing value to bool (performance warning)
31 #endif
32
33 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
34 #define THROW(msg) throw DialogUsage::Exception(msg, __FILE__,__LINE__);
35
36 using namespace resip;
37 using namespace std;
38
39 Data EndReasons[] =
40 {
41 "Not Specified",
42 "User Hung Up",
43 "Application Rejected Sdp(usually no common codec)",
44 "Illegal Sdp Negotiation",
45 "ACK not received",
46 "Session Timer Expired"
47 };
48
49 const Data& getEndReasonString(InviteSession::EndReason reason)
50 {
51 assert(reason >= InviteSession::NotSpecified && reason < InviteSession::ENDREASON_MAX); //!dcm! -- necessary?
52 return EndReasons[reason];
53 }
54
55 InviteSession::InviteSession(DialogUsageManager& dum, Dialog& dialog)
56 : DialogUsage(dum, dialog),
57 mState(Undefined),
58 mNitState(NitComplete),
59 mLastLocalSessionModification(new SipMessage),
60 mLastRemoteSessionModification(new SipMessage),
61 mInvite200(new SipMessage),
62 mLastNitResponse(new SipMessage),
63 mCurrentRetransmit200(0),
64 mSessionInterval(0),
65 mMinSE(90),
66 mSessionRefresher(false),
67 mSessionTimerSeq(0),
68 mSessionRefreshReInvite(false),
69 mSentRefer(false),
70 mReferSub(true),
71 mCurrentEncryptionLevel(DialogUsageManager::None),
72 mProposedEncryptionLevel(DialogUsageManager::None),
73 mEndReason(NotSpecified)
74 {
75 DebugLog ( << "^^^ InviteSession::InviteSession " << this);
76 assert(mDum.mInviteSessionHandler);
77 }
78
79 InviteSession::~InviteSession()
80 {
81 DebugLog ( << "^^^ InviteSession::~InviteSession " << this);
82 mDialog.mInviteSession = 0;
83 }
84
85 void
86 InviteSession::dialogDestroyed(const SipMessage& msg)
87 {
88 assert(0);
89
90 // !jf! Is this correct? Merged from main...
91 // !jf! what reason - guessed for now?
92 //mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, msg);
93 //delete this;
94 }
95
96 const SdpContents&
97 InviteSession::getLocalSdp() const
98 {
99 if(mCurrentLocalSdp.get())
100 {
101 return *mCurrentLocalSdp;
102 }
103 else
104 {
105 return SdpContents::Empty;
106 }
107 }
108
109 const SdpContents&
110 InviteSession::getRemoteSdp() const
111 {
112 if(mCurrentRemoteSdp.get())
113 {
114 return *mCurrentRemoteSdp;
115 }
116 else
117 {
118 return SdpContents::Empty;
119 }
120 }
121
122 const SdpContents&
123 InviteSession::getProposedRemoteSdp() const
124 {
125 if(mProposedRemoteSdp.get())
126 {
127 return *mProposedRemoteSdp;
128 }
129 else
130 {
131 return SdpContents::Empty;
132 }
133 }
134
135 const Data&
136 InviteSession::getDialogId() const
137 {
138 return mDialog.getId().getCallId();
139 }
140
141 InviteSessionHandle
142 InviteSession::getSessionHandle()
143 {
144 return InviteSessionHandle(mDum, getBaseHandle().getId());
145 }
146
147 void InviteSession::storePeerCapabilities(const SipMessage& msg)
148 {
149 if (msg.exists(h_Allows))
150 {
151 mPeerSupportedMethods = msg.header(h_Allows);
152 }
153 if (msg.exists(h_Supporteds))
154 {
155 mPeerSupportedOptionTags = msg.header(h_Supporteds);
156 }
157 if (msg.exists(h_AcceptEncodings))
158 {
159 mPeerSupportedEncodings = msg.header(h_AcceptEncodings);
160 }
161 if (msg.exists(h_AcceptLanguages))
162 {
163 mPeerSupportedLanguages = msg.header(h_AcceptLanguages);
164 }
165 if (msg.exists(h_AllowEvents))
166 {
167 mPeerAllowedEvents = msg.header(h_AllowEvents);
168 }
169 if (msg.exists(h_Accepts))
170 {
171 mPeerSupportedMimeTypes = msg.header(h_Accepts);
172 }
173 if (msg.exists(h_UserAgent))
174 {
175 mPeerUserAgent = msg.header(h_UserAgent).value();
176 }
177 }
178
179 bool
180 InviteSession::updateMethodSupported() const
181 {
182 // Check if Update is supported locally
183 if(mDum.getMasterProfile()->isMethodSupported(UPDATE))
184 {
185 // Check if peer supports UPDATE
186 return mPeerSupportedMethods.find(Token("UPDATE"));
187 }
188 return false;
189 }
190
191 const NameAddr&
192 InviteSession::myAddr() const
193 {
194 return mDialog.mLocalNameAddr;
195 }
196
197 const NameAddr&
198 InviteSession::peerAddr() const
199 {
200 return mDialog.mRemoteNameAddr;
201 }
202
203 bool
204 InviteSession::isConnected() const
205 {
206 switch (mState)
207 {
208 case Connected:
209 case SentUpdate:
210 case SentUpdateGlare:
211 case SentReinvite:
212 case SentReinviteGlare:
213 case SentReinviteNoOffer:
214 case SentReinviteAnswered:
215 case SentReinviteNoOfferGlare:
216 case ReceivedUpdate:
217 case ReceivedReinvite:
218 case ReceivedReinviteNoOffer:
219 case ReceivedReinviteSentOffer:
220 case Answered:
221 case WaitingToOffer:
222 case WaitingToRequestOffer:
223 return true;
224
225 default:
226 return false;
227 }
228 }
229
230 bool
231 InviteSession::isEarly() const
232 {
233 switch (mState)
234 {
235 case UAC_Early:
236 case UAC_EarlyWithOffer:
237 case UAC_EarlyWithAnswer:
238 case UAC_SentUpdateEarly:
239 case UAC_ReceivedUpdateEarly:
240 case UAC_SentAnswer:
241 case UAC_QueuedUpdate:
242 return true;
243 default:
244 return false;
245 }
246 }
247
248 bool
249 InviteSession::isAccepted() const
250 {
251 switch (mState)
252 {
253 case UAS_Start:
254 case UAS_Offer:
255 case UAS_OfferReliable:
256 case UAS_NoOffer:
257 case UAS_NoOfferReliable:
258 case UAS_ProvidedOffer:
259 case UAS_OfferProvidedAnswer:
260 case UAS_EarlyOffer:
261 case UAS_EarlyProvidedOffer:
262 case UAS_EarlyProvidedAnswer:
263 case UAS_EarlyNoOffer:
264 case UAS_FirstEarlyReliable:
265 case UAS_FirstSentOfferReliable:
266 case UAS_EarlyReliable:
267 return false;
268 default:
269 return true;
270 }
271 }
272
273 bool
274 InviteSession::isTerminated() const
275 {
276 switch (mState)
277 {
278 case Terminated:
279 case WaitingToTerminate:
280 case WaitingToHangup:
281 case UAC_Cancelled:
282 case UAS_WaitingToTerminate:
283 case UAS_WaitingToHangup:
284 return true;
285 default:
286 return false;
287 }
288 }
289
290 std::ostream&
291 InviteSession::dump(std::ostream& strm) const
292 {
293 strm << "INVITE: " << mId
294 << " " << toData(mState)
295 << " ADDR=" << myAddr()
296 << " PEER=" << peerAddr();
297 return strm;
298 }
299
300 void
301 InviteSession::requestOffer()
302 {
303 switch (mState)
304 {
305 case Connected:
306 case WaitingToRequestOffer:
307 case UAS_WaitingToRequestOffer:
308 transition(SentReinviteNoOffer);
309 mDialog.makeRequest(*mLastLocalSessionModification, INVITE);
310 mLastLocalSessionModification->setContents(0); // Clear the SDP contents from the INVITE
311 setSessionTimerHeaders(*mLastLocalSessionModification);
312
313 InfoLog (<< "Sending " << mLastLocalSessionModification->brief());
314
315 // call send to give app an chance to adorn the message.
316 send(mLastLocalSessionModification);
317 break;
318
319 case Answered:
320 // queue the offer to be sent after the ACK is received
321 transition(WaitingToRequestOffer);
322 break;
323
324 // ?slg? Can we handle all of the states listed in isConnected() ???
325 default:
326 WarningLog (<< "Can't requestOffer when not in Connected state");
327 throw DialogUsage::Exception("Can't request an offer", __FILE__,__LINE__);
328 }
329 }
330
331 void
332 InviteSession::provideOffer(const SdpContents& offer,
333 DialogUsageManager::EncryptionLevel level,
334 const SdpContents* alternative)
335 {
336 switch (mState)
337 {
338 case Connected:
339 case WaitingToOffer:
340 case UAS_WaitingToOffer:
341 if (updateMethodSupported())
342 {
343 transition(SentUpdate);
344 mDialog.makeRequest(*mLastLocalSessionModification, UPDATE);
345 }
346 else
347 {
348 transition(SentReinvite);
349 mDialog.makeRequest(*mLastLocalSessionModification, INVITE);
350 }
351 setSessionTimerHeaders(*mLastLocalSessionModification);
352
353 InfoLog (<< "Sending " << mLastLocalSessionModification->brief());
354 InviteSession::setSdp(*mLastLocalSessionModification, offer, alternative);
355 mProposedLocalSdp = InviteSession::makeSdp(offer, alternative);
356 mProposedEncryptionLevel = level;
357 DumHelper::setOutgoingEncryptionLevel(*mLastLocalSessionModification, mProposedEncryptionLevel);
358
359 // call send to give app an chance to adorn the message.
360 send(mLastLocalSessionModification);
361 break;
362
363 case Answered:
364 // queue the offer to be sent after the ACK is received
365 transition(WaitingToOffer);
366 mProposedEncryptionLevel = level;
367 mProposedLocalSdp = InviteSession::makeSdp(offer, alternative);
368 break;
369
370 case ReceivedReinviteNoOffer:
371 assert(!mProposedRemoteSdp.get());
372 transition(ReceivedReinviteSentOffer);
373 mDialog.makeResponse(*mInvite200, *mLastRemoteSessionModification, 200);
374 handleSessionTimerRequest(*mInvite200, *mLastRemoteSessionModification);
375 InviteSession::setSdp(*mInvite200, offer, 0);
376 mProposedLocalSdp = InviteSession::makeSdp(offer);
377
378 InfoLog (<< "Sending " << mInvite200->brief());
379 DumHelper::setOutgoingEncryptionLevel(*mInvite200, mCurrentEncryptionLevel);
380 send(mInvite200);
381 startRetransmit200Timer();
382 break;
383
384 default:
385 WarningLog (<< "Incorrect state to provideOffer: " << toData(mState));
386 throw DialogUsage::Exception("Can't provide an offer", __FILE__,__LINE__);
387 }
388 }
389
390 class InviteSessionProvideOfferExCommand : public DumCommandAdapter
391 {
392 public:
393 InviteSessionProvideOfferExCommand(InviteSession& inviteSession,
394 const SdpContents& offer,
395 DialogUsageManager::EncryptionLevel level,
396 const SdpContents* alternative)
397 : mInviteSession(inviteSession),
398 mOffer(offer)
399 {
400 }
401
402 virtual void executeCommand()
403 {
404 mInviteSession.provideOffer(mOffer, mLevel, mAlternative.get());
405 }
406
407 virtual std::ostream& encodeBrief(std::ostream& strm) const
408 {
409 return strm << "InviteSessionProvideOfferExCommand";
410 }
411 private:
412 InviteSession& mInviteSession;
413 SdpContents mOffer;
414 DialogUsageManager::EncryptionLevel mLevel;
415 std::auto_ptr<const SdpContents> mAlternative;
416 };
417
418 void
419 InviteSession::provideOfferCommand(const SdpContents& offer, DialogUsageManager::EncryptionLevel level, const SdpContents* alternative)
420 {
421 mDum.post(new InviteSessionProvideOfferExCommand(*this, offer, level, alternative));
422 }
423
424 void
425 InviteSession::provideOffer(const SdpContents& offer)
426 {
427 return provideOffer(offer, mCurrentEncryptionLevel, 0);
428 }
429
430 class InviteSessionProvideOfferCommand : public DumCommandAdapter
431 {
432 public:
433 InviteSessionProvideOfferCommand(InviteSession& inviteSession, const SdpContents& offer)
434 : mInviteSession(inviteSession),
435 mOffer(offer)
436 {
437 }
438
439 virtual void executeCommand()
440 {
441 mInviteSession.provideOffer(mOffer);
442 }
443
444 virtual std::ostream& encodeBrief(std::ostream& strm) const
445 {
446 return strm << "InviteSessionProvideOfferCommand";
447 }
448 private:
449 InviteSession& mInviteSession;
450 SdpContents mOffer;
451 };
452
453 void
454 InviteSession::provideOfferCommand(const SdpContents& offer)
455 {
456 mDum.post(new InviteSessionProvideOfferCommand(*this, offer));
457 }
458
459 void
460 InviteSession::provideAnswer(const SdpContents& answer)
461 {
462 switch (mState)
463 {
464 case ReceivedReinvite:
465 transition(Connected);
466 mDialog.makeResponse(*mInvite200, *mLastRemoteSessionModification, 200);
467 handleSessionTimerRequest(*mInvite200, *mLastRemoteSessionModification);
468 InviteSession::setSdp(*mInvite200, answer, 0);
469 mCurrentLocalSdp = InviteSession::makeSdp(answer);
470 mCurrentRemoteSdp = mProposedRemoteSdp;
471 InfoLog (<< "Sending " << mInvite200->brief());
472 DumHelper::setOutgoingEncryptionLevel(*mInvite200, mCurrentEncryptionLevel);
473 send(mInvite200);
474 startRetransmit200Timer();
475 break;
476
477 case ReceivedUpdate: // same as ReceivedReinvite case.
478 {
479 transition(Connected);
480
481 SharedPtr<SipMessage> response(new SipMessage);
482 mDialog.makeResponse(*response, *mLastRemoteSessionModification, 200);
483 handleSessionTimerRequest(*response, *mLastRemoteSessionModification);
484 InviteSession::setSdp(*response, answer, 0);
485 mCurrentLocalSdp = InviteSession::makeSdp(answer);
486 mCurrentRemoteSdp = mProposedRemoteSdp;
487 InfoLog (<< "Sending " << response->brief());
488 DumHelper::setOutgoingEncryptionLevel(*response, mCurrentEncryptionLevel);
489 send(response);
490 break;
491 }
492
493 case SentReinviteAnswered:
494 transition(Connected);
495 sendAck(&answer);
496
497 mCurrentRemoteSdp = mProposedRemoteSdp;
498 mCurrentLocalSdp = InviteSession::makeSdp(answer);
499 break;
500
501 default:
502 WarningLog (<< "Incorrect state to provideAnswer: " << toData(mState));
503 throw DialogUsage::Exception("Can't provide an answer", __FILE__,__LINE__);
504 }
505 }
506
507 class InviteSessionProvideAnswerCommand : public DumCommandAdapter
508 {
509 public:
510 InviteSessionProvideAnswerCommand(InviteSession& inviteSession, const SdpContents& answer)
511 : mInviteSession(inviteSession),
512 mAnswer(answer)
513 {
514 }
515
516 virtual void executeCommand()
517 {
518 mInviteSession.provideOffer(mAnswer);
519 }
520
521 virtual std::ostream& encodeBrief(std::ostream& strm) const
522 {
523 return strm << "InviteSessionProvideAnswerCommand";
524 }
525 private:
526 InviteSession& mInviteSession;
527 SdpContents mAnswer;
528 };
529
530 void
531 InviteSession::provideAnswerCommand(const SdpContents& answer)
532 {
533 mDum.post(new InviteSessionProvideAnswerCommand(*this, answer));
534 }
535
536 void
537 InviteSession::end()
538 {
539 if (isConnected())
540 {
541 end(NotSpecified);
542 }
543 //else
544 //{
545 // AppDialogSetHandle appDialogSetHandle = getAppDialogSet();
546 // if (appDialogSetHandle.isValid())
547 // {
548 // appDialogSetHandle->end();
549 // }
550 // else
551 // {
552 // assert(!"Has no mean of ending an Invite Session");
553 // }
554 //}
555 }
556
557 void
558 InviteSession::end(EndReason reason)
559 {
560 if (mEndReason == NotSpecified)
561 {
562 mEndReason = reason;
563 }
564
565 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
566
567 switch (mState)
568 {
569 case Connected:
570 case SentUpdate:
571 case SentUpdateGlare:
572 case SentReinviteGlare:
573 case SentReinviteNoOfferGlare:
574 case SentReinviteAnswered:
575 {
576 // !jf! do we need to store the BYE somewhere?
577 sendBye();
578 transition(Terminated);
579 handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
580 break;
581 }
582
583 case SentReinvite:
584 case SentReinviteNoOffer:
585 transition(WaitingToTerminate);
586 break;
587
588 case Answered:
589 case WaitingToOffer:
590 case WaitingToRequestOffer:
591 case ReceivedReinviteSentOffer:
592 if(mCurrentRetransmit200) // If retransmit200 timer is active then ACK is not received yet - wait for it
593 {
594 transition(WaitingToHangup);
595 }
596 else
597 {
598 // ACK has likely timedout - hangup immediately
599 sendBye();
600 transition(Terminated);
601 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
602 }
603 break;
604
605 case ReceivedUpdate:
606 case ReceivedReinvite:
607 case ReceivedReinviteNoOffer:
608 {
609 SharedPtr<SipMessage> response(new SipMessage);
610 mDialog.makeResponse(*response, *mLastRemoteSessionModification, 488);
611 InfoLog (<< "Sending " << response->brief());
612 send(response);
613
614 sendBye();
615 transition(Terminated);
616 handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
617 break;
618 }
619
620 case WaitingToTerminate: // ?slg? Why is this here?
621 {
622 sendBye();
623 transition(Terminated);
624 handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
625 break;
626 }
627
628 case Terminated:
629 // no-op.
630 break;
631
632 default:
633 assert(0);
634 break;
635 }
636 }
637
638 class InviteSessionEndCommand : public DumCommandAdapter
639 {
640 public:
641 InviteSessionEndCommand(InviteSession& inviteSession, InviteSession::EndReason reason)
642 : mInviteSession(inviteSession),
643 mReason(reason)
644 {
645 }
646
647 virtual void executeCommand()
648 {
649 mInviteSession.end(mReason);
650 }
651
652 virtual std::ostream& encodeBrief(std::ostream& strm) const
653 {
654 return strm << "InviteSessionEndCommand";
655 }
656 private:
657 InviteSession& mInviteSession;
658 InviteSession::EndReason mReason;
659 };
660
661 void
662 InviteSession::endCommand(EndReason reason)
663 {
664 mDum.post(new InviteSessionEndCommand(*this, reason));
665 }
666
667 void
668 InviteSession::reject(int statusCode, WarningCategory *warning)
669 {
670 switch (mState)
671 {
672 case ReceivedUpdate:
673 case ReceivedReinvite:
674 case ReceivedReinviteNoOffer:
675 {
676 transition(Connected);
677
678 SharedPtr<SipMessage> response(new SipMessage);
679 mDialog.makeResponse(*response, *mLastRemoteSessionModification, statusCode);
680 if(warning)
681 {
682 response->header(h_Warnings).push_back(*warning);
683 }
684 InfoLog (<< "Sending " << response->brief());
685 send(response);
686 break;
687 }
688
689 default:
690 assert(0);
691 break;
692 }
693 }
694
695 class InviteSessionRejectCommand : public DumCommandAdapter
696 {
697 public:
698 InviteSessionRejectCommand(InviteSession& inviteSession, int code, WarningCategory* warning)
699 : mInviteSession(inviteSession),
700 mCode(code),
701 mWarning(warning?new WarningCategory(*warning):0)
702 {
703 }
704
705 virtual void executeCommand()
706 {
707 mInviteSession.reject(mCode, mWarning.get());
708 }
709
710 virtual std::ostream& encodeBrief(std::ostream& strm) const
711 {
712 return strm << "InviteSessionRejectCommand";
713 }
714 private:
715 InviteSession& mInviteSession;
716 int mCode;
717 std::auto_ptr<WarningCategory> mWarning;
718 };
719
720 void
721 InviteSession::rejectCommand(int code, WarningCategory *warning)
722 {
723 mDum.post(new InviteSessionRejectCommand(*this, code, warning));
724 }
725
726 void
727 InviteSession::targetRefresh(const NameAddr& localUri)
728 {
729 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
730 {
731 mDialog.mLocalContact = localUri;
732 sessionRefresh();
733 }
734 else
735 {
736 WarningLog (<< "Can't targetRefresh before Connected");
737 throw UsageUseException("targetRefresh not allowed in this context", __FILE__, __LINE__);
738 }
739 }
740
741 void
742 InviteSession::refer(const NameAddr& referTo, bool referSub)
743 {
744 if (mSentRefer)
745 {
746 throw UsageUseException("Attempted to send overlapping refer", __FILE__, __LINE__);
747 }
748
749 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
750 {
751 mSentRefer = true;
752 mReferSub = referSub;
753 SharedPtr<SipMessage> refer(new SipMessage());
754 mDialog.makeRequest(*refer, REFER);
755 refer->header(h_ReferTo) = referTo;
756 refer->header(h_ReferredBy) = mDialog.mLocalContact; //
757 // !slg! is it ok to do this - should it be an option?
758 if (!referSub)
759 {
760 refer->header(h_ReferSub).value() = "false";
761 refer->header(h_Supporteds).push_back(Token(Symbols::NoReferSub));
762 }
763
764 send(refer);
765 }
766 else
767 {
768 WarningLog (<< "Can't refer before Connected");
769 assert(0);
770 throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__);
771 }
772 }
773
774 class InviteSessionReferCommand : public DumCommandAdapter
775 {
776 public:
777 InviteSessionReferCommand(InviteSession& inviteSession, const NameAddr& referTo, bool referSub)
778 : mInviteSession(inviteSession),
779 mReferTo(referTo),
780 mReferSub(referSub)
781 {
782
783 }
784
785 virtual void executeCommand()
786 {
787 mInviteSession.referCommand(mReferTo, mReferSub);
788 }
789
790 virtual std::ostream& encodeBrief(std::ostream& strm) const
791 {
792 return strm << "InviteSessionReferCommand";
793 }
794
795 private:
796 InviteSession& mInviteSession;
797 NameAddr mReferTo;
798 bool mReferSub;
799 };
800
801 void
802 InviteSession::referCommand(const NameAddr& referTo, bool referSub)
803 {
804 mDum.post(new InviteSessionReferCommand(*this, referTo, referSub));
805 }
806
807 void
808 InviteSession::refer(const NameAddr& referTo, InviteSessionHandle sessionToReplace, bool referSub)
809 {
810 if (!sessionToReplace.isValid())
811 {
812 throw UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__);
813 }
814
815 if (mSentRefer)
816 {
817 throw UsageUseException("Attempted to send overlapping refer", __FILE__, __LINE__);
818 }
819
820 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
821 {
822 mSentRefer = true;
823 mReferSub = referSub;
824 SharedPtr<SipMessage> refer(new SipMessage());
825 mDialog.makeRequest(*refer, REFER);
826
827 refer->header(h_ReferTo) = referTo;
828 refer->header(h_ReferredBy) = mDialog.mLocalContact; // ?slg? is it ok to do this - should it be an option?
829 CallId replaces;
830 DialogId id = sessionToReplace->mDialog.getId();
831 replaces.value() = id.getCallId();
832 replaces.param(p_toTag) = id.getRemoteTag();
833 replaces.param(p_fromTag) = id.getLocalTag();
834
835 refer->header(h_ReferTo).uri().embedded().header(h_Replaces) = replaces;
836
837 if (!referSub)
838 {
839 refer->header(h_ReferSub).value() = "false";
840 refer->header(h_Supporteds).push_back(Token(Symbols::NoReferSub));
841 }
842
843 send(refer);
844 }
845 else
846 {
847 WarningLog (<< "Can't refer before Connected");
848 assert(0);
849 throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__);
850 }
851 }
852
853 class InviteSessionReferExCommand : public DumCommandAdapter
854 {
855 public:
856 InviteSessionReferExCommand(InviteSession& inviteSession, const NameAddr& referTo, InviteSessionHandle sessionToReplace, bool referSub)
857 : mInviteSession(inviteSession),
858 mReferTo(referTo),
859 mSessionToReplace(sessionToReplace),
860 mReferSub(referSub)
861 {
862
863 }
864
865 virtual void executeCommand()
866 {
867 mInviteSession.referCommand(mReferTo, mSessionToReplace, mReferSub);
868 }
869
870 virtual std::ostream& encodeBrief(std::ostream& strm) const
871 {
872 return strm << "InviteSessionReferExCommand";
873 }
874
875 private:
876 InviteSession& mInviteSession;
877 InviteSessionHandle mSessionToReplace;
878 NameAddr mReferTo;
879 bool mReferSub;
880 };
881
882 void
883 InviteSession::referCommand(const NameAddr& referTo, InviteSessionHandle sessionToReplace, bool referSub)
884 {
885 mDum.post(new InviteSessionReferExCommand(*this, referTo, sessionToReplace, referSub));
886 }
887
888 void
889 InviteSession::info(const Contents& contents)
890 {
891 if (mNitState == NitComplete)
892 {
893 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
894 {
895 mNitState = NitProceeding;
896 SharedPtr<SipMessage> info(new SipMessage());
897 mDialog.makeRequest(*info, INFO);
898 // !jf! handle multipart here
899 info->setContents(&contents);
900 DumHelper::setOutgoingEncryptionLevel(*info, mCurrentEncryptionLevel);
901 send(info);
902 }
903 else
904 {
905 WarningLog (<< "Can't send INFO before Connected");
906 assert(0);
907 throw UsageUseException("Can't send INFO before Connected", __FILE__, __LINE__);
908 }
909 }
910 else
911 {
912 throw UsageUseException("Cannot start a non-invite transaction until the previous one has completed",
913 __FILE__, __LINE__);
914 }
915 }
916
917 class InviteSessionInfoCommand : public DumCommandAdapter
918 {
919 public:
920 InviteSessionInfoCommand(InviteSession& inviteSession, const Contents& contents)
921 : mInviteSession(inviteSession),
922 mContents(contents.clone())
923 {
924 }
925
926 virtual void executeCommand()
927 {
928 mInviteSession.info(*mContents);
929 }
930
931 virtual std::ostream& encodeBrief(std::ostream& strm) const
932 {
933 return strm << "InviteSessionInfoCommand";
934 }
935 private:
936 InviteSession& mInviteSession;
937 std::auto_ptr<Contents> mContents;
938 };
939
940 void
941 InviteSession::infoCommand(const Contents& contents)
942 {
943 mDum.post(new InviteSessionInfoCommand(*this, contents));
944 }
945
946 void
947 InviteSession::message(const Contents& contents)
948 {
949 if (mNitState == NitComplete)
950 {
951 if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite?
952 {
953 mNitState = NitProceeding;
954 SharedPtr<SipMessage> message(new SipMessage());
955 mDialog.makeRequest(*message, MESSAGE);
956 // !jf! handle multipart here
957 message->setContents(&contents);
958 DumHelper::setOutgoingEncryptionLevel(*message, mCurrentEncryptionLevel);
959 send(message);
960 InfoLog (<< "Trying to send MESSAGE: " << message);
961 }
962 else
963 {
964 WarningLog (<< "Can't send MESSAGE before Connected");
965 assert(0);
966 throw UsageUseException("Can't send MESSAGE before Connected", __FILE__, __LINE__);
967 }
968 }
969 else
970 {
971 throw UsageUseException("Cannot start a non-invite transaction until the previous one has completed",
972 __FILE__, __LINE__);
973 }
974 }
975
976 class InviteSessionMessageCommand : public DumCommandAdapter
977 {
978 public:
979 InviteSessionMessageCommand(InviteSession& inviteSession, const Contents& contents)
980 : mInviteSession(inviteSession),
981 mContents(contents.clone())
982 {
983 }
984
985 virtual void executeCommand()
986 {
987 mInviteSession.message(*mContents);
988 }
989
990 virtual std::ostream& encodeBrief(std::ostream& strm) const
991 {
992 return strm << "InviteSessionMessageCommand";
993 }
994 private:
995 InviteSession& mInviteSession;
996 std::auto_ptr<Contents> mContents;
997 };
998
999
1000 void
1001 InviteSession::messageCommand(const Contents& contents)
1002 {
1003 mDum.post(new InviteSessionMessageCommand(*this, contents));
1004 }
1005
1006 void
1007 InviteSession::dispatch(const SipMessage& msg)
1008 {
1009 // Look for 2xx retransmissions - resend ACK and filter out of state machine
1010 if(msg.header(h_CSeq).method() == INVITE && msg.isResponse() && msg.header(h_StatusLine).statusCode() / 200 == 1)
1011 {
1012 AckMap::iterator i = mAcks.find(msg.header(h_CSeq).sequence());
1013 if (i != mAcks.end())
1014 {
1015 send(i->second); // resend ACK
1016 return;
1017 }
1018 }
1019
1020 // !jf! do we need to handle 3xx here or is it handled elsewhere?
1021 switch (mState)
1022 {
1023 case Connected:
1024 dispatchConnected(msg);
1025 break;
1026 case SentUpdate:
1027 dispatchSentUpdate(msg);
1028 break;
1029 case SentReinvite:
1030 dispatchSentReinvite(msg);
1031 break;
1032 case SentReinviteNoOffer:
1033 dispatchSentReinviteNoOffer(msg);
1034 break;
1035 case SentReinviteAnswered:
1036 dispatchSentReinviteAnswered(msg);
1037 break;
1038 case SentUpdateGlare:
1039 case SentReinviteGlare:
1040 // The behavior is the same except for timer which is handled in dispatch(Timer)
1041 dispatchGlare(msg);
1042 break;
1043 case SentReinviteNoOfferGlare:
1044 dispatchReinviteNoOfferGlare(msg);
1045 break;
1046 case ReceivedUpdate:
1047 case ReceivedReinvite:
1048 case ReceivedReinviteNoOffer:
1049 dispatchReceivedUpdateOrReinvite(msg);
1050 break;
1051 case ReceivedReinviteSentOffer:
1052 dispatchReceivedReinviteSentOffer(msg);
1053 break;
1054 case Answered:
1055 dispatchAnswered(msg);
1056 break;
1057 case WaitingToOffer:
1058 dispatchWaitingToOffer(msg);
1059 break;
1060 case WaitingToRequestOffer:
1061 dispatchWaitingToRequestOffer(msg);
1062 break;
1063 case WaitingToTerminate:
1064 dispatchWaitingToTerminate(msg);
1065 break;
1066 case WaitingToHangup:
1067 dispatchWaitingToHangup(msg);
1068 break;
1069 case Terminated:
1070 dispatchTerminated(msg);
1071 break;
1072 case Undefined:
1073 default:
1074 assert(0);
1075 break;
1076 }
1077 }
1078
1079 void
1080 InviteSession::dispatch(const DumTimeout& timeout)
1081 {
1082 if (timeout.type() == DumTimeout::Retransmit200)
1083 {
1084 if (mCurrentRetransmit200)
1085 {
1086 InfoLog (<< "Retransmitting: " << endl << *mInvite200);
1087 //DumHelper::setOutgoingEncryptionLevel(*mInvite200, mCurrentEncryptionLevel);
1088 send(mInvite200);
1089 mCurrentRetransmit200 *= 2;
1090 mDum.addTimerMs(DumTimeout::Retransmit200, resipMin(Timer::T2, mCurrentRetransmit200), getBaseHandle(), timeout.seq());
1091 }
1092 }
1093 else if (timeout.type() == DumTimeout::WaitForAck)
1094 {
1095 if(mCurrentRetransmit200) // If retransmit200 timer is active then ACK is not received yet
1096 {
1097 if (timeout.seq() == mLastRemoteSessionModification->header(h_CSeq).sequence())
1098 {
1099 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1100
1101 // If we are waiting for an Ack and it times out, then end with a BYE
1102 if(mState == UAS_WaitingToHangup ||
1103 mState == WaitingToHangup)
1104 {
1105 sendBye();
1106 transition(Terminated);
1107 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
1108 }
1109 else if(mState == ReceivedReinviteSentOffer)
1110 {
1111 transition(Connected);
1112 mProposedLocalSdp.reset();
1113 mProposedEncryptionLevel = DialogUsageManager::None;
1114 //!dcm! -- should this be onIllegalNegotiation?
1115 mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), 0);
1116 }
1117 else if(mState == WaitingToOffer ||
1118 mState == UAS_WaitingToOffer)
1119 {
1120 assert(mProposedLocalSdp.get());
1121 mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle());
1122 if(!isTerminated())
1123 {
1124 provideProposedOffer();
1125 }
1126 }
1127 else if(mState == WaitingToRequestOffer ||
1128 mState == UAS_WaitingToRequestOffer)
1129 {
1130 mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle());
1131 if(!isTerminated())
1132 {
1133 requestOffer();
1134 }
1135 }
1136 else
1137 {
1138 // this is so the app can decided to ignore this. default implementation
1139 // will call end next
1140 mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle());
1141 }
1142 }
1143 }
1144 }
1145 else if (timeout.type() == DumTimeout::CanDiscardAck)
1146 {
1147 AckMap::iterator i = mAcks.find(timeout.seq());
1148 if (i != mAcks.end())
1149 {
1150 mAcks.erase(i);
1151 }
1152 }
1153 else if (timeout.type() == DumTimeout::Glare)
1154 {
1155 if (mState == SentUpdateGlare)
1156 {
1157 transition(SentUpdate);
1158
1159 InfoLog (<< "Retransmitting the UPDATE (glare condition timer)");
1160 mDialog.makeRequest(*mLastLocalSessionModification, UPDATE); // increments CSeq
1161 send(mLastLocalSessionModification);
1162 }
1163 else if (mState == SentReinviteGlare)
1164 {
1165 transition(SentReinvite);
1166
1167 InfoLog (<< "Retransmitting the reINVITE (glare condition timer)");
1168 mDialog.makeRequest(*mLastLocalSessionModification, INVITE); // increments CSeq
1169 send(mLastLocalSessionModification);
1170 }
1171 else if (mState == SentReinviteNoOfferGlare)
1172 {
1173 transition(SentReinviteNoOffer);
1174
1175 InfoLog (<< "Retransmitting the reINVITE-nooffer (glare condition timer)");
1176 mDialog.makeRequest(*mLastLocalSessionModification, INVITE); // increments CSeq
1177 send(mLastLocalSessionModification);
1178 }
1179 }
1180 else if (timeout.type() == DumTimeout::SessionExpiration)
1181 {
1182 if(timeout.seq() == mSessionTimerSeq)
1183 {
1184 // this is so the app can decided to ignore this. default implementation
1185 // will call end next - which will send a BYE
1186 mDum.mInviteSessionHandler->onSessionExpired(getSessionHandle());
1187 }
1188 }
1189 else if (timeout.type() == DumTimeout::SessionRefresh)
1190 {
1191 if(timeout.seq() == mSessionTimerSeq)
1192 {
1193 // Note: If not connected then we must be issueing a reinvite/update or
1194 // receiving one - in either case the session timer stuff will get
1195 // reset/renegotiated - thus just ignore this referesh
1196 if(mState == Connected)
1197 {
1198 sessionRefresh();
1199 }
1200 }
1201 }
1202 }
1203
1204 void
1205 InviteSession::dispatchConnected(const SipMessage& msg)
1206 {
1207 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1208 std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
1209
1210 switch (toEvent(msg, sdp.get()))
1211 {
1212 case OnInvite:
1213 case OnInviteReliable:
1214 *mLastRemoteSessionModification = msg;
1215 transition(ReceivedReinviteNoOffer);
1216 //handler->onDialogModified(getSessionHandle(), None, msg);
1217 handler->onOfferRequired(getSessionHandle(), msg);
1218 break;
1219
1220 case OnInviteOffer:
1221 case OnInviteReliableOffer:
1222 *mLastRemoteSessionModification = msg;
1223 transition(ReceivedReinvite);
1224 mCurrentEncryptionLevel = getEncryptionLevel(msg);
1225 mProposedRemoteSdp = sdp; // !nash! don't clone, simply hand over the ownership - InviteSession::makeSdp(*sdp);
1226
1227 //handler->onDialogModified(getSessionHandle(), Offer, msg);
1228 handler->onOffer(getSessionHandle(), msg, *mProposedRemoteSdp);
1229 break;
1230
1231 case On2xx:
1232 case On2xxOffer:
1233 case On2xxAnswer:
1234 // retransmission of 200I
1235 // !jf! Need to include the answer here.
1236 sendAck();
1237 break;
1238
1239 case OnUpdateOffer:
1240 transition(ReceivedUpdate);
1241
1242 // !kh!
1243 // Find out if it's an UPDATE requiring state change.
1244 // See rfc3311 5.2, 4th paragraph.
1245 *mLastRemoteSessionModification = msg;
1246 mCurrentEncryptionLevel = getEncryptionLevel(msg);
1247 mProposedRemoteSdp = sdp; // !nash! don't clone, simply hand over the ownership - InviteSession::makeSdp(*sdp);
1248 handler->onOffer(getSessionHandle(), msg, *mProposedRemoteSdp);
1249 break;
1250
1251 case OnUpdate:
1252 {
1253 // ?slg? no sdp in update - just respond immediately (likely session timer) - do we need a callback?
1254 SharedPtr<SipMessage> response(new SipMessage);
1255 *mLastRemoteSessionModification = msg;
1256 mDialog.makeResponse(*response, *mLastRemoteSessionModification, 200);
1257 handleSessionTimerRequest(*response, *mLastRemoteSessionModification);
1258 send(response);
1259 break;
1260 }
1261
1262 case OnUpdateRejected:
1263 case On200Update:
1264 WarningLog (<< "DUM delivered an UPDATE response in an incorrect state " << endl << msg);
1265 assert(0);
1266 break;
1267
1268 case OnAck:
1269 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1270 handler->onAckReceived(getSessionHandle(), msg);
1271 break;
1272
1273 default:
1274 dispatchOthers(msg);
1275 break;
1276 }
1277 }
1278
1279 void
1280 InviteSession::dispatchSentUpdate(const SipMessage& msg)
1281 {
1282 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1283 std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
1284
1285 switch (toEvent(msg, sdp.get()))
1286 {
1287 case OnInvite:
1288 case OnInviteReliable:
1289 case OnInviteOffer:
1290 case OnInviteReliableOffer:
1291 case OnUpdate:
1292 case OnUpdateOffer:
1293 {
1294 // glare
1295 SharedPtr<SipMessage> response(new SipMessage);
1296 mDialog.makeResponse(*response, msg, 491);
1297 send(response);
1298 break;
1299 }
1300
1301 case On200Update:
1302 transition(Connected);
1303 handleSessionTimerResponse(msg);
1304 if (sdp.get() && mProposedLocalSdp.get())
1305 {
1306 mCurrentEncryptionLevel = getEncryptionLevel(msg);
1307 setCurrentLocalSdp(msg);
1308 mCurrentRemoteSdp = sdp; // !nash! don't clone, simply hand over the ownership - InviteSession::makeSdp(*sdp);
1309 handler->onAnswer(getSessionHandle(), msg, *mCurrentRemoteSdp, InviteSessionHandler::Reinvite);
1310 }
1311 else if(mProposedLocalSdp.get())
1312 {
1313 // If we sent an offer in the Update Request and no answer is received
1314 handler->onIllegalNegotiation(getSessionHandle(), msg);
1315 mProposedLocalSdp.reset();
1316 mProposedEncryptionLevel = DialogUsageManager::None;
1317 }
1318 break;
1319
1320 case On491Update:
1321 transition(SentUpdateGlare);
1322 start491Timer();
1323 break;
1324
1325 case On422Update: // session timer
1326 if(msg.exists(h_MinSE))
1327 {
1328 // Change interval to min from 422 response
1329 mSessionInterval = msg.header(h_MinSE).value();
1330 mMinSE = mSessionInterval;
1331 sessionRefresh();
1332 }
1333 else
1334 {
1335 // Response must contain Min_SE - if not - just ignore
1336 // ?slg? callback?
1337 transition(Connected);
1338 mProposedLocalSdp.reset();
1339 mProposedEncryptionLevel = DialogUsageManager::None;
1340 }
1341 break;
1342
1343 case OnUpdateRejected:
1344 // !jf! - callback?
1345 mProposedLocalSdp.reset();
1346 mProposedEncryptionLevel = DialogUsageManager::None;
1347 transition(Connected);
1348 break;
1349
1350 case OnGeneralFailure:
1351 sendBye();
1352 transition(Terminated);
1353 handler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
1354 break;
1355
1356 default:
1357 dispatchOthers(msg);
1358 break;
1359 }
1360 }
1361
1362 void
1363 InviteSession::dispatchSentReinvite(const SipMessage& msg)
1364 {
1365 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1366 std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
1367
1368 switch (toEvent(msg, sdp.get()))
1369 {
1370 case OnInvite:
1371 case OnInviteReliable:
1372 case OnInviteOffer:
1373 case OnInviteReliableOffer:
1374 case OnUpdate:
1375 case OnUpdateOffer:
1376 {
1377 SharedPtr<SipMessage> response(new SipMessage);
1378 mDialog.makeResponse(*response, msg, 491);
1379 send(response);
1380 break;
1381 }
1382
1383 case On1xx:
1384 case On1xxEarly:
1385 // Some UA's send a 100 response to a ReInvite - just ignore it
1386 break;
1387
1388 case On2xxAnswer:
1389 case On2xxOffer: // !slg! doesn't really make sense
1390 {
1391 transition(Connected);
1392 handleSessionTimerResponse(msg);
1393 setCurrentLocalSdp(msg);
1394
1395 // !jf! I need to potentially include an answer in the ACK here
1396 sendAck();
1397 mCurrentEncryptionLevel = getEncryptionLevel(msg);
1398
1399 if (mSessionRefreshReInvite)
1400 {
1401 mSessionRefreshReInvite = false;
1402
1403 MD5Stream currentRemote;
1404 currentRemote<< *mCurrentRemoteSdp;
1405 MD5Stream newRemote;
1406 newRemote << *sdp;
1407 bool changed = currentRemote.getHex() != newRemote.getHex();
1408
1409 if (changed)
1410 {
1411 mCurrentRemoteSdp = sdp; // !nash! don't clone, simply hand over the ownership - InviteSession::makeSdp(*sdp);
1412 handler->onRemoteSdpChanged(getSessionHandle(), msg, *mCurrentRemoteSdp);
1413 }
1414 }
1415 else
1416 {
1417 handler->onAnswer(getSessionHandle(), msg, *sdp, InviteSessionHandler::Reinvite);
1418 }
1419
1420 // !jf! do I need to allow a reINVITE overlapping the retransmission of
1421 // the ACK when a 200I is received? If yes, then I need to store all
1422 // ACK messages for 64*T1
1423 break;
1424 }
1425 case On2xx:
1426 sendAck();
1427 transition(Connected);
1428 handleSessionTimerResponse(msg);
1429 handler->onIllegalNegotiation(getSessionHandle(), msg);
1430 mProposedLocalSdp.reset();
1431 mProposedEncryptionLevel = DialogUsageManager::None;
1432 break;
1433
1434 case On422Invite:
1435 if(msg.exists(h_MinSE))
1436 {
1437 // Change interval to min from 422 response
1438 mSessionInterval = msg.header(h_MinSE).value();
1439 mMinSE = mSessionInterval;
1440 sessionRefresh();
1441 }
1442 else
1443 {
1444 // Response must contact Min_SE - if not - just ignore
1445 // ?slg? callback?
1446 transition(Connected);
1447 mProposedLocalSdp.reset();
1448 mProposedEncryptionLevel = DialogUsageManager::None;
1449 }
1450 break;
1451
1452 case On491Invite:
1453 transition(SentReinviteGlare);
1454 start491Timer();
1455 break;
1456
1457 case OnGeneralFailure:
1458 sendBye();
1459 transition(Terminated);
1460 handler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
1461 break;
1462
1463 case OnInviteFailure:
1464 case On487Invite:
1465 case On489Invite:
1466 transition(Connected);
1467 mProposedLocalSdp.reset();
1468 handler->onOfferRejected(getSessionHandle(), &msg);
1469 break;
1470
1471 default:
1472 dispatchOthers(msg);
1473 break;
1474 }
1475 }
1476
1477 void
1478 InviteSession::dispatchSentReinviteNoOffer(const SipMessage& msg)
1479 {
1480 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1481 std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
1482
1483 switch (toEvent(msg, sdp.get()))
1484 {
1485 case OnInvite:
1486 case OnInviteReliable:
1487 case OnInviteOffer:
1488 case OnInviteReliableOffer:
1489 case OnUpdate:
1490 case OnUpdateOffer:
1491 {
1492 SharedPtr<SipMessage> response(new SipMessage);
1493 mDialog.makeResponse(*response, msg, 491);
1494 send(response);
1495 break;
1496 }
1497
1498 case On1xx:
1499 case On1xxEarly:
1500 // Some UA's send a 100 response to a ReInvite - just ignore it
1501 break;
1502
1503 case On2xxAnswer: // !slg! doesn't really make sense
1504 case On2xxOffer:
1505 {
1506 transition(SentReinviteAnswered);
1507 handleSessionTimerResponse(msg);
1508 // mLastSessionModification = msg; // ?slg? why are we storing 200's?
1509 mCurrentEncryptionLevel = getEncryptionLevel(msg);
1510 mProposedRemoteSdp = sdp; // !nash! don't clone, simply hand over the ownership - InviteSession::makeSdp(*sdp);
1511 handler->onOffer(getSessionHandle(), msg, *mProposedRemoteSdp);
1512
1513 // !jf! do I need to allow a reINVITE overlapping the retransmission of
1514 // the ACK when a 200I is received? If yes, then I need to store all
1515 // ACK messages for 64*T1
1516 break;
1517 }
1518
1519 case On2xx:
1520 sendAck();
1521 transition(Connected);
1522 handleSessionTimerResponse(msg);
1523 handler->onIllegalNegotiation(getSessionHandle(), msg);
1524 mProposedLocalSdp.reset();
1525 mProposedEncryptionLevel = DialogUsageManager::None;
1526 break;
1527
1528 case On422Invite:
1529 if(msg.exists(h_MinSE))
1530 {
1531 // Change interval to min from 422 response
1532 mSessionInterval = msg.header(h_MinSE).value();
1533 mMinSE = mSessionInterval;
1534 sessionRefresh();
1535 }
1536 else
1537 {
1538 // Response must contact Min_SE - if not - just ignore
1539 // ?slg? callback?
1540 transition(Connected);
1541 mProposedLocalSdp.reset();
1542 mProposedEncryptionLevel = DialogUsageManager::None;
1543 }
1544 break;
1545
1546 case On491Invite:
1547 transition(SentReinviteNoOfferGlare);
1548 start491Timer();
1549 break;
1550
1551 case OnGeneralFailure:
1552 sendBye();
1553 transition(Terminated);
1554 handler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
1555 break;
1556
1557 case OnInviteFailure:
1558 case On487Invite:
1559 case On489Invite:
1560 transition(Connected);
1561 mProposedLocalSdp.reset();
1562 handler->onOfferRejected(getSessionHandle(), &msg);
1563 break;
1564
1565 default:
1566 dispatchOthers(msg);
1567 break;
1568 }
1569 }
1570
1571 void
1572 InviteSession::dispatchReceivedReinviteSentOffer(const SipMessage& msg)
1573 {
1574 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1575 std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
1576
1577 switch (toEvent(msg, sdp.get()))
1578 {
1579 case OnInvite:
1580 case OnInviteReliable:
1581 case OnInviteOffer:
1582 case OnInviteReliableOffer:
1583 case OnUpdate:
1584 case OnUpdateOffer:
1585 {
1586 SharedPtr<SipMessage> response(new SipMessage);
1587 mDialog.makeResponse(*response, msg, 491);
1588 send(response);
1589 break;
1590 }
1591 case OnAckAnswer:
1592 transition(Connected);
1593 setCurrentLocalSdp(msg);
1594 mCurrentRemoteSdp = sdp; // !nash! don't clone, simply hand over the ownership - InviteSession::makeSdp(*sdp);
1595 mCurrentEncryptionLevel = getEncryptionLevel(msg);
1596 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1597 handler->onAnswer(getSessionHandle(), msg, *mCurrentRemoteSdp, InviteSessionHandler::Ack);
1598 break;
1599 case OnAck:
1600 if (mLastRemoteSessionModification->header(h_CSeq).sequence() > msg.header(h_CSeq).sequence())
1601 {
1602 InfoLog(<< "dropped stale ACK");
1603 }
1604 else
1605 {
1606 InfoLog(<< "Got Ack with no answer");
1607 transition(Connected);
1608 mProposedLocalSdp.reset();
1609 mProposedEncryptionLevel = DialogUsageManager::None;
1610 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1611 //!dcm! -- should this be onIllegalNegotiation?
1612 handler->onOfferRejected(getSessionHandle(), &msg);
1613 }
1614 break;
1615 default:
1616 dispatchOthers(msg);
1617 break;
1618 }
1619 }
1620
1621 void
1622 InviteSession::dispatchGlare(const SipMessage& msg)
1623 {
1624 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1625 MethodTypes method = msg.header(h_CSeq).method();
1626 if (msg.isRequest() && (method == INVITE || method == UPDATE))
1627 {
1628 DebugLog(<< "Re-INVITE or UPDATE received when in SentReinviteGlare or SentUpdateGlare" << endl);
1629 // Received inbound reinvite or update, when waiting to resend outbound reinvite or update
1630 handler->onOfferRejected(getSessionHandle(), &msg);
1631 dispatchConnected(msg); // act as if we received message in Connected state
1632 }
1633 else
1634 {
1635 dispatchOthers(msg);
1636 }
1637 }
1638
1639 void
1640 InviteSession::dispatchReinviteNoOfferGlare(const SipMessage& msg)
1641 {
1642 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1643 MethodTypes method = msg.header(h_CSeq).method();
1644 if (msg.isRequest() && (method == INVITE || method == UPDATE))
1645 {
1646 // Received inbound reinvite or update, when waiting to resend outbound reinvite or update
1647 handler->onOfferRequestRejected(getSessionHandle(), msg);
1648 dispatchConnected(msg); // act as if we received message in Connected state
1649 }
1650 else
1651 {
1652 dispatchOthers(msg);
1653 }
1654 }
1655
1656 void
1657 InviteSession::dispatchReceivedUpdateOrReinvite(const SipMessage& msg)
1658 {
1659 MethodTypes method = msg.header(h_CSeq).method();
1660 if (method == INVITE || method == UPDATE)
1661 {
1662 // Means that the UAC has sent us a second reINVITE or UPDATE before we
1663 // responded to the first one. Bastard!
1664 SharedPtr<SipMessage> response(new SipMessage);
1665 mDialog.makeResponse(*response, msg, 500);
1666 response->header(h_RetryAfter).value() = Random::getRandom() % 10;
1667 send(response);
1668 }
1669 else
1670 {
1671 dispatchOthers(msg);
1672 }
1673 }
1674
1675
1676 void
1677 InviteSession::dispatchAnswered(const SipMessage& msg)
1678 {
1679 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
1680 {
1681 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1682 transition(Connected);
1683 }
1684 else
1685 {
1686 dispatchOthers(msg);
1687 }
1688 }
1689
1690 void
1691 InviteSession::dispatchSentReinviteAnswered(const SipMessage& msg)
1692 {
1693 if (msg.isResponse() &&
1694 msg.header(h_CSeq).method() == INVITE &&
1695 msg.header(h_StatusLine).statusCode() / 200 == 1)
1696 {
1697 // Receving a 200 retransmission is possible - but we don't have an ACK response yet - we are still waiting for provideAnswer to be
1698 // called by the app - so just drop the retransmission
1699 return;
1700 }
1701 dispatchOthers(msg);
1702 }
1703
1704 void
1705 InviteSession::dispatchWaitingToOffer(const SipMessage& msg)
1706 {
1707 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
1708 {
1709 assert(mProposedLocalSdp.get());
1710 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1711 provideProposedOffer();
1712 }
1713 else
1714 {
1715 dispatchOthers(msg);
1716 }
1717 }
1718
1719 void
1720 InviteSession::dispatchWaitingToRequestOffer(const SipMessage& msg)
1721 {
1722 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
1723 {
1724 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1725 requestOffer();
1726 }
1727 else
1728 {
1729 dispatchOthers(msg);
1730 }
1731 }
1732
1733 void
1734 InviteSession::dispatchWaitingToTerminate(const SipMessage& msg)
1735 {
1736 if (msg.isResponse() &&
1737 msg.header(h_CSeq).method() == INVITE)
1738 {
1739 if(msg.header(h_StatusLine).statusCode() / 200 == 1) // Note: stack ACK's non-2xx final responses only
1740 {
1741 // !jf! Need to include the answer here.
1742 sendAck();
1743 }
1744 sendBye();
1745 transition(Terminated);
1746 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
1747 }
1748 }
1749
1750 void
1751 InviteSession::dispatchWaitingToHangup(const SipMessage& msg)
1752 {
1753 std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg);
1754
1755 switch (toEvent(msg, sdp.get()))
1756 {
1757 case OnAck:
1758 case OnAckAnswer:
1759 {
1760 mCurrentRetransmit200 = 0; // stop the 200 retransmit timer
1761
1762 sendBye();
1763 transition(Terminated);
1764 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended);
1765 break;
1766 }
1767
1768 default:
1769 break;
1770 }
1771 }
1772
1773 void
1774 InviteSession::dispatchTerminated(const SipMessage& msg)
1775 {
1776 InfoLog (<< "InviteSession::dispatchTerminated " << msg.brief());
1777
1778 if (msg.isRequest())
1779 {
1780 if (BYE == msg.header(h_CSeq).method())
1781 {
1782 SharedPtr<SipMessage> response(new SipMessage);
1783 mDialog.makeResponse(*response, msg, 200);
1784 send(response);
1785 }
1786 else
1787 {
1788 SharedPtr<SipMessage> response(new SipMessage);
1789 mDialog.makeResponse(*response, msg, 481);
1790 send(response);
1791 }
1792
1793 // !jf! means the peer sent BYE while we are waiting for response to BYE
1794 //mDum.destroy(this);
1795 }
1796 else
1797 {
1798 mDum.destroy(this);
1799 }
1800 }
1801
1802 void
1803 InviteSession::dispatchOthers(const SipMessage& msg)
1804 {
1805 // handle OnGeneralFailure
1806 // handle OnRedirect
1807
1808 switch (msg.header(h_CSeq).method())
1809 {
1810 case PRACK:
1811 dispatchPrack(msg);
1812 break;
1813 case CANCEL:
1814 dispatchCancel(msg);
1815 break;
1816 case BYE:
1817 dispatchBye(msg);
1818 break;
1819 case INFO:
1820 dispatchInfo(msg);
1821 break;
1822 case MESSAGE:
1823 dispatchMessage(msg);
1824 break;
1825 case ACK:
1826 // Ignore duplicate ACKs from 2xx reTransmissions
1827 break;
1828 default:
1829 // handled in Dialog
1830 WarningLog (<< "DUM delivered a "
1831 << msg.header(h_CSeq).unknownMethodName()
1832 << " to the InviteSession "
1833 << endl
1834 << msg);
1835 assert(0);
1836 break;
1837 }
1838 }
1839
1840 void
1841 InviteSession::dispatchUnhandledInvite(const SipMessage& msg)
1842 {
1843 assert(msg.isRequest());
1844 assert(msg.header(h_CSeq).method() == INVITE);
1845
1846 // If we get an INVITE request from the wire and we are not in
1847 // Connected state, reject the request and send a BYE
1848 SharedPtr<SipMessage> response(new SipMessage);
1849 mDialog.makeResponse(*response, msg, 400); // !jf! what code to use?
1850 InfoLog (<< "Sending " << response->brief());
1851 send(response);
1852
1853 sendBye();
1854 transition(Terminated);
1855 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
1856 }
1857
1858 void
1859 InviteSession::dispatchPrack(const SipMessage& msg)
1860 {
1861 assert(msg.header(h_CSeq).method() == PRACK);
1862 if(msg.isRequest())
1863 {
1864 SharedPtr<SipMessage> rsp(new SipMessage);
1865 mDialog.makeResponse(*rsp, msg, 481);
1866 send(rsp);
1867
1868 sendBye();
1869 // !jf! should we make some other callback here
1870 transition(Terminated);
1871 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg);
1872 }
1873 else
1874 {
1875 // ignore. could be PRACK/200
1876 }
1877 }
1878
1879 void
1880 InviteSession::dispatchCancel(const SipMessage& msg)
1881 {
1882 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1883 assert(msg.header(h_CSeq).method() == CANCEL);
1884 if(msg.isRequest())
1885 {
1886 SharedPtr<SipMessage> rsp(new SipMessage);
1887 mDialog.makeResponse(*rsp, msg, 200);
1888 send(rsp);
1889
1890 sendBye();
1891 // !jf! should we make some other callback here
1892 transition(Terminated);
1893 handler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, &msg);
1894 }
1895 else
1896 {
1897 WarningLog (<< "DUM let me send a CANCEL at an incorrect state " << endl << msg);
1898 assert(0);
1899 }
1900 }
1901
1902 void
1903 InviteSession::dispatchBye(const SipMessage& msg)
1904 {
1905 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1906
1907 if (msg.isRequest())
1908 {
1909
1910 SharedPtr<SipMessage> rsp(new SipMessage);
1911 InfoLog (<< "Received " << msg.brief());
1912 mDialog.makeResponse(*rsp, msg, 200);
1913 send(rsp);
1914
1915 // !jf! should we make some other callback here
1916 transition(Terminated);
1917 handler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, &msg);
1918 mDum.destroy(this);
1919 }
1920 else
1921 {
1922 WarningLog (<< "DUM let me send a BYE at an incorrect state " << endl << msg);
1923 assert(0);
1924 }
1925 }
1926
1927 void
1928 InviteSession::dispatchInfo(const SipMessage& msg)
1929 {
1930 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
1931 if (msg.isRequest())
1932 {
1933 InfoLog (<< "Received " << msg.brief());
1934 mDialog.makeResponse(*mLastNitResponse, msg, 200);
1935 handler->onInfo(getSessionHandle(), msg);
1936 }
1937 else
1938 {
1939 assert(mNitState == NitProceeding);
1940 mNitState = NitComplete;
1941 //!dcm! -- toss away 1xx to an info?
1942 if (msg.header(h_StatusLine).statusCode() >= 300)
1943 {
1944 handler->onInfoFailure(getSessionHandle(), msg);
1945 }
1946 else if (msg.header(h_StatusLine).statusCode() >= 200)
1947 {
1948 handler->onInfoSuccess(getSessionHandle(), msg);
1949 }
1950 }
1951 }
1952
1953 void
1954 InviteSession::acceptNIT(int statusCode, const Contents * contents)
1955 {
1956 if (statusCode / 100 != 2)
1957 {
1958 throw UsageUseException("Must accept with a 2xx", __FILE__, __LINE__);
1959 }
1960
1961 mLastNitResponse->header(h_StatusLine).statusCode() = statusCode;
1962 mLastNitResponse->setContents(contents);
1963 Helper::getResponseCodeReason(statusCode, mLastNitResponse->header(h_StatusLine).reason());
1964 send(mLastNitResponse);
1965 }
1966
1967 class InviteSessionAcceptNITCommand : public DumCommandAdapter
1968 {
1969 public:
1970 InviteSessionAcceptNITCommand(InviteSession& inviteSession, int statusCode, const Contents* contents)
1971 : mInviteSession(inviteSession),
1972 mStatusCode(statusCode),
1973 mContents(contents?contents->clone():0)
1974 {
1975
1976 }
1977
1978 virtual void executeCommand()
1979 {
1980 mInviteSession.acceptNITCommand(mStatusCode, mContents.get());
1981 }
1982
1983 virtual std::ostream& encodeBrief(std::ostream& strm) const
1984 {
1985 return strm << "InviteSessionAcceptNITCommand";
1986 }
1987 private:
1988 InviteSession& mInviteSession;
1989 int mStatusCode;
1990 std::auto_ptr<Contents> mContents;
1991 };
1992
1993 void
1994 InviteSession::acceptNITCommand(int statusCode, const Contents* contents)
1995 {
1996 mDum.post(new InviteSessionAcceptNITCommand(*this, statusCode, contents));
1997 }
1998
1999 void
2000 InviteSession::rejectNIT(int statusCode)
2001 {
2002 if (statusCode < 400)
2003 {
2004 throw UsageUseException("Must reject with a >= 4xx", __FILE__, __LINE__);
2005 }
2006 mLastNitResponse->header(h_StatusLine).statusCode() = statusCode;
2007 mLastNitResponse->releaseContents();
2008 Helper::getResponseCodeReason(statusCode, mLastNitResponse->header(h_StatusLine).reason());
2009 send(mLastNitResponse);
2010 }
2011
2012 class InviteSessionRejectNITCommand : public DumCommandAdapter
2013 {
2014 public:
2015 InviteSessionRejectNITCommand(InviteSession& inviteSession, int statusCode)
2016 : mInviteSession(inviteSession),
2017 mStatusCode(statusCode)
2018 {
2019 }
2020
2021 virtual void executeCommand()
2022 {
2023 mInviteSession.rejectNITCommand(mStatusCode);
2024 }
2025
2026 virtual std::ostream& encodeBrief(std::ostream& strm) const
2027 {
2028 return strm << "InviteSessionRejectNITCommand";
2029 }
2030 private:
2031 InviteSession& mInviteSession;
2032 int mStatusCode;
2033 };
2034
2035 void
2036 InviteSession::rejectNITCommand(int statusCode)
2037 {
2038 mDum.post(new InviteSessionRejectNITCommand(*this, statusCode));
2039 }
2040
2041 void
2042 InviteSession::dispatchMessage(const SipMessage& msg)
2043 {
2044 InviteSessionHandler* handler = mDum.mInviteSessionHandler;
2045 if (msg.isRequest())
2046 {
2047 InfoLog (<< "Received " << msg.brief());
2048 mDialog.makeResponse(*mLastNitResponse, msg, 200);
2049 mLastNitResponse->header(h_Contacts).clear();
2050 handler->onMessage(getSessionHandle(), msg);
2051 }
2052 else
2053 {
2054 assert(mNitState == NitProceeding);
2055 mNitState = NitComplete;
2056 //!dcm! -- toss away 1xx to an message?
2057 if (msg.header(h_StatusLine).statusCode() >= 300)
2058 {
2059 handler->onMessageFailure(getSessionHandle(), msg);
2060 }
2061 else if (msg.header(h_StatusLine).statusCode() >= 200)
2062 {
2063 handler->onMessageSuccess(getSessionHandle(), msg);
2064 }
2065 }
2066 }
2067
2068 void
2069 InviteSession::startRetransmit200Timer()
2070 {
2071 mCurrentRetransmit200 = Timer::T1;
2072 unsigned int seq = mLastRemoteSessionModification->header(h_CSeq).sequence();
2073 mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), seq);
2074 mDum.addTimerMs(DumTimeout::WaitForAck, Timer::TH, getBaseHandle(), seq);
2075 }
2076
2077 // RFC3261 section 14.1
2078 // If a UAC receives a 491 response to a re-INVITE, it SHOULD start a timer with
2079 // a value T chosen as follows:
2080 // 1. If the UAC is the owner of the Call-ID of the dialog ID, T has a randomly
2081 // chosen value between 2.1 and 4 seconds in units of 10 ms.
2082 // 2. If the UAC is not the owner of the Call-ID of the dialog ID, T has a
2083 // randomly chosen value of between 0 and 2 seconds in units of 10 ms.
2084 void
2085 InviteSession::start491Timer()
2086 {
2087 unsigned int seq = mLastLocalSessionModification->header(h_CSeq).sequence();
2088
2089 if (dynamic_cast<ClientInviteSession*>(this))
2090 {
2091 int timer = Random::getRandom() % (4000 - 2100);
2092 timer += 2100;
2093 timer -= timer % 10;
2094
2095 DebugLog(<< "491 timer value: " << timer << "ms" << endl);
2096 mDum.addTimerMs(DumTimeout::Glare, timer, getBaseHandle(), seq);
2097 }
2098 else
2099 {
2100 int timer = Random::getRandom() % 2000;
2101 timer -= timer % 10;
2102 DebugLog(<< "491 timer value: " << timer << "ms" << endl);
2103 mDum.addTimerMs(DumTimeout::Glare, timer, getBaseHandle(), seq);
2104 }
2105 }
2106
2107 void
2108 InviteSession::setSessionTimerHeaders(SipMessage &msg)
2109 {
2110 if(mSessionInterval >= 90) // If mSessionInterval is 0 then SessionTimers are considered disabled
2111 {
2112 msg.header(h_SessionExpires).value() = mSessionInterval;
2113 if(msg.isRequest())
2114 {
2115 msg.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresher ? "uac" : "uas");
2116 }
2117 else
2118 {
2119 msg.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresher ? "uas" : "uac");
2120 }
2121 msg.header(h_MinSE).value() = mMinSE;
2122 }
2123 else
2124 {
2125 msg.remove(h_SessionExpires);
2126 msg.remove(h_MinSE);
2127 }
2128 }
2129
2130 void
2131 InviteSession::sessionRefresh()
2132 {
2133 if (updateMethodSupported())
2134 {
2135 transition(SentUpdate);
2136 mDialog.makeRequest(*mLastLocalSessionModification, UPDATE);
2137 mLastLocalSessionModification->releaseContents(); // Don't send SDP
2138 }
2139 else
2140 {
2141 transition(SentReinvite);
2142 mDialog.makeRequest(*mLastLocalSessionModification, INVITE);
2143 InviteSession::setSdp(*mLastLocalSessionModification, mCurrentLocalSdp.get());
2144 mProposedLocalSdp = InviteSession::makeSdp(*mCurrentLocalSdp.get(), 0);
2145 mSessionRefreshReInvite = true;
2146 }
2147 setSessionTimerHeaders(*mLastLocalSessionModification);
2148
2149 InfoLog (<< "sessionRefresh: Sending " << mLastLocalSessionModification->brief());
2150 DumHelper::setOutgoingEncryptionLevel(*mLastLocalSessionModification, mCurrentEncryptionLevel);
2151 send(mLastLocalSessionModification);
2152 }
2153
2154 void
2155 InviteSession::setSessionTimerPreferences()
2156 {
2157 mSessionInterval = mDialog.mDialogSet.getUserProfile()->getDefaultSessionTime(); // Used only if remote doesn't request a time
2158 if(mSessionInterval != 0)
2159 {
2160 // If session timers are no disabled then ensure interval is greater than or equal to MinSE
2161 mSessionInterval = resipMax(mMinSE, mSessionInterval);
2162 }
2163 switch(mDialog.mDialogSet.getUserProfile()->getDefaultSessionTimerMode())
2164 {
2165 case Profile::PreferLocalRefreshes:
2166 mSessionRefresher = true; // Default refresher is Local
2167 break;
2168 case Profile::PreferRemoteRefreshes:
2169 mSessionRefresher = false; // Default refresher is Remote
2170 break;
2171 case Profile::PreferUASRefreshes:
2172 mSessionRefresher = dynamic_cast<ServerInviteSession*>(this) != NULL; // Default refresher is UAS (for the session) - callee
2173 break;
2174 case Profile::PreferUACRefreshes:
2175 mSessionRefresher = dynamic_cast<ClientInviteSession*>(this) != NULL; // Default refresher is UAC (for the session) - caller
2176 break;
2177 }
2178 }
2179
2180 void
2181 InviteSession::startSessionTimer()
2182 {
2183 if(mSessionInterval >= 90) // 90 is the absolute minimum - RFC4028
2184 {
2185 // Check if we are the refresher
2186 if(mSessionRefresher)
2187 {
2188 // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by RFC4028)
2189 mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq);
2190 }
2191 else
2192 {
2193 // Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 and one third of the SessionInterval, seconds before the session expires (recommended by RFC4028)
2194 mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin((UInt32)32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);
2195 }
2196 }
2197 else // Session Interval less than 90 - consider timers disabled
2198 {
2199 ++mSessionTimerSeq; // increment seq, incase old timers are running and now session timers are disabled
2200 }
2201 }
2202
2203 void
2204 InviteSession::handleSessionTimerResponse(const SipMessage& msg)
2205 {
2206 assert(msg.header(h_CSeq).method() == INVITE || msg.header(h_CSeq).method() == UPDATE);
2207
2208 // Allow Re-Invites and Updates to update the Peer P-Asserted-Identity
2209 if (msg.exists(h_PAssertedIdentities))
2210 {
2211 mPeerPAssertedIdentities = msg.header(h_PAssertedIdentities);
2212 }
2213
2214 // If session timers are locally supported then handle response
2215 if(mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
2216 {
2217 setSessionTimerPreferences();
2218
2219 if(msg.exists(h_Requires) && msg.header(h_Requires).find(Token(Symbols::Timer))
2220 && !msg.exists(h_SessionExpires))
2221 {
2222 // If no Session Expires in response and Requires header is present then session timer is to be 'turned off'
2223 mSessionInterval = 0;
2224 }
2225 // Process Session Timer headers
2226 else if(msg.exists(h_SessionExpires))
2227 {
2228 mSessionInterval = msg.header(h_SessionExpires).value();
2229 if(msg.header(h_SessionExpires).exists(p_refresher))
2230 {
2231 // Remote end specified refresher preference
2232 mSessionRefresher = (msg.header(h_SessionExpires).param(p_refresher) == Data("uac"));
2233 }
2234 }
2235 else
2236 {
2237 // Note: If no Requires or Session-Expires, then UAS does not support Session Timers
2238 // - we are free to use our SessionInterval settings (set above as a default)
2239 // If far end doesn't support then refresher must be local
2240 mSessionRefresher = true;
2241 }
2242
2243 // Update MinSE if specified and longer than current value
2244 if(msg.exists(h_MinSE))
2245 {
2246 mMinSE = resipMax(mMinSE, msg.header(h_MinSE).value());
2247 }
2248
2249 startSessionTimer();
2250 }
2251 }
2252
2253 void
2254 InviteSession::handleSessionTimerRequest(SipMessage &response, const SipMessage& request)
2255 {
2256 assert(request.header(h_CSeq).method() == INVITE || request.header(h_CSeq).method() == UPDATE);
2257
2258 // Allow Re-Invites and Updates to update the Peer P-Asserted-Identity
2259 if (request.exists(h_PAssertedIdentities))
2260 {
2261 mPeerPAssertedIdentities = request.header(h_PAssertedIdentities);
2262 }
2263
2264 // If session timers are locally supported then add necessary headers to response
2265 if(mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
2266 {
2267 setSessionTimerPreferences();
2268
2269 // Check if far-end supports
2270 bool farEndSupportsTimer = false;
2271 if(request.exists(h_Supporteds) && request.header(h_Supporteds).find(Token(Symbols::Timer)))
2272 {
2273 farEndSupportsTimer = true;
2274 if(request.exists(h_SessionExpires))
2275 {
2276 // Use Session Interval requested by remote - if none then use local settings
2277 mSessionInterval = request.header(h_SessionExpires).value();
2278 if(request.header(h_SessionExpires).exists(p_refresher))
2279 {
2280 mSessionRefresher = (request.header(h_SessionExpires).param(p_refresher) == Data("uas"));
2281 }
2282 }
2283
2284 // Update MinSE if specified and longer than current value
2285 if(request.exists(h_MinSE))
2286 {
2287 mMinSE = resipMax(mMinSE, request.header(h_MinSE).value());
2288 }
2289 }
2290 else
2291 {
2292 // If far end doesn't support then refresher must be local
2293 mSessionRefresher = true;
2294 }
2295
2296 // Add Session-Expires to response if required
2297 if(mSessionInterval >= 90)
2298 {
2299 if(farEndSupportsTimer)
2300 {
2301 // If far end supports session-timer then require it, if not already present
2302 if(!response.header(h_Requires).find(Token(Symbols::Timer)))
2303 {
2304 response.header(h_Requires).push_back(Token(Symbols::Timer));
2305 }
2306 }
2307 setSessionTimerHeaders(response);
2308 }
2309
2310 startSessionTimer();
2311 }
2312 }
2313
2314 Data
2315 InviteSession::toData(State state)
2316 {
2317 switch (state)
2318 {
2319 case Undefined:
2320 return "InviteSession::Undefined";
2321 case Connected:
2322 return "InviteSession::Connected";
2323 case SentUpdate:
2324 return "InviteSession::SentUpdate";
2325 case SentUpdateGlare:
2326 return "InviteSession::SentUpdateGlare";
2327 case SentReinvite:
2328 return "InviteSession::SentReinvite";
2329 case SentReinviteGlare:
2330 return "InviteSession::SentReinviteGlare";
2331 case SentReinviteNoOffer:
2332 return "InviteSession::SentReinviteNoOffer";
2333 case SentReinviteAnswered:
2334 return "InviteSession::SentReinviteAnswered";
2335 case SentReinviteNoOfferGlare:
2336 return "InviteSession::SentReinviteNoOfferGlare";
2337 case ReceivedUpdate:
2338 return "InviteSession::ReceivedUpdate";
2339 case ReceivedReinvite:
2340 return "InviteSession::ReceivedReinvite";
2341 case ReceivedReinviteNoOffer:
2342 return "InviteSession::ReceivedReinviteNoOffer";
2343 case ReceivedReinviteSentOffer:
2344 return "InviteSession::ReceivedReinviteSentOffer";
2345 case Answered:
2346 return "InviteSession::Answered";
2347 case WaitingToOffer:
2348 return "InviteSession::WaitingToOffer";
2349 case WaitingToRequestOffer:
2350 return "InviteSession::WaitingToRequestOffer";
2351 case WaitingToTerminate:
2352 return "InviteSession::WaitingToTerminate";
2353 case WaitingToHangup:
2354 return "InviteSession::WaitingToHangup";
2355 case Terminated:
2356 return "InviteSession::Terminated";
2357
2358 case UAC_Start:
2359 return "UAC_Start";
2360 case UAS_Offer:
2361 return "UAS_Offer";
2362 case UAS_OfferProvidedAnswer:
2363 return "UAS_OfferProvidedAnswer";
2364 case UAS_EarlyOffer:
2365 return "UAS_EarlyOffer";
2366 case UAS_EarlyProvidedAnswer:
2367 return "UAS_EarlyProvidedAnswer";
2368 case UAS_NoOffer:
2369 return "UAS_NoOffer";
2370 case UAS_ProvidedOffer:
2371 return "UAS_ProvidedOffer";
2372 case UAS_EarlyNoOffer:
2373 return "UAS_EarlyNoOffer";
2374 case UAS_EarlyProvidedOffer:
2375 return "UAS_EarlyProvidedOffer";
2376 case UAS_Accepted:
2377 return "UAS_Accepted";
2378 case UAS_WaitingToOffer:
2379 return "UAS_WaitingToOffer";
2380 case UAS_AcceptedWaitingAnswer:
2381 return "UAS_AcceptedWaitingAnswer";
2382 case UAC_Early:
2383 return "UAC_Early";
2384 case UAC_EarlyWithOffer:
2385 return "UAC_EarlyWithOffer";
2386 case UAC_EarlyWithAnswer:
2387 return "UAC_EarlyWithAnswer";
2388 case UAC_Answered:
2389 return "UAC_Answered";
2390 case UAC_SentUpdateEarly:
2391 return "UAC_SentUpdateEarly";
2392 case UAC_SentUpdateConnected:
2393 return "UAC_SentUpdateConnected";
2394 case UAC_ReceivedUpdateEarly:
2395 return "UAC_ReceivedUpdateEarly";
2396 case UAC_SentAnswer:
2397 return "UAC_SentAnswer";
2398 case UAC_QueuedUpdate:
2399 return "UAC_QueuedUpdate";
2400 case UAC_Cancelled:
2401 return "UAC_Cancelled";
2402
2403 case UAS_Start:
2404 return "UAS_Start";
2405 case UAS_OfferReliable:
2406 return "UAS_OfferReliable";
2407 case UAS_NoOfferReliable:
2408 return "UAS_NoOfferReliable";
2409 case UAS_FirstSentOfferReliable:
2410 return "UAS_FirstSentOfferReliable";
2411 case UAS_FirstEarlyReliable:
2412 return "UAS_FirstEarlyReliable";
2413 case UAS_EarlyReliable:
2414 return "UAS_EarlyReliable";
2415 case UAS_SentUpdate:
2416 return "UAS_SentUpdate";
2417 case UAS_SentUpdateAccepted:
2418 return "UAS_SentUpdateAccepted";
2419 case UAS_ReceivedUpdate:
2420 return "UAS_ReceivedUpdate";
2421 case UAS_ReceivedUpdateWaitingAnswer:
2422 return "UAS_ReceivedUpdateWaitingAnswer";
2423 case UAS_WaitingToTerminate:
2424 return "UAS_WaitingToTerminate";
2425 case UAS_WaitingToHangup:
2426 return "UAS_WaitingToHangup";
2427 case UAS_WaitingToRequestOffer:
2428 return "UAS_WaitingToRequestOffer";
2429 }
2430 assert(0);
2431 return "Undefined";
2432 }
2433
2434
2435 void
2436 InviteSession::transition(State target)
2437 {
2438 InfoLog (<< "Transition " << toData(mState) << " -> " << toData(target));
2439 mState = target;
2440 }
2441
2442 bool
2443 InviteSession::isReliable(const SipMessage& msg)
2444 {
2445 // Ensure supported both locally and remotely
2446 return msg.exists(h_Supporteds) && msg.header(h_Supporteds).find(Token(Symbols::C100rel)) &&
2447 mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::C100rel));
2448 }
2449
2450 std::auto_ptr<SdpContents>
2451 InviteSession::getSdp(const SipMessage& msg)
2452 {
2453 return Helper::getSdp(msg.getContents());
2454 }
2455
2456 std::auto_ptr<SdpContents>
2457 InviteSession::makeSdp(const SdpContents& sdp)
2458 {
2459 return std::auto_ptr<SdpContents>(static_cast<SdpContents*>(sdp.clone()));
2460 }
2461
2462 auto_ptr<Contents>
2463 InviteSession::makeSdp(const SdpContents& sdp,
2464 const SdpContents* alternative)
2465 {
2466 if (alternative)
2467 {
2468 MultipartAlternativeContents* mac = new MultipartAlternativeContents;
2469 mac->parts().push_back(alternative->clone());
2470 mac->parts().push_back(sdp.clone());
2471 return auto_ptr<Contents>(mac);
2472 }
2473 else
2474 {
2475 return auto_ptr<Contents>(sdp.clone());
2476 }
2477 }
2478
2479 void
2480 InviteSession::setSdp(SipMessage& msg, const SdpContents& sdp, const SdpContents* alternative)
2481 {
2482 // !jf! should deal with multipart here
2483
2484 // This will clone the sdp since the InviteSession also wants to keep its own
2485 // copy of the sdp around for the application to access
2486 if (alternative)
2487 {
2488 MultipartAlternativeContents* mac = new MultipartAlternativeContents;
2489 mac->parts().push_back(alternative->clone());
2490 mac->parts().push_back(sdp.clone());
2491 msg.setContents(auto_ptr<Contents>(mac));
2492 }
2493 else
2494 {
2495 msg.setContents(&sdp);
2496 }
2497 }
2498
2499 void
2500 InviteSession::setSdp(SipMessage& msg, const Contents* sdp)
2501 {
2502 assert(sdp);
2503 msg.setContents(sdp);
2504 }
2505
2506 void
2507 InviteSession::provideProposedOffer()
2508 {
2509 if (dynamic_cast<MultipartAlternativeContents*>(mProposedLocalSdp.get()))
2510 {
2511 provideOffer( *(dynamic_cast<SdpContents*>((dynamic_cast<MultipartAlternativeContents*>(mProposedLocalSdp.get()))->parts().back())),
2512 mProposedEncryptionLevel,
2513 dynamic_cast<SdpContents*>((dynamic_cast<MultipartAlternativeContents*>(mProposedLocalSdp.get()))->parts().front()));
2514 }
2515 else
2516 {
2517 provideOffer(*(dynamic_cast<SdpContents*>(mProposedLocalSdp.get())), mProposedEncryptionLevel, 0);
2518 }
2519 }
2520
2521 InviteSession::Event
2522 InviteSession::toEvent(const SipMessage& msg, const SdpContents* sdp)
2523 {
2524 MethodTypes method = msg.header(h_CSeq).method();
2525 int code = msg.isResponse() ? msg.header(h_StatusLine).statusCode() : 0;
2526 bool reliable = isReliable(msg);
2527 bool sentOffer = mProposedLocalSdp.get();
2528
2529 if (code == 481 || code == 408)
2530 {
2531 return OnGeneralFailure;
2532 }
2533 else if (code >= 300 && code <= 399)
2534 {
2535 return OnRedirect;
2536 }
2537 else if (method == INVITE && code == 0)
2538 {
2539 if (sdp)
2540 {
2541 if (reliable)
2542 {
2543 return OnInviteReliableOffer;
2544 }
2545 else
2546 {
2547 return OnInviteOffer;
2548 }
2549 }
2550 else
2551 {
2552 if (reliable)
2553 {
2554 return OnInviteReliable;
2555 }
2556 else
2557 {
2558 return OnInvite;
2559 }
2560 }
2561 }
2562 else if (method == INVITE && code > 100 && code < 200) // !kh! 100 is handled by transaction layer.
2563 {
2564 if (reliable)
2565 {
2566 if (sdp)
2567 {
2568 if (sentOffer)
2569 {
2570 return On1xxAnswer;
2571 }
2572 else
2573 {
2574 return On1xxOffer;
2575 }
2576 }
2577 else
2578 {
2579 return On1xx;
2580 }
2581 }
2582 else
2583 {
2584 if (sdp)
2585 {
2586 return On1xxEarly;
2587 }
2588 else
2589 {
2590 return On1xx;
2591 }
2592 }
2593 }
2594 else if (method == INVITE && code >= 200 && code < 300)
2595 {
2596 if (sdp)
2597 {
2598 if (sentOffer)
2599 {
2600 return On2xxAnswer;
2601 }
2602 else
2603 {
2604 return On2xxOffer;
2605 }
2606 }
2607 else
2608 {
2609 return On2xx;
2610 }
2611 }
2612 else if (method == INVITE && code == 422)
2613 {
2614 return On422Invite;
2615 }
2616 else if (method == INVITE && code == 487)
2617 {
2618 return On487Invite;
2619 }
2620 else if (method == INVITE && code == 489)
2621 {
2622 return On489Invite;
2623 }
2624 else if (method == INVITE && code == 491)
2625 {
2626 return On491Invite;
2627 }
2628 else if (method == INVITE && code >= 400)
2629 {
2630 return OnInviteFailure;
2631 }
2632 else if (method == ACK)
2633 {
2634 if (sdp)
2635 {
2636 return OnAckAnswer;
2637 }
2638 else
2639 {
2640 return OnAck;
2641 }
2642 }
2643 else if (method == CANCEL && code == 0)
2644 {
2645 return OnCancel;
2646 }
2647 else if (method == CANCEL && code / 200 == 1)
2648 {
2649 return On200Cancel;
2650 }
2651 else if (method == CANCEL && code >= 400)
2652 {
2653 return OnCancelFailure;
2654 }
2655 else if (method == BYE && code == 0)
2656 {
2657 return OnBye;
2658 }
2659 else if (method == BYE && code / 200 == 1)
2660 {
2661 return On200Bye;
2662 }
2663 else if (method == PRACK && code == 0)
2664 {
2665 return OnPrack;
2666 }
2667 else if (method == PRACK && code / 200 == 1)
2668 {
2669 return On200Prack;
2670 }
2671 else if (method == UPDATE && code == 0)
2672 {
2673 if (sdp)
2674 {
2675 return OnUpdateOffer;
2676 }
2677 else
2678 {
2679 return OnUpdate;
2680 }
2681 }
2682 else if (method == UPDATE && code / 200 == 1)
2683 {
2684 return On200Update;
2685 }
2686 else if (method == UPDATE && code == 422)
2687 {
2688 return On422Update;
2689 }
2690 else if (method == UPDATE && code == 489)
2691 {
2692 return On489Update;
2693 }
2694 else if (method == UPDATE && code == 491)
2695 {
2696 return On491Update;
2697 }
2698 else if (method == UPDATE && code >= 400)
2699 {
2700 return OnUpdateRejected;
2701 }
2702 else
2703 {
2704 //assert(0); // dispatchOthers will throw if the message type is really unknown
2705 return Unknown;
2706 }
2707 }
2708
2709 void InviteSession::sendAck(const SdpContents *sdp)
2710 {
2711 SharedPtr<SipMessage> ack(new SipMessage);
2712
2713 assert(mAcks.count(mLastLocalSessionModification->header(h_CSeq).sequence()) == 0);
2714
2715 mDialog.makeRequest(*ack, ACK);
2716
2717 // Copy Authorization, Proxy Authorization headers and CSeq from original Invite
2718 if(mLastLocalSessionModification->exists(h_Authorizations))
2719 {
2720 ack->header(h_Authorizations) = mLastLocalSessionModification->header(h_Authorizations);
2721 }
2722 if(mLastLocalSessionModification->exists(h_ProxyAuthorizations))
2723 {
2724 ack->header(h_ProxyAuthorizations) = mLastLocalSessionModification->header(h_ProxyAuthorizations);
2725 }
2726 ack->header(h_CSeq).sequence() = mLastLocalSessionModification->header(h_CSeq).sequence();
2727
2728 if(sdp != 0)
2729 {
2730 setSdp(*ack, *sdp);
2731 }
2732 mAcks[ack->header(h_CSeq).sequence()] = ack;
2733 mDum.addTimerMs(DumTimeout::CanDiscardAck, Timer::TH, getBaseHandle(), ack->header(h_CSeq).sequence());
2734
2735 InfoLog (<< "Sending " << ack->brief());
2736 send(ack);
2737 }
2738
2739 void InviteSession::sendBye()
2740 {
2741 SharedPtr<SipMessage> bye(new SipMessage());
2742 mDialog.makeRequest(*bye, BYE);
2743 Data txt;
2744 if (mEndReason != NotSpecified)
2745 {
2746 Token reason("SIP");
2747 txt = getEndReasonString(mEndReason);
2748 reason.param(p_description) = txt;
2749 bye->header(h_Reasons).push_back(reason);
2750 }
2751
2752 InfoLog (<< myAddr() << " Sending BYE " << txt);
2753 send(bye);
2754 }
2755
2756 DialogUsageManager::EncryptionLevel InviteSession::getEncryptionLevel(const SipMessage& msg)
2757 {
2758 DialogUsageManager::EncryptionLevel level = DialogUsageManager::None;
2759 const SecurityAttributes* secAttr = msg.getSecurityAttributes();
2760 if (secAttr)
2761 {
2762 SignatureStatus sig = secAttr->getSignatureStatus();
2763 bool sign = (SignatureTrusted == sig || SignatureCATrusted == sig || SignatureSelfSigned == sig);
2764 bool encrypted = secAttr->isEncrypted();
2765 if (encrypted && sign ) level = DialogUsageManager::SignAndEncrypt;
2766 else if (encrypted) level = DialogUsageManager::Encrypt;
2767 else if (sign) level = DialogUsageManager::Sign;
2768 }
2769 return level;
2770 }
2771
2772 void InviteSession::setCurrentLocalSdp(const SipMessage& msg)
2773 {
2774 assert(mProposedLocalSdp.get());
2775 if (dynamic_cast<MultipartAlternativeContents*>(mProposedLocalSdp.get()))
2776 {
2777 if (DialogUsageManager::Encrypt == getEncryptionLevel(msg) || DialogUsageManager::SignAndEncrypt == getEncryptionLevel(msg))
2778 {
2779 mCurrentLocalSdp = auto_ptr<SdpContents>(static_cast<SdpContents*>((dynamic_cast<MultipartAlternativeContents*>(mProposedLocalSdp.get()))->parts().back()->clone()));
2780 }
2781 else
2782 {
2783 mCurrentLocalSdp = auto_ptr<SdpContents>(static_cast<SdpContents*>((dynamic_cast<MultipartAlternativeContents*>(mProposedLocalSdp.get()))->parts().front()->clone()));
2784 }
2785 }
2786 else
2787 {
2788 mCurrentLocalSdp = auto_ptr<SdpContents>(static_cast<SdpContents*>(mProposedLocalSdp.get()->clone()));
2789 }
2790 mProposedLocalSdp.reset();
2791 }
2792
2793 void InviteSession::onReadyToSend(SipMessage& msg)
2794 {
2795 mDum.mInviteSessionHandler->onReadyToSend(getSessionHandle(), msg);
2796 }
2797
2798 void InviteSession::referNoSub(const SipMessage& msg)
2799 {
2800 assert(msg.isRequest() && msg.header(h_CSeq).method()==REFER);
2801 mLastReferNoSubRequest = msg;
2802 mDum.mInviteSessionHandler->onReferNoSub(getSessionHandle(), mLastReferNoSubRequest);
2803 }
2804
2805 void
2806 InviteSession::acceptReferNoSub(int statusCode)
2807 {
2808 if (statusCode / 100 != 2)
2809 {
2810 throw UsageUseException("Must accept with a 2xx", __FILE__, __LINE__);
2811 }
2812
2813 SharedPtr<SipMessage> response(new SipMessage);
2814 mDialog.makeResponse(*response, mLastReferNoSubRequest, statusCode);
2815 response->header(h_ReferSub).value() = "false";
2816 //response->header(h_Supporteds).push_back(Token(Symbols::NoReferSub));
2817
2818 send(response);
2819 }
2820
2821 void
2822 InviteSession::rejectReferNoSub(int responseCode)
2823 {
2824 if (responseCode < 400)
2825 {
2826 throw UsageUseException("Must reject with a >= 4xx", __FILE__, __LINE__);
2827 }
2828
2829 SharedPtr<SipMessage> response(new SipMessage);
2830 mDialog.makeResponse(*response, mLastReferNoSubRequest, responseCode);
2831 send(response);
2832 }
2833
2834 /* ====================================================================
2835 * The Vovida Software License, Version 1.0
2836 *
2837 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
2838 *
2839 * Redistribution and use in source and binary forms, with or without
2840 * modification, are permitted provided that the following conditions
2841 * are met:
2842 *
2843 * 1. Redistributions of source code must retain the above copyright
2844 * notice, this list of conditions and the following disclaimer.
2845 *
2846 * 2. Redistributions in binary form must reproduce the above copyright
2847 * notice, this list of conditions and the following disclaimer in
2848 * the documentation and/or other materials provided with the
2849
2850 * distribution.
2851 *
2852 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
2853 * and "Vovida Open Communication Application Library (VOCAL)" must
2854 * not be used to endorse or promote products derived from this
2855 * software without prior written permission. For written
2856 * permission, please contact vocal@vovida.org.
2857 *
2858 * 4. Products derived from this software may not be called "VOCAL", nor
2859 * may "VOCAL" appear in their name, without prior written
2860 * permission of Vovida Networks, Inc.
2861 *
2862 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
2863 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2864 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
2865 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
2866 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
2867 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
2868 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2869 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
2870 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
2871 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2872 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
2873 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
2874 * DAMAGE.
2875 *
2876 * ====================================================================
2877 *
2878 * This software consists of voluntary contributions made by Vovida
2879 * Networks, Inc. and many individuals on behalf of Vovida Networks,
2880 * Inc. For more information on Vovida Networks, Inc., please see
2881 * <http://www.vovida.org/>.
2882 *
2883 */
2884

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27