reSIProcate/DialogUsageManager  9680
ClientSubscription.cxx
Go to the documentation of this file.
00001 #include <queue>
00002 
00003 #include "resip/stack/Helper.hxx"
00004 #include "rutil/Logger.hxx"
00005 #include "resip/stack/SipFrag.hxx"
00006 #include "resip/stack/SipMessage.hxx"
00007 #include "resip/dum/ClientSubscription.hxx"
00008 #include "resip/dum/Dialog.hxx"
00009 #include "resip/dum/DialogUsageManager.hxx"
00010 #include "resip/dum/SubscriptionHandler.hxx"
00011 #include "resip/dum/SubscriptionCreator.hxx"
00012 #include "resip/dum/UsageUseException.hxx"
00013 
00014 #include "resip/dum/AppDialogSet.hxx"
00015 
00016 using namespace resip;
00017 
00018 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
00019 
00020 
00021 ClientSubscription::ClientSubscription(DialogUsageManager& dum, Dialog& dialog,
00022                                        const SipMessage& request, UInt32 defaultSubExpiration)
00023    : BaseSubscription(dum, dialog, request),
00024      mOnNewSubscriptionCalled(mEventType == "refer"),  // don't call onNewSubscription for Refer subscriptions
00025      mEnded(false),
00026      mNextRefreshSecs(0),
00027      mLastSubSecs(Timer::getTimeSecs()), // Not exactly, but more forgiving
00028      mDefaultExpires(defaultSubExpiration),
00029      mRefreshing(false),
00030      mHaveQueuedRefresh(false),
00031      mQueuedRefreshInterval(-1),
00032      mLargestNotifyCSeq(0)
00033 {
00034    DebugLog (<< "ClientSubscription::ClientSubscription from " << request.brief());   
00035    if(request.method() == SUBSCRIBE)
00036    {
00037       *mLastRequest = request;
00038       if (defaultSubExpiration > 0)
00039       {
00040          mLastRequest->header(h_Expires).value() = defaultSubExpiration;
00041       }
00042    }
00043    else
00044    {
00045       // If a NOTIFY request is use to make this ClientSubscription, then create the implied SUBSCRIBE 
00046       // request as the mLastRequest
00047       mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
00048    }
00049 }
00050 
00051 ClientSubscription::~ClientSubscription()
00052 {
00053    mDialog.mClientSubscriptions.remove(this);
00054 
00055    while (!mQueuedNotifies.empty())
00056    {
00057       delete mQueuedNotifies.front();
00058       mQueuedNotifies.pop_front();
00059    }
00060 
00061    clearDustbin();
00062 }
00063 
00064 ClientSubscriptionHandle 
00065 ClientSubscription::getHandle()
00066 {
00067    return ClientSubscriptionHandle(mDum, getBaseHandle().getId());
00068 }
00069 
00070 void
00071 ClientSubscription::dispatch(const SipMessage& msg)
00072 {
00073    DebugLog (<< "ClientSubscription::dispatch " << msg.brief());
00074    
00075    ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00076    assert(handler);
00077 
00078    clearDustbin();
00079 
00080    // asserts are checks the correctness of Dialog::dispatch
00081    if (msg.isRequest() )
00082    {
00083       assert( msg.header(h_RequestLine).getMethod() == NOTIFY );
00084       mRefreshing = false;
00085 
00086       // !dlb! 481 NOTIFY iff state is dead?
00087 
00089       //mLastNotify = msg;
00090 
00091       if (!mOnNewSubscriptionCalled && !getAppDialogSet()->isReUsed())
00092       {
00093          InfoLog (<< "[ClientSubscription] " << mLastRequest->header(h_To));
00094          if (msg.exists(h_Contacts))
00095          {
00096             mDialog.mRemoteTarget = msg.header(h_Contacts).front();
00097          }
00098          
00099          handler->onNewSubscription(getHandle(), msg);
00100          mOnNewSubscriptionCalled = true;
00101       }         
00102 
00103       bool outOfOrder = mLargestNotifyCSeq > msg.header(h_CSeq).sequence();
00104       if (!outOfOrder)
00105       {
00106          mLargestNotifyCSeq = msg.header(h_CSeq).sequence();
00107       }
00108       else
00109       {
00110          DebugLog(<< "received out of order notify");
00111       }
00112 
00113       mQueuedNotifies.push_back(new QueuedNotify(msg, outOfOrder));
00114       if (mQueuedNotifies.size() == 1)
00115       {
00116          DebugLog(<< "no queued notify");
00117          processNextNotify();
00118          return;
00119       }
00120       else
00121       {
00122          DebugLog(<< "Notify gets queued");
00123       }
00124    }
00125    else
00126    {
00127       DebugLog(<< "processing client subscription response");
00128       processResponse(msg);
00129    }
00130 }
00131 
00132 void 
00133 ClientSubscription::processResponse(const SipMessage& msg)
00134 {
00135    ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00136    assert(handler);
00137 
00138    mRefreshing = false;
00139    int statusCode = msg.header(h_StatusLine).statusCode();
00140 
00141    if (statusCode >= 200 && statusCode <300)
00142    {
00143       if (msg.exists(h_Expires))
00144       {
00145          // grab the expires from the 2xx in case there is not one on the NOTIFY .mjf.
00146          UInt32 expires = msg.header(h_Expires).value();
00147          UInt32 lastExpires = mLastRequest->header(h_Expires).value();
00148          if (expires < lastExpires)
00149          {
00150             mLastRequest->header(h_Expires).value() = expires;
00151          }
00152       }
00153 
00154       if(!mOnNewSubscriptionCalled)
00155       {
00156          // Timer for initial NOTIFY; since we don't know when the initial
00157          // SUBSRIBE is sent, we have to set the timer when the 200 comes in, if
00158          // it beat the NOTIFY.
00159          mDum.addTimer(DumTimeout::WaitForNotify, 
00160                  64*Timer::T1, 
00161                  getBaseHandle(),
00162                  ++mTimerSeq);
00163       }
00164 
00165       sendQueuedRefreshRequest();
00166    }
00167    else if (!mEnded &&
00168             statusCode == 481 &&
00169             msg.exists(h_Expires) && msg.header(h_Expires).value() > 0)
00170    {
00171       InfoLog (<< "Received 481 to SUBSCRIBE, reSUBSCRIBEing (presence server probably restarted) "
00172                << mLastRequest->header(h_To));
00173 
00174       reSubscribe();  // will delete "this"
00175       return;
00176    }
00177    else if (!mEnded &&
00178             (statusCode == 408 ||
00179              (statusCode == 503 && msg.getReceivedTransport() == 0) ||
00180              ((statusCode == 413 ||
00181                statusCode == 480 ||
00182                statusCode == 486 ||
00183                statusCode == 500 ||
00184                statusCode == 503 ||
00185                statusCode == 600 ||
00186                statusCode == 603) &&
00187               msg.exists(h_RetryAfter))))
00188    {
00189       int retry;
00190       int retryAfter = 0;
00191       if(msg.exists(h_RetryAfter))
00192       {
00193          retryAfter = msg.header(h_RetryAfter).value();
00194       }
00195 
00196       InfoLog (<< "Received " << statusCode << " to SUBSCRIBE "
00197                << mLastRequest->header(h_To));
00198       retry = handler->onRequestRetry(getHandle(), retryAfter, msg);
00199 
00200       if (retry < 0)
00201       {
00202          DebugLog(<< "Application requested failure on Retry-After");
00203          mEnded = true;
00204          handler->onTerminated(getHandle(), &msg);
00205          delete this;
00206          return;
00207       }
00208       else if (retry == 0)
00209       {
00210          DebugLog(<< "Application requested immediate retry on Retry-After");
00211 
00212          if (mOnNewSubscriptionCalled)
00213          {
00214             // If we already have a dialog, then just refresh again
00215             requestRefresh();
00216          }
00217          else
00218          {
00219             reSubscribe();  // will delete "this"
00220             return;
00221          }
00222       }
00223       else 
00224       {
00225          // leave the usage around until the timeout
00226          // !dlb! would be nice to set the state to something dead, but not used
00227          mDum.addTimer(DumTimeout::SubscriptionRetry, 
00228                        retry, 
00229                        getBaseHandle(),
00230                        ++mTimerSeq);
00231          // leave the usage around until the timeout
00232          return;
00233       }            
00234    }
00235    else if (msg.header(h_StatusLine).statusCode() >= 300)
00236    {
00237       if (msg.header(h_StatusLine).statusCode() == 423 
00238           && msg.exists(h_MinExpires))
00239       {
00240          requestRefresh(msg.header(h_MinExpires).value());            
00241       }
00242       else
00243       {
00244          mEnded = true;
00245          handler->onTerminated(getHandle(), &msg);
00246          delete this;
00247          return;
00248       }
00249    }
00250 }
00251 
00252 void 
00253 ClientSubscription::processNextNotify()
00254 {
00258    //assert(!mQueuedNotifies.empty());
00259    if (mQueuedNotifies.empty())
00260    {
00261       return;
00262    }
00263 
00264    QueuedNotify* qn = mQueuedNotifies.front();
00265    ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00266    assert(handler);
00267 
00268    unsigned long refreshInterval = 0;
00269    bool setRefreshTimer=false; 
00270    if (!qn->outOfOrder())
00271    {
00272       UInt32 expires = 0;
00273       //default to 3600 seconds so non-compliant endpoints don't result in leaked usages
00274       if (qn->notify().exists(h_SubscriptionState) && qn->notify().header(h_SubscriptionState).exists(p_expires))
00275       {
00276          expires = qn->notify().header(h_SubscriptionState).param(p_expires);
00277       }
00278       else if (mLastRequest->exists(h_Expires))
00279       {
00280          expires = mLastRequest->header(h_Expires).value();
00281       }
00282       else if (mDefaultExpires)
00283       {
00284          /* if we haven't gotten an expires value from:
00285             1. the subscription state from this notify
00286             2. the last request
00287             then use the default expires (meaning it came from the 2xx in response
00288             to the initial SUBSCRIBE). .mjf.
00289           */
00290          expires = mDefaultExpires;
00291       }
00292       else
00293       {
00294          expires = 3600;
00295       }
00296       
00297       if (!mLastRequest->exists(h_Expires))
00298       {
00299          DebugLog(<< "No expires header in last request, set to " << expires);
00300          mLastRequest->header(h_Expires).value() = expires;
00301       }
00302 
00303       if(!qn->notify().exists(h_SubscriptionState) || 
00304          !isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
00305       {
00306          // Don't do this stuff for a NOTIFY terminated.
00307          UInt64 now = Timer::getTimeSecs();
00308          refreshInterval = Helper::aBitSmallerThan((signed long)expires);
00309          
00310          if (mNextRefreshSecs == 0 || now + refreshInterval < mNextRefreshSecs)
00311          {
00312             mNextRefreshSecs = now + refreshInterval;
00313             setRefreshTimer = true;
00314          }
00315       }
00316    }
00317    //if no subscription state header, treat as an extension. Only allow for
00318    //refer to handle non-compliant implementations
00319    if (!qn->notify().exists(h_SubscriptionState))
00320    {
00321       if (qn->notify().exists(h_Event) && qn->notify().header(h_Event).value() == "refer")
00322       {
00323          SipFrag* frag  = dynamic_cast<SipFrag*>(qn->notify().getContents());
00324          if (frag)
00325          {
00326             if (frag->message().isResponse())
00327             {
00328                int code = frag->message().header(h_StatusLine).statusCode();
00329                if (code < 200)
00330                {
00331                   handler->onUpdateExtension(getHandle(), qn->notify(), qn->outOfOrder());
00332                }
00333                else
00334                {
00335                   acceptUpdate();
00336                   mEnded = true;                     
00337                   handler->onTerminated(getHandle(), &qn->notify());
00338                   delete this;
00339                }
00340             }
00341             else
00342             {
00343                acceptUpdate();
00344                mEnded = true;
00345                handler->onTerminated(getHandle(), &qn->notify());
00346                delete this;
00347             }
00348          }
00349          else
00350          {
00351             acceptUpdate();
00352             mEnded = true;
00353             handler->onTerminated(getHandle(), &qn->notify());
00354             delete this;
00355          }
00356       }
00357       else
00358       {            
00359          mDialog.makeResponse(*mLastResponse, qn->notify(), 400);
00360          mLastResponse->header(h_StatusLine).reason() = "Missing Subscription-State header";
00361          send(mLastResponse);
00362          mEnded = true;
00363          handler->onTerminated(getHandle(), &qn->notify());
00364          delete this;
00365       }
00366       return;
00367    }
00368 
00369    if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Active))
00370    {
00371       if (setRefreshTimer)
00372       {
00373          scheduleRefresh(refreshInterval);
00374       }
00375          
00376       handler->onUpdateActive(getHandle(), qn->notify(), qn->outOfOrder());
00377    }
00378    else if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Pending))
00379    {
00380       if (setRefreshTimer)
00381       {
00382          scheduleRefresh(refreshInterval);
00383       }
00384 
00385       handler->onUpdatePending(getHandle(), qn->notify(), qn->outOfOrder());
00386    }
00387    else if (isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
00388    {
00389       if(mLastRequest->header(h_Expires).value()!=0 &&
00390          isEqualNoCase(qn->notify().header(h_SubscriptionState).param(p_reason), "timeout"))
00391       {
00392          // Unexpected timeout of some sort. Look closer.
00393          if(mNextRefreshSecs==0)
00394          {
00395             // No refresh scheduled; maybe we are trying to avoid a tight SUB/
00396             // NOT loop here?
00397             if(Helper::aBitSmallerThan((signed long)(Timer::getTimeSecs() - mLastSubSecs)) < 2)
00398             {
00399                acceptUpdate(200, "I just sent a refresh, what more do you want "
00400                                  "from me?");
00401             }
00402             else
00403             {
00404                acceptUpdate(200, "Why didn't I refresh here?");
00405             }
00406          }
00407          else
00408          {
00409             acceptUpdate(200, "You terminated my subscription early! What "
00410                               "gives?");
00411          }
00412       }
00413       else
00414       {
00415          acceptUpdate();
00416       }
00417       mEnded = true;
00418       handler->onTerminated(getHandle(), &qn->notify());
00419       DebugLog (<< "[ClientSubscription] " << mLastRequest->header(h_To) << "[ClientSubscription] Terminated");                   
00420       delete this;
00421       return;
00422    }
00423    else if (!mEnded)
00424    {
00425       handler->onUpdateExtension(getHandle(), qn->notify(), qn->outOfOrder());
00426    }
00427    else if (mEnded)
00428    {
00429       // We received a NOTIFY message when we thought the subscription was
00430       // ended. This can happen, for example, when a previously sent NOTIFY gets
00431       // resent while we (ClientSubscription) are trying to terminate the
00432       // subscription. If we don't accept/reject this NOTIFY, it will stay into
00433       // the mQueuedNotifies queue and we'll never terminate the subscription
00434       // even if the server sends a NOTIFY/terminated. All received NOTIFY would
00435       // get piled up on mQueuedNotifies and they will never get processed.
00436       //
00437       // Note that if that NOTIFY is in fact the terminated one, it will get
00438       // caught by another if statement above and acted upon appropriately.
00439       //
00441       InfoLog(<< "[ClientSubscription] received NOTIFY when subscription was ended, rejecting it...");
00442       rejectUpdate(481);
00443    }
00444 }
00445 
00446 void
00447 ClientSubscription::dispatch(const DumTimeout& timer)
00448 {
00449    if (timer.seq() == mTimerSeq)
00450    {
00451       if(timer.type() == DumTimeout::WaitForNotify)
00452       {
00453          ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00454          if(mOnNewSubscriptionCalled && mEnded)
00455          {
00456             // NOTIFY terminated didn't come in
00457             handler->onTerminated(getHandle(),0);
00458             delete this;
00459             return;
00460          }
00461 
00462          // Initial NOTIFY never came in; let app decide what to do
00463          handler->onNotifyNotReceived(getHandle());
00464       }
00465       else if (timer.type() == DumTimeout::SubscriptionRetry)
00466       {
00467          // this indicates that the ClientSubscription was created by a 408
00468          if (mOnNewSubscriptionCalled)
00469          {
00470             InfoLog(<< "ClientSubscription: application retry refresh");
00471             requestRefresh();
00472          }
00473          else
00474          {
00475             InfoLog(<< "ClientSubscription: application retry new request");
00476             reSubscribe();  // will delete "this"
00477             return;
00478          }
00479       }
00480       else if(timer.type() == DumTimeout::Subscription)
00481       {
00482          requestRefresh();
00483       }
00484    }
00485    else if(timer.seq() == 0 && timer.type() == DumTimeout::SendNextNotify)
00486    {
00487       DebugLog(<< "got DumTimeout::SendNextNotify");
00488       processNextNotify();
00489    }
00490 }
00491 
00492 void
00493 ClientSubscription::requestRefresh(UInt32 expires)
00494 {
00495    if (!mEnded)
00496    {
00497       if (mRefreshing)
00498       {
00499          DebugLog(<< "queue up refresh request");
00500          mHaveQueuedRefresh = true;
00501          mQueuedRefreshInterval = expires;
00502          return;
00503       }
00504 
00505       mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
00507       //the map that stores the handlers, or part of the handler API
00508       if(expires > 0)
00509       {
00510          mLastRequest->header(h_Expires).value() = expires;
00511       }
00512       mNextRefreshSecs = 0;
00513       InfoLog (<< "Refresh subscription: " << mLastRequest->header(h_Contacts).front());
00514       mRefreshing = true;
00515       mLastSubSecs = Timer::getTimeSecs();
00516       send(mLastRequest);
00517       // Timer for reSUB NOTIFY.
00518       mDum.addTimer(DumTimeout::WaitForNotify, 
00519               64*Timer::T1, 
00520               getBaseHandle(),
00521               ++mTimerSeq);
00522    }
00523 }
00524 
00525 class ClientSubscriptionRefreshCommand : public DumCommandAdapter
00526 {
00527 public:
00528    ClientSubscriptionRefreshCommand(ClientSubscription& clientSubscription, UInt32 expires)
00529       : mClientSubscription(clientSubscription),
00530         mExpires(expires)
00531    {
00532 
00533    }
00534    virtual void executeCommand()
00535    {
00536       mClientSubscription.requestRefresh(mExpires);
00537    }
00538 
00539    virtual EncodeStream& encodeBrief(EncodeStream& strm) const
00540    {
00541       return strm << "ClientSubscriptionRefreshCommand";
00542    }
00543 private:
00544    ClientSubscription& mClientSubscription;
00545    UInt32 mExpires;
00546 };
00547 
00548 void
00549 ClientSubscription::requestRefreshCommand(UInt32 expires)
00550 {
00551    mDum.post(new ClientSubscriptionRefreshCommand(*this, expires));
00552 }
00553 
00554 void
00555 ClientSubscription::end()
00556 {
00557     end(false /* immediate? */);
00558 }
00559 
00560 void
00561 ClientSubscription::end(bool immediate)
00562 {
00563    InfoLog (<< "End subscription: " << mLastRequest->header(h_RequestLine).uri());
00564 
00565    if (!mEnded)
00566    {
00567       if(!immediate)
00568       {
00569          mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
00570          mLastRequest->header(h_Expires).value() = 0;
00571          mEnded = true;
00572          send(mLastRequest);
00573          // Timer for NOTIFY terminated
00574          mDum.addTimer(DumTimeout::WaitForNotify, 
00575                  64*Timer::T1, 
00576                  getBaseHandle(),
00577                  ++mTimerSeq);
00578       }
00579       else
00580       {
00581          delete this;
00582       }
00583    }
00584 }
00585 
00586 class ClientSubscriptionEndCommand : public DumCommandAdapter
00587 {
00588 public:
00589    ClientSubscriptionEndCommand(ClientSubscription& clientSubscription, bool immediate)
00590       :mClientSubscription(clientSubscription), mImmediate(immediate)
00591    {
00592 
00593    }
00594 
00595    virtual void executeCommand()
00596    {
00597       mClientSubscription.end(mImmediate);
00598    }
00599 
00600    virtual EncodeStream& encodeBrief(EncodeStream& strm) const
00601    {
00602       return strm << "ClientSubscriptionEndCommand";
00603    }
00604 private:
00605    ClientSubscription& mClientSubscription;
00606    bool mImmediate;
00607 };
00608 
00609 void
00610 ClientSubscription::endCommand(bool immediate)
00611 {
00612    mDum.post(new ClientSubscriptionEndCommand(*this, immediate));
00613 }
00614 
00615 void 
00616 ClientSubscription::acceptUpdate(int statusCode, const char* reason)
00617 {
00618    assert(!mQueuedNotifies.empty());
00619    if (mQueuedNotifies.empty())
00620    {
00621       InfoLog(<< "No queued notify to accept");
00622       return;
00623    }
00624 
00625    QueuedNotify* qn = mQueuedNotifies.front();
00626    mQueuedNotifies.pop_front();
00627    mDustbin.push_back(qn);
00628    mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
00629    if(reason)
00630    {
00631       mLastResponse->header(h_StatusLine).reason()=reason;
00632    }
00633    send(mLastResponse);
00634 }
00635 
00636 class ClientSubscriptionAcceptUpdateCommand : public DumCommandAdapter
00637 {
00638 public:
00639    ClientSubscriptionAcceptUpdateCommand(ClientSubscription& clientSubscription, int statusCode, const char* reason)
00640       : mClientSubscription(clientSubscription),
00641         mStatusCode(statusCode),
00642         mReason(reason ? Data(reason) : Data::Empty)
00643    {
00644 
00645    }
00646 
00647    virtual void executeCommand()
00648    {
00649       mClientSubscription.acceptUpdate(mStatusCode, mReason.c_str());
00650    }
00651 
00652    virtual EncodeStream& encodeBrief(EncodeStream& strm) const
00653    {
00654       return strm << "ClientSubscriptionAcceptUpdateCommand";
00655    }
00656 private:
00657    ClientSubscription& mClientSubscription;
00658    int mStatusCode;
00659    Data mReason;
00660 };
00661 
00662 void 
00663 ClientSubscription::acceptUpdateCommand(int statusCode, const char* reason)
00664 {
00665    mDum.post(new ClientSubscriptionAcceptUpdateCommand(*this, statusCode, reason));
00666 }
00667 
00668 void
00669 ClientSubscription::reSubscribe()
00670 {
00671    NameAddr target(mLastRequest->header(h_To));
00672    target.remove(p_tag);  // ensure To tag is removed
00673    SharedPtr<SipMessage> sub = mDum.makeSubscription(target, getUserProfile(), getEventType(), getAppDialogSet()->reuse());
00674    mDum.send(sub);
00675 
00676    delete this;
00677 }
00678 
00679 void 
00680 ClientSubscription::send(SharedPtr<SipMessage> msg)
00681 {
00682    DialogUsage::send(msg);
00683 
00684    if (!mEnded)
00685    {
00686       if (!mQueuedNotifies.empty() && msg->isResponse())
00687       {
00688          mDum.addTimer(DumTimeout::SendNextNotify, 
00689                        0, 
00690                        getBaseHandle(),
00691                        0);
00692       }
00693    }
00694 
00695 }
00696 
00697 void 
00698 ClientSubscription::rejectUpdate(int statusCode, const Data& reasonPhrase)
00699 {
00700    ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00701    assert(handler);   
00702    assert(!mQueuedNotifies.empty());
00703    if (mQueuedNotifies.empty())
00704    {
00705       InfoLog(<< "No queued notify to reject");
00706       return;
00707    }
00708 
00709    QueuedNotify* qn = mQueuedNotifies.front();
00710    mQueuedNotifies.pop_front();
00711    mDustbin.push_back(qn);
00712 
00713    mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
00714    if (!reasonPhrase.empty())
00715    {
00716       mLastResponse->header(h_StatusLine).reason() = reasonPhrase;
00717    }
00718    
00719    send(mLastResponse);
00720    switch (Helper::determineFailureMessageEffect(*mLastResponse))
00721    {
00722       case Helper::TransactionTermination:
00723       case Helper::RetryAfter:
00724          break;            
00725       case Helper::OptionalRetryAfter:
00726       case Helper::ApplicationDependant: 
00727          throw UsageUseException("Not a reasonable code to reject a NOTIFY with inside an established dialog.", 
00728                                  __FILE__, __LINE__);
00729          break;            
00730       case Helper::DialogTermination: //?dcm? -- throw or destroy this?
00731       case Helper::UsageTermination:
00732          mEnded = true;
00733          handler->onTerminated(getHandle(), mLastResponse.get());
00734          delete this;
00735          break;
00736    }
00737 }
00738 
00739 class ClientSubscriptionRejectUpdateCommand : public DumCommandAdapter
00740 {
00741 public:
00742    ClientSubscriptionRejectUpdateCommand(ClientSubscription& clientSubscription, int statusCode, const Data& reasonPhrase)
00743       : mClientSubscription(clientSubscription),
00744         mStatusCode(statusCode),
00745         mReasonPhrase(reasonPhrase)
00746    {
00747    }
00748 
00749    virtual void executeCommand()
00750    {
00751       mClientSubscription.rejectUpdate(mStatusCode, mReasonPhrase);
00752    }
00753 
00754    virtual EncodeStream& encodeBrief(EncodeStream& strm) const
00755    {
00756       return strm << "ClientSubscriptionRejectUpdateCommand";
00757    }
00758 private:
00759    ClientSubscription& mClientSubscription;
00760    int mStatusCode;
00761    Data mReasonPhrase;
00762 };
00763 
00764 void 
00765 ClientSubscription::rejectUpdateCommand(int statusCode, const Data& reasonPhrase)
00766 {
00767    mDum.post(new ClientSubscriptionRejectUpdateCommand(*this, statusCode, reasonPhrase));
00768 }
00769 
00770 void ClientSubscription::dialogDestroyed(const SipMessage& msg)
00771 {
00772    ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00773    assert(handler);   
00774    mEnded = true;
00775    handler->onTerminated(getHandle(), &msg);
00776    delete this;   
00777 }
00778 
00779 EncodeStream&
00780 ClientSubscription::dump(EncodeStream& strm) const
00781 {
00782    strm << "ClientSubscription " << mLastRequest->header(h_From).uri();
00783    return strm;
00784 }
00785 
00786 void 
00787 ClientSubscription::onReadyToSend(SipMessage& msg)
00788 {
00789    ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00790    assert(handler);
00791    handler->onReadyToSend(getHandle(), msg);
00792 }
00793 
00794 void
00795 ClientSubscription::flowTerminated()
00796 {
00797    // notify handler
00798    ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
00799    assert(handler);
00800    handler->onFlowTerminated(getHandle());
00801 }
00802 
00803 void
00804 ClientSubscription::sendQueuedRefreshRequest()
00805 {
00806    assert(!mRefreshing);
00807 
00808    if (mHaveQueuedRefresh)
00809    {
00810       DebugLog(<< "send queued refresh request");
00811       mHaveQueuedRefresh = false;
00812       requestRefresh(mQueuedRefreshInterval);
00813    }
00814 }
00815 
00816 void
00817 ClientSubscription::clearDustbin()
00818 {
00819    for (Dustbin::iterator it = mDustbin.begin(); it != mDustbin.end(); ++it)
00820    {
00821       delete *it;
00822    }
00823 
00824    mDustbin.clear();
00825 
00826 }
00827 
00828 void 
00829 ClientSubscription::scheduleRefresh(unsigned long refreshInterval)
00830 {
00831    if(mNextRefreshSecs-mLastSubSecs < 2)
00832    {
00833       // Server is using an unreasonably short expiry; we sent a SUB
00834       // very recently, and the server has told us to refresh almost 
00835       // immediately. By the time our refresh timer pops, less than two 
00836       // seconds will have elapsed since our last SUBSCRIBE. This is 
00837       // unacceptable. Just let the subscription end.
00838       // It is also possible that our refresh SUB has crossed an update NOTIFY 
00839       // on the wire; in this case, the right thing to do is to wait until a 
00840       // NOTIFY for our refresh SUB comes in, which is exactly what this code 
00841       // ends up doing in this case.
00842       // ?bwc? Make this minimum inter-SUBSCRIBE time configurable?
00843       WarningLog(<< "Server is using an unacceptably short expiry. "
00844                   "Letting the subscription end so we don't get in a"
00845                   " tight SUB/NOT loop.");
00846       mNextRefreshSecs=0;
00847    }
00848    else
00849    {
00850       mDum.addTimer(DumTimeout::Subscription, refreshInterval, getBaseHandle(), ++mTimerSeq);
00851       InfoLog (<< "[ClientSubscription] reSUBSCRIBE in " << refreshInterval);
00852    }
00853 }
00854 
00855 
00856 
00857 /* ====================================================================
00858  * The Vovida Software License, Version 1.0
00859  *
00860  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00861  *
00862  * Redistribution and use in source and binary forms, with or without
00863  * modification, are permitted provided that the following conditions
00864  * are met:
00865  *
00866  * 1. Redistributions of source code must retain the above copyright
00867  *    notice, this list of conditions and the following disclaimer.
00868  *
00869  * 2. Redistributions in binary form must reproduce the above copyright
00870  *    notice, this list of conditions and the following disclaimer in
00871  *    the documentation and/or other materials provided with the
00872  *    distribution.
00873  *
00874  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00875  *    and "Vovida Open Communication Application Library (VOCAL)" must
00876  *    not be used to endorse or promote products derived from this
00877  *    software without prior written permission. For written
00878  *    permission, please contact vocal@vovida.org.
00879  *
00880  * 4. Products derived from this software may not be called "VOCAL", nor
00881  *    may "VOCAL" appear in their name, without prior written
00882  *    permission of Vovida Networks, Inc.
00883  *
00884  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00885  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00886  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00887  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00888  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00889  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00890  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00891  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00892  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00893  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00894  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00895  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00896  * DAMAGE.
00897  *
00898  * ====================================================================
00899  *
00900  * This software consists of voluntary contributions made by Vovida
00901  * Networks, Inc. and many individuals on behalf of Vovida Networks,
00902  * Inc.  For more information on Vovida Networks, Inc., please see
00903  * <http://www.vovida.org/>.
00904  *
00905  */