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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

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