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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6507 - (hide annotations) (download)
Tue Aug 15 17:01:21 2006 UTC (13 years, 5 months ago) by sgodin
File MIME type: text/plain
File size: 74132 byte(s)
- changed ExpiresCategory.cxx to be an UInt32 so that Expires parameter can handle up to 2^32-1 as in RFC
- modified dum so that expiration values use UInt32 instead of int for storage

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

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