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

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

Parent Directory Parent Directory | Revision Log Revision Log


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