1 |
#include "resiprocate/MultipartMixedContents.hxx" |
2 |
#include "resiprocate/SdpContents.hxx" |
3 |
#include "resiprocate/SipMessage.hxx" |
4 |
#include "resiprocate/Helper.hxx" |
5 |
#include "resiprocate/dum/Dialog.hxx" |
6 |
#include "resiprocate/dum/DialogUsageManager.hxx" |
7 |
#include "resiprocate/dum/InviteSession.hxx" |
8 |
#include "resiprocate/dum/ServerInviteSession.hxx" |
9 |
#include "resiprocate/dum/ClientSubscription.hxx" |
10 |
#include "resiprocate/dum/ServerSubscription.hxx" |
11 |
#include "resiprocate/dum/ClientInviteSession.hxx" |
12 |
#include "resiprocate/dum/InviteSessionHandler.hxx" |
13 |
#include "resiprocate/dum/MasterProfile.hxx" |
14 |
#include "resiprocate/dum/UsageUseException.hxx" |
15 |
#include "resiprocate/os/Inserter.hxx" |
16 |
#include "resiprocate/os/Logger.hxx" |
17 |
#include "resiprocate/os/Timer.hxx" |
18 |
#include "resiprocate/os/Random.hxx" |
19 |
#include "resiprocate/os/compat.hxx" |
20 |
#include "resiprocate/os/WinLeakCheck.hxx" |
21 |
|
22 |
// Remove warning about 'this' use in initiator list - pointer is only stored |
23 |
#if defined(WIN32) |
24 |
#pragma warning( disable : 4355 ) // using this in base member initializer list |
25 |
#pragma warning( disable : 4800 ) // forcing value to bool (performance warning) |
26 |
#endif |
27 |
|
28 |
#define RESIPROCATE_SUBSYSTEM Subsystem::DUM |
29 |
#define THROW(msg) throw DialogUsage::Exception(msg, __FILE__,__LINE__); |
30 |
|
31 |
using namespace resip; |
32 |
using namespace std; |
33 |
|
34 |
InviteSession::InviteSession(DialogUsageManager& dum, Dialog& dialog) |
35 |
: DialogUsage(dum, dialog), |
36 |
mState(Undefined), |
37 |
mNitState(NitComplete), |
38 |
mCurrentRetransmit200(0), |
39 |
mSessionInterval(0), |
40 |
mMinSE(90), |
41 |
mSessionRefresher(false), |
42 |
mSessionTimerSeq(0), |
43 |
mSentRefer(false) |
44 |
{ |
45 |
DebugLog ( << "^^^ InviteSession::InviteSession " << this); |
46 |
assert(mDum.mInviteSessionHandler); |
47 |
} |
48 |
|
49 |
InviteSession::~InviteSession() |
50 |
{ |
51 |
DebugLog ( << "^^^ InviteSession::~InviteSession " << this); |
52 |
mDialog.mInviteSession = 0; |
53 |
} |
54 |
|
55 |
void |
56 |
InviteSession::dialogDestroyed(const SipMessage& msg) |
57 |
{ |
58 |
assert(0); |
59 |
|
60 |
// !jf! Is this correct? Merged from main... |
61 |
// !jf! what reason - guessed for now? |
62 |
//mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, msg); |
63 |
//delete this; |
64 |
} |
65 |
|
66 |
const SdpContents& |
67 |
InviteSession::getLocalSdp() const |
68 |
{ |
69 |
return *mCurrentLocalSdp; |
70 |
} |
71 |
|
72 |
const SdpContents& |
73 |
InviteSession::getRemoteSdp() const |
74 |
{ |
75 |
return *mCurrentRemoteSdp; |
76 |
} |
77 |
|
78 |
const Data& |
79 |
InviteSession::getDialogId() const |
80 |
{ |
81 |
return mDialog.getId().getCallId(); |
82 |
} |
83 |
|
84 |
InviteSessionHandle |
85 |
InviteSession::getSessionHandle() |
86 |
{ |
87 |
return InviteSessionHandle(mDum, getBaseHandle().getId()); |
88 |
} |
89 |
|
90 |
void InviteSession::storePeerCapabilities(const SipMessage& msg) |
91 |
{ |
92 |
// !slg! ToDo - add methods to get this data, App may be interested |
93 |
if (msg.exists(h_Allows)) |
94 |
{ |
95 |
mPeerSupportedMethods = msg.header(h_Allows); |
96 |
} |
97 |
if (msg.exists(h_Supporteds)) |
98 |
{ |
99 |
mPeerSupportedOptionTags = msg.header(h_Supporteds); |
100 |
} |
101 |
if (msg.exists(h_AcceptEncodings)) |
102 |
{ |
103 |
mPeerSupportedEncodings = msg.header(h_AcceptEncodings); |
104 |
} |
105 |
if (msg.exists(h_AcceptLanguages)) |
106 |
{ |
107 |
mPeerSupportedLanguages = msg.header(h_AcceptLanguages); |
108 |
} |
109 |
if (msg.exists(h_Accepts)) |
110 |
{ |
111 |
mPeerSupportedMimeTypes = msg.header(h_Accepts); |
112 |
} |
113 |
} |
114 |
|
115 |
bool |
116 |
InviteSession::updateMethodSupported() const |
117 |
{ |
118 |
// Check if Update is supported locally |
119 |
if(mDum.getMasterProfile()->isMethodSupported(UPDATE)) |
120 |
{ |
121 |
// Check if peer supports UPDATE |
122 |
return mPeerSupportedMethods.find(Token("UPDATE")); |
123 |
} |
124 |
return false; |
125 |
} |
126 |
|
127 |
const NameAddr& |
128 |
InviteSession::myAddr() const |
129 |
{ |
130 |
return mDialog.mLocalNameAddr; |
131 |
} |
132 |
|
133 |
const NameAddr& |
134 |
InviteSession::peerAddr() const |
135 |
{ |
136 |
return mDialog.mRemoteNameAddr; |
137 |
} |
138 |
|
139 |
bool |
140 |
InviteSession::isConnected() const |
141 |
{ |
142 |
switch (mState) |
143 |
{ |
144 |
case Connected: |
145 |
case SentUpdate: |
146 |
case SentUpdateGlare: |
147 |
case SentReinvite: |
148 |
case SentReinviteGlare: |
149 |
case ReceivedUpdate: |
150 |
case ReceivedReinvite: |
151 |
case ReceivedReinviteNoOffer: |
152 |
case Answered: |
153 |
case WaitingToOffer: |
154 |
return true; |
155 |
|
156 |
default: |
157 |
return false; |
158 |
} |
159 |
} |
160 |
|
161 |
bool |
162 |
InviteSession::isEarly() const |
163 |
{ |
164 |
switch (mState) |
165 |
{ |
166 |
case UAC_Start: |
167 |
case UAC_Early: |
168 |
case UAC_EarlyWithOffer: |
169 |
case UAC_EarlyWithAnswer: |
170 |
//case UAC_Answered: |
171 |
//case UAC_Terminated: |
172 |
case UAC_SentUpdateEarly: |
173 |
case UAC_SentUpdateConnected: |
174 |
case UAC_ReceivedUpdateEarly: |
175 |
//case UAC_SentAnswer: |
176 |
case UAC_QueuedUpdate: |
177 |
return true; |
178 |
|
179 |
default: |
180 |
return false; |
181 |
} |
182 |
} |
183 |
|
184 |
|
185 |
bool |
186 |
InviteSession::isTerminated() const |
187 |
{ |
188 |
switch (mState) |
189 |
{ |
190 |
case Terminated: |
191 |
case WaitingToTerminate: |
192 |
case UAC_Cancelled: |
193 |
case UAS_WaitingToTerminate: |
194 |
case UAS_WaitingToHangup: |
195 |
return true; |
196 |
default: |
197 |
return false; |
198 |
} |
199 |
} |
200 |
|
201 |
std::ostream& |
202 |
InviteSession::dump(std::ostream& strm) const |
203 |
{ |
204 |
strm << "INVITE: " << mId |
205 |
<< " " << toData(mState) |
206 |
<< " ADDR=" << myAddr() |
207 |
<< " PEER=" << peerAddr(); |
208 |
return strm; |
209 |
} |
210 |
|
211 |
void |
212 |
InviteSession::provideOffer(const SdpContents& offer) |
213 |
{ |
214 |
switch (mState) |
215 |
{ |
216 |
case Connected: |
217 |
case WaitingToOffer: |
218 |
case UAS_WaitingToOffer: |
219 |
if (updateMethodSupported()) |
220 |
{ |
221 |
transition(SentUpdate); |
222 |
mDialog.makeRequest(mLastSessionModification, UPDATE); |
223 |
} |
224 |
else |
225 |
{ |
226 |
transition(SentReinvite); |
227 |
mDialog.makeRequest(mLastSessionModification, INVITE); |
228 |
} |
229 |
setSessionTimerHeaders(mLastSessionModification); |
230 |
|
231 |
InfoLog (<< "Sending " << mLastSessionModification.brief()); |
232 |
InviteSession::setSdp(mLastSessionModification, offer); |
233 |
mProposedLocalSdp = InviteSession::makeSdp(offer); |
234 |
mDialog.send(mLastSessionModification); |
235 |
break; |
236 |
|
237 |
case Answered: |
238 |
// queue the offer to be sent after the ACK is received |
239 |
transition(WaitingToOffer); |
240 |
mProposedLocalSdp = InviteSession::makeSdp(offer); |
241 |
break; |
242 |
|
243 |
// ?slg? Can we handle all of the states listed in isConnected() ??? |
244 |
default: |
245 |
WarningLog (<< "Can't provideOffer when not in Connected state"); |
246 |
throw DialogUsage::Exception("Can't provide an offer", __FILE__,__LINE__); |
247 |
} |
248 |
} |
249 |
|
250 |
void |
251 |
InviteSession::provideAnswer(const SdpContents& answer) |
252 |
{ |
253 |
switch (mState) |
254 |
{ |
255 |
case ReceivedReinvite: |
256 |
transition(Connected); |
257 |
mDialog.makeResponse(mInvite200, mLastSessionModification, 200); |
258 |
handleSessionTimerRequest(mInvite200, mLastSessionModification); |
259 |
InviteSession::setSdp(mInvite200, answer); |
260 |
mCurrentLocalSdp = InviteSession::makeSdp(answer); |
261 |
mCurrentRemoteSdp = mProposedRemoteSdp; |
262 |
InfoLog (<< "Sending " << mInvite200.brief()); |
263 |
mDialog.send(mInvite200); |
264 |
startRetransmit200Timer(); |
265 |
break; |
266 |
|
267 |
case ReceivedUpdate: // same as ReceivedReinvite case. |
268 |
{ |
269 |
transition(Connected); |
270 |
|
271 |
SipMessage response; |
272 |
mDialog.makeResponse(response, mLastSessionModification, 200); |
273 |
handleSessionTimerRequest(response, mLastSessionModification); |
274 |
InviteSession::setSdp(response, answer); |
275 |
mCurrentLocalSdp = InviteSession::makeSdp(answer); |
276 |
mCurrentRemoteSdp = mProposedRemoteSdp; |
277 |
InfoLog (<< "Sending " << response.brief()); |
278 |
mDialog.send(response); |
279 |
break; |
280 |
} |
281 |
|
282 |
default: |
283 |
WarningLog (<< "Can't provideAnswer when not in Connected state"); |
284 |
throw DialogUsage::Exception("Can't provide an offer", __FILE__,__LINE__); |
285 |
} |
286 |
} |
287 |
|
288 |
void |
289 |
InviteSession::end() |
290 |
{ |
291 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
292 |
|
293 |
switch (mState) |
294 |
{ |
295 |
case Connected: |
296 |
{ |
297 |
// !jf! do we need to store the BYE somewhere? |
298 |
sendBye(); |
299 |
transition(Terminated); |
300 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended); |
301 |
break; |
302 |
} |
303 |
|
304 |
case SentUpdate: |
305 |
sendBye(); |
306 |
transition(Terminated); |
307 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended); |
308 |
break; |
309 |
|
310 |
case SentReinvite: |
311 |
transition(WaitingToTerminate); |
312 |
break; |
313 |
|
314 |
case ReceivedUpdate: |
315 |
case ReceivedReinvite: |
316 |
case ReceivedReinviteNoOffer: |
317 |
{ |
318 |
SipMessage response; |
319 |
mDialog.makeResponse(response, mLastSessionModification, 488); |
320 |
InfoLog (<< "Sending " << response.brief()); |
321 |
mDialog.send(response); |
322 |
|
323 |
sendBye(); |
324 |
transition(Terminated); |
325 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended); |
326 |
break; |
327 |
} |
328 |
|
329 |
case WaitingToTerminate: |
330 |
{ |
331 |
sendBye(); |
332 |
transition(Terminated); |
333 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended); |
334 |
break; |
335 |
} |
336 |
|
337 |
case Terminated: |
338 |
// no-op. |
339 |
break; |
340 |
|
341 |
default: |
342 |
assert(0); |
343 |
break; |
344 |
} |
345 |
} |
346 |
|
347 |
void |
348 |
InviteSession::reject(int statusCode, WarningCategory *warning) |
349 |
{ |
350 |
switch (mState) |
351 |
{ |
352 |
case ReceivedUpdate: |
353 |
case ReceivedReinvite: |
354 |
case ReceivedReinviteNoOffer: |
355 |
{ |
356 |
transition(Connected); |
357 |
|
358 |
SipMessage response; |
359 |
mDialog.makeResponse(response, mLastSessionModification, statusCode); |
360 |
if(warning) |
361 |
{ |
362 |
response.header(h_Warnings).push_back(*warning); |
363 |
} |
364 |
InfoLog (<< "Sending " << response.brief()); |
365 |
mDialog.send(response); |
366 |
break; |
367 |
} |
368 |
|
369 |
default: |
370 |
assert(0); |
371 |
break; |
372 |
} |
373 |
} |
374 |
|
375 |
void |
376 |
InviteSession::targetRefresh(const NameAddr& localUri) |
377 |
{ |
378 |
if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite? |
379 |
{ |
380 |
// !jf! add interface to Dialog |
381 |
//mDialog.setLocalContact(localUri); |
382 |
provideOffer(*mCurrentLocalSdp); |
383 |
} |
384 |
else |
385 |
{ |
386 |
WarningLog (<< "Can't targetRefresh before Connected"); |
387 |
assert(0); |
388 |
throw UsageUseException("targetRefresh not allowed in this context", __FILE__, __LINE__); |
389 |
} |
390 |
} |
391 |
|
392 |
void |
393 |
InviteSession::refer(const NameAddr& referTo) |
394 |
{ |
395 |
if (mSentRefer) |
396 |
{ |
397 |
throw UsageUseException("Attempted to send overlapping refer", __FILE__, __LINE__); |
398 |
} |
399 |
|
400 |
if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite? |
401 |
{ |
402 |
mSentRefer = true; |
403 |
SipMessage refer; |
404 |
mDialog.makeRequest(refer, REFER); |
405 |
refer.header(h_ReferTo) = referTo; |
406 |
refer.header(h_ReferredBy) = mDialog.mLocalContact; // !slg! is it ok to do this - should it be an option? |
407 |
mDialog.send(refer); |
408 |
} |
409 |
else |
410 |
{ |
411 |
WarningLog (<< "Can't refer before Connected"); |
412 |
assert(0); |
413 |
throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__); |
414 |
} |
415 |
} |
416 |
|
417 |
void |
418 |
InviteSession::refer(const NameAddr& referTo, InviteSessionHandle sessionToReplace) |
419 |
{ |
420 |
if (!sessionToReplace.isValid()) |
421 |
{ |
422 |
throw UsageUseException("Attempted to make a refer w/ and invalid replacement target", __FILE__, __LINE__); |
423 |
} |
424 |
|
425 |
if (mSentRefer) |
426 |
{ |
427 |
throw UsageUseException("Attempted to send overlapping refer", __FILE__, __LINE__); |
428 |
} |
429 |
|
430 |
if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite? |
431 |
{ |
432 |
mSentRefer = true; |
433 |
SipMessage refer; |
434 |
mDialog.makeRequest(refer, REFER); |
435 |
|
436 |
refer.header(h_ReferTo) = referTo; |
437 |
refer.header(h_ReferredBy) = mDialog.mLocalContact; // ?slg? is it ok to do this - should it be an option? |
438 |
CallId replaces; |
439 |
DialogId id = sessionToReplace->mDialog.getId(); |
440 |
replaces.value() = id.getCallId(); |
441 |
replaces.param(p_toTag) = id.getRemoteTag(); |
442 |
replaces.param(p_fromTag) = id.getLocalTag(); |
443 |
|
444 |
refer.header(h_ReferTo).uri().embedded().header(h_Replaces) = replaces; |
445 |
mDialog.send(refer); |
446 |
} |
447 |
else |
448 |
{ |
449 |
WarningLog (<< "Can't refer before Connected"); |
450 |
assert(0); |
451 |
throw UsageUseException("REFER not allowed in this context", __FILE__, __LINE__); |
452 |
} |
453 |
} |
454 |
|
455 |
void |
456 |
InviteSession::info(const Contents& contents) |
457 |
{ |
458 |
if (mNitState == NitComplete) |
459 |
{ |
460 |
if (isConnected()) // ?slg? likely not safe in any state except Connected - what should behaviour be if state is ReceivedReinvite? |
461 |
{ |
462 |
mNitState = NitProceeding; |
463 |
SipMessage info; |
464 |
mDialog.makeRequest(info, INFO); |
465 |
// !jf! handle multipart here |
466 |
info.setContents(&contents); |
467 |
mDialog.send(info); |
468 |
} |
469 |
else |
470 |
{ |
471 |
WarningLog (<< "Can't send INFO before Connected"); |
472 |
assert(0); |
473 |
throw UsageUseException("Can't send INFO before Connected", __FILE__, __LINE__); |
474 |
} |
475 |
} |
476 |
else |
477 |
{ |
478 |
throw UsageUseException("Cannot start a non-invite transaction until the previous one has completed", |
479 |
__FILE__, __LINE__); |
480 |
} |
481 |
} |
482 |
|
483 |
void |
484 |
InviteSession::dispatch(const SipMessage& msg) |
485 |
{ |
486 |
// !jf! do we need to handle 3xx here or is it handled elsewhere? |
487 |
switch (mState) |
488 |
{ |
489 |
case Connected: |
490 |
dispatchConnected(msg); |
491 |
break; |
492 |
case SentUpdate: |
493 |
dispatchSentUpdate(msg); |
494 |
break; |
495 |
case SentReinvite: |
496 |
dispatchSentReinvite(msg); |
497 |
break; |
498 |
case SentUpdateGlare: |
499 |
case SentReinviteGlare: |
500 |
// The behavior is the same except for timer which is handled in dispatch(Timer) |
501 |
dispatchGlare(msg); |
502 |
break; |
503 |
case ReceivedUpdate: |
504 |
case ReceivedReinvite: |
505 |
case ReceivedReinviteNoOffer: |
506 |
dispatchReceivedUpdateOrReinvite(msg); |
507 |
break; |
508 |
case Answered: |
509 |
dispatchAnswered(msg); |
510 |
break; |
511 |
case WaitingToOffer: |
512 |
dispatchWaitingToOffer(msg); |
513 |
break; |
514 |
case WaitingToTerminate: |
515 |
dispatchWaitingToTerminate(msg); |
516 |
break; |
517 |
case Terminated: |
518 |
dispatchTerminated(msg); |
519 |
break; |
520 |
case Undefined: |
521 |
default: |
522 |
assert(0); |
523 |
break; |
524 |
} |
525 |
} |
526 |
|
527 |
void |
528 |
InviteSession::dispatch(const DumTimeout& timeout) |
529 |
{ |
530 |
if (timeout.type() == DumTimeout::Retransmit200) |
531 |
{ |
532 |
if (mCurrentRetransmit200) |
533 |
{ |
534 |
InfoLog (<< "Retransmitting: " << endl << mInvite200); |
535 |
mDialog.send(mInvite200); |
536 |
mCurrentRetransmit200 *= 2; |
537 |
mDum.addTimerMs(DumTimeout::Retransmit200, resipMin(Timer::T2, mCurrentRetransmit200), getBaseHandle(), timeout.seq()); |
538 |
} |
539 |
} |
540 |
else if (timeout.type() == DumTimeout::WaitForAck) |
541 |
{ |
542 |
if(mCurrentRetransmit200) // If retransmit200 timer is active then ACK is not received yet |
543 |
{ |
544 |
mCurrentRetransmit200 = 0; // stop the 200 retransmit timer |
545 |
|
546 |
// this is so the app can decided to ignore this. default implementation |
547 |
// will call end next |
548 |
mDum.mInviteSessionHandler->onAckNotReceived(getSessionHandle()); |
549 |
|
550 |
// If we are waiting for an Ack and it times out, then end with a BYE |
551 |
if(mState == UAS_WaitingToHangup) |
552 |
{ |
553 |
sendBye(); |
554 |
transition(Terminated); |
555 |
mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended); |
556 |
} |
557 |
} |
558 |
} |
559 |
else if (timeout.type() == DumTimeout::Glare) |
560 |
{ |
561 |
if (mState == SentUpdateGlare) |
562 |
{ |
563 |
transition(SentUpdate); |
564 |
|
565 |
InfoLog (<< "Retransmitting the UPDATE (glare condition timer)"); |
566 |
mDialog.send(mLastSessionModification); |
567 |
} |
568 |
else if (mState == SentReinviteGlare) |
569 |
{ |
570 |
transition(SentReinvite); |
571 |
|
572 |
InfoLog (<< "Retransmitting the reINVITE (glare condition timer)"); |
573 |
mDialog.send(mLastSessionModification); |
574 |
} |
575 |
} |
576 |
else if (timeout.type() == DumTimeout::SessionExpiration) |
577 |
{ |
578 |
if(timeout.seq() == mSessionTimerSeq) |
579 |
{ |
580 |
// this is so the app can decided to ignore this. default implementation |
581 |
// will call end next |
582 |
mDum.mInviteSessionHandler->onSessionExpired(getSessionHandle()); |
583 |
} |
584 |
} |
585 |
else if (timeout.type() == DumTimeout::SessionRefresh) |
586 |
{ |
587 |
if(timeout.seq() == mSessionTimerSeq) |
588 |
{ |
589 |
if(mState == Connected) // Note: If not connected then we must be issueing a reinvite/update or receiving one - in either case the session timer stuff will get reset/renegotiated - thus just ignore this referesh |
590 |
{ |
591 |
sessionRefresh(); |
592 |
} |
593 |
} |
594 |
} |
595 |
} |
596 |
|
597 |
|
598 |
void |
599 |
InviteSession::dispatchConnected(const SipMessage& msg) |
600 |
{ |
601 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
602 |
std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg); |
603 |
|
604 |
switch (toEvent(msg, sdp.get())) |
605 |
{ |
606 |
case OnInvite: |
607 |
case OnInviteReliable: |
608 |
mLastSessionModification = msg; |
609 |
transition(ReceivedReinviteNoOffer); |
610 |
//handler->onDialogModified(getSessionHandle(), None, msg); |
611 |
handler->onOfferRequired(getSessionHandle(), msg); |
612 |
break; |
613 |
|
614 |
case OnInviteOffer: |
615 |
case OnInviteReliableOffer: |
616 |
mLastSessionModification = msg; |
617 |
transition(ReceivedReinvite); |
618 |
//handler->onDialogModified(getSessionHandle(), Offer, msg); |
619 |
handler->onOffer(getSessionHandle(), msg, *sdp); |
620 |
break; |
621 |
|
622 |
case On2xx: |
623 |
case On2xxOffer: |
624 |
case On2xxAnswer: |
625 |
// retransmission of 200I |
626 |
// !jf! Need to include the answer here. |
627 |
sendAck(); |
628 |
break; |
629 |
|
630 |
case OnUpdateOffer: |
631 |
transition(ReceivedUpdate); |
632 |
|
633 |
// !kh! |
634 |
// Find out if it's an UPDATE requiring state change. |
635 |
// See rfc3311 5.2, 4th paragraph. |
636 |
mLastSessionModification = msg; |
637 |
handler->onOffer(getSessionHandle(), msg, *sdp); |
638 |
break; |
639 |
|
640 |
case OnUpdate: |
641 |
{ |
642 |
// ?slg? no sdp in update - just responsd immediately (likely session timer) - do we need a callback? |
643 |
SipMessage response; |
644 |
mLastSessionModification = msg; |
645 |
mDialog.makeResponse(response, mLastSessionModification, 200); |
646 |
handleSessionTimerRequest(response, mLastSessionModification); |
647 |
send(response); |
648 |
break; |
649 |
} |
650 |
|
651 |
case OnUpdateRejected: |
652 |
case On200Update: |
653 |
WarningLog (<< "DUM delivered an UPDATE response in an incorrect state " << endl << msg); |
654 |
assert(0); |
655 |
break; |
656 |
|
657 |
case OnAck: |
658 |
mCurrentRetransmit200 = 0; // stop the 200 retransmit timer |
659 |
break; |
660 |
|
661 |
default: |
662 |
dispatchOthers(msg); |
663 |
break; |
664 |
} |
665 |
} |
666 |
|
667 |
|
668 |
|
669 |
void |
670 |
InviteSession::dispatchSentUpdate(const SipMessage& msg) |
671 |
{ |
672 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
673 |
std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg); |
674 |
|
675 |
switch (toEvent(msg, sdp.get())) |
676 |
{ |
677 |
case OnInvite: |
678 |
case OnInviteReliable: |
679 |
case OnInviteOffer: |
680 |
case OnInviteReliableOffer: |
681 |
case OnUpdate: |
682 |
case OnUpdateOffer: |
683 |
{ |
684 |
// glare |
685 |
SipMessage response; |
686 |
mDialog.makeResponse(response, msg, 491); |
687 |
send(response); |
688 |
break; |
689 |
} |
690 |
|
691 |
case On200Update: |
692 |
transition(Connected); |
693 |
handleSessionTimerResponse(msg); |
694 |
if (sdp.get()) |
695 |
{ |
696 |
mCurrentLocalSdp = mProposedLocalSdp; |
697 |
mCurrentRemoteSdp = InviteSession::makeSdp(*sdp); |
698 |
handler->onAnswer(getSessionHandle(), msg, *sdp); |
699 |
} |
700 |
else if(mProposedLocalSdp.get()) |
701 |
{ |
702 |
// If we sent an offer in the Update Request and no answer is received |
703 |
handler->onIllegalNegotiation(getSessionHandle(), msg); |
704 |
mProposedLocalSdp.release(); |
705 |
} |
706 |
break; |
707 |
|
708 |
case On491Update: |
709 |
transition(SentUpdateGlare); |
710 |
start491Timer(); |
711 |
break; |
712 |
|
713 |
case On422Update: |
714 |
if(msg.exists(h_MinSE)) |
715 |
{ |
716 |
// Change interval to min from 422 response |
717 |
mSessionInterval = msg.header(h_MinSE).value(); |
718 |
mMinSE = mSessionInterval; |
719 |
sessionRefresh(); |
720 |
} |
721 |
else |
722 |
{ |
723 |
// Response must contact Min_SE - if not - just ignore |
724 |
// ?slg? callback? |
725 |
transition(Connected); |
726 |
mProposedLocalSdp.release(); |
727 |
} |
728 |
break; |
729 |
|
730 |
case OnUpdateRejected: |
731 |
// !jf! - callback? |
732 |
mProposedLocalSdp.release(); |
733 |
transition(Connected); |
734 |
break; |
735 |
|
736 |
case OnGeneralFailure: |
737 |
sendBye(); |
738 |
transition(Terminated); |
739 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg); |
740 |
break; |
741 |
|
742 |
default: |
743 |
dispatchOthers(msg); |
744 |
break; |
745 |
} |
746 |
} |
747 |
|
748 |
void |
749 |
InviteSession::dispatchSentReinvite(const SipMessage& msg) |
750 |
{ |
751 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
752 |
std::auto_ptr<SdpContents> sdp = InviteSession::getSdp(msg); |
753 |
|
754 |
switch (toEvent(msg, sdp.get())) |
755 |
{ |
756 |
case OnInvite: |
757 |
case OnInviteReliable: |
758 |
case OnInviteOffer: |
759 |
case OnInviteReliableOffer: |
760 |
case OnUpdate: |
761 |
case OnUpdateOffer: |
762 |
{ |
763 |
SipMessage response; |
764 |
mDialog.makeResponse(response, msg, 491); |
765 |
send(response); |
766 |
break; |
767 |
} |
768 |
|
769 |
case On1xx: |
770 |
case On1xxEarly: |
771 |
// Some UA's send a 100 response to a ReInvite - just ignore it |
772 |
break; |
773 |
|
774 |
case On2xxAnswer: |
775 |
case On2xxOffer: |
776 |
{ |
777 |
transition(Connected); |
778 |
handleSessionTimerResponse(msg); |
779 |
mCurrentLocalSdp = mProposedLocalSdp; |
780 |
mCurrentRemoteSdp = InviteSession::makeSdp(*sdp); |
781 |
// !jf! I need to potentially include an answer in the ACK here |
782 |
sendAck(); |
783 |
handler->onAnswer(getSessionHandle(), msg, *sdp); |
784 |
|
785 |
|
786 |
// !jf! do I need to allow a reINVITE overlapping the retransmission of |
787 |
// the ACK when a 200I is received? If yes, then I need to store all |
788 |
// ACK messages for 64*T1 |
789 |
break; |
790 |
} |
791 |
case On2xx: |
792 |
sendAck(); |
793 |
transition(Connected); |
794 |
handleSessionTimerResponse(msg); |
795 |
handler->onIllegalNegotiation(getSessionHandle(), msg); |
796 |
mProposedLocalSdp.release(); |
797 |
break; |
798 |
|
799 |
case On422Invite: |
800 |
if(msg.exists(h_MinSE)) |
801 |
{ |
802 |
// Change interval to min from 422 response |
803 |
mSessionInterval = msg.header(h_MinSE).value(); |
804 |
mMinSE = mSessionInterval; |
805 |
sessionRefresh(); |
806 |
} |
807 |
else |
808 |
{ |
809 |
// Response must contact Min_SE - if not - just ignore |
810 |
// ?slg? callback? |
811 |
transition(Connected); |
812 |
mProposedLocalSdp.release(); |
813 |
} |
814 |
break; |
815 |
|
816 |
case On491Invite: |
817 |
transition(SentReinviteGlare); |
818 |
start491Timer(); |
819 |
break; |
820 |
|
821 |
case OnGeneralFailure: |
822 |
sendBye(); |
823 |
transition(Terminated); |
824 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg); |
825 |
break; |
826 |
|
827 |
case OnInviteFailure: |
828 |
transition(Connected); |
829 |
mProposedLocalSdp.release(); |
830 |
handler->onOfferRejected(getSessionHandle(), msg); |
831 |
break; |
832 |
|
833 |
default: |
834 |
dispatchOthers(msg); |
835 |
break; |
836 |
} |
837 |
} |
838 |
|
839 |
void |
840 |
InviteSession::dispatchGlare(const SipMessage& msg) |
841 |
{ |
842 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
843 |
MethodTypes method = msg.header(h_CSeq).method(); |
844 |
if (method == INVITE && msg.isRequest()) |
845 |
{ |
846 |
// Received inbound reinvite, when waiting to resend outbound reinvite or update |
847 |
transition(ReceivedReinvite); |
848 |
handler->onOfferRejected(getSessionHandle(), msg); |
849 |
} |
850 |
else if (method == UPDATE && msg.isRequest()) |
851 |
{ |
852 |
// Received inbound update, when waiting to resend outbound reinvite or update |
853 |
transition(ReceivedUpdate); |
854 |
handler->onOfferRejected(getSessionHandle(), msg); |
855 |
} |
856 |
else |
857 |
{ |
858 |
dispatchOthers(msg); |
859 |
} |
860 |
} |
861 |
|
862 |
void |
863 |
InviteSession::dispatchReceivedUpdateOrReinvite(const SipMessage& msg) |
864 |
{ |
865 |
MethodTypes method = msg.header(h_CSeq).method(); |
866 |
if (method == INVITE || method == UPDATE) |
867 |
{ |
868 |
// Means that the UAC has sent us a second reINVITE or UPDATE before we |
869 |
// responded to the first one. Bastard! |
870 |
SipMessage response; |
871 |
mDialog.makeResponse(response, msg, 500); |
872 |
response.header(h_RetryAfter).value() = Random::getRandom() % 10; |
873 |
mDialog.send(response); |
874 |
} |
875 |
else |
876 |
{ |
877 |
dispatchOthers(msg); |
878 |
} |
879 |
} |
880 |
|
881 |
|
882 |
void |
883 |
InviteSession::dispatchAnswered(const SipMessage& msg) |
884 |
{ |
885 |
if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK) |
886 |
{ |
887 |
mCurrentRetransmit200 = 0; // stop the 200 retransmit timer |
888 |
transition(Connected); |
889 |
} |
890 |
else |
891 |
{ |
892 |
dispatchOthers(msg); |
893 |
} |
894 |
} |
895 |
|
896 |
void |
897 |
InviteSession::dispatchWaitingToOffer(const SipMessage& msg) |
898 |
{ |
899 |
if (msg.isRequest() && msg.header(h_RequestLine).method() == ACK) |
900 |
{ |
901 |
mCurrentRetransmit200 = 0; // stop the 200 retransmit timer |
902 |
provideOffer(*mProposedLocalSdp); |
903 |
} |
904 |
else |
905 |
{ |
906 |
dispatchOthers(msg); |
907 |
} |
908 |
} |
909 |
|
910 |
void |
911 |
InviteSession::dispatchWaitingToTerminate(const SipMessage& msg) |
912 |
{ |
913 |
if (msg.isResponse() && |
914 |
msg.header(h_CSeq).method() == INVITE) |
915 |
{ |
916 |
if(msg.header(h_StatusLine).statusCode() / 200 == 1) // Note: stack ACK's non-2xx final responses only |
917 |
{ |
918 |
// !jf! Need to include the answer here. |
919 |
sendAck(); |
920 |
} |
921 |
sendBye(); |
922 |
transition(Terminated); |
923 |
mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::Ended); |
924 |
} |
925 |
} |
926 |
|
927 |
void |
928 |
InviteSession::dispatchTerminated(const SipMessage& msg) |
929 |
{ |
930 |
InfoLog (<< "InviteSession::dispatchTerminated " << msg.brief()); |
931 |
|
932 |
if (msg.isRequest()) |
933 |
{ |
934 |
SipMessage response; |
935 |
mDialog.makeResponse(response, msg, 481); |
936 |
mDialog.send(response); |
937 |
|
938 |
// !jf! means the peer sent BYE while we are waiting for response to BYE |
939 |
//mDum.destroy(this); |
940 |
} |
941 |
else |
942 |
{ |
943 |
mDum.destroy(this); |
944 |
} |
945 |
} |
946 |
|
947 |
void |
948 |
InviteSession::dispatchOthers(const SipMessage& msg) |
949 |
{ |
950 |
// handle OnGeneralFailure |
951 |
// handle OnRedirect |
952 |
|
953 |
switch (msg.header(h_CSeq).method()) |
954 |
{ |
955 |
case PRACK: |
956 |
dispatchPrack(msg); |
957 |
break; |
958 |
case CANCEL: |
959 |
dispatchCancel(msg); |
960 |
break; |
961 |
case BYE: |
962 |
dispatchBye(msg); |
963 |
break; |
964 |
case INFO: |
965 |
dispatchInfo(msg); |
966 |
break; |
967 |
case ACK: |
968 |
// Ignore duplicate ACKs from 2xx reTransmissions |
969 |
break; |
970 |
default: |
971 |
// handled in Dialog |
972 |
WarningLog (<< "DUM delivered a " |
973 |
<< msg.header(h_CSeq).unknownMethodName() |
974 |
<< " to the InviteSession " |
975 |
<< endl |
976 |
<< msg); |
977 |
assert(0); |
978 |
break; |
979 |
} |
980 |
} |
981 |
|
982 |
void |
983 |
InviteSession::dispatchUnhandledInvite(const SipMessage& msg) |
984 |
{ |
985 |
assert(msg.isRequest()); |
986 |
assert(msg.header(h_CSeq).method() == INVITE); |
987 |
|
988 |
// If we get an INVITE request from the wire and we are not in |
989 |
// Connected state, reject the request and send a BYE |
990 |
SipMessage response; |
991 |
mDialog.makeResponse(response, msg, 400); // !jf! what code to use? |
992 |
InfoLog (<< "Sending " << response.brief()); |
993 |
mDialog.send(response); |
994 |
|
995 |
sendBye(); |
996 |
transition(Terminated); |
997 |
mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg); |
998 |
} |
999 |
|
1000 |
void |
1001 |
InviteSession::dispatchPrack(const SipMessage& msg) |
1002 |
{ |
1003 |
assert(msg.header(h_CSeq).method() == PRACK); |
1004 |
if(msg.isRequest()) |
1005 |
{ |
1006 |
SipMessage rsp; |
1007 |
mDialog.makeResponse(rsp, msg, 481); |
1008 |
mDialog.send(rsp); |
1009 |
|
1010 |
sendBye(); |
1011 |
// !jf! should we make some other callback here |
1012 |
transition(Terminated); |
1013 |
mDum.mInviteSessionHandler->onTerminated(getSessionHandle(), InviteSessionHandler::GeneralFailure, &msg); |
1014 |
} |
1015 |
else |
1016 |
{ |
1017 |
// ignore. could be PRACK/200 |
1018 |
} |
1019 |
} |
1020 |
|
1021 |
void |
1022 |
InviteSession::dispatchCancel(const SipMessage& msg) |
1023 |
{ |
1024 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
1025 |
assert(msg.header(h_CSeq).method() == CANCEL); |
1026 |
if(msg.isRequest()) |
1027 |
{ |
1028 |
SipMessage rsp; |
1029 |
mDialog.makeResponse(rsp, msg, 200); |
1030 |
mDialog.send(rsp); |
1031 |
|
1032 |
sendBye(); |
1033 |
// !jf! should we make some other callback here |
1034 |
transition(Terminated); |
1035 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, &msg); |
1036 |
} |
1037 |
else |
1038 |
{ |
1039 |
WarningLog (<< "DUM let me send a CANCEL at an incorrect state " << endl << msg); |
1040 |
assert(0); |
1041 |
} |
1042 |
} |
1043 |
|
1044 |
void |
1045 |
InviteSession::dispatchBye(const SipMessage& msg) |
1046 |
{ |
1047 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
1048 |
|
1049 |
if (msg.isRequest()) |
1050 |
{ |
1051 |
|
1052 |
SipMessage rsp; |
1053 |
InfoLog (<< "Received " << msg.brief()); |
1054 |
mDialog.makeResponse(rsp, msg, 200); |
1055 |
mDialog.send(rsp); |
1056 |
|
1057 |
// !jf! should we make some other callback here |
1058 |
transition(Terminated); |
1059 |
handler->onTerminated(getSessionHandle(), InviteSessionHandler::PeerEnded, &msg); |
1060 |
mDum.destroy(this); |
1061 |
} |
1062 |
else |
1063 |
{ |
1064 |
WarningLog (<< "DUM let me send a BYE at an incorrect state " << endl << msg); |
1065 |
assert(0); |
1066 |
} |
1067 |
} |
1068 |
|
1069 |
void |
1070 |
InviteSession::dispatchInfo(const SipMessage& msg) |
1071 |
{ |
1072 |
InviteSessionHandler* handler = mDum.mInviteSessionHandler; |
1073 |
if (msg.isRequest()) |
1074 |
{ |
1075 |
InfoLog (<< "Received " << msg.brief()); |
1076 |
mDialog.makeResponse(mLastNitResponse, msg, 200); |
1077 |
handler->onInfo(getSessionHandle(), msg); |
1078 |
} |
1079 |
else |
1080 |
{ |
1081 |
assert(mNitState == NitProceeding); |
1082 |
mNitState = NitComplete; |
1083 |
//!dcm! -- toss away 1xx to an info? |
1084 |
if (msg.header(h_StatusLine).statusCode() >= 300) |
1085 |
{ |
1086 |
handler->onInfoFailure(getSessionHandle(), msg); |
1087 |
} |
1088 |
else if (msg.header(h_StatusLine).statusCode() >= 200) |
1089 |
{ |
1090 |
handler->onInfoSuccess(getSessionHandle(), msg); |
1091 |
} |
1092 |
} |
1093 |
} |
1094 |
|
1095 |
void |
1096 |
InviteSession::acceptInfo(int statusCode) |
1097 |
{ |
1098 |
if (statusCode / 100 != 2) |
1099 |
{ |
1100 |
throw UsageUseException("Must accept with a 2xx", __FILE__, __LINE__); |
1101 |
} |
1102 |
|
1103 |
mLastNitResponse.header(h_StatusLine).statusCode() = statusCode; |
1104 |
send(mLastNitResponse); |
1105 |
} |
1106 |
|
1107 |
void |
1108 |
InviteSession::rejectInfo(int statusCode) |
1109 |
{ |
1110 |
if (statusCode < 400) |
1111 |
{ |
1112 |
throw UsageUseException("Must reject with a >= 4xx", __FILE__, __LINE__); |
1113 |
} |
1114 |
mLastNitResponse.header(h_StatusLine).statusCode() = statusCode; |
1115 |
send(mLastNitResponse); |
1116 |
} |
1117 |
|
1118 |
void |
1119 |
InviteSession::startRetransmit200Timer() |
1120 |
{ |
1121 |
mCurrentRetransmit200 = Timer::T1; |
1122 |
int seq = mLastSessionModification.header(h_CSeq).sequence(); |
1123 |
mDum.addTimerMs(DumTimeout::Retransmit200, mCurrentRetransmit200, getBaseHandle(), seq); |
1124 |
mDum.addTimerMs(DumTimeout::WaitForAck, Timer::TH, getBaseHandle(), seq); |
1125 |
} |
1126 |
|
1127 |
void |
1128 |
InviteSession::start491Timer() |
1129 |
{ |
1130 |
int seq = mLastSessionModification.header(h_CSeq).sequence(); |
1131 |
int timer = Random::getRandom() % 4000; |
1132 |
mDum.addTimerMs(DumTimeout::Glare, timer, getBaseHandle(), seq); |
1133 |
} |
1134 |
|
1135 |
void |
1136 |
InviteSession::setSessionTimerHeaders(SipMessage &msg) |
1137 |
{ |
1138 |
if(mSessionInterval >= 90) // If mSessionInterval is 0 then SessionTimers are considered disabled |
1139 |
{ |
1140 |
msg.header(h_SessionExpires).value() = mSessionInterval; |
1141 |
if(msg.isRequest()) |
1142 |
{ |
1143 |
msg.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresher ? "uac" : "uas"); |
1144 |
} |
1145 |
else |
1146 |
{ |
1147 |
msg.header(h_SessionExpires).param(p_refresher) = Data(mSessionRefresher ? "uas" : "uac"); |
1148 |
} |
1149 |
msg.header(h_MinSE).value() = mMinSE; |
1150 |
} |
1151 |
else |
1152 |
{ |
1153 |
msg.remove(h_SessionExpires); |
1154 |
msg.remove(h_MinSE); |
1155 |
} |
1156 |
} |
1157 |
|
1158 |
void |
1159 |
InviteSession::sessionRefresh() |
1160 |
{ |
1161 |
if (updateMethodSupported()) |
1162 |
{ |
1163 |
transition(SentUpdate); |
1164 |
mDialog.makeRequest(mLastSessionModification, UPDATE); |
1165 |
mLastSessionModification.releaseContents(); // Don't send SDP |
1166 |
} |
1167 |
else |
1168 |
{ |
1169 |
transition(SentReinvite); |
1170 |
mDialog.makeRequest(mLastSessionModification, INVITE); |
1171 |
InviteSession::setSdp(mLastSessionModification, *mCurrentLocalSdp); |
1172 |
mProposedLocalSdp = InviteSession::makeSdp(*mCurrentLocalSdp); |
1173 |
} |
1174 |
setSessionTimerHeaders(mLastSessionModification); |
1175 |
|
1176 |
InfoLog (<< "sessionRefresh: Sending " << mLastSessionModification.brief()); |
1177 |
mDialog.send(mLastSessionModification); |
1178 |
} |
1179 |
|
1180 |
void |
1181 |
InviteSession::setSessionTimerPreferences() |
1182 |
{ |
1183 |
mSessionInterval = mDialog.mDialogSet.getUserProfile()->getDefaultSessionTime(); // Used only if remote doesn't request a time |
1184 |
if(mSessionInterval != 0) |
1185 |
{ |
1186 |
// If session timers are no disabled then ensure interval is greater than or equal to MinSE |
1187 |
mSessionInterval = resipMax(mMinSE, mSessionInterval); |
1188 |
} |
1189 |
switch(mDialog.mDialogSet.getUserProfile()->getDefaultSessionTimerMode()) |
1190 |
{ |
1191 |
case Profile::PreferLocalRefreshes: |
1192 |
mSessionRefresher = true; // Default refresher is Local |
1193 |
break; |
1194 |
case Profile::PreferRemoteRefreshes: |
1195 |
mSessionRefresher = false; // Default refresher is Remote |
1196 |
break; |
1197 |
case Profile::PreferUASRefreshes: |
1198 |
mSessionRefresher = dynamic_cast<ServerInviteSession*>(this) != NULL; // Default refresher is UAS (for the session) - callee |
1199 |
break; |
1200 |
case Profile::PreferUACRefreshes: |
1201 |
mSessionRefresher = dynamic_cast<ClientInviteSession*>(this) != NULL; // Default refresher is UAC (for the session) - caller |
1202 |
break; |
1203 |
} |
1204 |
} |
1205 |
|
1206 |
void |
1207 |
InviteSession::startSessionTimer() |
1208 |
{ |
1209 |
if(mSessionInterval >= 90) // 90 is the absolute minimum - RFC4028 |
1210 |
{ |
1211 |
// Check if we are the refresher |
1212 |
if(mSessionRefresher) |
1213 |
{ |
1214 |
// Start Session-Refresh Timer to mSessionInterval / 2 (recommended by RFC4028) |
1215 |
mDum.addTimer(DumTimeout::SessionRefresh, mSessionInterval / 2, getBaseHandle(), ++mSessionTimerSeq); |
1216 |
} |
1217 |
else |
1218 |
{ |
1219 |
// Start Session-Expiration Timer to mSessionInterval - BYE should be sent a minimum of 32 and one third of the SessionInterval, seconds before the session expires (recommended by RFC4028) |
1220 |
mDum.addTimer(DumTimeout::SessionExpiration, mSessionInterval - resipMin(32,mSessionInterval/3), getBaseHandle(), ++mSessionTimerSeq); |
1221 |
} |
1222 |
} |
1223 |
else // Session Interval less than 90 - consider timers disabled |
1224 |
{ |
1225 |
++mSessionTimerSeq; // increment seq, incase old timers are running and now session timers are disabled |
1226 |
} |
1227 |
} |
1228 |
|
1229 |
void |
1230 |
InviteSession::handleSessionTimerResponse(const SipMessage& msg) |
1231 |
{ |
1232 |
assert(msg.header(h_CSeq).method() == INVITE || msg.header(h_CSeq).method() == UPDATE); |
1233 |
|
1234 |
// If session timers are locally supported then handle response |
1235 |
if(mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::Timer))) |
1236 |
{ |
1237 |
setSessionTimerPreferences(); |
1238 |
|
1239 |
if(msg.exists(h_Requires) && msg.header(h_Requires).find(Token(Symbols::Timer)) |
1240 |
&& !msg.exists(h_SessionExpires)) |
1241 |
{ |
1242 |
// If no Session Expires in response and Requires header is present then session timer is to be 'turned off' |
1243 |
mSessionInterval = 0; |
1244 |
} |
1245 |
// Process Session Timer headers |
1246 |
else if(msg.exists(h_SessionExpires)) |
1247 |
{ |
1248 |
mSessionInterval = msg.header(h_SessionExpires).value(); |
1249 |
if(msg.header(h_SessionExpires).exists(p_refresher)) |
1250 |
{ |
1251 |
// Remote end specified refresher preference |
1252 |
mSessionRefresher = (msg.header(h_SessionExpires).param(p_refresher) == Data("uac")); |
1253 |
} |
1254 |
} |
1255 |
else |
1256 |
{ |
1257 |
// Note: If no Requires or Session-Expires, then UAS does not support Session Timers |
1258 |
// - we are free to use our SessionInterval settings (set above as a default) |
1259 |
// If far end doesn't support then refresher must be local |
1260 |
mSessionRefresher = true; |
1261 |
} |
1262 |
|
1263 |
// Update MinSE if specified and longer than current value |
1264 |
if(msg.exists(h_MinSE)) |
1265 |
{ |
1266 |
mMinSE = resipMax(mMinSE, msg.header(h_MinSE).value()); |
1267 |
} |
1268 |
|
1269 |
startSessionTimer(); |
1270 |
} |
1271 |
} |
1272 |
|
1273 |
void |
1274 |
InviteSession::handleSessionTimerRequest(SipMessage &response, const SipMessage& request) |
1275 |
{ |
1276 |
assert(request.header(h_CSeq).method() == INVITE || request.header(h_CSeq).method() == UPDATE); |
1277 |
|
1278 |
// If session timers are locally supported then add necessary headers to response |
1279 |
if(mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::Timer))) |
1280 |
{ |
1281 |
setSessionTimerPreferences(); |
1282 |
|
1283 |
// Check if far-end supports |
1284 |
bool farEndSupportsTimer = false; |
1285 |
if(request.exists(h_Supporteds) && request.header(h_Supporteds).find(Token(Symbols::Timer))) |
1286 |
{ |
1287 |
farEndSupportsTimer = true; |
1288 |
if(request.exists(h_SessionExpires)) |
1289 |
{ |
1290 |
// Use Session Interval requested by remote - if none then use local settings |
1291 |
mSessionInterval = request.header(h_SessionExpires).value(); |
1292 |
if(request.header(h_SessionExpires).exists(p_refresher)) |
1293 |
{ |
1294 |
mSessionRefresher = (request.header(h_SessionExpires).param(p_refresher) == Data("uas")); |
1295 |
} |
1296 |
} |
1297 |
|
1298 |
// Update MinSE if specified and longer than current value |
1299 |
if(request.exists(h_MinSE)) |
1300 |
{ |
1301 |
mMinSE = resipMax(mMinSE, request.header(h_MinSE).value()); |
1302 |
} |
1303 |
} |
1304 |
else |
1305 |
{ |
1306 |
// If far end doesn't support then refresher must be local |
1307 |
mSessionRefresher = true; |
1308 |
} |
1309 |
|
1310 |
// Add Session-Expires to response if required |
1311 |
if(mSessionInterval >= 90) |
1312 |
{ |
1313 |
if(farEndSupportsTimer) |
1314 |
{ |
1315 |
// If far end supports session-timer then require it, if not already present |
1316 |
if(!response.header(h_Requires).find(Token(Symbols::Timer))) |
1317 |
{ |
1318 |
response.header(h_Requires).push_back(Token(Symbols::Timer)); |
1319 |
} |
1320 |
} |
1321 |
setSessionTimerHeaders(response); |
1322 |
} |
1323 |
|
1324 |
startSessionTimer(); |
1325 |
} |
1326 |
} |
1327 |
|
1328 |
Data |
1329 |
InviteSession::toData(State state) |
1330 |
{ |
1331 |
switch (state) |
1332 |
{ |
1333 |
case Undefined: |
1334 |
return "InviteSession::Undefined"; |
1335 |
case Connected: |
1336 |
return "InviteSession::Connected"; |
1337 |
case SentUpdate: |
1338 |
return "InviteSession::SentUpdate"; |
1339 |
case SentUpdateGlare: |
1340 |
return "InviteSession::SentUpdateGlare"; |
1341 |
case SentReinvite: |
1342 |
return "InviteSession::SentReinvite"; |
1343 |
case SentReinviteGlare: |
1344 |
return "InviteSession::SentReinviteGlare"; |
1345 |
case ReceivedUpdate: |
1346 |
return "InviteSession::ReceivedUpdate"; |
1347 |
case ReceivedReinvite: |
1348 |
return "InviteSession::ReceivedReinvite"; |
1349 |
case ReceivedReinviteNoOffer: |
1350 |
return "InviteSession::ReceivedReinviteNoOffer"; |
1351 |
case Answered: |
1352 |
return "InviteSession::Answered"; |
1353 |
case WaitingToOffer: |
1354 |
return "InviteSession::WaitingToOffer"; |
1355 |
case WaitingToTerminate: |
1356 |
return "InviteSession::WaitingToTerminate"; |
1357 |
case Terminated: |
1358 |
return "InviteSession::Terminated"; |
1359 |
|
1360 |
case UAC_Start: |
1361 |
return "UAC_Start"; |
1362 |
case UAS_Offer: |
1363 |
return "UAS_Offer"; |
1364 |
case UAS_OfferProvidedAnswer: |
1365 |
return "UAS_OfferProvidedAnswer"; |
1366 |
case UAS_EarlyOffer: |
1367 |
return "UAS_EarlyOffer"; |
1368 |
case UAS_EarlyProvidedAnswer: |
1369 |
return "UAS_EarlyProvidedAnswer"; |
1370 |
case UAS_NoOffer: |
1371 |
return "UAS_NoOffer"; |
1372 |
case UAS_ProvidedOffer: |
1373 |
return "UAS_ProvidedOffer"; |
1374 |
case UAS_EarlyNoOffer: |
1375 |
return "UAS_EarlyNoOffer"; |
1376 |
case UAS_EarlyProvidedOffer: |
1377 |
return "UAS_EarlyProvidedOffer"; |
1378 |
case UAS_Accepted: |
1379 |
return "UAS_Accepted"; |
1380 |
case UAS_WaitingToOffer: |
1381 |
return "UAS_WaitingToOffer"; |
1382 |
case UAS_AcceptedWaitingAnswer: |
1383 |
return "UAS_AcceptedWaitingAnswer"; |
1384 |
case UAC_Early: |
1385 |
return "UAC_Early"; |
1386 |
case UAC_EarlyWithOffer: |
1387 |
return "UAC_EarlyWithOffer"; |
1388 |
case UAC_EarlyWithAnswer: |
1389 |
return "UAC_EarlyWithAnswer"; |
1390 |
case UAC_Answered: |
1391 |
return "UAC_Answered"; |
1392 |
case UAC_SentUpdateEarly: |
1393 |
return "UAC_SentUpdateEarly"; |
1394 |
case UAC_SentUpdateConnected: |
1395 |
return "UAC_SentUpdateConnected"; |
1396 |
case UAC_ReceivedUpdateEarly: |
1397 |
return "UAC_ReceivedUpdateEarly"; |
1398 |
case UAC_SentAnswer: |
1399 |
return "UAC_SentAnswer"; |
1400 |
case UAC_QueuedUpdate: |
1401 |
return "UAC_QueuedUpdate"; |
1402 |
case UAC_Cancelled: |
1403 |
return "UAC_Cancelled"; |
1404 |
|
1405 |
case UAS_Start: |
1406 |
return "UAS_Start"; |
1407 |
case UAS_OfferReliable: |
1408 |
return "UAS_OfferReliable"; |
1409 |
case UAS_NoOfferReliable: |
1410 |
return "UAS_NoOfferReliable"; |
1411 |
case UAS_FirstSentOfferReliable: |
1412 |
return "UAS_FirstSentOfferReliable"; |
1413 |
case UAS_FirstEarlyReliable: |
1414 |
return "UAS_FirstEarlyReliable"; |
1415 |
case UAS_EarlyReliable: |
1416 |
return "UAS_EarlyReliable"; |
1417 |
case UAS_SentUpdate: |
1418 |
return "UAS_SentUpdate"; |
1419 |
case UAS_SentUpdateAccepted: |
1420 |
return "UAS_SentUpdateAccepted"; |
1421 |
case UAS_ReceivedUpdate: |
1422 |
return "UAS_ReceivedUpdate"; |
1423 |
case UAS_ReceivedUpdateWaitingAnswer: |
1424 |
return "UAS_ReceivedUpdateWaitingAnswer"; |
1425 |
case UAS_WaitingToTerminate: |
1426 |
return "UAS_WaitingToTerminate"; |
1427 |
case UAS_WaitingToHangup: |
1428 |
return "UAS_WaitingToHangup"; |
1429 |
} |
1430 |
assert(0); |
1431 |
return "Undefined"; |
1432 |
} |
1433 |
|
1434 |
|
1435 |
void |
1436 |
InviteSession::transition(State target) |
1437 |
{ |
1438 |
InfoLog (<< "Transition " << toData(mState) << " -> " << toData(target)); |
1439 |
mState = target; |
1440 |
} |
1441 |
|
1442 |
bool |
1443 |
InviteSession::isReliable(const SipMessage& msg) |
1444 |
{ |
1445 |
// Ensure supported both locally and remotely |
1446 |
return msg.exists(h_Supporteds) && msg.header(h_Supporteds).find(Token(Symbols::C100rel)) && |
1447 |
mDum.getMasterProfile()->getSupportedOptionTags().find(Token(Symbols::C100rel)); |
1448 |
} |
1449 |
|
1450 |
std::auto_ptr<SdpContents> |
1451 |
InviteSession::getSdp(const SipMessage& msg) |
1452 |
{ |
1453 |
// !jf! this code doesn't yet work - definitely if USE_SSL=false |
1454 |
//Helper::ContentsSecAttrs attrs = Helper::extractFromPkcs7(msg, mDum.getSecurity()); |
1455 |
//return std::auto_ptr<SdpContents>(dynamic_cast<SdpContents*>(attrs.mContents.get())); |
1456 |
SdpContents* sdp = dynamic_cast<SdpContents*>(msg.getContents()); |
1457 |
if (sdp) |
1458 |
{ |
1459 |
SdpContents* cloned = static_cast<SdpContents*>(sdp->clone()); |
1460 |
return std::auto_ptr<SdpContents>(cloned); |
1461 |
} |
1462 |
else |
1463 |
{ |
1464 |
static std::auto_ptr<SdpContents> empty; |
1465 |
return empty; |
1466 |
} |
1467 |
} |
1468 |
|
1469 |
std::auto_ptr<SdpContents> |
1470 |
InviteSession::makeSdp(const SdpContents& sdp) |
1471 |
{ |
1472 |
return std::auto_ptr<SdpContents>(static_cast<SdpContents*>(sdp.clone())); |
1473 |
} |
1474 |
|
1475 |
void |
1476 |
InviteSession::setSdp(SipMessage& msg, const SdpContents& sdp) |
1477 |
{ |
1478 |
// !jf! should deal with multipart here |
1479 |
|
1480 |
// This will clone the sdp since the InviteSession also wants to keep its own |
1481 |
// copy of the sdp around for the application to access |
1482 |
msg.setContents(&sdp); |
1483 |
} |
1484 |
|
1485 |
InviteSession::Event |
1486 |
InviteSession::toEvent(const SipMessage& msg, const SdpContents* sdp) |
1487 |
{ |
1488 |
MethodTypes method = msg.header(h_CSeq).method(); |
1489 |
int code = msg.isResponse() ? msg.header(h_StatusLine).statusCode() : 0; |
1490 |
bool reliable = isReliable(msg); |
1491 |
bool sentOffer = mProposedLocalSdp.get(); |
1492 |
|
1493 |
if (code == 481 || code == 408) |
1494 |
{ |
1495 |
return OnGeneralFailure; |
1496 |
} |
1497 |
else if (code >= 300 && code <= 399) |
1498 |
{ |
1499 |
return OnRedirect; |
1500 |
} |
1501 |
else if (method == INVITE && code == 0) |
1502 |
{ |
1503 |
if (sdp) |
1504 |
{ |
1505 |
if (reliable) |
1506 |
{ |
1507 |
return OnInviteReliableOffer; |
1508 |
} |
1509 |
else |
1510 |
{ |
1511 |
return OnInviteOffer; |
1512 |
} |
1513 |
} |
1514 |
else |
1515 |
{ |
1516 |
if (reliable) |
1517 |
{ |
1518 |
return OnInviteReliable; |
1519 |
} |
1520 |
else |
1521 |
{ |
1522 |
return OnInvite; |
1523 |
} |
1524 |
} |
1525 |
} |
1526 |
else if (method == INVITE && code > 100 && code < 200) // !kh! 100 is handled by transaction layer. |
1527 |
{ |
1528 |
if (reliable) |
1529 |
{ |
1530 |
if (sdp) |
1531 |
{ |
1532 |
if (sentOffer) |
1533 |
{ |
1534 |
return On1xxAnswer; |
1535 |
} |
1536 |
else |
1537 |
{ |
1538 |
return On1xxOffer; |
1539 |
} |
1540 |
} |
1541 |
else |
1542 |
{ |
1543 |
return On1xx; |
1544 |
} |
1545 |
} |
1546 |
else |
1547 |
{ |
1548 |
if (sdp) |
1549 |
{ |
1550 |
return On1xxEarly; |
1551 |
} |
1552 |
else |
1553 |
{ |
1554 |
return On1xx; |
1555 |
} |
1556 |
} |
1557 |
} |
1558 |
else if (method == INVITE && code >= 200 && code < 300) |
1559 |
{ |
1560 |
if (sdp) |
1561 |
{ |
1562 |
if (sentOffer) |
1563 |
{ |
1564 |
return On2xxAnswer; |
1565 |
} |
1566 |
else |
1567 |
{ |
1568 |
return On2xxOffer; |
1569 |
} |
1570 |
} |
1571 |
else |
1572 |
{ |
1573 |
return On2xx; |
1574 |
} |
1575 |
} |
1576 |
else if (method == INVITE && code == 422) |
1577 |
{ |
1578 |
return On422Invite; |
1579 |
} |
1580 |
else if (method == INVITE && code == 487) |
1581 |
{ |
1582 |
return On487Invite; |
1583 |
} |
1584 |
else if (method == INVITE && code == 489) |
1585 |
{ |
1586 |
return On489Invite; |
1587 |
} |
1588 |
else if (method == INVITE && code == 491) |
1589 |
{ |
1590 |
return On491Invite; |
1591 |
} |
1592 |
else if (method == INVITE && code >= 400) |
1593 |
{ |
1594 |
return OnInviteFailure; |
1595 |
} |
1596 |
else if (method == ACK) |
1597 |
{ |
1598 |
if (sdp) |
1599 |
{ |
1600 |
return OnAckAnswer; |
1601 |
} |
1602 |
else |
1603 |
{ |
1604 |
return OnAck; |
1605 |
} |
1606 |
} |
1607 |
else if (method == CANCEL && code == 0) |
1608 |
{ |
1609 |
return OnCancel; |
1610 |
} |
1611 |
else if (method == CANCEL && code / 200 == 1) |
1612 |
{ |
1613 |
return On200Cancel; |
1614 |
} |
1615 |
else if (method == CANCEL && code >= 400) |
1616 |
{ |
1617 |
return OnCancelFailure; |
1618 |
} |
1619 |
else if (method == BYE && code == 0) |
1620 |
{ |
1621 |
return OnBye; |
1622 |
} |
1623 |
else if (method == BYE && code / 200 == 1) |
1624 |
{ |
1625 |
return On200Bye; |
1626 |
} |
1627 |
else if (method == PRACK && code == 0) |
1628 |
{ |
1629 |
return OnPrack; |
1630 |
} |
1631 |
else if (method == PRACK && code / 200 == 1) |
1632 |
{ |
1633 |
return On200Prack; |
1634 |
} |
1635 |
else if (method == UPDATE && code == 0) |
1636 |
{ |
1637 |
if (sdp) |
1638 |
{ |
1639 |
return OnUpdateOffer; |
1640 |
} |
1641 |
else |
1642 |
{ |
1643 |
return OnUpdate; |
1644 |
} |
1645 |
} |
1646 |
else if (method == UPDATE && code / 200 == 1) |
1647 |
{ |
1648 |
return On200Update; |
1649 |
} |
1650 |
else if (method == UPDATE && code == 422) |
1651 |
{ |
1652 |
return On422Update; |
1653 |
} |
1654 |
else if (method == UPDATE && code == 489) |
1655 |
{ |
1656 |
return On489Update; |
1657 |
} |
1658 |
else if (method == UPDATE && code == 491) |
1659 |
{ |
1660 |
return On491Update; |
1661 |
} |
1662 |
else if (method == UPDATE && code >= 400) |
1663 |
{ |
1664 |
return OnUpdateRejected; |
1665 |
} |
1666 |
else |
1667 |
{ |
1668 |
assert(0); |
1669 |
return Unknown; |
1670 |
} |
1671 |
} |
1672 |
|
1673 |
void InviteSession::sendAck(const SdpContents *sdp) |
1674 |
{ |
1675 |
SipMessage ack; |
1676 |
mDialog.makeRequest(ack, ACK); |
1677 |
if(sdp != 0) |
1678 |
{ |
1679 |
setSdp(ack, *sdp); |
1680 |
} |
1681 |
InfoLog (<< "Sending " << ack.brief()); |
1682 |
mDialog.send(ack); |
1683 |
} |
1684 |
|
1685 |
void InviteSession::sendBye() |
1686 |
{ |
1687 |
SipMessage bye; |
1688 |
mDialog.makeRequest(bye, BYE); |
1689 |
InfoLog (<< "Sending " << bye.brief()); |
1690 |
mDialog.send(bye); |
1691 |
} |
1692 |
|
1693 |
|
1694 |
/* ==================================================================== |
1695 |
* The Vovida Software License, Version 1.0 |
1696 |
* |
1697 |
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. |
1698 |
* |
1699 |
* Redistribution and use in source and binary forms, with or without |
1700 |
* modification, are permitted provided that the following conditions |
1701 |
* are met: |
1702 |
* |
1703 |
* 1. Redistributions of source code must retain the above copyright |
1704 |
* notice, this list of conditions and the following disclaimer. |
1705 |
* |
1706 |
* 2. Redistributions in binary form must reproduce the above copyright |
1707 |
* notice, this list of conditions and the following disclaimer in |
1708 |
* the documentation and/or other materials provided with the |
1709 |
|
1710 |
* distribution. |
1711 |
* |
1712 |
* 3. The names "VOCAL", "Vovida Open Communication Application Library", |
1713 |
* and "Vovida Open Communication Application Library (VOCAL)" must |
1714 |
* not be used to endorse or promote products derived from this |
1715 |
* software without prior written permission. For written |
1716 |
* permission, please contact vocal@vovida.org. |
1717 |
* |
1718 |
* 4. Products derived from this software may not be called "VOCAL", nor |
1719 |
* may "VOCAL" appear in their name, without prior written |
1720 |
* permission of Vovida Networks, Inc. |
1721 |
* |
1722 |
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
1723 |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
1724 |
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
1725 |
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
1726 |
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
1727 |
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
1728 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
1729 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
1730 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
1731 |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1732 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
1733 |
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
1734 |
* DAMAGE. |
1735 |
* |
1736 |
* ==================================================================== |
1737 |
* |
1738 |
* This software consists of voluntary contributions made by Vovida |
1739 |
* Networks, Inc. and many individuals on behalf of Vovida Networks, |
1740 |
* Inc. For more information on Vovida Networks, Inc., please see |
1741 |
* <http://www.vovida.org/>. |
1742 |
* |
1743 |
*/ |