rtphone/src/libs/resiprocate/resip/dum/DialogUsageManager.cxx

2642 lines
84 KiB
C++

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include "resip/stack/SecurityAttributes.hxx"
#include "resip/stack/ShutdownMessage.hxx"
#include "resip/stack/SipFrag.hxx"
#include "resip/stack/SipMessage.hxx"
#include "resip/stack/SipStack.hxx"
#include "resip/stack/Helper.hxx"
#include "resip/stack/TransactionUserMessage.hxx"
#include "resip/stack/ConnectionTerminated.hxx"
#include "resip/stack/KeepAlivePong.hxx"
#include "resip/dum/AppDialog.hxx"
#include "resip/dum/AppDialogSet.hxx"
#include "resip/dum/AppDialogSetFactory.hxx"
#include "resip/dum/BaseUsage.hxx"
#include "resip/dum/ClientAuthManager.hxx"
#include "resip/dum/ClientInviteSession.hxx"
#include "resip/dum/ClientOutOfDialogReq.hxx"
#include "resip/dum/ClientPublication.hxx"
#include "resip/dum/ClientRegistration.hxx"
#include "resip/dum/ClientSubscription.hxx"
#include "resip/dum/DefaultServerReferHandler.hxx"
#include "resip/dum/DestroyUsage.hxx"
#include "resip/dum/Dialog.hxx"
#include "resip/dum/DialogEventStateManager.hxx"
#include "resip/dum/DialogEventHandler.hxx"
#include "resip/dum/DialogUsageManager.hxx"
#include "resip/dum/ClientPagerMessage.hxx"
#include "resip/dum/DumException.hxx"
#include "resip/dum/DumShutdownHandler.hxx"
#include "resip/dum/DumFeatureMessage.hxx"
#include "resip/dum/ExternalMessageBase.hxx"
#include "resip/dum/ExternalMessageHandler.hxx"
#include "resip/dum/InviteSessionCreator.hxx"
#include "resip/dum/InviteSessionHandler.hxx"
#include "resip/dum/KeepAliveManager.hxx"
#include "resip/dum/KeepAliveTimeout.hxx"
#include "resip/dum/MasterProfile.hxx"
#include "resip/dum/OutOfDialogReqCreator.hxx"
#include "resip/dum/PagerMessageCreator.hxx"
#include "resip/dum/PublicationCreator.hxx"
#include "resip/dum/RedirectManager.hxx"
#include "resip/dum/RegistrationCreator.hxx"
#include "resip/dum/RemoteCertStore.hxx"
#include "resip/dum/RequestValidationHandler.hxx"
#include "resip/dum/ServerAuthManager.hxx"
#include "resip/dum/ServerInviteSession.hxx"
#include "resip/dum/ServerPublication.hxx"
#include "resip/dum/ServerSubscription.hxx"
#include "resip/dum/SubscriptionCreator.hxx"
#include "resip/dum/SubscriptionHandler.hxx"
#include "resip/dum/UserAuthInfo.hxx"
#include "resip/dum/DumFeature.hxx"
#include "resip/dum/IdentityHandler.hxx"
#include "resip/dum/DumDecrypted.hxx"
#include "resip/dum/CertMessage.hxx"
#include "resip/dum/OutgoingEvent.hxx"
#include "resip/dum/DumHelper.hxx"
#include "resip/dum/MergedRequestRemovalCommand.hxx"
#include "resip/dum/InMemorySyncPubDb.hxx"
#include "rutil/ResipAssert.h"
#include "rutil/Inserter.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Random.hxx"
#include "rutil/Lockable.hxx"
#include "rutil/Timer.hxx"
#include "rutil/WinLeakCheck.hxx"
#ifdef USE_SSL
#include "resip/stack/ssl/Security.hxx"
#include "resip/dum/ssl/EncryptionManager.hxx"
#endif
#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
using namespace resip;
using namespace std;
#ifdef RESIP_DUM_THREAD_DEBUG
#define threadCheck() \
do \
{ \
if(mThreadDebugKey) \
{ \
resip_assert(ThreadIf::tlsGetValue(mThreadDebugKey)); \
} \
} while (false)
#else
#define threadCheck() void()
#endif
DialogUsageManager::DialogUsageManager(SipStack& stack, bool createDefaultFeatures) :
TransactionUser(TransactionUser::DoNotRegisterForTransactionTermination,
TransactionUser::RegisterForConnectionTermination,
TransactionUser::RegisterForKeepAlivePongs),
mRedirectManager(new RedirectManager()),
mInviteSessionHandler(0),
mClientRegistrationHandler(0),
mServerRegistrationHandler(0),
mRedirectHandler(0),
mDialogSetHandler(0),
mRequestValidationHandler(0),
mRegistrationPersistenceManager(0),
mPublicationPersistenceManager(0),
mIsDefaultServerReferHandler(true),
mClientPagerMessageHandler(0),
mServerPagerMessageHandler(0),
mDialogEventStateManager(0),
mAppDialogSetFactory(new AppDialogSetFactory()),
mStack(stack),
mDumShutdownHandler(0),
mShutdownState(Running),
mThreadDebugKey(0),
mHiddenThreadDebugKey(0)
{
//TODO -- create default features
mStack.registerTransactionUser(*this);
addServerSubscriptionHandler("refer", new DefaultServerReferHandler());
mFifo.setDescription("DialogUsageManager::mFifo");
mIncomingTarget = new IncomingTarget(*this);
mOutgoingTarget = new OutgoingTarget(*this);
if (createDefaultFeatures)
{
SharedPtr<IdentityHandler> identity = SharedPtr<IdentityHandler>(new IdentityHandler(*this, *mIncomingTarget));
#if defined (USE_SSL)
SharedPtr<EncryptionManager> encryptionIncoming = SharedPtr<EncryptionManager>(new EncryptionManager(*this, *mIncomingTarget));
SharedPtr<EncryptionManager> encryptionOutgoing = SharedPtr<EncryptionManager>(new EncryptionManager(*this, *mOutgoingTarget));
#endif
// default incoming features.
addIncomingFeature(identity);
#if defined (USE_SSL)
addIncomingFeature(encryptionIncoming);
#endif
// default outgoing features.
#if defined (USE_SSL)
addOutgoingFeature(encryptionOutgoing);
#endif
}
}
DialogUsageManager::~DialogUsageManager()
{
mShutdownState = Destroying;
//InfoLog ( << "~DialogUsageManager" );
if(!mDialogSetMap.empty())
{
DebugLog(<< "DialogUsageManager::mDialogSetMap has " << mDialogSetMap.size() << " DialogSets");
DialogSetMap::const_iterator ds = mDialogSetMap.begin();
for(; ds != mDialogSetMap.end(); ++ds)
{
DebugLog(<< "DialgSetId:" << ds->first);
DialogSet::DialogMap::const_iterator d = ds->second->mDialogs.begin();
for(; d != ds->second->mDialogs.end(); ++d)
{
//const Dialog* p = &(d->second);
DebugLog(<<"DialogId:" << d->first << ", " << *d->second);
}
}
}
while(!mDialogSetMap.empty())
{
DialogSet* ds = mDialogSetMap.begin()->second;
delete ds; // Deleting a dialog set removes itself from the map
}
if(mIsDefaultServerReferHandler)
{
delete mServerSubscriptionHandlers["refer"];
}
delete mIncomingTarget;
delete mOutgoingTarget;
// Delete Server Publications
while (!mServerPublications.empty())
{
delete mServerPublications.begin()->second; // Deleting a ServerPublication removes itself from the map
}
// Remove any lingering incoming feature chain memory
for(FeatureChainMap::iterator it = mIncomingFeatureChainMap.begin(); it != mIncomingFeatureChainMap.end(); it++)
{
delete it->second;
}
//InfoLog ( << "~DialogUsageManager done" );
}
const Data&
DialogUsageManager::name() const
{
static Data n("DialogUsageManager");
return n;
}
void
DialogUsageManager::addTransport( TransportType protocol,
int port,
IpVersion version,
const Data& ipInterface,
const Data& sipDomainname, // only used
const Data& privateKeyPassPhrase,
SecurityTypes::SSLType sslType,
unsigned transportFlags)
{
mStack.addTransport(protocol, port, version, StunDisabled, ipInterface,
sipDomainname, privateKeyPassPhrase, sslType,
transportFlags);
}
SipStack&
DialogUsageManager::getSipStack()
{
return mStack;
}
const SipStack&
DialogUsageManager::getSipStack() const
{
return mStack;
}
Security*
DialogUsageManager::getSecurity()
{
return mStack.getSecurity();
}
Data
DialogUsageManager::getHostAddress()
{
return mStack.getHostAddress();
}
void
DialogUsageManager::onAllHandlesDestroyed()
{
if (mDumShutdownHandler)
{
switch (mShutdownState)
{
case ShutdownRequested:
InfoLog (<< "DialogUsageManager::onAllHandlesDestroyed: removing TU");
//assert(mHandleMap.empty());
mShutdownState = RemovingTransactionUser;
mStack.unregisterTransactionUser(*this);
break;
default:
break;
}
}
}
void
DialogUsageManager::shutdown(DumShutdownHandler* h)
{
InfoLog (<< "shutdown: dialogSets=" << mDialogSetMap.size());
mDumShutdownHandler = h;
mShutdownState = ShutdownRequested;
mStack.requestTransactionUserShutdown(*this);
shutdownWhenEmpty();
}
// void
// DialogUsageManager::shutdownIfNoUsages(DumShutdownHandler* h)
// {
// InfoLog (<< "shutdown when no usages");
//
// mDumShutdownHandler = h;
// mShutdownState = ShutdownRequested;
// assert(0);
// }
void
DialogUsageManager::forceShutdown(DumShutdownHandler* h)
{
WarningLog (<< "force shutdown ");
dumpHandles();
mDumShutdownHandler = h;
//HandleManager::shutdown(); // clear out usages
mShutdownState = ShutdownRequested;
DialogUsageManager::onAllHandlesDestroyed();
}
void DialogUsageManager::setAppDialogSetFactory(std::auto_ptr<AppDialogSetFactory> factory)
{
mAppDialogSetFactory = factory;
}
SharedPtr<MasterProfile>&
DialogUsageManager::getMasterProfile()
{
resip_assert(mMasterProfile.get());
return mMasterProfile;
}
SharedPtr<UserProfile>&
DialogUsageManager::getMasterUserProfile()
{
resip_assert(mMasterUserProfile.get());
return mMasterUserProfile;
}
void DialogUsageManager::setMasterProfile(const SharedPtr<MasterProfile>& masterProfile)
{
resip_assert(!mMasterProfile.get());
mMasterProfile = masterProfile;
mMasterUserProfile = masterProfile; // required so that we can return a reference to SharedPtr<UserProfile> in getMasterUserProfile
}
void DialogUsageManager::setKeepAliveManager(std::auto_ptr<KeepAliveManager> manager)
{
mKeepAliveManager = manager;
mKeepAliveManager->setDialogUsageManager(this);
}
void DialogUsageManager::setRedirectManager(std::auto_ptr<RedirectManager> manager)
{
mRedirectManager = manager;
}
void DialogUsageManager::setRedirectHandler(RedirectHandler* handler)
{
mRedirectHandler = handler;
}
RedirectHandler* DialogUsageManager::getRedirectHandler()
{
return mRedirectHandler;
}
void
DialogUsageManager::setClientAuthManager(std::auto_ptr<ClientAuthManager> manager)
{
mClientAuthManager = manager;
}
void
DialogUsageManager::setServerAuthManager(SharedPtr<ServerAuthManager> manager)
{
mIncomingFeatureList.insert(mIncomingFeatureList.begin(), manager);
}
void
DialogUsageManager::setClientRegistrationHandler(ClientRegistrationHandler* handler)
{
resip_assert(!mClientRegistrationHandler);
mClientRegistrationHandler = handler;
}
void
DialogUsageManager::setServerRegistrationHandler(ServerRegistrationHandler* handler)
{
resip_assert(!mServerRegistrationHandler);
mServerRegistrationHandler = handler;
}
void
DialogUsageManager::setDialogSetHandler(DialogSetHandler* handler)
{
mDialogSetHandler = handler;
}
void
DialogUsageManager::setInviteSessionHandler(InviteSessionHandler* handler)
{
resip_assert(!mInviteSessionHandler);
mInviteSessionHandler = handler;
}
void
DialogUsageManager::setRequestValidationHandler(RequestValidationHandler* handler)
{
resip_assert(!mRequestValidationHandler);
mRequestValidationHandler = handler;
}
void
DialogUsageManager::setRegistrationPersistenceManager(RegistrationPersistenceManager* manager)
{
resip_assert(!mRegistrationPersistenceManager);
mRegistrationPersistenceManager = manager;
}
void
DialogUsageManager::setPublicationPersistenceManager(PublicationPersistenceManager* manager)
{
resip_assert(!mPublicationPersistenceManager);
mPublicationPersistenceManager = manager;
}
void
DialogUsageManager::addTimer(DumTimeout::Type type, unsigned long duration,
BaseUsageHandle target, unsigned int cseq, unsigned int rseq)
{
DumTimeout t(type, duration, target, cseq, rseq);
mStack.post(t, duration, this);
}
void
DialogUsageManager::addTimerMs(DumTimeout::Type type, unsigned long duration,
BaseUsageHandle target, unsigned int cseq, unsigned int rseq,
const Data &transactionId /*= Data::Empty*/)
{
DumTimeout t(type, duration, target, cseq, rseq, transactionId);
mStack.postMS(t, duration, this);
}
void
DialogUsageManager::addClientSubscriptionHandler(const Data& eventType, ClientSubscriptionHandler* handler)
{
resip_assert(handler);
resip_assert(mClientSubscriptionHandlers.count(eventType) == 0);
mClientSubscriptionHandlers[eventType] = handler;
}
void
DialogUsageManager::addServerSubscriptionHandler(const Data& eventType, ServerSubscriptionHandler* handler)
{
resip_assert(handler);
//default do-nothing server side refer handler can be replaced
if (eventType == "refer" && mServerSubscriptionHandlers.count(eventType))
{
delete mServerSubscriptionHandlers[eventType];
mIsDefaultServerReferHandler = false;
//mServerSubscriptionHandlers.erase(eventType);
}
mServerSubscriptionHandlers[eventType] = handler;
}
void
DialogUsageManager::addClientPublicationHandler(const Data& eventType, ClientPublicationHandler* handler)
{
resip_assert(handler);
resip_assert(mClientPublicationHandlers.count(eventType) == 0);
mClientPublicationHandlers[eventType] = handler;
}
void
DialogUsageManager::addServerPublicationHandler(const Data& eventType, ServerPublicationHandler* handler)
{
resip_assert(handler);
resip_assert(mServerPublicationHandlers.count(eventType) == 0);
mServerPublicationHandlers[eventType] = handler;
}
void
DialogUsageManager::addOutOfDialogHandler(MethodTypes type, OutOfDialogHandler* handler)
{
resip_assert(handler);
resip_assert(mOutOfDialogHandlers.count(type) == 0);
mOutOfDialogHandlers[type] = handler;
}
void
DialogUsageManager::setClientPagerMessageHandler(ClientPagerMessageHandler* handler)
{
mClientPagerMessageHandler = handler;
}
void
DialogUsageManager::setServerPagerMessageHandler(ServerPagerMessageHandler* handler)
{
mServerPagerMessageHandler = handler;
}
void
DialogUsageManager::addExternalMessageHandler(ExternalMessageHandler* handler)
{
std::vector<ExternalMessageHandler*>::iterator found = std::find(mExternalMessageHandlers.begin(), mExternalMessageHandlers.end(), handler);
if (found == mExternalMessageHandlers.end())
{
mExternalMessageHandlers.push_back(handler);
}
}
void
DialogUsageManager::removeExternalMessageHandler(ExternalMessageHandler* handler)
{
std::vector<ExternalMessageHandler*>::iterator found = std::find(mExternalMessageHandlers.begin(), mExternalMessageHandlers.end(), handler);
if (found != mExternalMessageHandlers.end())
{
mExternalMessageHandlers.erase(found);
}
}
void
DialogUsageManager::clearExternalMessageHandler()
{
std::vector<ExternalMessageHandler*> empty;
empty.swap(mExternalMessageHandlers);
}
DialogSet*
DialogUsageManager::makeUacDialogSet(BaseCreator* creator, AppDialogSet* appDs)
{
threadCheck();
if (mDumShutdownHandler)
{
throw DumException("Cannot create new sessions when DUM is shutting down.", __FILE__, __LINE__);
}
if (appDs == 0)
{
appDs = new AppDialogSet(*this);
}
DialogSet* ds = new DialogSet(creator, *this);
appDs->mDialogSet = ds;
ds->mAppDialogSet = appDs;
StackLog ( << "************* Adding DialogSet ***************: " << ds->getId());
//StackLog ( << "Before: " << InserterP(mDialogSetMap) );
mDialogSetMap[ds->getId()] = ds;
StackLog ( << "DialogSetMap: " << InserterP(mDialogSetMap) );
return ds;
}
SharedPtr<SipMessage>
DialogUsageManager::makeNewSession(BaseCreator* creator, AppDialogSet* appDs)
{
makeUacDialogSet(creator, appDs);
return creator->getLastRequest();
}
void
DialogUsageManager::makeResponse(SipMessage& response,
const SipMessage& request,
int responseCode,
const Data& reason) const
{
resip_assert(request.isRequest());
Helper::makeResponse(response, request, responseCode, reason);
}
void
DialogUsageManager::sendResponse(const SipMessage& response)
{
resip_assert(response.isResponse());
mStack.send(response, this);
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, const Contents* initialOffer, AppDialogSet* appDs)
{
return makeInviteSession(target, userProfile, initialOffer, None, 0, appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target, const Contents* initialOffer, AppDialogSet* appDs)
{
return makeInviteSession(target, getMasterUserProfile(), initialOffer, None, 0, appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target,
const SharedPtr<UserProfile>& userProfile,
const Contents* initialOffer,
EncryptionLevel level,
const Contents* alternative,
AppDialogSet* appDs)
{
SharedPtr<SipMessage> inv = makeNewSession(new InviteSessionCreator(*this, target, userProfile, initialOffer, level, alternative), appDs);
DumHelper::setOutgoingEncryptionLevel(*inv, level);
return inv;
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target,
const Contents* initialOffer,
EncryptionLevel level,
const Contents* alternative,
AppDialogSet* appDs)
{
return makeInviteSession(target, getMasterUserProfile(), initialOffer, level, alternative, appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target,
const DialogSetId& dialogSetId,
const SharedPtr<UserProfile>& userProfile,
const Contents* initialOffer,
EncryptionLevel level,
const Contents* alternative,
AppDialogSet* appDs)
{
assert(mDialogSetMap.find(dialogSetId) == mDialogSetMap.end());
BaseCreator* baseCreator(new InviteSessionCreator(*this, target, userProfile, initialOffer, level, alternative));
baseCreator->getLastRequest()->header(h_CallID).value() = dialogSetId.getCallId();
baseCreator->getLastRequest()->header(h_From).param(p_tag) = dialogSetId.getLocalTag();
SharedPtr<SipMessage> inv = makeNewSession(baseCreator, appDs);
DumHelper::setOutgoingEncryptionLevel(*inv, level);
return inv;
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target,
InviteSessionHandle sessionToReplace,
const SharedPtr<UserProfile>& userProfile,
const Contents* initialOffer,
AppDialogSet* ads)
{
SharedPtr<SipMessage> inv = makeInviteSession(target, userProfile, initialOffer, ads);
// add replaces header
resip_assert(sessionToReplace.isValid());
if(sessionToReplace.isValid())
{
CallId replaces;
const DialogId& id = sessionToReplace->getDialogId();
replaces.value() = id.getCallId();
replaces.param(p_toTag) = id.getRemoteTag();
replaces.param(p_fromTag) = id.getLocalTag();
inv->header(h_Replaces) = replaces;
}
return inv;
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target,
InviteSessionHandle sessionToReplace,
const SharedPtr<UserProfile>& userProfile,
const Contents* initialOffer,
EncryptionLevel level,
const Contents* alternative,
AppDialogSet* ads)
{
SharedPtr<SipMessage> inv = makeInviteSession(target, userProfile, initialOffer, level, alternative, ads);
// add replaces header
resip_assert(sessionToReplace.isValid());
if(sessionToReplace.isValid())
{
CallId replaces;
const DialogId& id = sessionToReplace->getDialogId();
replaces.value() = id.getCallId();
replaces.param(p_toTag) = id.getRemoteTag();
replaces.param(p_fromTag) = id.getLocalTag();
inv->header(h_Replaces) = replaces;
}
return inv;
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSession(const NameAddr& target,
InviteSessionHandle sessionToReplace,
const Contents* initialOffer,
EncryptionLevel level,
const Contents* alternative ,
AppDialogSet* ads)
{
SharedPtr<SipMessage> inv = makeInviteSession(target, initialOffer, level, alternative, ads);
// add replaces header
resip_assert(sessionToReplace.isValid());
if(sessionToReplace.isValid())
{
CallId replaces;
const DialogId& id = sessionToReplace->getDialogId();
replaces.value() = id.getCallId();
replaces.param(p_toTag) = id.getRemoteTag();
replaces.param(p_fromTag) = id.getLocalTag();
inv->header(h_Replaces) = replaces;
}
return inv;
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSessionFromRefer(const SipMessage& refer,
ServerSubscriptionHandle serverSub,
const Contents* initialOffer,
AppDialogSet* appDs)
{
return makeInviteSessionFromRefer(refer, serverSub, initialOffer, None, 0, appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSessionFromRefer(const SipMessage& refer,
const SharedPtr<UserProfile>& userProfile,
const Contents* initialOffer,
AppDialogSet* appDs)
{
ServerSubscriptionHandle empty;
return makeInviteSessionFromRefer(refer, userProfile, empty, initialOffer, None, 0, appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSessionFromRefer(const SipMessage& refer,
ServerSubscriptionHandle serverSub,
const Contents* initialOffer,
EncryptionLevel level,
const Contents* alternative,
AppDialogSet* appDs)
{
return makeInviteSessionFromRefer(refer, serverSub.isValid() ? serverSub->mDialog.mDialogSet.getUserProfile() : getMasterUserProfile(), serverSub, initialOffer, level, alternative, appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeInviteSessionFromRefer(const SipMessage& refer,
const SharedPtr<UserProfile>& userProfile,
ServerSubscriptionHandle serverSub,
const Contents* initialOffer,
EncryptionLevel level,
const Contents* alternative,
AppDialogSet* appDs)
{
if (serverSub.isValid())
{
DebugLog(<< "implicit subscription");
//generate and send 100
SipFrag contents;
contents.message().header(h_StatusLine).statusCode() = 100;
contents.message().header(h_StatusLine).reason() = "Trying";
//will be cloned...ServerSub may not have the most efficient API possible
serverSub->setSubscriptionState(Active);
SharedPtr<SipMessage> notify = serverSub->update(&contents);
// mInviteSessionHandler->onReadyToSend(InviteSessionHandle::NotValid(), notify);
serverSub->send(notify);
}
//19.1.5
NameAddr target = refer.header(h_ReferTo);
target.uri().removeEmbedded();
target.uri().remove(p_method);
// !jf! this code assumes you have a UserProfile
SharedPtr<SipMessage> inv = makeNewSession(new InviteSessionCreator(*this,
target,
userProfile,
initialOffer, level, alternative, serverSub), appDs);
DumHelper::setOutgoingEncryptionLevel(*inv, level);
//could pass dummy target, then apply merge rules from 19.1.5...or
//makeNewSession would use rules from 19.1.5
if (refer.exists(h_ReferredBy))
{
inv->header(h_ReferredBy) = refer.header(h_ReferredBy);
}
const Uri& referTo = refer.header(h_ReferTo).uri();
//19.1.5
if (referTo.hasEmbedded() && referTo.embedded().exists(h_Replaces))
{
inv->header(h_Replaces) = referTo.embedded().header(h_Replaces);
}
return inv;
}
SharedPtr<SipMessage>
DialogUsageManager::makeRefer(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, const H_ReferTo::Type& referTo, AppDialogSet* appDs)
{
return makeNewSession(new SubscriptionCreator(*this, target, userProfile, referTo), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeRefer(const NameAddr& target, const H_ReferTo::Type& referTo, AppDialogSet* appDs)
{
return makeNewSession(new SubscriptionCreator(*this, target, getMasterUserProfile(), referTo), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeSubscription(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, const Data& eventType, AppDialogSet* appDs)
{
resip_assert(userProfile.get());
return makeNewSession(new SubscriptionCreator(*this, target, userProfile, eventType, userProfile->getDefaultSubscriptionTime()), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeSubscription(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, const Data& eventType,
UInt32 subscriptionTime, AppDialogSet* appDs)
{
return makeNewSession(new SubscriptionCreator(*this, target, userProfile, eventType, subscriptionTime), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeSubscription(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, const Data& eventType,
UInt32 subscriptionTime, int refreshInterval, AppDialogSet* appDs)
{
return makeNewSession(new SubscriptionCreator(*this, target, userProfile, eventType, subscriptionTime, refreshInterval), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeSubscription(const NameAddr& target, const Data& eventType, AppDialogSet* appDs)
{
return makeNewSession(new SubscriptionCreator(*this, target, getMasterUserProfile(), eventType, getMasterProfile()->getDefaultSubscriptionTime()), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeSubscription(const NameAddr& target, const Data& eventType,
UInt32 subscriptionTime, AppDialogSet* appDs)
{
return makeNewSession(new SubscriptionCreator(*this, target, getMasterUserProfile(), eventType, subscriptionTime), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeSubscription(const NameAddr& target, const Data& eventType,
UInt32 subscriptionTime, int refreshInterval, AppDialogSet* appDs)
{
return makeNewSession(new SubscriptionCreator(*this, target, getMasterUserProfile(), eventType, subscriptionTime, refreshInterval), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeRegistration(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, AppDialogSet* appDs)
{
resip_assert(userProfile.get());
return makeNewSession(new RegistrationCreator(*this, target, userProfile, userProfile->getDefaultRegistrationTime()), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeRegistration(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, UInt32 registrationTime, AppDialogSet* appDs)
{
return makeNewSession(new RegistrationCreator(*this, target, userProfile, registrationTime), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeRegistration(const NameAddr& target, AppDialogSet* appDs)
{
return makeNewSession(new RegistrationCreator(*this, target, getMasterUserProfile(), getMasterProfile()->getDefaultRegistrationTime()), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeRegistration(const NameAddr& target, UInt32 registrationTime, AppDialogSet* appDs)
{
return makeNewSession(new RegistrationCreator(*this, target, getMasterUserProfile(), registrationTime), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makePublication(const NameAddr& targetDocument,
const SharedPtr<UserProfile>& userProfile,
const Contents& body,
const Data& eventType,
UInt32 expiresSeconds,
AppDialogSet* appDs)
{
return makeNewSession(new PublicationCreator(*this, targetDocument, userProfile, body, eventType, expiresSeconds), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makePublication(const NameAddr& targetDocument,
const Contents& body,
const Data& eventType,
UInt32 expiresSeconds,
AppDialogSet* appDs)
{
return makeNewSession(new PublicationCreator(*this, targetDocument, getMasterUserProfile(), body, eventType, expiresSeconds), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeOutOfDialogRequest(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, const MethodTypes meth, AppDialogSet* appDs)
{
return makeNewSession(new OutOfDialogReqCreator(*this, meth, target, userProfile), appDs);
}
SharedPtr<SipMessage>
DialogUsageManager::makeOutOfDialogRequest(const NameAddr& target, const MethodTypes meth, AppDialogSet* appDs)
{
return makeNewSession(new OutOfDialogReqCreator(*this, meth, target, getMasterUserProfile()), appDs);
}
ClientPagerMessageHandle
DialogUsageManager::makePagerMessage(const NameAddr& target, const SharedPtr<UserProfile>& userProfile, AppDialogSet* appDs)
{
if (!mClientPagerMessageHandler)
{
throw DumException("Cannot send MESSAGE messages without a ClientPagerMessageHandler", __FILE__, __LINE__);
}
DialogSet* ds = makeUacDialogSet(new PagerMessageCreator(*this, target, userProfile), appDs);
ClientPagerMessage* cpm = new ClientPagerMessage(*this, *ds);
ds->mClientPagerMessage = cpm;
return cpm->getHandle();
}
ClientPagerMessageHandle
DialogUsageManager::makePagerMessage(const NameAddr& target, AppDialogSet* appDs)
{
return makePagerMessage(target, getMasterUserProfile(), appDs);
}
void
DialogUsageManager::send(SharedPtr<SipMessage> msg)
{
// !slg! There is probably a more efficient way to get the userProfile here (pass it in?)
DialogSet* ds = findDialogSet(DialogSetId(*msg));
UserProfile* userProfile;
if (ds == 0)
{
userProfile = getMasterUserProfile().get();
}
else
{
userProfile = ds->getUserProfile().get();
}
resip_assert(userProfile);
if (!userProfile->isAnonymous() && userProfile->hasUserAgent())
{
msg->header(h_UserAgent).value() = userProfile->getUserAgent();
}
if (userProfile->isAnonymous())
{
msg->remove(h_ReplyTo);
msg->remove(h_UserAgent);
msg->remove(h_Organization);
msg->remove(h_Server);
msg->remove(h_Subject);
msg->remove(h_InReplyTo);
msg->remove(h_CallInfos);
msg->remove(h_Warnings);
}
resip_assert(userProfile);
if (msg->isRequest()
&& userProfile->hasProxyRequires()
&& msg->header(h_RequestLine).method() != ACK
&& msg->header(h_RequestLine).method() != CANCEL)
{
msg->header(h_ProxyRequires) = userProfile->getProxyRequires();
}
// .bwc. This is to avoid leaving extra copies of the decorator in msg,
// when the caller of this function holds onto the reference (and this
// happens quite often in DUM). I would prefer to refactor such that we
// are operating on a copy in this function, but this would require a lot
// of work on the DumFeatureChain stuff (or, require an extra copy on top
// of the one we're doing when we send the message to the stack, which
// would chew up a lot of extra cycles).
msg->clearOutboundDecorators();
// Add outbound decorator from userprofile - note: it is important that this is
// done before the call to mClientAuthManager->addAuthentication, since the ClientAuthManager
// will install outbound decorators and we want these to run after the user provided ones, in
// case a user provided decorator modifes the message body used in auth.
SharedPtr<MessageDecorator> outboundDecorator = userProfile->getOutboundDecorator();
if (outboundDecorator.get())
{
msg->addOutboundDecorator(std::auto_ptr<MessageDecorator>(outboundDecorator->clone()));
}
if (msg->isRequest())
{
// We may not need to call reset() if makeRequest is always used.
if (msg->header(h_RequestLine).method() != CANCEL &&
msg->header(h_RequestLine).method() != ACK &&
msg->exists(h_Vias))
{
msg->header(h_Vias).front().param(p_branch).reset();
}
if (msg->exists(h_Vias))
{
if(userProfile->getRportEnabled())
{
msg->header(h_Vias).front().param(p_rport);
}
else
{
msg->header(h_Vias).front().remove(p_rport);
}
int fixedTransportPort = userProfile->getFixedTransportPort();
if(fixedTransportPort != 0)
{
msg->header(h_Vias).front().sentPort() = fixedTransportPort;
}
const Data& fixedTransportInterface = userProfile->getFixedTransportInterface();
if(!fixedTransportInterface.empty())
{
msg->header(h_Vias).front().sentHost() = fixedTransportInterface;
}
}
if (mClientAuthManager.get() && msg->header(h_RequestLine).method() != ACK)
{
mClientAuthManager->addAuthentication(*msg);
}
if (msg->header(h_RequestLine).method() == INVITE)
{
if (ds != 0)
{
if (mDialogEventStateManager)
{
Dialog* d = ds->findDialog(*msg);
if (d == 0)
{
// If we don't have a dialog yet and we are sending an INVITE, this is a new outbound (UAC) INVITE
mDialogEventStateManager->onTryingUac(*ds, *msg);
}
}
}
}
}
DebugLog (<< "SEND: " << std::endl << std::endl << *msg);
OutgoingEvent* event = new OutgoingEvent(msg);
outgoingProcess(auto_ptr<Message>(event));
}
void
DialogUsageManager::sendCommand(SharedPtr<SipMessage> request)
{
SendCommand* s=new SendCommand(request, *this);
post(s);
}
void DialogUsageManager::outgoingProcess(auto_ptr<Message> message)
{
Data tid = Data::Empty;
{
OutgoingEvent* sipMsg = dynamic_cast<OutgoingEvent*>(message.get());
if (sipMsg)
{
tid = sipMsg->getTransactionId();
}
DumFeatureMessage* featureMsg = dynamic_cast<DumFeatureMessage*>(message.get());
if (featureMsg)
{
InfoLog(<<"Got a DumFeatureMessage" << featureMsg);
tid = featureMsg->getTransactionId();
}
}
if (tid == Data::Empty && mOutgoingMessageInterceptor.get())
{
mOutgoingMessageInterceptor->process(message.get());
return;
}
else if (tid != Data::Empty && !mOutgoingFeatureList.empty())
{
FeatureChainMap::iterator it;
//efficiently find or create FeatureChain, should prob. be a utility template
{
FeatureChainMap::iterator lb = mOutgoingFeatureChainMap.lower_bound(tid);
if (lb != mOutgoingFeatureChainMap.end() && !(mOutgoingFeatureChainMap.key_comp()(tid, lb->first)))
{
it = lb;
}
else
{
it = mOutgoingFeatureChainMap.insert(lb, FeatureChainMap::value_type(tid, new DumFeatureChain(*this, mOutgoingFeatureList, *mOutgoingTarget)));
}
}
DumFeatureChain::ProcessingResult res = it->second->process(message.get());
if (res & DumFeatureChain::ChainDoneBit)
{
delete it->second;
mOutgoingFeatureChainMap.erase(it);
}
if (res & DumFeatureChain::EventTakenBit)
{
message.release();
return;
}
}
OutgoingEvent* event = dynamic_cast<OutgoingEvent*>(message.get());
//assert(event);
//.dcm. a TID collision can cause a message to be delivered to a finished
//chain. This is probably because pseudorandom was being used on Win32.
if (event)
{
if (event->message()->isRequest())
{
DialogSet* ds = findDialogSet(DialogSetId(*event->message()));
UserProfile* userProfile;
if (ds == 0)
{
userProfile = getMasterUserProfile().get();
}
else
{
userProfile = ds->getUserProfile().get();
}
resip_assert(userProfile);
//!dcm! -- unique SharedPtr to auto_ptr conversion prob. a worthwhile
//optimzation here. SharedPtr would have to be changed; would
//throw/assert if not unique.
std::auto_ptr<SipMessage> toSend(static_cast<SipMessage*>(event->message()->clone()));
// .bwc. Protect ourselves from garbage with an isWellFormed() check.
// (Code in Dialog doesn't check for well-formedness in the
// Record-Route stack, so bad stuff there can end up here)
if (event->message()->exists(h_Routes) &&
!event->message()->header(h_Routes).empty() &&
event->message()->header(h_Routes).front().isWellFormed() &&
!event->message()->header(h_Routes).front().uri().exists(p_lr))
{
Helper::processStrictRoute(*toSend);
sendUsingOutboundIfAppropriate(*userProfile, toSend);
}
else
{
sendUsingOutboundIfAppropriate(*userProfile, toSend);
}
}
else
{
sendResponse(*event->message());
}
}
}
void
DialogUsageManager::sendUsingOutboundIfAppropriate(UserProfile& userProfile, auto_ptr<SipMessage> msg)
{
//a little inefficient, branch parameter might be better
DialogId id(*msg);
if (userProfile.hasOutboundProxy() &&
(!findDialog(id) || userProfile.getForceOutboundProxyOnAllRequestsEnabled()))
{
DebugLog ( << "Using outbound proxy: "
<< userProfile.getOutboundProxy().uri()
<< " -> " << msg->brief());
if (userProfile.getExpressOutboundAsRouteSetEnabled())
{
// prepend the outbound proxy to the service route
msg->header(h_Routes).push_front(NameAddr(userProfile.getOutboundProxy().uri()));
if(userProfile.clientOutboundEnabled() && userProfile.mClientOutboundFlowTuple.mFlowKey != 0)
{
DebugLog ( << "Sending with client outbound flow tuple to express outbound" );
DebugLog ( << "Flow Tuple: " << userProfile.mClientOutboundFlowTuple << " and key: " << userProfile.mClientOutboundFlowTuple.mFlowKey);
mStack.sendTo(msg, userProfile.mClientOutboundFlowTuple, this);
}
else
{
DebugLog ( << "Sending to express outbound w/o flow tuple");
mStack.send(msg, this);
}
}
else
{
if(userProfile.clientOutboundEnabled() && userProfile.mClientOutboundFlowTuple.mFlowKey != 0)
{
DebugLog ( << "Sending to outbound (no express) with flow tuple");
mStack.sendTo(msg, userProfile.mClientOutboundFlowTuple, this);
}
else
{
DebugLog ( << "Sending to outbound uri");
mStack.sendTo(msg, userProfile.getOutboundProxy().uri(), this);
}
}
}
else
{
DebugLog (<< "Send: " << msg->brief());
if(userProfile.clientOutboundEnabled() && userProfile.mClientOutboundFlowTuple.mFlowKey != 0)
{
mStack.sendTo(msg, userProfile.mClientOutboundFlowTuple, this);
}
else
{
mStack.send(msg, this);
}
}
}
void
DialogUsageManager::end(DialogSetId setid)
{
DialogSet* ds = findDialogSet(setid);
if (ds == 0)
{
throw Exception("Request no longer exists", __FILE__, __LINE__);
}
else
{
ds->end();
}
}
void
DialogUsageManager::destroy(const BaseUsage* usage)
{
if (mShutdownState != Destroying)
{
post(new DestroyUsage(usage->mHandle));
}
else
{
InfoLog(<< "DialogUsageManager::destroy() not posting to stack");
}
}
void
DialogUsageManager::destroy(DialogSet* dset)
{
if (mShutdownState != Destroying)
{
post(new DestroyUsage(dset));
}
else
{
InfoLog(<< "DialogUsageManager::destroy() not posting to stack");
}
}
void
DialogUsageManager::destroy(Dialog* d)
{
if (mShutdownState != Destroying)
{
post(new DestroyUsage(d));
}
else
{
InfoLog(<< "DialogUsageManager::destroy() not posting to stack");
}
}
Dialog*
DialogUsageManager::findDialog(const DialogId& id)
{
DialogSet* ds = findDialogSet(id.getDialogSetId());
if (ds)
{
return ds->findDialog(id);
}
else
{
return 0;
}
}
InviteSessionHandle
DialogUsageManager::findInviteSession(DialogId id)
{
Dialog* dialog = findDialog(id);
if (dialog && dialog->mInviteSession)
{
return dialog->mInviteSession->getSessionHandle();
}
return InviteSessionHandle::NotValid();
}
pair<InviteSessionHandle, int>
DialogUsageManager::findInviteSession(CallId replaces)
{
//486/481/603 decision making logic where? App may not wish to keep track of
//invitesession state
//Logic is here for now.
InviteSessionHandle is = findInviteSession(DialogId(replaces.value(),
replaces.param(p_toTag),
replaces.param(p_fromTag)));
int ErrorStatusCode = 481; // Call/Transaction Does Not Exist
// If we matched a session - Do RFC3891 Section 3 Processing
if(is.isValid())
{
// Note some missing checks are:
// 1. If the Replaces header field matches more than one dialog, the UA must act as
// if no match was found
// 2. Verify that the initiator of the new Invite is authorized
if(is->isTerminated())
{
ErrorStatusCode = 603; // Declined
is = InviteSessionHandle::NotValid();
}
else if(is->isConnected())
{
// Check if early-only flag is present in replaces header
if(replaces.exists(p_earlyOnly))
{
ErrorStatusCode = 486; // Busy Here
is = InviteSessionHandle::NotValid();
}
}
else if(!is->isEarly())
{
// replaces can't be used on early dialogs that were not initiated by this UA - ie. InviteSession::Proceeding state
ErrorStatusCode = 481; // Call/Transaction Does Not Exist
is = InviteSessionHandle::NotValid();
}
}
return make_pair(is, ErrorStatusCode);
}
AppDialogHandle DialogUsageManager::findAppDialog(const DialogId& id)
{
Dialog* pDialog = findDialog(id);
if(pDialog && pDialog->mAppDialog)
{
return pDialog->mAppDialog->getHandle();
}
// Return an invalid handle
return AppDialogHandle();
}
AppDialogSetHandle DialogUsageManager::findAppDialogSet(const DialogSetId& id)
{
DialogSet* pDialogSet = findDialogSet(id);
if(pDialogSet && pDialogSet->mAppDialogSet)
{
return pDialogSet->mAppDialogSet->getHandle();
}
// Return an invalid handle
return AppDialogSetHandle();
}
void
DialogUsageManager::internalProcess(std::auto_ptr<Message> msg)
{
#ifdef RESIP_DUM_THREAD_DEBUG
if(!mThreadDebugKey)
{
// .bwc. Probably means multiple threads are trying to give DUM cycles
// simultaneously.
resip_assert(!mHiddenThreadDebugKey);
// No d'tor needed, since we're just going to use a pointer to this.
if(!ThreadIf::tlsKeyCreate(mThreadDebugKey, 0))
{
// .bwc. We really could pass anything here, but for the sake of
// passing a valid pointer, I have (completely arbitrarily) chosen a
// pointer to the DUM. All that matters is that this value is non-null
ThreadIf::tlsSetValue(mThreadDebugKey, this);
}
else
{
ErrLog(<< "ThreadIf::tlsKeyCreate() failed!");
}
}
#endif
threadCheck();
// After a Stack ShutdownMessage has been received, don't do anything else in dum
if (mShutdownState == Shutdown)
{
return;
}
{
TransactionUserMessage* tuMsg = dynamic_cast<TransactionUserMessage*>(msg.get());
if (tuMsg)
{
InfoLog (<< "TU unregistered ");
resip_assert(mShutdownState == RemovingTransactionUser);
resip_assert(tuMsg->type() == TransactionUserMessage::TransactionUserRemoved);
mShutdownState = Shutdown;
if (mDumShutdownHandler)
{
mDumShutdownHandler->onDumCanBeDeleted();
mDumShutdownHandler = 0; // prevent mDumShutdownHandler getting called more than once
}
return;
}
}
{
KeepAlivePong* pong = dynamic_cast<KeepAlivePong*>(msg.get());
if (pong)
{
DebugLog(<< "keepalive pong received from " << pong->getFlow());
if (mKeepAliveManager.get())
{
mKeepAliveManager->receivedPong(pong->getFlow());
}
return;
}
}
{
DestroyUsage* destroyUsage = dynamic_cast<DestroyUsage*>(msg.get());
if (destroyUsage)
{
//DebugLog(<< "Destroying usage" );
destroyUsage->destroy();
return;
}
}
{
DumTimeout* dumMsg = dynamic_cast<DumTimeout*>(msg.get());
if (dumMsg)
{
//DebugLog(<< "Timeout Message" );
if (!dumMsg->getBaseUsage().isValid())
{
return;
}
dumMsg->getBaseUsage()->dispatch(*dumMsg);
return;
}
}
{
KeepAliveTimeout* keepAliveMsg = dynamic_cast<KeepAliveTimeout*>(msg.get());
if (keepAliveMsg)
{
//DebugLog(<< "Keep Alive Message" );
if (mKeepAliveManager.get())
{
mKeepAliveManager->process(*keepAliveMsg);
}
return;
}
}
{
KeepAlivePongTimeout* keepAlivePongMsg = dynamic_cast<KeepAlivePongTimeout*>(msg.get());
if (keepAlivePongMsg)
{
//DebugLog(<< "Keep Alive Pong Message" );
if (mKeepAliveManager.get())
{
mKeepAliveManager->process(*keepAlivePongMsg);
}
return;
}
}
{
ConnectionTerminated* terminated = dynamic_cast<ConnectionTerminated*>(msg.get());
if (terminated)
{
// Notify all dialogSets, in case they need to react (ie. client outbound support)
// First find all applicable dialogsets, since flow token in user profile will
// be cleared by first dialogset we notify, then notify all dialogset's
std::list<DialogSet*> dialogSetsToNotify;
DialogSetMap::iterator it = mDialogSetMap.begin();
for(; it != mDialogSetMap.end(); it++)
{
if(it->second->mUserProfile->clientOutboundEnabled() &&
it->second->mUserProfile->getClientOutboundFlowTuple().mFlowKey == terminated->getFlow().mFlowKey && // Flow key is not part of Tuple operator=, check it first
it->second->mUserProfile->getClientOutboundFlowTuple() == terminated->getFlow())
{
if(it->second->getClientRegistration().isValid())
{
// ensure client registrations are notified first
dialogSetsToNotify.push_front(it->second);
}
else
{
dialogSetsToNotify.push_back(it->second);
}
}
}
// Now dispatch notification to all dialogsets found above
std::list<DialogSet*>::iterator it2 = dialogSetsToNotify.begin();
for(; it2 != dialogSetsToNotify.end();it2++)
{
(*it2)->flowTerminated(terminated->getFlow());
}
DebugLog(<< "connection terminated message");
if (mConnectionTerminatedEventDispatcher.dispatch(msg.get()))
{
msg.release();
}
return;
}
}
{
DumCommand* command = dynamic_cast<DumCommand*>(msg.get());
if (command)
{
//DebugLog(<< "DumCommand" );
command->executeCommand();
return;
}
}
{
ExternalMessageBase* externalMessage = dynamic_cast<ExternalMessageBase*>(msg.get());
if (externalMessage)
{
processExternalMessage(externalMessage);
return;
}
}
incomingProcess(msg);
}
void
DialogUsageManager::processExternalMessage(ExternalMessageBase* externalMessage)
{
bool handled = false;
for(std::vector<ExternalMessageHandler*>::iterator i = mExternalMessageHandlers.begin();
i != mExternalMessageHandlers.end(); ++i)
{
(*i)->onMessage(externalMessage, handled);
if (handled)
{
break;
}
}
}
void
DialogUsageManager::incomingProcess(std::auto_ptr<Message> msg)
{
//call or create feature chain if appropriate
Data tid = Data::Empty;
{
SipMessage* sipMsg = dynamic_cast<SipMessage*>(msg.get());
if (sipMsg)
{
tid = sipMsg->getTransactionId();
bool garbage=false;
Data reason;
if(!sipMsg->header(h_From).isWellFormed())
{
garbage=true;
reason.append("Malformed From, ",16);
}
if(!sipMsg->header(h_To).isWellFormed())
{
garbage=true;
reason.append("Malformed To, ",14);
}
if(!sipMsg->header(h_CallId).isWellFormed())
{
garbage=true;
reason.append("Malformed Call-Id, ",19);
}
if(garbage)
{
if(sipMsg->isRequest() && sipMsg->method()!=ACK)
{
// .bwc. Either we need to trim the last comma off, or make this
// a proper sentence fragment. This is more fun.
reason.append("fix your code!",14);
SipMessage failure;
makeResponse(failure, *sipMsg, 400, reason);
sendResponse(failure);
}
InfoLog (<< "Malformed header in message (" << reason << ") - rejecting/discarding: " << *sipMsg);
// .bwc. Only forge a response when appropriate, but return in any
// case.
return;
}
}
DumFeatureMessage* featureMsg = dynamic_cast<DumFeatureMessage*>(msg.get());
if (featureMsg)
{
//DebugLog(<<"Got a DumFeatureMessage" << featureMsg);
tid = featureMsg->getTransactionId();
}
}
if (tid != Data::Empty && !mIncomingFeatureList.empty())
{
FeatureChainMap::iterator it;
//efficiently find or create FeatureChain, should prob. be a utility template
{
FeatureChainMap::iterator lb = mIncomingFeatureChainMap.lower_bound(tid);
if (lb != mIncomingFeatureChainMap.end() && !(mIncomingFeatureChainMap.key_comp()(tid, lb->first)))
{
it = lb;
}
else
{
if(dynamic_cast<SipMessage*>(msg.get()))
{
it = mIncomingFeatureChainMap.insert(lb, FeatureChainMap::value_type(tid, new DumFeatureChain(*this, mIncomingFeatureList, *mIncomingTarget)));
}
else
{
// Certain messages from the wire (ie: CANCEL) can end a feature, however there may still be some
// pending Async requests (non-SipMessages) that are coming in - just drop them if so
return;
}
}
}
DumFeatureChain::ProcessingResult res = it->second->process(msg.get());
if (res & DumFeatureChain::ChainDoneBit)
{
delete it->second;
mIncomingFeatureChainMap.erase(it);
//DebugLog(<< "feature chain deleted" << endl);
}
if (res & DumFeatureChain::EventTakenBit)
{
msg.release();
//DebugLog(<< "event taken");
return;
}
}
try
{
DebugLog (<< "Got: " << msg->brief());
DumDecrypted* decryptedMsg = dynamic_cast<DumDecrypted*>(msg.get());
SipMessage* sipMsg = 0;
if (decryptedMsg)
{
sipMsg = decryptedMsg->decrypted();
}
else
{
sipMsg = dynamic_cast<SipMessage*>(msg.get());
}
if (sipMsg)
{
//DebugLog ( << "DialogUsageManager::process: " << sipMsg->brief());
if (sipMsg->isRequest())
{
// Validate Request URI
if( !validateRequestURI(*sipMsg) )
{
DebugLog (<< "Failed RequestURI validation " << *sipMsg);
return;
}
// Continue validation on all requests, except ACK and CANCEL
if(sipMsg->header(h_RequestLine).method() != ACK &&
sipMsg->header(h_RequestLine).method() != CANCEL)
{
if( !validateRequiredOptions(*sipMsg) )
{
DebugLog (<< "Failed required options validation " << *sipMsg);
return;
}
if( !validate100RelSupport(*sipMsg) )
{
DebugLog (<< "Remote party does not support 100rel " << *sipMsg);
return;
}
if( getMasterProfile()->validateContentEnabled() && !validateContent(*sipMsg) )
{
DebugLog (<< "Failed content validation " << *sipMsg);
return;
}
if( getMasterProfile()->validateAcceptEnabled() && !validateAccept(*sipMsg) )
{
DebugLog (<< "Failed accept validation " << *sipMsg);
return;
}
}
if (sipMsg->header(h_From).exists(p_tag))
{
if (mergeRequest(*sipMsg) )
{
InfoLog (<< "Merged request: " << *sipMsg);
return;
}
}
processRequest(*sipMsg);
}
else
{
processResponse(*sipMsg);
}
}
}
catch(BaseException& e)
{
//unparseable, bad 403 w/ 2543 trans it from FWD, etc
ErrLog(<<"Illegal message rejected: " << e.getMessage());
}
}
bool
DialogUsageManager::hasEvents() const
{
return mFifo.messageAvailable();
}
// return true if there is more to do
bool
DialogUsageManager::process(resip::Lockable* mutex)
{
if (mFifo.messageAvailable())
{
resip::PtrLock lock(mutex);
#ifdef RESIP_DUM_THREAD_DEBUG
mThreadDebugKey=mHiddenThreadDebugKey;
#endif
internalProcess(std::auto_ptr<Message>(mFifo.getNext()));
#ifdef RESIP_DUM_THREAD_DEBUG
// .bwc. Thread checking is disabled if mThreadDebugKey is 0; if the app
// is using this mutex-locked process() call, we only enable thread-
// checking while the mutex is locked. Accesses from another thread while
// the mutex is not locked are probably intentional. However, if the app
// accesses the DUM inappropriately anyway, we'll probably detect it if
// it happens during the internalProcess() call.
mHiddenThreadDebugKey=mThreadDebugKey;
mThreadDebugKey=0;
#endif
}
return mFifo.messageAvailable();
}
bool
DialogUsageManager::process(int timeoutMs, resip::Lockable* mutex)
{
std::auto_ptr<Message> message;
if(timeoutMs == -1)
{
message.reset(mFifo.getNext());
}
else
{
message.reset(mFifo.getNext(timeoutMs));
}
if (message.get())
{
resip::PtrLock lock(mutex);
#ifdef RESIP_DUM_THREAD_DEBUG
mThreadDebugKey=mHiddenThreadDebugKey;
#endif
internalProcess(message);
#ifdef RESIP_DUM_THREAD_DEBUG
// .bwc. Thread checking is disabled if mThreadDebugKey is 0; if the app
// is using this mutex-locked process() call, we only enable thread-
// checking while the mutex is locked. Accesses from another thread while
// the mutex is not locked are probably intentional. However, if the app
// accesses the DUM inappropriately anyway, we'll probably detect it if
// it happens during the internalProcess() call.
mHiddenThreadDebugKey=mThreadDebugKey;
mThreadDebugKey=0;
#endif
}
return mFifo.messageAvailable();
}
bool
DialogUsageManager::validateRequestURI(const SipMessage& request)
{
// RFC3261 - 8.2.1
if (!getMasterProfile()->isMethodSupported(request.header(h_RequestLine).getMethod()))
{
InfoLog (<< "Received an unsupported method: " << request.brief());
SipMessage failure;
makeResponse(failure, request, 405);
failure.header(h_Allows) = getMasterProfile()->getAllowedMethods();
sendResponse(failure);
if(mRequestValidationHandler)
mRequestValidationHandler->onInvalidMethod(request);
return false;
}
// RFC3261 - 8.2.2
if (!getMasterProfile()->isSchemeSupported(request.header(h_RequestLine).uri().scheme()))
{
InfoLog (<< "Received an unsupported scheme: " << request.brief());
SipMessage failure;
makeResponse(failure, request, 416);
sendResponse(failure);
if(mRequestValidationHandler)
mRequestValidationHandler->onInvalidScheme(request);
return false;
}
return true;
}
bool
DialogUsageManager::validateRequiredOptions(const SipMessage& request)
{
// RFC 2162 - 8.2.2
if(request.exists(h_Requires) && // Don't check requires if method is ACK or CANCEL
request.header(h_RequestLine).getMethod() != ACK &&
request.header(h_RequestLine).getMethod() != CANCEL)
{
Tokens unsupported = getMasterProfile()->getUnsupportedOptionsTags(request.header(h_Requires));
if (!unsupported.empty())
{
InfoLog (<< "Received an unsupported option tag(s): " << request.brief());
SipMessage failure;
makeResponse(failure, request, 420);
failure.header(h_Unsupporteds) = unsupported;
sendResponse(failure);
if(mRequestValidationHandler)
mRequestValidationHandler->onInvalidRequiredOptions(request);
return false;
}
}
return true;
}
bool
DialogUsageManager::validate100RelSupport(const SipMessage& request)
{
if(request.header(h_RequestLine).getMethod() == INVITE)
{
if (getMasterProfile()->getUasReliableProvisionalMode() == MasterProfile::Required)
{
if (!((request.exists(h_Requires) && request.header(h_Requires).find(Token(Symbols::C100rel)))
|| (request.exists(h_Supporteds) && request.header(h_Supporteds).find(Token(Symbols::C100rel)))))
{
SipMessage failure;
makeResponse(failure, request, 421);
failure.header(h_Requires).push_back(Token(Symbols::C100rel));
sendResponse(failure);
if(mRequestValidationHandler)
{
mRequestValidationHandler->on100RelNotSupportedByRemote(request);
}
return false;
}
}
}
return true;
}
bool
DialogUsageManager::validateContent(const SipMessage& request)
{
// RFC3261 - 8.2.3
// Don't need to validate content headers if they are specified as optional in the content-disposition
if (!(request.exists(h_ContentDisposition) &&
request.header(h_ContentDisposition).isWellFormed() &&
request.header(h_ContentDisposition).exists(p_handling) &&
isEqualNoCase(request.header(h_ContentDisposition).param(p_handling), Symbols::Optional)))
{
if (request.exists(h_ContentType) && !getMasterProfile()->isMimeTypeSupported(request.header(h_RequestLine).method(), request.header(h_ContentType)))
{
InfoLog (<< "Received an unsupported mime type: " << request.header(h_ContentType) << " for " << request.brief());
SipMessage failure;
makeResponse(failure, request, 415);
failure.header(h_Accepts) = getMasterProfile()->getSupportedMimeTypes(request.header(h_RequestLine).method());
sendResponse(failure);
if(mRequestValidationHandler)
mRequestValidationHandler->onInvalidContentType(request);
return false;
}
if (request.exists(h_ContentEncoding) && !getMasterProfile()->isContentEncodingSupported(request.header(h_ContentEncoding)))
{
InfoLog (<< "Received an unsupported mime type: " << request.header(h_ContentEncoding) << " for " << request.brief());
SipMessage failure;
makeResponse(failure, request, 415);
failure.header(h_AcceptEncodings) = getMasterProfile()->getSupportedEncodings();
sendResponse(failure);
if(mRequestValidationHandler)
mRequestValidationHandler->onInvalidContentEncoding(request);
return false;
}
if (getMasterProfile()->validateContentLanguageEnabled() &&
request.exists(h_ContentLanguages) && !getMasterProfile()->isLanguageSupported(request.header(h_ContentLanguages)))
{
InfoLog (<< "Received an unsupported language: " << request.header(h_ContentLanguages).front() << " for " << request.brief());
SipMessage failure;
makeResponse(failure, request, 415);
failure.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
sendResponse(failure);
if(mRequestValidationHandler)
mRequestValidationHandler->onInvalidContentLanguage(request);
return false;
}
}
return true;
}
bool
DialogUsageManager::validateAccept(const SipMessage& request)
{
MethodTypes method = request.header(h_RequestLine).method();
// checks for Accept to comply with SFTF test case 216
if(request.exists(h_Accepts))
{
for (Mimes::const_iterator i = request.header(h_Accepts).begin();
i != request.header(h_Accepts).end(); i++)
{
if (getMasterProfile()->isMimeTypeSupported(method, *i))
{
return true; // Accept header passes validation if we support as least one of the mime types
}
}
}
// If no Accept header then application/sdp should be assumed for certain methods
else if(method == INVITE ||
method == OPTIONS ||
method == PRACK ||
method == UPDATE)
{
if (getMasterProfile()->isMimeTypeSupported(request.header(h_RequestLine).method(), Mime("application", "sdp")))
{
return true;
}
}
else
{
// Other method without an Accept Header
return true;
}
InfoLog (<< "Received unsupported mime types in accept header: " << request.brief());
SipMessage failure;
makeResponse(failure, request, 406);
failure.header(h_Accepts) = getMasterProfile()->getSupportedMimeTypes(method);
sendResponse(failure);
if(mRequestValidationHandler)
mRequestValidationHandler->onInvalidAccept(request);
return false;
}
bool
DialogUsageManager::mergeRequest(const SipMessage& request)
{
resip_assert(request.isRequest());
resip_assert(request.isExternal());
if (!request.header(h_To).exists(p_tag))
{
if (mMergedRequests.count(MergedRequestKey(request, getMasterProfile()->checkReqUriInMergeDetectionEnabled())))
{
SipMessage failure;
makeResponse(failure, request, 482, "Merged Request");
failure.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
sendResponse(failure);
return true;
}
}
return false;
}
void
DialogUsageManager::processRequest(const SipMessage& request)
{
DebugLog ( << "DialogUsageManager::processRequest: " << request.brief());
if (mShutdownState != Running && mShutdownState != ShutdownRequested)
{
WarningLog (<< "Ignoring a request since we are shutting down " << request.brief());
SipMessage failure;
makeResponse(failure, request, 480, "UAS is shutting down");
sendResponse(failure);
return;
}
if (request.header(h_RequestLine).method() == PUBLISH)
{
processPublish(request);
return;
}
bool toTag = request.header(h_To).exists(p_tag);
if(request.header(h_RequestLine).getMethod() == REGISTER && toTag && getMasterProfile()->allowBadRegistrationEnabled())
{
toTag = false;
}
resip_assert(mAppDialogSetFactory.get());
// !jf! note, the logic was reversed during ye great merge of March of Ought 5
if (toTag ||
findDialogSet(DialogSetId(request)))
{
switch (request.header(h_RequestLine).getMethod())
{
case REGISTER:
{
SipMessage failure;
makeResponse(failure, request, 400, "Registration requests can't have To: tags.");
failure.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
sendResponse(failure);
break;
}
default:
{
DialogSet* ds = findDialogSet(DialogSetId(request));
if (ds == 0)
{
if (request.header(h_RequestLine).method() != ACK)
{
SipMessage failure;
makeResponse(failure, request, 481);
failure.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
InfoLog (<< "Rejected request (which was in a dialog) " << request.brief());
sendResponse(failure);
}
else
{
InfoLog (<< "ACK doesn't match any dialog" << request.brief());
}
}
else
{
InfoLog (<< "Handling in-dialog request: " << request.brief());
ds->dispatch(request);
}
}
}
}
else
{
switch (request.header(h_RequestLine).getMethod())
{
case ACK:
DebugLog (<< "Discarding request: " << request.brief());
break;
case PRACK:
case BYE:
case UPDATE:
case INFO: // !rm! in an ideal world
{
SipMessage failure;
makeResponse(failure, request, 481);
failure.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
sendResponse(failure);
break;
}
case CANCEL:
{
// find the appropropriate ServerInvSession
CancelMap::iterator i = mCancelMap.find(request.getTransactionId());
if (i != mCancelMap.end())
{
i->second->dispatch(request);
}
else
{
InfoLog (<< "Received a CANCEL on a non-existent transaction: tid=" << request.getTransactionId());
SipMessage failure;
makeResponse(failure, request, 481);
sendResponse(failure);
}
break;
}
case PUBLISH:
resip_assert(false);
return;
case SUBSCRIBE:
if (!checkEventPackage(request))
{
InfoLog (<< "Rejecting request (unsupported package) "
<< request.brief());
return;
}
/*FALLTHRU*/
case NOTIFY : // handle unsolicited (illegal) NOTIFYs
case INVITE: // new INVITE
case REFER: // out-of-dialog REFER
//case INFO : // handle non-dialog (illegal) INFOs
case OPTIONS : // handle non-dialog OPTIONS
case MESSAGE :
case REGISTER:
{
{
DialogSetId id(request);
//cryptographically dangerous
if(mDialogSetMap.find(id) != mDialogSetMap.end())
{
// this can only happen if someone sends us a request with the same callid and from tag as one
// that is in the process of destroying - since this is bad endpoint behaviour - we will
// reject the request with a 400 response
SipMessage badrequest;
makeResponse(badrequest, request, 400);
badrequest.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
sendResponse(badrequest);
return;
}
}
if (mDumShutdownHandler)
{
SipMessage forbidden;
makeResponse(forbidden, request, 480);
forbidden.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
sendResponse(forbidden);
return;
}
try
{
DialogSet* dset = new DialogSet(request, *this);
StackLog ( << "*********** Calling AppDialogSetFactory *************: " << dset->getId());
AppDialogSet* appDs = mAppDialogSetFactory->createAppDialogSet(*this, request);
appDs->mDialogSet = dset;
dset->setUserProfile(appDs->selectUASUserProfile(request));
dset->mAppDialogSet = appDs;
StackLog ( << "************* Adding DialogSet ***************: " << dset->getId());
//StackLog ( << "Before: " << Inserter(mDialogSetMap) );
mDialogSetMap[dset->getId()] = dset;
StackLog ( << "DialogSetMap: " << InserterP(mDialogSetMap) );
dset->dispatch(request);
}
catch (BaseException& e)
{
SipMessage failure;
makeResponse(failure, request, 400, e.getMessage());
failure.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
sendResponse(failure);
}
break;
}
case RESPONSE:
case SERVICE:
resip_assert(false);
break;
case UNKNOWN:
case MAX_METHODS:
resip_assert(false);
break;
}
}
}
void
DialogUsageManager::processResponse(const SipMessage& response)
{
if (response.header(h_CSeq).method() != CANCEL)
{
DialogSet* ds = findDialogSet(DialogSetId(response));
if (ds)
{
DebugLog ( << "DialogUsageManager::processResponse: " << std::endl << std::endl << response.brief());
ds->dispatch(response);
}
else
{
InfoLog (<< "Throwing away stray response: " << std::endl << std::endl << response.brief());
}
}
}
void
DialogUsageManager::processPublish(const SipMessage& request)
{
if (!checkEventPackage(request))
{
InfoLog(<< "Rejecting request (unsupported package) " << request.brief());
return;
}
if (request.exists(h_SIPIfMatch))
{
ServerPublications::iterator i = mServerPublications.find(request.header(h_SIPIfMatch).value());
if (i != mServerPublications.end())
{
i->second->dispatch(request);
}
else
{
// Check if publication exists in PublicationDb - may have been sync'd over,
// or exists from a restart. In this case, fabricate a new ServerSubcription
// to handle this request.
if (mPublicationPersistenceManager &&
mPublicationPersistenceManager->documentExists(request.header(h_Event).value(), request.header(h_RequestLine).uri().getAor(), request.header(h_SIPIfMatch).value()))
{
ServerPublication* sp = new ServerPublication(*this, request.header(h_SIPIfMatch).value(), request);
mServerPublications[request.header(h_SIPIfMatch).value()] = sp;
sp->dispatch(request);
}
else
{
SharedPtr<SipMessage> response(new SipMessage);
makeResponse(*response, request, 412);
send(response);
}
}
}
else
{
Data etag = Random::getCryptoRandomHex(8);
while (mServerPublications.find(etag) != mServerPublications.end())
{
etag = Random::getCryptoRandomHex(8);
}
if (request.getContents())
{
ServerPublication* sp = new ServerPublication(*this, etag, request);
mServerPublications[etag] = sp;
sp->dispatch(request);
}
else
{
// per 3903 (sec 6.5), a PUB w/ no SIPIfMatch must have contents. .mjf.
SharedPtr<SipMessage> response(new SipMessage);
makeResponse(*response, request, 400);
send(response);
}
}
}
bool
DialogUsageManager::checkEventPackage(const SipMessage& request)
{
int failureCode = 0;
MethodTypes method = request.header(h_RequestLine).method();
// || (method == NOTIFY && !request.exists(h_SubscriptionState)))
if (!request.exists(h_Event))
{
InfoLog (<< "No Event header in " << request.header(h_RequestLine).unknownMethodName());
failureCode = 400;
}
else
{
switch(method)
{
case SUBSCRIBE:
if (!getServerSubscriptionHandler(request.header(h_Event).value()))
{
InfoLog (<< "No handler for event package for SUBSCRIBE: "
<< request.header(h_Event).value());
failureCode = 489;
}
break;
case NOTIFY:
if (!getClientSubscriptionHandler(request.header(h_Event).value()))
{
InfoLog (<< "No handler for event package for NOTIFY: "
<< request.header(h_Event).value());
failureCode = 489;
}
break;
case PUBLISH:
if (!getServerPublicationHandler(request.header(h_Event).value()))
{
InfoLog (<< "No handler for event package for PUBLISH: "
<< request.header(h_Event).value());
failureCode = 489;
}
break;
default:
resip_assert(0);
}
}
if (failureCode > 0)
{
SharedPtr<SipMessage> response(new SipMessage);
makeResponse(*response, request, failureCode);
if(failureCode == 489)
{
response->header(h_AllowEvents) = getMasterProfile()->getAllowedEvents();
}
send(response);
return false;
}
return true;
}
DialogSet*
DialogUsageManager::findDialogSet(const DialogSetId& id)
{
threadCheck();
StackLog ( << "Looking for dialogSet: " << id << " in map:");
StackLog ( << "DialogSetMap: " << InserterP(mDialogSetMap) );
DialogSetMap::const_iterator it = mDialogSetMap.find(id);
if (it == mDialogSetMap.end())
{
StackLog ( << "Not found" );
return 0;
}
else
{
if(it->second->isDestroying())
{
StackLog ( << "isDestroying() == true" );
return 0;
}
else
{
StackLog ( << "found" );
return it->second;
}
}
}
BaseCreator*
DialogUsageManager::findCreator(const DialogId& id)
{
DialogSet* ds = findDialogSet(id.getDialogSetId());
if (ds)
{
return ds->getCreator();
}
else
{
return 0;
}
}
void
DialogUsageManager::removeDialogSet(const DialogSetId& dsId)
{
StackLog ( << "************* Removing DialogSet ***************: " << dsId);
//StackLog ( << "Before: " << Inserter(mDialogSetMap) );
mDialogSetMap.erase(dsId);
StackLog ( << "DialogSetMap: " << InserterP(mDialogSetMap) );
if (mRedirectManager.get())
{
mRedirectManager->removeDialogSet(dsId);
}
}
ClientSubscriptionHandler*
DialogUsageManager::getClientSubscriptionHandler(const Data& eventType)
{
map<Data, ClientSubscriptionHandler*>::iterator res = mClientSubscriptionHandlers.find(eventType);
if (res != mClientSubscriptionHandlers.end())
{
return res->second;
}
else
{
return 0;
}
}
ServerSubscriptionHandler*
DialogUsageManager::getServerSubscriptionHandler(const Data& eventType)
{
map<Data, ServerSubscriptionHandler*>::iterator res = mServerSubscriptionHandlers.find(eventType);
if (res != mServerSubscriptionHandlers.end())
{
return res->second;
}
else
{
return 0;
}
}
ClientPublicationHandler*
DialogUsageManager::getClientPublicationHandler(const Data& eventType)
{
map<Data, ClientPublicationHandler*>::iterator res = mClientPublicationHandlers.find(eventType);
if (res != mClientPublicationHandlers.end())
{
return res->second;
}
else
{
return 0;
}
}
ServerPublicationHandler*
DialogUsageManager::getServerPublicationHandler(const Data& eventType)
{
map<Data, ServerPublicationHandler*>::iterator res = mServerPublicationHandlers.find(eventType);
if (res != mServerPublicationHandlers.end())
{
return res->second;
}
else
{
return 0;
}
}
OutOfDialogHandler*
DialogUsageManager::getOutOfDialogHandler(const MethodTypes type)
{
map<MethodTypes, OutOfDialogHandler*>::iterator res = mOutOfDialogHandlers.find(type);
if (res != mOutOfDialogHandlers.end())
{
return res->second;
}
else
{
return 0;
}
}
void
DialogUsageManager::addIncomingFeature(resip::SharedPtr<DumFeature> feat)
{
mIncomingFeatureList.push_back(feat);
}
void
DialogUsageManager::addOutgoingFeature(resip::SharedPtr<DumFeature> feat)
{
// make sure EncryptionManager is the last feature in the list.
mOutgoingFeatureList.insert(mOutgoingFeatureList.begin(), feat);
}
void
DialogUsageManager::setOutgoingMessageInterceptor(SharedPtr<DumFeature> feat)
{
mOutgoingMessageInterceptor = feat;
}
void
DialogUsageManager::applyToAllServerSubscriptions(ServerSubscriptionFunctor* functor)
{
resip_assert(functor);
for (DialogSetMap::iterator it = mDialogSetMap.begin(); it != mDialogSetMap.end(); ++it)
{
for (DialogSet::DialogMap::iterator i = it->second->mDialogs.begin(); i != it->second->mDialogs.end(); ++i)
{
std::vector<ServerSubscriptionHandle> serverSubs = i->second->getServerSubscriptions();
for (std::vector<ServerSubscriptionHandle>::iterator iss = serverSubs.begin(); iss != serverSubs.end(); ++iss)
{
functor->apply(*iss);
}
}
}
}
void
DialogUsageManager::applyToAllClientSubscriptions(ClientSubscriptionFunctor* functor)
{
resip_assert(functor);
for (DialogSetMap::iterator it = mDialogSetMap.begin(); it != mDialogSetMap.end(); ++it)
{
for (DialogSet::DialogMap::iterator i = it->second->mDialogs.begin(); i != it->second->mDialogs.end(); ++i)
{
std::vector<ClientSubscriptionHandle> clientSubs = i->second->getClientSubscriptions();
for (std::vector<ClientSubscriptionHandle>::iterator ics = clientSubs.begin(); ics != clientSubs.end(); ++ics)
{
functor->apply(*ics);
}
}
}
}
void
DialogUsageManager::endAllServerSubscriptions(TerminateReason reason)
{
// Make a copy of the map - since calling end can cause an immediate delete this on the subscription and thus cause
// the object to remove itself from the mServerSubscriptions map, messing up our iterator
ServerSubscriptions tempSubscriptions = mServerSubscriptions;
ServerSubscriptions::iterator it = tempSubscriptions.begin();
for (; it != tempSubscriptions.end(); it++)
{
it->second->end(reason);
}
}
void
DialogUsageManager::endAllServerPublications()
{
// Make a copy of the map - since calling end can cause an immediate delete this on the publication and thus cause
// the object to remove itself from the mServerPublications map, messing up our iterator
ServerPublications tempPublications = mServerPublications;
ServerPublications::iterator it = tempPublications.begin();
for (; it != tempPublications.end(); it++)
{
it->second->end();
}
}
void
DialogUsageManager::registerForConnectionTermination(Postable* listener)
{
mConnectionTerminatedEventDispatcher.addListener(listener);
}
void
DialogUsageManager::unRegisterForConnectionTermination(Postable* listener)
{
mConnectionTerminatedEventDispatcher.removeListener(listener);
}
void
DialogUsageManager::requestMergedRequestRemoval(const MergedRequestKey& key)
{
// Only post delayed merge request removal if running, if we are shutting down, then there is no need
if (mShutdownState == Running)
{
DebugLog(<< "Got merged request removal request");
MergedRequestRemovalCommand command(*this, key);
mStack.postMS(command, Timer::TF, this);
}
}
void
DialogUsageManager::removeMergedRequest(const MergedRequestKey& key)
{
DebugLog(<< "Merged request removed");
mMergedRequests.erase(key);
}
TargetCommand::Target&
DialogUsageManager::dumIncomingTarget()
{
return *mIncomingTarget;
}
TargetCommand::Target&
DialogUsageManager::dumOutgoingTarget()
{
return *mOutgoingTarget;
}
DialogEventStateManager*
DialogUsageManager::createDialogEventStateManager(DialogEventHandler* handler)
{
if(handler)
{
mDialogEventStateManager = new DialogEventStateManager();
mDialogEventStateManager->mDialogEventHandler = handler;
}
else
{
delete mDialogEventStateManager;
mDialogEventStateManager=0;
}
return mDialogEventStateManager;
}
void
DialogUsageManager::setAdvertisedCapabilities(SipMessage& msg, SharedPtr<UserProfile> userProfile)
{
if(userProfile->isAdvertisedCapability(Headers::Allow))
{
msg.header(h_Allows) = getMasterProfile()->getAllowedMethods();
}
if(userProfile->isAdvertisedCapability(Headers::AcceptEncoding))
{
msg.header(h_AcceptEncodings) = getMasterProfile()->getSupportedEncodings();
}
if(userProfile->isAdvertisedCapability(Headers::AcceptLanguage))
{
msg.header(h_AcceptLanguages) = getMasterProfile()->getSupportedLanguages();
}
if(userProfile->isAdvertisedCapability(Headers::AllowEvents))
{
msg.header(h_AllowEvents) = getMasterProfile()->getAllowedEvents();
}
if(userProfile->isAdvertisedCapability(Headers::Supported))
{
msg.header(h_Supporteds) = getMasterProfile()->getSupportedOptionTags();
}
}
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The names "VOCAL", "Vovida Open Communication Application Library",
* and "Vovida Open Communication Application Library (VOCAL)" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact vocal@vovida.org.
*
* 4. Products derived from this software may not be called "VOCAL", nor
* may "VOCAL" appear in their name, without prior written
* permission of Vovida Networks, Inc.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* ====================================================================
*
* This software consists of voluntary contributions made by Vovida
* Networks, Inc. and many individuals on behalf of Vovida Networks,
* Inc. For more information on Vovida Networks, Inc., please see
* <http://www.vovida.org/>.
*
*/