reSIProcate/DialogUsageManager  9680
ClientRegistration.cxx
Go to the documentation of this file.
00001 #include <algorithm>
00002 #include <iterator>
00003 
00004 #include "resip/stack/Helper.hxx"
00005 #include "resip/dum/BaseCreator.hxx"
00006 #include "resip/dum/ClientAuthManager.hxx"
00007 #include "resip/dum/ClientRegistration.hxx"
00008 #include "resip/dum/RegistrationHandler.hxx"
00009 #include "resip/dum/DialogUsageManager.hxx"
00010 #include "resip/dum/Dialog.hxx"
00011 #include "resip/dum/MasterProfile.hxx"
00012 #include "resip/dum/UsageUseException.hxx"
00013 #include "rutil/Logger.hxx"
00014 #include "rutil/Inserter.hxx"
00015 #include "rutil/Random.hxx"
00016 #include "rutil/ParseBuffer.hxx"
00017 #include "rutil/WinLeakCheck.hxx"
00018 
00019 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
00020 
00021 using namespace resip;
00022 
00023 ClientRegistrationHandle
00024 ClientRegistration::getHandle()
00025 {
00026    return ClientRegistrationHandle(mDum, getBaseHandle().getId());
00027 }
00028 
00029 ClientRegistration::ClientRegistration(DialogUsageManager& dum,
00030                                        DialogSet& dialogSet,
00031                                        SharedPtr<SipMessage> request)
00032    : NonDialogUsage(dum, dialogSet),
00033      mLastRequest(request),
00034      mTimerSeq(0),
00035      mState(mLastRequest->exists(h_Contacts) ? Adding : Querying),
00036      mEndWhenDone(false),
00037      mUserRefresh(false),
00038      mRegistrationTime(mDialogSet.mUserProfile->getDefaultRegistrationTime()),
00039      mExpires(0),
00040      mQueuedState(None),
00041      mQueuedRequest(new SipMessage)
00042 {
00043    // If no Contacts header, this is a query
00044    if (mLastRequest->exists(h_Contacts))
00045    {
00046       mMyContacts = mLastRequest->header(h_Contacts);
00047    }
00048 
00049    if(mLastRequest->exists(h_Expires) && 
00050       mLastRequest->header(h_Expires).isWellFormed())
00051    {
00052       mRegistrationTime = mLastRequest->header(h_Expires).value();
00053    }
00054 
00055    mNetworkAssociation.setDum(&dum);
00056 }
00057 
00058 ClientRegistration::~ClientRegistration()
00059 {
00060    DebugLog ( << "ClientRegistration::~ClientRegistration" );
00061    mDialogSet.mClientRegistration = 0;
00062 
00063    // !dcm! Will not interact well with multiple registrations from the same AOR
00064    mDialogSet.mUserProfile->setServiceRoute(NameAddrs());
00065 }
00066 
00067 void
00068 ClientRegistration::addBinding(const NameAddr& contact)
00069 {
00070    addBinding(contact, mDialogSet.mUserProfile->getDefaultRegistrationTime());
00071 }
00072 
00073 SharedPtr<SipMessage>
00074 ClientRegistration::tryModification(ClientRegistration::State state)
00075 {
00076    if (mState != Registered)
00077    {
00078       if(mState != RetryAdding && mState != RetryRefreshing)
00079       {
00080          if (mQueuedState != None)
00081          {
00082             WarningLog (<< "Trying to modify bindings when another request is already queued");
00083             throw UsageUseException("Queuing multiple requests for Registration Bindings", __FILE__,__LINE__);
00084          }
00085 
00086          *mQueuedRequest = *mLastRequest;
00087          mQueuedState = state;
00088 
00089          return mQueuedRequest;
00090       }
00091       else
00092       {
00093           ++mTimerSeq;  // disable retry timer
00094       }
00095    }
00096 
00097    assert(mQueuedState == None);
00098    mState = state;
00099 
00100    return mLastRequest;
00101 }
00102 
00103 void
00104 ClientRegistration::addBinding(const NameAddr& contact, UInt32 registrationTime)
00105 {
00106    SharedPtr<SipMessage> next = tryModification(Adding);
00107    mMyContacts.push_back(contact);
00108    tagContact(mMyContacts.back());
00109 
00110    next->header(h_Contacts) = mMyContacts;
00111    mRegistrationTime = registrationTime;
00112    next->header(h_Expires).value() = mRegistrationTime;
00113    next->header(h_CSeq).sequence()++;
00114    // caller prefs
00115 
00116    if (mQueuedState == None)
00117    {
00118       send(next);
00119    }
00120 }
00121 
00122 void
00123 ClientRegistration::removeBinding(const NameAddr& contact)
00124 {
00125    if (mState == Removing)
00126    {
00127       WarningLog (<< "Already removing a binding");
00128       throw UsageUseException("Can't remove binding when already removing registration bindings", __FILE__,__LINE__);
00129    }
00130 
00131    SharedPtr<SipMessage> next = tryModification(Removing);
00132    for (NameAddrs::iterator i=mMyContacts.begin(); i != mMyContacts.end(); i++)
00133    {
00134       if (i->uri() == contact.uri())
00135       {
00136          next->header(h_Contacts).clear();
00137          next->header(h_Contacts).push_back(*i);
00138          next->header(h_Expires).value() = 0;
00139          next->header(h_CSeq).sequence()++;
00140 
00141          if (mQueuedState == None)
00142          {
00143             send(next);
00144          }
00145 
00146          mMyContacts.erase(i);
00147          return;
00148       }
00149    }
00150 
00151    // !jf! What state are we left in now?
00152    throw Exception("No such binding", __FILE__, __LINE__);
00153 }
00154 
00155 void
00156 ClientRegistration::removeAll(bool stopRegisteringWhenDone)
00157 {
00158    if (mState == Removing)
00159    {
00160       WarningLog (<< "Already removing a binding");
00161       throw UsageUseException("Can't remove binding when already removing registration bindings", __FILE__,__LINE__);
00162    }
00163 
00164    SharedPtr<SipMessage> next = tryModification(Removing);
00165 
00166    mAllContacts.clear();
00167    mMyContacts.clear();
00168 
00169    NameAddr all;
00170    all.setAllContacts();
00171    next->header(h_Contacts).clear();
00172    next->header(h_Contacts).push_back(all);
00173    next->header(h_Expires).value() = 0;
00174    next->header(h_CSeq).sequence()++;
00175    mEndWhenDone = stopRegisteringWhenDone;
00176 
00177    if (mQueuedState == None)
00178    {
00179       send(next);
00180    }
00181 }
00182 
00183 void
00184 ClientRegistration::removeMyBindings(bool stopRegisteringWhenDone)
00185 {
00186    InfoLog (<< "Removing binding");
00187 
00188    if (mState == Removing)
00189    {
00190       WarningLog (<< "Already removing a binding");
00191       throw UsageUseException("Can't remove binding when already removing registration bindings", __FILE__,__LINE__);
00192    }
00193 
00194    if (mMyContacts.empty())
00195    {
00196       WarningLog (<< "No bindings to remove");
00197       throw UsageUseException("No bindings to remove", __FILE__,__LINE__);
00198    }
00199 
00200    SharedPtr<SipMessage> next = tryModification(Removing);
00201 
00202    next->header(h_Contacts) = mMyContacts;
00203    mMyContacts.clear();
00204 
00205    NameAddrs& myContacts = next->header(h_Contacts);
00206 
00207    for (NameAddrs::iterator i=myContacts.begin(); i != myContacts.end(); i++)
00208    {
00209       i->param(p_expires) = 0;
00210    }
00211 
00212    next->remove(h_Expires);
00213    next->header(h_CSeq).sequence()++;
00214 
00215    // !jf! is this ok if queued
00216    mEndWhenDone = stopRegisteringWhenDone;
00217 
00218    if (mQueuedState == None)
00219    {
00220       send(next);
00221    }
00222 }
00223 
00224 class ClientRegistrationRemoveMyBindings : public DumCommandAdapter
00225 {
00226 public:
00227    ClientRegistrationRemoveMyBindings(ClientRegistration& clientRegistration, bool stopRegisteringWhenDone)
00228       : mClientRegistration(clientRegistration),
00229         mStopRegisteringWhenDone(stopRegisteringWhenDone)
00230    {
00231    }
00232 
00233    virtual void executeCommand()
00234    {
00235       mClientRegistration.removeMyBindings(mStopRegisteringWhenDone);
00236    }
00237 
00238    virtual EncodeStream& encodeBrief(EncodeStream& strm) const
00239    {
00240       return strm << "ClientRegistrationRemoveMyBindings";
00241    }
00242 private:
00243    ClientRegistration& mClientRegistration;
00244    bool mStopRegisteringWhenDone;
00245 };
00246 
00247 void
00248 ClientRegistration::removeMyBindingsCommand(bool stopRegisteringWhenDone)
00249 {
00250    mDum.post(new ClientRegistrationRemoveMyBindings(*this, stopRegisteringWhenDone));
00251 }
00252 
00253 void 
00254 ClientRegistration::stopRegistering()
00255 {
00256    //timers aren't a concern, as DUM checks for Handle validity before firing.
00257    delete this;
00258 }
00259 
00260 void
00261 ClientRegistration::requestRefresh(UInt32 expires)
00262 {
00263     // Set flag so that handlers get called for success/failure
00264     mUserRefresh = true;
00265     internalRequestRefresh(expires);
00266 }
00267 
00268 void
00269 ClientRegistration::internalRequestRefresh(UInt32 expires)
00270 {
00271    if(mState == RetryAdding && mState == RetryRefreshing)
00272    {
00273       // disable retry time and try refresh immediately
00274       ++mTimerSeq;
00275    }
00276    else if (mState != Registered)
00277    {
00278       InfoLog (<< "a request is already in progress, no need to refresh " << *this);
00279       return;
00280    }
00281 
00282    InfoLog (<< "requesting refresh of " << *this);
00283    
00284    mState = Refreshing;
00285    mLastRequest->header(h_CSeq).sequence()++;
00286    mLastRequest->header(h_Contacts)=mMyContacts;
00287    if(expires > 0)
00288    {
00289       mRegistrationTime = expires;
00290    }
00291    mLastRequest->header(h_Expires).value()=mRegistrationTime;
00292 
00293    send(mLastRequest);
00294 }
00295 
00296 const NameAddrs&
00297 ClientRegistration::myContacts()
00298 {
00299    return mMyContacts;
00300 }
00301 
00302 const NameAddrs&
00303 ClientRegistration::allContacts()
00304 {
00305    return mAllContacts;
00306 }
00307 
00308 UInt32
00309 ClientRegistration::whenExpires() const
00310 {
00311 // !cj! - TODO - I'm supisious these time are getting confused on what units they are in 
00312    UInt64 now = Timer::getTimeSecs();
00313    UInt64 ret = mExpires - now;
00314    return (UInt32)ret;
00315 }
00316 
00317 void
00318 ClientRegistration::end()
00319 {
00320    removeMyBindings(true);
00321 }
00322 
00323 class ClientRegistrationEndCommand : public DumCommandAdapter
00324 {
00325 public:
00326    ClientRegistrationEndCommand(ClientRegistration& clientRegistration)
00327       : mClientRegistration(clientRegistration)
00328    {
00329 
00330    }
00331 
00332    virtual void executeCommand()
00333    {
00334       mClientRegistration.end();
00335    }
00336 
00337    virtual EncodeStream& encodeBrief(EncodeStream& strm) const
00338    {
00339       return strm << "ClientRegistrationEndCommand";
00340    }
00341 private:
00342    ClientRegistration& mClientRegistration;
00343 };
00344 
00345 void
00346 ClientRegistration::endCommand()
00347 {
00348    mDum.post(new ClientRegistrationEndCommand(*this));
00349 }
00350 
00351 EncodeStream& 
00352 ClientRegistration::dump(EncodeStream& strm) const
00353 {
00354    strm << "ClientRegistration " << mLastRequest->header(h_From).uri();
00355    return strm;
00356 }
00357 
00358 void
00359 ClientRegistration::dispatch(const SipMessage& msg)
00360 {
00361    try
00362    {
00363       // !jf! there may be repairable errors that we can handle here
00364       assert(msg.isResponse());
00365       const int& code = msg.header(h_StatusLine).statusCode();
00366       bool nextHopSupportsOutbound = false;
00367       int keepAliveTime = 0;
00368 
00369       // If registration was successful - look for next hop indicating support for outbound
00370       if(mDialogSet.mUserProfile->clientOutboundEnabled() && msg.isExternal() && code >= 200 && code < 300)
00371       {
00372          // If ClientOutbound support is enabled and the registration response contains 
00373          // Requires: outbound or a Path with outbound then outbound is supported by the 
00374          // server, so store the flow key in the UserProfile 
00375          // and use it for sending all messages (DialogUsageManager::sendUsingOutboundIfAppropriate)
00376          try
00377          {
00378             if((!msg.empty(h_Paths) && msg.header(h_Paths).back().uri().exists(p_ob)) ||
00379                (!msg.empty(h_Requires) && msg.header(h_Requires).find(Token(Symbols::Outbound))))
00380             {
00381                mDialogSet.mUserProfile->mClientOutboundFlowTuple = msg.getSource();
00382                mDialogSet.mUserProfile->mClientOutboundFlowTuple.onlyUseExistingConnection = true;
00383                nextHopSupportsOutbound = true;
00384                if(!msg.empty(h_FlowTimer))
00385                {
00386                   keepAliveTime = msg.header(h_FlowTimer).value();
00387                }
00388             }
00389          }
00390          catch(BaseException&e)
00391          {
00392             ErrLog(<<"Error parsing Path or Requires:" << e);
00393          }
00394       }
00395 
00396       if(msg.isExternal())
00397       {
00398          const Data& receivedTransport = msg.header(h_Vias).front().transport();
00399          if(keepAliveTime == 0)
00400          {
00401             if(receivedTransport == Symbols::TCP ||
00402                receivedTransport == Symbols::TLS ||
00403                receivedTransport == Symbols::SCTP)
00404             {
00405                keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForStream();
00406             }
00407             else
00408             {
00409                keepAliveTime = mDialogSet.mUserProfile->getKeepAliveTimeForDatagram();
00410             }
00411          }
00412 
00413          if(keepAliveTime > 0)
00414          {
00415             mNetworkAssociation.update(msg, keepAliveTime, nextHopSupportsOutbound);
00416          }
00417       }
00418 
00419       if (code < 200)
00420       {
00421          // throw it away
00422          return;
00423       }
00424       else if (code < 300) // success
00425       {
00426          try
00427          {
00428             if (msg.exists(h_ServiceRoutes))
00429             {
00430                InfoLog(<< "Updating service route: " << Inserter(msg.header(h_ServiceRoutes)));
00431                mDialogSet.mUserProfile->setServiceRoute(msg.header(h_ServiceRoutes));
00432             }
00433             else
00434             {
00435                DebugLog(<< "Clearing service route (" << Inserter(getUserProfile()->getServiceRoute()) << ")");
00436                mDialogSet.mUserProfile->setServiceRoute(NameAddrs());
00437             }
00438          }
00439          catch(BaseException &e)
00440          {
00441             ErrLog(<< "Error Parsing Service Route:" << e);
00442          }    
00443 
00444          //gruu update, should be optimized
00445          try
00446          {
00447             if(mDialogSet.mUserProfile->gruuEnabled() && msg.exists(h_Contacts))
00448             {
00449                for (NameAddrs::const_iterator it = msg.header(h_Contacts).begin(); 
00450                     it != msg.header(h_Contacts).end(); it++)
00451                {
00452                   if (it->exists(p_Instance) && it->param(p_Instance) == mDialogSet.mUserProfile->getInstanceId())
00453                   {
00454                      if(it->exists(p_pubGruu))
00455                      {
00456                         mDialogSet.mUserProfile->setPublicGruu(Uri(it->param(p_pubGruu)));
00457                      }
00458                      if(it->exists(p_tempGruu))
00459                      {
00460                         mDialogSet.mUserProfile->setTempGruu(Uri(it->param(p_tempGruu)));
00461                      }
00462                      break;
00463                    }
00464                }
00465             }
00466          }
00467          catch(BaseException&e)
00468          {
00469             ErrLog(<<"Error parsing GRUU:" << e);
00470          }
00471          
00472          // !jf! consider what to do if no contacts
00473          // !ah! take list of ctcs and push into mMy or mOther as required.
00474 
00475          // make timers to re-register
00476          UInt32 expiry = calculateExpiry(msg);
00477          if(msg.exists(h_Contacts))
00478          {
00479             mAllContacts = msg.header(h_Contacts);
00480          }
00481          else
00482          {
00483             mAllContacts.clear();
00484          }
00485 
00486          if (expiry != 0 && expiry != UINT_MAX)
00487          {
00488             if(expiry>=7)
00489             {
00490                int exp = Helper::aBitSmallerThan(expiry);
00491                mExpires = exp + Timer::getTimeSecs();
00492                mDum.addTimer(DumTimeout::Registration,
00493                              exp,
00494                              getBaseHandle(),
00495                              ++mTimerSeq);
00496             }
00497             else
00498             {
00499                WarningLog(<< "Server is using an unreasonably low expiry: " 
00500                            << expiry 
00501                            << " We're just going to end this registration.");
00502                end();
00503                return;
00504             }
00505          }
00506 
00507          switch (mState)
00508          {
00509             case Querying:
00510             case Adding:
00511                if(expiry != 0)
00512                {
00513                   mState = Registered;
00514                   mDum.mClientRegistrationHandler->onSuccess(getHandle(), msg);
00515                }
00516                else
00517                {
00518                   mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
00519                   checkProfileRetry(msg);
00520                }
00521                break;
00522 
00523             case Removing:
00524                //mDum.mClientRegistrationHandler->onSuccess(getHandle(), msg);
00525                mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
00526                InfoLog (<< "Finished removing registration " << *this << " mEndWhenDone=" << mEndWhenDone);
00527                if (mEndWhenDone)
00528                {
00529                   // !kh!
00530                   // stopRegistering() deletes 'this'
00531                   // furthur processing makes no sense
00532                   //assert(mQueuedState == None);
00533                   stopRegistering();
00534                   return;
00535                }
00536                break;
00537 
00538             case Registered:
00539             case Refreshing:
00540                mState = Registered;
00541                if(expiry != 0)
00542                {
00543                   if(mUserRefresh)
00544                   {
00545                       mUserRefresh = false;
00546                       mDum.mClientRegistrationHandler->onSuccess(getHandle(), msg);
00547                   }
00548                }
00549                else
00550                {
00551                   mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
00552                   checkProfileRetry(msg);
00553                }
00554                break;
00555 
00556             default:
00557                break;
00558          }
00559 
00560          if (mQueuedState != None)
00561          {
00562             InfoLog (<< "Sending queued request: " << *mQueuedRequest);
00563             mState = mQueuedState;
00564             mQueuedState = None;
00565             *mLastRequest = *mQueuedRequest;
00566             send(mLastRequest);
00567          }
00568       }
00569       else
00570       {
00571          if((mState == Adding || mState == Refreshing) && !mEndWhenDone)
00572          {
00573             if (code == 423) // interval too short
00574             {
00575                UInt32 maxRegistrationTime = mDialogSet.mUserProfile->getDefaultMaxRegistrationTime();
00576                if (msg.exists(h_MinExpires) && 
00577                    (maxRegistrationTime == 0 || msg.header(h_MinExpires).value() < maxRegistrationTime)) // If maxRegistrationTime is enabled, then check it
00578                {
00579                   mRegistrationTime = msg.header(h_MinExpires).value();
00580                   mLastRequest->header(h_Expires).value() = mRegistrationTime;
00581                   mLastRequest->header(h_CSeq).sequence()++;
00582                   send(mLastRequest);
00583                   return;
00584                }
00585             }
00586             else if (code == 408 || (code == 503 && msg.getReceivedTransport() == 0))
00587             {
00588                int retry = mDum.mClientRegistrationHandler->onRequestRetry(getHandle(), 0, msg);
00589             
00590                if (retry < 0)
00591                {
00592                   DebugLog(<< "Application requested failure on Retry-After");
00593                }
00594                else if (retry == 0)
00595                {
00596                   DebugLog(<< "Application requested immediate retry on 408 or internal 503");
00597                
00598                   mLastRequest->header(h_CSeq).sequence()++;
00599                   send(mLastRequest);
00600                   mUserRefresh = true;  // Reset this flag, so that the onSuccess callback will be called if we are successful when re-trying
00601                   return;
00602                }
00603                else
00604                {
00605                   DebugLog(<< "Application requested delayed retry on 408 or internal 503: " << retry);
00606                   mExpires = 0;
00607                   switch(mState)
00608                   {
00609                   case Adding:
00610                      mState = RetryAdding;
00611                      break;
00612                   case Refreshing:
00613                      mState = RetryRefreshing;
00614                      break;
00615                   default:
00616                      assert(false);
00617                      break;
00618                   }
00619                   if(mDum.mClientAuthManager.get()) mDum.mClientAuthManager.get()->clearAuthenticationState(DialogSetId(*mLastRequest));
00620                   mDum.addTimer(DumTimeout::RegistrationRetry, 
00621                                 retry, 
00622                                 getBaseHandle(),
00623                                 ++mTimerSeq);       
00624                   mUserRefresh = true;  // Reset this flag, so that the onSuccess callback will be called if we are successful when re-trying
00625                   return;
00626                }
00627             }
00628          }
00629          
00630          mDum.mClientRegistrationHandler->onFailure(getHandle(), msg);
00631          mUserRefresh = true;  // Reset this flag, so that the onSuccess callback will be called if we are successful when re-trying
00632 
00633          // Retry if Profile setting is set
00634          unsigned int retryInterval = checkProfileRetry(msg);
00635          if(retryInterval > 0)
00636          {
00637             InfoLog( << "Registration error " << code << " for " << msg.header(h_To) << ", retrying in " << retryInterval << " seconds.");
00638             return;
00639          }
00640 
00641          // assume that if a failure occurred, the bindings are gone
00642          if (mEndWhenDone)
00643          {
00644             mDum.mClientRegistrationHandler->onRemoved(getHandle(), msg);
00645          }
00646          delete this;
00647       }
00648    }
00649    catch(BaseException& e)
00650    {
00651       InfoLog( << "Exception in ClientRegistration::dispatch: "  <<  e.getMessage());
00652       mDum.mClientRegistrationHandler->onFailure(getHandle(), msg);
00653       delete this;
00654    }
00655 }
00656 
00657 void 
00658 ClientRegistration::tagContact(NameAddr& contact) const
00659 {
00660    tagContact(contact, mDum, mDialogSet.mUserProfile);
00661 }
00662 
00663 void 
00664 ClientRegistration::tagContact(NameAddr& contact, DialogUsageManager& dum, SharedPtr<UserProfile>& userProfile)
00665 {
00666    if(contact.uri().host().empty() || 
00667       dum.getSipStack().isMyDomain(contact.uri().host(), contact.uri().port()))
00668    {
00669       // Contact points at us; it is appropriate to add a +sip.instance to 
00670       // this Contact. We don't need to have full gruu support enabled to add
00671       // a +sip.instance either...
00672       if(userProfile->hasInstanceId())
00673       {
00674          contact.param(p_Instance) = userProfile->getInstanceId();
00675          if(userProfile->getRegId() != 0)
00676          {
00677             contact.param(p_regid) = userProfile->getRegId();
00678          }
00679       }
00680       else if(userProfile->getRinstanceEnabled())
00681       {
00682          // !slg! poor mans instance id so that we can tell which contacts 
00683          // are ours - to be replaced by gruu someday.
00684          InfoLog(<< "You really should consider setting an instance id in"
00685                      " the UserProfile (see UserProfile::setInstanceId())."
00686                      " This is really easy, and makes this class much less "
00687                      "likely to clash with another endpoint registering at "
00688                      "the same AOR.");
00689          contact.uri().param(p_rinstance) = Random::getCryptoRandomHex(8);  
00690       }
00691       else if(!contact.uri().user().empty())
00692       {
00693          WarningLog(<< "Ok, not only have you not specified an instance id, "
00694                   "you have disabled the rinstance hack (ie; resip's \"poor"
00695                   " man's +sip.instance\"). We will try to match Contacts"
00696                   " based on what you've put in the user-part of your "
00697                   "Contact, but this can be dicey, especially if you've put"
00698                   " something there that another endpoint is likely to "
00699                   "use.");
00700       }
00701       else
00702       {
00703          ErrLog(<< "Ok, not only have you not specified an instance id, "
00704                   "you have disabled the rinstance hack (ie; resip's \"poor"
00705                   " man's +sip.instance\"), _and_ you haven't put anything"
00706                   " in the user-part of your Contact. This is asking for "
00707                   "confusion later. We'll do our best to try to match things"
00708                   " up later when the response comes in...");
00709       }
00710    }
00711    else
00712    {
00713       // Looks like a third-party registration. +sip.instance is out of the 
00714       // question, but we can still use rinstance.
00715       if(userProfile->getRinstanceEnabled())
00716       {
00717          // !slg! poor mans instance id so that we can tell which contacts 
00718          // are ours - to be replaced by gruu someday.
00719          contact.uri().param(p_rinstance) = Random::getCryptoRandomHex(8);  
00720       }
00721       else if(!contact.uri().user().empty())
00722       {
00723          WarningLog(<< "You're trying to do a third-party registration, but "
00724                   "you have disabled the rinstance hack (ie; resip's \"poor"
00725                   " man's +sip.instance\"). We will try to match Contacts"
00726                   " based on what you've put in the user-part of your "
00727                   "Contact, but this can be dicey, especially if you've put"
00728                   " something there that another endpoint is likely to "
00729                   "use.");
00730       }
00731       else
00732       {
00733          ErrLog(<< "You're trying to do a third-party registration,  and "
00734                   "not only have you disabled the rinstance hack (ie; "
00735                   "resip's \"poor man's +sip.instance\"), you haven't"
00736                   " put anything in the user-part of your Contact. This is "
00737                   "asking for confusion later. We'll do our best to try to "
00738                   "match things up later when the response comes in...");
00739       }
00740    }
00741    
00742    if (userProfile->getMethodsParamEnabled())
00743    {
00744       contact.param(p_methods) = dum.getMasterProfile()->getAllowedMethodsData();
00745    }
00746 
00747    // ?bwc? Host and port override?
00748 }
00749 
00750 unsigned long 
00751 ClientRegistration::calculateExpiry(const SipMessage& reg200) const
00752 {
00753    unsigned long expiry=mRegistrationTime;
00754    if(reg200.exists(h_Expires) &&
00755       reg200.header(h_Expires).isWellFormed() &&
00756       reg200.header(h_Expires).value() < expiry)
00757    {
00758       expiry=reg200.header(h_Expires).value();
00759    }
00760 
00761    if(!reg200.exists(h_Contacts))
00762    {
00763       return expiry;
00764    }
00765 
00766    const NameAddrs& contacts(reg200.header(h_Contacts));
00767 
00768    for(NameAddrs::const_iterator c=contacts.begin();c!=contacts.end();++c)
00769    {
00770       // Our expiry is never going to increase if we find one of our contacts, 
00771       // so if the expiry is not lower, we just ignore it. For registrars that
00772       // leave our requested expiry alone, this code ends up being pretty quick,
00773       // especially if there aren't contacts from other endpoints laying around.
00774       if(c->isWellFormed() &&
00775          c->exists(p_expires) && 
00776          c->param(p_expires) < expiry &&
00777          contactIsMine(*c))
00778       {
00779          expiry=c->param(p_expires);
00780       }
00781    }
00782    return expiry;
00783 }
00784 
00785 bool 
00786 ClientRegistration::contactIsMine(const NameAddr& contact) const
00787 {
00788    // Try to find this contact in mMyContacts
00789    if(mDialogSet.mUserProfile->hasInstanceId() && 
00790       contact.exists(p_Instance))
00791    {
00792       return contact.param(p_Instance)==mDialogSet.mUserProfile->getInstanceId();
00793    }
00794    else if(mDialogSet.mUserProfile->getRinstanceEnabled() &&
00795             contact.uri().exists(p_rinstance))
00796    {
00797       return rinstanceIsMine(contact.uri().param(p_rinstance));
00798    }
00799    else
00800    {
00801       return searchByUri(contact.uri());
00802    }
00803 }
00804 
00805 bool 
00806 ClientRegistration::rinstanceIsMine(const Data& rinstance) const
00807 {
00808    // !bwc! This could be made faster if we used a single rinstance...
00809    for(NameAddrs::const_iterator m=mMyContacts.begin(); m!=mMyContacts.end(); ++m)
00810    {
00811       if(m->uri().exists(p_rinstance) && m->uri().param(p_rinstance)==rinstance)
00812       {
00813          return true;
00814       }
00815    }
00816    return false;
00817 }
00818 
00819 bool 
00820 ClientRegistration::searchByUri(const Uri& cUri) const
00821 {
00822    for(NameAddrs::const_iterator m=mMyContacts.begin(); m!=mMyContacts.end(); ++m)
00823    {
00824       if(m->uri()==cUri)
00825       {
00826          return true;
00827       }
00828       else if(m->uri().host().empty() && 
00829                m->uri().user()==cUri.user() &&
00830                m->uri().scheme()==cUri.scheme() &&
00831                mDum.getSipStack().isMyDomain(cUri.host(), cUri.port()))
00832       {
00833          // Empty host-part in our contact; this means we're relying on the 
00834          // stack to fill out this Contact header. Also, the user-part matches.
00835          return true;
00836       }
00837    }
00838    return false;
00839 }
00840 
00841 unsigned int 
00842 ClientRegistration::checkProfileRetry(const SipMessage& msg)
00843 {
00844    unsigned int retryInterval = mDialogSet.mUserProfile->getDefaultRegistrationRetryTime();
00845    if (retryInterval > 0 &&
00846       (mState == Adding || mState == Refreshing) &&
00847       !mEndWhenDone)
00848    {
00849       if (msg.exists(h_RetryAfter) && msg.header(h_RetryAfter).value() > 0)
00850       {
00851          // Use retry interval from error response
00852          retryInterval = msg.header(h_RetryAfter).value();
00853       }
00854       mExpires = 0;
00855       switch(mState)
00856       {
00857       case Adding:
00858          mState = RetryAdding;
00859          break;
00860       case Refreshing:
00861          mState = RetryRefreshing;
00862          break;
00863       default:
00864          assert(false);
00865          break;
00866       }
00867 
00868       if(mDum.mClientAuthManager.get()) mDum.mClientAuthManager.get()->clearAuthenticationState(DialogSetId(*mLastRequest));
00869       mDum.addTimer(DumTimeout::RegistrationRetry,
00870          retryInterval,
00871          getBaseHandle(),
00872          ++mTimerSeq);
00873       return retryInterval;
00874    }
00875    return 0;
00876 }
00877 
00878 void
00879 ClientRegistration::dispatch(const DumTimeout& timer)
00880 {
00881    switch(timer.type())
00882    {
00883       case DumTimeout::Registration:
00884          // If you happen to be Adding/Updating when the timer goes off, you should just ignore
00885          // it since a new timer will get added when the 2xx is received.
00886          if (timer.seq() == mTimerSeq && mState == Registered)
00887          {
00888             if (!mMyContacts.empty())
00889             {
00890                internalRequestRefresh();
00891             }
00892          }
00893          break;
00894 
00895       case DumTimeout::RegistrationRetry:
00896          if (timer.seq() == mTimerSeq)
00897          {
00898             switch(mState)
00899             {
00900             case RetryAdding:
00901                mState = Adding;
00902                break;
00903             case RetryRefreshing:
00904                mState = Refreshing;
00905                break;
00906             default:
00907               assert(false);
00908               break;
00909             }
00910 
00911             // Resend last request
00912             mLastRequest->header(h_CSeq).sequence()++;
00913             mLastRequest->remove(h_ProxyAuthorizations);
00914             mLastRequest->remove(h_Authorizations); 
00915             send(mLastRequest);
00916          }
00917          break;
00918       default:
00919          break;
00920    }
00921 }
00922 
00923 void 
00924 ClientRegistration::flowTerminated()
00925 {
00926    // Clear the network association
00927    mNetworkAssociation.clear();
00928 
00929    // Notify application - not default handler implementation is to immediately attempt
00930    // a re-registration in order to form a new flow
00931    mDum.mClientRegistrationHandler->onFlowTerminated(getHandle());
00932 }
00933 
00934 
00935 /* ====================================================================
00936  * The Vovida Software License, Version 1.0
00937  *
00938  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00939  *
00940  * Redistribution and use in source and binary forms, with or without
00941  * modification, are permitted provided that the following conditions
00942  * are met:
00943  *
00944  * 1. Redistributions of source code must retain the above copyright
00945  *    notice, this list of conditions and the following disclaimer.
00946  *
00947  * 2. Redistributions in binary form must reproduce the above copyright
00948  *    notice, this list of conditions and the following disclaimer in
00949  *    the documentation and/or other materials provided with the
00950 
00951  *    distribution.
00952  *
00953  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00954  *    and "Vovida Open Communication Application Library (VOCAL)" must
00955  *    not be used to endorse or promote products derived from this
00956  *    software without prior written permission. For written
00957  *    permission, please contact vocal@vovida.org.
00958  *
00959  * 4. Products derived from this software may not be called "VOCAL", nor
00960  *    may "VOCAL" appear in their name, without prior written
00961  *    permission of Vovida Networks, Inc.
00962  *
00963  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00964  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00965  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00966  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00967  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00968  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00969  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00970  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00971  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00972  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00973  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00974  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00975  * DAMAGE.
00976  *
00977  * ====================================================================
00978  *
00979  * This software consists of voluntary contributions made by Vovida
00980  * Networks, Inc. and many individuals on behalf of Vovida Networks,
00981  * Inc.  For more information on Vovida Networks, Inc., please see
00982  * <http://www.vovida.org/>.
00983  *
00984  */