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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9033 - (show annotations) (download)
Sun Feb 27 17:17:15 2011 UTC (8 years, 9 months ago) by sgodin
File MIME type: text/plain
File size: 42146 byte(s)
-merge work from b-client-outbound-20110213 - client outbound support (RFC 5626)
   - New UserProfile settings
      - clientOutboundEnabled
      - setRegId
   - UserProfile now tracks flow used at registration time if server indicates outbound support, and 
     all usages that are assigned the same UserProfile will use this flow
   - ;ob parameter is added to contact headers of non-REGISTER requests if clientOutboundEnabled
   - ;reg-id parameter is added to contact headers of REGISTER requests if configured
   - ;+sip.instance is added to all contact headers if configured
   - ClientRegistration tagContact is now static and is used in RegistrationCreator for consistency
   - Keep alive pong responses are now routed from Stack to DUM Keepalive Manager
   - DUM Keep alive manager will terminate flows if a pong response is not received from 
     an outbound enabled connection after a default of 10 seconds
   -removed a bunch of non-implemented methods from DialogUsageManager.hxx
   -optimized some accesses to user profile from usages
   -added new makeInviteSession calls that accept an InviteSessionHandle and add an appropriate replaces header
   -treat internal 503 errors the same as 408 errors when dispatching onRequestRetry callback for
    ClientRegistration, ClientSubscription and ClientPublication
      - allows retry logic that is consistent when using both UDP or TCP/TLS transports
      - note:  TCP/TLS transports get a 503 error when they cannot connect, UDP transports typically
               get a 408 error when the far end is not present
   -ClientRegistration changes to ensure calling requestRefresh will not assert in any state
      - allow calling requestRefresh while a retry timer is running to request an immediate retry 
   -created onFlowTerminated callbacks on:
      - ClientRegistrationHandler - default implementation is to retry registration immediately
      - InviteSessionHandler - current default implementation is to do nothing
      - ServerSubscriptionHandler - default implementation is to end the Subscription
      - ClientSubscriptionHandler - default implementation is to reSubscribe (new Dialog)
   -fix for ContactInstanceRecord equality checks - registration server was not treating contacts with the same
    instance id as equal if the contact URI was different
1 #include "resip/stack/Contents.hxx"
2 #include "resip/stack/Helper.hxx"
3 #include "resip/stack/SipMessage.hxx"
4 #include "resip/dum/AppDialog.hxx"
5 #include "resip/dum/BaseCreator.hxx"
6 #include "resip/dum/ClientAuthManager.hxx"
7 #include "resip/dum/ClientInviteSession.hxx"
8 #include "resip/dum/ClientSubscription.hxx"
9 #include "resip/dum/Dialog.hxx"
10 #include "resip/dum/DialogUsageManager.hxx"
11 #include "resip/dum/MasterProfile.hxx"
12 #include "resip/dum/InviteSessionCreator.hxx"
13 #include "resip/dum/InviteSessionHandler.hxx"
14 #include "resip/dum/ServerInviteSession.hxx"
15 #include "resip/dum/ServerSubscription.hxx"
16 #include "resip/dum/SubscriptionHandler.hxx"
17 #include "resip/dum/UsageUseException.hxx"
18 #include "rutil/Logger.hxx"
19 #include "rutil/Inserter.hxx"
20 #include "rutil/WinLeakCheck.hxx"
21
22 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
23
24 using namespace resip;
25 using namespace std;
26
27 Dialog::Dialog(DialogUsageManager& dum, const SipMessage& msg, DialogSet& ds)
28 : mDum(dum),
29 mDialogSet(ds),
30 mId("INVALID", "INVALID", "INVALID"),
31 mClientSubscriptions(),
32 mServerSubscriptions(),
33 mInviteSession(0),
34 mType(Fake),
35 mRouteSet(),
36 mLocalContact(),
37 mLocalCSeq(0),
38 mRemoteCSeq(0),
39 mRemoteTarget(),
40 mLocalNameAddr(),
41 mRemoteNameAddr(),
42 mCallId(msg.header(h_CallID)),
43 mDefaultSubExpiration(0),
44 mAppDialog(0),
45 mDestroying(false),
46 mReUseDialogSet(false)
47 {
48 assert(msg.isExternal());
49
50 assert(msg.header(h_CSeq).method() != MESSAGE);
51 assert(msg.header(h_CSeq).method() != REGISTER);
52 assert(msg.header(h_CSeq).method() != PUBLISH);
53
54 mNetworkAssociation.setDum(&dum);
55
56 if (msg.isRequest()) // UAS
57 {
58 const SipMessage& request = msg;
59
60 switch (request.header(h_CSeq).method())
61 {
62 case INVITE:
63 mType = Invitation;
64 break;
65
66 case SUBSCRIBE:
67 case REFER:
68 case NOTIFY:
69 //!dcm! -- event header check
70 mType = Subscription;
71 break;
72
73 default:
74 mType = Fake;
75 }
76 if (request.exists(h_RecordRoutes))
77 {
78 mRouteSet = request.header(h_RecordRoutes);
79 }
80
81 switch (request.header(h_CSeq).method())
82 {
83 case INVITE:
84 case SUBSCRIBE:
85 case REFER:
86 case NOTIFY:
87 DebugLog ( << "UAS dialog ID creation, DS: " << ds.getId());
88 mId = DialogId(ds.getId(),
89 request.header(h_From).exists(p_tag) ? request.header(h_From).param(p_tag) : Data::Empty);
90
91 mRemoteNameAddr = request.header(h_From);
92 mLocalNameAddr = request.header(h_To);
93 mLocalNameAddr.param(p_tag) = mId.getLocalTag();
94 if (request.exists(h_Contacts) && request.header(h_Contacts).size() == 1)
95 {
96 const NameAddr& contact = request.header(h_Contacts).front();
97 if (isEqualNoCase(contact.uri().scheme(), Symbols::Sips) ||
98 isEqualNoCase(contact.uri().scheme(), Symbols::Sip))
99 {
100 // Store Remote Target
101 mRemoteTarget = contact;
102
103 // Create Local Contact
104 if(mDialogSet.mUserProfile->hasUserAgentCapabilities())
105 {
106 mLocalContact = mDialogSet.mUserProfile->getUserAgentCapabilities();
107 }
108 if(!mDialogSet.mUserProfile->isAnonymous() && mDialogSet.mUserProfile->hasPublicGruu())
109 {
110 mLocalContact.uri() = mDialogSet.mUserProfile->getPublicGruu();
111 }
112 else if(mDialogSet.mUserProfile->isAnonymous() && mDialogSet.mUserProfile->hasTempGruu())
113 {
114 mLocalContact.uri() = mDialogSet.mUserProfile->getTempGruu();
115 }
116 else
117 {
118 if (mDialogSet.mUserProfile->hasOverrideHostAndPort())
119 {
120 mLocalContact.uri() = mDialogSet.mUserProfile->getOverrideHostAndPort();
121 }
122 if(request.header(h_RequestLine).uri().user().empty())
123 {
124 mLocalContact.uri().user() = request.header(h_To).uri().user();
125 }
126 else
127 {
128 mLocalContact.uri().user() = request.header(h_RequestLine).uri().user();
129 }
130 const Data& instanceId = mDialogSet.mUserProfile->getInstanceId();
131 if (!contact.uri().exists(p_gr) && !instanceId.empty())
132 {
133 mLocalContact.param(p_Instance) = instanceId;
134 }
135 }
136 if(mDialogSet.mUserProfile->clientOutboundEnabled())
137 {
138 // Add ;ob parm to non-register requests - RFC5626 pg17
139 mLocalContact.uri().param(p_ob);
140 }
141 }
142 else
143 {
144 InfoLog(<< "Got an INVITE or SUBSCRIBE with invalid scheme");
145 InfoLog(<< request);
146 throw Exception("Invalid scheme in request", __FILE__, __LINE__);
147 }
148 }
149 else
150 {
151 InfoLog (<< "Got an INVITE or SUBSCRIBE that doesn't have exactly one contact");
152 InfoLog (<< request);
153 throw Exception("Too many (or no contact) contacts in request", __FILE__, __LINE__);
154 }
155 break;
156 default:
157 break;
158 }
159
160 mRemoteCSeq = request.header(h_CSeq).sequence();
161 mLocalCSeq = 1;
162
163 DebugLog ( << "************** Created Dialog as UAS **************" );
164 DebugLog ( << "mRemoteNameAddr: " << mRemoteNameAddr );
165 DebugLog ( << "mLocalNameAddr: " << mLocalNameAddr );
166 DebugLog ( << "mLocalContact: " << mLocalContact );
167 DebugLog ( << "mRemoteTarget: " << mRemoteTarget );
168 }
169 else if (msg.isResponse())
170 {
171 mId = DialogId(msg);
172 const SipMessage& response = msg;
173 mRemoteNameAddr = response.header(h_To);
174 mLocalNameAddr = response.header(h_From);
175
176 switch (msg.header(h_CSeq).method())
177 {
178 case INVITE:
179 mType = Invitation;
180 break;
181
182 case SUBSCRIBE:
183 case REFER:
184 mType = Subscription;
185 break;
186
187 default:
188 mType = Fake;
189 }
190
191 if (response.exists(h_RecordRoutes))
192 {
193 mRouteSet = response.header(h_RecordRoutes).reverse();
194 }
195
196 switch (response.header(h_CSeq).method())
197 {
198 case INVITE:
199 case SUBSCRIBE:
200 case REFER:
201 if (response.header(h_StatusLine).statusCode() > 100 &&
202 response.header(h_StatusLine).statusCode() < 300)
203 {
204
205 if (response.exists(h_Contacts) && response.header(h_Contacts).size() == 1)
206 {
207 const NameAddr& contact = response.header(h_Contacts).front();
208 if (isEqualNoCase(contact.uri().scheme(), Symbols::Sips) ||
209 isEqualNoCase(contact.uri().scheme(), Symbols::Sip))
210 {
211 BaseCreator* creator = mDialogSet.getCreator();
212
213 if( 0 == creator )
214 {
215 ErrLog(<< "BaseCreator is null for DialogSet");
216 ErrLog(<< response);
217 throw Exception("BaseCreator is null for DialogSet", __FILE__, __LINE__);
218 }
219
220 SharedPtr<SipMessage> lastRequest(creator->getLastRequest());
221
222 if( 0 == lastRequest.get() ||
223 !lastRequest->exists(h_Contacts) ||
224 lastRequest->header(h_Contacts).empty())
225 {
226 InfoLog(<< "lastRequest does not contain a valid contact");
227 InfoLog(<< response);
228 throw Exception("lastRequest does not contain a valid contact.", __FILE__, __LINE__);
229 }
230 mLocalContact = creator->getLastRequest()->header(h_Contacts).front();
231 mRemoteTarget = contact;
232 }
233 else
234 {
235 InfoLog (<< "Got an INVITE or SUBSCRIBE with invalid scheme");
236 DebugLog (<< response);
237 throw Exception("Bad scheme in contact in response", __FILE__, __LINE__);
238 }
239 }
240 else
241 {
242 InfoLog (<< "Got an INVITE or SUBSCRIBE that doesn't have exactly one contact");
243 DebugLog (<< response);
244 throw Exception("Too many contacts (or no contact) in response", __FILE__, __LINE__);
245 }
246 break;
247 default:
248 break;
249 }
250 }
251
252 mLocalCSeq = response.header(h_CSeq).sequence();
253 mRemoteCSeq = 0;
254 DebugLog ( << "************** Created Dialog as UAC **************" );
255 DebugLog ( << "mRemoteNameAddr: " << mRemoteNameAddr );
256 DebugLog ( << "mLocalNameAddr: " << mLocalNameAddr );
257 DebugLog ( << "mLocalContact: " << mLocalContact );
258 DebugLog ( << "mRemoteTarget: " << mRemoteTarget );
259 }
260 mDialogSet.addDialog(this);
261 DebugLog ( <<"Dialog::Dialog " << mId);
262 }
263
264 Dialog::~Dialog()
265 {
266 DebugLog ( <<"Dialog::~Dialog() ");
267
268 mDestroying = true;
269
270 while (!mClientSubscriptions.empty())
271 {
272 delete *mClientSubscriptions.begin();
273 }
274
275 while (!mServerSubscriptions.empty())
276 {
277 delete *mServerSubscriptions.begin();
278 }
279
280 delete mInviteSession;
281 mDialogSet.mDialogs.erase(this->getId());
282 delete mAppDialog;
283 if(!mReUseDialogSet)
284 {
285 mDialogSet.possiblyDie();
286 }
287 }
288
289 const DialogId&
290 Dialog::getId() const
291 {
292 return mId;
293 }
294
295 const NameAddr&
296 Dialog::getLocalNameAddr() const
297 {
298 return mLocalNameAddr;
299 }
300
301 const NameAddr&
302 Dialog::getLocalContact() const
303 {
304 return mLocalContact;
305 }
306
307 const NameAddr&
308 Dialog::getRemoteNameAddr() const
309 {
310 return mRemoteNameAddr;
311 }
312
313 const NameAddr&
314 Dialog::getRemoteTarget() const
315 {
316 return mRemoteTarget;
317 }
318
319 const NameAddrs&
320 Dialog::getRouteSet() const
321 {
322 return mRouteSet;
323 }
324
325 void
326 Dialog::cancel()
327 {
328 assert(mType == Invitation);
329 ClientInviteSession* uac = dynamic_cast<ClientInviteSession*>(mInviteSession);
330 assert (uac);
331 uac->cancel();
332 }
333
334 void
335 Dialog::end()
336 {
337 if (mInviteSession)
338 {
339 mInviteSession->end();
340 }
341
342 // End Subscriptions
343 // !jrm! WARN ClientSubscription and ServerSubscription have access to this dialog and will remove themselves
344 // from the m<client|server>Subscriptions collections in the call to end().
345 for (list<ClientSubscription*>::iterator it(mClientSubscriptions.begin());
346 it != mClientSubscriptions.end();)
347 {
348 ClientSubscription* c = *it;
349 it++;
350 c->end();
351 }
352
353 for (list<ServerSubscription*>::iterator it2(mServerSubscriptions.begin());
354 it2 != mServerSubscriptions.end();)
355 {
356 ServerSubscription* s = *it2;
357 it2++;
358 s->end();
359 }
360 }
361
362 void
363 Dialog::handleTargetRefresh(const SipMessage& msg)
364 {
365 switch(msg.header(h_CSeq).method())
366 {
367 case INVITE:
368 case UPDATE:
369 if (msg.isRequest() || (msg.isResponse() && msg.header(h_StatusLine).statusCode()/100 == 2))
370 {
371 //?dcm? modify local target; 12.2.2 of 3261 implies that the remote
372 //target is immediately modified. Should we wait until a 2xx class
373 //reponse is sent to a re-invite(easy when all send requests go
374 //through Dialog)
375 if (msg.exists(h_Contacts))
376 {
377 //.dcm. replace or check then replace
378 mRemoteTarget = msg.header(h_Contacts).front();
379 }
380 }
381 break;
382 default:
383 return;
384 }
385 }
386
387 void
388 Dialog::dispatch(const SipMessage& msg)
389 {
390 // !jf! Should be checking for messages with out of order CSeq and rejecting
391
392 DebugLog ( << "Dialog::dispatch: " << msg.brief());
393
394 if(msg.isExternal())
395 {
396 const Data& receivedTransport = msg.header(h_Vias).front().transport();
397 int keepAliveTime = 0;
398 if(receivedTransport == Symbols::TCP ||
399 receivedTransport == Symbols::TLS ||
400 receivedTransport == Symbols::SCTP)
401 {
402 keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForStream();
403 }
404 else
405 {
406 keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForDatagram();
407 }
408
409 if(keepAliveTime > 0)
410 {
411 mNetworkAssociation.update(msg, keepAliveTime, false /* targetSupportsOutbound */); // target supports outbound is detected in registration responses only
412 }
413 }
414
415 handleTargetRefresh(msg);
416 if (msg.isRequest())
417 {
418 const SipMessage& request = msg;
419 switch (request.header(h_CSeq).method())
420 {
421 case INVITE: // new INVITE
422 if (mInviteSession == 0)
423 {
424 DebugLog ( << "Dialog::dispatch -- Created new server invite session" << msg.brief());
425 mInviteSession = makeServerInviteSession(request);
426 }
427 mInviteSession->dispatch(request);
428 break;
429 //refactor, send bad request for BYE, INFO, CANCEL?
430 case BYE:
431 if (mInviteSession == 0)
432 {
433 InfoLog ( << "Spurious BYE" );
434 return;
435 }
436 else
437 {
438 mInviteSession->dispatch(request);
439 }
440 break;
441 case UPDATE:
442 if (mInviteSession == 0)
443 {
444 InfoLog ( << "Spurious UPDATE" );
445 return;
446 }
447 else
448 {
449 mInviteSession->dispatch(request);
450 }
451 break;
452 case INFO:
453 if (mInviteSession == 0)
454 {
455 InfoLog ( << "Spurious INFO" );
456 return;
457 }
458 else
459 {
460 mInviteSession->dispatch(request);
461 }
462 break;
463 case MESSAGE:
464 if (mInviteSession == 0)
465 {
466 InfoLog ( << "Spurious MESSAGE" );
467 return;
468 }
469 else
470 {
471 mInviteSession->dispatch(request);
472 }
473 break;
474 case ACK:
475 case CANCEL:
476 if (mInviteSession == 0)
477 {
478 InfoLog (<< "Drop stray ACK or CANCEL in dialog on the floor");
479 DebugLog (<< request);
480 }
481 else
482 {
483 mInviteSession->dispatch(request);
484 }
485 break;
486 case SUBSCRIBE:
487 {
488 ServerSubscription* server = findMatchingServerSub(request);
489 if (server)
490 {
491 server->dispatch(request);
492 }
493 else
494 {
495 if (request.exists(h_Event) && request.header(h_Event).value() == "refer")
496 {
497 InfoLog (<< "Received a subscribe to a non-existent refer subscription: " << request.brief());
498 SipMessage failure;
499 makeResponse(failure, request, 403);
500 mDum.sendResponse(failure);
501 return;
502 }
503 else
504 {
505 if (mDum.checkEventPackage(request))
506 {
507 server = makeServerSubscription(request);
508 mServerSubscriptions.push_back(server);
509 server->dispatch(request);
510 }
511 }
512 }
513 }
514 break;
515 case REFER:
516 {
517 // if (mInviteSession == 0)
518 // {
519 // InfoLog (<< "Received an in dialog refer in a non-invite dialog: " << request.brief());
520 // SipMessage failure;
521 // makeResponse(failure, request, 603);
522 // mDum.sendResponse(failure);
523 // return;
524 // }
525 // else
526
527 if (!request.exists(h_ReferTo))
528 {
529 InfoLog (<< "Received refer w/out a Refer-To: " << request.brief());
530 SipMessage failure;
531 makeResponse(failure, request, 400);
532 mDum.sendResponse(failure);
533 return;
534 }
535 else
536 {
537 if ((request.exists(h_ReferSub) &&
538 request.header(h_ReferSub).isWellFormed() &&
539 request.header(h_ReferSub).value()=="false") ||
540 (request.exists(h_Requires) &&
541 request.header(h_Requires).find(Token("norefersub"))))
542 {
543 assert(mInviteSession);
544 mInviteSession->referNoSub(msg);
545 }
546 else
547 {
548 ServerSubscription* server = findMatchingServerSub(request);
549 ServerSubscriptionHandle serverHandle;
550 if (server)
551 {
552 serverHandle = server->getHandle();
553 server->dispatch(request);
554 }
555 else
556 {
557 server = makeServerSubscription(request);
558 mServerSubscriptions.push_back(server);
559 serverHandle = server->getHandle();
560 server->dispatch(request);
561 }
562
563 if (mInviteSession)
564 {
565 mDum.mInviteSessionHandler->onRefer(mInviteSession->getSessionHandle(), serverHandle, msg);
566 }
567
568 }
569 }
570 }
571 break;
572 case NOTIFY:
573 {
574 ClientSubscription* client = findMatchingClientSub(request);
575 if (client)
576 {
577 client->dispatch(request);
578 }
579 else
580 {
581 BaseCreator* creator = mDialogSet.getCreator();
582 if (creator && (creator->getLastRequest()->header(h_RequestLine).method() == SUBSCRIBE ||
583 creator->getLastRequest()->header(h_RequestLine).method() == REFER))
584 {
585 DebugLog (<< "Making subscription (from creator) request: " << *creator->getLastRequest());
586 ClientSubscription* sub = makeClientSubscription(*creator->getLastRequest());
587 mClientSubscriptions.push_back(sub);
588 sub->dispatch(request);
589 }
590 else
591 {
592 if (mInviteSession != 0 && (!msg.exists(h_Event) || msg.header(h_Event).value() == "refer") &&
593 mDum.getClientSubscriptionHandler("refer")!=0)
594 {
595 DebugLog (<< "Making subscription from NOTIFY: " << msg);
596 ClientSubscription* sub = makeClientSubscription(msg);
597 mClientSubscriptions.push_back(sub);
598 ClientSubscriptionHandle client = sub->getHandle();
599 mDum.mInviteSessionHandler->onReferAccepted(mInviteSession->getSessionHandle(), client, msg);
600 sub->dispatch(request);
601 }
602 else
603 {
604 SharedPtr<SipMessage> response(new SipMessage);
605 makeResponse(*response, msg, 406);
606 send(response);
607 }
608 }
609 }
610 }
611 break;
612 default:
613 assert(0);
614 return;
615 }
616 }
617 else if (msg.isResponse())
618 {
619 // !jf! There is a substantial change in how this works in teltel-branch
620 // from how it worked in main branch pre merge.
621 // If the response doesn't match a cseq for a request I've sent, ignore
622 // the response
623 {//scope 'r' as it is invalidated below
624 RequestMap::iterator r = mRequests.find(msg.header(h_CSeq).sequence());
625 if (r != mRequests.end())
626 {
627 bool handledByAuth = false;
628 if (mDum.mClientAuthManager.get() &&
629 mDum.mClientAuthManager->handle(*mDialogSet.mUserProfile, *r->second, msg))
630 {
631 InfoLog( << "about to re-send request with digest credentials" << r->second->brief());
632
633 assert (r->second->isRequest());
634
635 mLocalCSeq++;
636 send(r->second);
637 handledByAuth = true;
638 }
639 mRequests.erase(r);
640 if (handledByAuth) return;
641 }
642 }
643
644 const SipMessage& response = msg;
645 int code = response.header(h_StatusLine).statusCode();
646 // If this is a 200 response to the initial request, then store the routeset (if present)
647 BaseCreator* creator = mDialogSet.getCreator();
648 if (creator && (creator->getLastRequest()->header(h_CSeq) == response.header(h_CSeq)) && code >=200 && code < 300)
649 {
650 if (response.exists(h_RecordRoutes))
651 {
652 mRouteSet = response.header(h_RecordRoutes).reverse();
653 }
654 else
655 {
656 // Ensure that if the route-set in the 200 is empty, then we overwrite any existing route-sets
657 mRouteSet.clear();
658 }
659 }
660
661 // !jf! should this only be for 2xx responses? !jf! Propose no as an
662 // answer !dcm! what is he on?
663 switch (response.header(h_CSeq).method())
664 {
665 case INVITE:
666 if (mInviteSession == 0)
667 {
668 DebugLog ( << "Dialog::dispatch -- Created new client invite session" << msg.brief());
669
670 mInviteSession = makeClientInviteSession(response);
671 if (mInviteSession)
672 {
673 mInviteSession->dispatch(response);
674 }
675 else
676 {
677 ErrLog( << "Dialog::dispatch -- Unable to create invite session from response" << msg.brief());
678 }
679 }
680 else
681 {
682 mInviteSession->dispatch(response);
683 }
684 break;
685 case BYE:
686 case ACK:
687 case CANCEL:
688 case INFO:
689 case MESSAGE:
690 case UPDATE:
691 if (mInviteSession)
692 {
693 mInviteSession->dispatch(response);
694 }
695 // else drop on the floor
696 break;
697
698 case REFER:
699 if(mInviteSession)
700 {
701 if (code >= 300)
702 {
703 mDum.mInviteSessionHandler->onReferRejected(mInviteSession->getSessionHandle(), msg);
704 }
705 else
706 {
707 //!dys! the OR condition below is not draft compliant.
708 if (!mInviteSession->mReferSub &&
709 ((msg.exists(h_ReferSub) && msg.header(h_ReferSub).value()=="false") ||
710 !msg.exists(h_ReferSub)))
711 {
712 DebugLog(<< "refer accepted with norefersub");
713 mDum.mInviteSessionHandler->onReferAccepted(mInviteSession->getSessionHandle(), ClientSubscriptionHandle::NotValid(), msg);
714 }
715 // else no need for action - first Notify will cause onReferAccepted to be called
716 }
717 mInviteSession->nitComplete();
718 break;
719 }
720 // fall through, out of dialog refer was sent.
721
722 case SUBSCRIBE:
723 {
724 int code = response.header(h_StatusLine).statusCode();
725 ClientSubscription* client = findMatchingClientSub(response);
726 if (client)
727 {
728 client->dispatch(response);
729 }
730 else if (code < 300)
731 {
732 /*
733 we're capturing the value from the expires header off
734 the 2xx because the ClientSubscription is only created
735 after receiving the NOTIFY that comes (usually) after
736 this 2xx. We really should be creating the
737 ClientSubscription at either the 2xx or the NOTIFY
738 whichever arrives first. .mjf.
739 Note: we're capturing a duration here (not the
740 absolute time because all the inputs to
741 ClientSubscription desling with the expiration are expecting
742 duration type values from the headers. .mjf.
743 */
744 if(response.exists(h_Expires))
745 {
746 mDefaultSubExpiration = response.header(h_Expires).value();
747 }
748 else
749 {
750 //?dcm? defaults to 3600 in ClientSubscription if no expires value
751 //is provided anywhere...should we assume the value from the
752 //sub in the basecreator if it exists?
753 mDefaultSubExpiration = 0;
754 }
755 return;
756 }
757 else
758 {
759 //!dcm! -- can't subscribe in an existing Dialog, this is all
760 //a bit of a hack; currently, spurious failure messages may cause callbacks
761 BaseCreator* creator = mDialogSet.getCreator();
762 if (!creator || !creator->getLastRequest()->exists(h_Event))
763 {
764 return;
765 }
766 else
767 {
768 ClientSubscriptionHandler* handler =
769 mDum.getClientSubscriptionHandler(creator->getLastRequest()->header(h_Event).value());
770 if (handler)
771 {
772 ClientSubscription* sub = makeClientSubscription(*creator->getLastRequest());
773 mClientSubscriptions.push_back(sub);
774 sub->dispatch(response);
775 }
776 }
777 }
778
779 }
780 break;
781 case NOTIFY:
782 {
783 //2xx responses are treated as retransmission quenchers(handled by
784 //the stack). Failures are dispatched to all ServerSubsscriptions,
785 //which may not be correct.
786
787 int code = msg.header(h_StatusLine).statusCode();
788 if (code >= 300)
789 {
790 //!dcm! -- ick, write guard
791 mDestroying = true;
792 for (list<ServerSubscription*>::iterator it = mServerSubscriptions.begin();
793 it != mServerSubscriptions.end(); )
794 {
795 ServerSubscription* s = *it;
796 it++;
797 s->dispatch(msg);
798 }
799 mDestroying = false;
800 possiblyDie();
801 }
802 // ServerSubscription* server = findMatchingServerSub(response);
803 // if (server)
804 // {
805 // server->dispatch(response);
806 // }
807 }
808 break;
809 default:
810 assert(0);
811 return;
812 }
813
814 #if 0 // merged from head back to teltel-branch
815 if (msg.header(h_StatusLine).statusCode() >= 400
816 && Helper::determineFailureMessageEffect(msg) == Helper::DialogTermination)
817 {
818 //kill all usages
819 mDestroying = true;
820
821 for (list<ServerSubscription*>::iterator it = mServerSubscriptions.begin();
822 it != mServerSubscriptions.end(); )
823 {
824 ServerSubscription* s = *it;
825 it++;
826 s->dialogDestroyed(msg);
827 }
828
829 for (list<ClientSubscription*>::iterator it = mClientSubscriptions.begin();
830 it != mClientSubscriptions.end(); )
831 {
832 ClientSubscription* s = *it;
833 it++;
834 s->dialogDestroyed(msg);
835 }
836 if (mInviteSession)
837 {
838 mInviteSession->dialogDestroyed(msg);
839 }
840 mDestroying = false;
841 possiblyDie(); //should aways result in destruction of this
842 return;
843 }
844 #endif
845 }
846 }
847
848 ServerSubscription*
849 Dialog::findMatchingServerSub(const SipMessage& msg)
850 {
851 for (std::list<ServerSubscription*>::iterator i=mServerSubscriptions.begin();
852 i != mServerSubscriptions.end(); ++i)
853 {
854 if ((*i)->matches(msg))
855 {
856 return *i;
857 }
858 }
859 return 0;
860 }
861
862 ClientSubscription*
863 Dialog::findMatchingClientSub(const SipMessage& msg)
864 {
865 for (std::list<ClientSubscription*>::iterator i=mClientSubscriptions.begin();
866 i != mClientSubscriptions.end(); ++i)
867 {
868 if ((*i)->matches(msg))
869 {
870 return *i;
871 }
872 }
873 return 0;
874 }
875
876 InviteSessionHandle
877 Dialog::getInviteSession()
878 {
879 if (mInviteSession)
880 {
881 return mInviteSession->getSessionHandle();
882 }
883 else
884 {
885 return InviteSessionHandle::NotValid();
886 }
887 }
888
889 std::vector<ClientSubscriptionHandle>
890 Dialog::findClientSubscriptions(const Data& event)
891 {
892 std::vector<ClientSubscriptionHandle> handles;
893
894 for (std::list<ClientSubscription*>::const_iterator i = mClientSubscriptions.begin();
895 i != mClientSubscriptions.end(); ++i)
896 {
897 if ( (*i)->getEventType() == event)
898 {
899 handles.push_back((*i)->getHandle());
900 }
901 }
902 return handles;
903 }
904
905 std::vector<ServerSubscriptionHandle>
906 Dialog::findServerSubscriptions(const Data& event)
907 {
908 std::vector<ServerSubscriptionHandle> handles;
909
910 for (std::list<ServerSubscription*>::const_iterator i = mServerSubscriptions.begin();
911 i != mServerSubscriptions.end(); ++i)
912 {
913 if ( (*i)->getEventType() == event)
914 {
915 handles.push_back((*i)->getHandle());
916 }
917 }
918 return handles;
919 }
920
921 std::vector<ClientSubscriptionHandle>
922 Dialog::getClientSubscriptions()
923 {
924 std::vector<ClientSubscriptionHandle> handles;
925
926 for (std::list<ClientSubscription*>::const_iterator i = mClientSubscriptions.begin();
927 i != mClientSubscriptions.end(); ++i)
928 {
929 handles.push_back((*i)->getHandle());
930 }
931
932 return handles;
933 }
934
935 std::vector<ServerSubscriptionHandle>
936 Dialog::getServerSubscriptions()
937 {
938 std::vector<ServerSubscriptionHandle> handles;
939
940 for (std::list<ServerSubscription*>::const_iterator i = mServerSubscriptions.begin();
941 i != mServerSubscriptions.end(); ++i)
942 {
943 handles.push_back((*i)->getHandle());
944 }
945
946 return handles;
947 }
948
949 void
950 Dialog::redirected(const SipMessage& msg)
951 {
952 //Established dialogs are not destroyed by a redirect
953 if (!mClientSubscriptions.empty() || !mServerSubscriptions.empty())
954 {
955 return;
956 }
957 if (mInviteSession)
958 {
959 ClientInviteSession* cInv = dynamic_cast<ClientInviteSession*>(mInviteSession);
960 if (cInv)
961 {
962 cInv->handleRedirect(msg);
963 mReUseDialogSet = true; // Set flag so that DialogSet will not be destroyed and new Request can use it
964 }
965 }
966 }
967
968 void
969 Dialog::makeRequest(SipMessage& request, MethodTypes method)
970 {
971 RequestLine rLine(method);
972
973 rLine.uri() = mRemoteTarget.uri();
974
975 request.header(h_RequestLine) = rLine;
976 request.header(h_To) = mRemoteNameAddr;
977 // request.header(h_To).param(p_tag) = mId.getRemoteTag();
978 request.header(h_From) = mLocalNameAddr;
979 // request.header(h_From).param(p_tag) = mId.getLocalTag();
980
981 request.header(h_CallId) = mCallId;
982
983 request.remove(h_RecordRoutes); //!dcm! -- all of this is rather messy
984 request.remove(h_Replaces);
985
986 request.remove(h_Contacts);
987 request.header(h_Contacts).push_front(mLocalContact);
988
989 request.header(h_CSeq).method() = method;
990 request.header(h_MaxForwards).value() = 70;
991
992 //must keep old via for cancel
993 if (method != CANCEL)
994 {
995 request.header(h_Routes) = mRouteSet;
996 request.remove(h_Vias);
997 Via via;
998 via.param(p_branch); // will create the branch
999 request.header(h_Vias).push_front(via);
1000 }
1001 else
1002 {
1003 assert(request.exists(h_Vias));
1004 }
1005
1006 //don't increment CSeq for ACK or CANCEL
1007 if (method != ACK && method != CANCEL)
1008 {
1009 request.header(h_CSeq).sequence() = ++mLocalCSeq;
1010 }
1011 else
1012 {
1013 // ACK and cancel have a minimal header set
1014 request.remove(h_Accepts);
1015 request.remove(h_AcceptEncodings);
1016 request.remove(h_AcceptLanguages);
1017 request.remove(h_Allows);
1018 request.remove(h_Requires);
1019 request.remove(h_ProxyRequires);
1020 request.remove(h_Supporteds);
1021 // request.header(h_CSeq).sequence() = ?; // Caller should provide original request, or modify CSeq to proper value after calling this method
1022 }
1023
1024 // If method is INVITE then advertise required headers
1025 if(method == INVITE || method == UPDATE)
1026 {
1027 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Allow)) request.header(h_Allows) = mDum.getMasterProfile()->getAllowedMethods();
1028 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptEncoding)) request.header(h_AcceptEncodings) = mDum.getMasterProfile()->getSupportedEncodings();
1029 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptLanguage)) request.header(h_AcceptLanguages) = mDum.getMasterProfile()->getSupportedLanguages();
1030 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AllowEvents)) request.header(h_AllowEvents) = mDum.getMasterProfile()->getAllowedEvents();
1031 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Supported)) request.header(h_Supporteds) = mDum.getMasterProfile()->getSupportedOptionTags();
1032 }
1033
1034 if (mDialogSet.mUserProfile->isAnonymous())
1035 {
1036 request.header(h_Privacys).push_back(PrivacyCategory(Symbols::id));
1037 }
1038
1039 DebugLog ( << "Dialog::makeRequest: " << std::endl << std::endl << request );
1040 }
1041
1042
1043 void
1044 Dialog::makeResponse(SipMessage& response, const SipMessage& request, int code)
1045 {
1046 assert( code >= 100 );
1047 response.remove(h_Contacts);
1048 if (code < 300 && code > 100)
1049 {
1050 assert(request.isRequest());
1051 assert(request.header(h_RequestLine).getMethod() == INVITE ||
1052 request.header(h_RequestLine).getMethod() == SUBSCRIBE ||
1053 request.header(h_RequestLine).getMethod() == BYE ||
1054 request.header(h_RequestLine).getMethod() == CANCEL ||
1055 request.header(h_RequestLine).getMethod() == REFER ||
1056 request.header(h_RequestLine).getMethod() == MESSAGE ||
1057 request.header(h_RequestLine).getMethod() == NOTIFY ||
1058 request.header(h_RequestLine).getMethod() == INFO ||
1059 request.header(h_RequestLine).getMethod() == OPTIONS ||
1060 request.header(h_RequestLine).getMethod() == UPDATE
1061 );
1062
1063 // assert (request.header(h_RequestLine).getMethod() == CANCEL || // Contact header is not required for Requests that do not form a dialog
1064 // request.header(h_RequestLine).getMethod() == BYE ||
1065 // request.header(h_Contacts).size() == 1);
1066 Helper::makeResponse(response, request, code, mLocalContact);
1067 response.header(h_To).param(p_tag) = mId.getLocalTag();
1068
1069 if((request.header(h_RequestLine).getMethod() == INVITE ||
1070 request.header(h_RequestLine).getMethod() == UPDATE)
1071 && code >= 200 && code < 300)
1072 {
1073 // Check if we should add our capabilites to the invite success response
1074 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Allow))
1075 {
1076 response.header(h_Allows) = mDum.getMasterProfile()->getAllowedMethods();
1077 }
1078 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptEncoding))
1079 {
1080 response.header(h_AcceptEncodings) = mDum.getMasterProfile()->getSupportedEncodings();
1081 }
1082 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AcceptLanguage))
1083 {
1084 response.header(h_AcceptLanguages) = mDum.getMasterProfile()->getSupportedLanguages();
1085 }
1086 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::AllowEvents))
1087 {
1088 response.header(h_AllowEvents) = mDum.getMasterProfile()->getAllowedEvents();
1089 }
1090 if(mDialogSet.mUserProfile->isAdvertisedCapability(Headers::Supported))
1091 {
1092 response.header(h_Supporteds) = mDum.getMasterProfile()->getSupportedOptionTags();
1093 }
1094 }
1095 }
1096 else
1097 {
1098 Helper::makeResponse(response, request, code);
1099 response.header(h_To).param(p_tag) = mId.getLocalTag();
1100 }
1101
1102 DebugLog ( << "Dialog::makeResponse: " << std::endl << std::endl << response);
1103 }
1104
1105
1106 ClientInviteSession*
1107 Dialog::makeClientInviteSession(const SipMessage& response)
1108 {
1109 InviteSessionCreator* creator = dynamic_cast<InviteSessionCreator*>(mDialogSet.getCreator());
1110 if (!creator)
1111 {
1112 assert(0); // !jf! this maybe can assert by evil UAS
1113 return 0;
1114 }
1115 //return mDum.createAppClientInviteSession(*this, *creator);
1116 return new ClientInviteSession(mDum, *this, creator->getLastRequest(),
1117 creator->getInitialOffer(), creator->getEncryptionLevel(), creator->getServerSubscription());
1118 }
1119
1120
1121
1122 ClientSubscription*
1123 Dialog::makeClientSubscription(const SipMessage& request)
1124 {
1125 return new ClientSubscription(mDum, *this, request, mDefaultSubExpiration);
1126 }
1127
1128
1129 ServerInviteSession*
1130 Dialog::makeServerInviteSession(const SipMessage& request)
1131 {
1132 return new ServerInviteSession(mDum, *this, request);
1133 }
1134
1135 ServerSubscription*
1136 Dialog::makeServerSubscription(const SipMessage& request)
1137 {
1138 return new ServerSubscription(mDum, *this, request);
1139 }
1140
1141 Dialog::Exception::Exception(const Data& msg, const Data& file, int line)
1142 : BaseException(msg, file, line)
1143 {
1144 }
1145
1146
1147 void
1148 Dialog::send(SharedPtr<SipMessage> msg)
1149 {
1150 if (msg->isRequest() && msg->header(h_CSeq).method() != ACK)
1151 {
1152 mRequests[msg->header(h_CSeq).sequence()] = msg;
1153 }
1154 mDum.send(msg);
1155 }
1156
1157 void
1158 Dialog::onForkAccepted()
1159 {
1160 ClientInviteSession* uac = dynamic_cast<ClientInviteSession*>(mInviteSession);
1161 if (uac)
1162 {
1163 uac->onForkAccepted();
1164 }
1165 }
1166
1167 void
1168 Dialog::possiblyDie()
1169 {
1170 if (!mDestroying)
1171 {
1172 if (mClientSubscriptions.empty() &&
1173 mServerSubscriptions.empty() &&
1174 !mInviteSession)
1175 {
1176 mDestroying = true;
1177 mDum.destroy(this);
1178 }
1179 }
1180 }
1181
1182 void
1183 Dialog::flowTerminated()
1184 {
1185 // Clear the network association
1186 mNetworkAssociation.clear();
1187
1188 // notify server subscirption dialogs
1189 std::list<ServerSubscription*> tempServerList = mServerSubscriptions; // Create copy since subscription can be deleted
1190 for (std::list<ServerSubscription*>::iterator is=tempServerList.begin();
1191 is != tempServerList.end(); ++is)
1192 {
1193 (*is)->flowTerminated();
1194 }
1195
1196 // notify client subscription dialogs
1197 std::list<ClientSubscription*> tempClientList = mClientSubscriptions; // Create copy since subscription can be deleted
1198 for (std::list<ClientSubscription*>::iterator ic=tempClientList.begin();
1199 ic != tempClientList.end(); ++ic)
1200 {
1201 (*ic)->flowTerminated();
1202 }
1203
1204 // notify invite session dialog
1205 if (mInviteSession)
1206 {
1207 mInviteSession->flowTerminated();
1208 }
1209 }
1210
1211 EncodeStream&
1212 resip::operator<<(EncodeStream& strm, const Dialog& dialog)
1213 {
1214 strm
1215 << "mClientSubscriptions("
1216 << dialog.mClientSubscriptions.size()
1217 << "), "
1218 << "mServerSubscriptions("
1219 << dialog.mServerSubscriptions.size()
1220 << ")";
1221 return strm;
1222 }
1223
1224
1225 /* ====================================================================
1226 * The Vovida Software License, Version 1.0
1227 *
1228 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
1229 *
1230 * Redistribution and use in source and binary forms, with or without
1231 * modification, are permitted provided that the following conditions
1232 * are met:
1233 *
1234 * 1. Redistributions of source code must retain the above copyright
1235 * notice, this list of conditions and the following disclaimer.
1236 *
1237 * 2. Redistributions in binary form must reproduce the above copyright
1238 * notice, this list of conditions and the following disclaimer in
1239 * the documentation and/or other materials provided with the
1240 * distribution.
1241 *
1242 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1243 * and "Vovida Open Communication Application Library (VOCAL)" must
1244 * not be used to endorse or promote products derived from this
1245 * software without prior written permission. For written
1246 * permission, please contact vocal@vovida.org.
1247 *
1248 * 4. Products derived from this software may not be called "VOCAL", nor
1249 * may "VOCAL" appear in their name, without prior written
1250 * permission of Vovida Networks, Inc.
1251 *
1252 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1253 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1254 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1255 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1256 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1257 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1258 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1259 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1260 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1261 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1262 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1263 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1264 * DAMAGE.
1265 *
1266 * ====================================================================
1267 *
1268 * This software consists of voluntary contributions made by Vovida
1269 * Networks, Inc. and many individuals on behalf of Vovida Networks,
1270 * Inc. For more information on Vovida Networks, Inc., please see
1271 * <http://www.vovida.org/>.
1272 *
1273 */
1274
1275
1276

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