reSIProcate/DialogUsageManager  9680
DialogEventStateManager.cxx
Go to the documentation of this file.
00001 #include "resip/dum/DialogEventStateManager.hxx"
00002 #include "rutil/Random.hxx"
00003 #include "rutil/Logger.hxx"
00004 
00005 namespace resip
00006 {
00007 
00008 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
00009 
00010 DialogEventStateManager::DialogEventStateManager()
00011    : mDialogEventHandler(0)
00012 {
00013 }
00014 
00015 DialogEventStateManager::~DialogEventStateManager()
00016 {
00017 }
00018 
00019 // we've received an INVITE
00020 void
00021 DialogEventStateManager::onTryingUas(Dialog& dialog, const SipMessage& invite)
00022 {
00023    DialogEventInfo* eventInfo = new DialogEventInfo();
00024    eventInfo->mDialogEventId = Random::getVersion4UuidUrn(); // !jjg! is this right?
00025    eventInfo->mDialogId = dialog.getId();
00026    eventInfo->mDirection = DialogEventInfo::Recipient;
00027    eventInfo->mCreationTimeSeconds = Timer::getTimeSecs();
00028    eventInfo->mInviteSession = InviteSessionHandle::NotValid();
00029    eventInfo->mRemoteOfferAnswer = (invite.getContents() != NULL ? std::auto_ptr<Contents>(invite.getContents()->clone()) : std::auto_ptr<Contents>());
00030    eventInfo->mLocalIdentity = dialog.getLocalNameAddr();
00031    eventInfo->mLocalTarget = dialog.getLocalContact().uri();  // !slg! TODO - fix me - the Dialog stored local contact has an empty hostname so that the stack will fill it in
00032    eventInfo->mRemoteIdentity = dialog.getRemoteNameAddr();
00033    eventInfo->mRemoteTarget = std::auto_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
00034    eventInfo->mRouteSet = dialog.getRouteSet();
00035    eventInfo->mState = DialogEventInfo::Trying;
00036 
00037    if (invite.exists(h_Replaces) &&
00038          invite.header(h_Replaces).isWellFormed())
00039    {
00040       Data replacesToTag = invite.header(h_Replaces).exists(p_toTag) ? invite.header(h_Replaces).param(p_toTag) : Data::Empty;
00041       Data replacesFromTag = invite.header(h_Replaces).exists(p_fromTag) ? invite.header(h_Replaces).param(p_fromTag) : Data::Empty;
00042 
00043       eventInfo->mReplacesId = std::auto_ptr<DialogId>(new DialogId(invite.header(h_Replaces).value(), 
00044          replacesToTag,
00045          replacesFromTag));
00046 
00047       std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(*(eventInfo->mReplacesId));
00048       if (it != mDialogIdToEventInfo.end())
00049       {
00050          it->second->mReplaced = true;
00051       }
00052    }
00053    if (invite.exists(h_ReferredBy) && 
00054          invite.header(h_ReferredBy).isWellFormed())
00055    {
00056       eventInfo->mReferredBy = std::auto_ptr<NameAddr>(new NameAddr(invite.header(h_ReferredBy)));
00057    }
00058 
00059    mDialogIdToEventInfo[dialog.getId()] = eventInfo;
00060 
00061    TryingDialogEvent evt(*eventInfo, invite);
00062    mDialogEventHandler->onTrying(evt);
00063 }
00064 
00065 // we've sent an INVITE
00066 void
00067 DialogEventStateManager::onTryingUac(DialogSet& dialogSet, const SipMessage& invite)
00068 {
00069    DialogId fakeId(dialogSet.getId(), Data::Empty);
00070    std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(fakeId);
00071 
00072    DialogEventInfo* eventInfo = 0;
00073 
00074    if (it != mDialogIdToEventInfo.end())
00075    {
00076       // .jjg. we will get in here if our INVITE gets challenged; just swallow the onTrying event in this case
00077       eventInfo = it->second;
00078       if (eventInfo->mState == DialogEventInfo::Trying)
00079       {
00080          return;
00081       }
00082    }
00083    else
00084    {
00085       eventInfo = new DialogEventInfo();
00086    }
00087 
00088    eventInfo->mDialogEventId = Random::getVersion4UuidUrn();
00089    eventInfo->mDialogId = DialogId(dialogSet.getId(), Data::Empty);
00090    eventInfo->mDirection = DialogEventInfo::Initiator;
00091    eventInfo->mCreationTimeSeconds = Timer::getTimeSecs();
00092    eventInfo->mInviteSession = InviteSessionHandle::NotValid();
00093    eventInfo->mLocalIdentity = invite.header(h_From);
00094    // ?bwc? Has something already checked for well-formedness here? 
00095    // Maybe DialogSet? We need to be absolutely certain that this exists and is
00096    // well-formed. Assert for now.
00097    assert(!invite.empty(h_Contacts));
00098    assert(invite.header(h_Contacts).front().isWellFormed());
00099    eventInfo->mLocalTarget = invite.header(h_Contacts).front().uri();
00100    eventInfo->mRemoteIdentity = invite.header(h_To);
00101    eventInfo->mLocalOfferAnswer = (invite.getContents() != NULL ? std::auto_ptr<Contents>(invite.getContents()->clone()) : std::auto_ptr<Contents>());
00102    eventInfo->mState = DialogEventInfo::Trying;
00103 
00104    if (invite.exists(h_ReferredBy) &&
00105          invite.header(h_ReferredBy).isWellFormed())
00106    {
00107       eventInfo->mReferredBy = std::auto_ptr<NameAddr>(new NameAddr(invite.header(h_ReferredBy)));
00108    }
00109 
00110    mDialogIdToEventInfo[eventInfo->mDialogId] = eventInfo;
00111 
00112    TryingDialogEvent evt(*eventInfo, invite);
00113    mDialogEventHandler->onTrying(evt);
00114 }
00115 
00116 // we've received a 1xx response without a remote tag
00117 void
00118 DialogEventStateManager::onProceedingUac(const DialogSet& dialogSet, const SipMessage& response)
00119 {
00120    DialogId fakeId(dialogSet.getId(), Data::Empty);
00121    std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.lower_bound(fakeId);
00122    if (it != mDialogIdToEventInfo.end() &&
00123       it->first.getDialogSetId() == dialogSet.getId())
00124    {
00125       if (it->first.getRemoteTag().empty())
00126       {
00127          // happy day case; no forks yet; e.g INVITE/1xx (no tag)/1xx (no tag)
00128          DialogEventInfo* eventInfo = it->second;
00129          eventInfo->mState = DialogEventInfo::Proceeding;
00130          if (!response.empty(h_Contacts))
00131          {
00132             // ?bwc? Has something already checked for well-formedness here? 
00133             // Maybe DialogSet? Assert for now.
00134             assert(response.header(h_Contacts).front().isWellFormed());
00135             eventInfo->mRemoteTarget = std::auto_ptr<Uri>(new Uri(response.header(h_Contacts).front().uri()));
00136          }
00137          ProceedingDialogEvent evt(*eventInfo);
00138          mDialogEventHandler->onProceeding(evt);
00139       }
00140       else
00141       {
00142          // forking; e.g. INVITE/180 (tag #1)/180 (no tag)
00143 
00144          // .jjg. The remote sender of the 180 (no tag) should either 'put up or shut up' as Byron put it
00145          // so we'll just ignore this...
00146       }
00147    }
00148 }
00149 
00150 // UAC: we've received a 1xx response WITH a remote tag
00151 // UAS: we've sent a 1xx response WITH a local tag
00152 void
00153 DialogEventStateManager::onEarly(const Dialog& dialog, InviteSessionHandle is)
00154 {
00155    DialogEventInfo* eventInfo = findOrCreateDialogInfo(dialog);
00156 
00157    if (eventInfo)
00158    {
00159       eventInfo->mState = DialogEventInfo::Early;
00160       eventInfo->mRouteSet = dialog.getRouteSet();
00161       eventInfo->mInviteSession = is;
00162 
00163       // local or remote target might change due to an UPDATE or re-INVITE
00164       eventInfo->mLocalTarget = dialog.getLocalContact().uri();   // !slg! TODO - fix me - the Dialog stored local contact has an empty hostname so that the stack will fill it in
00165       eventInfo->mRemoteTarget = std::auto_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
00166 
00167       EarlyDialogEvent evt(*eventInfo);
00168       mDialogEventHandler->onEarly(evt);
00169    }
00170 }
00171 
00172 void
00173 DialogEventStateManager::onConfirmed(const Dialog& dialog, InviteSessionHandle is)
00174 {
00175    DialogEventInfo* eventInfo = findOrCreateDialogInfo(dialog);
00176 
00177    if (eventInfo)
00178    {
00179       eventInfo->mInviteSession = is;
00180       eventInfo->mRouteSet = dialog.getRouteSet(); // won't change due to re-INVITEs, but is
00181                                                    // needed for the Trying --> Confirmed transition
00182       eventInfo->mState = DialogEventInfo::Confirmed;
00183 
00184       // local or remote target might change due to an UPDATE or re-INVITE
00185       eventInfo->mLocalTarget = dialog.getLocalContact().uri();   // !slg! TODO - fix me - the Dialog stored local contact has an empty hostname so that the stack will fill it in
00186       eventInfo->mRemoteTarget = std::auto_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
00187 
00188       // for the dialog that got the 200 OK
00189       SharedPtr<ConfirmedDialogEvent> confirmedEvt(new ConfirmedDialogEvent(*eventInfo));
00190       
00191       //mDialogEventHandler->onConfirmed(confirmedEvt);
00192       MultipleEventDialogEvent::EventVector events;
00193 
00194       // kill off any other dialogs in this dialog set, since certain proxy/registrars (like SER and sipX)
00195       // won't bother giving us updates on their status anyways!
00196       const DialogSetId& dialogSetId = dialog.getId().getDialogSetId();
00197       DialogId fakeId(dialogSetId, Data::Empty);
00198       std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.lower_bound(fakeId);
00199       while (it != mDialogIdToEventInfo.end() && 
00200             it->first.getDialogSetId() == dialogSetId)
00201       {
00202          DialogEventInfo::State dialogState = it->second->getState();
00203          if (dialogState == DialogEventInfo::Proceeding || dialogState == DialogEventInfo::Early)
00204          {
00205             // .jjg. we're killing a *specific* dialog *after* the successful completion of the initial INVITE transaction;
00206             // so just elminate this dialog, not the entire dialogset
00207             SharedPtr<TerminatedDialogEvent> evt(onDialogTerminatedImpl(it->second, InviteSessionHandler::RemoteCancel));
00208             events.push_back(evt);
00209             delete it->second;
00210             mDialogIdToEventInfo.erase(it++);
00211          }
00212          else
00213          {
00214             it++;
00215          }
00216       }
00217 
00218       if (events.size() > 0)
00219       {
00220          events.push_back(confirmedEvt);
00221          MultipleEventDialogEvent multipleEvt(events);
00222          mDialogEventHandler->onMultipleEvents(multipleEvt);
00223       }
00224       else
00225       {
00226          mDialogEventHandler->onConfirmed(*confirmedEvt);
00227       }
00228    }
00229 }
00230 
00231 void
00232 DialogEventStateManager::onTerminated(const Dialog& dialog, const SipMessage& msg, InviteSessionHandler::TerminatedReason reason)
00233 {
00234    std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(dialog.getId());
00235    if (it != mDialogIdToEventInfo.end())
00236    {
00237       DialogEventInfo::State dialogState = it->second->getState();
00238       if (dialogState == DialogEventInfo::Confirmed)
00239       {
00240          // .jjg. we're killing a *specific* dialog *after* the successful completion of the initial INVITE transaction;
00241          // so just elminate this dialog, not the entire dialogset
00242          std::auto_ptr<TerminatedDialogEvent> evt(onDialogTerminatedImpl(it->second, reason, getResponseCode(msg), getFrontContact(msg)));
00243          mDialogEventHandler->onTerminated(*evt);
00244          delete it->second;
00245          mDialogIdToEventInfo.erase(it++);
00246       }
00247       else
00248       {
00249          onDialogSetTerminatedImpl(dialog.getId().getDialogSetId(), msg, reason);
00250       }
00251    }
00252    else
00253    {
00254       onDialogSetTerminatedImpl(dialog.getId().getDialogSetId(), msg, reason);
00255    }
00256 }
00257 
00258 void
00259 DialogEventStateManager::onTerminated(const DialogSet& dialogSet, const SipMessage& msg, InviteSessionHandler::TerminatedReason reason)
00260 {
00261    onDialogSetTerminatedImpl(dialogSet.getId(), msg, reason);
00262 }
00263 
00264 void
00265 DialogEventStateManager::onDialogSetTerminatedImpl(const DialogSetId& dialogSetId, const SipMessage& msg, InviteSessionHandler::TerminatedReason reason)
00266 {
00267    DialogEventInfo* eventInfo = NULL;
00268 
00276    //find dialogSet.  All non-confirmed dialogs are destroyed by this event.
00277    //Confirmed dialogs are only destroyed by an exact match.
00278 
00279    DialogId fakeId(dialogSetId, Data::Empty);
00280    std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.lower_bound(fakeId);
00281 
00282    while (it != mDialogIdToEventInfo.end() && 
00283           it->first.getDialogSetId() == dialogSetId)
00284    {
00285       eventInfo = it->second;
00286       std::auto_ptr<TerminatedDialogEvent> evt(onDialogTerminatedImpl(eventInfo, reason, getResponseCode(msg), getFrontContact(msg)));
00287       mDialogEventHandler->onTerminated(*evt);
00288       delete it->second;
00289       mDialogIdToEventInfo.erase(it++);
00290    }
00291 }
00292 
00293 TerminatedDialogEvent*
00294 DialogEventStateManager::onDialogTerminatedImpl(DialogEventInfo* eventInfo, 
00295                                                 InviteSessionHandler::TerminatedReason reason,
00296                                                 int responseCode,
00297                                                 Uri* remoteTarget)
00298 {
00299    eventInfo->mState = DialogEventInfo::Terminated;
00300 
00301    // .jjg. when we get an INVITE w/Replaces, we mark the replaced dialog event info 
00302    // as 'replaced' (see onTryingUas);
00303    // when the replaced dialog is ended, it will be ended normally with a BYE or CANCEL, 
00304    // but since we've marked it as 'replaced' we can update the termination reason
00305    InviteSessionHandler::TerminatedReason actualReason = reason;
00306 
00307    if (eventInfo->mReplaced)
00308    {
00309       actualReason = InviteSessionHandler::Replaced;
00310    }
00311 
00312    if (remoteTarget)
00313    {
00314       eventInfo->mRemoteTarget = std::auto_ptr<Uri>(remoteTarget);
00315    }
00316 
00317    TerminatedDialogEvent* evt = new TerminatedDialogEvent(*eventInfo, actualReason, responseCode);
00318    return evt;
00319    //mDialogEventHandler->onTerminated(evt);
00320 }
00321 
00322 int 
00323 DialogEventStateManager::getResponseCode(const SipMessage& msg)
00324 {
00325    int respCode = 0;
00326    if (msg.isResponse())
00327    {
00328       respCode = msg.header(h_StatusLine).responseCode();
00329    }
00330    return respCode;
00331 }
00332 
00333 Uri* 
00334 DialogEventStateManager::getFrontContact(const SipMessage& msg)
00335 {
00336    Uri* pContact = NULL;
00337    if (msg.isResponse())
00338    {
00339       if (!msg.empty(h_Contacts))
00340       {
00341          // ?bwc? Has something already checked for well-formedness here? 
00342          // Maybe DialogSet? Assert for now.
00343          assert(msg.header(h_Contacts).front().isWellFormed());
00344          pContact = new Uri(msg.header(h_Contacts).front().uri());
00345       }
00346    }
00347    return pContact;
00348 }
00349 
00350 DialogEventStateManager::DialogEventInfos 
00351 DialogEventStateManager::getDialogEventInfo() const
00352 {
00353    DialogEventStateManager::DialogEventInfos infos;
00354    std::map<DialogId, DialogEventInfo*, DialogIdComparator>::const_iterator it = mDialogIdToEventInfo.begin();
00355    for (; it != mDialogIdToEventInfo.end(); it++)
00356    {
00357       infos.push_back(*(it->second));
00358    }
00359    return infos;
00360 }
00361 
00362 DialogEventInfo* 
00363 DialogEventStateManager::findOrCreateDialogInfo(const Dialog& dialog)
00364 {
00365    DialogEventInfo* eventInfo = NULL;
00366 
00375    std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(dialog.getId());
00376 
00377    if (it != mDialogIdToEventInfo.end())
00378    {
00379       return it->second;
00380    }
00381    else
00382    {
00383       // either we have a dialog set id with an empty remote tag, or we have other dialog(s) with different
00384       // remote tag(s)
00385       DialogId fakeId(dialog.getId().getDialogSetId(), Data::Empty);
00386       it = mDialogIdToEventInfo.lower_bound(fakeId);
00387 
00388       if (it != mDialogIdToEventInfo.end() && 
00389             it->first.getDialogSetId() == dialog.getId().getDialogSetId())
00390       {
00391          if (it->first.getRemoteTag().empty())
00392          {
00393             // convert this bad boy into a full on Dialog
00394             eventInfo = it->second;
00395             mDialogIdToEventInfo.erase(it);
00396             eventInfo->mDialogId = dialog.getId();
00397          }
00398          else
00399          {
00400             // clone this fellow member dialog, initializing it with a new id and creation time 
00401             DialogEventInfo* newForkInfo = new DialogEventInfo(*(it->second));
00402             newForkInfo->mDialogEventId = Random::getVersion4UuidUrn();
00403                newForkInfo->mCreationTimeSeconds = Timer::getTimeSecs();
00404             newForkInfo->mDialogId = dialog.getId();
00405             newForkInfo->mRemoteIdentity = dialog.getRemoteNameAddr();
00406             newForkInfo->mRemoteTarget = std::auto_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
00407             newForkInfo->mRouteSet = dialog.getRouteSet();
00408             eventInfo = newForkInfo;
00409          }
00410       }
00411       else
00412       {
00413          // .jjg. this can happen if onTryingUax(..) wasn't called yet for this dialog (set) id
00414          DebugLog(<< "DialogSetId " << fakeId << " was not found! This indicates a bug; onTryingUax() should have been called first!");
00415          return 0;
00416       }
00417    }
00418 
00419    mDialogIdToEventInfo[dialog.getId()] = eventInfo;
00420 
00421    return eventInfo;
00422 }
00423 
00424 } // namespace resip
00425 
00426 /* ====================================================================
00427 * The Vovida Software License, Version 1.0 
00428 * 
00429 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00430 * 
00431 * Redistribution and use in source and binary forms, with or without
00432 * modification, are permitted provided that the following conditions
00433 * are met:
00434 * 
00435 * 1. Redistributions of source code must retain the above copyright
00436 *    notice, this list of conditions and the following disclaimer.
00437 * 
00438 * 2. Redistributions in binary form must reproduce the above copyright
00439 *    notice, this list of conditions and the following disclaimer in
00440 *    the documentation and/or other materials provided with the
00441 *    distribution.
00442 * 
00443 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00444 *    and "Vovida Open Communication Application Library (VOCAL)" must
00445 *    not be used to endorse or promote products derived from this
00446 *    software without prior written permission. For written
00447 *    permission, please contact vocal@vovida.org.
00448 *
00449 * 4. Products derived from this software may not be called "VOCAL", nor
00450 *    may "VOCAL" appear in their name, without prior written
00451 *    permission of Vovida Networks, Inc.
00452 * 
00453 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00454 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00455 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00456 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00457 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00458 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00459 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00460 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00461 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00462 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00463 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00464 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00465 * DAMAGE.
00466 * 
00467 * ====================================================================
00468 * 
00469 * This software consists of voluntary contributions made by Vovida
00470 * Networks, Inc. and many individuals on behalf of Vovida Networks,
00471 * Inc.  For more information on Vovida Networks, Inc., please see
00472 * <http://www.vovida.org/>.
00473 *
00474 */