reSIProcate/DialogUsageManager  9680
DialogSet.cxx
Go to the documentation of this file.
00001 
00002 #include "resip/stack/Helper.hxx"
00003 #include "resip/dum/AppDialog.hxx"
00004 #include "resip/dum/AppDialogSet.hxx"
00005 #include "resip/dum/BaseCreator.hxx"
00006 #include "resip/dum/ClientAuthManager.hxx"
00007 #include "resip/dum/ClientOutOfDialogReq.hxx"
00008 #include "resip/dum/ClientPublication.hxx"
00009 #include "resip/dum/ClientRegistration.hxx"
00010 #include "resip/dum/ClientPagerMessage.hxx"
00011 #include "resip/dum/ServerPagerMessage.hxx"
00012 #include "resip/dum/Dialog.hxx"
00013 #include "resip/dum/DialogSet.hxx"
00014 #include "resip/dum/DialogSetHandler.hxx"
00015 #include "resip/dum/DialogEventStateManager.hxx"
00016 #include "resip/dum/DialogUsageManager.hxx"
00017 #include "resip/dum/MasterProfile.hxx"
00018 #include "resip/dum/RedirectManager.hxx"
00019 #include "resip/dum/UsageUseException.hxx"
00020 #include "resip/dum/ServerOutOfDialogReq.hxx"
00021 #include "resip/dum/ServerRegistration.hxx"
00022 #include "resip/dum/DumHelper.hxx"
00023 #include "resip/dum/SubscriptionCreator.hxx"
00024 #include "rutil/Logger.hxx"
00025 #include "rutil/Inserter.hxx"
00026 #include "rutil/WinLeakCheck.hxx"
00027 
00028 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
00029 
00030 using namespace resip;
00031 using namespace std;
00032 
00033 // UAC 
00034 DialogSet::DialogSet(BaseCreator* creator, DialogUsageManager& dum) :
00035    mMergeKey(),
00036    mDialogs(),
00037    mCreator(creator),
00038    mId(*creator->getLastRequest()),
00039    mDum(dum),
00040    mAppDialogSet(0),
00041    mState(Initial),
00042    mClientRegistration(0),
00043    mServerRegistration(0),
00044    mClientPublication(0),
00045    mClientOutOfDialogRequests(),
00046    mServerOutOfDialogRequest(0),
00047    mClientPagerMessage(0),
00048    mServerPagerMessage(0)
00049 {
00050    setUserProfile(creator->getUserProfile());
00051    assert(!creator->getLastRequest()->isExternal());
00052    DebugLog ( << " ************* Created DialogSet(UAC)  -- " << mId << "*************" );
00053 }
00054 
00055 // UAS 
00056 DialogSet::DialogSet(const SipMessage& request, DialogUsageManager& dum) :
00057    mMergeKey(request, dum.getMasterProfile()->checkReqUriInMergeDetectionEnabled()),
00058    mDialogs(),
00059    mCreator(0),
00060    mId(request),
00061    mDum(dum),
00062    mAppDialogSet(0),
00063    mState(Established),
00064    mClientRegistration(0),
00065    mServerRegistration(0),
00066    mClientPublication(0),
00067    mClientOutOfDialogRequests(),
00068    mServerOutOfDialogRequest(0),
00069    mClientPagerMessage(0),
00070    mServerPagerMessage(0)
00071 {
00072    assert(request.isRequest());
00073    assert(request.isExternal());
00074    mDum.mMergedRequests.insert(mMergeKey);
00075    if (request.header(h_RequestLine).method() == INVITE)
00076    {
00077       if(mDum.mCancelMap.count(request.getTransactionId()) != 0)
00078       {
00079          WarningLog ( << "An endpoint is using the same tid in multiple INVITE requests, ability to match CANCEL requests correctly may be comprimised, tid=" << request.getTransactionId() );
00080       }
00081       mCancelKey = request.getTransactionId();
00082       mDum.mCancelMap[mCancelKey] = this;
00083    }
00084    DebugLog ( << " ************* Created DialogSet(UAS) *************: " << mId);
00085 }
00086 
00087 DialogSet::~DialogSet()
00088 {
00089    if (mDum.mClientAuthManager.get())
00090    {
00091       mDum.mClientAuthManager->dialogSetDestroyed(getId());
00092    }
00093 
00094    if (mMergeKey != MergedRequestKey::Empty)
00095    {
00096       mDum.requestMergedRequestRemoval(mMergeKey);
00097    }
00098 
00099    if (!mCancelKey.empty())
00100    {
00101       mDum.mCancelMap.erase(mCancelKey);
00102    }
00103 
00104    delete mCreator;
00105    while(!mDialogs.empty())
00106    {
00107       delete mDialogs.begin()->second;
00108    }
00109 
00110    delete mClientRegistration;
00111    delete mServerRegistration;
00112    delete mClientPublication;
00113    delete mServerOutOfDialogRequest;
00114    delete mClientPagerMessage;
00115    delete mServerPagerMessage;
00116 
00117    while (!mClientOutOfDialogRequests.empty())
00118    {
00119       delete *mClientOutOfDialogRequests.begin();
00120    }
00121 
00122    DebugLog ( << " ********** DialogSet::~DialogSet: " << mId << "*************" );
00123    // !dcm! -- very delicate code, change the order things go horribly wrong
00124 
00125    mDum.removeDialogSet(this->getId());
00126    if (mAppDialogSet) 
00127    {
00128       mAppDialogSet->destroy();
00129    }
00130 }
00131 
00132 void DialogSet::possiblyDie()
00133 {
00134    if(mState != Destroying &&
00135       mDialogs.empty() &&
00136       // The following check ensures we are not a UAC DialogSet in the Initial or 
00137       // ReceivedProvisional states.
00138       // .slg. this check fixes a case where we might receive a short term usuage 
00139       //       request (such as OPTIONS) in the same dialogset as a UAC dialogset
00140       //       for which we have not created any Dialogs yet - in this case
00141       //       we don't want the dialogset to die, since the UAC usage is not complete.     
00142       (mCreator == 0 || (mState != Initial && mState != ReceivedProvisional)) &&  
00143       mClientOutOfDialogRequests.empty() &&
00144       !(mClientPublication ||
00145         mServerOutOfDialogRequest ||
00146         mClientPagerMessage ||
00147         mServerPagerMessage ||
00148         mClientRegistration ||
00149         mServerRegistration))
00150    {
00151       mState = Destroying;
00152       mDum.destroy(this);
00153    }
00154 }
00155 
00156 DialogSetId
00157 DialogSet::getId() const
00158 {
00159    return mId;
00160 }
00161 
00162 void
00163 DialogSet::addDialog(Dialog *dialog)
00164 {
00165    mDialogs[dialog->getId()] = dialog;
00166 }
00167 
00168 BaseCreator*
00169 DialogSet::getCreator()
00170 {
00171    return mCreator;
00172 }
00173 
00174 Dialog*
00175 DialogSet::findDialog(const SipMessage& msg)
00176 {
00177    if (msg.isResponse() && msg.header(h_StatusLine).statusCode() == 100)
00178    {
00179       return 0;
00180    }
00181    return findDialog(DialogId(msg));
00182 #if 0   
00183    DialogId id(msg);
00184    Dialog* dlog = findDialog(id);
00185    //vonage/2543 matching here
00186    if (dlog)
00187    {
00188       return dlog;
00189    }
00190    //match off transaction ID
00191    else if (msg.isResponse() && !msg.header(h_To).exists(p_tag))
00192    {
00193       for(DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
00194       {
00195          if (it->second->matches(msg))
00196          {
00197             return it->second;            
00198          }
00199       }
00200    }
00201    else if (msg.exists(h_Contacts) && !msg.header(h_Contacts).empty()
00202             && msg.isResponse() 
00203             && mDum.getProfile()->looseToTagMatching()
00204             && msg.header(h_To).exists(p_tag))     
00205    {
00206       const Uri& contact = msg.header(h_Contacts).front().uri();
00207       
00208       //match by contact
00209       for(DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
00210       {
00211          if (it->second->mRemoteTarget.uri() == msg.header(h_Contacts).front().uri())
00212          {
00213             // !dcm! in the vonage case, the to tag should be updated to match the fake
00214             //vonage tag introduced in the 200 which is also used for the BYE.
00215             //find out how deep this rabbit hole goes, may just have a pugabble
00216             //filter api that can be added for dialog matching if things get any
00217             //more specific--this is the VonageKludgeFilter
00218             Dialog* dialog = it->second;
00219             DialogId old = dialog->getId();
00220             dialog->mId = DialogId(old.getCallId(), old.getLocalTag(), msg.header(h_To).param(p_tag));
00221             dialog->mRemoteNameAddr.param(p_tag) = msg.header(h_To).param(p_tag);
00222             mDialogs.erase(it);
00223             mDialogs[dialog->getId()] = dialog;
00224             return dialog;
00225          }
00226       }
00227    }
00228    return 0;
00229 #endif
00230 }
00231 
00232 bool
00233 DialogSet::empty() const
00234 {
00235    return mDialogs.empty();
00236 }
00237 
00238 bool
00239 DialogSet::handledByAuthOrRedirect(const SipMessage& msg)
00240 {
00241    if (msg.isResponse() && !(mState == Terminating || 
00242                              mState == WaitingToEnd || 
00243                              mState == Destroying || 
00244                              mState == Cancelling))
00245    {
00246       // !dcm! -- multiple usage grief...only one of each method type allowed
00247       if (getCreator() &&
00248           msg.header(h_CSeq) == getCreator()->getLastRequest()->header(h_CSeq))
00249       {
00250          if (mDum.mClientAuthManager.get())
00251          {
00252             if (mDum.mClientAuthManager->handle(*getUserProfile().get(), *getCreator()->getLastRequest(), msg))
00253             {
00254                // Note:  ClientAuthManager->handle will end up incrementing the CSeq sequence of getLastRequest
00255                DebugLog( << "about to re-send request with digest credentials" );
00256                StackLog( << getCreator()->getLastRequest() );
00257                
00258                mDum.send(getCreator()->getLastRequest());
00259                return true;                     
00260             }
00261          }
00262          // !dcm! -- need to protect against 3xx highjacking a dialogset which
00263          //has a fully established dialog. also could case strange behaviour
00264          //by sending 401/407 at the wrong time.
00265          if (mDum.mRedirectManager.get() && mState != Established)  // !slg! for now don't handle redirect in established dialogs - alternatively we could treat as a target referesh (using 1st Contact) and reissue request
00266          {
00267             if (mDum.mRedirectManager->handle(*this, *getCreator()->getLastRequest(), msg))
00268             {
00269                //terminating existing dialogs(branches) as this is a final
00270                //response--?dcm?--merge w/ forking logic somehow?                              
00271                // !dcm! -- really, really horrible.  Should make a don't die
00272                //scoped guard
00273                mState = Initial;               
00274                for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end();)
00275                {
00276                   (it++)->second->redirected(msg);
00277                }
00278 
00279                if (mDialogs.size() == 0)
00280                {
00281                   if (mDum.mDialogEventStateManager)
00282                   {
00283                      mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Rejected);
00284                   }
00285                }
00286 
00287                InfoLog( << "about to re-send request to redirect destination" );
00288                DebugLog( << getCreator()->getLastRequest() );
00289                
00290                mDum.send(getCreator()->getLastRequest());
00291                return true;                     
00292             }
00293 
00294             // Check if a 422 response to initial Invite (RFC4028)
00295             if(msg.header(h_StatusLine).statusCode() == 422 && msg.exists(h_MinSE))
00296             {
00297                // Change interval to min from 422 response
00298                getCreator()->getLastRequest()->header(h_SessionExpires).value() = msg.header(h_MinSE).value();
00299                getCreator()->getLastRequest()->header(h_MinSE).value() = msg.header(h_MinSE).value();
00300                getCreator()->getLastRequest()->header(h_CSeq).sequence()++;
00301 
00302                InfoLog( << "about to re-send request with new session expiration time" );
00303                DebugLog( << getCreator()->getLastRequest() );
00304                
00305                mDum.send(getCreator()->getLastRequest());
00306                return true;                     
00307             }
00308          }
00309       }
00310    }
00311    return false;
00312 }
00313 
00314 
00315 void
00316 DialogSet::dispatch(const SipMessage& msg)
00317 {
00318    if(!mAppDialogSet)
00319    {
00320       // !bwc! There are conditions where reuse of the AppDialogSet will cause
00321       // us to hit this code. This is because the teardown of DialogSets is not
00322       // atomic, causing the DialogSet to hang around for a short time after it
00323       // has given up its AppDialogSet. Also, if we have multiple Usages in
00324       // this DialogSet, one of the Usages may decide to re-establish itself in 
00325       // a new Dialog, and take the AppDialogSet with it, leaving all the others
00326       // high and dry. This is a design issue that will take some real effort to
00327       // fix properly. This is a band-aid for now.
00328       // TODO fix this properly
00329       if(msg.isRequest())
00330       {
00331          if(msg.method() != ACK)
00332          {
00333             SipMessage err;
00334             Helper::makeResponse(err, msg, 500, "DialogSet: My AppDialogSet is "
00335                                        "missing!");
00336             mDum.sendResponse(err);
00337          }
00338       }
00339       else
00340       {
00341          ErrLog(<<"Response came in, but no AppDialogSet! Dropping this is very"
00342                   "likely to cause leaks, but continuing to process it is "
00343                   "likely to cause a core. Taking the lesser of two evils...");
00344       }
00345       return;
00346    }
00347 
00348    assert(msg.isRequest() || msg.isResponse());
00349 
00350    if (mState == WaitingToEnd)
00351    {
00352       assert(mDialogs.empty());
00353       if (msg.isResponse())         
00354       {
00355          int code = msg.header(h_StatusLine).statusCode();
00356          switch(mCreator->getLastRequest()->header(h_CSeq).method())
00357          {
00358             case INVITE:
00359                if (code / 100 == 1)
00360                {
00361                   mState = ReceivedProvisional;
00362                   end();
00363                }
00364                else if (code / 100 == 2)
00365                {
00366                   Dialog dialog(mDum, msg, *this);
00367 
00368                   SharedPtr<SipMessage> ack(new SipMessage);
00369                   dialog.makeRequest(*ack, ACK);
00370                   ack->header(h_CSeq).sequence() = msg.header(h_CSeq).sequence();
00371                   dialog.send(ack);
00372                   
00373                   SharedPtr<SipMessage> bye(new SipMessage);
00374                   dialog.makeRequest(*bye, BYE);
00375                   dialog.send(bye);
00376                   
00377                   if (mDum.mDialogEventStateManager)
00378                   {
00379                      mDum.mDialogEventStateManager->onTerminated(dialog, *bye, InviteSessionHandler::LocalBye);
00380                   }
00381                   // Note:  Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
00382                }
00383                else
00384                {
00385                   if (mDum.mDialogEventStateManager)
00386                   {
00387                      mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Rejected);
00388                   }           
00389                   mState = Destroying;
00390                   mDum.destroy(this);
00391                }
00392                break;
00393             case SUBSCRIBE:
00394                if (code / 100 == 1)
00395                {
00396                   // do nothing - wait for final response
00397                }
00398                else if (code / 100 == 2)
00399                {
00400                   Dialog dialog(mDum, msg, *this);
00401 
00402                   SharedPtr<SipMessage> unsubscribe(new SipMessage(*mCreator->getLastRequest().get()));  // create message from initial request so we get proper headers
00403                   dialog.makeRequest(*unsubscribe, SUBSCRIBE);
00404                   unsubscribe->header(h_Expires).value() = 0;
00405                   dialog.send(unsubscribe);
00406                   
00407                   // Note:  Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
00408                }
00409                else
00410                {
00411                   mState = Destroying;
00412                   mDum.destroy(this);
00413                }
00414                break;
00415             case PUBLISH:
00416                if (code / 100 == 1)
00417                {
00418                   // do nothing - wait for final response
00419                }
00420                else if (code / 100 == 2)
00421                {
00422                   Dialog dialog(mDum, msg, *this);
00423 
00424                   SharedPtr<SipMessage> unpublish(new SipMessage(*mCreator->getLastRequest().get()));  // create message from initial request so we get proper headers
00425                   dialog.makeRequest(*unpublish, PUBLISH);
00426                   unpublish->header(h_Expires).value() = 0;
00427                   dialog.send(unpublish);
00428                   
00429                   // Note:  Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
00430                }
00431                else
00432                {
00433                   mState = Destroying;
00434                   mDum.destroy(this);
00435                }
00436                break;
00437                // ?slg? shouldn't we handle register, ood and refer here too?
00438             default:
00439                mState = Destroying;
00440                mDum.destroy(this);
00441                break;
00442          }
00443       }
00444       else
00445       {
00446          SharedPtr<SipMessage> response(new SipMessage);         
00447          mDum.makeResponse(*response, msg, 481);
00448          mDum.send(response);
00449       }
00450       return;
00451    }
00452    else if(mState == Cancelling)
00453    {
00454       assert(mDialogs.empty());
00455       if (msg.isResponse())         
00456       {
00457          int code = msg.header(h_StatusLine).statusCode();
00458          if(mCreator->getLastRequest()->header(h_CSeq).method() == INVITE)
00459          {
00460             if (code / 100 == 1)
00461             {
00462                // do nothing - wait for final response
00463             }
00464             // 200/Inv crossing CANCEL case
00465             else if (code / 100 == 2)
00466             {
00467                Dialog dialog(mDum, msg, *this);
00468 
00469                SharedPtr<SipMessage> ack(new SipMessage);
00470                dialog.makeRequest(*ack, ACK);
00471                ack->header(h_CSeq).sequence() = msg.header(h_CSeq).sequence();
00472                dialog.send(ack);
00473                   
00474                SharedPtr<SipMessage> bye(new SipMessage);
00475                dialog.makeRequest(*bye, BYE);
00476                dialog.send(bye);                  
00477 
00478                // Note:  Destruction of this dialog object will cause DialogSet::possiblyDie to be called thus invoking mDum.destroy
00479             }
00480             else
00481             {
00482                mState = Destroying;
00483                mDum.destroy(this);
00484             }
00485          }
00486       }
00487       else // is a request
00488       {
00489          SharedPtr<SipMessage> response(new SipMessage);         
00490          mDum.makeResponse(*response, msg, 481);
00491          mDum.send(response);
00492       }
00493       return;
00494    }
00495 
00496    if (handledByAuthOrRedirect(msg))
00497    {
00498       return;
00499    }
00500 
00501    Dialog* dialog = 0;
00502    if (!(msg.isResponse() && msg.header(h_StatusLine).statusCode() == 100))  // Don't look for dialog if msg is a 100 response
00503    {
00504       DialogMap::iterator i = mDialogs.find(DialogId(msg));
00505       if (i != mDialogs.end())
00506       {
00507          dialog = i->second;
00508       }
00509    }
00510    if (dialog)
00511    {
00512       if(dialog->isDestroying())
00513       {
00514          if( msg.isRequest() )
00515          {
00516             StackLog (<< "Matching dialog is destroying, sending 481 " << endl << msg);
00517             SharedPtr<SipMessage> response(new SipMessage);
00518             mDum.makeResponse(*response, msg, 481);
00519             mDum.send(response);
00520          }
00521          else
00522          {
00523           StackLog (<< "Matching dialog is destroying, dropping response message " << endl << msg);
00524          }
00525          return;
00526       }
00527       else
00528       {
00529          DebugLog (<< "Found matching dialog " << *dialog << " for " << endl << endl << msg);
00530       }
00531    }
00532    else
00533    {
00534       StackLog (<< "No matching dialog for " << endl << endl << msg);
00535    }
00536    
00537    if (msg.isRequest())
00538    {
00539       const SipMessage& request = msg;
00540       switch (request.header(h_CSeq).method())
00541       {
00542          case INVITE:
00543          case CANCEL:  //cancel needs work
00544          case SUBSCRIBE:
00545             break; //dialog creating/handled by dialog
00546 
00547          case BYE:
00548          case INFO:
00549          case ACK:
00550          case UPDATE:
00551             if(!dialog)
00552             {
00553                SharedPtr<SipMessage> response(new SipMessage);         
00554                mDum.makeResponse(*response, msg, 481);
00555                mDum.send(response);
00556                return;
00557             }
00558             break;
00559             
00560          case REFER:
00561             if (request.header(h_To).exists(p_tag) || findDialog(request))
00562             {
00563                DebugLog(<< "in dialog refer request");
00564                break; // in dialog
00565             }
00566             else if((request.exists(h_ReferSub) && 
00567                      request.header(h_ReferSub).isWellFormed() &&
00568                      request.header(h_ReferSub).value()=="false") ||
00569                      (request.exists(h_Requires) &&
00570                      request.header(h_Requires).find(Token("norefersub"))))// out of dialog & noReferSub=true
00571             {
00572                DebugLog(<< "out of dialog refer request with norefersub");
00573                assert(mServerOutOfDialogRequest == 0);
00574                mServerOutOfDialogRequest = makeServerOutOfDialog(request);
00575                mServerOutOfDialogRequest->dispatch(request);
00576                return;
00577             }
00578             else
00579             {
00580                DebugLog(<< "out of dialog refer request with refer sub");
00581                break; // dialog creating
00582             }
00583             break;            
00584          case NOTIFY:
00585 
00586             // !jf! there shouldn't be a dialogset for ServerOutOfDialogReq
00587             if (request.header(h_To).exists(p_tag) || findDialog(request))
00588             {
00589                break; //dialog creating/handled by dialog
00590             }
00591             else // no to tag - unsolicited notify
00592             {
00593                // unsolicited - not allowed but commonly implemented
00594                // by large companies with a bridge as their logo
00595                assert(mServerOutOfDialogRequest == 0);
00596                mServerOutOfDialogRequest = makeServerOutOfDialog(request);
00597                mServerOutOfDialogRequest->dispatch(request);
00598                return;
00599             }
00600             break;
00601 
00602          case PUBLISH:
00603             assert(false); // handled in DialogUsageManager
00604             return;
00605             
00606          case REGISTER:
00607             // !jf! move this to DialogUsageManager
00608             if (mServerRegistration == 0)
00609             {
00610                mServerRegistration = makeServerRegistration(request);
00611             }
00612             mServerRegistration->dispatch(request);
00613             return;
00614 
00615          case MESSAGE:
00616             // !jf! move this to DialogUsageManager
00617             if(!dialog)
00618             {
00619                mServerPagerMessage = makeServerPagerMessage(request);
00620                mServerPagerMessage->dispatch(request);
00621                return;
00622             }
00623             break;
00624 
00625          default:
00626             // !jf! move this to DialogUsageManager
00627             DebugLog ( << "In DialogSet::dispatch, default(ServerOutOfDialogRequest), msg: " << msg );
00628             // only can be one ServerOutOfDialogReq at a time
00629             assert(mServerOutOfDialogRequest == 0);
00630             mServerOutOfDialogRequest = makeServerOutOfDialog(request);
00631             mServerOutOfDialogRequest->dispatch(request);
00632             return;
00633       }
00634    }
00635    else // the message is a response
00636    {
00637       const SipMessage& response = msg;
00638 
00639       int code = msg.header(h_StatusLine).statusCode();
00640       if (code == 423
00641           && msg.header(h_CSeq).method() == SUBSCRIBE
00642           && msg.exists(h_MinExpires)
00643           && getCreator()
00644           && msg.header(h_CSeq) == getCreator()->getLastRequest()->header(h_CSeq))
00645       {
00646          getCreator()->getLastRequest()->header(h_CSeq).sequence()++;
00647          getCreator()->getLastRequest()->header(h_Expires).value() = msg.header(h_MinExpires).value();
00648          DebugLog( << "Re sending inital(dialog forming) SUBSCRIBE due to 423, MinExpires is: " << msg.header(h_MinExpires).value());
00649          mDum.send(getCreator()->getLastRequest());
00650          return;
00651       }
00652       
00653       // We should only do DialogState processing if this is a response to our initial request
00654       if(getCreator() &&
00655          msg.header(h_CSeq) == getCreator()->getLastRequest()->header(h_CSeq))
00656       {
00657          switch(mState)
00658          {
00659             case Initial:
00660                if (code < 200)
00661                {
00662                   mState = ReceivedProvisional;
00663                }
00664                else if(code < 300)
00665                {
00666                   mState = Established;
00667                }
00668                else
00669                {
00670                   mState = Established;
00671                   if (!mDialogs.empty())
00672                   {
00673                      dispatchToAllDialogs(msg);
00674                      return;
00675                   }
00676                }
00677                break;
00678             case ReceivedProvisional:
00679                if (code < 200)
00680                {
00681                   // fall through
00682                }
00683                else if (code < 300)
00684                {
00685                   mState = Established;
00686                   for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
00687                   {
00688                      if (it->second != dialog) // this is dialog that accepted
00689                      {
00690                         it->second->onForkAccepted();
00691                      }
00692                   }
00693                }
00694                else // failure response
00695                {
00696                   mState = Established;
00697                   if (!mDialogs.empty())
00698                   {
00699                      dispatchToAllDialogs(msg);
00700                      return;
00701                   }
00702                }
00703                break;
00704             default:
00705                // !jf!
00706                break;            
00707          }
00708       }
00709 
00710       if (response.header(h_StatusLine).statusCode() == 100)
00711       {
00712          if (mDum.mDialogSetHandler)
00713          {
00714             mDum.mDialogSetHandler->onTrying(mAppDialogSet->getHandle(), msg);
00715          }
00716          return;
00717       }
00718       
00719       switch (response.header(h_CSeq).method())
00720       {
00721          case INVITE:
00722          case SUBSCRIBE:
00723          case BYE:
00724          case ACK:
00725          case CANCEL:
00726             break; 
00727 
00728          case PUBLISH:
00729             if (mClientPublication == 0)
00730             {
00731                mClientPublication = makeClientPublication(response);
00732             }
00733             mClientPublication->dispatch(response);
00734             return;
00735 
00736          case REGISTER:
00737             if (mClientRegistration == 0)
00738             {
00739                mClientRegistration = makeClientRegistration(response);
00740             }
00741             mClientRegistration->dispatch(response);
00742             return;
00743 
00744          case MESSAGE:
00745              if (dialog)
00746             {
00747                 break;
00748             }
00749             else if (mClientPagerMessage)
00750             {
00751                mClientPagerMessage->dispatch(response);
00752             }
00753             return;            
00754 
00755          case INFO:   
00756          case UPDATE:
00757             if (dialog)
00758             {
00759                break;
00760             }
00761             else // not allowed
00762             {
00763                return;
00764             }
00765          case REFER:
00766             if (dynamic_cast<SubscriptionCreator*>(getCreator()))
00767             {
00768                break;
00769             }
00770          case NOTIFY:
00771             if (dialog)
00772             {
00773                break;
00774             }
00775             
00776          default:
00777          {
00778             ClientOutOfDialogReq* req = findMatchingClientOutOfDialogReq(response);
00779             if (req == 0)
00780             {
00781                req = makeClientOutOfDialogReq(response);
00782                mClientOutOfDialogRequests.push_back(req);
00783             }
00784             req->dispatch(response);
00785             return;
00786          }
00787       }
00788    }
00789 
00790    if (dialog == 0)
00791    {
00792       if (msg.isRequest() && msg.header(h_RequestLine).method() == CANCEL)
00793       {
00794          dispatchToAllDialogs(msg);
00795          return;
00796       }
00797 
00798       if (msg.isResponse())
00799       {
00800          if( mCreator )
00801          {
00802             SharedPtr<SipMessage> lastRequest(mCreator->getLastRequest());
00803             if( 0 != lastRequest.get() && !(lastRequest->header(h_CSeq) == msg.header(h_CSeq)))
00804             {
00805                InfoLog(<< "Cannot create a dialog, cseq does not match initial dialog request (illegal mid-dialog fork? see 3261 14.1).");
00806                return;
00807             }
00808          }
00809          else
00810          {
00811             ErrLog(<< "Can’t create a dialog, on a UAS response.");
00812             return;
00813          }
00814 
00815          int code = msg.header(h_StatusLine).statusCode();
00816 
00817          if (code > 100 && code < 200 && 
00818              (!msg.exists(h_Contacts) ||
00819               !msg.exists(h_To) || !msg.header(h_To).exists(p_tag)))
00820          {
00821             InfoLog ( << "Cannot create a dialog, no Contact or To tag in 1xx." );
00822             if (mDum.mDialogSetHandler)
00823             {
00824                if (mDum.mDialogEventStateManager)
00825                {
00826                   mDum.mDialogEventStateManager->onProceedingUac(*this, msg);
00827                }
00828                mDum.mDialogSetHandler->onNonDialogCreatingProvisional(mAppDialogSet->getHandle(), msg);
00829             }
00830             return;         
00831          }
00832          // If failure response and no dialogs, create a dialog, otherwise
00833          else if (code >= 300 && !mDialogs.empty())
00834          {
00835             dispatchToAllDialogs(msg);
00836             return;
00837          }
00838       }
00839 
00840       DebugLog ( << "mState == " << mState << " Creating a new Dialog from msg: " << std::endl << std::endl <<msg);
00841       try
00842       {
00843          // !jf! This could throw due to bad header in msg, should we catch and rethrow
00844          dialog = new Dialog(mDum, msg, *this);
00845       }
00846       catch(BaseException& e)
00847       {
00848          InfoLog( << "Unable to create dialog: " << e.getMessage());
00849          if (msg.isResponse())
00850          {
00851             //don't delete on provisional responses, as FWD will eventually send a
00852             //valid 200
00853             if(mDialogs.empty() && 
00854                msg.header(h_StatusLine).statusCode() >= 200)
00855             {
00856                // really we should wait around 32s before deleting this
00857                if (mDum.mDialogEventStateManager)
00858                {
00859                   mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Error);
00860                }
00861                
00862                mState = Destroying;
00863                mDum.destroy(this);
00864             }
00865          }
00866          else
00867          {
00868             // !jf! derek thinks we should destroy only on invalid CANCEL or
00869             // BYE, hmmphh. see draft-sparks-sipping-dialogusage-01.txt
00870             SharedPtr<SipMessage> response(new SipMessage);
00871             mDum.makeResponse(*response, msg, 400);
00872             mDum.send(response);
00873             if(mDialogs.empty())
00874             {
00875                if (mDum.mDialogEventStateManager)
00876                {
00877                   mDum.mDialogEventStateManager->onTerminated(*this, msg, InviteSessionHandler::Error);
00878                }
00879                mState = Destroying;
00880                mDum.destroy(this);
00881             }
00882          }
00883          return;
00884       }
00885 
00886       assert(mState != WaitingToEnd);
00887       DebugLog ( << "### Calling CreateAppDialog ###: " << std::endl << std::endl <<msg);
00888       AppDialog* appDialog = mAppDialogSet->createAppDialog(msg);
00889       dialog->mAppDialog = appDialog;
00890       appDialog->mDialog = dialog;
00891       dialog->dispatch(msg);
00892    }
00893    else
00894    {     
00895       dialog->dispatch(msg);
00896    }
00897 }
00898 
00899 
00900 ClientOutOfDialogReq*
00901 DialogSet::findMatchingClientOutOfDialogReq(const SipMessage& msg)
00902 {
00903    for (std::list<ClientOutOfDialogReq*>::iterator i=mClientOutOfDialogRequests.begin();
00904         i != mClientOutOfDialogRequests.end(); ++i)
00905    {
00906       if ((*i)->matches(msg))
00907       {
00908          return *i;
00909       }
00910    }
00911    return 0;
00912 }
00913 
00914 Dialog*
00915 DialogSet::findDialog(const DialogId id)
00916 {
00917    StackLog (<< "findDialog: " << id << " in " << InserterP(mDialogs));
00918 
00919    DialogMap::iterator i = mDialogs.find(id);
00920    if (i == mDialogs.end())
00921    {
00922       return 0;
00923    }
00924    else
00925    {
00926       if(i->second->isDestroying())
00927       {
00928          return 0;
00929       }
00930       else
00931       {
00932          return i->second;
00933       }
00934    }
00935 }
00936 
00937 void
00938 DialogSet::end()
00939 {
00940    switch(mState)
00941    {
00942       case Initial:
00943          mState = WaitingToEnd;
00944          break;
00945       case WaitingToEnd:
00946          break;         
00947       case ReceivedProvisional:
00948       {
00949          assert (mCreator->getLastRequest()->header(h_CSeq).method() == INVITE);
00950          mState = Terminating;
00951          // !jf! this should be made exception safe
00952          SharedPtr<SipMessage> cancel(Helper::makeCancel(*getCreator()->getLastRequest()));
00953          mDum.send(cancel);
00954 
00955          if (mDum.mDialogEventStateManager)
00956          {
00957             mDum.mDialogEventStateManager->onTerminated(*this, *cancel, InviteSessionHandler::LocalCancel);
00958          }
00959 
00960          if (mDialogs.empty())
00961          {
00962             mState = Cancelling;
00963          }
00964          else
00965          {
00966             //need to lag and do last element ouside of look as this DialogSet will be
00967             //deleted if all dialogs are destroyed
00968             for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
00969             {
00970                try
00971                {
00972                   it->second->cancel();
00973                }
00974                catch(UsageUseException& e)
00975                {
00976                   InfoLog (<< "Caught: " << e);
00977                }
00978             }
00979          }
00980       }            
00981       break;         
00982       case Established:
00983       {
00984          for (DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); ++it)
00985          {
00986             try
00987             {
00988                it->second->end();
00989             }
00990             catch(UsageUseException& e)
00991             {
00992                InfoLog (<< "Caught: " << e);
00993             }
00994          }            
00995          mState = Terminating;
00996          break;
00997       }
00998       case Terminating:
00999       case Cancelling:
01000       case Destroying:
01001          DebugLog (<< "DialogSet::end() called on a DialogSet that is already Terminating");
01002          //assert(0);
01003    }
01004 }
01005 
01006 
01007 ClientRegistrationHandle
01008 DialogSet::getClientRegistration()
01009 {
01010    if (mClientRegistration)
01011    {
01012       return mClientRegistration->getHandle();
01013    }
01014    else
01015    {
01016       return ClientRegistrationHandle::NotValid();
01017    }
01018 }
01019 
01020 ServerRegistrationHandle
01021 DialogSet::getServerRegistration()
01022 {
01023    if (mServerRegistration)
01024    {
01025       return mServerRegistration->getHandle();
01026    }
01027    else
01028    {
01029       return ServerRegistrationHandle::NotValid();
01030    }
01031 }
01032 
01033 ClientPublicationHandle
01034 DialogSet::getClientPublication()
01035 {
01036    if (mClientPublication)
01037    {
01038       return mClientPublication->getHandle();
01039    }
01040    else
01041    {
01042       return ClientPublicationHandle::NotValid();
01043    }
01044 }
01045 
01046 ClientRegistration*
01047 DialogSet::makeClientRegistration(const SipMessage& response)
01048 {
01049    BaseCreator* creator = getCreator();
01050    assert(creator);
01051    return new ClientRegistration(mDum, *this, creator->getLastRequest());
01052 }
01053 
01054 ClientPublication*
01055 DialogSet::makeClientPublication(const SipMessage& response)
01056 {
01057    BaseCreator* creator = getCreator();
01058    assert(creator);
01059    return new ClientPublication(mDum, *this, creator->getLastRequest());
01060 }
01061 
01062 ClientOutOfDialogReq*
01063 DialogSet::makeClientOutOfDialogReq(const SipMessage& response)
01064 {
01065    BaseCreator* creator = getCreator();
01066    assert(creator);
01067    return new ClientOutOfDialogReq(mDum, *this, *creator->getLastRequest());
01068 }
01069 
01070 ServerRegistration*
01071 DialogSet::makeServerRegistration(const SipMessage& request)
01072 {
01073    return new ServerRegistration(mDum, *this, request);
01074 }
01075 
01076 ServerOutOfDialogReq*
01077 DialogSet::makeServerOutOfDialog(const SipMessage& request)
01078 {
01079    return new ServerOutOfDialogReq(mDum, *this, request);
01080 }
01081 
01082 ServerPagerMessage*
01083 DialogSet::makeServerPagerMessage(const SipMessage& request)
01084 {
01085    return new ServerPagerMessage(mDum, *this, request);
01086 }
01087 
01088 ServerOutOfDialogReqHandle
01089 DialogSet::getServerOutOfDialog()
01090 {
01091    if (mServerOutOfDialogRequest)
01092    {
01093       return mServerOutOfDialogRequest->getHandle();
01094    }
01095    else
01096    {
01097       return ServerOutOfDialogReqHandle::NotValid();
01098    }
01099 }
01100 
01101 void DialogSet::dispatchToAllDialogs(const SipMessage& msg)
01102 {
01103    if (!mDialogs.empty())
01104    {
01105       for(DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
01106       {
01107          it->second->dispatch(msg);         
01108       }
01109    }
01110 }
01111 
01112 SharedPtr<UserProfile>
01113 DialogSet::getUserProfile() const
01114 {
01115    if(mUserProfile.get())
01116    {
01117       return mUserProfile;
01118    }
01119    else
01120    {
01121       // If no UserProfile set then use UserProfile of the MasterProfile
01122       return mDum.getMasterUserProfile();
01123    }
01124 }
01125 
01126 void
01127 DialogSet::setUserProfile(SharedPtr<UserProfile> userProfile)
01128 {
01129    assert(userProfile.get());
01130    mUserProfile = userProfile;
01131 }
01132 
01133 void 
01134 DialogSet::flowTerminated(const Tuple& flow)
01135 {
01136    // The flow has failed - clear the flow key/tuple in the UserProfile
01137    mUserProfile->clearClientOutboundFlowTuple();
01138 
01139    // If this profile is configured for client outbound and the connectionTerminated
01140    // matches the connection stored in the profile, then notify the client registration
01141    // and all dialogs in this dialogset that the flow has terminated
01142    // Check other usage types that we send requests on
01143    if(mClientRegistration)
01144    {
01145       mClientRegistration->flowTerminated();
01146    }
01147 
01148    for(DialogMap::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
01149    {
01150       it->second->flowTerminated();
01151    }
01152 }
01153 
01154 EncodeStream& 
01155 resip::operator<<(EncodeStream& strm, const DialogSet& ds)
01156 {
01157    // Used in Inserter, so keeping brief (ie. Id is already logged by Inserter)
01158    strm << "state=" << ds.mState;
01159    return strm;
01160 }
01161 
01162 
01163 /* ====================================================================
01164  * The Vovida Software License, Version 1.0
01165  *
01166  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
01167  *
01168  * Redistribution and use in source and binary forms, with or without
01169  * modification, are permitted provided that the following conditions
01170  * are met:
01171  *
01172  * 1. Redistributions of source code must retain the above copyright
01173  *    notice, this list of conditions and the following disclaimer.
01174  *
01175  * 2. Redistributions in binary form must reproduce the above copyright
01176  *    notice, this list of conditions and the following disclaimer in
01177  *    the documentation and/or other materials provided with the
01178  *    distribution.
01179  *
01180  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
01181  *    and "Vovida Open Communication Application Library (VOCAL)" must
01182  *    not be used to endorse or promote products derived from this
01183  *    software without prior written permission. For written
01184 1 *    permission, please contact vocal@vovida.org.
01185  *
01186  * 4. Products derived from this software may not be called "VOCAL", nor
01187  *    may "VOCAL" appear in their name, without prior written
01188  *    permission of Vovida Networks, Inc.
01189  *
01190  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
01191  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
01192  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
01193  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
01194  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
01195  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
01196  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
01197  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
01198  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
01199  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
01200  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
01201  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
01202  * DAMAGE.
01203  *
01204  * ====================================================================
01205  *
01206  * This software consists of voluntary contributions made by Vovida
01207  * Networks, Inc. and many individuals on behalf of Vovida Networks,
01208  * Inc.  For more information on Vovida Networks, Inc., please see
01209  * <http://www.vovida.org/>.
01210  *
01211  */