reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
ConversationManager.cxx
Go to the documentation of this file.
1// resip includes
2#include <rutil/Log.hxx>
3#include <rutil/Logger.hxx>
4#include <rutil/Random.hxx>
12
13#include "ReconSubsystem.hxx"
14#include "UserAgent.hxx"
17#include "Conversation.hxx"
18#include "Participant.hxx"
19#include "BridgeMixer.hxx"
20#include "DtmfEvent.hxx"
21#include "RemoteParticipant.hxx"
26
27#if defined(WIN32) && !defined(__GNUC__)
28#pragma warning( disable : 4355 )
29#endif
30
31using namespace recon;
32using namespace resip;
33using namespace std;
34
35#define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
36
37ConversationManager::ConversationManager(std::shared_ptr<MediaStackAdapter> mediaStackAdapter,
38 std::shared_ptr<ConfigParse> configParse)
39: mUserAgent(0),
40 mMediaStackAdapter(mediaStackAdapter),
41 mConfigParse(configParse),
42 mShuttingDown(false),
43 mCurrentConversationHandle(1),
44 mCurrentParticipantHandle(1)
45{
46 if(mConfigParse)
47 {
48 std::vector<int> intervals;
49 if(configParse->getConfigValue("KeyframeIntervals", intervals))
50 {
51 mKeyframeIntervals = intervals;
52 }
53 }
54}
55
57{
58 resip_assert(mConversations.empty());
59 resip_assert(mParticipants.empty());
60 InfoLog(<< "ConversationManager::~ConversationManager destroying BridgeMixer...");
61 mBridgeMixer.reset(); // Make sure the mixer is destroyed before the media interface
62 InfoLog(<< "ConversationManager::~ConversationManager destroying MediaStackAdapter...");
63 mMediaStackAdapter.reset();
64 InfoLog(<< "ConversationManager::~ConversationManager complete.");
65}
66
68{
69 switch (partType)
70 {
72 return "Local";
74 return "Remote";
76 return "MediaResource";
78 return "RemoteIMPager";
80 return "RemoteIMSession";
81 }
82 assert(false);
83 return "UNKNOWN";
84}
85
86void
88{
89 mUserAgent = userAgent;
90 mMediaStackAdapter->setUserAgent(userAgent);
91}
92
93void
94ConversationManager::shutdown()
95{
96 InfoLog(<< "ConversationManager::shutting down. Ending any active conversations and participants...");
97
98 mShuttingDown = true;
99
100 // Destroy each Conversation
101 ConversationMap tempConvs = mConversations; // Create copy for safety, since ending conversations can immediately remove themselves from map
102 ConversationMap::iterator i;
103 for(i = tempConvs.begin(); i != tempConvs.end(); i++)
104 {
105 InfoLog(<< "Destroying conversation: " << i->second->getHandle());
106 i->second->destroy();
107 }
108
109 // End each Participant
110 ParticipantMap tempParts = mParticipants; // Create copy for safety, since ending participants can immediately remove themselves from map
111 ParticipantMap::iterator j;
112 for(j = tempParts.begin(); j != tempParts.end(); j++)
113 {
114 InfoLog(<< "Destroying participant: " << j->second->getParticipantHandle());
115 j->second->destroyParticipant();
116 }
117
118 if(mMediaStackAdapter.get())
119 {
120 InfoLog(<< "ConversationManager::shutting down MediaStackAdapter...");
121 mMediaStackAdapter->shutdown();
122 //mMediaStackAdapter.reset(); // Reset here causes a deadlock, will reset in destructor instead
123 }
124 InfoLog(<< "ConversationManager::shutdown.");
125}
126
127void
128ConversationManager::process()
129{
130 if(!mShuttingDown)
131 {
132 mMediaStackAdapter->process();
133 }
134}
135
138{
139 if (mShuttingDown) return 0; // Don't allow new things to be created when we are shutting down
141
142 CreateConversationCmd* cmd = new CreateConversationCmd(this, convHandle, autoHoldMode, 0);
143 post(cmd);
144 return convHandle;
145}
146
147void
153
154void
156{
157 JoinConversationCmd* cmd = new JoinConversationCmd(this, sourceConvHandle, destConvHandle);
158 post(cmd);
159}
160
162ConversationManager::createRemoteParticipant(ConversationHandle convHandle, const resip::NameAddr& destination, ParticipantForkSelectMode forkSelectMode, const std::shared_ptr<ConversationProfile>& conversationProfile, const std::multimap<resip::Data,resip::Data>& extraHeaders)
163{
164 if (mShuttingDown) return 0; // Don't allow new things to be created when we are shutting down
166
167 CreateRemoteParticipantCmd* cmd = new CreateRemoteParticipantCmd(this, partHandle, convHandle, destination, forkSelectMode, conversationProfile, extraHeaders);
168 post(cmd);
169
170 return partHandle;
171}
172
174ConversationManager::createRemoteIMPagerParticipant(const resip::NameAddr& destination, const std::shared_ptr<ConversationProfile>& conversationProfile)
175{
176 if (mShuttingDown) return 0; // Don't allow new things to be created when we are shutting down
178
179 CreateRemoteIMPagerParticipantCmd* cmd = new CreateRemoteIMPagerParticipantCmd(this, partHandle, destination, conversationProfile);
180 post(cmd);
181
182 return partHandle;
183}
184
186ConversationManager::createRemoteIMSessionParticipant(const resip::NameAddr& destination, ParticipantForkSelectMode forkSelectMode, const std::shared_ptr<ConversationProfile>& conversationProfile, const std::multimap<resip::Data, resip::Data>& extraHeaders)
187{
188 if (mShuttingDown) return 0; // Don't allow new things to be created when we are shutting down
190
191 CreateRemoteIMSessionParticipantCmd* cmd = new CreateRemoteIMSessionParticipantCmd(this, partHandle, destination, forkSelectMode, conversationProfile, extraHeaders);
192 post(cmd);
193
194 return partHandle;
195}
196
198ConversationManager::createMediaResourceParticipant(ConversationHandle convHandle, const Uri& mediaUrl, const std::shared_ptr<Data>& audioBuffer, void* recordingCircularBuffer)
199{
200 if (mShuttingDown) return 0; // Don't allow new things to be created when we are shutting down
202
203 CreateMediaResourceParticipantCmd* cmd = new CreateMediaResourceParticipantCmd(this, partHandle, convHandle, mediaUrl, audioBuffer, recordingCircularBuffer);
204 post(cmd);
205
206 return partHandle;
207}
208
211{
212 if (mShuttingDown) return 0; // Don't allow new things to be created when we are shutting down
213 ParticipantHandle partHandle = 0;
214 if (getMediaStackAdapter().supportsLocalAudio())
215 {
216 partHandle = getNewParticipantHandle();
217
218 CreateLocalParticipantCmd* cmd = new CreateLocalParticipantCmd(this, partHandle);
219 post(cmd);
220 }
221 else
222 {
223 WarningLog(<< "createLocalParticipant called when local audio support is disabled.");
224 }
225
226 return partHandle;
227}
228
229void
231{
232 DestroyParticipantCmd* cmd = new DestroyParticipantCmd(this, partHandle);
233 post(cmd);
234}
235
236void
238{
239 AddParticipantCmd* cmd = new AddParticipantCmd(this, convHandle, partHandle);
240 post(cmd);
241}
242
243void
245{
246 RemoveParticipantCmd* cmd = new RemoveParticipantCmd(this, convHandle, partHandle);
247 post(cmd);
248}
249
250void
252{
253 MoveParticipantCmd* cmd = new MoveParticipantCmd(this, partHandle, sourceConvHandle, destConvHandle);
254 post(cmd);
255}
256
257void
258ConversationManager::modifyParticipantContribution(ConversationHandle convHandle, ParticipantHandle partHandle, unsigned int inputGain, unsigned int outputGain)
259{
260 ModifyParticipantContributionCmd* cmd = new ModifyParticipantContributionCmd(this, convHandle, partHandle, inputGain, outputGain);
261 post(cmd);
262}
263
264void
266{
267 ModifyParticipantRecordChannelCmd* cmd = new ModifyParticipantRecordChannelCmd(this, partHandle, channelNum);
268 post(cmd);
269}
270
271void
277
278void
280{
281 AlertParticipantCmd* cmd = new AlertParticipantCmd(this, partHandle, earlyFlag);
282 post(cmd);
283}
284
285void
287{
288 AnswerParticipantCmd* cmd = new AnswerParticipantCmd(this, partHandle);
289 post(cmd);
290}
291
292void
294{
295 RejectParticipantCmd* cmd = new RejectParticipantCmd(this, partHandle, rejectCode);
296 post(cmd);
297}
298
299void
300ConversationManager::redirectParticipant(ParticipantHandle partHandle, const NameAddr& destination, unsigned int redirectCode, RedirectSuccessCondition successCondition)
301{
302 RedirectParticipantCmd* cmd = new RedirectParticipantCmd(this, partHandle, destination, redirectCode, successCondition);
303 post(cmd);
304}
305
306void
308{
309 RedirectToParticipantCmd* cmd = new RedirectToParticipantCmd(this, partHandle, destPartHandle, successCondition);
310 post(cmd);
311}
312
313void
315{
316 HoldParticipantCmd* cmd = new HoldParticipantCmd(this, partHandle, hold);
317 post(cmd);
318}
319
320void
321ConversationManager::sendIMToParticipant(ParticipantHandle partHandle, std::unique_ptr<Contents> contents)
322{
323 SendIMToParticipantCmd* cmd = new SendIMToParticipantCmd(this, partHandle, std::move(contents));
324 post(cmd);
325}
326
327void
328ConversationManager::startApplicationTimer(unsigned int timerId, unsigned int timerData1, unsigned int timerData2, unsigned int durationMs)
329{
330 ApplicationTimerCmd cmd(this, timerId, timerData1, timerData2);
331 post(cmd, std::chrono::milliseconds(durationMs));
332}
333
334void
335ConversationManager::startApplicationTimer(unsigned int timerId, unsigned int timerData1, unsigned int timerData2, std::chrono::duration<double> duration)
336{
337 ApplicationTimerCmd cmd(this, timerId, timerData1, timerData2);
338 post(cmd, duration);
339}
340
343{
344 return mCurrentConversationHandle++;
345}
346
347void
348ConversationManager::registerConversation(Conversation *conversation)
349{
350 mConversations[conversation->getHandle()] = conversation;
351
352 std::lock_guard<std::mutex> lock(mConversationHandlesMutex);
353 mConversationHandles.insert(conversation->getHandle());
354}
355
356void
357ConversationManager::unregisterConversation(Conversation *conversation)
358{
359 mConversations.erase(conversation->getHandle());
360
361 std::lock_guard<std::mutex> lock(mConversationHandlesMutex);
362 mConversationHandles.erase(conversation->getHandle());
363}
364
367{
368 return mCurrentParticipantHandle++;
369}
370
371void
372ConversationManager::registerParticipant(Participant *participant)
373{
374 mParticipants[participant->getParticipantHandle()] = participant;
375
376 std::lock_guard<std::mutex> lock(mParticipantHandlesMutex);
377 mParticipantHandlesByType[participant->getParticipantType()].insert(participant->getParticipantHandle());
378}
379
380void
381ConversationManager::unregisterParticipant(Participant *participant)
382{
383 InfoLog(<< "participant unregistered, handle=" << participant->getParticipantHandle());
384 mParticipants.erase(participant->getParticipantHandle());
385
386 std::lock_guard<std::mutex> lock(mParticipantHandlesMutex);
387 mParticipantHandlesByType[participant->getParticipantType()].erase(participant->getParticipantHandle());
388}
389
390void
392{
393 if (mUserAgent)
394 {
395 mUserAgent->getDialogUsageManager().post(msg);
396 }
397}
398
399void
400ConversationManager::post(resip::ApplicationMessage& message, std::chrono::duration<double> duration)
401{
402 if (mUserAgent)
403 {
404 mUserAgent->post(message, duration);
405 }
406}
407
408void
409ConversationManager::buildSdpOffer(ConversationProfile* profile, SdpContents& offer)
410{
411 // copy over session capabilities
412 offer = profile->sessionCaps();
413
414 // Set sessionid and version for this offer
415 uint64_t currentTime = Timer::getTimeMicroSec();
416 offer.session().origin().getSessionId() = currentTime;
417 offer.session().origin().getVersion() = currentTime;
418
419 // Set local port in offer
420 // make sure at least one medium is present
421 resip_assert(offer.session().media().size() > 0);
422 // make sure at least one medium is audio
423 resip_assert(offer.session().getMediaByType("audio").size() > 0);
424}
425
427ConversationManager::getParticipant(ParticipantHandle partHandle)
428{
429 ParticipantMap::iterator i = mParticipants.find(partHandle);
430 if(i != mParticipants.end())
431 {
432 return i->second;
433 }
434 else
435 {
436 return 0;
437 }
438}
439
442{
443 ConversationMap::iterator i = mConversations.find(convHandle);
444 if(i != mConversations.end())
445 {
446 return i->second;
447 }
448 else
449 {
450 return 0;
451 }
452}
453
454std::set<ConversationHandle>
456{
457 std::lock_guard<std::mutex> lock(mConversationHandlesMutex);
458 return mConversationHandles;
459}
460
461std::set<ParticipantHandle>
463{
464 std::lock_guard<std::mutex> lock(mParticipantHandlesMutex);
465 std::set<ParticipantHandle> participantHandles;
466 auto it = mParticipantHandlesByType.find(participantType);
467 if (it != mParticipantHandlesByType.end())
468 {
469 participantHandles = it->second;
470 }
471 return participantHandles;
472};
473
474void
476{
477 mMediaResourceCache.addToCache(name, buffer, type);
478}
479
480bool
482{
483 return mMediaResourceCache.getFromCache(name, buffer, type);
484}
485
486void
487ConversationManager::requestKeyframe(ParticipantHandle partHandle, std::chrono::duration<double> duration)
488{
489 RequestKeyframeCmd cmd(this, partHandle);
490 post(cmd, duration);
491}
492
493void
494ConversationManager::requestKeyframeFromPeer(ParticipantHandle partHandle, std::chrono::duration<double> duration)
495{
496 RequestKeyframeFromPeerCmd cmd(this, partHandle);
497 post(cmd, duration);
498}
499
500void
501ConversationManager::requestKeyframeFromPeerRecurring(ParticipantHandle partHandle, std::chrono::duration<double> interval)
502{
503 RequestKeyframeFromPeerRecurringCmd cmd(this, partHandle, interval);
504 post(cmd, interval);
505}
506
507void
508ConversationManager::notifyMediaEvent(ParticipantHandle partHandle, MediaEvent::MediaEventType eventType, MediaEvent::MediaDirection direction)
509{
510 Participant* participant = getParticipant(partHandle);
511 if (participant)
512 {
513 if (eventType == MediaEvent::RESOURCE_DONE || eventType == MediaEvent::RESOURCE_FAILED)
514 {
515 MediaResourceParticipant* mrPart = dynamic_cast<MediaResourceParticipant*>(participant);
516 if (mrPart)
517 {
518 if (eventType == MediaEvent::RESOURCE_FAILED)
519 {
521 }
522 mrPart->resourceDone();
523 }
524 }
525 else if (eventType == MediaEvent::VOICE_STARTED || eventType == MediaEvent::VOICE_STOPPED)
526 {
527 onParticipantVoiceActivity(partHandle, eventType == MediaEvent::VOICE_STARTED ? true : false, direction == MediaEvent::DIRECTION_IN);
528 }
529 }
530}
531
532void
533ConversationManager::notifyDtmfEvent(ParticipantHandle partHandle, int dtmf, int duration, bool up)
534{
535 // Call virtual method that applications can override
536 onDtmfEvent(partHandle, dtmf, duration, up);
537}
538
541{
542 RemoteParticipant* rp = nullptr;
543 if (dynamic_cast<RemoteIMSessionParticipantDialogSet*>(&rpds) != nullptr)
544 {
545 rp = new RemoteIMSessionParticipant(*this, dum, rpds);
546 }
547 else
548 {
550 }
551 return rp;
552}
553
556{
557 RemoteParticipant* rp = nullptr;
558 if (dynamic_cast<RemoteIMSessionParticipantDialogSet*>(&rpds) != nullptr)
559 {
560 rp = new RemoteIMSessionParticipant(partHandle, *this, dum, rpds);
561 }
562 else
563 {
564 rp = getMediaStackAdapter().createRemoteParticipantInstance(partHandle, dum, rpds);
565 }
566 return rp;
567}
568
570ConversationManager::createRemoteIMSessionParticipantDialogSetInstance(ParticipantForkSelectMode forkSelectMode, std::shared_ptr<ConversationProfile> conversationProfile)
571{
572 return new RemoteIMSessionParticipantDialogSet(*this, forkSelectMode, conversationProfile);
573}
574
575void
576ConversationManager::setMediaStackAdapter(std::shared_ptr<MediaStackAdapter> mediaStackAdapter)
577{
578 mMediaStackAdapter = mediaStackAdapter;
579 if(mediaStackAdapter)
580 {
581 mediaStackAdapter->conversationManagerReady(this);
582 }
583}
584
585void
587{
588 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onNewSession(h, oat, msg);
589}
590
591void
593{
594 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onNewSession(h, oat, msg);
595}
596
597void
599{
600 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onFailure(h, msg);
601}
602
603void
605{
606 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onEarlyMedia(h, msg, sdp);
607}
608
609void
611{
612 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onProvisional(h, msg);
613}
614
615void
617{
618 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onConnected(h, msg);
619}
620
621void
623{
624 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onConnected(h, msg);
625}
626
627void
632
633void
638
639void
644
645void
647{
648 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onRedirected(h, msg);
649}
650
651void
653{
654 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onAnswer(h, msg, sdp);
655}
656
657void
659{
660 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onOffer(h, msg, sdp);
661}
662
663void
668
669void
674
675void
680
681void
683{
684 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onRemoteSdpChanged(h, msg, sdp);
685}
686
687void
689{
690 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onInfo(h, msg);
691}
692
693void
695{
696 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onInfoSuccess(h, msg);
697}
698
699void
701{
702 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onInfoFailure(h, msg);
703}
704
705void
707{
708 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onRefer(h, ssh, msg);
709}
710
711void
716
717void
722
723void
725{
726 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onReferNoSub(h, msg);
727}
728
729void
731{
732 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onMessage(h, msg);
733}
734
735void
740
741void
746
747void
752
754// DialogSetHandler ///////////////////////////////////////////////////
756void
758{
759 RemoteParticipantDialogSet *remoteParticipantDialogSet = dynamic_cast<RemoteParticipantDialogSet *>(h.get());
760 if(remoteParticipantDialogSet)
761 {
762 remoteParticipantDialogSet->onTrying(h, msg);
763 }
764 else
765 {
766 InfoLog(<< "onTrying(AppDialogSetHandle): " << msg.brief());
767 }
768}
769
770void
772{
773 RemoteParticipantDialogSet *remoteParticipantDialogSet = dynamic_cast<RemoteParticipantDialogSet *>(h.get());
774 if(remoteParticipantDialogSet)
775 {
776 remoteParticipantDialogSet->onNonDialogCreatingProvisional(h, msg);
777 }
778 else
779 {
780 InfoLog(<< "onNonDialogCreatingProvisional(AppDialogSetHandle): " << msg.brief());
781 }
782}
783
785// ClientSubscriptionHandler ///////////////////////////////////////////////////
787void
789{
790 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onUpdatePending(h, msg, outOfOrder);
791}
792
793void
795{
796 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onUpdateActive(h, msg, outOfOrder);
797}
798
799void
801{
802 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onUpdateExtension(h, msg, outOfOrder);
803}
804
805void
807{
808 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onTerminated(h, msg);
809}
810
811void
813{
814 dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onNewSubscription(h, msg);
815}
816
817int
819{
820 return dynamic_cast<RemoteParticipant *>(h->getAppDialog().get())->onRequestRetry(h, retryMinimum, msg);
821}
822
824// ServerSubscriptionHandler ///////////////////////////////////////////////////
826void
828{
829 InfoLog(<< "onNewSubscription(ServerSubscriptionHandle): " << msg.brief());
830}
831
832void
834{
835 InfoLog(<< "onNewSubscriptionFromRefer(ServerSubscriptionHandle): " << msg.brief());
836 // Received an out-of-dialog refer request with implicit subscription
837 try
838 {
839 if(msg.exists(h_ReferTo))
840 {
841 // Check if TargetDialog header is present
842 if(msg.exists(h_TargetDialog))
843 {
844 pair<InviteSessionHandle, int> presult;
845 presult = mUserAgent->getDialogUsageManager().findInviteSession(msg.header(h_TargetDialog));
846 if(!(presult.first == InviteSessionHandle::NotValid()))
847 {
848 RemoteParticipant* participantToRefer = (RemoteParticipant*)presult.first->getAppDialog().get();
849
850 participantToRefer->onRefer(presult.first, ss, msg);
851 return;
852 }
853 }
854
855 // Create new Participant
858
859 // Set pending OOD info in Participant - causes accept or reject to be called later
860 participant->setPendingOODReferInfo(ss, msg);
861
862 // Notify application
863 ConversationProfile* profile = dynamic_cast<ConversationProfile*>(ss->getUserProfile().get());
864 if(profile)
865 {
866 onRequestOutgoingParticipant(participant->getParticipantHandle(), msg, *profile);
867 }
868 else
869 {
870 // FIXME - could we do something else here?
871 WarningLog(<<"not an instance of ConversationProfile, not calling onRequestOutgoingParticipant");
872 }
873 }
874 else
875 {
876 WarningLog (<< "Received refer w/out a Refer-To: " << msg.brief());
877 ss->send(ss->reject(400));
878 }
879 }
880 catch(BaseException &e)
881 {
882 WarningLog(<< "onNewSubscriptionFromRefer exception: " << e);
883 }
884 catch(...)
885 {
886 WarningLog(<< "onNewSubscriptionFromRefer unknown exception");
887 }
888}
889
890void
892{
893 InfoLog(<< "onRefresh(ServerSubscriptionHandle): " << msg.brief());
894}
895
896void
898{
899 InfoLog(<< "onTerminated(ServerSubscriptionHandle)");
900}
901
902void
906
907void
909{
910 WarningLog(<< "onNotifyRejected(ServerSubscriptionHandle): " << msg.brief());
911}
912
913void
915{
916 WarningLog(<< "onError(ServerSubscriptionHandle): " << msg.brief());
917}
918
919void
921{
922 InfoLog(<< "onExpiredByClient(ServerSubscriptionHandle): " << notify.brief());
923}
924
925void
927{
928 InfoLog(<< "onExpired(ServerSubscriptionHandle): " << msg.brief());
929}
930
931bool
933{
934 return true;
935}
936
939{
940 return 60;
941}
942
944// OutOfDialogHandler //////////////////////////////////////////////////////////
946void
948{
949 InfoLog(<< "onSuccess(ClientOutOfDialogReqHandle): " << msg.brief());
950}
951
952void
954{
955 InfoLog(<< "onFailure(ClientOutOfDialogReqHandle): " << msg.brief());
956}
957
958void
960{
961 InfoLog(<< "onReceivedRequest(ServerOutOfDialogReqHandle): " << msg.brief());
962
963 switch(msg.method())
964 {
965 case OPTIONS:
966 {
967 auto optionsAnswer = ood->answerOptions();
968
969 ConversationProfile* convProfile = dynamic_cast<ConversationProfile*>(ood->getUserProfile().get());
970 if (convProfile)
971 {
972 // Attach an offer to the options request
973 SdpContents sdp;
974 buildSdpOffer(convProfile, sdp);
975 optionsAnswer->setContents(&sdp);
976 }
977
978 ood->send(optionsAnswer);
979 break;
980 }
981 case REFER:
982 {
983 // Received an OOD refer request with no refer subscription
984 try
985 {
986 if(msg.exists(h_ReferTo))
987 {
988 // Check if TargetDialog header is present
989 if(msg.exists(h_TargetDialog))
990 {
991 pair<InviteSessionHandle, int> presult;
992 presult = mUserAgent->getDialogUsageManager().findInviteSession(msg.header(h_TargetDialog));
993 if(!(presult.first == InviteSessionHandle::NotValid()))
994 {
995 RemoteParticipant* participantToRefer = (RemoteParticipant*)presult.first->getAppDialog().get();
996
997 // Accept the Refer
998 ood->send(ood->accept(202 /* Refer Accepted */));
999
1000 participantToRefer->doReferNoSub(msg);
1001 return;
1002 }
1003 }
1004
1005 // Create new Participant
1007 RemoteParticipant *participant = participantDialogSet->createUACOriginalRemoteParticipant(getNewParticipantHandle());
1008
1009 // Set pending OOD info in Participant - causes accept or reject to be called later
1010 participant->setPendingOODReferInfo(ood, msg);
1011
1012 // Notify application
1013 ConversationProfile* profile = dynamic_cast<ConversationProfile*>(ood->getUserProfile().get());
1014 resip_assert(profile);
1015 if(profile)
1016 {
1017 onRequestOutgoingParticipant(participant->getParticipantHandle(), msg, *profile);
1018 }
1019 else
1020 {
1021 // FIXME - could we do something else here?
1022 WarningLog(<<"not an instance of ConversationProfile, not calling onRequestOutgoingParticipant");
1023 }
1024 }
1025 else
1026 {
1027 WarningLog (<< "onReceivedRequest(ServerOutOfDialogReqHandle): Received refer w/out a Refer-To: " << msg.brief());
1028 ood->send(ood->reject(400));
1029 }
1030 }
1031 catch(BaseException &e)
1032 {
1033 WarningLog(<< "onReceivedRequest(ServerOutOfDialogReqHandle): exception " << e);
1034 }
1035 catch(...)
1036 {
1037 WarningLog(<< "onReceivedRequest(ServerOutOfDialogReqHandle): unknown exception");
1038 }
1039
1040 break;
1041 }
1042 default:
1043 break;
1044 }
1045}
1046
1048// RedirectHandler /////////////////////////////////////////////////////////////
1050void
1052{
1053 InfoLog(<< "onRedirectReceived(AppDialogSetHandle): " << msg.brief());
1054}
1055
1056bool
1058{
1059 InfoLog(<< "onTryingNextTarget(AppDialogSetHandle): " << msg.brief());
1060 // Always allow redirection for now
1061 return true;
1062}
1063
1064
1066// ClientPagerMessageHandler ///////////////////////////////////////////////////
1068void
1070{
1071 return dynamic_cast<RemoteIMPagerParticipant*>(h->getAppDialogSet().get())->onSuccess(h, status);
1072}
1073
1074void
1075ConversationManager::onFailure(ClientPagerMessageHandle h, const SipMessage& status, std::unique_ptr<Contents> contents)
1076{
1077 return dynamic_cast<RemoteIMPagerParticipant*>(h->getAppDialogSet().get())->onFailure(h, status, std::move(contents));
1078}
1079
1080
1082// ServerPagerMessageHandler ///////////////////////////////////////////////////
1084void
1086{
1087 RemoteIMPagerParticipant* remoteIMPagerParticipant = nullptr;
1088
1089 {
1090 // First see if we already have a RemoteIMPagerParticipant for this CallId yet or not
1091 for (ParticipantMap::iterator i = mParticipants.begin(); i != mParticipants.end(); i++)
1092 {
1093 remoteIMPagerParticipant = dynamic_cast<RemoteIMPagerParticipant*>(i->second);
1094 if (remoteIMPagerParticipant != nullptr && remoteIMPagerParticipant->doesMessageMatch(message))
1095 {
1096 // Found existing remoteIMPagerParticipant, break out
1097 break;
1098 }
1099 remoteIMPagerParticipant = nullptr;
1100 }
1101 }
1102
1103 if (remoteIMPagerParticipant == nullptr && mUserAgent != nullptr)
1104 {
1105 // If we make it here, we didn't find an existing RemoteIMPagerParticipant via the callId,
1106 // for From header matching create a new one now.
1107 remoteIMPagerParticipant = new RemoteIMPagerParticipant(getNewParticipantHandle(), *this);
1108 }
1109
1110 if (remoteIMPagerParticipant)
1111 {
1112 remoteIMPagerParticipant->onMessageArrived(h, message);
1113 }
1114 else
1115 {
1116 h->send(h->reject(500)); // Reject with internal server error
1117 }
1118
1119}
1120
1121
1122/* ====================================================================
1123
1124 Copyright (c) 2021-2023, SIP Spectrum, Inc. http://www.sipspectrum.com
1125 Copyright (c) 2021, Daniel Pocock https://danielpocock.com
1126 Copyright (c) 2007-2008, Plantronics, Inc.
1127 All rights reserved.
1128
1129 Redistribution and use in source and binary forms, with or without
1130 modification, are permitted provided that the following conditions are
1131 met:
1132
1133 1. Redistributions of source code must retain the above copyright
1134 notice, this list of conditions and the following disclaimer.
1135
1136 2. Redistributions in binary form must reproduce the above copyright
1137 notice, this list of conditions and the following disclaimer in the
1138 documentation and/or other materials provided with the distribution.
1139
1140 3. Neither the name of Plantronics nor the names of its contributors
1141 may be used to endorse or promote products derived from this
1142 software without specific prior written permission.
1143
1144 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1145 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1146 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1147 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1148 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1149 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1150 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1151 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1152 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1153 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1154 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1155
1156 ==================================================================== */
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define resip_assert(x)
This file needs to work for both C and C++ code.
virtual void onConnectedConfirmed(resip::InviteSessionHandle, const resip::SipMessage &msg)
called when ACK (with out an answer) is received for initial invite (UAS)
virtual void onReferNoSub(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onMediaResourceParticipantFailed(ParticipantHandle partHandle)
Notifies an application about a failure in a media resource participant.
virtual bool getBufferFromMediaResourceCache(const resip::Data &name, resip::Data **buffer, int *type)
This function is used to retrieve a chunk of memory from the media/prompt cache.
virtual void onMessage(resip::InviteSessionHandle, const resip::SipMessage &msg)
called when MESSAGE message is received
virtual void onMessageFailure(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void joinConversation(ConversationHandle sourceConvHandle, ConversationHandle destConvHandle)
Joins all participants from source conversation into destination conversation and destroys source con...
virtual void onError(resip::ServerSubscriptionHandle, const resip::SipMessage &msg)
virtual ParticipantHandle createRemoteIMSessionParticipant(const resip::NameAddr &destination, ParticipantForkSelectMode forkSelectMode=ForkSelectAutomatic, const std::shared_ptr< ConversationProfile > &callerProfile=nullptr, const std::multimap< resip::Data, resip::Data > &extraHeaders=std::multimap< resip::Data, resip::Data >())
Creates a new remote IM participant that will attempted to be reached immediately at the specified ad...
virtual void redirectParticipant(ParticipantHandle partHandle, const resip::NameAddr &destination, unsigned int redirectCode=302, RedirectSuccessCondition successCondition=RedirectSuccessOnConnected)
Redirects the participant to another endpoint.
virtual void startApplicationTimer(unsigned int timerId, unsigned int timerData1, unsigned int timerData2, unsigned int durationMs)
This function is used to start a timer on behalf of recon based application.
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 onReferAccepted(resip::InviteSessionHandle, resip::ClientSubscriptionHandle, const resip::SipMessage &msg)
called when an REFER message receives an accepted response
std::set< ParticipantHandle > getParticipantHandlesByType(ParticipantType participantType) const
virtual bool onTryingNextTarget(resip::AppDialogSetHandle, const resip::SipMessage &request)
virtual void requestKeyframeFromPeer(ParticipantHandle partHandle, std::chrono::duration< double > durationMs)
virtual void onFailure(resip::ClientInviteSessionHandle h, const resip::SipMessage &msg)
Received a failure response from UAS.
virtual void onNonDialogCreatingProvisional(resip::AppDialogSetHandle, const resip::SipMessage &msg)
virtual void moveParticipant(ParticipantHandle partHandle, ConversationHandle sourceConvHandle, ConversationHandle destConvHandle)
Removed the specified participant from the specified conversation.
virtual void onConnected(resip::ClientInviteSessionHandle h, const resip::SipMessage &msg)
called when a dialog initiated as a UAC enters the connected state
virtual void onExpiredByClient(resip::ServerSubscriptionHandle, const resip::SipMessage &sub, resip::SipMessage &notify)
virtual bool hasDefaultExpires() const
dcm! – a bit clunky, but really want these objects to not have state
virtual void setMediaStackAdapter(std::shared_ptr< MediaStackAdapter > mediaStackAdapter)
virtual void onOfferRequired(resip::InviteSessionHandle, const resip::SipMessage &msg)
called when an Invite w/out offer is sent, or any other context which requires an offer from the user
virtual void answerParticipant(ParticipantHandle partHandle)
Signal to the participant that the call is answered.
virtual void setUserAgent(UserAgent *userAgent)
virtual void onInfoSuccess(resip::InviteSessionHandle, const resip::SipMessage &msg)
called when response to INFO message is received
virtual void onUpdateExtension(resip::ClientSubscriptionHandle, const resip::SipMessage &notify, bool outOfOrder)
void post(resip::Message *message)
virtual void onRedirected(resip::ClientInviteSessionHandle, const resip::SipMessage &msg)
called when a 3xx with valid targets is encountered in an early dialog This is different then getti...
virtual void modifyParticipantRecordChannel(ParticipantHandle partHandle, unsigned int channelNum)
If the participant is mixed to a multi-channel recording resource, then this controls if a participan...
ParticipantHandle getNewParticipantHandle()
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage &msg)
called when an REFER message is received.
virtual void rejectParticipant(ParticipantHandle partHandle, unsigned int rejectCode)
Rejects an incoming remote participant with the specified code.
virtual void onMessageSuccess(resip::InviteSessionHandle, const resip::SipMessage &msg)
called when response to MESSAGE message is received
virtual void onAnswer(resip::InviteSessionHandle, const resip::SipMessage &msg, const resip::SdpContents &)
called when an answer is received - has nothing to do with user answering the call
virtual void onProvisional(resip::ClientInviteSessionHandle, const resip::SipMessage &msg)
called when dialog enters the Early state - typically after getting 18x
MediaStackAdapter & getMediaStackAdapter()
virtual void sendIMToParticipant(ParticipantHandle partHandle, std::unique_ptr< resip::Contents > contents)
Sends a MESSAGE with specified Contents to RemoteParticipant, RemoteIMPagerParticipant,...
virtual void onUpdatePending(resip::ClientSubscriptionHandle h, const resip::SipMessage &notify, bool outOfOrder)
virtual void onSuccess(resip::ClientOutOfDialogReqHandle, const resip::SipMessage &response)
virtual ParticipantHandle createRemoteParticipant(ConversationHandle convHandle, const resip::NameAddr &destination, ParticipantForkSelectMode forkSelectMode=ForkSelectAutomatic, const std::shared_ptr< ConversationProfile > &conversationProfile=nullptr, const std::multimap< resip::Data, resip::Data > &extraHeaders=std::multimap< resip::Data, resip::Data >())
Creates a new remote participant in the specified conversation that is attempted to be reached at the...
virtual void onUpdateActive(resip::ClientSubscriptionHandle h, const resip::SipMessage &notify, bool outOfOrder)
virtual void requestKeyframe(ParticipantHandle partHandle, std::chrono::duration< double > durationMs)
virtual void onTrying(resip::AppDialogSetHandle, const resip::SipMessage &msg)
static const char * participantTypeToString(ParticipantType partType)
virtual void onExpired(resip::ServerSubscriptionHandle, resip::SipMessage &notify)
virtual void destroyParticipant(ParticipantHandle partHandle)
Ends connections to the participant and removes it from all active conversations.
virtual void onForkDestroyed(resip::ClientInviteSessionHandle)
called when a fork that was created through a 1xx never receives a 2xx because another fork answered ...
virtual void onInfo(resip::InviteSessionHandle, const resip::SipMessage &msg)
called when INFO message is received the application must call acceptNIT() or rejectNIT() once it is ...
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 onOffer(resip::InviteSessionHandle handle, const resip::SipMessage &msg, const resip::SdpContents &offer)
called when an offer is received - must send an answer soon after this
virtual void onNotifyRejected(resip::ServerSubscriptionHandle, const resip::SipMessage &msg)
virtual void addBufferToMediaResourceCache(const resip::Data &name, const resip::Data &buffer, int type)
This function is used to add a chunk of memory to a media/prompt cache.
virtual void onDtmfEvent(ParticipantHandle partHandle, int dtmf, int duration, bool up)=0
Notifies an application when an RFC2833 DTMF event is received from a particular remote participant.
Conversation * getConversation(ConversationHandle convHandle)
virtual void removeParticipant(ConversationHandle convHandle, ParticipantHandle partHandle)
Removed the specified participant from the specified conversation.
virtual void alertParticipant(ParticipantHandle partHandle, bool earlyFlag=true)
Signal to the participant that it should provide ringback.
virtual void requestKeyframeFromPeerRecurring(ParticipantHandle partHandle, std::chrono::duration< double > interval)
ConversationManager(std::shared_ptr< MediaStackAdapter > mediaStackAdapter, std::shared_ptr< resip::ConfigParse > configParse=std::shared_ptr< resip::ConfigParse >())
virtual void addParticipant(ConversationHandle convHandle, ParticipantHandle partHandle)
Adds the specified participant to the specified conversation.
virtual void onRequestOutgoingParticipant(ParticipantHandle partHandle, const resip::SipMessage &msg, ConversationProfile &conversationProfile)=0
Notifies an application about a new remote participant that is trying to be contacted.
virtual void onOfferRequestRejected(resip::InviteSessionHandle, const resip::SipMessage &msg)
Called when an error response is received for a reinvite-nobody request (via requestOffer)
virtual void onReadyToSend(resip::ServerSubscriptionHandle, resip::SipMessage &)
virtual void onReferRejected(resip::InviteSessionHandle, const resip::SipMessage &msg)
called when an REFER message receives a failure response
virtual void destroyConversation(ConversationHandle convHandle)
Destroys an existing Conversation, and ends all participants that solely belong to this conversation.
ConversationHandle getNewConversationHandle()
virtual RemoteParticipant * createAppropriateRemoteParticipantInstance(resip::DialogUsageManager &dum, RemoteParticipantDialogSet &rpds)
virtual void onRemoteSdpChanged(resip::InviteSessionHandle, const resip::SipMessage &msg, const resip::SdpContents &sdp)
called when a modified body is received in a 2xx response to a session-timer reINVITE.
virtual void onRefresh(resip::ServerSubscriptionHandle, const resip::SipMessage &sub)
virtual void holdParticipant(ParticipantHandle partHandle, bool hold)
Manually puts a paricipant on hold, or takes off hold without needing to move it in and out of any co...
virtual void onOfferRejected(resip::InviteSessionHandle, const resip::SipMessage *msg)
called if an offer in a UPDATE or re-INVITE was rejected - not real useful.
virtual ParticipantHandle createRemoteIMPagerParticipant(const resip::NameAddr &destination, const std::shared_ptr< ConversationProfile > &conversationProfile=nullptr)
Creates a new remote IM participant that will be attempted to be reached when sendIMToParticipant is ...
virtual void onNewSession(resip::ClientInviteSessionHandle h, resip::InviteSession::OfferAnswerType oat, const resip::SipMessage &msg)
called when an initial INVITE or the intial response to an outoing invite
virtual void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage &message)
virtual void onTerminated(resip::InviteSessionHandle h, resip::InviteSessionHandler::TerminatedReason reason, const resip::SipMessage *msg)
virtual void onReceivedRequest(resip::ServerOutOfDialogReqHandle, const resip::SipMessage &request)
virtual void onNewSubscription(resip::ClientSubscriptionHandle h, const resip::SipMessage &notify)
virtual uint32_t getDefaultExpires() const
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.
std::set< ConversationHandle > getConversationHandles() const
virtual ParticipantHandle createLocalParticipant()
Creates a new local participant in the specified conversation (if supported).
virtual void onParticipantVoiceActivity(recon::ParticipantHandle partHandle, bool on, bool inbound)
Notifies an application when voice activity is detected to on or off from a remote participant.
virtual void onInfoFailure(resip::InviteSessionHandle, const resip::SipMessage &msg)
virtual void onStaleCallTimeout(resip::ClientInviteSessionHandle)
UAC gets no final response within the stale call timeout (default is 3 minutes).
virtual void onEarlyMedia(resip::ClientInviteSessionHandle, const resip::SipMessage &, const resip::SdpContents &)
called when an in-dialog provisional response is received that contains a body
virtual void onRedirectReceived(resip::AppDialogSetHandle, const resip::SipMessage &response)
virtual int onRequestRetry(resip::ClientSubscriptionHandle h, int retryMinimum, const resip::SipMessage &notify)
virtual void outputBridgeMatrix(ConversationHandle convHandle=0)
Logs a multiline representation of the current state of the mixing matrix.
virtual void onNewSubscriptionFromRefer(resip::ServerSubscriptionHandle, const resip::SipMessage &sub)
virtual RemoteParticipantDialogSet * createRemoteIMSessionParticipantDialogSetInstance(ConversationManager::ParticipantForkSelectMode forkSelectMode=ConversationManager::ForkSelectAutomatic, std::shared_ptr< ConversationProfile > conversationProfile=nullptr)
This class extends the resip UserProfile to include Conversation Manager specific settings.
virtual resip::SdpContents & sessionCaps()
Get/Set the session capabilities.
This class is used to manage membership of participants.
ConversationHandle getHandle()
The classes defined here are used to pass commands from the application thread to the UserAgent threa...
This class represents an event that happens to a media participant.
bool getFromCache(const resip::Data &name, resip::Data **buffer, int *type)
void addToCache(const resip::Data &name, const resip::Data &buffer, int type)
This class represents a media resource participant.
virtual RemoteParticipant * createRemoteParticipantInstance(resip::DialogUsageManager &dum, RemoteParticipantDialogSet &rpds)=0
virtual RemoteParticipantDialogSet * createRemoteParticipantDialogSetInstance(ConversationManager::ParticipantForkSelectMode forkSelectMode=ConversationManager::ForkSelectAutomatic, std::shared_ptr< ConversationProfile > conversationProfile=nullptr)=0
This is the base class for a RemoteParticipant, LocalParticipant and a MediaResourceParticipant.
virtual ParticipantHandle getParticipantHandle()
virtual ConversationManager::ParticipantType getParticipantType()
This class represent an Instant Message remote participant.
virtual void onMessageArrived(resip::ServerPagerMessageHandle, const resip::SipMessage &message)
virtual bool doesMessageMatch(resip::SipMessage message)
This class is used by Invite DialogSets used for IM Sessions only (m=message SDP).
This class is used by Invite DialogSets.
virtual void onNonDialogCreatingProvisional(resip::AppDialogSetHandle, const resip::SipMessage &msg)
virtual void onTrying(resip::AppDialogSetHandle, const resip::SipMessage &msg)
virtual RemoteParticipant * createUACOriginalRemoteParticipant(ParticipantHandle handle)
This class represent a remote participant.
virtual void onRefer(resip::InviteSessionHandle, resip::ServerSubscriptionHandle, const resip::SipMessage &msg)
virtual void setPendingOODReferInfo(resip::ServerOutOfDialogReqHandle ood, const resip::SipMessage &referMsg)
virtual void doReferNoSub(const resip::SipMessage &msg)
This class is one of two main classes of concern to an application using the UserAgent library.
Definition UserAgent.hxx:56
resip::DialogUsageManager & getDialogUsageManager()
virtual void post(resip::Message *msg)
A base-class for messages used by applications.
The abstract base-class for all exceptions thrown by resip code.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
InviteSessionHandle findInviteSession(const DialogId &id)
std::shared_ptr< UserProfile > getUserProfile() const
AppDialogHandle getAppDialog() const
static Handle< InviteSession > NotValid()
Definition Handle.hxx:84
TerminatedReason
called when an dialog enters the terminated state - this can happen after getting a BYE,...
The base-class used for message-passing.
Definition Message.hxx:21
virtual Brief brief() const
return a facet for brief encoding of message
Definition Message.cxx:16
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
Definition NameAddr.hxx:19
const uint64_t & getVersion() const
returns the session version
const uint64_t & getSessionId() const
returns the session ID
std::list< std::reference_wrapper< Medium > > getMediaByType(const Data &type)
return session Media lines filtered by type
const Origin & origin() const
return session Origin line
const MediumContainer & media() const
return session Media lines
Provides an interface to process and generate SDP bodies (MIME content-type application/sdp).
Session & session()
get the parsed SDP
void send(std::shared_ptr< SipMessage > msg) override
std::shared_ptr< SipMessage > reject(int responseCode)
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
static uint64_t getTimeMicroSec()
Returns the current clock time in microseconds.
Definition Timer.hxx:66
void post(Message *msg)
Posts a Message to this TransactionUser's fifo.
Represents the "SIP-URI" and "SIPS-URI" elements in the RFC 3261 grammar.
Definition Uri.hxx:22
unsigned int ConversationHandle
unsigned int ParticipantHandle
dcm! – add UnusedChecking(_enum) below;
unsigned int uint32_t
Definition stdint.h:85
unsigned __int64 uint64_t
Definition stdint.h:95