reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
ParkManager.cxx
Go to the documentation of this file.
1#include "../UserAgent.hxx"
2#include "AppSubsystem.hxx"
3#include "ParkManager.hxx"
4#include "ParkOrbit.hxx"
5#include "Server.hxx"
6
9#include <rutil/Log.hxx>
10#include <rutil/Logger.hxx>
12
13#include <utility>
14
15using namespace recon;
16using namespace resip;
17using namespace std;
18
19#define RESIPROCATE_SUBSYSTEM AppSubsystem::MOHPARKSERVER
20
21#define PARKLOG_PREFIX << "ParkManager[" << mParkUri << "] "
22
23static const resip::ExtensionParameter p_orbit("orbit");
24static const resip::ExtensionParameter p_automaton("automaton");
25static const resip::ExtensionParameter p_byeless("+sip.byeless");
26static const resip::ExtensionParameter p_rendering("+sip.rendering");
27
28namespace mohparkserver
29{
30
32 mServer(server),
33 mConversationProfileHandle(0),
34 mOrbitRangeStart(0),
35 mNumOrbits(0),
36 mMaxParkTime(0)
37{
38}
39
43
44void
46{
47 // Initialize park settings
49
50 // Setup ConversationProfile - If Park setting specific outbound proxy is not specified, then use global
51 // outbound proxy setting.
53 !settings.mOutboundProxy.uri().host().empty() ? settings.mOutboundProxy : mServer.mConfig.mOutboundProxy);
54
55 // Create Orbit Profiles
57 !settings.mOutboundProxy.uri().host().empty() ? settings.mOutboundProxy : mServer.mConfig.mOutboundProxy);
58
59 InfoLog(PARKLOG_PREFIX << "startup: MOHFilenameUrl=" << settings.mMOHFilenameUrl);
60}
61
62void
63ParkManager::initializeConversationProfile(const NameAddr& uri, const Data& password, unsigned long registrationTime, const resip::NameAddr& outboundProxy)
64{
65 Lock lock(mMutex);
66
67 if(mConversationProfileHandle)
68 {
69 mServer.mMyUserAgent->destroyConversationProfile(mConversationProfileHandle);
70 mConversationProfileHandle = 0;
71 }
72
73 auto parkConversationProfile = std::make_shared<ConversationProfile>(mServer.mUserAgentMasterProfile);
74 parkConversationProfile->setDefaultRegistrationTime(registrationTime);
75 parkConversationProfile->setDefaultRegistrationRetryTime(120); // 2 mins
76 parkConversationProfile->setDefaultFrom(uri);
77 parkConversationProfile->setDigestCredential(uri.uri().host(), uri.uri().user(), password);
78 if(!outboundProxy.uri().host().empty())
79 {
80 parkConversationProfile->setOutboundProxy(outboundProxy.uri());
81 }
82 parkConversationProfile->challengeOODReferRequests() = false;
83 parkConversationProfile->setExtraHeadersInReferNotifySipFragEnabled(true); // Enable dialog identifying headers in SipFrag bodies of Refer Notifies
84 NameAddr capabilities;
85 capabilities.param(p_automaton);
86 capabilities.param(p_byeless);
87 capabilities.param(p_rendering) = "\"no\"";
88 parkConversationProfile->setUserAgentCapabilities(capabilities); // Same as above
89 parkConversationProfile->natTraversalMode() = ConversationProfile::NoNatTraversal;
90 parkConversationProfile->secureMediaMode() = ConversationProfile::NoSecureMedia;
91 mServer.buildSessionCapabilities(parkConversationProfile->sessionCaps());
92 mConversationProfileHandle = mServer.mMyUserAgent->addConversationProfile(parkConversationProfile);
93 mParkUri = uri;
94}
95
96void
98 unsigned long numOrbits,
99 const NameAddr& uri,
100 const Data& password,
101 unsigned long registrationTime,
102 const resip::NameAddr& outboundProxy)
103{
104 Lock lock(mMutex);
105
106 // Remove current Profiles (if set)
107 OrbitProfileMap::iterator itOrbit = mOrbitProfiles.begin();
108 for(;itOrbit!=mOrbitProfiles.end();itOrbit++)
109 {
110 mServer.mMyUserAgent->destroyConversationProfile(itOrbit->second);
111 }
112 mOrbitProfiles.clear();
113
114 // Store orbit range values
115 mOrbitRangeStart = orbitStart;
116 mNumOrbits = numOrbits;
117
118 // Clear free list and rebuild it
119 mFreeOrbitList.clear();
120
121 for(unsigned long orbit = mOrbitRangeStart; orbit < mOrbitRangeStart + mNumOrbits; orbit++)
122 {
123 auto orbitConversationProfile = std::make_shared<ConversationProfile>(mServer.mUserAgentMasterProfile);
124 Data orbitData((uint64_t)orbit);
125 orbitConversationProfile->setDefaultRegistrationTime(registrationTime);
126 orbitConversationProfile->setDefaultRegistrationRetryTime(120); // 2 mins
127 orbitConversationProfile->setDefaultFrom(uri);
128 orbitConversationProfile->getDefaultFrom().uri().user() = orbitData;
129 orbitConversationProfile->setDigestCredential(uri.uri().host(), orbitData, password);
130 if(!outboundProxy.uri().host().empty())
131 {
132 orbitConversationProfile->setOutboundProxy(outboundProxy.uri());
133 }
134 orbitConversationProfile->challengeOODReferRequests() = false;
135 orbitConversationProfile->setExtraHeadersInReferNotifySipFragEnabled(true); // Enable dialog identifying headers in SipFrag bodies of Refer Notifies
136 NameAddr capabilities;
137 capabilities.param(p_automaton);
138 capabilities.param(p_byeless);
139 capabilities.param(p_rendering) = "\"no\"";
140 orbitConversationProfile->setUserAgentCapabilities(capabilities); // Same as above
141 orbitConversationProfile->natTraversalMode() = ConversationProfile::NoNatTraversal;
142 orbitConversationProfile->secureMediaMode() = ConversationProfile::NoSecureMedia;
143 mServer.buildSessionCapabilities(orbitConversationProfile->sessionCaps());
144 mOrbitProfiles[orbit] = mServer.mMyUserAgent->addConversationProfile(orbitConversationProfile);
145
146 // If orbit is free - add to free list
147 if(mOrbits.find(orbit) == mOrbits.end())
148 {
149 mFreeOrbitList.push_back(orbit);
150 }
151 }
152}
153
154void
155ParkManager::initializeParkSettings(unsigned long maxParkTime, const resip::Uri& musicFilename)
156{
157 Lock lock(mMutex);
158 mMaxParkTime = maxParkTime;
159 mMusicFilename = musicFilename;
160}
161
162void
163ParkManager::shutdown(bool shuttingDownServer)
164{
165 Lock lock(mMutex);
166 OrbitMap::iterator it = mOrbits.begin();
167 for(;it!=mOrbits.end();it++)
168 {
169 // Delete all Orbit objects
170 delete it->second;
171 }
172 mOrbits.clear();
173 mOrbitsByParticipant.clear();
174
175 // If shutting down server, then we shouldn't remove the conversation profiles here
176 // shutting down the ConversationManager will take care of this. We need to be sure
177 // we don't remove all conversation profiles when we are still processing SipMessages,
178 // since recon requires at least one to be present for inbound processing.
179 if(!shuttingDownServer)
180 {
181 // Destroy main Park profile
182 if(mConversationProfileHandle)
183 {
184 mServer.mMyUserAgent->destroyConversationProfile(mConversationProfileHandle);
185 mConversationProfileHandle = 0;
186 }
187
188 // Destroy Orbit Profiles
189 OrbitProfileMap::iterator itOrbit = mOrbitProfiles.begin();
190 for(;itOrbit!=mOrbitProfiles.end();itOrbit++)
191 {
192 mServer.mMyUserAgent->destroyConversationProfile(itOrbit->second);
193 }
194 mOrbitProfiles.clear();
195 }
196 InfoLog(PARKLOG_PREFIX << "shutdown");
197}
198
199bool
201{
202 Lock lock(mMutex);
203 if(profile.getHandle() == mConversationProfileHandle)
204 {
205 return true;
206 }
207 else
208 {
209 // check orbit profiles
210 OrbitProfileMap::iterator it = mOrbitProfiles.begin();
211 for(;it!=mOrbitProfiles.end();it++)
212 {
213 if(profile.getHandle() == it->second)
214 {
215 return true;
216 }
217 }
218 }
219 return false;
220}
221
222ParkOrbit*
223ParkManager::getOrbit(unsigned long orbit)
224{
225 resip_assert((orbit >= mOrbitRangeStart) &&
226 (orbit < (mOrbitRangeStart + mNumOrbits)));
227
228 // Check if Orbit is created or not yet
229 OrbitMap::iterator it = mOrbits.find(orbit);
230 ParkOrbit* parkOrbit = 0;
231 if(it == mOrbits.end())
232 {
233 // Create Orbit
234 parkOrbit = new ParkOrbit(mServer, orbit, mMaxParkTime, mMusicFilename);
235 mOrbits[orbit] = parkOrbit;
236
237 // Remove from free list
238 std::deque<unsigned long>::iterator itFree = mFreeOrbitList.begin();
239 for(;itFree!=mFreeOrbitList.end();itFree++)
240 {
241 if(orbit == *itFree)
242 {
243 mFreeOrbitList.erase(itFree);
244 break;
245 }
246 }
247 }
248 else
249 {
250 // Use existing
251 parkOrbit = it->second;
252 }
253 return parkOrbit;
254}
255
256ParkOrbit*
257ParkManager::getOrbitByParticipant(recon::ParticipantHandle participantHandle)
258{
259 OrbitsByParticipantMap::iterator it = mOrbitsByParticipant.find(participantHandle);
260 if(it != mOrbitsByParticipant.end())
261 {
262 return it->second;
263 }
264 return 0;
265}
266
267bool
268ParkManager::addParticipantToOrbit(ParkOrbit* orbit, recon::ParticipantHandle participantHandle, const resip::Uri& parkedUri, const resip::Uri& parkerUri)
269{
270 if(orbit->addParticipant(participantHandle, parkedUri, parkerUri))
271 {
272 mOrbitsByParticipant[participantHandle] = orbit; // add participant to orbit index
273 return true;
274 }
275 return false;
276}
277
278void
280{
281 Lock lock(mMutex);
282
283 unsigned long orbit = 0;
284 resip_assert(msg.method() == REFER);
285
286 // Check if Orbit parameter has been specified on the To header
287 if(msg.header(h_To).uri().exists(p_orbit))
288 {
289 orbit = msg.header(h_To).uri().param(p_orbit).convertUnsignedLong();
290 }
291
292 if((orbit >= mOrbitRangeStart) &&
293 (orbit < (mOrbitRangeStart + mNumOrbits)))
294 {
295 // Park call at specified orbit
296 ParkOrbit* parkOrbit = getOrbit(orbit);
297 resip_assert(parkOrbit);
298 addParticipantToOrbit(parkOrbit, participantHandle, msg.header(h_ReferTo).uri().getAorAsUri(), msg.header(h_From).uri());
299 InfoLog(PARKLOG_PREFIX << "parkParticipant: parked at orbit=" << orbit);
300 }
301 else
302 {
303 // If no orbit was specified, or specified number is bad - select a free orbit, and redirect request to use newly allocated orbit
304 if(mFreeOrbitList.size() > 0)
305 {
306 unsigned long freeorbit = mFreeOrbitList.front();
307 // Move free item to end of list, to reduce chance it will be double allocated
308 mFreeOrbitList.pop_front();
309 mFreeOrbitList.push_back(freeorbit);
310 InfoLog(PARKLOG_PREFIX << "parkParticipant: no valid orbit specified (orbit=" << orbit << ") redirecting to free orbit=" << freeorbit);
311 NameAddr destination(mParkUri);
312 destination.uri().param(p_orbit) = Data((uint64_t)freeorbit);
313 mServer.redirectParticipant(participantHandle, destination);
314 }
315 else
316 {
317 // No free orbits
318 WarningLog(PARKLOG_PREFIX << "parkParticipant: no free orbits available, rejecing with 486 busy.");
319 mServer.rejectParticipant(participantHandle, 486 /* Busy */);
320 }
321 }
322}
323
324void
326{
327 Lock lock(mMutex);
328
329 // Get orbit number, either from To Uri parameter, or To user
330 unsigned long orbit = 0;
331 if(msg.header(h_To).uri().exists(p_orbit))
332 {
333 orbit = msg.header(h_To).uri().param(p_orbit).convertUnsignedLong();
334 }
335 else
336 {
337 orbit = msg.header(h_To).uri().user().convertUnsignedLong();
338 }
339 if((orbit >= mOrbitRangeStart) &&
340 (orbit < (mOrbitRangeStart + mNumOrbits)))
341 {
342 // Check if this is a direct call, or a transferred call. We will allow transferred calls to be parked, as an alternative parking method
343 if(msg.exists(h_ReferredBy))
344 {
345 // If a Referred-By header is present then this was a transferred call - park it
346 // Park call at specified orbit
347 ParkOrbit* parkOrbit = getOrbit(orbit);
348 resip_assert(parkOrbit);
349 addParticipantToOrbit(parkOrbit, participantHandle, msg.header(h_From).uri(), msg.header(h_ReferredBy).uri());
350 InfoLog(PARKLOG_PREFIX << "incomingParticipant: parked at orbit=" << orbit);
351 }
352 else // Direct call - retrieval attempt
353 {
354 // Orbit is valid - see if we have a call parked there
355 OrbitMap::iterator it = mOrbits.find(orbit);
356 ParticipantHandle participantToRetrieve = 0;
357 if(it!=mOrbits.end() && (participantToRetrieve = it->second->getNextQueuedParticipant()) != 0)
358 {
359 // Answer incoming call and then immediately redirect to parked call
360 mServer.addParticipant(it->second->getConversationHandle(), participantHandle);
361 mServer.modifyParticipantContribution(it->second->getConversationHandle(), participantHandle, 0, 0 /* Mute participant */);
362 mServer.answerParticipant(participantHandle);
363 mServer.redirectToParticipant(participantHandle, participantToRetrieve);
364
365 InfoLog(PARKLOG_PREFIX << "incomingParticipant: retrieving participant " << participantToRetrieve << " from orbit " << orbit);
366 }
367 else
368 {
369 WarningLog(PARKLOG_PREFIX << "incomingParticipant: orbit " << orbit << " has no call to retrieve, rejecting with 404.");
370 mServer.rejectParticipant(participantHandle, 404 /* Not Found */);
371 }
372 }
373 }
374 else
375 {
376 WarningLog(PARKLOG_PREFIX << "incomingParticipant: valid orbit not found in To header (" << msg.header(h_To).uri() << "), rejecting with 404.");
377 mServer.rejectParticipant(participantHandle, 404 /* Not Found */);
378 }
379}
380
381bool
383{
384 Lock lock(mMutex);
385 ParkOrbit* orbit = getOrbitByParticipant(participantHandle);
386 if(orbit)
387 {
388 mOrbitsByParticipant.erase(participantHandle); // Remove participant from orbit index
389 if(orbit->removeParticipant(participantHandle))
390 {
391 InfoLog(PARKLOG_PREFIX << "removeParticipant: from orbit " << orbit->getOrbit() << " with " << orbit->getNumParticipants() << " remaining participants");
392 if(orbit->getNumParticipants() == 0)
393 {
394 // Last participant just left orbit - destroy it
395 unsigned long orbitNum = orbit->getOrbit();
396 if((orbitNum >= mOrbitRangeStart) &&
397 (orbitNum < (mOrbitRangeStart + mNumOrbits)))
398 {
399 // Only add back to free list, if in currently configured range
400 mFreeOrbitList.push_back(orbitNum);
401 }
402 delete orbit;
403 mOrbits.erase(orbitNum);
404 }
405 return true;
406 }
407 }
408 return false;
409}
410
411void
413{
414 Lock lock(mMutex);
415
416 OrbitMap::iterator it = mOrbits.begin();
417 for(; it != mOrbits.end(); it++)
418 {
419 ParkOrbit::ParticipantQueue::iterator it2 = it->second->mParticipants.begin();
420 for(; it2 != it->second->mParticipants.end(); it2++)
421 {
422 callInfos.push_back(ActiveCallInfo((*it2)->mParkedUri,
423 (*it2)->mParkerUri,
424 Data("Parked at " + Data((uint64_t)(it->first))),
425 (*it2)->mParticipantHandle,
426 it->second->mConversationHandle));
427 }
428 }
429}
430
431bool
433{
434 Lock lock(mMutex);
435
436 // Try to see if participant is still around
437 ParkOrbit* orbit = getOrbitByParticipant(participantHandle);
438 if(orbit)
439 {
440 orbit->onMaxParkTimeout(participantHandle);
441 return true;
442 }
443 return false;
444}
445
446
447}
448
449/* ====================================================================
450
451 Copyright (c) 2011-2016, SIP Spectrum, Inc.
452 All rights reserved.
453
454 Redistribution and use in source and binary forms, with or without
455 modification, are permitted provided that the following conditions are
456 met:
457
458 1. Redistributions of source code must retain the above copyright
459 notice, this list of conditions and the following disclaimer.
460
461 2. Redistributions in binary form must reproduce the above copyright
462 notice, this list of conditions and the following disclaimer in the
463 documentation and/or other materials provided with the distribution.
464
465 3. Neither the name of SIP Spectrum nor the names of its contributors
466 may be used to endorse or promote products derived from this
467 software without specific prior written permission.
468
469 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
470 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
471 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
472 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
473 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
474 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
475 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
476 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
477 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
478 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
479 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
480
481 ==================================================================== */
482
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
static const resip::ExtensionParameter p_automaton("automaton")
static const resip::ExtensionParameter p_rendering("+sip.rendering")
static const resip::ExtensionParameter p_byeless("+sip.byeless")
static const resip::ExtensionParameter p_automaton("automaton")
static const resip::ExtensionParameter p_orbit("orbit")
#define PARKLOG_PREFIX
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.
bool onMaxParkTimeout(recon::ParticipantHandle participantHandle)
void parkParticipant(recon::ParticipantHandle participantHandle, const resip::SipMessage &msg)
void shutdown(bool shuttingDownServer)
void getActiveCallsInfo(CallInfoList &callInfos)
ParkManager(Server &server)
void initializeParkSettings(unsigned long maxParkTime, const resip::Uri &musicFilename)
void incomingParticipant(recon::ParticipantHandle participantHandle, const resip::SipMessage &msg)
bool removeParticipant(recon::ParticipantHandle participantHandle)
void startup(ConfigParser::ParkSettings &settings)
void initializeOrbitConversationProfiles(unsigned long orbitStart, unsigned long numOrbits, const resip::NameAddr &uri, const resip::Data &password, unsigned long registrationTime, const resip::NameAddr &outboundProxy)
bool isMyProfile(recon::ConversationProfile &profile)
void initializeConversationProfile(const resip::NameAddr &uri, const resip::Data &password, unsigned long registrationTime, const resip::NameAddr &outboundProxy)
bool onMaxParkTimeout(recon::ParticipantHandle participantHandle)
unsigned long getNumParticipants()
Definition ParkOrbit.hxx:34
bool removeParticipant(recon::ParticipantHandle participantHandle)
Definition ParkOrbit.cxx:68
unsigned long getOrbit()
Definition ParkOrbit.hxx:33
ConfigParser & mConfig
Definition Server.hxx:72
virtual void redirectParticipant(ParticipantHandle partHandle, const resip::NameAddr &destination, unsigned int redirectCode=302, RedirectSuccessCondition successCondition=RedirectSuccessOnConnected)
Redirects the participant to another endpoint.
virtual void answerParticipant(ParticipantHandle partHandle)
Signal to the participant that the call is answered.
virtual void rejectParticipant(ParticipantHandle partHandle, unsigned int rejectCode)
Rejects an incoming remote participant with the specified code.
virtual void redirectToParticipant(ParticipantHandle partHandle, ParticipantHandle destPartHandle, RedirectSuccessCondition successCondition=RedirectSuccessOnConnected)
This is used for attended transfer scenarios where both participants are no longer managed by the con...
virtual void addParticipant(ConversationHandle convHandle, ParticipantHandle partHandle)
Adds the specified participant to the specified conversation.
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
unsigned long convertUnsignedLong() const
Definition Data.cxx:1724
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.
const Uri & uri() const
Represents a SIP message.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
resip::MethodTypes method() const
returns the method type of the message
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
bool exists(const Param< Uri > &paramType) const
Definition Uri.cxx:1701
Data & user()
Definition Uri.hxx:42
Data & host()
Definition Uri.hxx:40
Uri getAorAsUri(TransportType transportTypeToRemoveDefaultPort=UNKNOWN_TRANSPORT) const
Definition Uri.cxx:1103
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
std::list< ActiveCallInfo > CallInfoList
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
unsigned __int64 uint64_t
Definition stdint.h:95