reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
DialogEventStateManager.cxx
Go to the documentation of this file.
2#include "rutil/Random.hxx"
3#include "rutil/Logger.hxx"
4
5namespace resip
6{
7
8#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
9
10DialogEventStateManager::DialogEventStateManager()
11 : mDialogEventHandler(nullptr)
12{
13}
14
15// we've received an INVITE
16void
17DialogEventStateManager::onTryingUas(Dialog& dialog, const SipMessage& invite)
18{
19 DialogEventInfo* eventInfo = new DialogEventInfo();
20 eventInfo->mDialogEventId = Random::getVersion4UuidUrn(); // !jjg! is this right?
21 eventInfo->mDialogId = dialog.getId();
22 eventInfo->mDirection = DialogEventInfo::Recipient;
23 eventInfo->mCreationTimeSeconds = Timer::getTimeSecs();
24 eventInfo->mInviteSession = InviteSessionHandle::NotValid();
25 eventInfo->mRemoteOfferAnswer = (invite.getContents() != nullptr ? std::unique_ptr<Contents>(invite.getContents()->clone()) : nullptr);
26 eventInfo->mLocalIdentity = dialog.getLocalNameAddr();
27 eventInfo->mLocalTarget = dialog.getLocalContact().uri(); // !slg! TODO - fix me - the Dialog stored local contact has an empty hostname so that the stack will fill it in
28 eventInfo->mRemoteIdentity = dialog.getRemoteNameAddr();
29 eventInfo->mRemoteTarget = std::unique_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
30 eventInfo->mRouteSet = dialog.getRouteSet();
31 eventInfo->mState = DialogEventInfo::Trying;
32
33 if (invite.exists(h_Replaces) &&
34 invite.header(h_Replaces).isWellFormed())
35 {
36 Data replacesToTag = invite.header(h_Replaces).exists(p_toTag) ? invite.header(h_Replaces).param(p_toTag) : Data::Empty;
37 Data replacesFromTag = invite.header(h_Replaces).exists(p_fromTag) ? invite.header(h_Replaces).param(p_fromTag) : Data::Empty;
38
39 eventInfo->mReplacesId = std::unique_ptr<DialogId>(new DialogId(invite.header(h_Replaces).value(),
42
43 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(*(eventInfo->mReplacesId));
44 if (it != mDialogIdToEventInfo.end())
45 {
46 // If the call to be replaced is early and it is a recipient, then it cannot get replaced (according to RFC3891)
47 // so we don't want to set the flag in this case. Using inverted logic statement.
48 if (it->second->getState() != DialogEventInfo::Early || it->second->getDirection() != DialogEventInfo::Recipient)
49 {
50 it->second->mReplaced = true;
51 }
52 }
53 }
54 if (invite.exists(h_ReferredBy) &&
55 invite.header(h_ReferredBy).isWellFormed())
56 {
57 eventInfo->mReferredBy = std::unique_ptr<NameAddr>(new NameAddr(invite.header(h_ReferredBy)));
58 }
59
60 mDialogIdToEventInfo[dialog.getId()] = eventInfo;
61
62 TryingDialogEvent evt(*eventInfo, invite);
63 mDialogEventHandler->onTrying(evt);
64}
65
66// we've sent an INVITE
67void
68DialogEventStateManager::onTryingUac(DialogSet& dialogSet, const SipMessage& invite)
69{
70 DialogId fakeId(dialogSet.getId(), Data::Empty);
71 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(fakeId);
72
73 DialogEventInfo* eventInfo = 0;
74
75 if (it != mDialogIdToEventInfo.end())
76 {
77 // .jjg. we will get in here if our INVITE gets challenged; just swallow the onTrying event in this case
78 eventInfo = it->second;
79 if (eventInfo->mState == DialogEventInfo::Trying)
80 {
81 return;
82 }
83 }
84 else
85 {
86 eventInfo = new DialogEventInfo();
87 }
88
89 eventInfo->mDialogEventId = Random::getVersion4UuidUrn();
90 eventInfo->mDialogId = DialogId(dialogSet.getId(), Data::Empty);
91 eventInfo->mDirection = DialogEventInfo::Initiator;
92 eventInfo->mCreationTimeSeconds = Timer::getTimeSecs();
93 eventInfo->mInviteSession = InviteSessionHandle::NotValid();
94 eventInfo->mLocalIdentity = invite.header(h_From);
95 // ?bwc? Has something already checked for well-formedness here?
96 // Maybe DialogSet? We need to be absolutely certain that this exists and is
97 // well-formed. Assert for now.
99 resip_assert(invite.header(h_Contacts).front().isWellFormed());
100 eventInfo->mLocalTarget = invite.header(h_Contacts).front().uri();
101 eventInfo->mRemoteIdentity = invite.header(h_To);
102 eventInfo->mLocalOfferAnswer = (invite.getContents() != nullptr ? std::unique_ptr<Contents>(invite.getContents()->clone()) : nullptr);
103 eventInfo->mState = DialogEventInfo::Trying;
104
105 if (invite.exists(h_ReferredBy) &&
106 invite.header(h_ReferredBy).isWellFormed())
107 {
108 eventInfo->mReferredBy = std::unique_ptr<NameAddr>(new NameAddr(invite.header(h_ReferredBy)));
109 }
110
111 mDialogIdToEventInfo[eventInfo->mDialogId] = eventInfo;
112
113 TryingDialogEvent evt(*eventInfo, invite);
114 mDialogEventHandler->onTrying(evt);
115}
116
117// we've received a 1xx response without a remote tag
118void
119DialogEventStateManager::onProceedingUac(const DialogSet& dialogSet, const SipMessage& response)
120{
121 DialogId fakeId(dialogSet.getId(), Data::Empty);
122 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.lower_bound(fakeId);
123 if (it != mDialogIdToEventInfo.end() &&
124 it->first.getDialogSetId() == dialogSet.getId())
125 {
126 if (it->first.getRemoteTag().empty())
127 {
128 // happy day case; no forks yet; e.g INVITE/1xx (no tag)/1xx (no tag)
129 DialogEventInfo* eventInfo = it->second;
130 eventInfo->mState = DialogEventInfo::Proceeding;
131 if (!response.empty(h_Contacts))
132 {
133 // ?bwc? Has something already checked for well-formedness here?
134 // Maybe DialogSet? Assert for now.
135 resip_assert(response.header(h_Contacts).front().isWellFormed());
136 eventInfo->mRemoteTarget = std::unique_ptr<Uri>(new Uri(response.header(h_Contacts).front().uri()));
137 }
138 ProceedingDialogEvent evt(*eventInfo);
139 mDialogEventHandler->onProceeding(evt);
140 }
141 else
142 {
143 // forking; e.g. INVITE/180 (tag #1)/180 (no tag)
144
145 // .jjg. The remote sender of the 180 (no tag) should either 'put up or shut up' as Byron put it
146 // so we'll just ignore this...
147 }
148 }
149}
150
151// UAC: we've received a 1xx response WITH a remote tag
152// UAS: we've sent a 1xx response WITH a local tag
153void
154DialogEventStateManager::onEarly(const Dialog& dialog, InviteSessionHandle is)
155{
156 DialogEventInfo* eventInfo = findOrCreateDialogInfo(dialog);
157
158 if (eventInfo)
159 {
160 eventInfo->mState = DialogEventInfo::Early;
161 eventInfo->mRouteSet = dialog.getRouteSet();
162 eventInfo->mInviteSession = is;
163
164 // local or remote target might change due to an UPDATE or re-INVITE
165 eventInfo->mLocalTarget = dialog.getLocalContact().uri(); // !slg! TODO - fix me - the Dialog stored local contact has an empty hostname so that the stack will fill it in
166 eventInfo->mRemoteTarget = std::unique_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
167
168 EarlyDialogEvent evt(*eventInfo);
169 mDialogEventHandler->onEarly(evt);
170 }
171}
172
173void
174DialogEventStateManager::onConfirmed(const Dialog& dialog, InviteSessionHandle is)
175{
176 DialogEventInfo* eventInfo = findOrCreateDialogInfo(dialog);
177
178 if (eventInfo)
179 {
180 eventInfo->mInviteSession = is;
181 eventInfo->mRouteSet = dialog.getRouteSet(); // won't change due to re-INVITEs, but is
182 // needed for the Trying --> Confirmed transition
183 eventInfo->mState = DialogEventInfo::Confirmed;
184
185 // local or remote target might change due to an UPDATE or re-INVITE
186 eventInfo->mLocalTarget = dialog.getLocalContact().uri(); // !slg! TODO - fix me - the Dialog stored local contact has an empty hostname so that the stack will fill it in
187 eventInfo->mRemoteTarget = std::unique_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
188
189 // Set the latest sdp
190 eventInfo->mLocalOfferAnswer = (is->hasLocalOfferAnswer() ? std::unique_ptr<Contents>(is->getLocalOfferAnswer().clone()) : nullptr);
191 eventInfo->mRemoteOfferAnswer = (is->hasRemoteOfferAnswer() ? std::unique_ptr<Contents>(is->getRemoteOfferAnswer().clone()) : nullptr);
192
193 // for the dialog that got the 200 OK
194 std::shared_ptr<ConfirmedDialogEvent> confirmedEvt(new ConfirmedDialogEvent(*eventInfo));
195
196 //mDialogEventHandler->onConfirmed(confirmedEvt);
197 MultipleEventDialogEvent::EventVector events;
198
199 // kill off any other dialogs in this dialog set, since certain proxy/registrars (like SER and sipX)
200 // won't bother giving us updates on their status anyways!
201 const DialogSetId& dialogSetId = dialog.getId().getDialogSetId();
202 DialogId fakeId(dialogSetId, Data::Empty);
203 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.lower_bound(fakeId);
204 while (it != mDialogIdToEventInfo.end() &&
205 it->first.getDialogSetId() == dialogSetId)
206 {
207 DialogEventInfo::State dialogState = it->second->getState();
208 if (dialogState == DialogEventInfo::Proceeding || dialogState == DialogEventInfo::Early)
209 {
210 // .jjg. we're killing a *specific* dialog *after* the successful completion of the initial INVITE transaction;
211 // so just eliminate this dialog, not the entire dialogset
212 events.emplace_back(std::shared_ptr<TerminatedDialogEvent>(onDialogTerminatedImpl(it->second, InviteSessionHandler::RemoteCancel)));
213 delete it->second;
214 mDialogIdToEventInfo.erase(it++);
215 }
216 else
217 {
218 it++;
219 }
220 }
221
222 if (!events.empty())
223 {
224 events.push_back(confirmedEvt);
225 MultipleEventDialogEvent multipleEvt(events);
226 mDialogEventHandler->onMultipleEvents(multipleEvt);
227 }
228 else
229 {
230 mDialogEventHandler->onConfirmed(*confirmedEvt);
231 }
232 }
233}
234
235void
236DialogEventStateManager::onTerminated(const Dialog& dialog, const SipMessage& msg, InviteSessionHandler::TerminatedReason reason)
237{
238 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(dialog.getId());
239 if (it != mDialogIdToEventInfo.end())
240 {
241 DialogEventInfo::State dialogState = it->second->getState();
242 if (dialogState == DialogEventInfo::Confirmed)
243 {
244 // .jjg. we're killing a *specific* dialog *after* the successful completion of the initial INVITE transaction;
245 // so just elminate this dialog, not the entire dialogset
246 std::unique_ptr<TerminatedDialogEvent> evt(onDialogTerminatedImpl(it->second, reason, getResponseCode(msg), getFrontContact(msg)));
247 mDialogEventHandler->onTerminated(*evt);
248 delete it->second;
249 mDialogIdToEventInfo.erase(it++);
250 }
251 else
252 {
253 onDialogSetTerminatedImpl(dialog.getId().getDialogSetId(), msg, reason);
254 }
255 }
256 else
257 {
258 onDialogSetTerminatedImpl(dialog.getId().getDialogSetId(), msg, reason);
259 }
260}
261
262void
263DialogEventStateManager::onTerminated(const DialogSet& dialogSet, const SipMessage& msg, InviteSessionHandler::TerminatedReason reason)
264{
265 onDialogSetTerminatedImpl(dialogSet.getId(), msg, reason);
266}
267
268void
269DialogEventStateManager::onDialogSetTerminatedImpl(const DialogSetId& dialogSetId, const SipMessage& msg, InviteSessionHandler::TerminatedReason reason)
270{
271 DialogEventInfo* eventInfo = NULL;
272
280 //find dialogSet. All non-confirmed dialogs are destroyed by this event.
281 //Confirmed dialogs are only destroyed by an exact match.
282
283 DialogId fakeId(dialogSetId, Data::Empty);
284 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.lower_bound(fakeId);
285
286 while (it != mDialogIdToEventInfo.end() &&
287 it->first.getDialogSetId() == dialogSetId)
288 {
289 eventInfo = it->second;
290 std::unique_ptr<TerminatedDialogEvent> evt(onDialogTerminatedImpl(eventInfo, reason, getResponseCode(msg), getFrontContact(msg)));
291 mDialogEventHandler->onTerminated(*evt);
292 delete it->second;
293 mDialogIdToEventInfo.erase(it++);
294 }
295}
296
297TerminatedDialogEvent*
298DialogEventStateManager::onDialogTerminatedImpl(DialogEventInfo* eventInfo,
299 InviteSessionHandler::TerminatedReason reason,
300 int responseCode,
301 Uri* remoteTarget)
302{
303 eventInfo->mState = DialogEventInfo::Terminated;
304
305 // .jjg. when we get an INVITE w/Replaces, we mark the replaced dialog event info
306 // as 'replaced' (see onTryingUas);
307 // when the replaced dialog is ended, it will be ended normally with a BYE or CANCEL,
308 // but since we've marked it as 'replaced' we can update the termination reason
309 InviteSessionHandler::TerminatedReason actualReason = reason;
310
311 if (eventInfo->mReplaced)
312 {
313 actualReason = InviteSessionHandler::Replaced;
314 }
315
316 if (remoteTarget)
317 {
318 eventInfo->mRemoteTarget = std::unique_ptr<Uri>(remoteTarget);
319 }
320
321 TerminatedDialogEvent* evt = new TerminatedDialogEvent(*eventInfo, actualReason, responseCode);
322 return evt;
323 //mDialogEventHandler->onTerminated(evt);
324}
325
326int
327DialogEventStateManager::getResponseCode(const SipMessage& msg)
328{
329 int respCode = 0;
330 if (msg.isResponse())
331 {
332 respCode = msg.header(h_StatusLine).responseCode();
333 }
334 return respCode;
335}
336
337Uri*
338DialogEventStateManager::getFrontContact(const SipMessage& msg)
339{
340 Uri* pContact = NULL;
341 if (msg.isResponse())
342 {
343 if (!msg.empty(h_Contacts))
344 {
345 // ?bwc? Has something already checked for well-formedness here?
346 // Maybe DialogSet? Assert for now.
347 resip_assert(msg.header(h_Contacts).front().isWellFormed());
348 pContact = new Uri(msg.header(h_Contacts).front().uri());
349 }
350 }
351 return pContact;
352}
353
354DialogEventStateManager::DialogEventInfos
355DialogEventStateManager::getDialogEventInfo() const
356{
358 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::const_iterator it = mDialogIdToEventInfo.begin();
359 for (; it != mDialogIdToEventInfo.end(); it++)
360 {
361 infos.push_back(*(it->second));
362 }
363 return infos;
364}
365
367DialogEventStateManager::getDialogEventInfo(const Uri& entityUri, bool bMatchRemoteIdentityOnly) const
368{
370 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::const_iterator it = mDialogIdToEventInfo.begin();
371 for (; it != mDialogIdToEventInfo.end(); it++)
372 {
374 {
375 // Return all calls to or from the requested entity
376 if (it->second->getLocalIdentity().uri().getAOR(false) == entityUri.getAOR(false) ||
377 it->second->getRemoteIdentity().uri().getAOR(false) == entityUri.getAOR(false))
378 {
379 infos.push_back(*(it->second));
380 }
381 }
382 else
383 {
384 // Return only those dialogs whose remote identity matches the requested entity
385 if (it->second->getRemoteIdentity().uri().getAOR(false) == entityUri.getAOR(false))
386 {
387 infos.push_back(*(it->second));
388 }
389 }
390 }
391 return infos;
392}
393
395DialogEventStateManager::findOrCreateDialogInfo(const Dialog& dialog)
396{
398
407 std::map<DialogId, DialogEventInfo*, DialogIdComparator>::iterator it = mDialogIdToEventInfo.find(dialog.getId());
408
409 if (it != mDialogIdToEventInfo.end())
410 {
411 return it->second;
412 }
413 else
414 {
415 // either we have a dialog set id with an empty remote tag, or we have other dialog(s) with different
416 // remote tag(s)
417 DialogId fakeId(dialog.getId().getDialogSetId(), Data::Empty);
418 it = mDialogIdToEventInfo.lower_bound(fakeId);
419
420 if (it != mDialogIdToEventInfo.end() &&
421 it->first.getDialogSetId() == dialog.getId().getDialogSetId())
422 {
423 if (it->first.getRemoteTag().empty())
424 {
425 // convert this bad boy into a full on Dialog
426 eventInfo = it->second;
427 mDialogIdToEventInfo.erase(it);
428 eventInfo->mDialogId = dialog.getId();
429 }
430 else
431 {
432 // clone this fellow member dialog, initializing it with a new id and creation time
433 DialogEventInfo* newForkInfo = new DialogEventInfo(*(it->second));
434 newForkInfo->mDialogEventId = Random::getVersion4UuidUrn();
435 newForkInfo->mCreationTimeSeconds = Timer::getTimeSecs();
436 newForkInfo->mDialogId = dialog.getId();
437 newForkInfo->mRemoteIdentity = dialog.getRemoteNameAddr();
438 newForkInfo->mRemoteTarget = std::unique_ptr<Uri>(new Uri(dialog.getRemoteTarget().uri()));
439 newForkInfo->mRouteSet = dialog.getRouteSet();
441 }
442 }
443 else
444 {
445 // .jjg. this can happen if onTryingUax(..) wasn't called yet for this dialog (set) id
446 DebugLog(<< "DialogSetId " << fakeId << " was not found! This indicates a bug; onTryingUax() should have been called first!");
447 return 0;
448 }
449 }
450
451 mDialogIdToEventInfo[dialog.getId()] = eventInfo;
452
453 return eventInfo;
454}
455
456} // namespace resip
457
458/* ====================================================================
459* The Vovida Software License, Version 1.0
460*
461* Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
462*
463* Redistribution and use in source and binary forms, with or without
464* modification, are permitted provided that the following conditions
465* are met:
466*
467* 1. Redistributions of source code must retain the above copyright
468* notice, this list of conditions and the following disclaimer.
469*
470* 2. Redistributions in binary form must reproduce the above copyright
471* notice, this list of conditions and the following disclaimer in
472* the documentation and/or other materials provided with the
473* distribution.
474*
475* 3. The names "VOCAL", "Vovida Open Communication Application Library",
476* and "Vovida Open Communication Application Library (VOCAL)" must
477* not be used to endorse or promote products derived from this
478* software without prior written permission. For written
479* permission, please contact vocal@vovida.org.
480*
481* 4. Products derived from this software may not be called "VOCAL", nor
482* may "VOCAL" appear in their name, without prior written
483* permission of Vovida Networks, Inc.
484*
485* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
486* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
487* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
488* NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
489* NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
490* IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
491* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
492* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
493* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
494* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
495* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
496* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
497* DAMAGE.
498*
499* ====================================================================
500*
501* This software consists of voluntary contributions made by Vovida
502* Networks, Inc. and many individuals on behalf of Vovida Networks,
503* Inc. For more information on Vovida Networks, Inc., please see
504* <http://www.vovida.org/>.
505*
506*/
#define DebugLog(args_)
Definition Logger.hxx:128
#define resip_assert(x)
This file needs to work for both C and C++ code.
std::vector< DialogEventInfo > DialogEventInfos
const DialogSetId & getDialogSetId() const
Definition DialogId.cxx:93
dcm! – kill typedef std::list<DialogId> DialogIdSet;
Definition Dialog.hxx:29
const NameAddr & getRemoteNameAddr() const noexcept
Definition Dialog.cxx:343
const NameAddr & getRemoteTarget() const noexcept
Definition Dialog.cxx:349
const DialogId & getId() const noexcept
Definition Dialog.cxx:325
const NameAddrs & getRouteSet() const noexcept
Definition Dialog.cxx:361
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
dcm! – add UnusedChecking(_enum) below;
void list()
Definition userAdmin.cxx:66