reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
DialogSet.cxx
Go to the documentation of this file.
1
12#include "resip/dum/Dialog.hxx"
24#include "rutil/Logger.hxx"
25#include "rutil/Inserter.hxx"
27
28#include <utility>
29
30#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
31
32using namespace resip;
33using namespace std;
34
35// UAC
37 mMergeKey(),
38 mDialogs(),
39 mCreator(creator),
40 mId(*creator->getLastRequest()),
41 mDum(dum),
42 mAppDialogSet(0),
43 mState(Initial),
44 mClientRegistration(0),
45 mServerRegistration(0),
46 mClientPublication(0),
47 mClientOutOfDialogRequests(),
48 mServerOutOfDialogRequest(0),
49 mClientPagerMessage(0),
50 mServerPagerMessage(0)
51{
53 resip_assert(!creator->getLastRequest()->isExternal());
54 DebugLog ( << " ************* Created DialogSet(UAC) -- " << mId << "*************" );
55}
56
57// UAS
59 mMergeKey(request, dum.getMasterProfile()->checkReqUriInMergeDetectionEnabled()),
60 mDialogs(),
61 mCreator(0),
62 mId(request),
63 mDum(dum),
64 mAppDialogSet(0),
65 mState(Established),
66 mClientRegistration(0),
67 mServerRegistration(0),
68 mClientPublication(0),
69 mClientOutOfDialogRequests(),
70 mServerOutOfDialogRequest(0),
71 mClientPagerMessage(0),
72 mServerPagerMessage(0)
73{
74 resip_assert(request.isRequest());
75 resip_assert(request.isExternal());
76 mDum.mMergedRequests.insert(mMergeKey);
77 if (request.header(h_RequestLine).method() == INVITE)
78 {
79 mCancelKey = request.getTransactionId();
80
81 auto it = mDum.mCancelMap.find(mCancelKey);
82 if (it != mDum.mCancelMap.end())
83 {
84 WarningLog ( << "An endpoint is using the same tid in multiple INVITE requests, ability to match CANCEL requests correctly may be comprimised, tid=" << mCancelKey );
85 it->second = this;
86 }
87 else
88 {
89 mDum.mCancelMap[mCancelKey] = this;
90 }
91 }
92 DebugLog ( << " ************* Created DialogSet(UAS) *************: " << mId);
93}
94
96{
97 if (mDum.mClientAuthManager)
98 {
99 mDum.mClientAuthManager->dialogSetDestroyed(getId());
100 }
101
102 if (mMergeKey != MergedRequestKey::Empty)
103 {
104 mDum.requestMergedRequestRemoval(mMergeKey);
105 }
106
107 if (!mCancelKey.empty())
108 {
109 mDum.mCancelMap.erase(mCancelKey);
110 }
111
112 delete mCreator;
113 while(!mDialogs.empty())
114 {
115 delete mDialogs.begin()->second;
116 }
117
118 delete mClientRegistration;
119 delete mServerRegistration;
120 delete mClientPublication;
121 delete mServerOutOfDialogRequest;
122 delete mClientPagerMessage;
123 delete mServerPagerMessage;
124
125 while (!mClientOutOfDialogRequests.empty())
126 {
127 delete *mClientOutOfDialogRequests.begin();
128 }
129
130 DebugLog ( << " ********** DialogSet::~DialogSet: " << mId << "*************" );
131 // !dcm! -- very delicate code, change the order things go horribly wrong
132
133 mDum.removeDialogSet(this->getId());
134 if (mAppDialogSet)
135 {
136 mAppDialogSet->destroy();
137 }
138}
139
140void DialogSet::possiblyDie()
141{
142 if(mState != Destroying &&
143 mDialogs.empty() &&
144 // The following check ensures we are not a UAC DialogSet in the Initial or
145 // ReceivedProvisional states.
146 // .slg. this check fixes a case where we might receive a short term usuage
147 // request (such as OPTIONS) in the same dialogset as a UAC dialogset
148 // for which we have not created any Dialogs yet - in this case
149 // we don't want the dialogset to die, since the UAC usage is not complete.
150 (getCreator() == 0 || (mState != Initial && mState != ReceivedProvisional)) &&
151 mClientOutOfDialogRequests.empty() &&
152 !(mClientPublication ||
153 mServerOutOfDialogRequest ||
154 mClientPagerMessage ||
155 mServerPagerMessage ||
156 mClientRegistration ||
157 mServerRegistration))
158 {
159 mState = Destroying;
160 mDum.destroy(this);
161 }
162}
163
166{
167 return mId;
168}
169
170void
172{
173 mDialogs[dialog->getId()] = dialog;
174}
175
177DialogSet::getCreator() const noexcept
178{
179 return mCreator;
180}
181
182Dialog*
183DialogSet::findDialog(const SipMessage& msg)
184{
185 if (msg.isResponse() && msg.header(h_StatusLine).statusCode() == 100)
186 {
187 return 0;
188 }
189 return findDialog(DialogId(msg));
190}
191
192bool
193DialogSet::empty() const noexcept
194{
195 return mDialogs.empty();
196}
197
198bool
199DialogSet::handledByAuthOrRedirect(const SipMessage& msg)
200{
201 if (msg.isResponse() && !(mState == Terminating ||
202 mState == WaitingToEnd ||
203 mState == Destroying ||
204 mState == Cancelling))
205 {
206 // !dcm! -- multiple usage grief...only one of each method type allowed
207 if (getCreator() &&
208 msg.header(h_CSeq) == getCreator()->getLastRequest()->header(h_CSeq))
209 {
210 if (mDum.mClientAuthManager)
211 {
212 if (mDum.mClientAuthManager->handle(*getUserProfile(), *getCreator()->getLastRequest(), msg))
213 {
214 // Note: ClientAuthManager->handle will end up incrementing the CSeq sequence of getLastRequest
215 DebugLog( << "about to re-send request with digest credentials" );
216 StackLog( << getCreator()->getLastRequest() );
217
218 mDum.send(getCreator()->getLastRequest());
219 return true;
220 }
221 }
222 // !dcm! -- need to protect against 3xx highjacking a dialogset which
223 //has a fully established dialog. also could case strange behaviour
224 //by sending 401/407 at the wrong time.
225 if (mDum.mRedirectManager && mState != Established) // !slg! for now don't handle redirect in established dialogs - alternatively we could treat as a target refresh (using 1st Contact) and reissue request
226 {
227 if (mDum.mRedirectManager->handle(*this, *getCreator()->getLastRequest(), msg))
228 {
229 //terminating existing dialogs(branches) as this is a final
230 //response--?dcm?--merge w/ forking logic somehow?
231 // !dcm! -- really, really horrible. Should make a don't die
232 //scoped guard
233 mState = Initial;
234 for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end();)
235 {
236 (it++)->second->redirected(msg);
237 }
238
239 if (mDialogs.empty())
240 {
241 if (mDum.mDialogEventStateManager)
242 {
243 mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Rejected);
244 }
245 }
246
247 InfoLog( << "about to re-send request to redirect destination" );
248 DebugLog( << getCreator()->getLastRequest() );
249
250 mDum.send(getCreator()->getLastRequest());
251 return true;
252 }
253
254 // Check if a 422 response to initial Invite (RFC4028)
255 if(msg.header(h_StatusLine).statusCode() == 422 && msg.exists(h_MinSE))
256 {
257 // Change interval to min from 422 response
258 getCreator()->getLastRequest()->header(h_SessionExpires).value() = msg.header(h_MinSE).value();
259 getCreator()->getLastRequest()->header(h_MinSE).value() = msg.header(h_MinSE).value();
260 getCreator()->getLastRequest()->header(h_CSeq).sequence()++;
261
262 InfoLog( << "about to re-send request with new session expiration time" );
263 DebugLog( << getCreator()->getLastRequest() );
264
265 mDum.send(getCreator()->getLastRequest());
266 return true;
267 }
268 }
269 }
270 }
271 return false;
272}
273
274
275void
277{
278 if(!mAppDialogSet)
279 {
280 // !bwc! There are conditions where reuse of the AppDialogSet will cause
281 // us to hit this code. This is because the teardown of DialogSets is not
282 // atomic, causing the DialogSet to hang around for a short time after it
283 // has given up its AppDialogSet. Also, if we have multiple Usages in
284 // this DialogSet, one of the Usages may decide to re-establish itself in
285 // a new Dialog, and take the AppDialogSet with it, leaving all the others
286 // high and dry. This is a design issue that will take some real effort to
287 // fix properly. This is a band-aid for now.
288 // TODO fix this properly
289 if(msg.isRequest())
290 {
291 if(msg.method() != ACK)
292 {
293 SipMessage err;
294 Helper::makeResponse(err, msg, 500, "DialogSet: My AppDialogSet is "
295 "missing!");
296 mDum.sendResponse(err);
297 }
298 }
299 else
300 {
301 ErrLog(<<"Response came in, but no AppDialogSet! Dropping this is very "
302 "likely to cause leaks, but continuing to process it is "
303 "likely to cause a core. Taking the lesser of two evils...");
304 }
305 return;
306 }
307
308 resip_assert(msg.isRequest() || msg.isResponse());
309
310 if (mState == WaitingToEnd)
311 {
312 resip_assert(mDialogs.empty());
313 if (msg.isResponse())
314 {
315 MethodTypes method;
316 if (getCreator())
317 {
318 method = getCreator()->getLastRequest()->method();
319 }
320 else
321 {
322 method = msg.method();
323 }
324 int code = msg.header(h_StatusLine).statusCode();
325 switch(method)
326 {
327 case INVITE:
328 if (code / 100 == 1)
329 {
330 mState = ReceivedProvisional;
331 end();
332 }
333 else if (code / 100 == 2)
334 {
335 Dialog dialog(mDum, msg, *this);
336
337 auto ack = std::make_shared<SipMessage>();
338 dialog.makeRequest(*ack, ACK);
339 ack->header(h_CSeq).sequence() = msg.header(h_CSeq).sequence();
340 dialog.send(ack);
341
342 auto bye = std::make_shared<SipMessage>();
343 dialog.makeRequest(*bye, BYE);
344 addEndReasonToMessage(*bye);
345 dialog.send(bye);
346
347 if (mDum.mDialogEventStateManager)
348 {
349 mDum.mDialogEventStateManager->onTerminated(dialog, *bye, InviteSessionHandler::LocalBye);
350 }
351 // Note: Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
352 }
353 else
354 {
355 if (mDum.mDialogEventStateManager)
356 {
357 mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Rejected);
358 }
359 mState = Destroying;
360 mDum.destroy(this);
361 }
362 break;
363 case SUBSCRIBE:
364 if (code / 100 == 1)
365 {
366 // do nothing - wait for final response
367 }
368 else if ((code / 100 == 2) && getCreator())
369 {
370 Dialog dialog(mDum, msg, *this);
371
372 auto unsubscribe = std::make_shared<SipMessage>(*getCreator()->getLastRequest()); // create message from initial request so we get proper headers
373 dialog.makeRequest(*unsubscribe, SUBSCRIBE);
374 unsubscribe->header(h_Expires).value() = 0;
375 dialog.send(unsubscribe);
376
377 // Note: Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
378 }
379 else
380 {
381 mState = Destroying;
382 mDum.destroy(this);
383 }
384 break;
385 case PUBLISH:
386 if (code / 100 == 1)
387 {
388 // do nothing - wait for final response
389 }
390 else if ((code / 100 == 2) && getCreator())
391 {
392 Dialog dialog(mDum, msg, *this);
393
394 auto unpublish = std::make_shared<SipMessage>(*getCreator()->getLastRequest()); // create message from initial request so we get proper headers
395 dialog.makeRequest(*unpublish, PUBLISH);
396 unpublish->header(h_Expires).value() = 0;
397 dialog.send(unpublish);
398
399 // Note: Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
400 }
401 else
402 {
403 mState = Destroying;
404 mDum.destroy(this);
405 }
406 break;
407 // ?slg? shouldn't we handle register, ood and refer here too?
408 default:
409 mState = Destroying;
410 mDum.destroy(this);
411 break;
412 }
413 }
414 else
415 {
416 auto response = std::make_shared<SipMessage>();
417 mDum.makeResponse(*response, msg, 481);
418 mDum.send(response);
419 }
420 return;
421 }
422 else if(mState == Cancelling)
423 {
424 resip_assert(mDialogs.empty());
425 if (msg.isResponse())
426 {
427 MethodTypes method;
428 if (getCreator())
429 {
430 method = getCreator()->getLastRequest()->method();
431 }
432 else
433 {
434 method = msg.method();
435 }
436 int code = msg.header(h_StatusLine).statusCode();
437 if(method == INVITE)
438 {
439 if (code / 100 == 1)
440 {
441 // do nothing - wait for final response
442 }
443 // 200/Inv crossing CANCEL case
444 else if (code / 100 == 2)
445 {
446 Dialog dialog(mDum, msg, *this);
447
448 auto ack = std::make_shared<SipMessage>();
449 dialog.makeRequest(*ack, ACK);
450 ack->header(h_CSeq).sequence() = msg.header(h_CSeq).sequence();
451 dialog.send(ack);
452
453 auto bye = std::make_shared<SipMessage>();
454 dialog.makeRequest(*bye, BYE);
455 addEndReasonToMessage(*bye);
456 dialog.send(bye);
457
458 // Note: Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
459 }
460 else
461 {
462 mState = Destroying;
463 mDum.destroy(this);
464 }
465 }
466 }
467 else // is a request
468 {
469 auto response = std::make_shared<SipMessage>();
470 mDum.makeResponse(*response, msg, 481);
471 mDum.send(response);
472 }
473 return;
474 }
475
476 if (handledByAuthOrRedirect(msg))
477 {
478 return;
479 }
480
481 Dialog* dialog = 0;
482 if (!(msg.isResponse() && msg.header(h_StatusLine).statusCode() == 100)) // Don't look for dialog if msg is a 100 response
483 {
484 DialogMap::iterator i = mDialogs.find(DialogId(msg));
485 if (i != mDialogs.end())
486 {
487 dialog = i->second;
488 }
489 }
490 if (dialog)
491 {
492 if(dialog->isDestroying())
493 {
494 if( msg.isRequest() )
495 {
496 StackLog (<< "Matching dialog is destroying, sending 481 " << endl << msg);
497 auto response = std::make_shared<SipMessage>();
498 mDum.makeResponse(*response, msg, 481);
499 mDum.send(response);
500 }
501 else
502 {
503 StackLog (<< "Matching dialog is destroying, dropping response message " << endl << msg);
504 }
505 return;
506 }
507 else
508 {
509 DebugLog (<< "Found matching dialog " << *dialog << " for " << endl << endl << msg);
510 }
511 }
512 else
513 {
514 StackLog (<< "No matching dialog for " << endl << endl << msg);
515 }
516
517 if (msg.isRequest())
518 {
519 const SipMessage& request = msg;
520 switch (request.header(h_CSeq).method())
521 {
522 case INVITE:
523 case SUBSCRIBE:
524 // If this is an external request without an existing Dialog, and it is in a UAC DialogSet of the same method,
525 // then something strange is going on (ie: We started with a UAC INVITE, now we have an unmatched inbound INVITE
526 // in the same DialogSet). Reject the new request.
527 if (request.isExternal() && // External request (UAS)
528 !dialog && // No existing dialog for this request
529 getCreator() && // DialogSet was created as UAC DialogSet
530 getCreator()->getLastRequest()->method() == request.method()) // Initial creation SIP method matches new request
531 {
532 auto response = std::make_shared<SipMessage>();
533 mDum.makeResponse(*response, msg, 400, "New dialog UAS request received in UAC DialogSet");
534 mDum.send(response);
535 return;
536 }
537 break; //dialog creating/handled by dialog
538
539 case CANCEL: //cancel needs work
540 break;
541
542 case BYE:
543 case INFO:
544 case ACK:
545 case PRACK:
546 case UPDATE:
547 if(!dialog)
548 {
549 auto response = std::make_shared<SipMessage>();
550 mDum.makeResponse(*response, msg, 481);
551 mDum.send(response);
552 return;
553 }
554 break;
555
556 case REFER:
557 if (dialog)
558 {
559 DebugLog(<< "in dialog refer request");
560 break; // in dialog
561 }
562 else if (request.header(h_To).exists(p_tag))
563 {
564 // We have a To tag, but don't have an existing dialog, we shouldn't be creating a new one, reject
565 auto response = std::make_shared<SipMessage>();
566 mDum.makeResponse(*response, msg, 481);
567 mDum.send(response);
568 return;
569 }
570 // We have an out-of-dialog request, check if it needs an implied subscription dialog
571 else if((request.exists(h_ReferSub) &&
572 request.header(h_ReferSub).isWellFormed() &&
573 request.header(h_ReferSub).value()=="false") ||
574 (request.exists(h_Requires) &&
575 request.header(h_Requires).find(Token("norefersub"))))// out of dialog & noReferSub=true
576 {
577 // No dialog needed here
578 DebugLog(<< "out of dialog refer request with norefersub");
579 resip_assert(mServerOutOfDialogRequest == 0);
580 mServerOutOfDialogRequest = makeServerOutOfDialog(request);
581 mServerOutOfDialogRequest->dispatch(request);
582 return;
583 }
584 else
585 {
586 DebugLog(<< "out of dialog refer request with refer sub");
587 break; // dialog creating
588 }
589 break;
590
591 case NOTIFY:
592 if (dialog)
593 {
594 break; //dialog creating/handled by dialog
595 }
596 else if (request.header(h_To).exists(p_tag))
597 {
598 // We have a To tag, but don't have an existing dialog, we shouldn't be creating a new one, reject
599 auto response = std::make_shared<SipMessage>();
600 mDum.makeResponse(*response, msg, 481);
601 mDum.send(response);
602 return;
603 }
604 else // no to tag - unsolicited notify
605 {
606 // unsolicited - not allowed but commonly implemented
607 // by large companies with a bridge as their logo
608 resip_assert(mServerOutOfDialogRequest == 0);
609 mServerOutOfDialogRequest = makeServerOutOfDialog(request);
610 mServerOutOfDialogRequest->dispatch(request);
611 return;
612 }
613 break;
614
615 case PUBLISH:
616 resip_assert(false); // handled in DialogUsageManager
617 return;
618
619 case REGISTER:
620 // !jf! move this to DialogUsageManager
621 if (mServerRegistration == 0)
622 {
623 mServerRegistration = makeServerRegistration(request);
624 }
625 mServerRegistration->dispatch(request);
626 return;
627
628 case MESSAGE:
629 // !jf! move this to DialogUsageManager
630 if(!dialog)
631 {
632 mServerPagerMessage = makeServerPagerMessage(request);
633 mServerPagerMessage->dispatch(request);
634 return;
635 }
636 break;
637
638 case OPTIONS:
639 if (dialog)
640 {
641 DebugLog(<< "in dialog options request");
642 break;
643 }
644 // fallthrough
645
646 default:
647 // !jf! move this to DialogUsageManager
648 DebugLog ( << "In DialogSet::dispatch, default(ServerOutOfDialogRequest), msg: " << msg );
649 // only can be one ServerOutOfDialogReq at a time
650 resip_assert(mServerOutOfDialogRequest == 0);
651 mServerOutOfDialogRequest = makeServerOutOfDialog(request);
652 mServerOutOfDialogRequest->dispatch(request);
653 return;
654 }
655 }
656 else // the message is a response
657 {
658 const SipMessage& response = msg;
659
660 int code = msg.header(h_StatusLine).statusCode();
661 if (code == 423
662 && msg.header(h_CSeq).method() == SUBSCRIBE
663 && msg.exists(h_MinExpires)
664 && getCreator()
665 && msg.header(h_CSeq) == getCreator()->getLastRequest()->header(h_CSeq))
666 {
667 getCreator()->getLastRequest()->header(h_CSeq).sequence()++;
668 getCreator()->getLastRequest()->header(h_Expires).value() = msg.header(h_MinExpires).value();
669 DebugLog( << "Re sending inital(dialog forming) SUBSCRIBE due to 423, MinExpires is: " << msg.header(h_MinExpires).value());
670 mDum.send(getCreator()->getLastRequest());
671 return;
672 }
673
674 // We should only do DialogState processing if this is a response to our initial request
675 if(getCreator() &&
676 msg.header(h_CSeq) == getCreator()->getLastRequest()->header(h_CSeq))
677 {
678 switch(mState)
679 {
680 case Initial:
681 if (code < 200)
682 {
683 mState = ReceivedProvisional;
684 }
685 else if(code < 300)
686 {
687 mState = Established;
688 }
689 else
690 {
691 mState = Established;
692 if (!mDialogs.empty())
693 {
694 dispatchToAllDialogs(msg);
695 return;
696 }
697 }
698 break;
699 case ReceivedProvisional:
700 if (code < 200)
701 {
702 // fall through
703 }
704 else if (code < 300)
705 {
706 mState = Established;
707 for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
708 {
709 if (it->second != dialog) // this is dialog that accepted
710 {
711 it->second->onForkAccepted();
712 }
713 }
714 }
715 else // failure response
716 {
717 mState = Established;
718 if (!mDialogs.empty())
719 {
720 dispatchToAllDialogs(msg);
721 return;
722 }
723 }
724 break;
725 default:
726 // !jf!
727 break;
728 }
729 }
730
731 if (response.header(h_StatusLine).statusCode() == 100)
732 {
733 if (mDum.mDialogSetHandler)
734 {
735 mDum.mDialogSetHandler->onTrying(mAppDialogSet->getHandle(), msg);
736 }
737 return;
738 }
739
740 switch (response.header(h_CSeq).method())
741 {
742 case INVITE:
743 case SUBSCRIBE:
744 case BYE:
745 case ACK:
746 case CANCEL:
747 break;
748
749 case PUBLISH:
750 if (mClientPublication == 0 && getCreator())
751 {
752 mClientPublication = makeClientPublication(response);
753 }
754 if (mClientPublication)
755 {
756 mClientPublication->dispatch(response);
757 }
758 return;
759
760 case REGISTER:
761 if (mClientRegistration == 0 && getCreator())
762 {
763 mClientRegistration = makeClientRegistration(response);
764 }
765 if (mClientRegistration)
766 {
767 mClientRegistration->dispatch(response);
768 }
769 return;
770
771 case MESSAGE:
772 if (dialog)
773 {
774 break;
775 }
776 else if (mClientPagerMessage)
777 {
778 mClientPagerMessage->dispatch(response);
779 }
780 return;
781
782 case INFO:
783 case UPDATE:
784 case PRACK:
785 if (dialog)
786 {
787 break;
788 }
789 else // not allowed
790 {
791 return;
792 }
793 case REFER:
794 if (dynamic_cast<SubscriptionCreator*>(getCreator()))
795 {
796 break;
797 }
798 case NOTIFY:
799 case OPTIONS:
800 if (dialog)
801 {
802 break;
803 }
804
805 default:
806 {
807 ClientOutOfDialogReq* req = findMatchingClientOutOfDialogReq(response);
808 if (req == 0)
809 {
810 // We should only create a new ClientOutOfDialogReq if this is a response to our initial request
811 if (!getCreator() || !(msg.header(h_CSeq) == getCreator()->getLastRequest()->header(h_CSeq)))
812 {
813 InfoLog(<< "Cannot create a ClientOutOfDialogReq, initial dialog request is missing or cseq does not match, ignoring.");
814 return;
815 }
816 req = makeClientOutOfDialogReq(response);
817 mClientOutOfDialogRequests.push_back(req);
818 }
819 req->dispatch(response);
820 return;
821 }
822 }
823 }
824
825 if (dialog == 0)
826 {
827 if (msg.isRequest() && msg.header(h_RequestLine).method() == CANCEL)
828 {
829 if(!mDialogs.empty())
830 {
831 dispatchToAllDialogs(msg);
832 }
833 else
834 {
835 DebugLog ( << "In DialogSet::dispatch, CANCEL received but no dialogs to dispatch to - respond with 481, msg: " << msg );
836 // Race condition - Dialogset is still around, but dialog is gone (potentially BYE'd).
837 // Need to respond to CANCEL in order for transaction in stack to go away
838 auto response = std::make_shared<SipMessage>();
839 mDum.makeResponse(*response, msg, 481);
840 mDum.send(response);
841 }
842 return;
843 }
844
845 if (msg.isResponse())
846 {
847 if(getCreator())
848 {
849 auto lastRequest = getCreator()->getLastRequest();
850 if (lastRequest && !(lastRequest->header(h_CSeq) == msg.header(h_CSeq)))
851 {
852 InfoLog(<< "Cannot create a dialog, cseq does not match initial dialog request (illegal mid-dialog fork? see 3261 14.1).");
853 return;
854 }
855 }
856 else
857 {
858 ErrLog(<< "Can't create a dialog, on a UAS response.");
859 return;
860 }
861
862 int code = msg.header(h_StatusLine).statusCode();
863
864 if (code > 100 && code < 200 &&
865 (!msg.exists(h_Contacts) ||
866 !msg.exists(h_To) || !msg.header(h_To).exists(p_tag)))
867 {
868 InfoLog ( << "Cannot create a dialog, no Contact or To tag in 1xx." );
869 if (mDum.mDialogSetHandler)
870 {
871 if (mDum.mDialogEventStateManager)
872 {
873 mDum.mDialogEventStateManager->onProceedingUac(*this, msg);
874 }
875 mDum.mDialogSetHandler->onNonDialogCreatingProvisional(mAppDialogSet->getHandle(), msg);
876 }
877 return;
878 }
879 // If failure response and no dialogs, create a dialog, otherwise
880 else if (code >= 300 && !mDialogs.empty())
881 {
882 dispatchToAllDialogs(msg);
883 return;
884 }
885 }
886
887 DebugLog ( << "mState == " << mState << " Creating a new Dialog from msg: " << std::endl << std::endl <<msg);
888 try
889 {
890 // !jf! This could throw due to bad header in msg, should we catch and rethrow
891 dialog = new Dialog(mDum, msg, *this);
892 }
893 catch(BaseException& e)
894 {
895 InfoLog( << "Unable to create dialog: " << e.getMessage());
896 if (msg.isResponse())
897 {
898 //don't delete on provisional responses, as FWD will eventually send a
899 //valid 200
900 if(mDialogs.empty() &&
901 msg.header(h_StatusLine).statusCode() >= 200)
902 {
903 // really we should wait around 32s before deleting this
904 if (mDum.mDialogEventStateManager)
905 {
906 mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Error);
907 }
908
909 mState = Destroying;
910 mDum.destroy(this);
911 }
912 }
913 else
914 {
915 // !jf! derek thinks we should destroy only on invalid CANCEL or
916 // BYE, hmmphh. see draft-sparks-sipping-dialogusage-01.txt
917 auto response = std::make_shared<SipMessage>();
918 mDum.makeResponse(*response, msg, 400);
919 mDum.send(response);
920 if(mDialogs.empty())
921 {
922 if (mDum.mDialogEventStateManager)
923 {
924 mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Error);
925 }
926 mState = Destroying;
927 mDum.destroy(this);
928 }
929 }
930 return;
931 }
932
933 resip_assert(mState != WaitingToEnd);
934 DebugLog ( << "### Calling CreateAppDialog ###: " << std::endl << std::endl <<msg);
935 AppDialog* appDialog = mAppDialogSet->createAppDialog(msg);
936 dialog->mAppDialog = appDialog;
937 appDialog->mDialog = dialog;
938 dialog->dispatch(msg);
939 }
940 else
941 {
942 dialog->dispatch(msg);
943 }
944}
945
946
948DialogSet::findMatchingClientOutOfDialogReq(const SipMessage& msg)
949{
950 for (std::list<ClientOutOfDialogReq*>::iterator i=mClientOutOfDialogRequests.begin();
951 i != mClientOutOfDialogRequests.end(); ++i)
952 {
953 if ((*i)->matches(msg))
954 {
955 return *i;
956 }
957 }
958 return 0;
959}
960
961Dialog*
962DialogSet::findDialog(const DialogId id)
963{
964 StackLog (<< "findDialog: " << id << " in " << InserterP(mDialogs));
965
966 DialogMap::iterator i = mDialogs.find(id);
967 if (i == mDialogs.end())
968 {
969 return 0;
970 }
971 else
972 {
973 if(i->second->isDestroying())
974 {
975 return 0;
976 }
977 else
978 {
979 return i->second;
980 }
981 }
982}
983
984void
985DialogSet::end(const Data& endReason)
986{
987 mEndReason = endReason;
988 end();
989}
990
991void
993{
994 mEndReasons = endReasons;
995 end();
996}
997
998void
999DialogSet::addEndReasonToMessage(SipMessage& msg)
1000{
1001 if (mEndReasons.size() > 0)
1002 {
1003 msg.header(h_Reasons) = mEndReasons;
1004 }
1005 else if (mEndReason.size() > 0)
1006 {
1007 Token reason("SIP");
1008 reason.param(p_text) = mEndReason;
1009 // RFC 3326 reason-text is a quoted-string
1010 reason.getParameterByEnum(ParameterTypes::text)->setQuoted(true);
1011 msg.header(h_Reasons).push_back(reason);
1012 }
1013}
1014
1015void
1017{
1018 switch(mState)
1019 {
1020 case Initial:
1021 mState = WaitingToEnd;
1022 break;
1023 case WaitingToEnd:
1024 break;
1025 case ReceivedProvisional:
1026 {
1027 if (getCreator() && getCreator()->getLastRequest()->header(h_CSeq).method() == INVITE)
1028 {
1029 mState = Terminating;
1030 // !jf! this should be made exception safe
1031 std::shared_ptr<SipMessage> cancel(Helper::makeCancel(*getCreator()->getLastRequest()));
1032 addEndReasonToMessage(*cancel);
1033 mDum.send(cancel);
1034
1035 if (mDum.mDialogEventStateManager)
1036 {
1037 mDum.mDialogEventStateManager->onTerminated(*this, *cancel, InviteSessionHandler::LocalCancel);
1038 }
1039
1040 if (mDialogs.empty())
1041 {
1042 mState = Cancelling;
1043 }
1044 else
1045 {
1046 //need to lag and do last element ouside of look as this DialogSet will be
1047 //deleted if all dialogs are destroyed
1048 for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
1049 {
1050 try
1051 {
1052 it->second->cancel();
1053 }
1054 catch (UsageUseException& e)
1055 {
1056 InfoLog(<< "Caught: " << e);
1057 }
1058 }
1059 }
1060 }
1061 else
1062 {
1063 // Non-Invite Dialogset
1064 if (mDialogs.empty())
1065 {
1066 mState = WaitingToEnd;
1067 }
1068 else
1069 {
1070 for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); ++it)
1071 {
1072 try
1073 {
1074 it->second->end(mEndReason, mEndReasons);
1075 }
1076 catch (UsageUseException& e)
1077 {
1078 InfoLog(<< "Caught: " << e);
1079 }
1080 }
1081 mState = Terminating;
1082 }
1083 }
1084 }
1085 break;
1086 case Established:
1087 {
1088 for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); ++it)
1089 {
1090 try
1091 {
1092 it->second->end(mEndReason, mEndReasons);
1093 }
1094 catch(UsageUseException& e)
1095 {
1096 InfoLog (<< "Caught: " << e);
1097 }
1098 }
1099 mState = Terminating;
1100 break;
1101 }
1102 case Terminating:
1103 case Cancelling:
1104 case Destroying:
1105 DebugLog (<< "DialogSet::end() called on a DialogSet that is already Terminating");
1106 //assert(0);
1107 }
1108}
1109
1110
1113{
1114 if (mClientRegistration)
1115 {
1116 return mClientRegistration->getHandle();
1117 }
1118 else
1119 {
1121 }
1122}
1123
1126{
1127 if (mServerRegistration)
1128 {
1129 return mServerRegistration->getHandle();
1130 }
1131 else
1132 {
1134 }
1135}
1136
1139{
1140 if (mClientPublication)
1141 {
1142 return mClientPublication->getHandle();
1143 }
1144 else
1145 {
1147 }
1148}
1149
1151DialogSet::makeClientRegistration(const SipMessage& response)
1152{
1153 BaseCreator* creator = getCreator();
1154 resip_assert(creator);
1155 return new ClientRegistration(mDum, *this, creator->getLastRequest());
1156}
1157
1159DialogSet::makeClientPublication(const SipMessage& response)
1160{
1161 BaseCreator* creator = getCreator();
1162 resip_assert(creator);
1163 return new ClientPublication(mDum, *this, creator->getLastRequest());
1164}
1165
1167DialogSet::makeClientOutOfDialogReq(const SipMessage& response)
1168{
1169 BaseCreator* creator = getCreator();
1170 resip_assert(creator);
1171 return new ClientOutOfDialogReq(mDum, *this, *creator->getLastRequest());
1172}
1173
1175DialogSet::makeServerRegistration(const SipMessage& request)
1176{
1177 return new ServerRegistration(mDum, *this, request);
1178}
1179
1181DialogSet::makeServerOutOfDialog(const SipMessage& request)
1182{
1183 return new ServerOutOfDialogReq(mDum, *this, request);
1184}
1185
1187DialogSet::makeServerPagerMessage(const SipMessage& request)
1188{
1189 return new ServerPagerMessage(mDum, *this, request);
1190}
1191
1194{
1195 if (mServerOutOfDialogRequest)
1196 {
1197 return mServerOutOfDialogRequest->getHandle();
1198 }
1199 else
1200 {
1202 }
1203}
1204
1205void DialogSet::dispatchToAllDialogs(const SipMessage& msg)
1206{
1207 if (!mDialogs.empty())
1208 {
1209 for(DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
1210 {
1211 it->second->dispatch(msg);
1212 }
1213 }
1214}
1215
1216std::shared_ptr<UserProfile>
1218{
1219 // If no UserProfile set then use UserProfile of the MasterProfile
1220 return mUserProfile ? mUserProfile : mDum.getMasterUserProfile();
1221}
1222
1223void
1224DialogSet::setUserProfile(std::shared_ptr<UserProfile> userProfile)
1225{
1226 resip_assert(userProfile.get());
1227 mUserProfile = userProfile;
1228}
1229
1230void
1231DialogSet::flowTerminated(const Tuple& flow)
1232{
1233 // The flow has failed - clear the flow key/tuple in the UserProfile
1234 mUserProfile->clearClientOutboundFlowTuple();
1235
1236 // If this profile is configured for client outbound and the connectionTerminated
1237 // matches the connection stored in the profile, then notify the client registration
1238 // and all dialogs in this dialogset that the flow has terminated
1239 // Check other usage types that we send requests on
1240 if(mClientRegistration)
1241 {
1242 mClientRegistration->flowTerminated();
1243 }
1244
1245 for(DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
1246 {
1247 it->second->flowTerminated();
1248 }
1249}
1250
1252resip::operator<<(EncodeStream& strm, const DialogSet& ds)
1253{
1254 // Used in Inserter, so keeping brief (ie. Id is already logged by Inserter)
1255 strm << "state=" << ds.mState;
1256 return strm;
1257}
1258
1259
1260/* ====================================================================
1261 * The Vovida Software License, Version 1.0
1262 *
1263 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1264 *
1265 * Redistribution and use in source and binary forms, with or without
1266 * modification, are permitted provided that the following conditions
1267 * are met:
1268 *
1269 * 1. Redistributions of source code must retain the above copyright
1270 * notice, this list of conditions and the following disclaimer.
1271 *
1272 * 2. Redistributions in binary form must reproduce the above copyright
1273 * notice, this list of conditions and the following disclaimer in
1274 * the documentation and/or other materials provided with the
1275 * distribution.
1276 *
1277 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1278 * and "Vovida Open Communication Application Library (VOCAL)" must
1279 * not be used to endorse or promote products derived from this
1280 * software without prior written permission. For written
12811 * permission, please contact vocal@vovida.org.
1282 *
1283 * 4. Products derived from this software may not be called "VOCAL", nor
1284 * may "VOCAL" appear in their name, without prior written
1285 * permission of Vovida Networks, Inc.
1286 *
1287 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1288 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1289 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1290 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1291 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1292 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1293 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1294 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1295 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1296 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1297 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1298 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1299 * DAMAGE.
1300 *
1301 * ====================================================================
1302 *
1303 * This software consists of voluntary contributions made by Vovida
1304 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1305 * Inc. For more information on Vovida Networks, Inc., please see
1306 * <http://www.vovida.org/>.
1307 *
1308 */
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
virtual void destroy()
virtual AppDialog * createAppDialog(const SipMessage &)
AppDialogSetHandle getHandle()
std::shared_ptr< UserProfile > getUserProfile() const noexcept
std::shared_ptr< SipMessage > getLastRequest() const noexcept
The abstract base-class for all exceptions thrown by resip code.
const Data & getMessage() const noexcept
virtual void dispatch(const SipMessage &msg)
void dispatch(const SipMessage &msg) override
void dispatch(const SipMessage &msg) override
ClientPublicationHandle getHandle()
dcm! – shutdown/deletion API – end?
ClientRegistrationHandle getHandle()
void dispatch(const SipMessage &msg) override
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
virtual void onTrying(AppDialogSetHandle, const SipMessage &msg)=0
virtual void onNonDialogCreatingProvisional(AppDialogSetHandle, const SipMessage &msg)=0
friend class ClientOutOfDialogReq
Definition DialogSet.hxx:61
friend class ServerRegistration
Definition DialogSet.hxx:60
ClientPublicationHandle getClientPublication()
friend class ClientRegistration
Definition DialogSet.hxx:59
void addDialog(Dialog *)
BaseCreator * getCreator() const noexcept
friend class ClientPublication
Definition DialogSet.hxx:63
ServerRegistrationHandle getServerRegistration()
ServerOutOfDialogReqHandle getServerOutOfDialog()
friend class Dialog
Definition DialogSet.hxx:54
friend class ServerOutOfDialogReq
Definition DialogSet.hxx:62
void dispatch(const SipMessage &msg)
void setUserProfile(std::shared_ptr< UserProfile > userProfile)
bool empty() const noexcept
ClientRegistrationHandle getClientRegistration()
friend class ServerPagerMessage
Definition DialogSet.hxx:66
DialogSet(BaseCreator *creator, DialogUsageManager &dum)
Definition DialogSet.cxx:36
std::shared_ptr< UserProfile > getUserProfile() const
virtual ~DialogSet()
Definition DialogSet.cxx:95
DialogSetId getId() const
void send(std::shared_ptr< SipMessage > request)
std::shared_ptr< UserProfile > & getMasterUserProfile()
dcm! – kill typedef std::list<DialogId> DialogIdSet;
Definition Dialog.hxx:29
void makeRequest(SipMessage &request, MethodTypes method, bool incrementCSeq=true)
Definition Dialog.cxx:1033
bool isDestroying() const noexcept
Definition Dialog.hxx:81
const DialogId & getId() const noexcept
Definition Dialog.cxx:325
void dispatch(const SipMessage &msg)
Definition Dialog.cxx:477
void send(std::shared_ptr< SipMessage > msg)
Definition Dialog.cxx:1198
static Handle< T > NotValid()
Definition Handle.hxx:84
static SipMessage * makeCancel(const SipMessage &request)
Make a new Cancel request for the specified request.
Definition Helper.cxx:261
static void makeResponse(SipMessage &response, const SipMessage &request, int responseCode, const Data &reason=Data::Empty, const Data &hostname=Data::Empty, const Data &warning=Data::Empty)
Make a response to a provided request.
Definition Helper.cxx:412
bool isWellFormed() const
Returns true iff this element was parsed successfully, according to the implementation of parse().
static const MergedRequestKey Empty
size_t size() const
return the size of the mParsers vector
Container class for ParserCategory, used by SipMessage to represent multi-valued headers (Contact,...
MethodTypes & method()
ServerOutOfDialogReqHandle getHandle()
void dispatch(const SipMessage &msg) override
void dispatch(const SipMessage &msg) override
ServerRegistrationHandle getHandle()
void dispatch(const SipMessage &msg) override
Represents a SIP message.
bool isRequest() const
Returns true if message is a request, false otherwise.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
virtual const Data & getTransactionId() const
Returns the transaction id from the branch or if 2543, the computed hash.
bool isResponse() const
Returns true if message is a response, false otherwise.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
bool isExternal() const
Check if SipMessage is to be treated as it came off the wire.
resip::MethodTypes method() const
returns the method type of the message
Represents the "token" element in the RFC 3261 grammar.
Definition Token.hxx:18
Represents a network address.
Definition Tuple.hxx:59
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348
InserterPClass< T > InserterP(const T &t)
Templatized function to construct an instance of InserterClass for a container to be inserted.
Definition Inserter.hxx:558
RequestLineType h_RequestLine
Definition Headers.cxx:347
EncodeStream & operator<<(EncodeStream &strm, const BaseException &e)
#define EncodeStream