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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10975 - (show annotations) (download)
Sun Feb 16 20:04:10 2014 UTC (5 years, 8 months ago) by sgodin
File MIME type: text/plain
File size: 28390 byte(s)
-removed use of raw transport pointers in Tuple and SipMessage (finally!!)
 -required repro record route logic to be modified - no longer store transport specific
  record routes in the Transport class - the Proxy class now tracks these
 -removed SipMessage::getReceivedTransport and added isFromWire method that can be used in
  it's place in various locations

1 #include <queue>
2
3 #include "resip/stack/Helper.hxx"
4 #include "rutil/Logger.hxx"
5 #include "resip/stack/SipFrag.hxx"
6 #include "resip/stack/SipMessage.hxx"
7 #include "resip/dum/ClientSubscription.hxx"
8 #include "resip/dum/Dialog.hxx"
9 #include "resip/dum/DialogUsageManager.hxx"
10 #include "resip/dum/SubscriptionHandler.hxx"
11 #include "resip/dum/SubscriptionCreator.hxx"
12 #include "resip/dum/UsageUseException.hxx"
13
14 #include "resip/dum/AppDialogSet.hxx"
15
16 using namespace resip;
17
18 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
19
20
21 ClientSubscription::ClientSubscription(DialogUsageManager& dum, Dialog& dialog,
22 const SipMessage& request, UInt32 defaultSubExpiration)
23 : BaseSubscription(dum, dialog, request),
24 mOnNewSubscriptionCalled(mEventType == "refer"), // don't call onNewSubscription for Refer subscriptions
25 mEnded(false),
26 mNextRefreshSecs(0),
27 mLastSubSecs(Timer::getTimeSecs()), // Not exactly, but more forgiving
28 mDefaultExpires(defaultSubExpiration),
29 mRefreshing(false),
30 mHaveQueuedRefresh(false),
31 mQueuedRefreshInterval(-1),
32 mLargestNotifyCSeq(0)
33 {
34 DebugLog (<< "ClientSubscription::ClientSubscription from " << request.brief());
35 if(request.method() == SUBSCRIBE)
36 {
37 *mLastRequest = request;
38 if (defaultSubExpiration > 0)
39 {
40 mLastRequest->header(h_Expires).value() = defaultSubExpiration;
41 }
42 }
43 else
44 {
45 // If a NOTIFY request is use to make this ClientSubscription, then create the implied SUBSCRIBE
46 // request as the mLastRequest
47 mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
48 }
49 }
50
51 ClientSubscription::~ClientSubscription()
52 {
53 mDialog.mClientSubscriptions.remove(this);
54
55 while (!mQueuedNotifies.empty())
56 {
57 delete mQueuedNotifies.front();
58 mQueuedNotifies.pop_front();
59 }
60
61 clearDustbin();
62 }
63
64 ClientSubscriptionHandle
65 ClientSubscription::getHandle()
66 {
67 return ClientSubscriptionHandle(mDum, getBaseHandle().getId());
68 }
69
70 void
71 ClientSubscription::dispatch(const SipMessage& msg)
72 {
73 DebugLog (<< "ClientSubscription::dispatch " << msg.brief());
74
75 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
76 assert(handler);
77
78 clearDustbin();
79
80 // asserts are checks the correctness of Dialog::dispatch
81 if (msg.isRequest() )
82 {
83 assert( msg.header(h_RequestLine).getMethod() == NOTIFY );
84 mRefreshing = false;
85
86 // !dlb! 481 NOTIFY iff state is dead?
87
88 //!dcm! -- heavy, should just store enough information to make response
89 //mLastNotify = msg;
90
91 //!fj! There is a bug that prevents onNewSubscription from being called
92 // when, for example, the UAS sends a 408 back and
93 // ClientSubscriptionHandler::onRequestRetry returns 0. A fix was
94 // attempted in revision 10128 but it created a more important
95 // regression. See
96 // http://list.resiprocate.org/archive/resiprocate-devel/thrd83.html#08362
97 // for more details.
98 if (!mOnNewSubscriptionCalled && !getAppDialogSet()->isReUsed())
99 {
100 InfoLog (<< "[ClientSubscription] " << mLastRequest->header(h_To));
101 if (msg.exists(h_Contacts))
102 {
103 mDialog.mRemoteTarget = msg.header(h_Contacts).front();
104 }
105
106 handler->onNewSubscription(getHandle(), msg);
107 mOnNewSubscriptionCalled = true;
108 }
109
110 bool outOfOrder = mLargestNotifyCSeq > msg.header(h_CSeq).sequence();
111 if (!outOfOrder)
112 {
113 mLargestNotifyCSeq = msg.header(h_CSeq).sequence();
114 }
115 else
116 {
117 DebugLog(<< "received out of order notify");
118 }
119
120 mQueuedNotifies.push_back(new QueuedNotify(msg, outOfOrder));
121 if (mQueuedNotifies.size() == 1)
122 {
123 DebugLog(<< "no queued notify");
124 processNextNotify();
125 return;
126 }
127 else
128 {
129 DebugLog(<< "Notify gets queued");
130 }
131 }
132 else
133 {
134 DebugLog(<< "processing client subscription response");
135 processResponse(msg);
136 }
137 }
138
139 void
140 ClientSubscription::processResponse(const SipMessage& msg)
141 {
142 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
143 assert(handler);
144
145 mRefreshing = false;
146 int statusCode = msg.header(h_StatusLine).statusCode();
147
148 if (statusCode >= 200 && statusCode <300)
149 {
150 if (msg.exists(h_Expires))
151 {
152 // grab the expires from the 2xx in case there is not one on the NOTIFY .mjf.
153 UInt32 expires = msg.header(h_Expires).value();
154 UInt32 lastExpires = mLastRequest->header(h_Expires).value();
155 if (expires < lastExpires)
156 {
157 mLastRequest->header(h_Expires).value() = expires;
158 }
159 }
160
161 if(!mOnNewSubscriptionCalled)
162 {
163 // Timer for initial NOTIFY; since we don't know when the initial
164 // SUBSRIBE is sent, we have to set the timer when the 200 comes in, if
165 // it beat the NOTIFY.
166 mDum.addTimer(DumTimeout::WaitForNotify,
167 64*Timer::T1,
168 getBaseHandle(),
169 ++mTimerSeq);
170 }
171
172 sendQueuedRefreshRequest();
173 }
174 else if (!mEnded &&
175 statusCode == 481 &&
176 msg.exists(h_Expires) && msg.header(h_Expires).value() > 0)
177 {
178 InfoLog (<< "Received 481 to SUBSCRIBE, reSUBSCRIBEing (presence server probably restarted) "
179 << mLastRequest->header(h_To));
180
181 reSubscribe(); // will delete "this"
182 return;
183 }
184 else if (!mEnded &&
185 (statusCode == 408 ||
186 (statusCode == 503 && !msg.isFromWire()) ||
187 ((statusCode == 413 ||
188 statusCode == 480 ||
189 statusCode == 486 ||
190 statusCode == 500 ||
191 statusCode == 503 ||
192 statusCode == 600 ||
193 statusCode == 603) &&
194 msg.exists(h_RetryAfter))))
195 {
196 int retry;
197 int retryAfter = 0;
198 if(msg.exists(h_RetryAfter))
199 {
200 retryAfter = msg.header(h_RetryAfter).value();
201 }
202
203 InfoLog (<< "Received " << statusCode << " to SUBSCRIBE "
204 << mLastRequest->header(h_To));
205 retry = handler->onRequestRetry(getHandle(), retryAfter, msg);
206
207 if (retry < 0)
208 {
209 DebugLog(<< "Application requested failure on Retry-After");
210 mEnded = true;
211 handler->onTerminated(getHandle(), &msg);
212 delete this;
213 return;
214 }
215 else if (retry == 0)
216 {
217 DebugLog(<< "Application requested immediate retry on Retry-After");
218
219 if (mOnNewSubscriptionCalled)
220 {
221 // If we already have a dialog, then just refresh again
222 requestRefresh();
223 }
224 else
225 {
226 reSubscribe(); // will delete "this"
227 return;
228 }
229 }
230 else
231 {
232 // leave the usage around until the timeout
233 // !dlb! would be nice to set the state to something dead, but not used
234 mDum.addTimer(DumTimeout::SubscriptionRetry,
235 retry,
236 getBaseHandle(),
237 ++mTimerSeq);
238 // leave the usage around until the timeout
239 return;
240 }
241 }
242 else if (msg.header(h_StatusLine).statusCode() >= 300)
243 {
244 if (msg.header(h_StatusLine).statusCode() == 423
245 && msg.exists(h_MinExpires))
246 {
247 requestRefresh(msg.header(h_MinExpires).value());
248 }
249 else
250 {
251 mEnded = true;
252 handler->onTerminated(getHandle(), &msg);
253 delete this;
254 return;
255 }
256 }
257 }
258
259 void
260 ClientSubscription::processNextNotify()
261 {
262 //!dcm! There is a timing issue in this code which can cause this to be
263 //!called when there are no queued NOTIFY messages. Probably a subscription
264 //!teardown/timer crossover.
265 //assert(!mQueuedNotifies.empty());
266 if (mQueuedNotifies.empty())
267 {
268 return;
269 }
270
271 QueuedNotify* qn = mQueuedNotifies.front();
272 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
273 assert(handler);
274
275 unsigned long refreshInterval = 0;
276 bool setRefreshTimer=false;
277 if (!qn->outOfOrder())
278 {
279 UInt32 expires = 0;
280 //default to 3600 seconds so non-compliant endpoints don't result in leaked usages
281 if (qn->notify().exists(h_SubscriptionState) && qn->notify().header(h_SubscriptionState).exists(p_expires))
282 {
283 expires = qn->notify().header(h_SubscriptionState).param(p_expires);
284 }
285 else if (mLastRequest->exists(h_Expires))
286 {
287 expires = mLastRequest->header(h_Expires).value();
288 }
289 else if (mDefaultExpires)
290 {
291 /* if we haven't gotten an expires value from:
292 1. the subscription state from this notify
293 2. the last request
294 then use the default expires (meaning it came from the 2xx in response
295 to the initial SUBSCRIBE). .mjf.
296 */
297 expires = mDefaultExpires;
298 }
299 else
300 {
301 expires = 3600;
302 }
303
304 if (!mLastRequest->exists(h_Expires))
305 {
306 DebugLog(<< "No expires header in last request, set to " << expires);
307 mLastRequest->header(h_Expires).value() = expires;
308 }
309
310 if(!qn->notify().exists(h_SubscriptionState) ||
311 !isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
312 {
313 // Don't do this stuff for a NOTIFY terminated.
314 UInt64 now = Timer::getTimeSecs();
315 refreshInterval = Helper::aBitSmallerThan((signed long)expires);
316
317 if (mNextRefreshSecs == 0 || now + refreshInterval < mNextRefreshSecs)
318 {
319 mNextRefreshSecs = now + refreshInterval;
320 setRefreshTimer = true;
321 }
322 }
323 }
324 //if no subscription state header, treat as an extension. Only allow for
325 //refer to handle non-compliant implementations
326 if (!qn->notify().exists(h_SubscriptionState))
327 {
328 if (qn->notify().exists(h_Event) && qn->notify().header(h_Event).value() == "refer")
329 {
330 SipFrag* frag = dynamic_cast<SipFrag*>(qn->notify().getContents());
331 if (frag)
332 {
333 if (frag->message().isResponse())
334 {
335 int code = frag->message().header(h_StatusLine).statusCode();
336 if (code < 200)
337 {
338 handler->onUpdateExtension(getHandle(), qn->notify(), qn->outOfOrder());
339 }
340 else
341 {
342 acceptUpdate();
343 mEnded = true;
344 handler->onTerminated(getHandle(), &qn->notify());
345 delete this;
346 }
347 }
348 else
349 {
350 acceptUpdate();
351 mEnded = true;
352 handler->onTerminated(getHandle(), &qn->notify());
353 delete this;
354 }
355 }
356 else
357 {
358 acceptUpdate();
359 mEnded = true;
360 handler->onTerminated(getHandle(), &qn->notify());
361 delete this;
362 }
363 }
364 else
365 {
366 mDialog.makeResponse(*mLastResponse, qn->notify(), 400);
367 mLastResponse->header(h_StatusLine).reason() = "Missing Subscription-State header";
368 send(mLastResponse);
369 mEnded = true;
370 handler->onTerminated(getHandle(), &qn->notify());
371 delete this;
372 }
373 return;
374 }
375
376 if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Active))
377 {
378 if (setRefreshTimer)
379 {
380 scheduleRefresh(refreshInterval);
381 }
382
383 handler->onUpdateActive(getHandle(), qn->notify(), qn->outOfOrder());
384 }
385 else if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Pending))
386 {
387 if (setRefreshTimer)
388 {
389 scheduleRefresh(refreshInterval);
390 }
391
392 handler->onUpdatePending(getHandle(), qn->notify(), qn->outOfOrder());
393 }
394 else if (isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
395 {
396 if(mLastRequest->header(h_Expires).value()!=0 &&
397 isEqualNoCase(qn->notify().header(h_SubscriptionState).param(p_reason), "timeout"))
398 {
399 // Unexpected timeout of some sort. Look closer.
400 if(mNextRefreshSecs==0)
401 {
402 // No refresh scheduled; maybe we are trying to avoid a tight SUB/
403 // NOT loop here?
404 if(Helper::aBitSmallerThan((signed long)(Timer::getTimeSecs() - mLastSubSecs)) < 2)
405 {
406 acceptUpdate(200, "I just sent a refresh, what more do you want "
407 "from me?");
408 }
409 else
410 {
411 acceptUpdate(200, "Why didn't I refresh here?");
412 }
413 }
414 else
415 {
416 acceptUpdate(200, "You terminated my subscription early! What "
417 "gives?");
418 }
419 }
420 else
421 {
422 acceptUpdate();
423 }
424 mEnded = true;
425 handler->onTerminated(getHandle(), &qn->notify());
426 DebugLog (<< "[ClientSubscription] " << mLastRequest->header(h_To) << "[ClientSubscription] Terminated");
427 delete this;
428 return;
429 }
430 else if (!mEnded)
431 {
432 handler->onUpdateExtension(getHandle(), qn->notify(), qn->outOfOrder());
433 }
434 else if (mEnded)
435 {
436 // We received a NOTIFY message when we thought the subscription was
437 // ended. This can happen, for example, when a previously sent NOTIFY gets
438 // resent while we (ClientSubscription) are trying to terminate the
439 // subscription. If we don't accept/reject this NOTIFY, it will stay into
440 // the mQueuedNotifies queue and we'll never terminate the subscription
441 // even if the server sends a NOTIFY/terminated. All received NOTIFY would
442 // get piled up on mQueuedNotifies and they will never get processed.
443 //
444 // Note that if that NOTIFY is in fact the terminated one, it will get
445 // caught by another if statement above and acted upon appropriately.
446 //
447 //!fjoanis! Is 481 a proper error code in this case?
448 InfoLog(<< "[ClientSubscription] received NOTIFY when subscription was ended, rejecting it...");
449 rejectUpdate(481);
450 }
451 }
452
453 void
454 ClientSubscription::dispatch(const DumTimeout& timer)
455 {
456 if (timer.seq() == mTimerSeq)
457 {
458 if(timer.type() == DumTimeout::WaitForNotify)
459 {
460 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
461 if(mOnNewSubscriptionCalled && mEnded)
462 {
463 // NOTIFY terminated didn't come in
464 handler->onTerminated(getHandle(),0);
465 delete this;
466 return;
467 }
468
469 // Initial NOTIFY never came in; let app decide what to do
470 handler->onNotifyNotReceived(getHandle());
471 }
472 else if (timer.type() == DumTimeout::SubscriptionRetry)
473 {
474 // this indicates that the ClientSubscription was created by a 408
475 if (mOnNewSubscriptionCalled)
476 {
477 InfoLog(<< "ClientSubscription: application retry refresh");
478 requestRefresh();
479 }
480 else
481 {
482 InfoLog(<< "ClientSubscription: application retry new request");
483 reSubscribe(); // will delete "this"
484 return;
485 }
486 }
487 else if(timer.type() == DumTimeout::Subscription)
488 {
489 requestRefresh();
490 }
491 }
492 else if(timer.seq() == 0 && timer.type() == DumTimeout::SendNextNotify)
493 {
494 DebugLog(<< "got DumTimeout::SendNextNotify");
495 processNextNotify();
496 }
497 }
498
499 void
500 ClientSubscription::requestRefresh(UInt32 expires)
501 {
502 if (!mEnded)
503 {
504 if (mRefreshing)
505 {
506 DebugLog(<< "queue up refresh request");
507 mHaveQueuedRefresh = true;
508 mQueuedRefreshInterval = expires;
509 return;
510 }
511
512 mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
513 //!dcm! -- need a mechanism to retrieve this for the event package...part of
514 //the map that stores the handlers, or part of the handler API
515 if(expires > 0)
516 {
517 mLastRequest->header(h_Expires).value() = expires;
518 }
519 mNextRefreshSecs = 0;
520 InfoLog (<< "Refresh subscription: " << mLastRequest->header(h_Contacts).front());
521 mRefreshing = true;
522 mLastSubSecs = Timer::getTimeSecs();
523 send(mLastRequest);
524 // Timer for reSUB NOTIFY.
525 mDum.addTimer(DumTimeout::WaitForNotify,
526 64*Timer::T1,
527 getBaseHandle(),
528 ++mTimerSeq);
529 }
530 }
531
532 class ClientSubscriptionRefreshCommand : public DumCommandAdapter
533 {
534 public:
535 ClientSubscriptionRefreshCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, UInt32 expires)
536 : mClientSubscriptionHandle(clientSubscriptionHandle),
537 mExpires(expires)
538 {
539
540 }
541 virtual void executeCommand()
542 {
543 if(mClientSubscriptionHandle.isValid())
544 {
545 mClientSubscriptionHandle->requestRefresh(mExpires);
546 }
547 }
548
549 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
550 {
551 return strm << "ClientSubscriptionRefreshCommand";
552 }
553 private:
554 ClientSubscriptionHandle mClientSubscriptionHandle;
555 UInt32 mExpires;
556 };
557
558 void
559 ClientSubscription::requestRefreshCommand(UInt32 expires)
560 {
561 mDum.post(new ClientSubscriptionRefreshCommand(getHandle(), expires));
562 }
563
564 void
565 ClientSubscription::end()
566 {
567 end(false /* immediate? */);
568 }
569
570 void
571 ClientSubscription::end(bool immediate)
572 {
573 InfoLog (<< "End subscription: " << mLastRequest->header(h_RequestLine).uri());
574
575 if (!mEnded)
576 {
577 if(!immediate)
578 {
579 mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
580 mLastRequest->header(h_Expires).value() = 0;
581 mEnded = true;
582 send(mLastRequest);
583 // Timer for NOTIFY terminated
584 mDum.addTimer(DumTimeout::WaitForNotify,
585 64*Timer::T1,
586 getBaseHandle(),
587 ++mTimerSeq);
588 }
589 else
590 {
591 delete this;
592 }
593 }
594 }
595
596 class ClientSubscriptionEndCommand : public DumCommandAdapter
597 {
598 public:
599 ClientSubscriptionEndCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, bool immediate)
600 :mClientSubscriptionHandle(clientSubscriptionHandle), mImmediate(immediate)
601 {
602
603 }
604
605 virtual void executeCommand()
606 {
607 if(mClientSubscriptionHandle.isValid())
608 {
609 mClientSubscriptionHandle->end(mImmediate);
610 }
611 }
612
613 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
614 {
615 return strm << "ClientSubscriptionEndCommand";
616 }
617 private:
618 ClientSubscriptionHandle mClientSubscriptionHandle;
619 bool mImmediate;
620 };
621
622 void
623 ClientSubscription::endCommand(bool immediate)
624 {
625 mDum.post(new ClientSubscriptionEndCommand(getHandle(), immediate));
626 }
627
628 void
629 ClientSubscription::acceptUpdate(int statusCode, const char* reason)
630 {
631 assert(!mQueuedNotifies.empty());
632 if (mQueuedNotifies.empty())
633 {
634 InfoLog(<< "No queued notify to accept");
635 return;
636 }
637
638 QueuedNotify* qn = mQueuedNotifies.front();
639 mQueuedNotifies.pop_front();
640 mDustbin.push_back(qn);
641 mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
642 if(reason)
643 {
644 mLastResponse->header(h_StatusLine).reason()=reason;
645 }
646 send(mLastResponse);
647 }
648
649 class ClientSubscriptionAcceptUpdateCommand : public DumCommandAdapter
650 {
651 public:
652 ClientSubscriptionAcceptUpdateCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, int statusCode, const char* reason)
653 : mClientSubscriptionHandle(clientSubscriptionHandle),
654 mStatusCode(statusCode),
655 mReason(reason ? Data(reason) : Data::Empty)
656 {
657
658 }
659
660 virtual void executeCommand()
661 {
662 if(mClientSubscriptionHandle.isValid())
663 {
664 mClientSubscriptionHandle->acceptUpdate(mStatusCode, mReason.c_str());
665 }
666 }
667
668 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
669 {
670 return strm << "ClientSubscriptionAcceptUpdateCommand";
671 }
672 private:
673 ClientSubscriptionHandle mClientSubscriptionHandle;
674 int mStatusCode;
675 Data mReason;
676 };
677
678 void
679 ClientSubscription::acceptUpdateCommand(int statusCode, const char* reason)
680 {
681 mDum.post(new ClientSubscriptionAcceptUpdateCommand(getHandle(), statusCode, reason));
682 }
683
684 void
685 ClientSubscription::reSubscribe()
686 {
687 NameAddr target(mLastRequest->header(h_To));
688 target.remove(p_tag); // ensure To tag is removed
689 SharedPtr<SipMessage> sub = mDum.makeSubscription(target, getUserProfile(), getEventType(), getAppDialogSet()->reuse());
690 mDum.send(sub);
691
692 delete this;
693 }
694
695 void
696 ClientSubscription::send(SharedPtr<SipMessage> msg)
697 {
698 DialogUsage::send(msg);
699
700 if (!mEnded)
701 {
702 if (!mQueuedNotifies.empty() && msg->isResponse())
703 {
704 mDum.addTimer(DumTimeout::SendNextNotify,
705 0,
706 getBaseHandle(),
707 0);
708 }
709 }
710
711 }
712
713 void
714 ClientSubscription::rejectUpdate(int statusCode, const Data& reasonPhrase)
715 {
716 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
717 assert(handler);
718 assert(!mQueuedNotifies.empty());
719 if (mQueuedNotifies.empty())
720 {
721 InfoLog(<< "No queued notify to reject");
722 return;
723 }
724
725 QueuedNotify* qn = mQueuedNotifies.front();
726 mQueuedNotifies.pop_front();
727 mDustbin.push_back(qn);
728
729 mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
730 if (!reasonPhrase.empty())
731 {
732 mLastResponse->header(h_StatusLine).reason() = reasonPhrase;
733 }
734
735 send(mLastResponse);
736 switch (Helper::determineFailureMessageEffect(*mLastResponse))
737 {
738 case Helper::TransactionTermination:
739 case Helper::RetryAfter:
740 break;
741 case Helper::OptionalRetryAfter:
742 case Helper::ApplicationDependant:
743 throw UsageUseException("Not a reasonable code to reject a NOTIFY with inside an established dialog.",
744 __FILE__, __LINE__);
745 break;
746 case Helper::DialogTermination: //?dcm? -- throw or destroy this?
747 case Helper::UsageTermination:
748 mEnded = true;
749 handler->onTerminated(getHandle(), mLastResponse.get());
750 delete this;
751 break;
752 }
753 }
754
755 class ClientSubscriptionRejectUpdateCommand : public DumCommandAdapter
756 {
757 public:
758 ClientSubscriptionRejectUpdateCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, int statusCode, const Data& reasonPhrase)
759 : mClientSubscriptionHandle(clientSubscriptionHandle),
760 mStatusCode(statusCode),
761 mReasonPhrase(reasonPhrase)
762 {
763 }
764
765 virtual void executeCommand()
766 {
767 if(mClientSubscriptionHandle.isValid())
768 {
769 mClientSubscriptionHandle->rejectUpdate(mStatusCode, mReasonPhrase);
770 }
771 }
772
773 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
774 {
775 return strm << "ClientSubscriptionRejectUpdateCommand";
776 }
777 private:
778 ClientSubscriptionHandle mClientSubscriptionHandle;
779 int mStatusCode;
780 Data mReasonPhrase;
781 };
782
783 void
784 ClientSubscription::rejectUpdateCommand(int statusCode, const Data& reasonPhrase)
785 {
786 mDum.post(new ClientSubscriptionRejectUpdateCommand(getHandle(), statusCode, reasonPhrase));
787 }
788
789 void ClientSubscription::dialogDestroyed(const SipMessage& msg)
790 {
791 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
792 assert(handler);
793 mEnded = true;
794 handler->onTerminated(getHandle(), &msg);
795 delete this;
796 }
797
798 EncodeStream&
799 ClientSubscription::dump(EncodeStream& strm) const
800 {
801 strm << "ClientSubscription " << mLastRequest->header(h_From).uri();
802 return strm;
803 }
804
805 void
806 ClientSubscription::onReadyToSend(SipMessage& msg)
807 {
808 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
809 assert(handler);
810 handler->onReadyToSend(getHandle(), msg);
811 }
812
813 void
814 ClientSubscription::flowTerminated()
815 {
816 // notify handler
817 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
818 assert(handler);
819 handler->onFlowTerminated(getHandle());
820 }
821
822 void
823 ClientSubscription::sendQueuedRefreshRequest()
824 {
825 assert(!mRefreshing);
826
827 if (mHaveQueuedRefresh)
828 {
829 DebugLog(<< "send queued refresh request");
830 mHaveQueuedRefresh = false;
831 requestRefresh(mQueuedRefreshInterval);
832 }
833 }
834
835 void
836 ClientSubscription::clearDustbin()
837 {
838 for (Dustbin::iterator it = mDustbin.begin(); it != mDustbin.end(); ++it)
839 {
840 delete *it;
841 }
842
843 mDustbin.clear();
844
845 }
846
847 void
848 ClientSubscription::scheduleRefresh(unsigned long refreshInterval)
849 {
850 if(mNextRefreshSecs-mLastSubSecs < 2)
851 {
852 // Server is using an unreasonably short expiry; we sent a SUB
853 // very recently, and the server has told us to refresh almost
854 // immediately. By the time our refresh timer pops, less than two
855 // seconds will have elapsed since our last SUBSCRIBE. This is
856 // unacceptable. Just let the subscription end.
857 // It is also possible that our refresh SUB has crossed an update NOTIFY
858 // on the wire; in this case, the right thing to do is to wait until a
859 // NOTIFY for our refresh SUB comes in, which is exactly what this code
860 // ends up doing in this case.
861 // ?bwc? Make this minimum inter-SUBSCRIBE time configurable?
862 WarningLog(<< "Server is using an unacceptably short expiry. "
863 "Letting the subscription end so we don't get in a"
864 " tight SUB/NOT loop.");
865 mNextRefreshSecs=0;
866 }
867 else
868 {
869 mDum.addTimer(DumTimeout::Subscription, refreshInterval, getBaseHandle(), ++mTimerSeq);
870 InfoLog (<< "[ClientSubscription] reSUBSCRIBE in " << refreshInterval);
871 }
872 }
873
874
875
876 /* ====================================================================
877 * The Vovida Software License, Version 1.0
878 *
879 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
880 *
881 * Redistribution and use in source and binary forms, with or without
882 * modification, are permitted provided that the following conditions
883 * are met:
884 *
885 * 1. Redistributions of source code must retain the above copyright
886 * notice, this list of conditions and the following disclaimer.
887 *
888 * 2. Redistributions in binary form must reproduce the above copyright
889 * notice, this list of conditions and the following disclaimer in
890 * the documentation and/or other materials provided with the
891 * distribution.
892 *
893 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
894 * and "Vovida Open Communication Application Library (VOCAL)" must
895 * not be used to endorse or promote products derived from this
896 * software without prior written permission. For written
897 * permission, please contact vocal@vovida.org.
898 *
899 * 4. Products derived from this software may not be called "VOCAL", nor
900 * may "VOCAL" appear in their name, without prior written
901 * permission of Vovida Networks, Inc.
902 *
903 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
904 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
905 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
906 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
907 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
908 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
909 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
910 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
911 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
912 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
913 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
914 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
915 * DAMAGE.
916 *
917 * ====================================================================
918 *
919 * This software consists of voluntary contributions made by Vovida
920 * Networks, Inc. and many individuals on behalf of Vovida Networks,
921 * Inc. For more information on Vovida Networks, Inc., please see
922 * <http://www.vovida.org/>.
923 *
924 */

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