reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
RemoteParticipantDialogSet.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
6#include "ReconSubsystem.hxx"
9#include "Conversation.hxx"
10#include "UserAgent.hxx"
11#include "MediaStreamEvent.hxx"
12
13#include <rutil/Log.hxx>
14#include <rutil/Logger.hxx>
15#include <rutil/Random.hxx>
16#include <rutil/DnsUtil.hxx>
20
22
23#include <utility>
24
25using namespace recon;
26using namespace resip;
27using namespace std;
28
29#define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
30
33 std::shared_ptr<ConversationProfile> conversationProfile) :
34 AppDialogSet(conversationManager.getUserAgent()->getDialogUsageManager()),
35 mConversationManager(conversationManager),
36 mActiveRemoteParticipantHandle(0),
37 mUACOriginalRemoteParticipant(0),
38 mNumDialogs(0),
39 mForkSelectMode(forkSelectMode),
40 mConversationProfile(conversationProfile),
41 mUACConnectedDialogId(Data::Empty, Data::Empty, Data::Empty),
42 mProposedSdp(0),
43 mSecureMediaMode(ConversationProfile::NoSecureMedia),
44 mSecureMediaRequired(false)
45{
46
47 InfoLog(<< "RemoteParticipantDialogSet created.");
48}
49
51{
52 // If we have no dialogs and mUACOriginalRemoteParticipant is set, then we have not passed
53 // ownership of mUACOriginalRemoteParticipant to DUM - so we need to delete the participant
54 if(mNumDialogs == 0 && mUACOriginalRemoteParticipant)
55 {
56 delete mUACOriginalRemoteParticipant;
57 }
58
59 InfoLog(<< "RemoteParticipantDialogSet destroyed. mActiveRemoteParticipantHandle=" << mActiveRemoteParticipantHandle);
60}
61
62std::shared_ptr<UserProfile>
67
68std::shared_ptr<ConversationProfile>
70{
71 // UAS Dialogs should have a user profile at this point from the stack calling selectUASUserProfile (above)
72 // For UAC dialogs, we may need to use the locally stored ConversationProfile, at least until the resip
73 // DialogSet is created. If a ConversationProfile wasn't provided for the UAC call then we need to
74 // get default outgoing ConversationProfile.
75 if (getUserProfile())
76 {
77 return dynamic_pointer_cast<ConversationProfile>(getUserProfile());
78 }
79 if (mConversationProfile)
80 {
81 DebugLog(<< "RemoteParticipantDialogSet::getConversationProfile: using locally stored ConversationProfile for UAC");
82 return mConversationProfile;
83 }
84 else
85 {
86 DebugLog(<< "RemoteParticipantDialogSet::getConversationProfile: no locally stored ConversationProfile, falling back to default for UAC");
88 }
89}
90
91void
92RemoteParticipantDialogSet::processMediaStreamReadyEvent(std::shared_ptr<MediaStreamReadyEvent::StreamParams> streamParams)
93{
94 //InfoLog( << "processMediaStreamReadyEvent: rtpTuple=" << rtpTuple << " rtcpTuple=" << rtcpTuple);
95
96 if(mPendingInvite.get() != 0)
97 {
98 // Pending Invite Request
99 doSendInvite(mPendingInvite);
100 mPendingInvite.reset();
101 }
102
103 if(mPendingOfferAnswer.mSdp != nullptr)
104 {
105 // Pending Offer or Answer
106 doProvideOfferAnswer(mPendingOfferAnswer.mOffer,
107 std::move(mPendingOfferAnswer.mSdp),
108 mPendingOfferAnswer.mInviteSessionHandle,
109 mPendingOfferAnswer.mPostOfferAnswerAccept,
110 mPendingOfferAnswer.mPostAnswerAlert);
111 resip_assert(mPendingOfferAnswer.mSdp == nullptr);
112 }
113}
114
115void
117{
118 InfoLog( << "processMediaStreamErrorEvent, error=" << errorCode);
119
120 // Note: in the case of an initial invite we must issue the invite in order for dum to cleanup state
121 // properly - this is not ideal, since it may cause endpoint phone device to ring a little
122 // before receiving the cancel
123 if(mPendingInvite.get() != 0)
124 {
125 // Pending Invite Request - Falling back to using local address/port - but then end() immediate
126 doSendInvite(mPendingInvite);
127 mPendingInvite.reset();
128 }
129
130 // End call
131 if(mNumDialogs > 0)
132 {
133 std::map<DialogId, RemoteParticipant*>::iterator it;
134 for(it = mDialogs.begin(); it != mDialogs.end(); it++)
135 {
136 it->second->destroyParticipant();
137 }
138 }
139 else
140 {
141 end();
142 }
143}
144
145void
146RemoteParticipantDialogSet::sendInvite(std::shared_ptr<SipMessage> invite)
147{
148 if(!isAsyncMediaSetup())
149 {
150 doSendInvite(invite);
151 }
152 else
153 {
154 // Wait until media stream is ready
155 mPendingInvite = invite;
156 }
157}
158
159void
160RemoteParticipantDialogSet::doSendInvite(std::shared_ptr<SipMessage> invite)
161{
162 // Fix up address and port in SDP if we have remote info
163 // Note: the only time we don't is if there was an error preparing the media stream
164 if(!isAsyncMediaSetup())
165 {
166 SdpContents* sdp = dynamic_cast<SdpContents*>(invite->getContents());
167 fixUpSdp(sdp);
168 }
169
170 // Send the invite
171 mDum.send(invite);
172}
173
174void
175RemoteParticipantDialogSet::provideOffer(std::unique_ptr<resip::SdpContents> offer, resip::InviteSessionHandle& inviteSessionHandle, bool postOfferAccept)
176{
177 if(!isAsyncMediaSetup())
178 {
179 doProvideOfferAnswer(true /* offer */, std::move(offer), inviteSessionHandle, postOfferAccept, false);
180 }
181 else
182 {
183 resip_assert(mPendingOfferAnswer.mSdp == nullptr);
184 mPendingOfferAnswer.mOffer = true;
185 mPendingOfferAnswer.mSdp = std::move(offer);
186 mPendingOfferAnswer.mInviteSessionHandle = inviteSessionHandle;
187 mPendingOfferAnswer.mPostOfferAnswerAccept = postOfferAccept;
188 mPendingOfferAnswer.mPostAnswerAlert = false;
189 }
190}
191
192void
193RemoteParticipantDialogSet::provideAnswer(std::unique_ptr<resip::SdpContents> answer, resip::InviteSessionHandle& inviteSessionHandle, bool postAnswerAccept, bool postAnswerAlert)
194{
195 if(!isAsyncMediaSetup())
196 {
197 doProvideOfferAnswer(false /* offer */, std::move(answer), inviteSessionHandle, postAnswerAccept, postAnswerAlert);
198 }
199 else
200 {
201 resip_assert(mPendingOfferAnswer.mSdp == nullptr);
202 mPendingOfferAnswer.mOffer = false;
203 mPendingOfferAnswer.mSdp = std::move(answer);
204 mPendingOfferAnswer.mInviteSessionHandle = inviteSessionHandle;
205 mPendingOfferAnswer.mPostOfferAnswerAccept = postAnswerAccept;
206 mPendingOfferAnswer.mPostAnswerAlert = postAnswerAlert;
207 }
208}
209
210void
211RemoteParticipantDialogSet::doProvideOfferAnswer(bool offer, std::unique_ptr<resip::SdpContents> sdp, resip::InviteSessionHandle& inviteSessionHandle, bool postOfferAnswerAccept, bool postAnswerAlert)
212{
213 if(inviteSessionHandle.isValid() && !inviteSessionHandle->isTerminated())
214 {
215 // Fix up address and port in SDP if we have remote info
216 // Note: the only time we don't is if there was an error preparing the media stream
217 if(!isAsyncMediaSetup())
218 {
219 fixUpSdp(sdp.get());
220 }
221
222 if(offer)
223 {
224 inviteSessionHandle->provideOffer(*sdp);
225 }
226 else
227 {
228 inviteSessionHandle->provideAnswer(*sdp);
229 }
230
231 // Adjust RTP Streams
232 RemoteParticipant* rp = dynamic_cast<RemoteParticipant*>(inviteSessionHandle->getAppDialog().get());
233 rp->adjustRTPStreams(offer);
234
235 // Do Post Answer Operations
236 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(inviteSessionHandle.get());
237 if(sis)
238 {
239 if(postAnswerAlert)
240 {
241 sis->provisional(180,true);
242 }
243 if(postOfferAnswerAccept)
244 {
245 sis->accept();
246 if(rp->getState() == RemoteParticipant::Connecting)
247 {
248 rp->stateTransition(RemoteParticipant::Accepted);
249 }
250 }
251 }
252 }
253}
254
255void
257{
258 // If we have a pending answer, then just flag to accept when complete
259 if(mPendingOfferAnswer.mSdp != nullptr &&
260 !mPendingOfferAnswer.mOffer)
261 {
262 mPendingOfferAnswer.mPostOfferAnswerAccept = true;
263 }
264 else if(inviteSessionHandle.isValid())
265 {
266 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(inviteSessionHandle.get());
267 if(sis)
268 {
269 sis->accept();
270 }
271 }
272}
273
276{
277 resip_assert(!mUACOriginalRemoteParticipant);
279 mUACOriginalRemoteParticipant = participant;
280 setActiveRemoteParticipantHandle(participant->getParticipantHandle()); // Store this since it may not be safe to access mUACOriginalRemoteParticipant pointer after corresponding Dialog has been created
281 return participant;
282}
283
284AppDialog*
286{
287 mNumDialogs++;
288
289 if(mUACOriginalRemoteParticipant) // UAC DialogSet
290 {
291 // Need to either return participant already created, or create a new one.
292 if(mNumDialogs > 1)
293 {
294 // forking occured and we now have multiple dialogs in this dialog set
296
297 InfoLog(<< "Forking occurred for original UAC participant handle=" << mUACOriginalRemoteParticipant->getParticipantHandle() <<
298 " this is leg number " << mNumDialogs << " new handle=" << participant->getParticipantHandle());
299
300 // Create Related Conversations for each conversation UACOriginalRemoteParticipant was in when first Dialog is created
301 std::list<ConversationHandle>::iterator it;
302 for(it = mUACOriginalConversationHandles.begin(); it != mUACOriginalConversationHandles.end(); it++)
303 {
305 if(conversation)
306 {
308 }
309 }
310
311 mDialogs[DialogId(msg)] = participant;
312 return participant;
313 }
314 else
315 {
316 // Get Conversations from Remote Participant and store - used later for creating related conversations
317 const Participant::ConversationMap& conversations = mUACOriginalRemoteParticipant->getConversations();
318 Participant::ConversationMap::const_iterator it;
319 for(it = conversations.begin(); it != conversations.end(); it++)
320 {
321 mUACOriginalConversationHandles.push_back(it->second->getHandle());
322 }
323
324 mDialogs[DialogId(msg)] = mUACOriginalRemoteParticipant;
325 return mUACOriginalRemoteParticipant;
326 }
327 }
328 else
329 {
332 mDialogs[DialogId(msg)] = participant; // Note: !slg! DialogId is not quite right here, since there is no To Tag on the INVITE
333 return participant;
334 }
335}
336
338{
340
341 std::map<DialogId, RemoteParticipant*>::iterator it;
342 for (it = mDialogs.begin(); it != mDialogs.end(); it++)
343 {
344 // It can take up to 32 seconds for a cancelled leg Dialog/DialogSet to actually get destroyed, we
345 // don't want to make recon users wait this time, so signal the participant as destroyed immediately
346 // and let DUM handle the rest in the background.
347 it->second->notifyTerminating();
348
349 // Destroy all related conversations
350 if (it->second->getParticipantHandle() != requestingParticipant)
351 {
352 InfoLog(<< "Ending Participant=" << requestingParticipant << " with related dialogs, ending conversations for related participant=" << it->second->getParticipantHandle());
353 it->second->destroyConversations();
354 }
355 }
356}
357
358void
360{
361 InfoLog(<< "setProposedSdp: handle=" << handle << ", proposedSdp=" << sdp);
362 mProposedSdp.reset(new SdpContents(sdp));
363}
364
365void
367{
368 resip_assert(mUACConnectedDialogId.getCallId().empty());
369 mUACConnectedDialogId = dialogId;
371 if(mForkSelectMode == ConversationManager::ForkSelectAutomatic ||
373 {
374 std::map<DialogId, RemoteParticipant*>::iterator it;
375 for(it = mDialogs.begin(); it != mDialogs.end(); it++)
376 {
377 if(it->first != dialogId)
378 {
379 InfoLog(<< "Connected to forked leg " << dialogId << " - stale dialog " << it->first << " and related conversation(s) will be ended.");
380 it->second->destroyConversations();
381 }
382 }
383 }
384}
385
386bool
388{
389 return !mUACConnectedDialogId.getCallId().empty();
390}
391
392bool
394{
395 return (!mUACConnectedDialogId.getCallId().empty() && dialogId != mUACConnectedDialogId);
396}
397
398void
400{
401 std::map<DialogId, RemoteParticipant*>::iterator it = mDialogs.find(dialogId);
402 if(it != mDialogs.end())
403 {
404 if(it->second == mUACOriginalRemoteParticipant) mUACOriginalRemoteParticipant = 0;
405 mDialogs.erase(it);
406 }
407
408 // If we have no more dialogs and we never went connected - make sure we cancel the Invite transaction
409 if(mDialogs.size() == 0 && !isUACConnected())
410 {
411 end();
412 }
413}
414
417{
418 return mForkSelectMode;
419}
420
421void
426
427
429// DialogSetHandler ///////////////////////////////////////////////////
431void
433{
434 if(!isUACConnected() && mUACOriginalRemoteParticipant)
435 {
436 InfoLog(<< "onTrying: handle=" << mUACOriginalRemoteParticipant->getParticipantHandle() << ", " << msg.brief());
437 if (mUACOriginalRemoteParticipant->getParticipantHandle()) mConversationManager.onParticipantProceeding(mUACOriginalRemoteParticipant->getParticipantHandle(), msg);
438 }
439}
440
441void
443{
444 resip_assert(msg.header(h_StatusLine).responseCode() != 100);
445 // It possible to get a provisional from another fork after receiving a 200 - if so, don't generate an event
446 if(!isUACConnected() && mUACOriginalRemoteParticipant)
447 {
448 InfoLog(<< "onNonDialogCreatingProvisional: handle=" << mUACOriginalRemoteParticipant->getParticipantHandle() << ", " << msg.brief());
449 if(mUACOriginalRemoteParticipant->getParticipantHandle()) mConversationManager.onParticipantAlerting(mUACOriginalRemoteParticipant->getParticipantHandle(), msg);
450 }
451}
452
453
454/* ====================================================================
455
456 Copyright (c) 2021-2026, SIP Spectrum, Inc. www.sipspectrum.com
457 Copyright (c) 2021, Daniel Pocock https://danielpocock.com
458 Copyright (c) 2007-2008, Plantronics, Inc.
459 All rights reserved.
460
461 Redistribution and use in source and binary forms, with or without
462 modification, are permitted provided that the following conditions are
463 met:
464
465 1. Redistributions of source code must retain the above copyright
466 notice, this list of conditions and the following disclaimer.
467
468 2. Redistributions in binary form must reproduce the above copyright
469 notice, this list of conditions and the following disclaimer in the
470 documentation and/or other materials provided with the distribution.
471
472 3. Neither the name of Plantronics nor the names of its contributors
473 may be used to endorse or promote products derived from this
474 software without specific prior written permission.
475
476 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
477 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
478 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
479 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
480 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
481 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
482 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
483 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
484 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
485 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
486 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
487
488 ==================================================================== */
#define DebugLog(args_)
Definition Logger.hxx:128
#define InfoLog(args_)
Definition Logger.hxx:131
#define resip_assert(x)
This file needs to work for both C and C++ code.
This class is one of two main classes of concern to an application using the UserAgent library.
virtual void onParticipantAlerting(ParticipantHandle partHandle, const resip::SipMessage &msg)=0
Notifies an application that a remote participant call attempt is alerting the remote party.
Conversation * getConversation(ConversationHandle convHandle)
virtual RemoteParticipant * createAppropriateRemoteParticipantInstance(resip::DialogUsageManager &dum, RemoteParticipantDialogSet &rpds)
virtual void onParticipantProceeding(ParticipantHandle partHandle, const resip::SipMessage &msg)
Notifies an application that a remote participant call attempt is proceeding at the first hop.
This class extends the resip UserProfile to include Conversation Manager specific settings.
This class is used to manage membership of participants.
void createRelatedConversation(RemoteParticipant *newForkedParticipant, ParticipantHandle origParticipantHandle)
std::map< ConversationHandle, Conversation * > ConversationMap
virtual ParticipantHandle getParticipantHandle()
const ConversationMap & getConversations()
virtual void endIncludeRelated(ParticipantHandle requestingParticipant)
virtual std::shared_ptr< resip::UserProfile > selectUASUserProfile(const resip::SipMessage &)
virtual void removeDialog(const resip::DialogId &dialogId)
std::shared_ptr< ConversationProfile > getConversationProfile()
void provideOffer(std::unique_ptr< resip::SdpContents > offer, resip::InviteSessionHandle &inviteSessionHandle, bool postOfferAccept)
virtual void setActiveRemoteParticipantHandle(ParticipantHandle handle)
virtual void processMediaStreamErrorEvent(unsigned int errorCode)
virtual void setProposedSdp(ParticipantHandle handle, const resip::SdpContents &sdp)
virtual resip::AppDialog * createAppDialog(const resip::SipMessage &msg)
virtual void onNonDialogCreatingProvisional(resip::AppDialogSetHandle, const resip::SipMessage &msg)
virtual void onTrying(resip::AppDialogSetHandle, const resip::SipMessage &msg)
virtual RemoteParticipant * createUACOriginalRemoteParticipant(ParticipantHandle handle)
RemoteParticipantDialogSet(ConversationManager &conversationManager, ConversationManager::ParticipantForkSelectMode forkSelectMode=ConversationManager::ForkSelectAutomatic, std::shared_ptr< ConversationProfile > conversationProfile=nullptr)
virtual void fixUpSdp(resip::SdpContents *sdp)=0
void sendInvite(std::shared_ptr< resip::SipMessage > invite)
void accept(resip::InviteSessionHandle &inviteSessionHandle)
virtual bool isStaleFork(const resip::DialogId &dialogId)
std::map< resip::DialogId, RemoteParticipant * > mDialogs
virtual void setUACConnected(const resip::DialogId &dialogId, ParticipantHandle partHandle)
void provideAnswer(std::unique_ptr< resip::SdpContents > answer, resip::InviteSessionHandle &inviteSessionHandle, bool postAnswerAccept, bool postAnswerAlert)
virtual void processMediaStreamReadyEvent(std::shared_ptr< MediaStreamReadyEvent::StreamParams > streamParams)
virtual ConversationManager::ParticipantForkSelectMode getForkSelectMode()
This class represent a remote participant.
virtual void adjustRTPStreams(bool sendingOffer=false)=0
std::shared_ptr< ConversationProfile > getDefaultOutgoingConversationProfile()
virtual std::shared_ptr< ConversationProfile > getIncomingConversationProfile(const resip::SipMessage &msg)
virtual void end()
DialogUsageManager & mDum
virtual std::shared_ptr< UserProfile > getUserProfile()
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
const Data & getCallId() const
Definition DialogId.cxx:99
void send(std::shared_ptr< SipMessage > request)
AppDialogHandle getAppDialog() const
bool isValid() const
Definition Handle.hxx:25
virtual void provideAnswer(const Contents &answer)
Similar to provideOffer - called to set the answer to be signaled to the peer.
virtual void provideOffer(const Contents &offer)
Called to set the offer that will be used in the next message that sends an offer.
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
void accept(int statusCode=200)
accept an initial invite (2xx) - this is only applicable to the UAS
void provisional(int code=180, bool earlyFlag=true)
send a 1xx - provisional response.
Represents a SIP message.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
unsigned int ParticipantHandle
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348