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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3112 - (show annotations) (download)
Thu Jul 15 21:36:48 2004 UTC (15 years, 4 months ago) by derek
Original Path: main/sip/resiprocate/dum/InviteSession.cxx
File size: 20758 byte(s)
DialogSet cancel change(deletion while iterating problem)
Dialog routeset now updated by 200
DialogUsageManager send copies message if ProcessStrictRoute will modify the message
1 #include "resiprocate/SipMessage.hxx"
2 #include "resiprocate/SdpContents.hxx"
3 #include "resiprocate/dum/Dialog.hxx"
4 #include "resiprocate/dum/DialogUsageManager.hxx"
5 #include "resiprocate/dum/InviteSession.hxx"
6 #include "resiprocate/dum/InviteSessionHandler.hxx"
7 #include "resiprocate/dum/UsageUseException.hxx"
8 #include "resiprocate/os/Logger.hxx"
9
10 #if defined(WIN32) && defined(_DEBUG) &&defined(LEAK_CHECK)// Used for tracking down memory leaks in Visual Studio
11 #define _CRTDBG_MAP_ALLOC
12 #include <stdlib.h>
13 #include <crtdbg.h>
14 #define new new( _NORMAL_BLOCK, __FILE__, __LINE__)
15 #endif // defined(WIN32) && defined(_DEBUG)
16
17
18 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
19
20 using namespace resip;
21
22 unsigned long
23 InviteSession::T1 = 500;
24
25 unsigned long
26 InviteSession::T2 = 8 * T1;
27
28 unsigned long
29 InviteSession::TimerH = 64 * T1;
30
31 InviteSession::InviteSession(DialogUsageManager& dum, Dialog& dialog, State initialState)
32 : DialogUsage(dum, dialog),
33 mState(initialState),
34 mOfferState(Nothing),
35 mCurrentLocalSdp(0),
36 mCurrentRemoteSdp(0),
37 mProposedLocalSdp(0),
38 mProposedRemoteSdp(0),
39 mNextOfferOrAnswerSdp(0),
40 mCurrentRetransmit200(0)
41
42 {
43 InfoLog ( << "^^^ InviteSession::InviteSession " << this);
44 assert(mDum.mInviteSessionHandler);
45 }
46
47 InviteSession::~InviteSession()
48 {
49 InfoLog ( << "^^^ InviteSession::~InviteSession " << this);
50 delete mCurrentLocalSdp;
51 delete mCurrentRemoteSdp;
52 delete mProposedLocalSdp;
53 delete mProposedRemoteSdp;
54 delete mNextOfferOrAnswerSdp;
55 mDialog.mInviteSession = 0;
56 }
57
58 SipMessage&
59 InviteSession::modifySession()
60 {
61 if (mNextOfferOrAnswerSdp == 0 || mState != Connected)
62 {
63 throw new UsageUseException("Must be in the connected state and have propsed an offer to call modifySession",
64 __FILE__, __LINE__);
65 }
66 mState = ReInviting;
67 mDialog.makeRequest(mLastRequest, INVITE);
68 return mLastRequest;
69 }
70
71
72 SipMessage&
73 InviteSession::acceptOffer(int statusCode)
74 {
75 if (mNextOfferOrAnswerSdp == 0 || mState != ReInviting)
76 {
77 throw new UsageUseException("Must be in the ReInviting state and have propsed an answer to call answerModifySession",
78 __FILE__, __LINE__);
79 }
80 mState = AcceptingReInvite;
81 mDialog.makeResponse(mFinalResponse, mLastRequest, statusCode);
82 return mFinalResponse;
83 }
84
85 void
86 InviteSession::setOffer(const SdpContents* sdp)
87 {
88 if (mProposedRemoteSdp)
89 {
90 throw UsageUseException("Cannot set an offer with an oustanding remote offer", __FILE__, __LINE__);
91 }
92 assert(mNextOfferOrAnswerSdp == 0);
93 mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());
94 }
95
96 void
97 InviteSession::setAnswer(const SdpContents* sdp)
98 {
99 if (mProposedLocalSdp )
100 {
101 throw UsageUseException("Cannot set an answer with an oustanding offer", __FILE__, __LINE__);
102 }
103 assert(mNextOfferOrAnswerSdp == 0);
104 mNextOfferOrAnswerSdp = static_cast<SdpContents*>(sdp->clone());
105 }
106
107 const SdpContents*
108 InviteSession::getLocalSdp()
109 {
110 return mCurrentLocalSdp;
111 }
112
113 const SdpContents*
114 InviteSession::getRemoteSdp()
115 {
116 return mCurrentRemoteSdp;
117 }
118
119 InviteSessionHandle
120 InviteSession::getSessionHandle()
121 {
122 return InviteSessionHandle(mDum, getBaseHandle().getId());
123 }
124
125
126 void
127 InviteSession::dispatch(const DumTimeout& timeout)
128 {
129 if (timeout.type() == DumTimeout::Retransmit200 && (mState == Accepting || mState == AcceptingReInvite ))
130 {
131 mDum.send(mFinalResponse);
132 mDum.addTimerMs(DumTimeout::Retransmit200, resipMin(T2, mCurrentRetransmit200*2), getBaseHandle(), 0);
133 }
134 else if (timeout.type() == DumTimeout::WaitForAck && mState != Connected)
135 {
136 mDialog.makeResponse(mLastResponse, mLastRequest, 408);
137 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), mLastResponse);
138 delete this;
139 }
140 }
141
142 void
143 InviteSession::dispatch(const SipMessage& msg)
144 {
145 std::pair<OfferAnswerType, const SdpContents*> offans;
146 offans = InviteSession::getOfferOrAnswer(msg);
147
148 switch(mState)
149 {
150 case Terminated:
151 //!dcm! -- 481 behaviour here, should pretty much die on anything
152 //eventually 200 to BYE could be handled further out
153 if (msg.isResponse())
154 {
155 int code = msg.header(h_StatusLine).statusCode();
156 if ((code == 200 && msg.header(h_CSeq).method() == BYE) || code > 399)
157 {
158 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
159 delete this;
160 return;
161 }
162 }
163 break;
164 case Connected:
165 if (msg.isRequest())
166 {
167 switch(msg.header(h_RequestLine).method())
168 {
169 // reINVITE
170 case INVITE:
171 mState = ReInviting;
172 mDialog.update(msg);
173 mLastRequest = msg; // !slg!
174 mDum.mInviteSessionHandler->onDialogModified(getSessionHandle(), msg);
175 if (offans.first != None)
176 {
177 incomingSdp(msg, offans.second);
178 }
179 break;
180
181 case BYE:
182 mState = Terminated;
183 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
184 mDialog.makeResponse(mLastResponse, msg, 200);
185 send(mLastResponse);
186 break;
187
188 case UPDATE:
189 assert(0);
190 break;
191
192 case INFO:
193 mDum.mInviteSessionHandler->onInfo(getSessionHandle(), msg);
194 break;
195
196 case REFER:
197 //handled in Dialog
198 assert(0);
199 break;
200
201 default:
202 InfoLog (<< "Ignoring request in an INVITE dialog: " << msg.brief());
203 break;
204 }
205 }
206 else
207 {
208 //!dcm! -- need to change this logic for when we don't have an ACK yet
209 if ( msg.header(h_StatusLine).statusCode() == 200)
210 {
211 //retransmist ack
212 mDum.send(mAck);
213 }
214 }
215 break;
216 case ReInviting:
217 if (msg.isResponse() && msg.header(h_CSeq).method() == INVITE
218 && msg.header(h_StatusLine).statusCode() == 200)
219 {
220 mState = Connected;
221 send(ackConnection());
222 if (offans.first != None)
223 {
224 incomingSdp(msg, offans.second);
225 }
226 }
227 else
228 {
229 ErrLog ( << "Spurious message sent to UAS " << msg );
230 return;
231 }
232 break;
233 case Accepting:
234 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
235 {
236 mState = Connected;
237 mDum.mInviteSessionHandler->onConnected(getSessionHandle(), msg);
238 if (offans.first != None)
239 {
240 InviteSession::incomingSdp(msg, offans.second);
241 }
242 }
243 else
244 {
245 ErrLog ( << "Spurious message sent to UAS " << msg );
246 return;
247 }
248 break;
249 case AcceptingReInvite:
250 if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK)
251 {
252 mState = Connected;
253 //this shouldn't happen, but it may be allowed(DUM API doesn't
254 //support this for re-invite)
255 if (offans.first != None)
256 {
257 InviteSession::incomingSdp(msg, offans.second);
258 }
259 }
260 else
261 {
262 ErrLog ( << "Spurious message sent to UAS " << msg );
263 return;
264 }
265 break;
266
267
268 default:
269 DebugLog ( << "Throwing away strange message: " << msg );
270 //throw message away
271 // assert(0); //all other cases should be handled in base classes
272
273 }
274 }
275
276 SipMessage&
277 InviteSession::makeRefer(const NameAddr& referTo)
278 {
279 mDialog.makeRequest(mLastRequest, REFER);
280 mLastRequest.header(h_ReferTo) = referTo;
281 return mLastRequest;
282 }
283
284 SipMessage&
285 InviteSession::makeRefer(const NameAddr& referTo, InviteSessionHandle sessionToReplace)
286 {
287 if (!sessionToReplace.isValid())
288 {
289 throw new UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__);
290 }
291
292 mDialog.makeRequest(mLastRequest, REFER);
293 mLastRequest.header(h_ReferTo) = referTo;
294 CallId replaces;
295 DialogId id = sessionToReplace->mDialog.getId();
296 replaces.value() = id.getCallId();
297 replaces.param(p_toTag) = id.getRemoteTag();
298 replaces.param(p_fromTag) = id.getLocalTag();
299
300 mLastRequest.header(h_ReferTo).uri().embedded().header(h_Replaces) = replaces;
301 return mLastRequest;
302 }
303
304 SipMessage&
305 InviteSession::end()
306 {
307 InfoLog ( << "InviteSession::end, state: " << mState);
308 switch (mState)
309 {
310 case Terminated:
311 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
312 break;
313 case Connected:
314 case Accepting:
315 InfoLog ( << "InviteSession::end, connected or Accepting" );
316 mDialog.makeRequest(mLastRequest, BYE);
317 //new transaction
318 assert(mLastRequest.header(h_Vias).size() == 1);
319 // mLastRequest.header(h_Vias).front().param(p_branch).reset();
320 mState = Terminated;
321 return mLastRequest;
322 break;
323 default:
324 assert(0); // out of states
325 }
326 throw UsageUseException("Programmer error", __FILE__, __LINE__); //make VC++ happy
327 }
328
329 // If sdp==0, it means the last offer failed
330 // !dcm! -- eventually handle confused UA's that send offers/answers at
331 // inappropriate times, probably with a different callback
332 void
333 InviteSession::incomingSdp(const SipMessage& msg, const SdpContents* sdp)
334 {
335 switch (mOfferState)
336 {
337 case Nothing:
338 assert(mCurrentLocalSdp == 0);
339 assert(mCurrentRemoteSdp == 0);
340 assert(mProposedLocalSdp == 0);
341 assert(mProposedRemoteSdp == 0);
342 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
343 mOfferState = Offerred;
344 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
345 break;
346
347 case Offerred:
348 assert(mCurrentLocalSdp == 0);
349 assert(mCurrentRemoteSdp == 0);
350 mCurrentLocalSdp = mProposedLocalSdp;
351 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
352 delete mProposedRemoteSdp;
353 mProposedLocalSdp = 0;
354 mProposedRemoteSdp = 0;
355 mOfferState = Answered;
356 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
357 break;
358
359 case Answered:
360 assert(mProposedLocalSdp == 0);
361 assert(mProposedRemoteSdp == 0);
362 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
363 mOfferState = CounterOfferred;
364 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
365 break;
366
367 case CounterOfferred:
368 assert(mCurrentLocalSdp);
369 assert(mCurrentRemoteSdp);
370 mOfferState = Answered;
371 if (sdp) // !slg! There currenlty doesn't seem to be anyone calling this with sdp == 0
372 {
373 delete mCurrentLocalSdp;
374 delete mCurrentRemoteSdp;
375 mCurrentLocalSdp = mProposedLocalSdp;
376 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
377 delete mProposedRemoteSdp;
378 mProposedLocalSdp = 0;
379 mProposedRemoteSdp = 0;
380 mOfferState = Answered;
381 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
382 }
383 else
384 {
385 delete mProposedLocalSdp;
386 delete mProposedRemoteSdp;
387 mProposedLocalSdp = 0;
388 mProposedRemoteSdp = 0;
389 // !jf! is this right?
390 mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);
391 }
392 break;
393 }
394 }
395
396 void
397 InviteSession::send(SipMessage& msg)
398 {
399 if (msg.isRequest())
400 {
401 //unless the message is an ACK(in which case it is mAck)
402 //strip out the SDP after sending
403 switch(msg.header(h_RequestLine).getMethod())
404 {
405 case INVITE:
406 case UPDATE:
407 if (mNextOfferOrAnswerSdp)
408 {
409 msg.setContents(mNextOfferOrAnswerSdp);
410 sendSdp(mNextOfferOrAnswerSdp);
411 mNextOfferOrAnswerSdp = 0;
412 }
413 break;
414 default:
415 break;
416 }
417
418 if (msg.header(h_RequestLine).getMethod() == ACK)
419 {
420 mDum.send(msg);
421 }
422 else
423 {
424 mDum.send(msg);
425 msg.releaseContents();
426 }
427 }
428 else
429 {
430 int code = msg.header(h_StatusLine).statusCode();
431 //!dcm! -- probably kill this object earlier, handle 200 to bye in
432 //DialogUsageManager...very soon
433 if (msg.header(h_CSeq).method() == BYE && code == 200) //!dcm! -- not 2xx?
434
435 {
436 mState = Terminated;
437 mDum.send(msg);
438 delete this;
439 }
440 else if (code >= 200 && code < 300 && msg.header(h_CSeq).method() == INVITE)
441 {
442 assert(&msg == &mFinalResponse);
443 mCurrentRetransmit200 = T1;
444 mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), 0);
445 mDum.addTimerMs(DumTimeout::WaitForAck, TimerH, getBaseHandle(), 0);
446
447 //!dcm! -- this should be mFinalResponse...maybe assign here in
448 //case the user wants to be very strange
449 if (mNextOfferOrAnswerSdp)
450 {
451 msg.setContents(mNextOfferOrAnswerSdp);
452 sendSdp(mNextOfferOrAnswerSdp);
453 mNextOfferOrAnswerSdp = 0;
454 }
455 mDum.send(msg);
456 }
457 else
458 {
459 mDum.send(msg);
460 msg.releaseContents();
461 }
462 }
463 }
464
465 void
466 InviteSession::sendSdp(SdpContents* sdp)
467 {
468 switch (mOfferState)
469 {
470 case Nothing:
471 assert(mCurrentLocalSdp == 0);
472 assert(mCurrentRemoteSdp == 0);
473 mProposedLocalSdp = sdp;
474 mOfferState = Offerred;
475 break;
476
477 case Offerred:
478 assert(mCurrentLocalSdp == 0);
479 assert(mCurrentRemoteSdp == 0);
480 mCurrentLocalSdp = sdp;
481 mCurrentRemoteSdp = mProposedRemoteSdp;
482 delete mProposedLocalSdp;
483 mProposedLocalSdp = 0;
484 mProposedRemoteSdp = 0;
485 mOfferState = Answered;
486 break;
487
488 case Answered:
489 assert(mProposedLocalSdp == 0);
490 assert(mProposedRemoteSdp == 0);
491 mProposedLocalSdp = sdp;
492 mOfferState = CounterOfferred;
493 break;
494
495 case CounterOfferred:
496 assert(mCurrentLocalSdp);
497 assert(mCurrentRemoteSdp);
498 if (sdp) // !slg! There currenlty doesn't seem to be anyone calling this with sdp == 0
499 {
500 delete mCurrentLocalSdp;
501 delete mCurrentRemoteSdp;
502 mCurrentLocalSdp = sdp;
503 mCurrentRemoteSdp = mProposedRemoteSdp;
504 delete mProposedLocalSdp;
505 mProposedLocalSdp = 0;
506 mProposedRemoteSdp = 0;
507 }
508 else
509 {
510 delete mProposedLocalSdp;
511 delete mProposedRemoteSdp;
512 mProposedLocalSdp = 0;
513 mProposedRemoteSdp = 0;
514 }
515 mOfferState = Answered;
516 break;
517 }
518 }
519
520 std::pair<InviteSession::OfferAnswerType, const SdpContents*>
521 InviteSession::getOfferOrAnswer(const SipMessage& msg) const
522 {
523 std::pair<InviteSession::OfferAnswerType, const SdpContents*> ret;
524 ret.first = None;
525
526 const SdpContents* contents = dynamic_cast<const SdpContents*>(msg.getContents());
527 if (contents)
528 {
529 static Token c100rel(Symbols::C100rel);
530 if (msg.isRequest() || msg.header(h_StatusLine).responseCode() == 200 ||
531 msg.exists(h_Supporteds) && msg.header(h_Supporteds).find(c100rel))
532 {
533 switch (mOfferState)
534 {
535 case None:
536 ret.first = Offer;
537 ret.second = contents;
538 break;
539
540 case Offerred:
541 ret.first = Answer;
542 ret.second = contents;
543 break;
544
545 case Answered:
546 ret.first = Offer;
547 ret.second = contents;
548 break;
549
550 case CounterOfferred:
551 ret.first = Answer;
552 ret.second = contents;
553 break;
554 }
555 }
556 }
557 return ret;
558 }
559
560 void
561 InviteSession::copyAuthorizations(SipMessage& request)
562 {
563 #if 0
564 if (mLastRequest.exists(h_ProxyAuthorizations))
565 {
566 // should make the next auth (change nextNonce)
567 request.header(h_ProxyAuthorizations) = mLastRequest.header(h_ProxyAuthorizations);
568 }
569 if (mLastRequest.exists(h_ProxyAuthorizations))
570 {
571 // should make the next auth (change nextNonce)
572 request.header(h_ProxyAuthorizations) = mLastRequest.header(h_ProxyAuthorizations);
573 }
574 #endif
575 }
576
577 SipMessage&
578 InviteSession::rejectOffer(int statusCode)
579 {
580 if (statusCode < 400)
581 {
582 throw new UsageUseException("Must reject with a 4xx", __FILE__, __LINE__);
583 }
584 //sdp state change here--go to initial state?
585 mDialog.makeResponse(mLastResponse, mLastRequest, statusCode);
586 return mLastResponse;
587 }
588
589 SipMessage&
590 InviteSession::targetRefresh(const NameAddr& localUri)
591 {
592 assert(0);
593 return mLastRequest;
594 }
595
596 SipMessage&
597 InviteSession::ackConnection()
598 {
599 //if not a reinvite, and a pending offer exists, throw
600 makeAck();
601 //new transaction
602 assert(mAck.header(h_Vias).size() == 1);
603 // mAck.header(h_Vias).front().param(p_branch).reset();
604 return mAck;
605 }
606
607 void
608 InviteSession::makeAck()
609 {
610 mAck = mLastRequest;
611
612 InfoLog ( << "InviteSession::makeAck:before: " << mAck );
613
614 mDialog.makeRequest(mAck, ACK);
615 if (mNextOfferOrAnswerSdp)
616 {
617 mAck.setContents(mNextOfferOrAnswerSdp);
618 sendSdp(mNextOfferOrAnswerSdp);
619 mNextOfferOrAnswerSdp = 0;
620 }
621
622 InfoLog ( << "InviteSession::makeAck:after: " << mAck );
623 }
624
625 /* ====================================================================
626 * The Vovida Software License, Version 1.0
627 *
628 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
629 *
630 * Redistribution and use in source and binary forms, with or without
631 * modification, are permitted provided that the following conditions
632 * are met:
633 *
634 * 1. Redistributions of source code must retain the above copyright
635 * notice, this list of conditions and the following disclaimer.
636 *
637 * 2. Redistributions in binary form must reproduce the above copyright
638 * notice, this list of conditions and the following disclaimer in
639 * the documentation and/or other materials provided with the
640
641 * distribution.
642 *
643 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
644 * and "Vovida Open Communication Application Library (VOCAL)" must
645 * not be used to endorse or promote products derived from this
646 * software without prior written permission. For written
647 * permission, please contact vocal@vovida.org.
648 *
649 * 4. Products derived from this software may not be called "VOCAL", nor
650 * may "VOCAL" appear in their name, without prior written
651 * permission of Vovida Networks, Inc.
652 *
653 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
654 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
655 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
656 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
657 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
658 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
659 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
660 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
661 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
662 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
663 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
664 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
665 * DAMAGE.
666 *
667 * ====================================================================
668 *
669 * This software consists of voluntary contributions made by Vovida
670 * Networks, Inc. and many individuals on behalf of Vovida Networks,
671 * Inc. For more information on Vovida Networks, Inc., please see
672 * <http://www.vovida.org/>.
673 *
674 */

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27