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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3613 - (hide annotations) (download)
Wed Nov 24 01:03:05 2004 UTC (15 years ago) by jason
Original Path: main/sip/resiprocate/dum/InviteSession.cxx
File size: 39058 byte(s)
reverting back to revision 3604 due to missing checkin. 

1 derek 3138 #include "resiprocate/SdpContents.hxx"
2 sgodin 3338 #include "resiprocate/MultipartMixedContents.hxx"
3 jason 2725 #include "resiprocate/SipMessage.hxx"
4     #include "resiprocate/dum/Dialog.hxx"
5     #include "resiprocate/dum/DialogUsageManager.hxx"
6     #include "resiprocate/dum/InviteSession.hxx"
7 sgodin 3392 #include "resiprocate/dum/ClientInviteSession.hxx"
8     #include "resiprocate/dum/ServerInviteSession.hxx"
9 jason 2846 #include "resiprocate/dum/InviteSessionHandler.hxx"
10 jason 3613 #include "resiprocate/dum/Profile.hxx"
11 derek 2961 #include "resiprocate/dum/UsageUseException.hxx"
12 jason 2856 #include "resiprocate/os/Logger.hxx"
13 derek 3255 #include "resiprocate/os/Timer.hxx"
14 derek 3295 #include "resiprocate/os/Inserter.hxx"
15 jason 2555
16 derek 3094 #if defined(WIN32) && defined(_DEBUG) &&defined(LEAK_CHECK)// Used for tracking down memory leaks in Visual Studio
17 derek 3092 #define _CRTDBG_MAP_ALLOC
18     #include <stdlib.h>
19     #include <crtdbg.h>
20     #define new new( _NORMAL_BLOCK, __FILE__, __LINE__)
21     #endif // defined(WIN32) && defined(_DEBUG)
22 sgodin 3091
23 sgodin 3314 // Remove warning about 'this' use in initiator list - pointer is only stored
24     #if defined(WIN32)
25     #pragma warning( disable : 4355 ) // using this in base member initializer list
26     #endif
27 derek 3092
28 jason 2856 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
29 jason 2846
30 davidb 2603 using namespace resip;
31 derek 3255 using namespace std;
32 davidb 2603
33 derek 2976 InviteSession::InviteSession(DialogUsageManager& dum, Dialog& dialog, State initialState)
34 derek 3089 : DialogUsage(dum, dialog),
35 derek 2976 mState(initialState),
36 derek 3255 mNitState(NitComplete),
37 jason 2846 mOfferState(Nothing),
38 jason 2725 mCurrentLocalSdp(0),
39     mCurrentRemoteSdp(0),
40     mProposedLocalSdp(0),
41 derek 2965 mProposedRemoteSdp(0),
42 derek 2990 mNextOfferOrAnswerSdp(0),
43 jason 3257 mUserConnected(false),
44 jason 3278 mQueuedBye(0),
45 sgodin 3392 mSessionInterval(0),
46     mSessionRefresherUAS(false),
47     mSessionTimerSeq(0),
48 jason 3147 mDestroyer(this),
49 jason 3278 mCurrentRetransmit200(Timer::T1)
50 jason 2555 {
51 jason 3156 DebugLog ( << "^^^ InviteSession::InviteSession " << this);
52 jason 2846 assert(mDum.mInviteSessionHandler);
53 jason 2555 }
54    
55 derek 2858 InviteSession::~InviteSession()
56     {
57 jason 3156 DebugLog ( << "^^^ InviteSession::~InviteSession " << this);
58 derek 2965 delete mCurrentLocalSdp;
59     delete mCurrentRemoteSdp;
60     delete mProposedLocalSdp;
61     delete mProposedRemoteSdp;
62     delete mNextOfferOrAnswerSdp;
63 derek 3276 delete mQueuedBye;
64 derek 2858 mDialog.mInviteSession = 0;
65     }
66 jason 2846
67 derek 3064 SipMessage&
68     InviteSession::modifySession()
69     {
70 derek 3308 DebugLog( << "InviteSession::modifySession: " << mDialog.getId());
71 derek 3291 if (mNextOfferOrAnswerSdp == 0 || mState != Connected || mOfferState != Answered)
72 derek 3064 {
73 nash 3401 throw UsageUseException("Must be in the connected state and have propsed an offer to call modifySession",
74 derek 3064 __FILE__, __LINE__);
75     }
76     mState = ReInviting;
77 jason 3433 mDialog.makeRequest(mLastRequest, INVITE);
78 derek 3064 return mLastRequest;
79     }
80    
81 derek 3255 SipMessage&
82     InviteSession::makeFinalResponse(int code)
83     {
84 derek 3289 int cseq = mLastIncomingRequest.header(h_CSeq).sequence();
85 derek 3255 SipMessage& finalResponse = mFinalResponseMap[cseq];
86 derek 3289 mDialog.makeResponse(finalResponse, mLastIncomingRequest, 200);
87 sgodin 3392
88     // Add Session Timer info to response (if required)
89     handleSessionTimerRequest(mLastIncomingRequest, finalResponse);
90    
91     // Check if we should add our capabilites to the invite success response
92 jason 3613 if(mDum.getProfile()->isAdvertisedCapability(Headers::Allow)) finalResponse.header(h_Allows) = mDum.getProfile()->getAllowedMethods();
93     if(mDum.getProfile()->isAdvertisedCapability(Headers::AcceptEncoding)) finalResponse.header(h_AcceptEncodings) = mDum.getProfile()->getSupportedEncodings();
94     if(mDum.getProfile()->isAdvertisedCapability(Headers::AcceptLanguage)) finalResponse.header(h_AcceptLanguages) = mDum.getProfile()->getSupportedLanguages();
95     if(mDum.getProfile()->isAdvertisedCapability(Headers::Supported)) finalResponse.header(h_Supporteds) = mDum.getProfile()->getSupportedOptionTags();
96 sgodin 3392
97 derek 3255 return finalResponse;
98     }
99 derek 3064
100     SipMessage&
101 derek 3291 InviteSession::acceptDialogModification(int statusCode)
102 derek 3064 {
103     if (mNextOfferOrAnswerSdp == 0 || mState != ReInviting)
104     {
105 nash 3401 throw UsageUseException("Must be in the ReInviting state and have propsed an answer to call answerModifySession",
106 derek 3064 __FILE__, __LINE__);
107     }
108 derek 3255 mState = Connected;
109     return makeFinalResponse(statusCode);
110 derek 3064 }
111    
112 jason 2866 void
113     InviteSession::setOffer(const SdpContents* sdp)
114     {
115 derek 3308 DebugLog( << "InviteSession::setOffer: " << mDialog.getId());
116 derek 2965 if (mProposedRemoteSdp)
117     {
118     throw UsageUseException("Cannot set an offer with an oustanding remote offer", __FILE__, __LINE__);
119     }
120 sgodin 3091 assert(mNextOfferOrAnswerSdp == 0);
121 derek 2965 mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());
122 jason 2866 }
123    
124     void
125     InviteSession::setAnswer(const SdpContents* sdp)
126     {
127 derek 3308 DebugLog( << "InviteSession::setAnswer: " << mDialog.getId());
128 derek 2965 if (mProposedLocalSdp )
129     {
130     throw UsageUseException("Cannot set an answer with an oustanding offer", __FILE__, __LINE__);
131     }
132 sgodin 3091 assert(mNextOfferOrAnswerSdp == 0);
133 derek 2965 mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());
134 jason 2866 }
135    
136 jason 2555 const SdpContents*
137 jason 3408 InviteSession::getLocalSdp() const
138 jason 2555 {
139 jason 2725 return mCurrentLocalSdp;
140 jason 2555 }
141    
142     const SdpContents*
143 jason 3408 InviteSession::getRemoteSdp() const
144 jason 2555 {
145 jason 2725 return mCurrentRemoteSdp;
146 jason 2555 }
147 davidb 2575
148 jason 2941 InviteSessionHandle
149     InviteSession::getSessionHandle()
150     {
151     return InviteSessionHandle(mDum, getBaseHandle().getId());
152     }
153    
154 jason 2856 void
155 derek 2990 InviteSession::dispatch(const DumTimeout& timeout)
156     {
157 derek 3138 Destroyer::Guard guard(mDestroyer);
158 derek 3255 if (timeout.type() == DumTimeout::Retransmit200)
159 derek 2990 {
160 derek 3255 CSeqToMessageMap::iterator it = mFinalResponseMap.find(timeout.seq());
161     if (it != mFinalResponseMap.end())
162     {
163     mDum.send(it->second);
164     mCurrentRetransmit200 *= 2;
165     mDum.addTimerMs(DumTimeout::Retransmit200, resipMin(Timer::T2, mCurrentRetransmit200), getBaseHandle(), timeout.seq());
166     }
167 derek 2990 }
168 derek 3255 else if (timeout.type() == DumTimeout::WaitForAck)
169 derek 2990 {
170 derek 3255 CSeqToMessageMap::iterator it = mFinalResponseMap.find(timeout.seq());
171     if (it != mFinalResponseMap.end())
172     {
173 jason 3433 // BYE could be queued if end() is called when we are still waiting for far end ACK to be received
174 sgodin 3363 if (mQueuedBye)
175     {
176     mState = Terminated;
177     mLastRequest = *mQueuedBye;
178     delete mQueuedBye;
179     mQueuedBye = 0;
180     send(mLastRequest);
181     }
182     else
183     {
184     mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle(), it->second);
185     }
186    
187 derek 3255 mFinalResponseMap.erase(it);
188     }
189 derek 2990 }
190 derek 3255 else if (timeout.type() == DumTimeout::CanDiscardAck)
191     {
192     assert(mAckMap.find(timeout.seq()) != mFinalResponseMap.end());
193     mAckMap.erase(timeout.seq());
194     }
195 sgodin 3392 else if (timeout.type() == DumTimeout::SessionExpiration)
196     {
197     if(timeout.seq() == mSessionTimerSeq)
198     {
199     if(mState != Terminated)
200     {
201 jason 3425 end(); // end expired session
202 sgodin 3392 }
203     }
204     }
205     else if (timeout.type() == DumTimeout::SessionRefresh)
206     {
207     if(timeout.seq() == mSessionTimerSeq)
208     {
209     if(mState == Connected)
210     {
211     mState = ReInviting;
212     setOffer(mCurrentLocalSdp);
213     // Should likely call targetRefresh when implemented - for now only ReInvites are used
214 jason 3433 mDialog.makeRequest(mLastRequest, INVITE);
215 sgodin 3392 if(mSessionInterval >= 90)
216     {
217 jason 3408 mLastRequest.header(h_SessionExpires).value() = mSessionInterval;
218    
219 sgodin 3392 mLastRequest.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresherUAS ? "uas" : "uac");
220     }
221     send(mLastRequest);
222     }
223     }
224     }
225 derek 2990 }
226    
227 sgodin 3392 void
228     InviteSession::handleSessionTimerResponse(const SipMessage& msg)
229     {
230     // If session timers are locally supported then handle response
231 jason 3613 if(mDum.getProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
232 sgodin 3392 {
233     bool fUAS = dynamic_cast<ServerInviteSession*>(this) != NULL;
234    
235     // Process Session Timer headers
236     if(msg.exists(h_Requires) && msg.header(h_Requires).find(Token(Symbols::Timer)))
237     {
238     if(msg.exists(h_SessionExpires))
239     {
240     mSessionInterval = msg.header(h_SessionExpires).value();
241     mSessionRefresherUAS = fUAS; // Default to us as refresher
242     if(msg.header(h_SessionExpires).exists(p_refresher))
243     {
244     mSessionRefresherUAS = (msg.header(h_SessionExpires).param(p_refresher) == Data("uas"));
245     }
246     }
247     else
248     {
249     // If no Session Expires in response then session timer is to be 'turned off'
250     mSessionInterval = 0;
251     }
252     }
253     else if(msg.exists(h_SessionExpires)) // If UAS decides to be the refresher - then he MAY not set the Requires header to timer
254     {
255     mSessionInterval = msg.header(h_SessionExpires).value();
256     mSessionRefresherUAS = fUAS; // Default to us as refresher
257     if(msg.header(h_SessionExpires).exists(p_refresher))
258     {
259     mSessionRefresherUAS = (msg.header(h_SessionExpires).param(p_refresher) == Data("uas"));
260     }
261     }
262     // Note: If no Requires or Session-Expires, then UAS does not support Session Timers - we are free to use our settings
263    
264     if(mSessionInterval >= 90) // 90 is the absolute minimum
265     {
266     // Check if we are the refresher
267     if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))
268     {
269     // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by draft-ietf-sip-session-timer-15)
270     mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq);
271     }
272     else
273     {
274 jason 3433 // Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 or SessionInterval/3 seconds before the session expires (recommended by draft-ietf-sip-session-timer-15)
275 sgodin 3392 mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);
276     }
277     }
278     }
279     }
280    
281     void
282     InviteSession::handleSessionTimerRequest(const SipMessage& request, SipMessage &response)
283     {
284     // If session timers are locally supported then add necessary headers to response
285 jason 3613 if(mDum.getProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
286 sgodin 3392 {
287     bool fUAS = dynamic_cast<ServerInviteSession*>(this) != NULL;
288    
289     // Check if we are the refresher
290     if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))
291     {
292     // If we receive a reinvite, but we are the refresher - don't process for session timers (probably just a TargetRefresh or hold request)
293     return;
294     }
295    
296 jason 3613 mSessionInterval = mDum.getProfile()->getDefaultSessionTime(); // Used only if UAC doesn't request a time
297 sgodin 3392 mSessionRefresherUAS = true; // Used only if UAC doesn't request a time
298    
299     // Check if far-end supports
300     bool farEndSupportsTimer = false;
301     if(request.exists(h_Supporteds) && request.header(h_Supporteds).find(Token(Symbols::Timer)))
302     {
303     farEndSupportsTimer = true;
304     if(request.exists(h_SessionExpires))
305     {
306     // Use Session Interval requested by UAC - if none then use local settings
307     mSessionInterval = request.header(h_SessionExpires).value();
308     mSessionRefresherUAS = fUAS; // Default to us as refresher
309     if(request.header(h_SessionExpires).exists(p_refresher))
310     {
311     mSessionRefresherUAS = (request.header(h_SessionExpires).param(p_refresher) == Data("uas"));
312     }
313     }
314     }
315    
316     // Add Session-Expires if required
317     if(mSessionInterval >= 90)
318     {
319     if(farEndSupportsTimer)
320     {
321     response.header(h_Requires).push_back(Token(Symbols::Timer));
322     }
323     response.header(h_SessionExpires).value() = mSessionInterval;
324     response.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresherUAS ? "uas" : "uac");
325    
326     // Check if we are the refresher
327     if((fUAS && mSessionRefresherUAS) || (!fUAS && !mSessionRefresherUAS))
328     {
329     // Start Session-Refresh Timer to mSessionInterval / 2 (recommended by draft-ietf-sip-session-timer-15)
330     mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq);
331     }
332     else
333     {
334 jason 3433 // Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 or SessionInterval/3 seconds before the session expires (recommended by draft-ietf-sip-session-timer-15)
335 sgodin 3392 mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);
336     }
337     }
338     }
339     }
340    
341 derek 2990 void
342 jason 2856 InviteSession::dispatch(const SipMessage& msg)
343     {
344 derek 3138 Destroyer::Guard guard(mDestroyer);
345 jason 2856 std::pair<OfferAnswerType, const SdpContents*> offans;
346     offans = InviteSession::getOfferOrAnswer(msg);
347 derek 3255
348     //ugly. non-invite-transactions(nit) don't interact with the invite
349     //transaction state machine(for now we have a separate INFO state machine)
350     //it's written as a gerneric NIT satet machine, but method isn't checked, and
351     //info is the only NIT so far. This should eventually live in Dialog, with a
352     //current method to determine valid responses.
353 jason 3433 if (msg.header(h_CSeq).method() == INFO)
354 derek 3255 {
355     if (msg.isRequest())
356     {
357 derek 3261 SipMessage response;
358     mDialog.makeResponse(response, msg, 200);
359     send(response);
360 derek 3255 mDum.mInviteSessionHandler->onInfo(getSessionHandle(), msg);
361     }
362     else
363     {
364     if (mNitState == NitProceeding)
365     {
366     int code = msg.header(h_StatusLine).statusCode();
367     if (code < 200)
368     {
369     //ignore
370     }
371     else if (code < 300)
372     {
373     mNitState = NitComplete;
374     mDum.mInviteSessionHandler->onInfoSuccess(getSessionHandle(), msg);
375     }
376     else
377     {
378     mNitState = NitComplete;
379     mDum.mInviteSessionHandler->onInfoFailure(getSessionHandle(), msg);
380     }
381     }
382     }
383     return;
384     }
385 jason 2856
386 jason 3433 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK &&
387 derek 3255 (mState == Connected || mState == ReInviting))
388     {
389     //quench 200 retransmissions
390     mFinalResponseMap.erase(msg.header(h_CSeq).sequence());
391 derek 3291 if (msg.header(h_CSeq).sequence() == mLastIncomingRequest.header(h_CSeq).sequence())
392     {
393 jason 3433 // BYE could be queued if end() is called when we are still waiting for far end ACK to be received
394 sgodin 3363 if (mQueuedBye)
395     {
396     mState = Terminated;
397     mLastRequest = *mQueuedBye;
398     delete mQueuedBye;
399     mQueuedBye = 0;
400     send(mLastRequest);
401     return;
402     }
403    
404 derek 3291 if (offans.first != None)
405     {
406     if (mOfferState == Answered)
407     {
408 jason 3433 //SDP in invite and in ACK.
409 derek 3291 mDum.mInviteSessionHandler->onIllegalNegotiation(getSessionHandle(), msg);
410     }
411     else
412     {
413     //delaying onConnected until late SDP
414     InviteSession::incomingSdp(msg, offans.second);
415     if (!mUserConnected)
416     {
417     mUserConnected = true;
418     mDum.mInviteSessionHandler->onConnected(getSessionHandle(), msg);
419     }
420     }
421     }
422 derek 3308 //temporary hack
423     else if (mState != ReInviting && mOfferState != Answered)
424 derek 3255 {
425 jason 3433 //no SDP in ACK when one is required
426 derek 3255 mDum.mInviteSessionHandler->onIllegalNegotiation(getSessionHandle(), msg);
427     }
428 derek 3291 }
429 derek 3255 }
430    
431 derek 2961 switch(mState)
432 jason 2856 {
433 derek 2961 case Terminated:
434 derek 2978 //!dcm! -- 481 behaviour here, should pretty much die on anything
435 jason 3433 //eventually 200 to BYE could be handled further out
436 derek 2997 if (msg.isResponse())
437 derek 2961 {
438 derek 2997 int code = msg.header(h_StatusLine).statusCode();
439 jason 3433 if ((code == 200 && msg.header(h_CSeq).method() == BYE) || code > 399)
440 derek 2997 {
441     mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
442 derek 3138 guard.destroy();
443 derek 3006 return;
444 derek 2997 }
445 derek 2961 }
446 derek 3138 else
447     {
448     //make a function to do this & the occurences of this in DialogUsageManager
449     SipMessage failure;
450     mDum.makeResponse(failure, msg, 481);
451 jason 3613 failure.header(h_AcceptLanguages) = mDum.mProfile->getSupportedLanguages();
452 derek 3138 mDum.sendResponse(failure);
453     }
454 derek 2961 break;
455     case Connected:
456     if (msg.isRequest())
457     {
458     switch(msg.header(h_RequestLine).method())
459     {
460 sgodin 3091 // reINVITE
461 jason 3433 case INVITE:
462 derek 3255 {
463     if (mOfferState == Answered)
464 derek 2961 {
465 derek 3255 mState = ReInviting;
466     mDialog.update(msg);
467 derek 3289 mLastIncomingRequest = msg;
468 derek 3291 mDum.mInviteSessionHandler->onDialogModified(getSessionHandle(), offans.first, msg);
469 derek 3255 if (offans.first != None)
470     {
471     incomingSdp(msg, offans.second);
472     }
473 derek 3291 else
474     {
475     mDum.mInviteSessionHandler->onOfferRequired(getSessionHandle(), msg);
476     }
477 derek 2961 }
478 derek 3255 else
479     {
480     //4??
481     SipMessage failure;
482     mDialog.makeResponse(failure, msg, 491);
483     InfoLog (<< "Sending 491 - overlapping Invite transactions");
484     mDum.sendResponse(failure);
485     }
486     }
487     break;
488 jason 3433 case BYE:
489 derek 2978 mState = Terminated;
490     mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
491     mDialog.makeResponse(mLastResponse, msg, 200);
492     send(mLastResponse);
493 derek 2961 break;
494 jason 2856
495 jason 3433 case UPDATE:
496 derek 2961 assert(0);
497     break;
498 jason 2856
499 jason 3433 case INFO:
500 derek 2961 mDum.mInviteSessionHandler->onInfo(getSessionHandle(), msg);
501 derek 3255 break;
502 jason 3433 case REFER:
503 derek 3101 //handled in Dialog
504     assert(0);
505 derek 2961 break;
506 sgodin 3332
507 jason 3433 case CANCEL:
508 sgodin 3332 // A Cancel can get received in an established dialog if it crosses with our 200 response
509     // on the wire - it should be responsed to, but should not effect the dialog state (InviteSession).
510     // RFC3261 Section 9.2
511     mDialog.makeResponse(mLastResponse, msg, 200);
512     send(mLastResponse);
513     break;
514 jason 2856
515 derek 2961 default:
516 jason 3433 InfoLog (<< "Ignoring request in an INVITE dialog: " << msg.brief());
517 derek 2961 break;
518     }
519 derek 3006 }
520     else
521     {
522 jason 3433 if ( msg.header(h_StatusLine).statusCode() == 200 && msg.header(h_CSeq).method() == INVITE)
523 derek 3006 {
524 derek 3255 CSeqToMessageMap::iterator it = mAckMap.find(msg.header(h_CSeq).sequence());
525     if (it != mAckMap.end())
526     {
527 derek 3282 mDum.send(it->second);
528 derek 3255 }
529 derek 3006 }
530     }
531 derek 3064 break;
532     case ReInviting:
533 jason 3433 if (msg.header(h_CSeq).method() == INVITE)
534 derek 3064 {
535 derek 3255 if (msg.isResponse())
536 derek 3089 {
537 derek 3255 int code = msg.header(h_StatusLine).statusCode();
538     if (code < 200)
539 derek 3167 {
540 derek 3276 return;
541 derek 3255 }
542     else if (code < 300)
543     {
544     if (msg.header(h_CSeq).sequence() == mLastRequest.header(h_CSeq).sequence())
545     {
546     mState = Connected;
547 derek 3276 //user has called end, so no more callbacks relating to
548     //this usage other than onTerminated
549     if (mQueuedBye)
550     {
551 jason 3433 send(makeAck()); // ACK the 200 first then send BYE
552 sgodin 3363 mState = Terminated;
553 derek 3276 mLastRequest = *mQueuedBye;
554     delete mQueuedBye;
555     mQueuedBye = 0;
556     send(mLastRequest);
557     return;
558     }
559 sgodin 3392
560     // Handle any Session Timer headers in response
561     handleSessionTimerResponse(msg);
562    
563 derek 3255 if (offans.first != None)
564     {
565 derek 3291 if (offans.first == Answer)
566     {
567 jason 3433 //no late media required, so just send the ACK
568 derek 3291 send(makeAck());
569     }
570 derek 3295 incomingSdp(msg, offans.second);
571 derek 3255 }
572     else
573     {
574 derek 3295 //no offer or answer in 200, this will eventually be
575     //legal with PRACK/UPDATE
576     send(makeAck());
577 derek 3255 if (mOfferState != Answered)
578     {
579     //reset the sdp state machine
580     incomingSdp(msg, 0);
581     mDum.mInviteSessionHandler->onIllegalNegotiation(getSessionHandle(), msg);
582     }
583     }
584     }
585     else //200 retransmission that overlaps with this Invite transaction
586     {
587     CSeqToMessageMap::iterator it = mAckMap.find(msg.header(h_CSeq).sequence());
588     if (it != mAckMap.end())
589     {
590 derek 3282 mDum.send(it->second);
591 derek 3255 }
592     }
593 derek 3167 }
594 sgodin 3392 else if(code == 408 || code == 481)
595     {
596     // If ReInvite response is Timeout (408) or Transaction Does not Exits (481) - end dialog
597     mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
598     guard.destroy();
599     }
600 derek 3241 else
601     {
602 sgodin 3392 // !slg! handle 491 response and retry???
603    
604 derek 3276 mState = Connected;
605     //user has called end, so no more callbacks relating to
606     //this usage other than onTerminated
607     if (mQueuedBye)
608     {
609 jason 3433 send(makeAck()); // ACK the 200 first then send BYE
610 sgodin 3363 mState = Terminated;
611 derek 3276 mLastRequest = *mQueuedBye;
612     delete mQueuedBye;
613     mQueuedBye = 0;
614     send(mLastRequest);
615     return;
616     }
617 derek 3291 //reset the sdp state machine
618     incomingSdp(msg, 0);
619 derek 3255 mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);
620 derek 3241 }
621 derek 3089 }
622 derek 3167 else
623     {
624 derek 3255 SipMessage failure;
625     mDialog.makeResponse(failure, msg, 491);
626     InfoLog (<< "Sending 491 - overlapping Invite transactions");
627     mDum.sendResponse(failure);
628     return;
629 derek 3167 }
630 derek 3064 }
631 jason 3433 else if(msg.header(h_CSeq).method() == BYE && msg.isRequest())
632 sgodin 3363 {
633 jason 3433 // Inbound BYE crosses with outbound REINVITE
634 jason 3408
635     mState = Terminated;
636    
637    
638    
639     mDum.mInviteSessionHandler->onTerminated(getSessionHandle(),msg);
640    
641     mDialog.makeResponse(mLastResponse, msg, 200);
642    
643     send(mLastResponse);
644    
645 sgodin 3363 }
646 derek 3064 else
647     {
648     ErrLog ( << "Spurious message sent to UAS " << msg );
649     return;
650     }
651     break;
652 derek 2961 default:
653 derek 3024 DebugLog ( << "Throwing away strange message: " << msg );
654     //throw message away
655     // assert(0); //all other cases should be handled in base classes
656    
657 jason 2856 }
658     }
659    
660 derek 2955 SipMessage&
661 derek 3255 InviteSession::makeInfo(auto_ptr<Contents> contents)
662     {
663     if (mNitState == NitProceeding)
664     {
665 nash 3401 throw UsageUseException("Cannot start a non-invite transaction until the previous one has completed",
666 derek 3255 __FILE__, __LINE__);
667     }
668     mNitState = NitProceeding;
669 jason 3433 mDialog.makeRequest(mLastNit, INFO);
670 derek 3293 mLastNit.releaseContents();
671 derek 3261 mLastNit.setContents(contents);
672 derek 3255 return mLastNit;
673     }
674    
675     SipMessage&
676 derek 3039 InviteSession::makeRefer(const NameAddr& referTo)
677 derek 2955 {
678 jason 3433 mDialog.makeRequest(mLastRequest, REFER);
679 derek 3058 mLastRequest.header(h_ReferTo) = referTo;
680 jason 3433 // mLastRequest.header(h_ReferTo).param(p_method) = getMethodName(INVITE);
681 derek 3058 return mLastRequest;
682 derek 2955 }
683 jason 2856
684 derek 3112 SipMessage&
685     InviteSession::makeRefer(const NameAddr& referTo, InviteSessionHandle sessionToReplace)
686     {
687     if (!sessionToReplace.isValid())
688     {
689 nash 3401 throw UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__);
690 derek 3112 }
691    
692 jason 3433 mDialog.makeRequest(mLastRequest, REFER);
693 derek 3112 mLastRequest.header(h_ReferTo) = referTo;
694     CallId replaces;
695     DialogId id = sessionToReplace->mDialog.getId();
696     replaces.value() = id.getCallId();
697     replaces.param(p_toTag) = id.getRemoteTag();
698     replaces.param(p_fromTag) = id.getLocalTag();
699    
700     mLastRequest.header(h_ReferTo).uri().embedded().header(h_Replaces) = replaces;
701     return mLastRequest;
702     }
703    
704 jason 3425 void
705 jason 2621 InviteSession::end()
706     {
707 derek 3006 InfoLog ( << "InviteSession::end, state: " << mState);
708 derek 2961 switch (mState)
709     {
710     case Terminated:
711 derek 2965 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
712 derek 2961 break;
713     case Connected:
714 jason 3433 // Check state of 200 retrans map to see if we have recieved an ACK or not yet
715 sgodin 3363 if (mFinalResponseMap.find(mLastIncomingRequest.header(h_CSeq).sequence()) != mFinalResponseMap.end())
716     {
717     if(!mQueuedBye)
718     {
719 jason 3433 // No ACK yet - send BYE after ACK is received
720 sgodin 3363 mQueuedBye = new SipMessage(mLastRequest);
721 jason 3433 mDialog.makeRequest(*mQueuedBye, BYE);
722 sgodin 3363 }
723     else
724     {
725     throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
726     }
727     }
728     else
729     {
730     InfoLog ( << "InviteSession::end, Connected" );
731 jason 3433 mDialog.makeRequest(mLastRequest, BYE);
732 sgodin 3363 //new transaction
733     assert(mLastRequest.header(h_Vias).size() == 1);
734     mState = Terminated;
735 jason 3425 send(mLastRequest);
736 sgodin 3363 }
737 derek 2961 break;
738 derek 3276 case ReInviting:
739 sgodin 3363 if(!mQueuedBye)
740     {
741     mQueuedBye = new SipMessage(mLastRequest);
742 jason 3433 mDialog.makeRequest(*mQueuedBye, BYE);
743 sgodin 3363 }
744     else
745     {
746     throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
747     }
748 derek 3276 break;
749 derek 2961 default:
750     assert(0); // out of states
751     }
752 jason 2621 }
753    
754 jason 2809 // If sdp==0, it means the last offer failed
755 derek 2965 // !dcm! -- eventually handle confused UA's that send offers/answers at
756     // inappropriate times, probably with a different callback
757 jason 2846 void
758     InviteSession::incomingSdp(const SipMessage& msg, const SdpContents* sdp)
759 jason 2809 {
760     switch (mOfferState)
761     {
762 jason 2846 case Nothing:
763 jason 2809 assert(mCurrentLocalSdp == 0);
764     assert(mCurrentRemoteSdp == 0);
765 sgodin 3091 assert(mProposedLocalSdp == 0);
766     assert(mProposedRemoteSdp == 0);
767 jason 2846 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
768 jason 2809 mOfferState = Offerred;
769 jason 2846 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
770 jason 2809 break;
771    
772     case Offerred:
773 sgodin 3091 assert(mCurrentLocalSdp == 0);
774     assert(mCurrentRemoteSdp == 0);
775 jason 2809 mCurrentLocalSdp = mProposedLocalSdp;
776 jason 2846 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
777 sgodin 3091 delete mProposedRemoteSdp;
778 jason 2809 mProposedLocalSdp = 0;
779     mProposedRemoteSdp = 0;
780     mOfferState = Answered;
781 jason 2846 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
782 jason 2809 break;
783    
784     case Answered:
785     assert(mProposedLocalSdp == 0);
786     assert(mProposedRemoteSdp == 0);
787 jason 2846 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
788 jason 2809 mOfferState = CounterOfferred;
789 jason 2846 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
790 jason 2809 break;
791 derek 2965
792 jason 2809 case CounterOfferred:
793     assert(mCurrentLocalSdp);
794     assert(mCurrentRemoteSdp);
795 jason 2846 mOfferState = Answered;
796 sgodin 3071 if (sdp) // !slg! There currenlty doesn't seem to be anyone calling this with sdp == 0
797 jason 2809 {
798 derek 2965 delete mCurrentLocalSdp;
799     delete mCurrentRemoteSdp;
800 jason 2809 mCurrentLocalSdp = mProposedLocalSdp;
801 jason 2846 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
802 sgodin 3091 delete mProposedRemoteSdp;
803 derek 2965 mProposedLocalSdp = 0;
804     mProposedRemoteSdp = 0;
805     mOfferState = Answered;
806 jason 2846 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
807 jason 2809 }
808     else
809     {
810 sgodin 3091 delete mProposedLocalSdp;
811     delete mProposedRemoteSdp;
812 jason 2809 mProposedLocalSdp = 0;
813     mProposedRemoteSdp = 0;
814 jason 2846 // !jf! is this right?
815 derek 3291 // mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);
816 jason 2809 }
817     break;
818     }
819     }
820    
821 derek 2965 void
822     InviteSession::send(SipMessage& msg)
823     {
824 derek 3138 Destroyer::Guard guard(mDestroyer);
825 derek 3293 //handle NITs separately
826 jason 3433 if (msg.header(h_CSeq).method() == INFO)
827 derek 3293 {
828     mDum.send(msg);
829     return;
830     }
831    
832 derek 3282 msg.releaseContents();
833 derek 3276 if (mQueuedBye && (mQueuedBye == &msg))
834     {
835     //queued
836     return;
837     }
838    
839 derek 2965 if (msg.isRequest())
840     {
841 derek 3089 switch(msg.header(h_RequestLine).getMethod())
842     {
843 jason 3433 case INVITE:
844     case UPDATE:
845     case ACK:
846 derek 3089 if (mNextOfferOrAnswerSdp)
847     {
848 sgodin 3091 msg.setContents(mNextOfferOrAnswerSdp);
849 derek 3089 sendSdp(mNextOfferOrAnswerSdp);
850     mNextOfferOrAnswerSdp = 0;
851     }
852     break;
853     default:
854     break;
855     }
856 derek 3282 mDum.send(msg);
857 derek 2965 }
858     else
859     {
860     int code = msg.header(h_StatusLine).statusCode();
861     //!dcm! -- probably kill this object earlier, handle 200 to bye in
862     //DialogUsageManager...very soon
863 jason 3433 if (msg.header(h_CSeq).method() == BYE && code == 200) //!dcm! -- not 2xx?
864 derek 2965
865     {
866     mState = Terminated;
867     mDum.send(msg);
868 jason 3433 //mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg); // This is actually called when recieving the BYE message so that the BYE message can be passed to onTerminated
869 derek 3138 guard.destroy();
870 derek 2965 }
871 jason 3433 else if (code >= 200 && code < 300 && msg.header(h_CSeq).method() == INVITE)
872 derek 2965 {
873 derek 3255 int seq = msg.header(h_CSeq).sequence();
874     mCurrentRetransmit200 = Timer::T1;
875     mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), seq);
876     mDum.addTimerMs(DumTimeout::WaitForAck, Timer::TH, getBaseHandle(), seq);
877 derek 2990
878     //!dcm! -- this should be mFinalResponse...maybe assign here in
879 derek 2965 //case the user wants to be very strange
880     if (mNextOfferOrAnswerSdp)
881     {
882 sgodin 3091 msg.setContents(mNextOfferOrAnswerSdp);
883 derek 2965 sendSdp(mNextOfferOrAnswerSdp);
884 derek 2997 mNextOfferOrAnswerSdp = 0;
885 derek 2978 }
886     mDum.send(msg);
887     }
888     else
889     {
890     mDum.send(msg);
891     }
892 derek 2965 }
893     }
894    
895 jason 2621 void
896 derek 2965 InviteSession::sendSdp(SdpContents* sdp)
897 jason 2809 {
898     switch (mOfferState)
899     {
900 jason 2846 case Nothing:
901 jason 2809 assert(mCurrentLocalSdp == 0);
902     assert(mCurrentRemoteSdp == 0);
903 derek 2965 mProposedLocalSdp = sdp;
904 jason 2809 mOfferState = Offerred;
905     break;
906    
907     case Offerred:
908 sgodin 3091 assert(mCurrentLocalSdp == 0);
909     assert(mCurrentRemoteSdp == 0);
910 derek 2965 mCurrentLocalSdp = sdp;
911 jason 2809 mCurrentRemoteSdp = mProposedRemoteSdp;
912 sgodin 3091 delete mProposedLocalSdp;
913 jason 2809 mProposedLocalSdp = 0;
914     mProposedRemoteSdp = 0;
915     mOfferState = Answered;
916     break;
917    
918     case Answered:
919     assert(mProposedLocalSdp == 0);
920     assert(mProposedRemoteSdp == 0);
921 derek 2965 mProposedLocalSdp = sdp;
922 jason 2809 mOfferState = CounterOfferred;
923     break;
924    
925     case CounterOfferred:
926     assert(mCurrentLocalSdp);
927     assert(mCurrentRemoteSdp);
928 derek 3298 if (sdp)
929 jason 2809 {
930 derek 3064 delete mCurrentLocalSdp;
931     delete mCurrentRemoteSdp;
932 sgodin 3091 mCurrentLocalSdp = sdp;
933 jason 2809 mCurrentRemoteSdp = mProposedRemoteSdp;
934 sgodin 3091 delete mProposedLocalSdp;
935     mProposedLocalSdp = 0;
936     mProposedRemoteSdp = 0;
937 jason 2809 }
938 sgodin 3091 else
939     {
940     delete mProposedLocalSdp;
941     delete mProposedRemoteSdp;
942     mProposedLocalSdp = 0;
943     mProposedRemoteSdp = 0;
944     }
945 jason 2809 mOfferState = Answered;
946     break;
947     }
948     }
949    
950 jason 2846 std::pair<InviteSession::OfferAnswerType, const SdpContents*>
951     InviteSession::getOfferOrAnswer(const SipMessage& msg) const
952     {
953     std::pair<InviteSession::OfferAnswerType, const SdpContents*> ret;
954     ret.first = None;
955 sgodin 3338 const SdpContents* contents = NULL;
956    
957     MultipartMixedContents* mixed = dynamic_cast<MultipartMixedContents*>(msg.getContents());
958     if ( mixed )
959     {
960     // Look for first SDP Contents in a multipart contents
961     MultipartMixedContents::Parts& parts = mixed->parts();
962     for( MultipartMixedContents::Parts::const_iterator i = parts.begin();
963     i != parts.end();
964     ++i)
965     {
966     contents = dynamic_cast<const SdpContents*>(*i);
967     if(contents != NULL) break; // Found SDP contents
968     }
969     }
970     else
971     {
972     contents = dynamic_cast<const SdpContents*>(msg.getContents());
973     }
974    
975 jason 2846 if (contents)
976     {
977     static Token c100rel(Symbols::C100rel);
978 derek 2976 if (msg.isRequest() || msg.header(h_StatusLine).responseCode() == 200 ||
979 derek 3308 (msg.exists(h_Requires) && msg.header(h_Requires).find(c100rel)))
980 jason 2846 {
981     switch (mOfferState)
982     {
983 sgodin 3340 case Nothing:
984 jason 2846 ret.first = Offer;
985     ret.second = contents;
986     break;
987    
988     case Offerred:
989     ret.first = Answer;
990     ret.second = contents;
991     break;
992    
993     case Answered:
994     ret.first = Offer;
995     ret.second = contents;
996     break;
997    
998     case CounterOfferred:
999     ret.first = Answer;
1000     ret.second = contents;
1001     break;
1002     }
1003     }
1004 derek 3255 else if (msg.isResponse() &&
1005     msg.header(h_StatusLine).responseCode() < 200 &&
1006     msg.header(h_StatusLine).responseCode() >= 180)
1007     {
1008     ret.second = contents;
1009     }
1010 jason 2846 }
1011     return ret;
1012     }
1013    
1014 derek 2955 SipMessage&
1015 derek 3291 InviteSession::rejectDialogModification(int statusCode)
1016 derek 2965 {
1017 derek 3101 if (statusCode < 400)
1018     {
1019 nash 3401 throw UsageUseException("Must reject with a 4xx", __FILE__, __LINE__);
1020 derek 3101 }
1021 derek 3289 mDialog.makeResponse(mLastResponse, mLastIncomingRequest, statusCode);
1022 derek 3298 mState = Connected;
1023     sendSdp(0);
1024 derek 2976 return mLastResponse;
1025 derek 2965 }
1026    
1027     SipMessage&
1028 derek 2955 InviteSession::targetRefresh(const NameAddr& localUri)
1029     {
1030     assert(0);
1031 derek 2981 return mLastRequest;
1032 derek 2955 }
1033 davidb 2576
1034 derek 3255 void
1035     InviteSession::send()
1036 derek 2981 {
1037 derek 3295 InfoLog ( << "InviteSession::send(void)");
1038     if (mOfferState == Answered || mState != Connected)
1039 derek 3255 {
1040 nash 3401 throw UsageUseException("Cannot call send when there it no Offer/Answer negotiation to do", __FILE__, __LINE__);
1041 derek 3255 }
1042     send(makeAck());
1043 derek 2981 }
1044    
1045 derek 3255 SipMessage&
1046 derek 2965 InviteSession::makeAck()
1047 derek 2849 {
1048 derek 3255 InfoLog ( << "InviteSession::makeAck" );
1049 derek 2992
1050 derek 3255 int cseq = mLastRequest.header(h_CSeq).sequence();
1051 derek 3295 if (mAckMap.find(cseq) != mAckMap.end())
1052     {
1053     InfoLog ( << "CSeq collision in ack map: " << Inserter(mAckMap) );
1054     }
1055    
1056 derek 3255 assert(mAckMap.find(cseq) == mAckMap.end());
1057     SipMessage& ack = mAckMap[cseq];
1058     ack = mLastRequest;
1059 jason 3433 mDialog.makeRequest(ack, ACK);
1060 derek 3255 mDum.addTimerMs(DumTimeout::CanDiscardAck, Timer::TH, getBaseHandle(), cseq);
1061 derek 2992
1062 derek 3255 assert(ack.header(h_Vias).size() == 1);
1063    
1064 derek 3291 // if (mNextOfferOrAnswerSdp)
1065     // {
1066     // ack.setContents(mNextOfferOrAnswerSdp);
1067     // sendSdp(mNextOfferOrAnswerSdp);
1068     // mNextOfferOrAnswerSdp = 0;
1069     // }
1070 derek 3255 return ack;
1071 derek 2849 }
1072    
1073 davidb 2575 /* ====================================================================
1074     * The Vovida Software License, Version 1.0
1075     *
1076     * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1077     *
1078     * Redistribution and use in source and binary forms, with or without
1079     * modification, are permitted provided that the following conditions
1080     * are met:
1081     *
1082     * 1. Redistributions of source code must retain the above copyright
1083     * notice, this list of conditions and the following disclaimer.
1084     *
1085     * 2. Redistributions in binary form must reproduce the above copyright
1086     * notice, this list of conditions and the following disclaimer in
1087     * the documentation and/or other materials provided with the
1088    
1089     * distribution.
1090     *
1091     * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1092     * and "Vovida Open Communication Application Library (VOCAL)" must
1093     * not be used to endorse or promote products derived from this
1094     * software without prior written permission. For written
1095     * permission, please contact vocal@vovida.org.
1096     *
1097     * 4. Products derived from this software may not be called "VOCAL", nor
1098     * may "VOCAL" appear in their name, without prior written
1099     * permission of Vovida Networks, Inc.
1100     *
1101     * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1102     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1103     * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1104     * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1105     * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1106     * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1107     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1108     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1109     * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1110     * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1111     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1112     * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1113     * DAMAGE.
1114     *
1115     * ====================================================================
1116     *
1117     * This software consists of voluntary contributions made by Vovida
1118     * Networks, Inc. and many individuals on behalf of Vovida Networks,
1119     * Inc. For more information on Vovida Networks, Inc., please see
1120     * <http://www.vovida.org/>.
1121     *
1122     */

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27