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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27