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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10876 - (show annotations) (download)
Thu Jan 16 18:45:01 2014 UTC (5 years, 10 months ago) by sgodin
File MIME type: text/plain
File size: 38930 byte(s)
-fixed some transaction state memory leaks in DUM - resulting from race conditions in DUM Cancel handling
 -Rece#1 - DialogSet.cxx - if Dialogset is still around, but dialog is gone (potentially BYE'd).
           Need to respond to CANCEL in order for transaction in stack to go away
 -Race#2 - ServerAuthManager - if we are waiting for an async user auth info to arrive on an INVITE with
           credentials when a CANCEL arrives, we did not correctly respond to the CANCEL
         - DialogUsageManager - needed a change to handle the User Auth info arriving after destroying the 
           feature chain due to a CANCEL 

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

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27