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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10975 - (hide annotations) (download)
Sun Feb 16 20:04:10 2014 UTC (5 years, 9 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 daniel 5767 #include <queue>
2    
3 derek 5283 #include "resip/stack/Helper.hxx"
4 jason 5276 #include "rutil/Logger.hxx"
5 derek 5283 #include "resip/stack/SipFrag.hxx"
6     #include "resip/stack/SipMessage.hxx"
7 jason 5276 #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 derek 4062
14 jason 5276 #include "resip/dum/AppDialogSet.hxx"
15 cktam 4408
16 derek 4062 using namespace resip;
17    
18     #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
19    
20    
21 mfroman 6495 ClientSubscription::ClientSubscription(DialogUsageManager& dum, Dialog& dialog,
22 sgodin 6507 const SipMessage& request, UInt32 defaultSubExpiration)
23 derek 4062 : BaseSubscription(dum, dialog, request),
24     mOnNewSubscriptionCalled(mEventType == "refer"), // don't call onNewSubscription for Refer subscriptions
25 jason 4414 mEnded(false),
26 bcampen 8958 mNextRefreshSecs(0),
27     mLastSubSecs(Timer::getTimeSecs()), // Not exactly, but more forgiving
28 mfroman 6495 mDefaultExpires(defaultSubExpiration),
29 daniel 5767 mRefreshing(false),
30     mHaveQueuedRefresh(false),
31     mQueuedRefreshInterval(-1),
32 daniel 5825 mLargestNotifyCSeq(0)
33 derek 4062 {
34 derek 5646 DebugLog (<< "ClientSubscription::ClientSubscription from " << request.brief());
35 sgodin 8701 if(request.method() == SUBSCRIBE)
36     {
37     *mLastRequest = request;
38 jgeras 8778 if (defaultSubExpiration > 0)
39     {
40     mLastRequest->header(h_Expires).value() = defaultSubExpiration;
41     }
42 sgodin 8701 }
43     else
44     {
45 sgodin 9033 // 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 sgodin 8701 }
49 derek 4062 }
50    
51     ClientSubscription::~ClientSubscription()
52     {
53     mDialog.mClientSubscriptions.remove(this);
54 daniel 5767
55     while (!mQueuedNotifies.empty())
56     {
57     delete mQueuedNotifies.front();
58     mQueuedNotifies.pop_front();
59     }
60 daniel 5770
61     clearDustbin();
62 derek 4062 }
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 jason 6090 DebugLog (<< "ClientSubscription::dispatch " << msg.brief());
74    
75 derek 4062 ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
76     assert(handler);
77    
78 daniel 5770 clearDustbin();
79    
80 derek 4062 // asserts are checks the correctness of Dialog::dispatch
81     if (msg.isRequest() )
82     {
83     assert( msg.header(h_RequestLine).getMethod() == NOTIFY );
84 daniel 5767 mRefreshing = false;
85 derek 4062
86     // !dlb! 481 NOTIFY iff state is dead?
87    
88     //!dcm! -- heavy, should just store enough information to make response
89 daniel 5767 //mLastNotify = msg;
90 derek 4062
91 fjoanis 10327 //!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 fjoanis 10326 if (!mOnNewSubscriptionCalled && !getAppDialogSet()->isReUsed())
99 derek 4062 {
100 daniel 5757 InfoLog (<< "[ClientSubscription] " << mLastRequest->header(h_To));
101 jason 6090 if (msg.exists(h_Contacts))
102     {
103     mDialog.mRemoteTarget = msg.header(h_Contacts).front();
104     }
105    
106 derek 4062 handler->onNewSubscription(getHandle(), msg);
107     mOnNewSubscriptionCalled = true;
108     }
109 daniel 5767
110     bool outOfOrder = mLargestNotifyCSeq > msg.header(h_CSeq).sequence();
111     if (!outOfOrder)
112 derek 4062 {
113 daniel 5767 mLargestNotifyCSeq = msg.header(h_CSeq).sequence();
114 derek 4062 }
115 daniel 5767 else
116 daniel 5748 {
117 daniel 5767 DebugLog(<< "received out of order notify");
118 daniel 5748 }
119 daniel 5767
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 derek 4062 else
128     {
129 daniel 5767 DebugLog(<< "Notify gets queued");
130 derek 4062 }
131 daniel 5767 }
132     else
133     {
134     DebugLog(<< "processing client subscription response");
135     processResponse(msg);
136     }
137     }
138 jason 4414
139 daniel 5767 void
140     ClientSubscription::processResponse(const SipMessage& msg)
141     {
142     ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
143     assert(handler);
144    
145     mRefreshing = false;
146 sgodin 9033 int statusCode = msg.header(h_StatusLine).statusCode();
147 mfroman 6495
148 sgodin 9033 if (statusCode >= 200 && statusCode <300)
149 daniel 5767 {
150 mfroman 6495 if (msg.exists(h_Expires))
151     {
152     // grab the expires from the 2xx in case there is not one on the NOTIFY .mjf.
153 sgodin 6507 UInt32 expires = msg.header(h_Expires).value();
154     UInt32 lastExpires = mLastRequest->header(h_Expires).value();
155 mfroman 6495 if (expires < lastExpires)
156     {
157     mLastRequest->header(h_Expires).value() = expires;
158     }
159     }
160 bcampen 8478
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 daniel 5767 sendQueuedRefreshRequest();
173     }
174 sgodin 8204 else if (!mEnded &&
175 sgodin 9033 statusCode == 481 &&
176 daniel 5767 msg.exists(h_Expires) && msg.header(h_Expires).value() > 0)
177     {
178     InfoLog (<< "Received 481 to SUBSCRIBE, reSUBSCRIBEing (presence server probably restarted) "
179 sgodin 8204 << mLastRequest->header(h_To));
180 daniel 5767
181 sgodin 9033 reSubscribe(); // will delete "this"
182 daniel 5767 return;
183     }
184 sgodin 8204 else if (!mEnded &&
185 sgodin 9033 (statusCode == 408 ||
186 sgodin 10975 (statusCode == 503 && !msg.isFromWire()) ||
187 sgodin 9033 ((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 daniel 5767 {
196     int retry;
197 sgodin 9033 int retryAfter = 0;
198     if(msg.exists(h_RetryAfter))
199 derek 4062 {
200 sgodin 9033 retryAfter = msg.header(h_RetryAfter).value();
201 derek 4062 }
202    
203 sgodin 9033 InfoLog (<< "Received " << statusCode << " to SUBSCRIBE "
204     << mLastRequest->header(h_To));
205     retry = handler->onRequestRetry(getHandle(), retryAfter, msg);
206    
207 daniel 5767 if (retry < 0)
208 derek 4062 {
209 daniel 5767 DebugLog(<< "Application requested failure on Retry-After");
210     mEnded = true;
211 bcampen 8478 handler->onTerminated(getHandle(), &msg);
212 daniel 5767 delete this;
213     return;
214     }
215     else if (retry == 0)
216     {
217     DebugLog(<< "Application requested immediate retry on Retry-After");
218 sgodin 8204
219     if (mOnNewSubscriptionCalled)
220 derek 4062 {
221 sgodin 8204 // If we already have a dialog, then just refresh again
222     requestRefresh();
223 derek 4062 }
224     else
225 daniel 5767 {
226 sgodin 9033 reSubscribe(); // will delete "this"
227 sgodin 8204 return;
228 derek 4062 }
229 daniel 5767 }
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 derek 4062 return;
240 sgodin 8204 }
241 daniel 5767 }
242     else if (msg.header(h_StatusLine).statusCode() >= 300)
243     {
244     if (msg.header(h_StatusLine).statusCode() == 423
245     && msg.exists(h_MinExpires))
246 derek 4062 {
247 daniel 5767 requestRefresh(msg.header(h_MinExpires).value());
248 derek 4062 }
249 daniel 5767 else
250     {
251     mEnded = true;
252 bcampen 8478 handler->onTerminated(getHandle(), &msg);
253 daniel 5767 delete this;
254     return;
255     }
256     }
257     }
258 derek 4062
259 daniel 5767 void
260     ClientSubscription::processNextNotify()
261     {
262 bcampen 7466 //!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 daniel 5767
271     QueuedNotify* qn = mQueuedNotifies.front();
272     ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
273     assert(handler);
274    
275     unsigned long refreshInterval = 0;
276 sgodin 8773 bool setRefreshTimer=false;
277 daniel 5767 if (!qn->outOfOrder())
278     {
279 sgodin 6507 UInt32 expires = 0;
280 daniel 5767 //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 derek 4062 {
283 daniel 5767 expires = qn->notify().header(h_SubscriptionState).param(p_expires);
284 derek 4062 }
285 daniel 5767 else if (mLastRequest->exists(h_Expires))
286 derek 4062 {
287 daniel 5767 expires = mLastRequest->header(h_Expires).value();
288 derek 4062 }
289 mfroman 6495 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 daniel 5767 else
300 derek 4062 {
301 daniel 5767 expires = 3600;
302 derek 4062 }
303 daniel 5767
304     if (!mLastRequest->exists(h_Expires))
305 derek 4062 {
306 daniel 5825 DebugLog(<< "No expires header in last request, set to " << expires);
307 daniel 5767 mLastRequest->header(h_Expires).value() = expires;
308 derek 4062 }
309 bcampen 8958
310 bcampen 8987 if(!qn->notify().exists(h_SubscriptionState) ||
311     !isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
312 daniel 5767 {
313 bcampen 8958 // 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 daniel 5767 }
323 derek 4062 }
324 daniel 5767 //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 derek 4062 {
328 daniel 5767 if (qn->notify().exists(h_Event) && qn->notify().header(h_Event).value() == "refer")
329 derek 4062 {
330 daniel 5767 SipFrag* frag = dynamic_cast<SipFrag*>(qn->notify().getContents());
331     if (frag)
332 dlb 4474 {
333 daniel 5767 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 bcampen 8478 handler->onTerminated(getHandle(), &qn->notify());
345 daniel 5767 delete this;
346     }
347     }
348 bcampen 8201 else
349     {
350     acceptUpdate();
351     mEnded = true;
352 bcampen 8478 handler->onTerminated(getHandle(), &qn->notify());
353 bcampen 8201 delete this;
354     }
355 dlb 4474 }
356     else
357     {
358 daniel 5767 acceptUpdate();
359     mEnded = true;
360 bcampen 8478 handler->onTerminated(getHandle(), &qn->notify());
361 jason 4401 delete this;
362 derek 4062 }
363 daniel 5767 }
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 bcampen 8478 handler->onTerminated(getHandle(), &qn->notify());
371 derek 4062 delete this;
372     }
373 daniel 5767 return;
374     }
375    
376     if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Active))
377     {
378 sgodin 8773 if (setRefreshTimer)
379 derek 4062 {
380 bcampen 8958 scheduleRefresh(refreshInterval);
381 derek 4062 }
382 daniel 5767
383     handler->onUpdateActive(getHandle(), qn->notify(), qn->outOfOrder());
384 derek 4062 }
385 daniel 5767 else if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Pending))
386     {
387 sgodin 8773 if (setRefreshTimer)
388 daniel 5767 {
389 bcampen 8958 scheduleRefresh(refreshInterval);
390 daniel 5767 }
391    
392     handler->onUpdatePending(getHandle(), qn->notify(), qn->outOfOrder());
393     }
394     else if (isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
395     {
396 bcampen 8958 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 daniel 5767 mEnded = true;
425 bcampen 8478 handler->onTerminated(getHandle(), &qn->notify());
426 daniel 5767 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 sgodin 8806 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 derek 4062 }
452    
453     void
454     ClientSubscription::dispatch(const DumTimeout& timer)
455     {
456     if (timer.seq() == mTimerSeq)
457     {
458 bcampen 8478 if(timer.type() == DumTimeout::WaitForNotify)
459 derek 4062 {
460 bcampen 8478 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 dlb 4300 // 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 sgodin 9033 reSubscribe(); // will delete "this"
484     return;
485 dlb 4300 }
486 derek 4062 }
487 sgodin 9033 else if(timer.type() == DumTimeout::Subscription)
488 derek 4062 {
489     requestRefresh();
490     }
491     }
492 sgodin 8700 else if(timer.seq() == 0 && timer.type() == DumTimeout::SendNextNotify)
493 daniel 5767 {
494 sgodin 8700 DebugLog(<< "got DumTimeout::SendNextNotify");
495     processNextNotify();
496 daniel 5767 }
497 derek 4062 }
498    
499     void
500 sgodin 6507 ClientSubscription::requestRefresh(UInt32 expires)
501 derek 4062 {
502     if (!mEnded)
503     {
504 daniel 5767 if (mRefreshing)
505     {
506     DebugLog(<< "queue up refresh request");
507     mHaveQueuedRefresh = true;
508     mQueuedRefreshInterval = expires;
509     return;
510     }
511    
512 daniel 5757 mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
513 derek 4062 //!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 daniel 5757 mLastRequest->header(h_Expires).value() = expires;
518 derek 4062 }
519 bcampen 8958 mNextRefreshSecs = 0;
520 daniel 5757 InfoLog (<< "Refresh subscription: " << mLastRequest->header(h_Contacts).front());
521 daniel 5767 mRefreshing = true;
522 bcampen 8958 mLastSubSecs = Timer::getTimeSecs();
523 derek 5644 send(mLastRequest);
524 bcampen 8478 // Timer for reSUB NOTIFY.
525     mDum.addTimer(DumTimeout::WaitForNotify,
526     64*Timer::T1,
527     getBaseHandle(),
528     ++mTimerSeq);
529 derek 4062 }
530     }
531    
532 nash 7042 class ClientSubscriptionRefreshCommand : public DumCommandAdapter
533     {
534     public:
535 fjoanis 9956 ClientSubscriptionRefreshCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, UInt32 expires)
536     : mClientSubscriptionHandle(clientSubscriptionHandle),
537 nash 7042 mExpires(expires)
538     {
539    
540     }
541     virtual void executeCommand()
542     {
543 fjoanis 9956 if(mClientSubscriptionHandle.isValid())
544     {
545     mClientSubscriptionHandle->requestRefresh(mExpires);
546     }
547 nash 7042 }
548    
549 jmatthewsr 8161 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
550 nash 7042 {
551     return strm << "ClientSubscriptionRefreshCommand";
552     }
553     private:
554 fjoanis 9956 ClientSubscriptionHandle mClientSubscriptionHandle;
555 nash 7042 UInt32 mExpires;
556     };
557    
558 derek 4062 void
559 nash 7042 ClientSubscription::requestRefreshCommand(UInt32 expires)
560     {
561 fjoanis 9956 mDum.post(new ClientSubscriptionRefreshCommand(getHandle(), expires));
562 nash 7042 }
563    
564     void
565 derek 4062 ClientSubscription::end()
566     {
567 sgodin 9299 end(false /* immediate? */);
568     }
569    
570     void
571     ClientSubscription::end(bool immediate)
572     {
573 daniel 5757 InfoLog (<< "End subscription: " << mLastRequest->header(h_RequestLine).uri());
574 derek 4062
575     if (!mEnded)
576     {
577 sgodin 9299 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 derek 4062 }
594     }
595    
596 nash 7042 class ClientSubscriptionEndCommand : public DumCommandAdapter
597     {
598     public:
599 fjoanis 9956 ClientSubscriptionEndCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, bool immediate)
600     :mClientSubscriptionHandle(clientSubscriptionHandle), mImmediate(immediate)
601 nash 7042 {
602    
603     }
604    
605     virtual void executeCommand()
606     {
607 fjoanis 9956 if(mClientSubscriptionHandle.isValid())
608     {
609     mClientSubscriptionHandle->end(mImmediate);
610     }
611 nash 7042 }
612    
613 jmatthewsr 8161 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
614 nash 7042 {
615     return strm << "ClientSubscriptionEndCommand";
616     }
617     private:
618 fjoanis 9956 ClientSubscriptionHandle mClientSubscriptionHandle;
619 sgodin 9299 bool mImmediate;
620 nash 7042 };
621    
622     void
623 sgodin 9299 ClientSubscription::endCommand(bool immediate)
624 nash 7042 {
625 fjoanis 9956 mDum.post(new ClientSubscriptionEndCommand(getHandle(), immediate));
626 nash 7042 }
627    
628 derek 4062 void
629 bcampen 8958 ClientSubscription::acceptUpdate(int statusCode, const char* reason)
630 derek 4062 {
631 daniel 5767 assert(!mQueuedNotifies.empty());
632 daniel 6266 if (mQueuedNotifies.empty())
633     {
634     InfoLog(<< "No queued notify to accept");
635     return;
636     }
637    
638 daniel 5770 QueuedNotify* qn = mQueuedNotifies.front();
639 daniel 5767 mQueuedNotifies.pop_front();
640 daniel 5770 mDustbin.push_back(qn);
641 daniel 5767 mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
642 bcampen 8958 if(reason)
643     {
644     mLastResponse->header(h_StatusLine).reason()=reason;
645     }
646 derek 5644 send(mLastResponse);
647 derek 4062 }
648    
649 nash 7042 class ClientSubscriptionAcceptUpdateCommand : public DumCommandAdapter
650     {
651     public:
652 fjoanis 9956 ClientSubscriptionAcceptUpdateCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, int statusCode, const char* reason)
653     : mClientSubscriptionHandle(clientSubscriptionHandle),
654 bcampen 8981 mStatusCode(statusCode),
655     mReason(reason ? Data(reason) : Data::Empty)
656 nash 7042 {
657    
658     }
659    
660     virtual void executeCommand()
661     {
662 fjoanis 9956 if(mClientSubscriptionHandle.isValid())
663     {
664     mClientSubscriptionHandle->acceptUpdate(mStatusCode, mReason.c_str());
665     }
666 nash 7042 }
667    
668 jmatthewsr 8161 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
669 nash 7042 {
670     return strm << "ClientSubscriptionAcceptUpdateCommand";
671     }
672     private:
673 fjoanis 9956 ClientSubscriptionHandle mClientSubscriptionHandle;
674 nash 7042 int mStatusCode;
675 bcampen 8981 Data mReason;
676 nash 7042 };
677    
678 derek 4062 void
679 bcampen 8981 ClientSubscription::acceptUpdateCommand(int statusCode, const char* reason)
680 nash 7042 {
681 fjoanis 9956 mDum.post(new ClientSubscriptionAcceptUpdateCommand(getHandle(), statusCode, reason));
682 nash 7042 }
683    
684 sgodin 9033 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 nash 7042 void
696 daniel 5767 ClientSubscription::send(SharedPtr<SipMessage> msg)
697     {
698     DialogUsage::send(msg);
699    
700     if (!mEnded)
701     {
702 vann 7008 if (!mQueuedNotifies.empty() && msg->isResponse())
703 daniel 5767 {
704     mDum.addTimer(DumTimeout::SendNextNotify,
705     0,
706     getBaseHandle(),
707     0);
708     }
709     }
710 daniel 5825
711 daniel 5767 }
712    
713     void
714 derek 4062 ClientSubscription::rejectUpdate(int statusCode, const Data& reasonPhrase)
715     {
716     ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
717     assert(handler);
718 daniel 5767 assert(!mQueuedNotifies.empty());
719 daniel 6266 if (mQueuedNotifies.empty())
720     {
721     InfoLog(<< "No queued notify to reject");
722     return;
723     }
724    
725 daniel 5770 QueuedNotify* qn = mQueuedNotifies.front();
726 daniel 5767 mQueuedNotifies.pop_front();
727 daniel 5770 mDustbin.push_back(qn);
728 daniel 5767
729     mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
730 derek 4062 if (!reasonPhrase.empty())
731     {
732 daniel 5757 mLastResponse->header(h_StatusLine).reason() = reasonPhrase;
733 derek 4062 }
734    
735 derek 5644 send(mLastResponse);
736 daniel 5757 switch (Helper::determineFailureMessageEffect(*mLastResponse))
737 derek 4062 {
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 daniel 5767 mEnded = true;
749 bcampen 8478 handler->onTerminated(getHandle(), mLastResponse.get());
750 derek 4062 delete this;
751     break;
752     }
753     }
754    
755 nash 7042 class ClientSubscriptionRejectUpdateCommand : public DumCommandAdapter
756     {
757     public:
758 fjoanis 9956 ClientSubscriptionRejectUpdateCommand(const ClientSubscriptionHandle& clientSubscriptionHandle, int statusCode, const Data& reasonPhrase)
759     : mClientSubscriptionHandle(clientSubscriptionHandle),
760 nash 7042 mStatusCode(statusCode),
761     mReasonPhrase(reasonPhrase)
762     {
763     }
764    
765     virtual void executeCommand()
766     {
767 fjoanis 9956 if(mClientSubscriptionHandle.isValid())
768     {
769     mClientSubscriptionHandle->rejectUpdate(mStatusCode, mReasonPhrase);
770     }
771 nash 7042 }
772    
773 jmatthewsr 8161 virtual EncodeStream& encodeBrief(EncodeStream& strm) const
774 nash 7042 {
775     return strm << "ClientSubscriptionRejectUpdateCommand";
776     }
777     private:
778 fjoanis 9956 ClientSubscriptionHandle mClientSubscriptionHandle;
779 nash 7042 int mStatusCode;
780     Data mReasonPhrase;
781     };
782    
783     void
784     ClientSubscription::rejectUpdateCommand(int statusCode, const Data& reasonPhrase)
785     {
786 fjoanis 9956 mDum.post(new ClientSubscriptionRejectUpdateCommand(getHandle(), statusCode, reasonPhrase));
787 nash 7042 }
788    
789 derek 4062 void ClientSubscription::dialogDestroyed(const SipMessage& msg)
790     {
791     ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
792     assert(handler);
793 daniel 5767 mEnded = true;
794 bcampen 8478 handler->onTerminated(getHandle(), &msg);
795 derek 4062 delete this;
796     }
797    
798 jmatthewsr 8161 EncodeStream&
799     ClientSubscription::dump(EncodeStream& strm) const
800 derek 4062 {
801 daniel 5757 strm << "ClientSubscription " << mLastRequest->header(h_From).uri();
802 derek 4062 return strm;
803     }
804    
805 sgodin 9033 void
806     ClientSubscription::onReadyToSend(SipMessage& msg)
807 daniel 5505 {
808     ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
809     assert(handler);
810     handler->onReadyToSend(getHandle(), msg);
811     }
812 derek 4062
813 daniel 5767 void
814 sgodin 9033 ClientSubscription::flowTerminated()
815     {
816     // notify handler
817     ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
818     assert(handler);
819     handler->onFlowTerminated(getHandle());
820     }
821    
822     void
823 daniel 5767 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 daniel 5770 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 bcampen 8958 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 derek 4062 /* ====================================================================
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