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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3431 - (show annotations) (download)
Tue Nov 2 12:17:27 2004 UTC (15 years, 1 month ago) by kenho
Original Path: main/sip/resiprocate/dum/InviteSession.cxx
File size: 39356 byte(s)
Complemented missing RESIP_ prefix for resip::REGISTER, resip::INVITE, resip::UNKNOWN, resip::ACK and etc. for BCB compiliant support.
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, RESIP_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 // RESIP_BYE could be queued if end() is called when we are still waiting for far end RESIP_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 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, RESIP_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 - RESIP_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 - RESIP_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() == RESIP_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() == RESIP_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 // RESIP_BYE could be queued if end() is called when we are still waiting for far end RESIP_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 RESIP_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 RESIP_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 RESIP_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() == RESIP_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 RESIP_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 RESIP_BYE:
489 mState = Terminated;
490 mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg);
491 mDialog.makeResponse(mLastResponse, msg, 200);
492 send(mLastResponse);
493 break;
494
495 case RESIP_UPDATE:
496 assert(0);
497 break;
498
499 case RESIP_INFO:
500 mDum.mInviteSessionHandler->onInfo(getSessionHandle(), msg);
501 break;
502 case RESIP_REFER:
503 //handled in Dialog
504 assert(0);
505 break;
506
507 case RESIP_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 RESIP_INVITE dialog: " << msg.brief());
517 break;
518 }
519 }
520 else
521 {
522 if ( msg.header(h_StatusLine).statusCode() == 200 && msg.header(h_CSeq).method() == RESIP_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() == RESIP_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()); // RESIP_ACK the 200 first then send RESIP_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 RESIP_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()); // RESIP_ACK the 200 first then send RESIP_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() == RESIP_BYE && msg.isRequest())
632 {
633 // Inbound RESIP_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, RESIP_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, RESIP_REFER);
679 mLastRequest.header(h_ReferTo) = referTo;
680 // mLastRequest.header(h_ReferTo).param(p_method) = getMethodName(RESIP_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, RESIP_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 void
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 RESIP_ACK or not yet
715 if (mFinalResponseMap.find(mLastIncomingRequest.header(h_CSeq).sequence()) != mFinalResponseMap.end())
716 {
717 if(!mQueuedBye)
718 {
719 // No RESIP_ACK yet - send RESIP_BYE after RESIP_ACK is received
720 mQueuedBye = new SipMessage(mLastRequest);
721 mDialog.makeRequest(*mQueuedBye, RESIP_BYE);
722 }
723 else
724 {
725 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
726 }
727 }
728 else
729 {
730 InfoLog ( << "InviteSession::end, Connected" );
731 mDialog.makeRequest(mLastRequest, RESIP_BYE);
732 //new transaction
733 assert(mLastRequest.header(h_Vias).size() == 1);
734 mState = Terminated;
735 send(mLastRequest);
736 }
737 break;
738 case ReInviting:
739 if(!mQueuedBye)
740 {
741 mQueuedBye = new SipMessage(mLastRequest);
742 mDialog.makeRequest(*mQueuedBye, RESIP_BYE);
743 }
744 else
745 {
746 throw UsageUseException("Cannot end a session that has already been cancelled.", __FILE__, __LINE__);
747 }
748 break;
749 default:
750 assert(0); // out of states
751 }
752 }
753
754 // If sdp==0, it means the last offer failed
755 // !dcm! -- eventually handle confused UA's that send offers/answers at
756 // inappropriate times, probably with a different callback
757 void
758 InviteSession::incomingSdp(const SipMessage& msg, const SdpContents* sdp)
759 {
760 switch (mOfferState)
761 {
762 case Nothing:
763 assert(mCurrentLocalSdp == 0);
764 assert(mCurrentRemoteSdp == 0);
765 assert(mProposedLocalSdp == 0);
766 assert(mProposedRemoteSdp == 0);
767 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
768 mOfferState = Offerred;
769 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
770 break;
771
772 case Offerred:
773 assert(mCurrentLocalSdp == 0);
774 assert(mCurrentRemoteSdp == 0);
775 mCurrentLocalSdp = mProposedLocalSdp;
776 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
777 delete mProposedRemoteSdp;
778 mProposedLocalSdp = 0;
779 mProposedRemoteSdp = 0;
780 mOfferState = Answered;
781 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
782 break;
783
784 case Answered:
785 assert(mProposedLocalSdp == 0);
786 assert(mProposedRemoteSdp == 0);
787 mProposedRemoteSdp = static_cast<SdpContents*>(sdp->clone());
788 mOfferState = CounterOfferred;
789 mDum.mInviteSessionHandler->onOffer(getSessionHandle(), msg, sdp);
790 break;
791
792 case CounterOfferred:
793 assert(mCurrentLocalSdp);
794 assert(mCurrentRemoteSdp);
795 mOfferState = Answered;
796 if (sdp) // !slg! There currenlty doesn't seem to be anyone calling this with sdp == 0
797 {
798 delete mCurrentLocalSdp;
799 delete mCurrentRemoteSdp;
800 mCurrentLocalSdp = mProposedLocalSdp;
801 mCurrentRemoteSdp = static_cast<SdpContents*>(sdp->clone());
802 delete mProposedRemoteSdp;
803 mProposedLocalSdp = 0;
804 mProposedRemoteSdp = 0;
805 mOfferState = Answered;
806 mDum.mInviteSessionHandler->onAnswer(getSessionHandle(), msg, sdp);
807 }
808 else
809 {
810 delete mProposedLocalSdp;
811 delete mProposedRemoteSdp;
812 mProposedLocalSdp = 0;
813 mProposedRemoteSdp = 0;
814 // !jf! is this right?
815 // mDum.mInviteSessionHandler->onOfferRejected(getSessionHandle(), msg);
816 }
817 break;
818 }
819 }
820
821 void
822 InviteSession::send(SipMessage& msg)
823 {
824 Destroyer::Guard guard(mDestroyer);
825 //handle NITs separately
826 if (msg.header(h_CSeq).method() == RESIP_INFO)
827 {
828 mDum.send(msg);
829 return;
830 }
831
832 msg.releaseContents();
833 if (mQueuedBye && (mQueuedBye == &msg))
834 {
835 //queued
836 return;
837 }
838
839 if (msg.isRequest())
840 {
841 switch(msg.header(h_RequestLine).getMethod())
842 {
843 case RESIP_INVITE:
844 case RESIP_UPDATE:
845 case RESIP_ACK:
846 if (mNextOfferOrAnswerSdp)
847 {
848 msg.setContents(mNextOfferOrAnswerSdp);
849 sendSdp(mNextOfferOrAnswerSdp);
850 mNextOfferOrAnswerSdp = 0;
851 }
852 break;
853 default:
854 break;
855 }
856 mDum.send(msg);
857 }
858 else
859 {
860 int code = msg.header(h_StatusLine).statusCode();
861 //!dcm! -- probably kill this object earlier, handle 200 to bye in
862 //DialogUsageManager...very soon
863 if (msg.header(h_CSeq).method() == RESIP_BYE && code == 200) //!dcm! -- not 2xx?
864
865 {
866 mState = Terminated;
867 mDum.send(msg);
868 //mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), msg); // This is actually called when recieving the RESIP_BYE message so that the RESIP_BYE message can be passed to onTerminated
869 guard.destroy();
870 }
871 else if (code >= 200 && code < 300 && msg.header(h_CSeq).method() == RESIP_INVITE)
872 {
873 int seq = msg.header(h_CSeq).sequence();
874 mCurrentRetransmit200 = Timer::T1;
875 mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), seq);
876 mDum.addTimerMs(DumTimeout::WaitForAck, Timer::TH, getBaseHandle(), seq);
877
878 //!dcm! -- this should be mFinalResponse...maybe assign here in
879 //case the user wants to be very strange
880 if (mNextOfferOrAnswerSdp)
881 {
882 msg.setContents(mNextOfferOrAnswerSdp);
883 sendSdp(mNextOfferOrAnswerSdp);
884 mNextOfferOrAnswerSdp = 0;
885 }
886 mDum.send(msg);
887 }
888 else
889 {
890 mDum.send(msg);
891 }
892 }
893 }
894
895 void
896 InviteSession::sendSdp(SdpContents* sdp)
897 {
898 switch (mOfferState)
899 {
900 case Nothing:
901 assert(mCurrentLocalSdp == 0);
902 assert(mCurrentRemoteSdp == 0);
903 mProposedLocalSdp = sdp;
904 mOfferState = Offerred;
905 break;
906
907 case Offerred:
908 assert(mCurrentLocalSdp == 0);
909 assert(mCurrentRemoteSdp == 0);
910 mCurrentLocalSdp = sdp;
911 mCurrentRemoteSdp = mProposedRemoteSdp;
912 delete mProposedLocalSdp;
913 mProposedLocalSdp = 0;
914 mProposedRemoteSdp = 0;
915 mOfferState = Answered;
916 break;
917
918 case Answered:
919 assert(mProposedLocalSdp == 0);
920 assert(mProposedRemoteSdp == 0);
921 mProposedLocalSdp = sdp;
922 mOfferState = CounterOfferred;
923 break;
924
925 case CounterOfferred:
926 assert(mCurrentLocalSdp);
927 assert(mCurrentRemoteSdp);
928 if (sdp)
929 {
930 delete mCurrentLocalSdp;
931 delete mCurrentRemoteSdp;
932 mCurrentLocalSdp = sdp;
933 mCurrentRemoteSdp = mProposedRemoteSdp;
934 delete mProposedLocalSdp;
935 mProposedLocalSdp = 0;
936 mProposedRemoteSdp = 0;
937 }
938 else
939 {
940 delete mProposedLocalSdp;
941 delete mProposedRemoteSdp;
942 mProposedLocalSdp = 0;
943 mProposedRemoteSdp = 0;
944 }
945 mOfferState = Answered;
946 break;
947 }
948 }
949
950 std::pair<InviteSession::OfferAnswerType, const SdpContents*>
951 InviteSession::getOfferOrAnswer(const SipMessage& msg) const
952 {
953 std::pair<InviteSession::OfferAnswerType, const SdpContents*> ret;
954 ret.first = None;
955 const SdpContents* contents = NULL;
956
957 MultipartMixedContents* mixed = dynamic_cast<MultipartMixedContents*>(msg.getContents());
958 if ( mixed )
959 {
960 // Look for first SDP Contents in a multipart contents
961 MultipartMixedContents::Parts& parts = mixed->parts();
962 for( MultipartMixedContents::Parts::const_iterator i = parts.begin();
963 i != parts.end();
964 ++i)
965 {
966 contents = dynamic_cast<const SdpContents*>(*i);
967 if(contents != NULL) break; // Found SDP contents
968 }
969 }
970 else
971 {
972 contents = dynamic_cast<const SdpContents*>(msg.getContents());
973 }
974
975 if (contents)
976 {
977 static Token c100rel(Symbols::C100rel);
978 if (msg.isRequest() || msg.header(h_StatusLine).responseCode() == 200 ||
979 (msg.exists(h_Requires) && msg.header(h_Requires).find(c100rel)))
980 {
981 switch (mOfferState)
982 {
983 case Nothing:
984 ret.first = Offer;
985 ret.second = contents;
986 break;
987
988 case Offerred:
989 ret.first = Answer;
990 ret.second = contents;
991 break;
992
993 case Answered:
994 ret.first = Offer;
995 ret.second = contents;
996 break;
997
998 case CounterOfferred:
999 ret.first = Answer;
1000 ret.second = contents;
1001 break;
1002 }
1003 }
1004 else if (msg.isResponse() &&
1005 msg.header(h_StatusLine).responseCode() < 200 &&
1006 msg.header(h_StatusLine).responseCode() >= 180)
1007 {
1008 ret.second = contents;
1009 }
1010 }
1011 return ret;
1012 }
1013
1014 SipMessage&
1015 InviteSession::rejectDialogModification(int statusCode)
1016 {
1017 if (statusCode < 400)
1018 {
1019 throw UsageUseException("Must reject with a 4xx", __FILE__, __LINE__);
1020 }
1021 mDialog.makeResponse(mLastResponse, mLastIncomingRequest, statusCode);
1022 mState = Connected;
1023 sendSdp(0);
1024 return mLastResponse;
1025 }
1026
1027 SipMessage&
1028 InviteSession::targetRefresh(const NameAddr& localUri)
1029 {
1030 assert(0);
1031 return mLastRequest;
1032 }
1033
1034 void
1035 InviteSession::send()
1036 {
1037 InfoLog ( << "InviteSession::send(void)");
1038 if (mOfferState == Answered || mState != Connected)
1039 {
1040 throw UsageUseException("Cannot call send when there it no Offer/Answer negotiation to do", __FILE__, __LINE__);
1041 }
1042 send(makeAck());
1043 }
1044
1045 SipMessage&
1046 InviteSession::makeAck()
1047 {
1048 InfoLog ( << "InviteSession::makeAck" );
1049
1050 int cseq = mLastRequest.header(h_CSeq).sequence();
1051 if (mAckMap.find(cseq) != mAckMap.end())
1052 {
1053 InfoLog ( << "CSeq collision in ack map: " << Inserter(mAckMap) );
1054 }
1055
1056 assert(mAckMap.find(cseq) == mAckMap.end());
1057 SipMessage& ack = mAckMap[cseq];
1058 ack = mLastRequest;
1059 mDialog.makeRequest(ack, RESIP_ACK);
1060 mDum.addTimerMs(DumTimeout::CanDiscardAck, Timer::TH, getBaseHandle(), cseq);
1061
1062 assert(ack.header(h_Vias).size() == 1);
1063
1064 // if (mNextOfferOrAnswerSdp)
1065 // {
1066 // ack.setContents(mNextOfferOrAnswerSdp);
1067 // sendSdp(mNextOfferOrAnswerSdp);
1068 // mNextOfferOrAnswerSdp = 0;
1069 // }
1070 return ack;
1071 }
1072
1073 /* ====================================================================
1074 * The Vovida Software License, Version 1.0
1075 *
1076 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1077 *
1078 * Redistribution and use in source and binary forms, with or without
1079 * modification, are permitted provided that the following conditions
1080 * are met:
1081 *
1082 * 1. Redistributions of source code must retain the above copyright
1083 * notice, this list of conditions and the following disclaimer.
1084 *
1085 * 2. Redistributions in binary form must reproduce the above copyright
1086 * notice, this list of conditions and the following disclaimer in
1087 * the documentation and/or other materials provided with the
1088
1089 * distribution.
1090 *
1091 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1092 * and "Vovida Open Communication Application Library (VOCAL)" must
1093 * not be used to endorse or promote products derived from this
1094 * software without prior written permission. For written
1095 * permission, please contact vocal@vovida.org.
1096 *
1097 * 4. Products derived from this software may not be called "VOCAL", nor
1098 * may "VOCAL" appear in their name, without prior written
1099 * permission of Vovida Networks, Inc.
1100 *
1101 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1102 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1103 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1104 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1105 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1106 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1107 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1108 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1109 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1110 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1111 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1112 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1113 * DAMAGE.
1114 *
1115 * ====================================================================
1116 *
1117 * This software consists of voluntary contributions made by Vovida
1118 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1119 * Inc. For more information on Vovida Networks, Inc., please see
1120 * <http://www.vovida.org/>.
1121 *
1122 */

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27