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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27