/[resiprocate]/main/resip/recon/RemoteParticipant.cxx
ViewVC logotype

Contents of /main/resip/recon/RemoteParticipant.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9493 - (show annotations) (download)
Sat Apr 7 10:56:50 2012 UTC (7 years, 9 months ago) by dpocock
File MIME type: text/plain
File size: 91510 byte(s)
Include config.h from even more places where it may be needed
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #include "ConversationManager.hxx"
6
7 #include "sdp/SdpHelperResip.hxx"
8 #include "sdp/Sdp.hxx"
9
10 #include <sdp/SdpCodec.h> // sipX SdpCodec
11
12 #include "RemoteParticipant.hxx"
13 #include "Conversation.hxx"
14 #include "UserAgent.hxx"
15 #include "DtmfEvent.hxx"
16 #include "ReconSubsystem.hxx"
17
18 #include <rutil/Log.hxx>
19 #include <rutil/Logger.hxx>
20 #include <rutil/DnsUtil.hxx>
21 #include <rutil/Random.hxx>
22 #include <resip/stack/SipFrag.hxx>
23 #include <resip/stack/ExtensionHeader.hxx>
24 #include <resip/dum/DialogUsageManager.hxx>
25 #include <resip/dum/ClientInviteSession.hxx>
26 #include <resip/dum/ServerInviteSession.hxx>
27 #include <resip/dum/ClientSubscription.hxx>
28 #include <resip/dum/ServerOutOfDialogReq.hxx>
29 #include <resip/dum/ServerSubscription.hxx>
30
31 #include <rutil/WinLeakCheck.hxx>
32
33 using namespace recon;
34 using namespace sdpcontainer;
35 using namespace resip;
36 using namespace std;
37
38 #define RESIPROCATE_SUBSYSTEM ReconSubsystem::RECON
39
40 // UAC
41 RemoteParticipant::RemoteParticipant(ParticipantHandle partHandle,
42 ConversationManager& conversationManager,
43 DialogUsageManager& dum,
44 RemoteParticipantDialogSet& remoteParticipantDialogSet)
45 : Participant(partHandle, conversationManager),
46 AppDialog(dum),
47 mDum(dum),
48 mDialogSet(remoteParticipantDialogSet),
49 mDialogId(Data::Empty, Data::Empty, Data::Empty),
50 mState(Connecting),
51 mOfferRequired(false),
52 mLocalHold(true),
53 mRemoteHold(false),
54 mLocalSdp(0),
55 mRemoteSdp(0)
56 {
57 InfoLog(<< "RemoteParticipant created (UAC), handle=" << mHandle);
58 }
59
60 // UAS - or forked leg
61 RemoteParticipant::RemoteParticipant(ConversationManager& conversationManager,
62 DialogUsageManager& dum,
63 RemoteParticipantDialogSet& remoteParticipantDialogSet)
64 : Participant(conversationManager),
65 AppDialog(dum),
66 mDum(dum),
67 mDialogSet(remoteParticipantDialogSet),
68 mDialogId(Data::Empty, Data::Empty, Data::Empty),
69 mState(Connecting),
70 mOfferRequired(false),
71 mLocalHold(true),
72 mLocalSdp(0),
73 mRemoteSdp(0)
74 {
75 InfoLog(<< "RemoteParticipant created (UAS or forked leg), handle=" << mHandle);
76 }
77
78 RemoteParticipant::~RemoteParticipant()
79 {
80 if(!mDialogId.getCallId().empty())
81 {
82 mDialogSet.removeDialog(mDialogId);
83 }
84 // unregister from Conversations
85 // Note: ideally this functionality would exist in Participant Base class - but dynamic_cast required in unregisterParticipant will not work
86 ConversationMap::iterator it;
87 for(it = mConversations.begin(); it != mConversations.end(); it++)
88 {
89 it->second->unregisterParticipant(this);
90 }
91 mConversations.clear();
92
93 // Delete Sdp memory
94 if(mLocalSdp) delete mLocalSdp;
95 if(mRemoteSdp) delete mRemoteSdp;
96
97 InfoLog(<< "RemoteParticipant destroyed, handle=" << mHandle);
98 }
99
100 unsigned int
101 RemoteParticipant::getLocalRTPPort()
102 {
103 return mDialogSet.getLocalRTPPort();
104 }
105
106 //static const resip::ExtensionHeader h_AlertInfo("Alert-Info");
107
108 void
109 RemoteParticipant::initiateRemoteCall(const NameAddr& destination)
110 {
111 SdpContents offer;
112 SharedPtr<ConversationProfile> profile = mConversationManager.getUserAgent()->getDefaultOutgoingConversationProfile();
113 buildSdpOffer(mLocalHold, offer);
114 SharedPtr<SipMessage> invitemsg = mDum.makeInviteSession(
115 destination,
116 profile,
117 &offer,
118 &mDialogSet);
119
120 mDialogSet.sendInvite(invitemsg);
121
122 // Clear any pending hold/unhold requests since our offer/answer here will handle it
123 if(mPendingRequest.mType == Hold ||
124 mPendingRequest.mType == Unhold)
125 {
126 mPendingRequest.mType = None;
127 }
128
129 // Adjust RTP streams
130 adjustRTPStreams(true);
131
132 // Special case of this call - since call in addToConversation will not work, since we didn't know our bridge port at that time
133 applyBridgeMixWeights();
134 }
135
136 int
137 RemoteParticipant::getConnectionPortOnBridge()
138 {
139 if(mDialogSet.getActiveRemoteParticipantHandle() == mHandle)
140 {
141 return mDialogSet.getConnectionPortOnBridge();
142 }
143 else
144 {
145 // If this is not active fork leg, then we don't want to effect the bridge mixer.
146 // Note: All forked endpoints/participants have the same connection port on the bridge
147 return -1;
148 }
149 }
150
151 int
152 RemoteParticipant::getMediaConnectionId()
153 {
154 return mDialogSet.getMediaConnectionId();
155 }
156
157 void
158 RemoteParticipant::destroyParticipant()
159 {
160 try
161 {
162 if(mState != Terminating)
163 {
164 stateTransition(Terminating);
165 if(mInviteSessionHandle.isValid())
166 {
167 mInviteSessionHandle->end();
168 }
169 else
170 {
171 mDialogSet.end();
172 }
173 }
174 }
175 catch(BaseException &e)
176 {
177 WarningLog(<< "RemoteParticipant::destroyParticipant exception: " << e);
178 }
179 catch(...)
180 {
181 WarningLog(<< "RemoteParticipant::destroyParticipant unknown exception");
182 }
183 }
184
185 void
186 RemoteParticipant::addToConversation(Conversation* conversation, unsigned int inputGain, unsigned int outputGain)
187 {
188 Participant::addToConversation(conversation, inputGain, outputGain);
189 if(mLocalHold && !conversation->shouldHold()) // If we are on hold and we now shouldn't be, then unhold
190 {
191 unhold();
192 }
193 }
194
195 void
196 RemoteParticipant::removeFromConversation(Conversation *conversation)
197 {
198 Participant::removeFromConversation(conversation);
199 checkHoldCondition();
200 }
201
202 void
203 RemoteParticipant::checkHoldCondition()
204 {
205 // Return to Offer a hold sdp if we are not in any conversations, or all the conversations we are in have conditions such that a hold is required
206 bool shouldHold = true;
207 ConversationMap::iterator it;
208 for(it = mConversations.begin(); it != mConversations.end(); it++)
209 {
210 if(!it->second->shouldHold())
211 {
212 shouldHold = false;
213 break;
214 }
215 }
216 if(mLocalHold != shouldHold)
217 {
218 if(shouldHold)
219 {
220 hold();
221 }
222 else
223 {
224 unhold();
225 }
226 }
227 }
228
229 void
230 RemoteParticipant::stateTransition(State state)
231 {
232 Data stateName;
233
234 switch(state)
235 {
236 case Connecting:
237 stateName = "Connecting"; break;
238 case Accepted:
239 stateName = "Accepted"; break;
240 case Connected:
241 stateName = "Connected"; break;
242 case Redirecting:
243 stateName = "Redirecting"; break;
244 case Holding:
245 stateName = "Holding"; break;
246 case Unholding:
247 stateName = "Unholding"; break;
248 case Replacing:
249 stateName = "Replacing"; break;
250 case PendingOODRefer:
251 stateName = "PendingOODRefer"; break;
252 case Terminating:
253 stateName = "Terminating"; break;
254 default:
255 stateName = "Unknown: " + Data(state); break;
256 }
257 InfoLog( << "RemoteParticipant::stateTransition of handle=" << mHandle << " to state=" << stateName );
258 mState = state;
259
260 if(mState == Connected && mPendingRequest.mType != None)
261 {
262 PendingRequestType type = mPendingRequest.mType;
263 mPendingRequest.mType = None;
264 switch(type)
265 {
266 case Hold:
267 hold();
268 break;
269 case Unhold:
270 unhold();
271 break;
272 case Redirect:
273 redirect(mPendingRequest.mDestination);
274 break;
275 case RedirectTo:
276 redirectToParticipant(mPendingRequest.mDestInviteSessionHandle);
277 break;
278 case None:
279 break;
280 }
281 }
282 }
283
284 void
285 RemoteParticipant::accept()
286 {
287 try
288 {
289 // Accept SIP call if required
290 if(mState == Connecting && mInviteSessionHandle.isValid())
291 {
292 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
293 if(sis && !sis->isAccepted())
294 {
295 if(getLocalRTPPort() == 0)
296 {
297 WarningLog(<< "RemoteParticipant::accept cannot accept call, since no free RTP ports, rejecting instead.");
298 sis->reject(480); // Temporarily Unavailable - no free RTP ports
299 return;
300 }
301 // Clear any pending hold/unhold requests since our offer/answer here will handle it
302 if(mPendingRequest.mType == Hold ||
303 mPendingRequest.mType == Unhold)
304 {
305 mPendingRequest.mType = None;
306 }
307 if(mOfferRequired)
308 {
309 provideOffer(true /* postOfferAccept */);
310 }
311 else if(mPendingOffer.get() != 0)
312 {
313 provideAnswer(*mPendingOffer.get(), true /* postAnswerAccept */, false /* postAnswerAlert */);
314 }
315 else
316 {
317 // It is possible to get here if the app calls alert with early true. There is special logic in
318 // RemoteParticipantDialogSet::accept to handle the case then an alert call followed immediately by
319 // accept. In this case the answer from the alert will be queued waiting on the flow to be ready, and
320 // we need to ensure the accept call is also delayed until the answer completes.
321 mDialogSet.accept(mInviteSessionHandle);
322 }
323 stateTransition(Accepted);
324 }
325 }
326 // Accept Pending OOD Refer if required
327 else if(mState == PendingOODRefer)
328 {
329 acceptPendingOODRefer();
330 }
331 else
332 {
333 WarningLog(<< "RemoteParticipant::accept called in invalid state: " << mState);
334 }
335 }
336 catch(BaseException &e)
337 {
338 WarningLog(<< "RemoteParticipant::accept exception: " << e);
339 }
340 catch(...)
341 {
342 WarningLog(<< "RemoteParticipant::accept unknown exception");
343 }
344 }
345
346 void
347 RemoteParticipant::alert(bool earlyFlag)
348 {
349 try
350 {
351 if(mState == Connecting && mInviteSessionHandle.isValid())
352 {
353 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
354 if(sis && !sis->isAccepted())
355 {
356 if(earlyFlag && mPendingOffer.get() != 0)
357 {
358 if(getLocalRTPPort() == 0)
359 {
360 WarningLog(<< "RemoteParticipant::alert cannot alert call with early media, since no free RTP ports, rejecting instead.");
361 sis->reject(480); // Temporarily Unavailable - no free RTP ports
362 return;
363 }
364
365 provideAnswer(*mPendingOffer.get(), false /* postAnswerAccept */, true /* postAnswerAlert */);
366 mPendingOffer.release();
367 }
368 else
369 {
370 sis->provisional(180, earlyFlag);
371 }
372 }
373 }
374 else
375 {
376 WarningLog(<< "RemoteParticipant::alert called in invalid state: " << mState);
377 }
378 }
379 catch(BaseException &e)
380 {
381 WarningLog(<< "RemoteParticipant::alert exception: " << e);
382 }
383 catch(...)
384 {
385 WarningLog(<< "RemoteParticipant::alert unknown exception");
386 }
387 }
388
389 void
390 RemoteParticipant::reject(unsigned int rejectCode)
391 {
392 try
393 {
394 // Reject UAS Invite Session if required
395 if(mState == Connecting && mInviteSessionHandle.isValid())
396 {
397 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
398 if(sis && !sis->isAccepted())
399 {
400 sis->reject(rejectCode);
401 }
402 }
403 // Reject Pending OOD Refer request if required
404 else if(mState == PendingOODRefer)
405 {
406 rejectPendingOODRefer(rejectCode);
407 }
408 else
409 {
410 WarningLog(<< "RemoteParticipant::reject called in invalid state: " << mState);
411 }
412 }
413 catch(BaseException &e)
414 {
415 WarningLog(<< "RemoteParticipant::reject exception: " << e);
416 }
417 catch(...)
418 {
419 WarningLog(<< "RemoteParticipant::reject unknown exception");
420 }
421 }
422
423 void
424 RemoteParticipant::redirect(NameAddr& destination)
425 {
426 try
427 {
428 if(mPendingRequest.mType == None)
429 {
430 if((mState == Connecting || mState == Accepted || mState == Connected) && mInviteSessionHandle.isValid())
431 {
432 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
433 // If this is a UAS session and we haven't sent a final response yet - then redirect via 302 response
434 if(sis && !sis->isAccepted() && mState == Connecting)
435 {
436 NameAddrs destinations;
437 destinations.push_back(destination);
438 mConversationManager.onParticipantRedirectSuccess(mHandle);
439 sis->redirect(destinations);
440 }
441 else if(mInviteSessionHandle->isConnected()) // redirect via blind transfer
442 {
443 mInviteSessionHandle->refer(NameAddr(destination.uri()) /* remove tags */, true /* refersub */);
444 stateTransition(Redirecting);
445 }
446 else
447 {
448 mPendingRequest.mType = Redirect;
449 mPendingRequest.mDestination = destination;
450 }
451 }
452 else if(mState == PendingOODRefer)
453 {
454 redirectPendingOODRefer(destination);
455 }
456 else
457 {
458 mPendingRequest.mType = Redirect;
459 mPendingRequest.mDestination = destination;
460 }
461 }
462 else
463 {
464 WarningLog(<< "RemoteParticipant::redirect error: request pending");
465 mConversationManager.onParticipantRedirectFailure(mHandle, 406 /* Not Acceptable */);
466 }
467 }
468 catch(BaseException &e)
469 {
470 WarningLog(<< "RemoteParticipant::redirect exception: " << e);
471 }
472 catch(...)
473 {
474 WarningLog(<< "RemoteParticipant::redirect unknown exception");
475 }
476 }
477
478 void
479 RemoteParticipant::redirectToParticipant(InviteSessionHandle& destParticipantInviteSessionHandle)
480 {
481 try
482 {
483 if(destParticipantInviteSessionHandle.isValid())
484 {
485 if(mPendingRequest.mType == None)
486 {
487 if((mState == Connecting || mState == Accepted || mState == Connected) && mInviteSessionHandle.isValid())
488 {
489 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
490 // If this is a UAS session and we haven't sent a final response yet - then redirect via 302 response
491 if(sis && !sis->isAccepted() && mState == Connecting)
492 {
493 NameAddrs destinations;
494 destinations.push_back(NameAddr(destParticipantInviteSessionHandle->peerAddr().uri())); // ensure we don't get to or from tag by only using the inner uri()
495 mConversationManager.onParticipantRedirectSuccess(mHandle);
496 sis->redirect(destinations);
497 }
498 else if(mInviteSessionHandle->isConnected()) // redirect via attended transfer (with replaces)
499 {
500 mInviteSessionHandle->refer(NameAddr(destParticipantInviteSessionHandle->peerAddr().uri()) /* remove tags */, destParticipantInviteSessionHandle /* session to replace) */, true /* refersub */);
501 stateTransition(Redirecting);
502 }
503 else
504 {
505 mPendingRequest.mType = RedirectTo;
506 mPendingRequest.mDestInviteSessionHandle = destParticipantInviteSessionHandle;
507 }
508 }
509 else
510 {
511 mPendingRequest.mType = RedirectTo;
512 mPendingRequest.mDestInviteSessionHandle = destParticipantInviteSessionHandle;
513 }
514 }
515 else
516 {
517 WarningLog(<< "RemoteParticipant::redirectToParticipant error: request pending");
518 mConversationManager.onParticipantRedirectFailure(mHandle, 406 /* Not Acceptable */);
519 }
520 }
521 else
522 {
523 WarningLog(<< "RemoteParticipant::redirectToParticipant error: destParticipant has no valid InviteSession");
524 mConversationManager.onParticipantRedirectFailure(mHandle, 406 /* Not Acceptable */);
525 }
526 }
527 catch(BaseException &e)
528 {
529 WarningLog(<< "RemoteParticipant::redirectToParticipant exception: " << e);
530 }
531 catch(...)
532 {
533 WarningLog(<< "RemoteParticipant::redirectToParticipant unknown exception");
534 }
535 }
536
537 void
538 RemoteParticipant::hold()
539 {
540 mLocalHold=true;
541
542 InfoLog(<< "RemoteParticipant::hold request: handle=" << mHandle);
543
544 try
545 {
546 if(mPendingRequest.mType == None)
547 {
548 if(mState == Connected && mInviteSessionHandle.isValid())
549 {
550 provideOffer(false /* postOfferAccept */);
551 stateTransition(Holding);
552 }
553 else
554 {
555 mPendingRequest.mType = Hold;
556 }
557 }
558 else if(mPendingRequest.mType == Unhold)
559 {
560 mPendingRequest.mType = None; // Unhold pending, so move to do nothing
561 return;
562 }
563 else if(mPendingRequest.mType == Hold)
564 {
565 return; // Hold already pending
566 }
567 else
568 {
569 WarningLog(<< "RemoteParticipant::hold error: request already pending");
570 }
571 }
572 catch(BaseException &e)
573 {
574 WarningLog(<< "RemoteParticipant::hold exception: " << e);
575 }
576 catch(...)
577 {
578 WarningLog(<< "RemoteParticipant::hold unknown exception");
579 }
580 }
581
582 void
583 RemoteParticipant::unhold()
584 {
585 mLocalHold=false;
586
587 InfoLog(<< "RemoteParticipant::unhold request: handle=" << mHandle);
588
589 try
590 {
591 if(mPendingRequest.mType == None)
592 {
593 if(mState == Connected && mInviteSessionHandle.isValid())
594 {
595 provideOffer(false /* postOfferAccept */);
596 stateTransition(Unholding);
597 }
598 else
599 {
600 mPendingRequest.mType = Unhold;
601 }
602 }
603 else if(mPendingRequest.mType == Hold)
604 {
605 mPendingRequest.mType = None; // Hold pending, so move do nothing
606 return;
607 }
608 else if(mPendingRequest.mType == Unhold)
609 {
610 return; // Unhold already pending
611 }
612 else
613 {
614 WarningLog(<< "RemoteParticipant::unhold error: request already pending");
615 }
616 }
617 catch(BaseException &e)
618 {
619 WarningLog(<< "RemoteParticipant::unhold exception: " << e);
620 }
621 catch(...)
622 {
623 WarningLog(<< "RemoteParticipant::unhold unknown exception");
624 }
625 }
626
627 void
628 RemoteParticipant::setPendingOODReferInfo(ServerOutOfDialogReqHandle ood, const SipMessage& referMsg)
629 {
630 stateTransition(PendingOODRefer);
631 mPendingOODReferMsg = referMsg;
632 mPendingOODReferNoSubHandle = ood;
633 }
634
635 void
636 RemoteParticipant::setPendingOODReferInfo(ServerSubscriptionHandle ss, const SipMessage& referMsg)
637 {
638 stateTransition(PendingOODRefer);
639 mPendingOODReferMsg = referMsg;
640 mPendingOODReferSubHandle = ss;
641 }
642
643 void
644 RemoteParticipant::acceptPendingOODRefer()
645 {
646 if(mState == PendingOODRefer)
647 {
648 SharedPtr<UserProfile> profile;
649 bool accepted = false;
650 if(mPendingOODReferNoSubHandle.isValid())
651 {
652 mPendingOODReferNoSubHandle->send(mPendingOODReferNoSubHandle->accept(202)); // Accept OOD Refer
653 profile = mPendingOODReferNoSubHandle->getUserProfile();
654 accepted = true;
655 }
656 else if(mPendingOODReferSubHandle.isValid())
657 {
658 mPendingOODReferSubHandle->send(mPendingOODReferSubHandle->accept(202)); // Accept OOD Refer
659 profile = mPendingOODReferSubHandle->getUserProfile();
660 accepted = true;
661 }
662 if(accepted)
663 {
664 // Create offer
665 SdpContents offer;
666 buildSdpOffer(mLocalHold, offer);
667
668 // Build the Invite
669 SharedPtr<SipMessage> invitemsg = mDum.makeInviteSessionFromRefer(mPendingOODReferMsg,
670 profile,
671 mPendingOODReferSubHandle, // Note will be invalid if refer no-sub, which is fine
672 &offer,
673 DialogUsageManager::None, //EncryptionLevel
674 0, //Aleternative Contents
675 &mDialogSet);
676 mDialogSet.sendInvite(invitemsg);
677
678 adjustRTPStreams(true);
679
680 stateTransition(Connecting);
681 }
682 else
683 {
684 WarningLog(<< "acceptPendingOODRefer - no valid handles");
685 mConversationManager.onParticipantTerminated(mHandle, 500);
686 delete this;
687 }
688 }
689 }
690
691 void
692 RemoteParticipant::rejectPendingOODRefer(unsigned int statusCode)
693 {
694 if(mState == PendingOODRefer)
695 {
696 if(mPendingOODReferNoSubHandle.isValid())
697 {
698 mPendingOODReferNoSubHandle->send(mPendingOODReferNoSubHandle->reject(statusCode));
699 mConversationManager.onParticipantTerminated(mHandle, statusCode);
700 }
701 else if(mPendingOODReferSubHandle.isValid())
702 {
703 mPendingOODReferSubHandle->send(mPendingOODReferSubHandle->reject(statusCode));
704 mConversationManager.onParticipantTerminated(mHandle, statusCode);
705 }
706 else
707 {
708 WarningLog(<< "rejectPendingOODRefer - no valid handles");
709 mConversationManager.onParticipantTerminated(mHandle, 500);
710 }
711 mDialogSet.destroy(); // Will also cause "this" to be deleted
712 }
713 }
714
715 void
716 RemoteParticipant::redirectPendingOODRefer(resip::NameAddr& destination)
717 {
718 if(mState == PendingOODRefer)
719 {
720 if(mPendingOODReferNoSubHandle.isValid())
721 {
722 SharedPtr<SipMessage> redirect = mPendingOODReferNoSubHandle->reject(302 /* Moved Temporarily */);
723 redirect->header(h_Contacts).clear();
724 redirect->header(h_Contacts).push_back(destination);
725 mPendingOODReferNoSubHandle->send(redirect);
726 mConversationManager.onParticipantTerminated(mHandle, 302 /* Moved Temporarily */);
727 }
728 else if(mPendingOODReferSubHandle.isValid())
729 {
730 SharedPtr<SipMessage> redirect = mPendingOODReferSubHandle->reject(302 /* Moved Temporarily */);
731 redirect->header(h_Contacts).clear();
732 redirect->header(h_Contacts).push_back(destination);
733 mPendingOODReferSubHandle->send(redirect);
734 mConversationManager.onParticipantTerminated(mHandle, 302 /* Moved Temporarily */);
735 }
736 else
737 {
738 WarningLog(<< "rejectPendingOODRefer - no valid handles");
739 mConversationManager.onParticipantTerminated(mHandle, 500);
740 }
741 mDialogSet.destroy(); // Will also cause "this" to be deleted
742 }
743 }
744
745 void
746 RemoteParticipant::processReferNotify(const SipMessage& notify)
747 {
748 unsigned int code = 400; // Bad Request - default if for some reason a valid sipfrag is not present
749
750 SipFrag* frag = dynamic_cast<SipFrag*>(notify.getContents());
751 if (frag)
752 {
753 // Get StatusCode from SipFrag
754 if (frag->message().isResponse())
755 {
756 code = frag->message().header(h_StatusLine).statusCode();
757 }
758 }
759
760 // Check if success or failure response code was in SipFrag
761 if(code >= 200 && code < 300)
762 {
763 if(mState == Redirecting)
764 {
765 if (mHandle) mConversationManager.onParticipantRedirectSuccess(mHandle);
766 stateTransition(Connected);
767 }
768 }
769 else if(code >= 300)
770 {
771 if(mState == Redirecting)
772 {
773 if (mHandle) mConversationManager.onParticipantRedirectFailure(mHandle, code);
774 stateTransition(Connected);
775 }
776 }
777 }
778
779 void
780 RemoteParticipant::provideOffer(bool postOfferAccept)
781 {
782 std::auto_ptr<SdpContents> offer(new SdpContents);
783 assert(mInviteSessionHandle.isValid());
784
785 buildSdpOffer(mLocalHold, *offer);
786
787 mDialogSet.provideOffer(offer, mInviteSessionHandle, postOfferAccept);
788 mOfferRequired = false;
789 }
790
791 bool
792 RemoteParticipant::provideAnswer(const SdpContents& offer, bool postAnswerAccept, bool postAnswerAlert)
793 {
794 auto_ptr<SdpContents> answer(new SdpContents);
795 assert(mInviteSessionHandle.isValid());
796 bool answerOk = buildSdpAnswer(offer, *answer);
797
798 if(answerOk)
799 {
800 mDialogSet.provideAnswer(answer, mInviteSessionHandle, postAnswerAccept, postAnswerAlert);
801 }
802 else
803 {
804 mInviteSessionHandle->reject(488);
805 }
806
807 return answerOk;
808 }
809
810 void
811 RemoteParticipant::buildSdpOffer(bool holdSdp, SdpContents& offer)
812 {
813 SdpContents::Session::Medium *audioMedium = 0;
814 ConversationProfile *profile = dynamic_cast<ConversationProfile*>(mDialogSet.getUserProfile().get());
815 if(!profile) // This can happen for UAC calls
816 {
817 profile = mConversationManager.getUserAgent()->getDefaultOutgoingConversationProfile().get();
818 }
819
820 // If we already have a local sdp for this sesion, then use this to form the next offer - doing so will ensure
821 // that we do not switch codecs or payload id's mid session.
822 if(mInviteSessionHandle.isValid() && mInviteSessionHandle->getLocalSdp().session().media().size() != 0)
823 {
824 offer = mInviteSessionHandle->getLocalSdp();
825
826 // Set sessionid and version for this sdp
827 UInt64 currentTime = Timer::getTimeMicroSec();
828 offer.session().origin().getSessionId() = currentTime;
829 offer.session().origin().getVersion() = currentTime;
830
831 // Find the audio medium
832 for (std::list<SdpContents::Session::Medium>::iterator mediaIt = offer.session().media().begin();
833 mediaIt != offer.session().media().end(); mediaIt++)
834 {
835 if(mediaIt->name() == "audio" &&
836 (mediaIt->protocol() == Symbols::RTP_AVP ||
837 mediaIt->protocol() == Symbols::RTP_SAVP ||
838 mediaIt->protocol() == Symbols::UDP_TLS_RTP_SAVP))
839 {
840 audioMedium = &(*mediaIt);
841 break;
842 }
843 }
844 assert(audioMedium);
845
846 // Add any codecs from our capabilities that may not be in current local sdp - since endpoint may have changed and may now be capable
847 // of handling codecs that it previously could not (common when endpoint is a B2BUA).
848
849 SdpContents& sessionCaps = dynamic_cast<ConversationProfile*>(mDialogSet.getUserProfile().get())->sessionCaps();
850 int highPayloadId = 96; // Note: static payload id's are in range of 0-96
851 // Iterate through codecs in session caps and check if already in offer
852 for (std::list<SdpContents::Session::Codec>::iterator codecsIt = sessionCaps.session().media().front().codecs().begin();
853 codecsIt != sessionCaps.session().media().front().codecs().end(); codecsIt++)
854 {
855 bool found=false;
856 bool payloadIdCollision=false;
857 for (std::list<SdpContents::Session::Codec>::iterator codecsIt2 = audioMedium->codecs().begin();
858 codecsIt2 != audioMedium->codecs().end(); codecsIt2++)
859 {
860 if(isEqualNoCase(codecsIt->getName(), codecsIt2->getName()) &&
861 codecsIt->getRate() == codecsIt2->getRate())
862 {
863 found = true;
864 }
865 else if(codecsIt->payloadType() == codecsIt2->payloadType())
866 {
867 payloadIdCollision = true;
868 }
869 // Keep track of highest payload id in offer - used if we need to resolve a payload id conflict
870 if(codecsIt2->payloadType() > highPayloadId)
871 {
872 highPayloadId = codecsIt2->payloadType();
873 }
874 }
875 if(!found)
876 {
877 if(payloadIdCollision)
878 {
879 highPayloadId++;
880 codecsIt->payloadType() = highPayloadId;
881 }
882 else if(codecsIt->payloadType() > highPayloadId)
883 {
884 highPayloadId = codecsIt->payloadType();
885 }
886 audioMedium->addCodec(*codecsIt);
887 }
888 }
889 }
890 else
891 {
892 // Build base offer
893 mConversationManager.buildSdpOffer(profile, offer);
894
895 // Assumes there is only 1 media stream in session caps and it the audio one
896 audioMedium = &offer.session().media().front();
897 assert(audioMedium);
898
899 // Set the local RTP Port
900 audioMedium->port() = mDialogSet.getLocalRTPPort();
901 }
902
903 // Add Crypto attributes (if required) - assumes there is only 1 media stream
904 audioMedium->clearAttribute("crypto");
905 audioMedium->clearAttribute("encryption");
906 audioMedium->clearAttribute("tcap");
907 audioMedium->clearAttribute("pcfg");
908 offer.session().clearAttribute("fingerprint");
909 offer.session().clearAttribute("setup");
910 if(mDialogSet.getSecureMediaMode() == ConversationProfile::Srtp)
911 {
912 // Note: We could add the crypto attribute to the "SDP Capabilties Negotiation"
913 // potential configuration if secure media is not required - but other implementations
914 // should ignore them any way if just plain RTP is used. It is thought the
915 // current implementation will increase interopability. (ie. SNOM Phones)
916
917 Data crypto;
918
919 switch(mDialogSet.getSrtpCryptoSuite())
920 {
921 case flowmanager::MediaStream::SRTP_AES_CM_128_HMAC_SHA1_32:
922 crypto = "1 AES_CM_128_HMAC_SHA1_32 inline:" + mDialogSet.getLocalSrtpSessionKey().base64encode();
923 audioMedium->addAttribute("crypto", crypto);
924 crypto = "2 AES_CM_128_HMAC_SHA1_80 inline:" + mDialogSet.getLocalSrtpSessionKey().base64encode();
925 audioMedium->addAttribute("crypto", crypto);
926 break;
927 default:
928 crypto = "1 AES_CM_128_HMAC_SHA1_80 inline:" + mDialogSet.getLocalSrtpSessionKey().base64encode();
929 audioMedium->addAttribute("crypto", crypto);
930 crypto = "2 AES_CM_128_HMAC_SHA1_32 inline:" + mDialogSet.getLocalSrtpSessionKey().base64encode();
931 audioMedium->addAttribute("crypto", crypto);
932 break;
933 }
934 if(mDialogSet.getSecureMediaRequired())
935 {
936 audioMedium->protocol() = Symbols::RTP_SAVP;
937 }
938 else
939 {
940 audioMedium->protocol() = Symbols::RTP_AVP;
941 audioMedium->addAttribute("encryption", "optional"); // Used by SNOM phones?
942 audioMedium->addAttribute("tcap", "1 RTP/SAVP"); // draft-ietf-mmusic-sdp-capability-negotiation-08
943 audioMedium->addAttribute("pcfg", "1 t=1");
944 }
945 }
946 #ifdef USE_SSL
947 else if(mDialogSet.getSecureMediaMode() == ConversationProfile::SrtpDtls)
948 {
949 if(mConversationManager.getFlowManager().getDtlsFactory())
950 {
951 // Note: We could add the fingerprint and setup attributes to the "SDP Capabilties Negotiation"
952 // potential configuration if secure media is not required - but other implementations
953 // should ignore them any way if just plain RTP is used. It is thought the
954 // current implementation will increase interopability.
955
956 // Add fingerprint attribute
957 char fingerprint[100];
958 mConversationManager.getFlowManager().getDtlsFactory()->getMyCertFingerprint(fingerprint);
959 offer.session().addAttribute("fingerprint", "SHA-1 " + Data(fingerprint));
960 //offer.session().addAttribute("acap", "1 fingerprint:SHA-1 " + Data(fingerprint));
961
962 // Add setup attribute
963 offer.session().addAttribute("setup", "actpass");
964
965 if(mDialogSet.getSecureMediaRequired())
966 {
967 audioMedium->protocol() = Symbols::UDP_TLS_RTP_SAVP;
968 }
969 else
970 {
971 audioMedium->protocol() = Symbols::RTP_AVP;
972 audioMedium->addAttribute("tcap", "1 UDP/TLS/RTP/SAVP"); // draft-ietf-mmusic-sdp-capability-negotiation-08
973 audioMedium->addAttribute("pcfg", "1 t=1");
974 //audioMedium->addAttribute("pcfg", "1 t=1 a=1");
975 }
976 }
977 }
978 #endif
979
980 audioMedium->clearAttribute("sendrecv");
981 audioMedium->clearAttribute("sendonly");
982 audioMedium->clearAttribute("recvonly");
983 audioMedium->clearAttribute("inactive");
984
985 if(holdSdp)
986 {
987 if(mRemoteHold)
988 {
989 audioMedium->addAttribute("inactive");
990 }
991 else
992 {
993 audioMedium->addAttribute("sendonly");
994 }
995 }
996 else
997 {
998 if(mRemoteHold)
999 {
1000 audioMedium->addAttribute("recvonly");
1001 }
1002 else
1003 {
1004 audioMedium->addAttribute("sendrecv");
1005 }
1006 }
1007 setProposedSdp(offer);
1008 }
1009
1010 bool
1011 RemoteParticipant::answerMediaLine(SdpContents::Session::Medium& mediaSessionCaps, const SdpMediaLine& sdpMediaLine, SdpContents& answer, bool potential)
1012 {
1013 SdpMediaLine::SdpTransportProtocolType protocolType = sdpMediaLine.getTransportProtocolType();
1014 bool valid = false;
1015
1016 // If this is a valid audio medium then process it
1017 if(sdpMediaLine.getMediaType() == SdpMediaLine::MEDIA_TYPE_AUDIO &&
1018 (protocolType == SdpMediaLine::PROTOCOL_TYPE_RTP_AVP ||
1019 protocolType == SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP ||
1020 protocolType == SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP) &&
1021 sdpMediaLine.getConnections().size() != 0 &&
1022 sdpMediaLine.getConnections().front().getPort() != 0)
1023 {
1024 SdpContents::Session::Medium medium("audio", getLocalRTPPort(), 1,
1025 protocolType == SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP ? Symbols::RTP_SAVP :
1026 (protocolType == SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP ? Symbols::UDP_TLS_RTP_SAVP :
1027 Symbols::RTP_AVP));
1028
1029 // Check secure media properties and requirements
1030 bool secureMediaRequired = mDialogSet.getSecureMediaRequired() || protocolType != SdpMediaLine::PROTOCOL_TYPE_RTP_AVP;
1031
1032 if(mDialogSet.getSecureMediaMode() == ConversationProfile::Srtp ||
1033 protocolType == SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP) // allow accepting of SAVP profiles, even if SRTP is not enabled as a SecureMedia mode
1034 {
1035 bool supportedCryptoSuite = false;
1036 SdpMediaLine::CryptoList::const_iterator itCrypto = sdpMediaLine.getCryptos().begin();
1037 for(; !supportedCryptoSuite && itCrypto!=sdpMediaLine.getCryptos().end(); itCrypto++)
1038 {
1039 Data cryptoKeyB64(itCrypto->getCryptoKeyParams().front().getKeyValue());
1040 Data cryptoKey = cryptoKeyB64.base64decode();
1041
1042 if(cryptoKey.size() == SRTP_MASTER_KEY_LEN)
1043 {
1044 switch(itCrypto->getSuite())
1045 {
1046 case SdpMediaLine::CRYPTO_SUITE_TYPE_AES_CM_128_HMAC_SHA1_80:
1047 medium.addAttribute("crypto", Data(itCrypto->getTag()) + " AES_CM_128_HMAC_SHA1_80 inline:" + mDialogSet.getLocalSrtpSessionKey().base64encode());
1048 supportedCryptoSuite = true;
1049 break;
1050 case SdpMediaLine::CRYPTO_SUITE_TYPE_AES_CM_128_HMAC_SHA1_32:
1051 medium.addAttribute("crypto", Data(itCrypto->getTag()) + " AES_CM_128_HMAC_SHA1_32 inline:" + mDialogSet.getLocalSrtpSessionKey().base64encode());
1052 supportedCryptoSuite = true;
1053 break;
1054 default:
1055 break;
1056 }
1057 }
1058 else
1059 {
1060 InfoLog(<< "SDES crypto key found in SDP, but is not of correct length after base 64 decode: " << cryptoKey.size());
1061 }
1062 }
1063 if(!supportedCryptoSuite && secureMediaRequired)
1064 {
1065 InfoLog(<< "Secure media stream is required, but there is no supported crypto attributes in the offer - skipping this stream...");
1066 return false;
1067 }
1068 }
1069 #ifdef USE_SSL
1070 else if(mConversationManager.getFlowManager().getDtlsFactory() &&
1071 (mDialogSet.getSecureMediaMode() == ConversationProfile::SrtpDtls ||
1072 protocolType == SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP)) // allow accepting of DTLS SAVP profiles, even if DTLS-SRTP is not enabled as a SecureMedia mode
1073 {
1074 bool supportedFingerprint = false;
1075
1076 // We will only process Dtls-Srtp if fingerprint is in SHA-1 format
1077 if(sdpMediaLine.getFingerPrintHashFunction() == SdpMediaLine::FINGERPRINT_HASH_FUNC_SHA_1)
1078 {
1079 answer.session().clearAttribute("fingerprint"); // ensure we don't add these twice
1080 answer.session().clearAttribute("setup"); // ensure we don't add these twice
1081
1082 // Add fingerprint attribute to answer
1083 char fingerprint[100];
1084 mConversationManager.getFlowManager().getDtlsFactory()->getMyCertFingerprint(fingerprint);
1085 answer.session().addAttribute("fingerprint", "SHA-1 " + Data(fingerprint));
1086
1087 // Add setup attribute
1088 if(sdpMediaLine.getTcpSetupAttribute() == SdpMediaLine::TCP_SETUP_ATTRIBUTE_ACTIVE)
1089 {
1090 answer.session().addAttribute("setup", "passive");
1091 }
1092 else
1093 {
1094 answer.session().addAttribute("setup", "active");
1095 }
1096
1097 supportedFingerprint = true;
1098 }
1099 if(!supportedFingerprint && secureMediaRequired)
1100 {
1101 InfoLog(<< "Secure media stream is required, but there is no supported fingerprint attributes in the offer - skipping this stream...");
1102 return false;
1103 }
1104 }
1105 #endif
1106
1107 if(potential && !sdpMediaLine.getPotentialMediaViewString().empty())
1108 {
1109 medium.addAttribute("acfg", sdpMediaLine.getPotentialMediaViewString());
1110 }
1111
1112 // Iterate through codecs and look for supported codecs - tag found ones by storing their payload id
1113 SdpMediaLine::CodecList::const_iterator itCodec = sdpMediaLine.getCodecs().begin();
1114 for(; itCodec != sdpMediaLine.getCodecs().end(); itCodec++)
1115 {
1116 std::list<SdpContents::Session::Codec>::iterator bestCapsCodecMatchIt = mediaSessionCaps.codecs().end();
1117 bool modeInOffer = itCodec->getFormatParameters().prefix("mode=");
1118
1119 // Loop through allowed codec list and see if codec is supported locally
1120 for (std::list<SdpContents::Session::Codec>::iterator capsCodecsIt = mediaSessionCaps.codecs().begin();
1121 capsCodecsIt != mediaSessionCaps.codecs().end(); capsCodecsIt++)
1122 {
1123 if(isEqualNoCase(capsCodecsIt->getName(), itCodec->getMimeSubtype()) &&
1124 (unsigned int)capsCodecsIt->getRate() == itCodec->getRate())
1125 {
1126 bool modeInCaps = capsCodecsIt->parameters().prefix("mode=");
1127 if(!modeInOffer && !modeInCaps)
1128 {
1129 // If mode is not specified in either - then we have a match
1130 bestCapsCodecMatchIt = capsCodecsIt;
1131 break;
1132 }
1133 else if(modeInOffer && modeInCaps)
1134 {
1135 if(isEqualNoCase(capsCodecsIt->parameters(), itCodec->getFormatParameters()))
1136 {
1137 bestCapsCodecMatchIt = capsCodecsIt;
1138 break;
1139 }
1140 // If mode is specified in both, and doesn't match - then we have no match
1141 }
1142 else
1143 {
1144 // Mode is specified on either offer or caps - this match is a potential candidate
1145 // As a rule - use first match of this kind only
1146 if(bestCapsCodecMatchIt == mediaSessionCaps.codecs().end())
1147 {
1148 bestCapsCodecMatchIt = capsCodecsIt;
1149 }
1150 }
1151 }
1152 }
1153
1154 if(bestCapsCodecMatchIt != mediaSessionCaps.codecs().end())
1155 {
1156 SdpContents::Session::Codec codec(*bestCapsCodecMatchIt);
1157 codec.payloadType() = itCodec->getPayloadType(); // honour offered payload id - just to be nice :)
1158 medium.addCodec(codec);
1159 if(!valid && !isEqualNoCase(bestCapsCodecMatchIt->getName(), "telephone-event"))
1160 {
1161 // Consider offer valid if we see any matching codec other than telephone-event
1162 valid = true;
1163 }
1164 }
1165 }
1166
1167 if(valid)
1168 {
1169 // copy ptime attribute from session caps (if exists)
1170 if(mediaSessionCaps.exists("ptime"))
1171 {
1172 medium.addAttribute("ptime", mediaSessionCaps.getValues("ptime").front());
1173 }
1174
1175 // Check requested direction
1176 unsigned int remoteRtpPort = sdpMediaLine.getConnections().front().getPort();
1177 if(sdpMediaLine.getDirection() == SdpMediaLine::DIRECTION_TYPE_INACTIVE ||
1178 (mLocalHold && (sdpMediaLine.getDirection() == SdpMediaLine::DIRECTION_TYPE_SENDONLY || remoteRtpPort == 0))) // If remote inactive or both sides are holding
1179 {
1180 medium.addAttribute("inactive");
1181 }
1182 else if(sdpMediaLine.getDirection() == SdpMediaLine::DIRECTION_TYPE_SENDONLY || remoteRtpPort == 0 /* old RFC 2543 hold */)
1183 {
1184 medium.addAttribute("recvonly");
1185 }
1186 else if(sdpMediaLine.getDirection() == SdpMediaLine::DIRECTION_TYPE_RECVONLY || mLocalHold)
1187 {
1188 medium.addAttribute("sendonly");
1189 }
1190 else
1191 {
1192 // Note: sendrecv is the default in SDP
1193 medium.addAttribute("sendrecv");
1194 }
1195 answer.session().addMedium(medium);
1196 }
1197 }
1198 return valid;
1199 }
1200
1201 bool
1202 RemoteParticipant::buildSdpAnswer(const SdpContents& offer, SdpContents& answer)
1203 {
1204 // Note: this implementation has minimal support for draft-ietf-mmusic-sdp-capabilities-negotiation
1205 // for responding "best-effort" / optional SRTP (Dtls-SRTP) offers
1206
1207 bool valid = false;
1208 Sdp* remoteSdp = SdpHelperResip::createSdpFromResipSdp(offer);
1209
1210 try
1211 {
1212 // copy over session capabilities
1213 answer = dynamic_cast<ConversationProfile*>(mDialogSet.getUserProfile().get())->sessionCaps();
1214
1215 // Set sessionid and version for this answer
1216 UInt64 currentTime = Timer::getTimeMicroSec();
1217 answer.session().origin().getSessionId() = currentTime;
1218 answer.session().origin().getVersion() = currentTime;
1219
1220 // Set local port in answer
1221 // for now we only allow 1 audio media
1222 assert(answer.session().media().size() == 1);
1223 SdpContents::Session::Medium& mediaSessionCaps = dynamic_cast<ConversationProfile*>(mDialogSet.getUserProfile().get())->sessionCaps().session().media().front();
1224 assert(mediaSessionCaps.name() == "audio");
1225 assert(mediaSessionCaps.codecs().size() > 0);
1226
1227 // Copy t= field from sdp (RFC3264)
1228 assert(answer.session().getTimes().size() > 0);
1229 if(offer.session().getTimes().size() >= 1)
1230 {
1231 answer.session().getTimes().clear();
1232 answer.session().addTime(offer.session().getTimes().front());
1233 }
1234
1235 // Clear out m= lines in answer then populate below
1236 answer.session().media().clear();
1237
1238 // Loop through each offered m= line and provide a response
1239 Sdp::MediaLineList::const_iterator itMediaLine = remoteSdp->getMediaLines().begin();
1240 for(; itMediaLine != remoteSdp->getMediaLines().end(); itMediaLine++)
1241 {
1242 bool mediaLineValid = false;
1243
1244 // We only process one media stream - so if we already have a valid - just reject the rest
1245 if(valid)
1246 {
1247 SdpContents::Session::Medium rejmedium((*itMediaLine)->getMediaTypeString(), 0, 1, // Reject medium by specifying port 0 (RFC3264)
1248 (*itMediaLine)->getTransportProtocolTypeString());
1249 if((*itMediaLine)->getCodecs().size() > 0)
1250 {
1251 rejmedium.addCodec(SdpContents::Session::Codec((*itMediaLine)->getCodecs().front().getMimeSubtype(),
1252 (*itMediaLine)->getCodecs().front().getRate(),
1253 (*itMediaLine)->getCodecs().front().getFormatParameters()));
1254 rejmedium.codecs().front().payloadType() = (*itMediaLine)->getCodecs().front().getPayloadType();
1255 }
1256 answer.session().addMedium(rejmedium);
1257 continue;
1258 }
1259
1260 // Give preference to potential configuration first - if there are any
1261 SdpMediaLine::SdpMediaLineList::const_iterator itPotentialMediaLine = (*itMediaLine)->getPotentialMediaViews().begin();
1262 for(; itPotentialMediaLine != (*itMediaLine)->getPotentialMediaViews().end(); itPotentialMediaLine++)
1263 {
1264 mediaLineValid = answerMediaLine(mediaSessionCaps, *itPotentialMediaLine, answer, true);
1265 if(mediaLineValid)
1266 {
1267 // We have a valid potential media - line - copy over normal media line to make
1268 // further processing easier
1269 *(*itMediaLine) = *itPotentialMediaLine;
1270 valid = true;
1271 break;
1272 }
1273 }
1274 if(!mediaLineValid)
1275 {
1276 // Process SDP normally
1277 mediaLineValid = answerMediaLine(mediaSessionCaps, *(*itMediaLine), answer, false);
1278 if(!mediaLineValid)
1279 {
1280 SdpContents::Session::Medium rejmedium((*itMediaLine)->getMediaTypeString(), 0, 1, // Reject medium by specifying port 0 (RFC3264)
1281 (*itMediaLine)->getTransportProtocolTypeString());
1282 if((*itMediaLine)->getCodecs().size() > 0)
1283 {
1284 rejmedium.addCodec(SdpContents::Session::Codec((*itMediaLine)->getCodecs().front().getMimeSubtype(),
1285 (*itMediaLine)->getCodecs().front().getRate(),
1286 (*itMediaLine)->getCodecs().front().getFormatParameters()));
1287 rejmedium.codecs().front().payloadType() = (*itMediaLine)->getCodecs().front().getPayloadType();
1288 }
1289 answer.session().addMedium(rejmedium);
1290 }
1291 else
1292 {
1293 valid = true;
1294 }
1295 }
1296 } // end loop through m= offers
1297 }
1298 catch(BaseException &e)
1299 {
1300 WarningLog( << "buildSdpAnswer: exception parsing SDP offer: " << e.getMessage());
1301 valid = false;
1302 }
1303 catch(...)
1304 {
1305 WarningLog( << "buildSdpAnswer: unknown exception parsing SDP offer");
1306 valid = false;
1307 }
1308
1309 //InfoLog( << "SDPOffer: " << offer);
1310 //InfoLog( << "SDPAnswer: " << answer);
1311 if(valid)
1312 {
1313 setLocalSdp(answer);
1314 setRemoteSdp(offer, remoteSdp);
1315 }
1316 else
1317 {
1318 delete remoteSdp;
1319 }
1320 return valid;
1321 }
1322
1323 #ifdef OLD_CODE
1324 // Note: This old code used to serve 2 purposes -
1325 // 1 - that we do not change payload id's mid session
1326 // 2 - that we do not add codecs or media streams that have previously rejected
1327 // Purpose 2 is not correct. RFC3264 states we need purpose 1, but you are allowed to add new codecs mid-session
1328 //
1329 // Decision to comment out this code and implement purpose 1 elsewhere - leaving this code here for reference (for now)
1330 // as it may be useful for something in the future.
1331 bool
1332 RemoteParticipant::formMidDialogSdpOfferOrAnswer(const SdpContents& localSdp, const SdpContents& remoteSdp, SdpContents& newSdp, bool offer)
1333 {
1334 bool valid = false;
1335
1336 try
1337 {
1338 // start with current localSdp
1339 newSdp = localSdp;
1340
1341 // Clear all m= lines are rebuild
1342 newSdp.session().media().clear();
1343
1344 // Set sessionid and version for this sdp
1345 UInt64 currentTime = Timer::getTimeMicroSec();
1346 newSdp.session().origin().getSessionId() = currentTime;
1347 newSdp.session().origin().getVersion() = currentTime;
1348
1349 // Loop through each m= line in local Sdp and remove or disable if not in remote
1350 for (std::list<SdpContents::Session::Medium>::const_iterator localMediaIt = localSdp.session().media().begin();
1351 localMediaIt != localSdp.session().media().end(); localMediaIt++)
1352 {
1353 for (std::list<SdpContents::Session::Medium>::const_iterator remoteMediaIt = remoteSdp.session().media().begin();
1354 remoteMediaIt != remoteSdp.session().media().end(); remoteMediaIt++)
1355 {
1356 if(localMediaIt->name() == remoteMediaIt->name() && localMediaIt->protocol() == remoteMediaIt->protocol())
1357 {
1358 // Found an m= line match, proceed to process codecs
1359 SdpContents::Session::Medium medium(localMediaIt->name(), localMediaIt->port(), localMediaIt->multicast(), localMediaIt->protocol());
1360
1361 // Iterate through local codecs and look for remote supported codecs
1362 for (std::list<SdpContents::Session::Codec>::const_iterator localCodecsIt = localMediaIt->codecs().begin();
1363 localCodecsIt != localMediaIt->codecs().end(); localCodecsIt++)
1364 {
1365 // Loop through remote supported codec list and see if codec is supported
1366 for (std::list<SdpContents::Session::Codec>::const_iterator remoteCodecsIt = remoteMediaIt->codecs().begin();
1367 remoteCodecsIt != remoteMediaIt->codecs().end(); remoteCodecsIt++)
1368 {
1369 if(isEqualNoCase(localCodecsIt->getName(), remoteCodecsIt->getName()) &&
1370 localCodecsIt->getRate() == remoteCodecsIt->getRate())
1371 {
1372 // matching supported codec found - add to newSdp
1373 medium.addCodec(*localCodecsIt);
1374 if(!valid && !isEqualNoCase(localCodecsIt->getName(), "telephone-event"))
1375 {
1376 // Consider valid if we see any matching codec other than telephone-event
1377 valid = true;
1378 }
1379 break;
1380 }
1381 }
1382 }
1383
1384 // copy ptime attribute from session caps (if exists)
1385 if(localMediaIt->exists("ptime"))
1386 {
1387 medium.addAttribute("ptime", localMediaIt->getValues("ptime").front());
1388 }
1389
1390 if(offer)
1391 {
1392 if(mLocalHold)
1393 {
1394 if(remoteMediaIt->exists("inactive") ||
1395 remoteMediaIt->exists("sendonly") ||
1396 remoteMediaIt->port() == 0) // If remote inactive or both sides are holding
1397 {
1398 medium.addAttribute("inactive");
1399 }
1400 else
1401 {
1402 medium.addAttribute("sendonly");
1403 }
1404 }
1405 else
1406 {
1407 if(remoteMediaIt->exists("inactive") || remoteMediaIt->exists("sendonly") || remoteMediaIt->port() == 0 /* old RFC 2543 hold */)
1408 {
1409 medium.addAttribute("recvonly");
1410 }
1411 else
1412 {
1413 medium.addAttribute("sendrecv");
1414 }
1415 }
1416 }
1417 else // This is an sdp answer
1418 {
1419 // Check requested direction
1420 if(remoteMediaIt->exists("inactive") ||
1421 (mLocalHold && (remoteMediaIt->exists("sendonly") || remoteMediaIt->port() == 0))) // If remote inactive or both sides are holding
1422 {
1423 medium.addAttribute("inactive");
1424 }
1425 else if(remoteMediaIt->exists("sendonly") || remoteMediaIt->port() == 0 /* old RFC 2543 hold */)
1426 {
1427 medium.addAttribute("recvonly");
1428 }
1429 else if(remoteMediaIt->exists("recvonly") || mLocalHold)
1430 {
1431 medium.addAttribute("sendonly");
1432 }
1433 else
1434 {
1435 // Note: sendrecv is the default in SDP
1436 medium.addAttribute("sendrecv");
1437 }
1438 }
1439
1440 newSdp.session().addMedium(medium);
1441 break;
1442 }
1443 }
1444 }
1445 }
1446 catch(BaseException &e)
1447 {
1448 WarningLog( << "formMidDialogSdpOfferOrAnswer: exception: " << e.getMessage());
1449 valid = false;
1450 }
1451 catch(...)
1452 {
1453 WarningLog( << "formMidDialogSdpOfferOrAnswer: unknown exception");
1454 valid = false;
1455 }
1456
1457 return valid;
1458 }
1459 #endif
1460
1461 void
1462 RemoteParticipant::destroyConversations()
1463 {
1464 ConversationMap temp = mConversations; // Copy since we may end up being destroyed
1465 ConversationMap::iterator it;
1466 for(it = temp.begin(); it != temp.end(); it++)
1467 {
1468 it->second->destroy();
1469 }
1470 }
1471
1472 void
1473 RemoteParticipant::setProposedSdp(const resip::SdpContents& sdp)
1474 {
1475 mDialogSet.setProposedSdp(mHandle, sdp);
1476 }
1477
1478 void
1479 RemoteParticipant::setLocalSdp(const resip::SdpContents& sdp)
1480 {
1481 if(mLocalSdp) delete mLocalSdp;
1482 mLocalSdp = 0;
1483 InfoLog(<< "setLocalSdp: handle=" << mHandle << ", localSdp=" << sdp);
1484 mLocalSdp = SdpHelperResip::createSdpFromResipSdp(sdp);
1485 }
1486
1487 void
1488 RemoteParticipant::setRemoteSdp(const resip::SdpContents& sdp, bool answer)
1489 {
1490 if(mRemoteSdp) delete mRemoteSdp;
1491 mRemoteSdp = 0;
1492 InfoLog(<< "setRemoteSdp: handle=" << mHandle << ", remoteSdp=" << sdp);
1493 mRemoteSdp = SdpHelperResip::createSdpFromResipSdp(sdp);
1494 if(answer && mDialogSet.getProposedSdp())
1495 {
1496 if(mLocalSdp) delete mLocalSdp;
1497 mLocalSdp = new sdpcontainer::Sdp(*mDialogSet.getProposedSdp()); // copied
1498 }
1499 }
1500
1501 void
1502 RemoteParticipant::setRemoteSdp(const resip::SdpContents& sdp, Sdp* remoteSdp) // Note: sdp only passed for logging purposes
1503 {
1504 if(mRemoteSdp) delete mRemoteSdp;
1505 InfoLog(<< "setRemoteSdp: handle=" << mHandle << ", remoteSdp=" << sdp);
1506 mRemoteSdp = remoteSdp;
1507 }
1508
1509 void
1510 RemoteParticipant::adjustRTPStreams(bool sendingOffer)
1511 {
1512 //if(mHandle) mConversationManager.onParticipantMediaUpdate(mHandle, localSdp, remoteSdp);
1513 int mediaDirection = SdpMediaLine::DIRECTION_TYPE_INACTIVE;
1514 Data remoteIPAddress;
1515 unsigned int remoteRtpPort=0;
1516 unsigned int remoteRtcpPort=0;
1517 Sdp *localSdp = sendingOffer ? mDialogSet.getProposedSdp() : mLocalSdp;
1518 Sdp *remoteSdp = sendingOffer ? 0 : mRemoteSdp;
1519 const SdpMediaLine::CodecList* localCodecs;
1520 const SdpMediaLine::CodecList* remoteCodecs;
1521 bool supportedCryptoSuite = false;
1522 bool supportedFingerprint = false;
1523
1524 assert(localSdp);
1525
1526 /*
1527 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", localSdp=" << localSdp);
1528 if(remoteSdp)
1529 {
1530 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", remoteSdp=" << *remoteSdp);
1531 }*/
1532
1533 int localMediaDirection = SdpMediaLine::DIRECTION_TYPE_INACTIVE;
1534
1535 Sdp::MediaLineList::const_iterator itMediaLine = localSdp->getMediaLines().begin();
1536 for(; itMediaLine != localSdp->getMediaLines().end(); itMediaLine++)
1537 {
1538 DebugLog(<< "adjustRTPStreams: handle=" << mHandle <<
1539 ", found media line in local sdp, mediaType=" << SdpMediaLine::SdpMediaTypeString[(*itMediaLine)->getMediaType()] <<
1540 ", transportType=" << SdpMediaLine::SdpTransportProtocolTypeString[(*itMediaLine)->getTransportProtocolType()] <<
1541 ", numConnections=" << (*itMediaLine)->getConnections().size() <<
1542 ", port=" << ((*itMediaLine)->getConnections().size() > 0 ? (*itMediaLine)->getConnections().front().getPort() : 0));
1543 if((*itMediaLine)->getMediaType() == SdpMediaLine::MEDIA_TYPE_AUDIO &&
1544 ((*itMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_RTP_AVP ||
1545 (*itMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP ||
1546 (*itMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP) &&
1547 (*itMediaLine)->getConnections().size() != 0 &&
1548 (*itMediaLine)->getConnections().front().getPort() != 0)
1549 {
1550 //InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", found audio media line in local sdp");
1551 localMediaDirection = (*itMediaLine)->getDirection();
1552 localCodecs = &(*itMediaLine)->getCodecs();
1553 break;
1554 }
1555 }
1556
1557 if(remoteSdp)
1558 {
1559 int remoteMediaDirection = SdpMediaLine::DIRECTION_TYPE_INACTIVE;
1560
1561 Sdp::MediaLineList::const_iterator itRemMediaLine = remoteSdp->getMediaLines().begin();
1562 for(; itRemMediaLine != remoteSdp->getMediaLines().end(); itRemMediaLine++)
1563 {
1564 //InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", found media line in remote sdp");
1565 if((*itRemMediaLine)->getMediaType() == SdpMediaLine::MEDIA_TYPE_AUDIO &&
1566 ((*itRemMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_RTP_AVP ||
1567 (*itRemMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP ||
1568 (*itRemMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP) &&
1569 (*itRemMediaLine)->getConnections().size() != 0 &&
1570 (*itRemMediaLine)->getConnections().front().getPort() != 0)
1571 {
1572 //InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", found audio media line in remote sdp");
1573 remoteMediaDirection = (*itRemMediaLine)->getDirection();
1574 remoteRtpPort = (*itRemMediaLine)->getConnections().front().getPort();
1575 remoteRtcpPort = (*itRemMediaLine)->getRtcpConnections().front().getPort();
1576 remoteIPAddress = (*itRemMediaLine)->getConnections().front().getAddress();
1577 remoteCodecs = &(*itRemMediaLine)->getCodecs();
1578
1579 // Process Crypto settings (if required) - createSRTPSession using remote key
1580 // Note: Top crypto in remote sdp will always be the correct suite/key
1581 if(mDialogSet.getSecureMediaMode() == ConversationProfile::Srtp ||
1582 (*itRemMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_RTP_SAVP)
1583 {
1584 SdpMediaLine::CryptoList::const_iterator itCrypto = (*itRemMediaLine)->getCryptos().begin();
1585 for(; itCrypto != (*itRemMediaLine)->getCryptos().end(); itCrypto++)
1586 {
1587 Data cryptoKeyB64(itCrypto->getCryptoKeyParams().front().getKeyValue());
1588 Data cryptoKey = cryptoKeyB64.base64decode();
1589
1590 if(cryptoKey.size() == SRTP_MASTER_KEY_LEN)
1591 {
1592 switch(itCrypto->getSuite())
1593 {
1594 case SdpMediaLine::CRYPTO_SUITE_TYPE_AES_CM_128_HMAC_SHA1_80:
1595 mDialogSet.createSRTPSession(flowmanager::MediaStream::SRTP_AES_CM_128_HMAC_SHA1_80, cryptoKey.data(), cryptoKey.size());
1596 supportedCryptoSuite = true;
1597 break;
1598 case SdpMediaLine::CRYPTO_SUITE_TYPE_AES_CM_128_HMAC_SHA1_32:
1599 mDialogSet.createSRTPSession(flowmanager::MediaStream::SRTP_AES_CM_128_HMAC_SHA1_32, cryptoKey.data(), cryptoKey.size());
1600 supportedCryptoSuite = true;
1601 break;
1602 default:
1603 break;
1604 }
1605 }
1606 else
1607 {
1608 InfoLog(<< "SDES crypto key found in SDP, but is not of correct length after base 64 decode: " << cryptoKey.size());
1609 }
1610 if(supportedCryptoSuite)
1611 {
1612 break;
1613 }
1614 }
1615 }
1616 // Process Fingerprint and setup settings (if required)
1617 else if((*itRemMediaLine)->getTransportProtocolType() == SdpMediaLine::PROTOCOL_TYPE_UDP_TLS_RTP_SAVP)
1618 {
1619 // We will only process Dtls-Srtp if fingerprint is in SHA-1 format
1620 if((*itRemMediaLine)->getFingerPrintHashFunction() == SdpMediaLine::FINGERPRINT_HASH_FUNC_SHA_1)
1621 {
1622 if(!(*itRemMediaLine)->getFingerPrint().empty())
1623 {
1624 InfoLog(<< "Fingerprint retrieved from remote SDP: " << (*itRemMediaLine)->getFingerPrint());
1625 // ensure we only accept media streams with this fingerprint
1626 mDialogSet.setRemoteSDPFingerprint((*itRemMediaLine)->getFingerPrint());
1627
1628 // If remote setup value is not active then we must be the Dtls client - ensure client DtlsSocket is create
1629 if((*itRemMediaLine)->getTcpSetupAttribute() != SdpMediaLine::TCP_SETUP_ATTRIBUTE_ACTIVE)
1630 {
1631 // If we are the active end, then kick start the DTLS handshake
1632 mDialogSet.startDtlsClient(remoteIPAddress.c_str(), remoteRtpPort, remoteRtcpPort);
1633 }
1634
1635 supportedFingerprint = true;
1636 }
1637 }
1638 else if((*itRemMediaLine)->getFingerPrintHashFunction() != SdpMediaLine::FINGERPRINT_HASH_FUNC_NONE)
1639 {
1640 InfoLog(<< "Fingerprint found, but is not using SHA-1 hash.");
1641 }
1642 }
1643
1644 break;
1645 }
1646 }
1647
1648 if(remoteMediaDirection == SdpMediaLine::DIRECTION_TYPE_INACTIVE ||
1649 remoteMediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDONLY)
1650 {
1651 mRemoteHold = true;
1652 }
1653 else
1654 {
1655 mRemoteHold = false;
1656 }
1657
1658 // Check if any conversations are broadcast only - if so, then we need to send media to parties on hold
1659 bool broadcastOnly = false;
1660 ConversationMap::iterator it;
1661 for(it = mConversations.begin(); it != mConversations.end(); it++)
1662 {
1663 if(it->second->broadcastOnly())
1664 {
1665 broadcastOnly = true;
1666 break;
1667 }
1668 }
1669
1670 // Aggregate local and remote direction attributes to determine overall media direction
1671 if((mLocalHold && !broadcastOnly) ||
1672 localMediaDirection == SdpMediaLine::DIRECTION_TYPE_INACTIVE ||
1673 remoteMediaDirection == SdpMediaLine::DIRECTION_TYPE_INACTIVE)
1674 {
1675 mediaDirection = SdpMediaLine::DIRECTION_TYPE_INACTIVE;
1676 }
1677 else if(localMediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDONLY)
1678 {
1679 mediaDirection = SdpMediaLine::DIRECTION_TYPE_SENDONLY;
1680 }
1681 else if(localMediaDirection == SdpMediaLine::DIRECTION_TYPE_RECVONLY)
1682 {
1683 mediaDirection = SdpMediaLine::DIRECTION_TYPE_RECVONLY;
1684 }
1685 else if(remoteMediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDONLY)
1686 {
1687 mediaDirection = SdpMediaLine::DIRECTION_TYPE_RECVONLY;
1688 }
1689 else if(remoteMediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDONLY)
1690 {
1691 mediaDirection = SdpMediaLine::DIRECTION_TYPE_RECVONLY;
1692 }
1693 else
1694 {
1695 mediaDirection = SdpMediaLine::DIRECTION_TYPE_SENDRECV;
1696 }
1697 }
1698 else
1699 {
1700 // No remote SDP info - so put direction into receive only mode (unless inactive)
1701 if(mLocalHold ||
1702 localMediaDirection == SdpMediaLine::DIRECTION_TYPE_INACTIVE ||
1703 localMediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDONLY)
1704 {
1705 mediaDirection = SdpMediaLine::DIRECTION_TYPE_INACTIVE;
1706 }
1707 else
1708 {
1709 mediaDirection = SdpMediaLine::DIRECTION_TYPE_RECVONLY;
1710 }
1711 }
1712
1713 if(remoteSdp && mDialogSet.getSecureMediaRequired() && !supportedCryptoSuite && !supportedFingerprint)
1714 {
1715 InfoLog(<< "Secure media is required and no valid support found in remote sdp - ending call.");
1716 destroyParticipant();
1717 return;
1718 }
1719
1720 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", mediaDirection=" << SdpMediaLine::SdpDirectionTypeString[mediaDirection] << ", remoteIp=" << remoteIPAddress << ", remotePort=" << remoteRtpPort);
1721
1722 if(!remoteIPAddress.empty() && remoteRtpPort != 0)
1723 {
1724 mDialogSet.setActiveDestination(remoteIPAddress.c_str(), remoteRtpPort, remoteRtcpPort);
1725 }
1726
1727 if((mediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDRECV ||
1728 mediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDONLY) &&
1729 !remoteIPAddress.empty() && remoteRtpPort != 0 &&
1730 remoteCodecs && localCodecs)
1731 {
1732 // Calculate intersection of local and remote codecs, and pass remote codecs that exist locally to RTP send fn
1733 int numCodecs=0;
1734 ::SdpCodec** codecs = new ::SdpCodec*[remoteCodecs->size()];
1735 SdpMediaLine::CodecList::const_iterator itRemoteCodec = remoteCodecs->begin();
1736 for(; itRemoteCodec != remoteCodecs->end(); itRemoteCodec++)
1737 {
1738 bool modeInRemote = itRemoteCodec->getFormatParameters().prefix("mode=");
1739 SdpMediaLine::CodecList::const_iterator bestCapsCodecMatchIt = localCodecs->end();
1740 SdpMediaLine::CodecList::const_iterator itLocalCodec = localCodecs->begin();
1741 for(; itLocalCodec != localCodecs->end(); itLocalCodec++)
1742 {
1743 if(isEqualNoCase(itRemoteCodec->getMimeSubtype(), itLocalCodec->getMimeSubtype()) &&
1744 itRemoteCodec->getRate() == itLocalCodec->getRate())
1745 {
1746 bool modeInLocal = itLocalCodec->getFormatParameters().prefix("mode=");
1747 if(!modeInLocal && !modeInRemote)
1748 {
1749 // If mode is not specified in either - then we have a match
1750 bestCapsCodecMatchIt = itLocalCodec;
1751 break;
1752 }
1753 else if(modeInLocal && modeInRemote)
1754 {
1755 if(isEqualNoCase(itRemoteCodec->getFormatParameters(), itLocalCodec->getFormatParameters()))
1756 {
1757 bestCapsCodecMatchIt = itLocalCodec;
1758 break;
1759 }
1760 // If mode is specified in both, and doesn't match - then we have no match
1761 }
1762 else
1763 {
1764 // Mode is specified on either offer or caps - this match is a potential candidate
1765 // As a rule - use first match of this kind only
1766 if(bestCapsCodecMatchIt == localCodecs->end())
1767 {
1768 bestCapsCodecMatchIt = itLocalCodec;
1769 }
1770 }
1771 }
1772 }
1773 if(bestCapsCodecMatchIt != localCodecs->end())
1774 {
1775 codecs[numCodecs++] = new ::SdpCodec(itRemoteCodec->getPayloadType(),
1776 itRemoteCodec->getMimeType().c_str(),
1777 itRemoteCodec->getMimeSubtype().c_str(),
1778 itRemoteCodec->getRate(),
1779 itRemoteCodec->getPacketTime(),
1780 itRemoteCodec->getNumChannels(),
1781 itRemoteCodec->getFormatParameters().c_str());
1782
1783 UtlString codecString;
1784 codecs[numCodecs-1]->toString(codecString);
1785
1786 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", sending to destination address " << remoteIPAddress << ":" <<
1787 remoteRtpPort << " (RTCP on " << remoteRtcpPort << "): " << codecString.data());
1788 }
1789 }
1790
1791 if(numCodecs > 0)
1792 {
1793 getMediaInterface()->getInterface()->startRtpSend(mDialogSet.getMediaConnectionId(), numCodecs, codecs);
1794 }
1795 else
1796 {
1797 WarningLog(<< "adjustRTPStreams: handle=" << mHandle << ", something went wrong during SDP negotiations, no common codec found.");
1798 }
1799 for(int i = 0; i < numCodecs; i++)
1800 {
1801 delete codecs[i];
1802 }
1803 delete [] codecs;
1804 }
1805 else
1806 {
1807 if(getMediaInterface()->getInterface()->isSendingRtpAudio(mDialogSet.getMediaConnectionId()))
1808 {
1809 getMediaInterface()->getInterface()->stopRtpSend(mDialogSet.getMediaConnectionId());
1810 }
1811 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", stop sending.");
1812 }
1813
1814 if(mediaDirection == SdpMediaLine::DIRECTION_TYPE_SENDRECV ||
1815 mediaDirection == SdpMediaLine::DIRECTION_TYPE_RECVONLY)
1816 {
1817 if(!getMediaInterface()->getInterface()->isReceivingRtpAudio(mDialogSet.getMediaConnectionId()))
1818 {
1819 // !SLG! - we could make this better, no need to recalculate this every time
1820 // We are always willing to receive any of our locally supported codecs
1821 int numCodecs=0;
1822 ::SdpCodec** codecs = new ::SdpCodec*[localCodecs->size()];
1823 SdpMediaLine::CodecList::const_iterator itLocalCodec = localCodecs->begin();
1824 for(; itLocalCodec != localCodecs->end(); itLocalCodec++)
1825 {
1826 codecs[numCodecs++] = new ::SdpCodec(itLocalCodec->getPayloadType(),
1827 itLocalCodec->getMimeType().c_str(),
1828 itLocalCodec->getMimeSubtype().c_str(),
1829 itLocalCodec->getRate(),
1830 itLocalCodec->getPacketTime(),
1831 itLocalCodec->getNumChannels(),
1832 itLocalCodec->getFormatParameters().c_str());
1833 UtlString codecString;
1834 codecs[numCodecs-1]->toString(codecString);
1835 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", receving: " << codecString.data());
1836 }
1837
1838 getMediaInterface()->getInterface()->startRtpReceive(mDialogSet.getMediaConnectionId(), numCodecs, codecs);
1839 for(int i = 0; i < numCodecs; i++)
1840 {
1841 delete codecs[i];
1842 }
1843 delete [] codecs;
1844 }
1845 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", receiving...");
1846 }
1847 else
1848 {
1849 // Never stop receiving - keep reading buffers and let mixing matrix handle supression of audio output
1850 //if(getMediaInterface()->getInterface()->isReceivingRtpAudio(mDialogSet.getMediaConnectionId()))
1851 //{
1852 // getMediaInterface()->getInterface()->stopRtpReceive(mDialogSet.getMediaConnectionId());
1853 //}
1854 InfoLog(<< "adjustRTPStreams: handle=" << mHandle << ", stop receiving (mLocalHold=" << mLocalHold << ").");
1855 }
1856 }
1857
1858 void
1859 RemoteParticipant::replaceWithParticipant(RemoteParticipant* replacingParticipant)
1860 {
1861 // Copy our local hold setting to the replacing participant to replace us
1862 replacingParticipant->mLocalHold = mLocalHold;
1863
1864 // We are about to adjust the participant handle of the replacing participant to ours
1865 // ensure that the mapping is also adjusted in the replacing participants dialog set
1866 if(replacingParticipant->mHandle == replacingParticipant->mDialogSet.getActiveRemoteParticipantHandle())
1867 {
1868 replacingParticipant->mDialogSet.setActiveRemoteParticipantHandle(mHandle);
1869 }
1870
1871 Participant::replaceWithParticipant(replacingParticipant);
1872 }
1873
1874 void
1875 RemoteParticipant::onDtmfEvent(int dtmf, int duration, bool up)
1876 {
1877 if(mHandle) mConversationManager.onDtmfEvent(mHandle, dtmf, duration, up);
1878 }
1879
1880 void
1881 RemoteParticipant::onNewSession(ClientInviteSessionHandle h, InviteSession::OfferAnswerType oat, const SipMessage& msg)
1882 {
1883 InfoLog(<< "onNewSession(Client): handle=" << mHandle << ", " << msg.brief());
1884 mInviteSessionHandle = h->getSessionHandle();
1885 mDialogId = getDialogId();
1886 }
1887
1888 void
1889 RemoteParticipant::onNewSession(ServerInviteSessionHandle h, InviteSession::OfferAnswerType oat, const SipMessage& msg)
1890 {
1891 InfoLog(<< "onNewSession(Server): handle=" << mHandle << ", " << msg.brief());
1892
1893 mInviteSessionHandle = h->getSessionHandle();
1894 mDialogId = getDialogId();
1895
1896 // First check if this INVITE is to replace an existing session
1897 if(msg.exists(h_Replaces))
1898 {
1899 pair<InviteSessionHandle, int> presult;
1900 presult = mDum.findInviteSession(msg.header(h_Replaces));
1901 if(!(presult.first == InviteSessionHandle::NotValid()))
1902 {
1903 RemoteParticipant* participantToReplace = dynamic_cast<RemoteParticipant *>(presult.first->getAppDialog().get());
1904 InfoLog(<< "onNewSession(Server): handle=" << mHandle << ", to replace handle=" << participantToReplace->getParticipantHandle() << ", " << msg.brief());
1905
1906 // Assume Participant Handle of old call
1907 participantToReplace->replaceWithParticipant(this); // adjust conversation mappings
1908
1909 // Session to replace was found - end old session and flag to auto-answer this session after SDP offer-answer is complete
1910 participantToReplace->destroyParticipant();
1911 stateTransition(Replacing);
1912 return;
1913 }
1914 }
1915
1916 // Check for Auto-Answer indication - support draft-ietf-answer-mode-01
1917 // and Answer-After parameter of Call-Info header
1918 ConversationProfile* profile = dynamic_cast<ConversationProfile*>(h->getUserProfile().get());
1919 assert(profile);
1920 bool autoAnswerRequired;
1921 bool autoAnswer = profile->shouldAutoAnswer(msg, &autoAnswerRequired);
1922 if(!autoAnswer && autoAnswerRequired) // If we can't autoAnswer but it was required, we must reject the call
1923 {
1924 WarningCategory warning;
1925 warning.hostname() = DnsUtil::getLocalHostName();
1926 warning.code() = 399; /* Misc. */
1927 warning.text() = "automatic answer forbidden";
1928 setHandle(0); // Don't generate any callbacks for this rejected invite
1929 h->reject(403 /* Forbidden */, &warning);
1930 return;
1931 }
1932
1933 // notify of new participant
1934 if(mHandle) mConversationManager.onIncomingParticipant(mHandle, msg, autoAnswer, *profile);
1935 }
1936
1937 void
1938 RemoteParticipant::onFailure(ClientInviteSessionHandle h, const SipMessage& msg)
1939 {
1940 stateTransition(Terminating);
1941 InfoLog(<< "onFailure: handle=" << mHandle << ", " << msg.brief());
1942 // If ForkSelectMode is automatic, then ensure we destory any conversations, except the original
1943 if(mDialogSet.getForkSelectMode() == ConversationManager::ForkSelectAutomatic &&
1944 mHandle != mDialogSet.getActiveRemoteParticipantHandle())
1945 {
1946 destroyConversations();
1947 }
1948 }
1949
1950 void
1951 RemoteParticipant::onEarlyMedia(ClientInviteSessionHandle h, const SipMessage& msg, const SdpContents& sdp)
1952 {
1953 InfoLog(<< "onEarlyMedia: handle=" << mHandle << ", " << msg.brief());
1954 if(!mDialogSet.isStaleFork(getDialogId()))
1955 {
1956 setRemoteSdp(sdp, true);
1957 adjustRTPStreams();
1958 }
1959 }
1960
1961 void
1962 RemoteParticipant::onProvisional(ClientInviteSessionHandle h, const SipMessage& msg)
1963 {
1964 InfoLog(<< "onProvisional: handle=" << mHandle << ", " << msg.brief());
1965 assert(msg.header(h_StatusLine).responseCode() != 100);
1966
1967 if(!mDialogSet.isStaleFork(getDialogId()))
1968 {
1969 if(mHandle) mConversationManager.onParticipantAlerting(mHandle, msg);
1970 }
1971 }
1972
1973 void
1974 RemoteParticipant::onConnected(ClientInviteSessionHandle h, const SipMessage& msg)
1975 {
1976 InfoLog(<< "onConnected(Client): handle=" << mHandle << ", " << msg.brief());
1977
1978 // Check if this is the first leg in a potentially forked call to send a 200
1979 if(!mDialogSet.isUACConnected())
1980 {
1981 if(mHandle) mConversationManager.onParticipantConnected(mHandle, msg);
1982
1983 mDialogSet.setUACConnected(getDialogId(), mHandle);
1984 stateTransition(Connected);
1985 }
1986 else
1987 {
1988 // We already have a 200 response - send a BYE to this leg
1989 h->end();
1990 }
1991 }
1992
1993 void
1994 RemoteParticipant::onConnected(InviteSessionHandle, const SipMessage& msg)
1995 {
1996 InfoLog(<< "onConnected: handle=" << mHandle << ", " << msg.brief());
1997 stateTransition(Connected);
1998 }
1999
2000 void
2001 RemoteParticipant::onConnectedConfirmed(InviteSessionHandle, const SipMessage& msg)
2002 {
2003 InfoLog(<< "onConnectedConfirmed: handle=" << mHandle << ", " << msg.brief());
2004 stateTransition(Connected);
2005 }
2006
2007 void
2008 RemoteParticipant::onStaleCallTimeout(ClientInviteSessionHandle)
2009 {
2010 WarningLog(<< "onStaleCallTimeout: handle=" << mHandle);
2011 }
2012
2013 void
2014 RemoteParticipant::onTerminated(InviteSessionHandle h, InviteSessionHandler::TerminatedReason reason, const SipMessage* msg)
2015 {
2016 stateTransition(Terminating);
2017 switch(reason)
2018 {
2019 case InviteSessionHandler::RemoteBye:
2020 InfoLog(<< "onTerminated: handle=" << mHandle << ", received a BYE from peer");
2021 break;
2022 case InviteSessionHandler::RemoteCancel:
2023 InfoLog(<< "onTerminated: handle=" << mHandle << ", received a CANCEL from peer");
2024 break;
2025 case InviteSessionHandler::Rejected:
2026 InfoLog(<< "onTerminated: handle=" << mHandle << ", received a rejection from peer");
2027 break;
2028 case InviteSessionHandler::LocalBye:
2029 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended locally via BYE");
2030 break;
2031 case InviteSessionHandler::LocalCancel:
2032 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended locally via CANCEL");
2033 break;
2034 case InviteSessionHandler::Replaced:
2035 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to being replaced");
2036 break;
2037 case InviteSessionHandler::Referred:
2038 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to being reffered");
2039 break;
2040 case InviteSessionHandler::Error:
2041 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to an error");
2042 break;
2043 case InviteSessionHandler::Timeout:
2044 InfoLog(<< "onTerminated: handle=" << mHandle << ", ended due to a timeout");
2045 break;
2046 default:
2047 assert(false);
2048 break;
2049 }
2050 unsigned int statusCode = 0;
2051 if(msg)
2052 {
2053 if(msg->isResponse())
2054 {
2055 statusCode = msg->header(h_StatusLine).responseCode();
2056 }
2057 }
2058
2059 // If this is a referred call and the refer is still around - then switch back to referrer (ie. failed transfer recovery)
2060 if(mHandle && mReferringAppDialog.isValid())
2061 {
2062 RemoteParticipant* participant = (RemoteParticipant*)mReferringAppDialog.get();
2063
2064 replaceWithParticipant(participant); // adjust conversation mappings
2065 if(participant->getParticipantHandle())
2066 {
2067 participant->adjustRTPStreams();
2068 return;
2069 }
2070 }
2071
2072 // Ensure terminating party is from answered fork before generating event
2073 if(!mDialogSet.isStaleFork(getDialogId()))
2074 {
2075 if(mHandle) mConversationManager.onParticipantTerminated(mHandle, statusCode);
2076 }
2077 }
2078
2079 void
2080 RemoteParticipant::onRedirected(ClientInviteSessionHandle, const SipMessage& msg)
2081 {
2082 InfoLog(<< "onRedirected: handle=" << mHandle << ", " << msg.brief());
2083 }
2084
2085 void
2086 RemoteParticipant::onAnswer(InviteSessionHandle h, const SipMessage& msg, const SdpContents& sdp)
2087 {
2088 InfoLog(<< "onAnswer: handle=" << mHandle << ", " << msg.brief());
2089
2090 // Ensure answering party is from answered fork before generating event
2091 if(!mDialogSet.isStaleFork(getDialogId()))
2092 {
2093 setRemoteSdp(sdp, true);
2094 adjustRTPStreams();
2095 }
2096 stateTransition(Connected); // This is valid until PRACK is implemented
2097 }
2098
2099 void
2100 RemoteParticipant::onOffer(InviteSessionHandle h, const SipMessage& msg, const SdpContents& offer)
2101 {
2102 InfoLog(<< "onOffer: handle=" << mHandle << ", " << msg.brief());
2103 if(mState == Connecting && mInviteSessionHandle.isValid())
2104 {
2105 ServerInviteSession* sis = dynamic_cast<ServerInviteSession*>(mInviteSessionHandle.get());
2106 if(sis && !sis->isAccepted())
2107 {
2108 // Don't set answer now - store offer and set when needed - so that sendHoldSdp() can be calculated at the right instant
2109 // we need to allow time for app to add to a conversation before alerting(with early flag) or answering
2110 mPendingOffer = std::auto_ptr<SdpContents>(static_cast<SdpContents*>(offer.clone()));
2111 return;
2112 }
2113 }
2114
2115 if(getLocalRTPPort() == 0)
2116 {
2117 WarningLog(<< "RemoteParticipant::onOffer cannot continue due to no free RTP ports, rejecting offer.");
2118 h->reject(480); // Temporarily Unavailable
2119 }
2120 else
2121 {
2122 if(provideAnswer(offer, mState==Replacing /* postAnswerAccept */, false /* postAnswerAlert */))
2123 {
2124 if(mState == Replacing)
2125 {
2126 stateTransition(Connecting);
2127 }
2128 }
2129 }
2130 }
2131
2132 void
2133 RemoteParticipant::onOfferRequired(InviteSessionHandle h, const SipMessage& msg)
2134 {
2135 InfoLog(<< "onOfferRequired: handle=" << mHandle << ", " << msg.brief());
2136 // We are being asked to provide SDP to the remote end - we should no longer be considering that
2137 // remote end wants us to be on hold
2138 mRemoteHold = false;
2139
2140 if(mState == Connecting && !h->isAccepted())
2141 {
2142 // If we haven't accepted yet - delay providing the offer until accept is called (this allows time
2143 // for a localParticipant to be added before generating the offer)
2144 mOfferRequired = true;
2145 }
2146 else
2147 {
2148 if(getLocalRTPPort() == 0)
2149 {
2150 WarningLog(<< "RemoteParticipant::onOfferRequired cannot continue due to no free RTP ports, rejecting offer request.");
2151 h->reject(480); // Temporarily Unavailable
2152 }
2153 else
2154 {
2155 provideOffer(mState == Replacing /* postOfferAccept */);
2156 if(mState == Replacing)
2157 {
2158 stateTransition(Connecting);
2159 }
2160 }
2161 }
2162 }
2163
2164 void
2165 RemoteParticipant::onOfferRejected(InviteSessionHandle, const SipMessage* msg)
2166 {
2167 if(msg)
2168 {
2169 InfoLog(<< "onOfferRejected: handle=" << mHandle << ", " << msg->brief());
2170 }
2171 else
2172 {
2173 InfoLog(<< "onOfferRejected: handle=" << mHandle);
2174 }
2175 }
2176
2177 void
2178 RemoteParticipant::onOfferRequestRejected(InviteSessionHandle h, const SipMessage& msg)
2179 {
2180 InfoLog(<< "onOfferRequestRejected: handle=" << mHandle << ", " << msg.brief());
2181 assert(0); // We never send a request for an offer (ie. Invite with no SDP)
2182 }
2183
2184 void
2185 RemoteParticipant::onRemoteSdpChanged(InviteSessionHandle h, const SipMessage& msg, const SdpContents& sdp)
2186 {
2187 InfoLog(<< "onRemoteSdpChanged: handle=" << mHandle << ", " << msg.brief());
2188 setRemoteSdp(sdp);
2189 adjustRTPStreams();
2190 }
2191
2192 void
2193 RemoteParticipant::onInfo(InviteSessionHandle, const SipMessage& msg)
2194 {
2195 InfoLog(<< "onInfo: handle=" << mHandle << ", " << msg.brief());
2196 //assert(0);
2197 }
2198
2199 void
2200 RemoteParticipant::onInfoSuccess(InviteSessionHandle, const SipMessage& msg)
2201 {
2202 InfoLog(<< "onInfoSuccess: handle=" << mHandle << ", " << msg.brief());
2203 assert(0); // We never send an info request
2204 }
2205
2206 void
2207 RemoteParticipant::onInfoFailure(InviteSessionHandle, const SipMessage& msg)
2208 {
2209 InfoLog(<< "onInfoFailure: handle=" << mHandle << ", " << msg.brief());
2210 assert(0); // We never send an info request
2211 }
2212
2213 void
2214 RemoteParticipant::onRefer(InviteSessionHandle is, ServerSubscriptionHandle ss, const SipMessage& msg)
2215 {
2216 InfoLog(<< "onRefer: handle=" << mHandle << ", " << msg.brief());
2217
2218 try
2219 {
2220 // Accept the Refer
2221 ss->send(ss->accept(202 /* Refer Accepted */));
2222
2223 // Figure out hold SDP before removing ourselves from the conversation
2224 bool holdSdp = mLocalHold;
2225
2226 // Create new Participant - but use same participant handle
2227 RemoteParticipantDialogSet* participantDialogSet = new RemoteParticipantDialogSet(mConversationManager, mDialogSet.getForkSelectMode());
2228 RemoteParticipant *participant = participantDialogSet->createUACOriginalRemoteParticipant(mHandle); // This will replace old participant in ConversationManager map
2229 participant->mReferringAppDialog = getHandle();
2230
2231 replaceWithParticipant(participant); // adjust conversation mappings
2232
2233 // Create offer
2234 SdpContents offer;
2235 participant->buildSdpOffer(holdSdp, offer);
2236
2237 // Build the Invite
2238 SharedPtr<SipMessage> NewInviteMsg = mDum.makeInviteSessionFromRefer(msg, ss->getHandle(), &offer, participantDialogSet);
2239 participantDialogSet->sendInvite(NewInviteMsg);
2240
2241 // Set RTP stack to listen
2242 participant->adjustRTPStreams(true);
2243 }
2244 catch(BaseException &e)
2245 {
2246 WarningLog(<< "onRefer exception: " << e);
2247 }
2248 catch(...)
2249 {
2250 WarningLog(<< "onRefer unknown exception");
2251 }
2252 }
2253
2254 void
2255 RemoteParticipant::doReferNoSub(const SipMessage& msg)
2256 {
2257 // Figure out hold SDP before removing ourselves from the conversation
2258 bool holdSdp = mLocalHold;
2259
2260 // Create new Participant - but use same participant handle
2261 RemoteParticipantDialogSet* participantDialogSet = new RemoteParticipantDialogSet(mConversationManager, mDialogSet.getForkSelectMode());
2262 RemoteParticipant *participant = participantDialogSet->createUACOriginalRemoteParticipant(mHandle); // This will replace old participant in ConversationManager map
2263 participant->mReferringAppDialog = getHandle();
2264
2265 replaceWithParticipant(participant); // adjust conversation mappings
2266
2267 // Create offer
2268 SdpContents offer;
2269 participant->buildSdpOffer(holdSdp, offer);
2270
2271 // Build the Invite
2272 SharedPtr<SipMessage> NewInviteMsg = mDum.makeInviteSessionFromRefer(msg, mDialogSet.getUserProfile(), &offer, participantDialogSet);
2273 participantDialogSet->sendInvite(NewInviteMsg);
2274
2275 // Set RTP stack to listen
2276 participant->adjustRTPStreams(true);
2277 }
2278
2279 void
2280 RemoteParticipant::onReferNoSub(InviteSessionHandle is, const SipMessage& msg)
2281 {
2282 InfoLog(<< "onReferNoSub: handle=" << mHandle << ", " << msg.brief());
2283
2284 try
2285 {
2286 // Accept the Refer
2287 is->acceptReferNoSub(202 /* Refer Accepted */);
2288
2289 doReferNoSub(msg);
2290 }
2291 catch(BaseException &e)
2292 {
2293 WarningLog(<< "onReferNoSub exception: " << e);
2294 }
2295 catch(...)
2296 {
2297 WarningLog(<< "onReferNoSub unknown exception");
2298 }
2299 }
2300
2301 void
2302 RemoteParticipant::onReferAccepted(InviteSessionHandle, ClientSubscriptionHandle, const SipMessage& msg)
2303 {
2304 InfoLog(<< "onReferAccepted: handle=" << mHandle << ", " << msg.brief());
2305 }
2306
2307 void
2308 RemoteParticipant::onReferRejected(InviteSessionHandle, const SipMessage& msg)
2309 {
2310 InfoLog(<< "onReferRejected: handle=" << mHandle << ", " << msg.brief());
2311 if(msg.isResponse() && mState == Redirecting)
2312 {
2313 if(mHandle) mConversationManager.onParticipantRedirectFailure(mHandle, msg.header(h_StatusLine).responseCode());
2314 stateTransition(Connected);
2315 }
2316 }
2317
2318 void
2319 RemoteParticipant::onMessage(InviteSessionHandle, const SipMessage& msg)
2320 {
2321 InfoLog(<< "onMessage: handle=" << mHandle << ", " << msg.brief());
2322 }
2323
2324 void
2325 RemoteParticipant::onMessageSuccess(InviteSessionHandle, const SipMessage& msg)
2326 {
2327 InfoLog(<< "onMessageSuccess: handle=" << mHandle << ", " << msg.brief());
2328 }
2329
2330 void
2331 RemoteParticipant::onMessageFailure(InviteSessionHandle, const SipMessage& msg)
2332 {
2333 InfoLog(<< "onMessageFailure: handle=" << mHandle << ", " << msg.brief());
2334 }
2335
2336 void
2337 RemoteParticipant::onForkDestroyed(ClientInviteSessionHandle)
2338 {
2339 InfoLog(<< "onForkDestroyed: handle=" << mHandle);
2340 }
2341
2342
2343 ////////////////////////////////////////////////////////////////////////////////
2344 // ClientSubscriptionHandler ///////////////////////////////////////////////////
2345 ////////////////////////////////////////////////////////////////////////////////
2346 void
2347 RemoteParticipant::onUpdatePending(ClientSubscriptionHandle h, const SipMessage& notify, bool outOfOrder)
2348 {
2349 InfoLog(<< "onUpdatePending(ClientSub): handle=" << mHandle << ", " << notify.brief());
2350 if (notify.exists(h_Event) && notify.header(h_Event).value() == "refer")
2351 {
2352 h->acceptUpdate();
2353 processReferNotify(notify);
2354 }
2355 else
2356 {
2357 h->rejectUpdate(400, Data("Only notifies for refers are allowed."));
2358 }
2359 }
2360
2361 void
2362 RemoteParticipant::onUpdateActive(ClientSubscriptionHandle h, const SipMessage& notify, bool outOfOrder)
2363 {
2364 InfoLog(<< "onUpdateActive(ClientSub): handle=" << mHandle << ", " << notify.brief());
2365 if (notify.exists(h_Event) && notify.header(h_Event).value() == "refer")
2366 {
2367 h->acceptUpdate();
2368 processReferNotify(notify);
2369 }
2370 else
2371 {
2372 h->rejectUpdate(400, Data("Only notifies for refers are allowed."));
2373 }
2374 }
2375
2376 void
2377 RemoteParticipant::onUpdateExtension(ClientSubscriptionHandle h, const SipMessage& notify, bool outOfOrder)
2378 {
2379 InfoLog(<< "onUpdateExtension(ClientSub): handle=" << mHandle << ", " << notify.brief());
2380 if (notify.exists(h_Event) && notify.header(h_Event).value() == "refer")
2381 {
2382 h->acceptUpdate();
2383 processReferNotify(notify);
2384 }
2385 else
2386 {
2387 h->rejectUpdate(400, Data("Only notifies for refers are allowed."));
2388 }
2389 }
2390
2391 void
2392 RemoteParticipant::onTerminated(ClientSubscriptionHandle h, const SipMessage* notify)
2393 {
2394 if(notify)
2395 {
2396 InfoLog(<< "onTerminated(ClientSub): handle=" << mHandle << ", " << notify->brief());
2397 if (notify->isRequest() && notify->exists(h_Event) && notify->header(h_Event).value() == "refer")
2398 {
2399 // Note: Final notify is sometimes only passed in the onTerminated callback
2400 processReferNotify(*notify);
2401 }
2402 else if(notify->isResponse() && mState == Redirecting)
2403 {
2404 if(mHandle) mConversationManager.onParticipantRedirectFailure(mHandle, notify->header(h_StatusLine).responseCode());
2405 stateTransition(Connected);
2406 }
2407 }
2408 else
2409 {
2410 // Timed out waiting for notify
2411 InfoLog(<< "onTerminated(ClientSub): handle=" << mHandle);
2412 if(mState == Redirecting)
2413 {
2414 if(mHandle) mConversationManager.onParticipantRedirectFailure(mHandle, 408);
2415 stateTransition(Connected);
2416 }
2417 }
2418 }
2419
2420 void
2421 RemoteParticipant::onNewSubscription(ClientSubscriptionHandle h, const SipMessage& notify)
2422 {
2423 InfoLog(<< "onNewSubscription(ClientSub): handle=" << mHandle << ", " << notify.brief());
2424 }
2425
2426 int
2427 RemoteParticipant::onRequestRetry(ClientSubscriptionHandle h, int retryMinimum, const SipMessage& notify)
2428 {
2429 InfoLog(<< "onRequestRetry(ClientSub): handle=" << mHandle << ", " << notify.brief());
2430 return -1;
2431 }
2432
2433
2434 /* ====================================================================
2435
2436 Copyright (c) 2007-2008, Plantronics, Inc.
2437 All rights reserved.
2438
2439 Redistribution and use in source and binary forms, with or without
2440 modification, are permitted provided that the following conditions are
2441 met:
2442
2443 1. Redistributions of source code must retain the above copyright
2444 notice, this list of conditions and the following disclaimer.
2445
2446 2. Redistributions in binary form must reproduce the above copyright
2447 notice, this list of conditions and the following disclaimer in the
2448 documentation and/or other materials provided with the distribution.
2449
2450 3. Neither the name of Plantronics nor the names of its contributors
2451 may be used to endorse or promote products derived from this
2452 software without specific prior written permission.
2453
2454 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2455 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2456 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2457 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2458 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2459 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2460 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2461 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2462 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2463 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2464 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2465
2466 ==================================================================== */

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27