reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
MOHManager.cxx
Go to the documentation of this file.
1#include "../UserAgent.hxx"
2#include "AppSubsystem.hxx"
3#include "MOHManager.hxx"
4#include "Server.hxx"
5
7#include <rutil/Log.hxx>
8#include <rutil/Logger.hxx>
10
11using namespace recon;
12using namespace resip;
13using namespace std;
14
15#define RESIPROCATE_SUBSYSTEM AppSubsystem::MOHPARKSERVER
16
17#define MOHLOG_PREFIX << "MOHManager[" << mMOHUri << "] "
18
19static const resip::ExtensionParameter p_automaton("automaton");
20static const resip::ExtensionParameter p_byeless("+sip.byeless");
21static const resip::ExtensionParameter p_rendering("+sip.rendering");
22
23namespace mohparkserver
24{
25
27 mServer(server),
28 mConversationProfileHandle(0),
29 mMusicFilenameChanged(false)
30{
31}
32
36
37void
39{
40 // Initialize settings
42
43 // Setup ConversationProfile - If MOH setting specific outbound proxy is not specified, then use global
44 // outbound proxy setting.
46 !settings.mOutboundProxy.uri().host().empty() ? settings.mOutboundProxy : mServer.mConfig.mOutboundProxy);
47
48 // Create an initial conversation and start music
50 mServer.createMediaResourceParticipant(convHandle, settings.mMOHFilenameUrl); // Play Music
51 mConversations[convHandle];
52 mMusicFilenameChanged = false;
53
54 InfoLog(MOHLOG_PREFIX << "startup: MOHFilenameUrl=" << settings.mMOHFilenameUrl);
55}
56
57void
58MOHManager::initializeConversationProfile(const NameAddr& uri, const Data& password, unsigned long registrationTime, const resip::NameAddr& outboundProxy)
59{
60 if(mConversationProfileHandle)
61 {
62 mServer.mMyUserAgent->destroyConversationProfile(mConversationProfileHandle);
63 mConversationProfileHandle = 0;
64 }
65
66 // Setup ConversationProfile
67 auto mohConversationProfile = std::make_shared<ConversationProfile>(mServer.mUserAgentMasterProfile);
68 mohConversationProfile->setDefaultRegistrationTime(registrationTime);
69 mohConversationProfile->setDefaultRegistrationRetryTime(120); // 2 mins
70 mohConversationProfile->setDefaultFrom(uri);
71 mohConversationProfile->setDigestCredential(uri.uri().host(), uri.uri().user(), password);
72 if(!outboundProxy.uri().host().empty())
73 {
74 mohConversationProfile->setOutboundProxy(outboundProxy.uri());
75 }
76 mohConversationProfile->challengeOODReferRequests() = false;
77 mohConversationProfile->setExtraHeadersInReferNotifySipFragEnabled(true); // Enable dialog identifying headers in SipFrag bodies of Refer Notifies - required for a music on hold server
78 NameAddr capabilities;
79 capabilities.param(p_automaton);
80 capabilities.param(p_byeless);
81 capabilities.param(p_rendering) = "\"no\"";
82 mohConversationProfile->setUserAgentCapabilities(capabilities);
83 mohConversationProfile->natTraversalMode() = ConversationProfile::NoNatTraversal;
84 mohConversationProfile->secureMediaMode() = ConversationProfile::NoSecureMedia;
85 mServer.buildSessionCapabilities(mohConversationProfile->sessionCaps());
86 mConversationProfileHandle = mServer.mMyUserAgent->addConversationProfile(mohConversationProfile);
87 mMOHUri = uri;
88}
89
90void
92{
93 Lock lock(mMutex);
94 mMusicFilename = musicFilename;
95 // If there is a single conversation with no participants, then there are no
96 // current parties on hold - re-create the conversation with new music
97 if(mConversations.size() == 1 && mConversations.begin()->second.size() == 0)
98 {
99 mServer.destroyConversation(mConversations.begin()->first);
100 mConversations.clear();
101
102 // re-create an initial conversation and start music
104 mServer.createMediaResourceParticipant(convHandle, mMusicFilename); // Play Music
105 mConversations[convHandle];
106 }
107 else
108 {
109 mMusicFilenameChanged = true;
110 }
111}
112
113void
114MOHManager::shutdown(bool shuttingDownServer)
115{
116 Lock lock(mMutex);
117 // Destroy all conversations
118 ConversationMap::iterator it = mConversations.begin();
119 for (; it != mConversations.end(); it++)
120 {
121 // Clean up participant memory
122 ParticipantMap::iterator partIt = it->second.begin();
123 for (; partIt != it->second.end(); partIt++)
124 {
125 delete partIt->second;
126 }
127 it->second.clear();
128
129 mServer.destroyConversation(it->first);
130 }
131 mConversations.clear();
132
133 // If shutting down server, then we shouldn't remove the conversation profiles here
134 // shutting down the ConversationManager will take care of this. We need to be sure
135 // we don't remove all conversation profiles when we are still processing SipMessages,
136 // since recon requires at least one to be present for inbound processing.
137 if(mConversationProfileHandle && !shuttingDownServer)
138 {
139 mServer.mMyUserAgent->destroyConversationProfile(mConversationProfileHandle);
140 mConversationProfileHandle = 0;
141 }
142 InfoLog(MOHLOG_PREFIX << "shutdown");
143}
144
145bool
147{
148 Lock lock(mMutex);
149 return profile.getHandle() == mConversationProfileHandle;
150}
151
152void
153MOHManager::addParticipant(ParticipantHandle participantHandle, const Uri& heldUri, const Uri& holdingUri)
154{
155 Lock lock(mMutex);
156 ConversationHandle conversationToUse = 0;
157 // Check if we have an existing conversation with room to add this party
158 ConversationMap::iterator it = mConversations.begin();
159 for(; it != mConversations.end(); it++)
160 {
161 if(it->second.size() < DEFAULT_BRIDGE_MAX_IN_OUTPUTS-3)
162 {
163 // Found an existing conversation with room - add the participant here
164 conversationToUse = it->first;
165 break;
166 }
167 }
168
169 // No conversation found that we can use - create a new one
170 if(!conversationToUse)
171 {
173 InfoLog(MOHLOG_PREFIX << "addParticipant: created new conversation for music on hold, id=" << conversationToUse);
174
175 // Play Music
176 mServer.createMediaResourceParticipant(conversationToUse, mMusicFilename);
177 }
178 else
179 {
180 InfoLog(MOHLOG_PREFIX << "addParticipant: using existing conversation for music on hold, id=" << conversationToUse);
181 }
182
183 resip_assert(conversationToUse);
184
185 mServer.addParticipant(conversationToUse, participantHandle);
186 mServer.modifyParticipantContribution(conversationToUse, participantHandle, 100, 0 /* Mute participant */);
187 mServer.answerParticipant(participantHandle);
188 mConversations[conversationToUse].insert(std::make_pair(participantHandle, new ParticipantMOHInfo(participantHandle, heldUri, holdingUri)));
189}
190
191bool
193{
194 Lock lock(mMutex);
195 // Find Conversation that participant is in
196 ConversationMap::iterator it = mConversations.begin();
197 for(; it != mConversations.end(); it++)
198 {
199 ParticipantMap::iterator partIt = it->second.find(participantHandle);
200 if(partIt != it->second.end())
201 {
202 InfoLog(MOHLOG_PREFIX << "removeParticipant: found in conversation id=" << it->first << ", size=" << it->second.size());
203
204 // Found! Remove from conversation
205 delete partIt->second;
206 it->second.erase(partIt);
207
208 // Check if conversation is now empty, and it's not the last conversation
209 if(it->second.size() == 0)
210 {
211 if(mConversations.size() > 1)
212 {
213 // Destroy conversation (and containing media participant)
214 mServer.destroyConversation(it->first);
215
216 // Remove Conversation from Map
217 mConversations.erase(it);
218
219 InfoLog(MOHLOG_PREFIX << "removeParticipant: last participant in conversation, destroying conversation, num conversations now=" << mConversations.size());
220 }
221 else if(mConversations.size() == 1 && mMusicFilenameChanged) // If the initial conversation is empty, and the music filename setting changed, then restart it
222 {
223 mServer.destroyConversation(mConversations.begin()->first);
224 mConversations.clear();
225
226 // re-create an initial conversation and start music
228 mServer.createMediaResourceParticipant(convHandle, mMusicFilename); // Play Music
229 mConversations[convHandle];
230 mMusicFilenameChanged = false;
231 }
232 }
233 return true;
234 }
235 }
236 return false;
237}
238
239void
241{
242 Lock lock(mMutex);
243 // Find Conversation that participant is in
244 ConversationMap::iterator it = mConversations.begin();
245 for(; it != mConversations.end(); it++)
246 {
247 ParticipantMap::iterator partIt = it->second.begin();
248 for(; partIt != it->second.end(); partIt++)
249 {
250 callInfos.push_back(ActiveCallInfo(partIt->second->mHeldUri, partIt->second->mHoldingUri, "MOH", partIt->first, it->first));
251 }
252 }
253}
254
255}
256
257/* ====================================================================
258
259 Copyright (c) 2021, SIP Spectrum, Inc.
260 Copyright (c) 2010-2016, SIP Spectrum, Inc.
261 All rights reserved.
262
263 Redistribution and use in source and binary forms, with or without
264 modification, are permitted provided that the following conditions are
265 met:
266
267 1. Redistributions of source code must retain the above copyright
268 notice, this list of conditions and the following disclaimer.
269
270 2. Redistributions in binary form must reproduce the above copyright
271 notice, this list of conditions and the following disclaimer in the
272 documentation and/or other materials provided with the distribution.
273
274 3. Neither the name of SIP Spectrum nor the names of its contributors
275 may be used to endorse or promote products derived from this
276 software without specific prior written permission.
277
278 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
279 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
280 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
281 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
282 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
283 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
284 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
285 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
286 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
287 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
288 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
289
290 ==================================================================== */
291
#define InfoLog(args_)
Definition Logger.hxx:131
#define MOHLOG_PREFIX
static const resip::ExtensionParameter p_automaton("automaton")
static const resip::ExtensionParameter p_rendering("+sip.rendering")
static const resip::ExtensionParameter p_byeless("+sip.byeless")
#define resip_assert(x)
This file needs to work for both C and C++ code.
MOHManager(Server &server)
bool removeParticipant(recon::ParticipantHandle participantHandle)
void startup(ConfigParser::MOHSettings &settings)
void addParticipant(recon::ParticipantHandle participantHandle, const resip::Uri &heldUri, const resip::Uri &holdingUri)
void shutdown(bool shuttingDownServer)
void getActiveCallsInfo(CallInfoList &callInfos)
void initializeSettings(const resip::Uri &musicFilename)
bool isMyProfile(recon::ConversationProfile &profile)
void initializeConversationProfile(const resip::NameAddr &uri, const resip::Data &password, unsigned long registrationTime, const resip::NameAddr &outboundProxy)
ConfigParser & mConfig
Definition Server.hxx:72
virtual ParticipantHandle createMediaResourceParticipant(ConversationHandle convHandle, const resip::Uri &mediaUrl, const std::shared_ptr< resip::Data > &playAudioBuffer=std::shared_ptr< resip::Data >(), void *recordingCircularBuffer=0)
Creates a new media resource participant in the specified conversation.
virtual void answerParticipant(ParticipantHandle partHandle)
Signal to the participant that the call is answered.
virtual void addParticipant(ConversationHandle convHandle, ParticipantHandle partHandle)
Adds the specified participant to the specified conversation.
virtual void destroyConversation(ConversationHandle convHandle)
Destroys an existing Conversation, and ends all participants that solely belong to this conversation.
virtual ConversationHandle createConversation(AutoHoldMode autoHoldMode=AutoHoldEnabled)
Create a new empty Conversation to which participants can be added.
virtual void modifyParticipantContribution(ConversationHandle convHandle, ParticipantHandle partHandle, unsigned int inputGain, unsigned int outputGain)
Modifies how the participant contributes to the particular conversation.
This class extends the resip UserProfile to include Conversation Manager specific settings.
virtual ConversationProfileHandle getHandle()
Get the conversation profile handle.
void destroyConversationProfile(ConversationProfileHandle handle)
Destroys an existing Conversation Profile.
ConversationProfileHandle addConversationProfile(std::shared_ptr< ConversationProfile > conversationProfile, bool defaultOutgoing=true)
Adds a Conversation Profile to be managed, by the user agent.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
bool empty() const noexcept
Checks whether the Data is empty.
Definition Data.hxx:591
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
Data & user()
Definition Uri.hxx:42
Data & host()
Definition Uri.hxx:40
std::list< ActiveCallInfo > CallInfoList
unsigned int ConversationHandle
unsigned int ParticipantHandle
dcm! – add UnusedChecking(_enum) below;
std::unique_lock< Mutex > Lock
A convenience class to lock a Lockable (such as a Mutex) on object creation, and unlock on destructio...
Definition Lock.hxx:28