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

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

Parent Directory Parent Directory | Revision Log Revision Log


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