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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3408 - (show annotations) (download)
Tue Oct 26 11:41:57 2004 UTC (15 years, 1 month ago) by jason
Original Path: main/sip/resiprocate/dum/InviteSession.cxx
File size: 39298 byte(s)
make accessors for sdp const


1 #include "resiprocate/SdpContents.hxx"
2 #include "resiprocate/MultipartMixedContents.hxx"
3 #include "resiprocate/SipMessage.hxx"
4 #include "resiprocate/dum/Dialog.hxx"
5 #include "resiprocate/dum/DialogUsageManager.hxx"
6 #include "resiprocate/dum/InviteSession.hxx"
7 #include "resiprocate/dum/ClientInviteSession.hxx"
8 #include "resiprocate/dum/ServerInviteSession.hxx"
9 #include "resiprocate/dum/InviteSessionHandler.hxx"
10 #include "resiprocate/dum/Profile.hxx"
11 #include "resiprocate/dum/UsageUseException.hxx"
12 #include "resiprocate/os/Logger.hxx"
13 #include "resiprocate/os/Timer.hxx"
14 #include "resiprocate/os/Inserter.hxx"
15
16 #if defined(WIN32) && defined(_DEBUG) &&defined(LEAK_CHECK)// Used for tracking down memory leaks in Visual Studio
17 #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
23 // 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
28 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
29
30 using namespace resip;
31 using namespace std;
32
33 InviteSession::InviteSession(DialogUsageManager& dum, Dialog& dialog, State initialState)
34 : DialogUsage(dum, dialog),
35 mState(initialState),
36 mNitState(NitComplete),
37 mOfferState(Nothing),
38 mCurrentLocalSdp(0),
39 mCurrentRemoteSdp(0),
40 mProposedLocalSdp(0),
41 mProposedRemoteSdp(0),
42 mNextOfferOrAnswerSdp(0),
43 mUserConnected(false),
44 mQueuedBye(0),
45 mSessionInterval(0),
46 mSessionRefresherUAS(false),
47 mSessionTimerSeq(0),
48 mDestroyer(this),
49 mCurrentRetransmit200(Timer::T1)
50 {
51 DebugLog ( << "^^^ InviteSession::InviteSession " << this);
52 assert(mDum.mInviteSessionHandler);
53 }
54
55 InviteSession::~InviteSession()
56 {
57 DebugLog ( << "^^^ InviteSession::~InviteSession " << this);
58 delete mCurrentLocalSdp;
59 delete mCurrentRemoteSdp;
60 delete mProposedLocalSdp;
61 delete mProposedRemoteSdp;
62 delete mNextOfferOrAnswerSdp;
63 delete mQueuedBye;
64 mDialog.mInviteSession = 0;
65 }
66
67 SipMessage&
68 InviteSession::modifySession()
69 {
70 DebugLog( << "InviteSession::modifySession: " << mDialog.getId());
71 if (mNextOfferOrAnswerSdp == 0 || mState != Connected || mOfferState != Answered)
72 {
73 throw UsageUseException("Must be in the connected state and have propsed an offer to call modifySession",
74 __FILE__, __LINE__);
75 }
76 mState = ReInviting;
77 mDialog.makeRequest(mLastRequest, INVITE);
78 return mLastRequest;
79 }
80
81 SipMessage&
82 InviteSession::makeFinalResponse(int code)
83 {
84 int cseq = mLastIncomingRequest.header(h_CSeq).sequence();
85 SipMessage& finalResponse = mFinalResponseMap[cseq];
86 mDialog.makeResponse(finalResponse, mLastIncomingRequest, 200);
87
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 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
97 return finalResponse;
98 }
99
100 SipMessage&
101 InviteSession::acceptDialogModification(int statusCode)
102 {
103 if (mNextOfferOrAnswerSdp == 0 || mState != ReInviting)
104 {
105 throw UsageUseException("Must be in the ReInviting state and have propsed an answer to call answerModifySession",
106 __FILE__, __LINE__);
107 }
108 mState = Connected;
109 return makeFinalResponse(statusCode);
110 }
111
112 void
113 InviteSession::setOffer(const SdpContents* sdp)
114 {
115 DebugLog( << "InviteSession::setOffer: " << mDialog.getId());
116 if (mProposedRemoteSdp)
117 {
118 throw UsageUseException("Cannot set an offer with an oustanding remote offer", __FILE__, __LINE__);
119 }
120 assert(mNextOfferOrAnswerSdp == 0);
121 mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());
122 }
123
124 void
125 InviteSession::setAnswer(const SdpContents* sdp)
126 {
127 DebugLog( << "InviteSession::setAnswer: " << mDialog.getId());
128 if (mProposedLocalSdp )
129 {
130 throw UsageUseException("Cannot set an answer with an oustanding offer", __FILE__, __LINE__);
131 }
132 assert(mNextOfferOrAnswerSdp == 0);
133 mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());
134 }
135
136 const SdpContents*
137 InviteSession::getLocalSdp() const
138 {
139 return mCurrentLocalSdp;
140 }
141
142 const SdpContents*
143 InviteSession::getRemoteSdp() const
144 {
145 return mCurrentRemoteSdp;
146 }
147
148 InviteSessionHandle
149 InviteSession::getSessionHandle()
150 {
151 return InviteSessionHandle(mDum, getBaseHandle().getId());
152 }
153
154 void
155 InviteSession::dispatch(const DumTimeout& timeout)
156 {
157 Destroyer::Guard guard(mDestroyer);
158 if (timeout.type() == DumTimeout::Retransmit200)
159 {
160 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 }
168 else if (timeout.type() == DumTimeout::WaitForAck)
169 {
170 CSeqToMessageMap::iterator it = mFinalResponseMap.find(timeout.seq());
171 if (it != mFinalResponseMap.end())
172 {
173 // BYE could be queued if end() is called when we are still waiting for far end ACK to be received
174 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 mFinalResponseMap.erase(it);
188 }
189 }
190 else if (timeout.type() == DumTimeout::CanDiscardAck)
191 {
192 assert(mAckMap.find(timeout.seq()) != mFinalResponseMap.end());
193 mAckMap.erase(timeout.seq());
194 }
195 else if (timeout.type() == DumTimeout::SessionExpiration)
196 {
197 if(timeout.seq() == mSessionTimerSeq)
198 {
199 if(mState != Terminated)
200 {
201 send(end()); // end expired session
202 }
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 mDialog.makeRequest(mLastRequest, INVITE);
215 if(mSessionInterval >= 90)
216 {
217 mLastRequest.header(h_SessionExpires).value() = mSessionInterval;
218
219 mLastRequest.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresherUAS ? "uas" : "uac");
220 }
221 send(mLastRequest);
222 }
223 }
224 }
225 }
226
227 void
228 InviteSession::handleSessionTimerResponse(const SipMessage& msg)
229 {
230 // If session timers are locally supported then handle response
231 if(mDum.getProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
232 {
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 // 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 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 if(mDum.getProfile()->getSupportedOptionTags().find(Token(Symbols::Timer)))
286 {
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 mSessionInterval = mDum.getProfile()->getDefaultSessionTime(); // Used only if UAC doesn't request a time
297 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 // 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 mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq);
336 }
337 }
338 }
339 }
340
341 void
342 InviteSession::dispatch(const SipMessage& msg)
343 {
344 Destroyer::Guard guard(mDestroyer);
345 std::pair<OfferAnswerType, const SdpContents*> offans;
346 offans = InviteSession::getOfferOrAnswer(msg);
347
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 if (msg.header(h_CSeq).method() == INFO)
354 {
355 if (msg.isRequest())
356 {
357 SipMessage response;
358 mDialog.makeResponse(response, msg, 200);
359 send(response);
360 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
386 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK &&
387 (mState == Connected || mState == ReInviting))
388 {
389 //quench 200 retransmissions
390 mFinalResponseMap.erase(msg.header(h_CSeq).sequence());
391 if (msg.header(h_CSeq).sequence() == mLastIncomingRequest.header(h_CSeq).sequence())
392 {
393 // BYE could be queued if end() is called when we are still waiting for far end ACK to be received
394 if (mQueuedBye)
395 {
396 mState = Terminated;
397 mLastRequest = *mQueuedBye;
398 delete mQueuedBye;
399 mQueuedBye = 0;
400 send(mLastRequest);
401 return;
402 }
403
404 if (offans.first != None)
405 {
406 if (mOfferState == Answered)
407 {
408 //SDP in invite and in ACK.
409 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 //temporary hack
423 else if (mState != ReInviting && mOfferState != Answered)
424 {
425 //no SDP in ACK when one is required
426 mDum.mInviteSessionHandler->onIllegalNegotiation(getSessionHandle(), msg);
427 }
428 }
429 }
430
431 switch(mState)
432 {
433 case Terminated:
434 //!dcm! -- 481 behaviour here, should pretty much die on anything
435 //eventually 200 to BYE could be handled further out
436 if (msg.isResponse())
437 {
438 int code = msg.header(h_StatusLine).statusCode();
439 if ((code == 200 && msg.header(h_CSeq).method() == BYE) || code > 399)
440 {
441 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
442 guard.destroy();
443 return;
444 }
445 }
446 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 failure.header(h_AcceptLanguages) = mDum.mProfile->getSupportedLanguages();
452 mDum.sendResponse(failure);
453 }
454 break;
455 case Connected:
456 if (msg.isRequest())
457 {
458 switch(msg.header(h_RequestLine).method())
459 {
460 // reINVITE
461 case INVITE:
462 {
463 if (mOfferState == Answered)
464 {
465 mState = ReInviting;
466 mDialog.update(msg);
467 mLastIncomingRequest = msg;
468 mDum.mInviteSessionHandler->onDialogModified(getSessionHandle(), offans.first, msg);
469 if (offans.first != None)
470 {
471 incomingSdp(msg, offans.second);
472 }
473 else
474 {
475 mDum.mInviteSessionHandler->onOfferRequired(getSessionHandle(), msg);
476 }
477 }
478 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 case BYE:
489 mState = Terminated;
490 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
491 mDialog.makeResponse(mLastResponse, msg, 200);
492 send(mLastResponse);
493 break;
494
495 case UPDATE:
496 assert(0);
497 break;
498
499 case INFO:
500 mDum.mInviteSessionHandler->onInfo(getSessionHandle(), msg);
501 break;
502 case REFER:
503 //handled in Dialog
504 assert(0);
505 break;
506
507 case CANCEL:
508 // 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
515 default:
516 InfoLog (<< "Ignoring request in an INVITE dialog: " << msg.brief());
517 break;
518 }
519 }
520 else
521 {
522 if ( msg.header(h_StatusLine).statusCode() == 200 && msg.header(h_CSeq).method() == INVITE)
523 {
524 CSeqToMessageMap::iterator it = mAckMap.find(msg.header(h_CSeq).sequence());
525 if (it != mAckMap.end())
526 {
527 mDum.send(it->second);
528 }
529 }
530 }
531 break;
532 case ReInviting:
533 if (msg.header(h_CSeq).method() == INVITE)
534 {
535 if (msg.isResponse())
536 {
537 int code = msg.header(h_StatusLine).statusCode();
538 if (code < 200)
539 {
540 return;
541 }
542 else if (code < 300)
543 {
544 if (msg.header(h_CSeq).sequence() == mLastRequest.header(h_CSeq).sequence())
545 {
546 mState = Connected;
547 //user has called end, so no more callbacks relating to
548 //this usage other than onTerminated
549 if (mQueuedBye)
550 {
551 send(makeAck()); // ACK the 200 first then send BYE
552 mState = Terminated;
553 mLastRequest = *mQueuedBye;
554 delete mQueuedBye;
555 mQueuedBye = 0;
556 send(mLastRequest);
557 return;
558 }
559
560 // Handle any Session Timer headers in response
561 handleSessionTimerResponse(msg);
562
563 if (offans.first != None)
564 {
565 if (offans.first == Answer)
566 {
567 //no late media required, so just send the ACK
568 send(makeAck());
569 }
570 incomingSdp(msg, offans.second);
571 }
572 else
573 {
574 //no offer or answer in 200, this will eventually be
575 //legal with PRACK/UPDATE
576 send(makeAck());
577 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 mDum.send(it->second);
591 }
592 }
593 }
594 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 else
601 {
602 // !slg! handle 491 response and retry???
603
604 mState = Connected;
605 //user has called end, so no more callbacks relating to
606 //this usage other than onTerminated
607 if (mQueuedBye)
608 {
609 send(makeAck()); // ACK the 200 first then send BYE
610 mState = Terminated;
611 mLastRequest = *mQueuedBye;
612 delete mQueuedBye;
613 mQueuedBye = 0;
614 send(mLastRequest);
615 return;
616 }
617 //reset the sdp state machine
618 incomingSdp(msg, 0);
619 mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);
620 }
621 }
622 else
623 {
624 SipMessage failure;
625 mDialog.makeResponse(failure, msg, 491);
626 InfoLog (<< "Sending 491 - overlapping Invite transactions");
627 mDum.sendResponse(failure);
628 return;
629 }
630 }
631 else if(msg.header(h_CSeq).method() == BYE && msg.isRequest())
632 {
633 // Inbound BYE crosses with outbound REINVITE
634
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 }
646 else
647 {
648 ErrLog ( << "Spurious message sent to UAS " << msg );
649 return;
650 }
651 break;
652 default:
653 DebugLog ( << "Throwing away strange message: " << msg );
654 //throw message away
655 // assert(0); //all other cases should be handled in base classes
656
657 }
658 }
659
660 SipMessage&
661 InviteSession::makeInfo(auto_ptr<Contents> contents)
662 {
663 if (mNitState == NitProceeding)
664 {
665 throw UsageUseException("Cannot start a non-invite transaction until the previous one has completed",
666 __FILE__, __LINE__);
667 }
668 mNitState = NitProceeding;
669 mDialog.makeRequest(mLastNit, INFO);
670 mLastNit.releaseContents();
671 mLastNit.setContents(contents);
672 return mLastNit;
673 }
674
675 SipMessage&
676 InviteSession::makeRefer(const NameAddr& referTo)
677 {
678 mDialog.makeRequest(mLastRequest, REFER);
679 mLastRequest.header(h_ReferTo) = referTo;
680 // mLastRequest.header(h_ReferTo).param(p_method) = getMethodName(INVITE);
681 return mLastRequest;
682 }
683
684 SipMessage&
685 InviteSession::makeRefer(const NameAddr& referTo, InviteSessionHandle sessionToReplace)
686 {
687 if (!sessionToReplace.isValid())
688 {
689 throw UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__);
690 }
691
692 mDialog.makeRequest(mLastRequest, REFER);
693 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 SipMessage&
705 InviteSession::end()
706 {
707 InfoLog ( << "InviteSession::end, state: " << mState);
708 switch (mState)
709 {
710 case Terminated:
711 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
712 break;
713 case Connected:
714 // Check state of 200 retrans map to see if we have recieved an ACK or not yet
715 if (mFinalResponseMap.find(mLastIncomingRequest.header(h_CSeq).sequence()) != mFinalResponseMap.end())
716 {
717 if(!mQueuedBye)
718 {
719 // No ACK yet - send BYE after ACK is received
720 mQueuedBye = new SipMessage(mLastRequest);
721 mDialog.makeRequest(*mQueuedBye, BYE);
722 return *mQueuedBye;
723 }
724 else
725 {
726 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
727 }
728 }
729 else
730 {
731 InfoLog ( << "InviteSession::end, Connected" );
732 mDialog.makeRequest(mLastRequest, BYE);
733 //new transaction
734 assert(mLastRequest.header(h_Vias).size() == 1);
735 // mLastRequest.header(h_Vias).front().param(p_branch).reset();
736 mState = Terminated;
737 return mLastRequest;
738 }
739 break;
740 case ReInviting:
741 if(!mQueuedBye)
742 {
743 mQueuedBye = new SipMessage(mLastRequest);
744 mDialog.makeRequest(*mQueuedBye, BYE);
745 return *mQueuedBye;
746 }
747 else
748 {
749 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
750 }
751 break;
752 default:
753 assert(0); // out of states
754 }
755 throw UsageUseException("Programmer error", __FILE__, __LINE__); //make VC++ happy
756 }
757
758 // If sdp==0, it means the last offer failed
759 // !dcm! -- eventually handle confused UA's that send offers/answers at
760 // inappropriate times, probably with a different callback
761 void
762 InviteSession::incomingSdp(const SipMessage& msg, const SdpContents* sdp)
763 {
764 switch (mOfferState)
765 {
766 case Nothing:
767 assert(mCurrentLocalSdp == 0);
768 assert(mCurrentRemoteSdp == 0);
769 assert(mProposedLocalSdp == 0);
770 assert(mProposedRemoteSdp == 0);
771 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
772 mOfferState = Offerred;
773 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
774 break;
775
776 case Offerred:
777 assert(mCurrentLocalSdp == 0);
778 assert(mCurrentRemoteSdp == 0);
779 mCurrentLocalSdp = mProposedLocalSdp;
780 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
781 delete mProposedRemoteSdp;
782 mProposedLocalSdp = 0;
783 mProposedRemoteSdp = 0;
784 mOfferState = Answered;
785 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
786 break;
787
788 case Answered:
789 assert(mProposedLocalSdp == 0);
790 assert(mProposedRemoteSdp == 0);
791 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
792 mOfferState = CounterOfferred;
793 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
794 break;
795
796 case CounterOfferred:
797 assert(mCurrentLocalSdp);
798 assert(mCurrentRemoteSdp);
799 mOfferState = Answered;
800 if (sdp) // !slg! There currenlty doesn't seem to be anyone calling this with sdp == 0
801 {
802 delete mCurrentLocalSdp;
803 delete mCurrentRemoteSdp;
804 mCurrentLocalSdp = mProposedLocalSdp;
805 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
806 delete mProposedRemoteSdp;
807 mProposedLocalSdp = 0;
808 mProposedRemoteSdp = 0;
809 mOfferState = Answered;
810 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
811 }
812 else
813 {
814 delete mProposedLocalSdp;
815 delete mProposedRemoteSdp;
816 mProposedLocalSdp = 0;
817 mProposedRemoteSdp = 0;
818 // !jf! is this right?
819 // mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);
820 }
821 break;
822 }
823 }
824
825 void
826 InviteSession::send(SipMessage& msg)
827 {
828 Destroyer::Guard guard(mDestroyer);
829 //handle NITs separately
830 if (msg.header(h_CSeq).method() == INFO)
831 {
832 mDum.send(msg);
833 return;
834 }
835
836 msg.releaseContents();
837 if (mQueuedBye && (mQueuedBye == &msg))
838 {
839 //queued
840 return;
841 }
842
843 if (msg.isRequest())
844 {
845 switch(msg.header(h_RequestLine).getMethod())
846 {
847 case INVITE:
848 case UPDATE:
849 case ACK:
850 if (mNextOfferOrAnswerSdp)
851 {
852 msg.setContents(mNextOfferOrAnswerSdp);
853 sendSdp(mNextOfferOrAnswerSdp);
854 mNextOfferOrAnswerSdp = 0;
855 }
856 break;
857 default:
858 break;
859 }
860 mDum.send(msg);
861 }
862 else
863 {
864 int code = msg.header(h_StatusLine).statusCode();
865 //!dcm! -- probably kill this object earlier, handle 200 to bye in
866 //DialogUsageManager...very soon
867 if (msg.header(h_CSeq).method() == BYE && code == 200) //!dcm! -- not 2xx?
868
869 {
870 mState = Terminated;
871 mDum.send(msg);
872 //mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg); // This is actually called when recieving the BYE message so that the BYE message can be passed to onTerminated
873 guard.destroy();
874 }
875 else if (code >= 200 && code < 300 && msg.header(h_CSeq).method() == INVITE)
876 {
877 int seq = msg.header(h_CSeq).sequence();
878 mCurrentRetransmit200 = Timer::T1;
879 mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), seq);
880 mDum.addTimerMs(DumTimeout::WaitForAck, Timer::TH, getBaseHandle(), seq);
881
882 //!dcm! -- this should be mFinalResponse...maybe assign here in
883 //case the user wants to be very strange
884 if (mNextOfferOrAnswerSdp)
885 {
886 msg.setContents(mNextOfferOrAnswerSdp);
887 sendSdp(mNextOfferOrAnswerSdp);
888 mNextOfferOrAnswerSdp = 0;
889 }
890 mDum.send(msg);
891 }
892 else
893 {
894 mDum.send(msg);
895 }
896 }
897 }
898
899 void
900 InviteSession::sendSdp(SdpContents* sdp)
901 {
902 switch (mOfferState)
903 {
904 case Nothing:
905 assert(mCurrentLocalSdp == 0);
906 assert(mCurrentRemoteSdp == 0);
907 mProposedLocalSdp = sdp;
908 mOfferState = Offerred;
909 break;
910
911 case Offerred:
912 assert(mCurrentLocalSdp == 0);
913 assert(mCurrentRemoteSdp == 0);
914 mCurrentLocalSdp = sdp;
915 mCurrentRemoteSdp = mProposedRemoteSdp;
916 delete mProposedLocalSdp;
917 mProposedLocalSdp = 0;
918 mProposedRemoteSdp = 0;
919 mOfferState = Answered;
920 break;
921
922 case Answered:
923 assert(mProposedLocalSdp == 0);
924 assert(mProposedRemoteSdp == 0);
925 mProposedLocalSdp = sdp;
926 mOfferState = CounterOfferred;
927 break;
928
929 case CounterOfferred:
930 assert(mCurrentLocalSdp);
931 assert(mCurrentRemoteSdp);
932 if (sdp)
933 {
934 delete mCurrentLocalSdp;
935 delete mCurrentRemoteSdp;
936 mCurrentLocalSdp = sdp;
937 mCurrentRemoteSdp = mProposedRemoteSdp;
938 delete mProposedLocalSdp;
939 mProposedLocalSdp = 0;
940 mProposedRemoteSdp = 0;
941 }
942 else
943 {
944 delete mProposedLocalSdp;
945 delete mProposedRemoteSdp;
946 mProposedLocalSdp = 0;
947 mProposedRemoteSdp = 0;
948 }
949 mOfferState = Answered;
950 break;
951 }
952 }
953
954 std::pair<InviteSession::OfferAnswerType, const SdpContents*>
955 InviteSession::getOfferOrAnswer(const SipMessage& msg) const
956 {
957 std::pair<InviteSession::OfferAnswerType, const SdpContents*> ret;
958 ret.first = None;
959 const SdpContents* contents = NULL;
960
961 MultipartMixedContents* mixed = dynamic_cast<MultipartMixedContents*>(msg.getContents());
962 if ( mixed )
963 {
964 // Look for first SDP Contents in a multipart contents
965 MultipartMixedContents::Parts& parts = mixed->parts();
966 for( MultipartMixedContents::Parts::const_iterator i = parts.begin();
967 i != parts.end();
968 ++i)
969 {
970 contents = dynamic_cast<const SdpContents*>(*i);
971 if(contents != NULL) break; // Found SDP contents
972 }
973 }
974 else
975 {
976 contents = dynamic_cast<const SdpContents*>(msg.getContents());
977 }
978
979 if (contents)
980 {
981 static Token c100rel(Symbols::C100rel);
982 if (msg.isRequest() || msg.header(h_StatusLine).responseCode() == 200 ||
983 (msg.exists(h_Requires) && msg.header(h_Requires).find(c100rel)))
984 {
985 switch (mOfferState)
986 {
987 case Nothing:
988 ret.first = Offer;
989 ret.second = contents;
990 break;
991
992 case Offerred:
993 ret.first = Answer;
994 ret.second = contents;
995 break;
996
997 case Answered:
998 ret.first = Offer;
999 ret.second = contents;
1000 break;
1001
1002 case CounterOfferred:
1003 ret.first = Answer;
1004 ret.second = contents;
1005 break;
1006 }
1007 }
1008 else if (msg.isResponse() &&
1009 msg.header(h_StatusLine).responseCode() < 200 &&
1010 msg.header(h_StatusLine).responseCode() >= 180)
1011 {
1012 ret.second = contents;
1013 }
1014 }
1015 return ret;
1016 }
1017
1018 SipMessage&
1019 InviteSession::rejectDialogModification(int statusCode)
1020 {
1021 if (statusCode < 400)
1022 {
1023 throw UsageUseException("Must reject with a 4xx", __FILE__, __LINE__);
1024 }
1025 mDialog.makeResponse(mLastResponse, mLastIncomingRequest, statusCode);
1026 mState = Connected;
1027 sendSdp(0);
1028 return mLastResponse;
1029 }
1030
1031 SipMessage&
1032 InviteSession::targetRefresh(const NameAddr& localUri)
1033 {
1034 assert(0);
1035 return mLastRequest;
1036 }
1037
1038 void
1039 InviteSession::send()
1040 {
1041 InfoLog ( << "InviteSession::send(void)");
1042 if (mOfferState == Answered || mState != Connected)
1043 {
1044 throw UsageUseException("Cannot call send when there it no Offer/Answer negotiation to do", __FILE__, __LINE__);
1045 }
1046 send(makeAck());
1047 }
1048
1049 SipMessage&
1050 InviteSession::makeAck()
1051 {
1052 InfoLog ( << "InviteSession::makeAck" );
1053
1054 int cseq = mLastRequest.header(h_CSeq).sequence();
1055 if (mAckMap.find(cseq) != mAckMap.end())
1056 {
1057 InfoLog ( << "CSeq collision in ack map: " << Inserter(mAckMap) );
1058 }
1059
1060 assert(mAckMap.find(cseq) == mAckMap.end());
1061 SipMessage& ack = mAckMap[cseq];
1062 ack = mLastRequest;
1063 mDialog.makeRequest(ack, ACK);
1064 mDum.addTimerMs(DumTimeout::CanDiscardAck, Timer::TH, getBaseHandle(), cseq);
1065
1066 assert(ack.header(h_Vias).size() == 1);
1067
1068 // if (mNextOfferOrAnswerSdp)
1069 // {
1070 // ack.setContents(mNextOfferOrAnswerSdp);
1071 // sendSdp(mNextOfferOrAnswerSdp);
1072 // mNextOfferOrAnswerSdp = 0;
1073 // }
1074 return ack;
1075 }
1076
1077 /* ====================================================================
1078 * The Vovida Software License, Version 1.0
1079 *
1080 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1081 *
1082 * Redistribution and use in source and binary forms, with or without
1083 * modification, are permitted provided that the following conditions
1084 * are met:
1085 *
1086 * 1. Redistributions of source code must retain the above copyright
1087 * notice, this list of conditions and the following disclaimer.
1088 *
1089 * 2. Redistributions in binary form must reproduce the above copyright
1090 * notice, this list of conditions and the following disclaimer in
1091 * the documentation and/or other materials provided with the
1092
1093 * distribution.
1094 *
1095 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1096 * and "Vovida Open Communication Application Library (VOCAL)" must
1097 * not be used to endorse or promote products derived from this
1098 * software without prior written permission. For written
1099 * permission, please contact vocal@vovida.org.
1100 *
1101 * 4. Products derived from this software may not be called "VOCAL", nor
1102 * may "VOCAL" appear in their name, without prior written
1103 * permission of Vovida Networks, Inc.
1104 *
1105 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1106 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1107 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1108 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1109 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1110 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1111 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1112 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1113 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1114 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1115 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1116 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1117 * DAMAGE.
1118 *
1119 * ====================================================================
1120 *
1121 * This software consists of voluntary contributions made by Vovida
1122 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1123 * Inc. For more information on Vovida Networks, Inc., please see
1124 * <http://www.vovida.org/>.
1125 *
1126 */

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27