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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27