blob: d70aaf501687e65c5c05b5769d9e246b40d2d231 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2004--2005, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/p2p/base/session.h"
29#include "talk/base/common.h"
30#include "talk/base/logging.h"
31#include "talk/base/helpers.h"
32#include "talk/base/scoped_ptr.h"
33#include "talk/base/sslstreamadapter.h"
34#include "talk/xmpp/constants.h"
35#include "talk/xmpp/jid.h"
36#include "talk/p2p/base/dtlstransport.h"
37#include "talk/p2p/base/p2ptransport.h"
38#include "talk/p2p/base/sessionclient.h"
39#include "talk/p2p/base/transport.h"
40#include "talk/p2p/base/transportchannelproxy.h"
41#include "talk/p2p/base/transportinfo.h"
42
43#include "talk/p2p/base/constants.h"
44
45namespace cricket {
46
47bool BadMessage(const buzz::QName type,
48 const std::string& text,
49 MessageError* err) {
50 err->SetType(type);
51 err->SetText(text);
52 return false;
53}
54
55TransportProxy::~TransportProxy() {
56 for (ChannelMap::iterator iter = channels_.begin();
57 iter != channels_.end(); ++iter) {
58 iter->second->SignalDestroyed(iter->second);
59 delete iter->second;
60 }
61}
62
63std::string TransportProxy::type() const {
64 return transport_->get()->type();
65}
66
67TransportChannel* TransportProxy::GetChannel(int component) {
68 return GetChannelProxy(component);
69}
70
71TransportChannel* TransportProxy::CreateChannel(
72 const std::string& name, int component) {
73 ASSERT(GetChannel(component) == NULL);
74 ASSERT(!transport_->get()->HasChannel(component));
75
76 // We always create a proxy in case we need to change out the transport later.
77 TransportChannelProxy* channel =
78 new TransportChannelProxy(content_name(), name, component);
79 channels_[component] = channel;
80
81 // If we're already negotiated, create an impl and hook it up to the proxy
82 // channel. If we're connecting, create an impl but don't hook it up yet.
83 if (negotiated_) {
84 SetChannelProxyImpl(component, channel);
85 } else if (connecting_) {
86 GetOrCreateChannelProxyImpl(component);
87 }
88 return channel;
89}
90
91bool TransportProxy::HasChannel(int component) {
92 return transport_->get()->HasChannel(component);
93}
94
95void TransportProxy::DestroyChannel(int component) {
96 TransportChannel* channel = GetChannel(component);
97 if (channel) {
98 // If the state of TransportProxy is not NEGOTIATED
99 // then TransportChannelProxy and its impl are not
100 // connected. Both must be connected before
101 // deletion.
102 if (!negotiated_) {
103 SetChannelProxyImpl(component, GetChannelProxy(component));
104 }
105
106 channels_.erase(component);
107 channel->SignalDestroyed(channel);
108 delete channel;
109 }
110}
111
112void TransportProxy::ConnectChannels() {
113 if (!connecting_) {
114 if (!negotiated_) {
115 for (ChannelMap::iterator iter = channels_.begin();
116 iter != channels_.end(); ++iter) {
117 GetOrCreateChannelProxyImpl(iter->first);
118 }
119 }
120 connecting_ = true;
121 }
122 // TODO(juberti): Right now Transport::ConnectChannels doesn't work if we
123 // don't have any channels yet, so we need to allow this method to be called
124 // multiple times. Once we fix Transport, we can move this call inside the
125 // if (!connecting_) block.
126 transport_->get()->ConnectChannels();
127}
128
129void TransportProxy::CompleteNegotiation() {
130 if (!negotiated_) {
131 for (ChannelMap::iterator iter = channels_.begin();
132 iter != channels_.end(); ++iter) {
133 SetChannelProxyImpl(iter->first, iter->second);
134 }
135 negotiated_ = true;
136 }
137}
138
139void TransportProxy::AddSentCandidates(const Candidates& candidates) {
140 for (Candidates::const_iterator cand = candidates.begin();
141 cand != candidates.end(); ++cand) {
142 sent_candidates_.push_back(*cand);
143 }
144}
145
146void TransportProxy::AddUnsentCandidates(const Candidates& candidates) {
147 for (Candidates::const_iterator cand = candidates.begin();
148 cand != candidates.end(); ++cand) {
149 unsent_candidates_.push_back(*cand);
150 }
151}
152
153bool TransportProxy::GetChannelNameFromComponent(
154 int component, std::string* channel_name) const {
155 const TransportChannelProxy* channel = GetChannelProxy(component);
156 if (channel == NULL) {
157 return false;
158 }
159
160 *channel_name = channel->name();
161 return true;
162}
163
164bool TransportProxy::GetComponentFromChannelName(
165 const std::string& channel_name, int* component) const {
166 const TransportChannelProxy* channel = GetChannelProxyByName(channel_name);
167 if (channel == NULL) {
168 return false;
169 }
170
171 *component = channel->component();
172 return true;
173}
174
175TransportChannelProxy* TransportProxy::GetChannelProxy(int component) const {
176 ChannelMap::const_iterator iter = channels_.find(component);
177 return (iter != channels_.end()) ? iter->second : NULL;
178}
179
180TransportChannelProxy* TransportProxy::GetChannelProxyByName(
181 const std::string& name) const {
182 for (ChannelMap::const_iterator iter = channels_.begin();
183 iter != channels_.end();
184 ++iter) {
185 if (iter->second->name() == name) {
186 return iter->second;
187 }
188 }
189 return NULL;
190}
191
192TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl(
193 int component) {
194 TransportChannelImpl* impl = transport_->get()->GetChannel(component);
195 if (impl == NULL) {
196 impl = transport_->get()->CreateChannel(component);
197 impl->SetSessionId(sid_);
198 }
199 return impl;
200}
201
202void TransportProxy::SetChannelProxyImpl(
203 int component, TransportChannelProxy* transproxy) {
204 TransportChannelImpl* impl = GetOrCreateChannelProxyImpl(component);
205 ASSERT(impl != NULL);
206 transproxy->SetImplementation(impl);
207}
208
209// This function muxes |this| onto |target| by repointing |this| at
210// |target|'s transport and setting our TransportChannelProxies
211// to point to |target|'s underlying implementations.
212bool TransportProxy::SetupMux(TransportProxy* target) {
213 // Bail out if there's nothing to do.
214 if (transport_ == target->transport_) {
215 return true;
216 }
217
218 // Run through all channels and remove any non-rtp transport channels before
219 // setting target transport channels.
220 for (ChannelMap::const_iterator iter = channels_.begin();
221 iter != channels_.end(); ++iter) {
222 if (!target->transport_->get()->HasChannel(iter->first)) {
223 // Remove if channel doesn't exist in |transport_|.
224 iter->second->SetImplementation(NULL);
225 } else {
226 // Replace the impl for all the TransportProxyChannels with the channels
227 // from |target|'s transport. Fail if there's not an exact match.
228 iter->second->SetImplementation(
229 target->transport_->get()->CreateChannel(iter->first));
230 }
231 }
232
233 // Now replace our transport. Must happen afterwards because
234 // it deletes all impls as a side effect.
235 transport_ = target->transport_;
236 transport_->get()->SignalCandidatesReady.connect(
237 this, &TransportProxy::OnTransportCandidatesReady);
238 set_candidates_allocated(target->candidates_allocated());
239 return true;
240}
241
242void TransportProxy::SetRole(TransportRole role) {
243 transport_->get()->SetRole(role);
244}
245
246bool TransportProxy::SetLocalTransportDescription(
247 const TransportDescription& description, ContentAction action) {
248 // If this is an answer, finalize the negotiation.
249 if (action == CA_ANSWER) {
250 CompleteNegotiation();
251 }
252 return transport_->get()->SetLocalTransportDescription(description, action);
253}
254
255bool TransportProxy::SetRemoteTransportDescription(
256 const TransportDescription& description, ContentAction action) {
257 // If this is an answer, finalize the negotiation.
258 if (action == CA_ANSWER) {
259 CompleteNegotiation();
260 }
261 return transport_->get()->SetRemoteTransportDescription(description, action);
262}
263
264void TransportProxy::OnSignalingReady() {
265 // If we're starting a new allocation sequence, reset our state.
266 set_candidates_allocated(false);
267 transport_->get()->OnSignalingReady();
268}
269
270bool TransportProxy::OnRemoteCandidates(const Candidates& candidates,
271 std::string* error) {
272 // Ensure the transport is negotiated before handling candidates.
273 // TODO(juberti): Remove this once everybody calls SetLocalTD.
274 CompleteNegotiation();
275
276 // Verify each candidate before passing down to transport layer.
277 for (Candidates::const_iterator cand = candidates.begin();
278 cand != candidates.end(); ++cand) {
279 if (!transport_->get()->VerifyCandidate(*cand, error))
280 return false;
281 if (!HasChannel(cand->component())) {
282 *error = "Candidate has unknown component: " + cand->ToString() +
283 " for content: " + content_name_;
284 return false;
285 }
286 }
287 transport_->get()->OnRemoteCandidates(candidates);
288 return true;
289}
290
wu@webrtc.org91053e72013-08-10 07:18:04 +0000291void TransportProxy::SetIdentity(
292 talk_base::SSLIdentity* identity) {
293 transport_->get()->SetIdentity(identity);
294}
295
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296std::string BaseSession::StateToString(State state) {
297 switch (state) {
298 case Session::STATE_INIT:
299 return "STATE_INIT";
300 case Session::STATE_SENTINITIATE:
301 return "STATE_SENTINITIATE";
302 case Session::STATE_RECEIVEDINITIATE:
303 return "STATE_RECEIVEDINITIATE";
304 case Session::STATE_SENTPRACCEPT:
305 return "STATE_SENTPRACCEPT";
306 case Session::STATE_SENTACCEPT:
307 return "STATE_SENTACCEPT";
308 case Session::STATE_RECEIVEDPRACCEPT:
309 return "STATE_RECEIVEDPRACCEPT";
310 case Session::STATE_RECEIVEDACCEPT:
311 return "STATE_RECEIVEDACCEPT";
312 case Session::STATE_SENTMODIFY:
313 return "STATE_SENTMODIFY";
314 case Session::STATE_RECEIVEDMODIFY:
315 return "STATE_RECEIVEDMODIFY";
316 case Session::STATE_SENTREJECT:
317 return "STATE_SENTREJECT";
318 case Session::STATE_RECEIVEDREJECT:
319 return "STATE_RECEIVEDREJECT";
320 case Session::STATE_SENTREDIRECT:
321 return "STATE_SENTREDIRECT";
322 case Session::STATE_SENTTERMINATE:
323 return "STATE_SENTTERMINATE";
324 case Session::STATE_RECEIVEDTERMINATE:
325 return "STATE_RECEIVEDTERMINATE";
326 case Session::STATE_INPROGRESS:
327 return "STATE_INPROGRESS";
328 case Session::STATE_DEINIT:
329 return "STATE_DEINIT";
330 default:
331 break;
332 }
333 return "STATE_" + talk_base::ToString(state);
334}
335
336BaseSession::BaseSession(talk_base::Thread* signaling_thread,
337 talk_base::Thread* worker_thread,
338 PortAllocator* port_allocator,
339 const std::string& sid,
340 const std::string& content_type,
341 bool initiator)
342 : state_(STATE_INIT),
343 error_(ERROR_NONE),
344 signaling_thread_(signaling_thread),
345 worker_thread_(worker_thread),
346 port_allocator_(port_allocator),
347 sid_(sid),
348 content_type_(content_type),
349 transport_type_(NS_GINGLE_P2P),
350 initiator_(initiator),
351 identity_(NULL),
352 local_description_(NULL),
353 remote_description_(NULL),
354 ice_tiebreaker_(talk_base::CreateRandomId64()),
355 role_switch_(false) {
356 ASSERT(signaling_thread->IsCurrent());
357}
358
359BaseSession::~BaseSession() {
360 ASSERT(signaling_thread()->IsCurrent());
361
362 ASSERT(state_ != STATE_DEINIT);
363 LogState(state_, STATE_DEINIT);
364 state_ = STATE_DEINIT;
365 SignalState(this, state_);
366
367 for (TransportMap::iterator iter = transports_.begin();
368 iter != transports_.end(); ++iter) {
369 delete iter->second;
370 }
371
372 delete remote_description_;
373 delete local_description_;
374}
375
wu@webrtc.org91053e72013-08-10 07:18:04 +0000376bool BaseSession::SetIdentity(talk_base::SSLIdentity* identity) {
377 if (identity_)
378 return false;
379 identity_ = identity;
380 for (TransportMap::iterator iter = transports_.begin();
381 iter != transports_.end(); ++iter) {
382 iter->second->SetIdentity(identity_);
383 }
384 return true;
385}
386
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000387bool BaseSession::PushdownTransportDescription(ContentSource source,
388 ContentAction action) {
389 if (source == CS_LOCAL) {
390 return PushdownLocalTransportDescription(local_description_, action);
391 }
392 return PushdownRemoteTransportDescription(remote_description_, action);
393}
394
395bool BaseSession::PushdownLocalTransportDescription(
396 const SessionDescription* sdesc,
397 ContentAction action) {
398 // Update the Transports with the right information, and trigger them to
399 // start connecting.
400 for (TransportMap::iterator iter = transports_.begin();
401 iter != transports_.end(); ++iter) {
402 // If no transport info was in this session description, ret == false
403 // and we just skip this one.
404 TransportDescription tdesc;
405 bool ret = GetTransportDescription(
406 sdesc, iter->second->content_name(), &tdesc);
407 if (ret) {
408 if (!iter->second->SetLocalTransportDescription(tdesc, action)) {
409 return false;
410 }
411
412 iter->second->ConnectChannels();
413 }
414 }
415
416 return true;
417}
418
419bool BaseSession::PushdownRemoteTransportDescription(
420 const SessionDescription* sdesc,
421 ContentAction action) {
422 // Update the Transports with the right information.
423 for (TransportMap::iterator iter = transports_.begin();
424 iter != transports_.end(); ++iter) {
425 TransportDescription tdesc;
426
427 // If no transport info was in this session description, ret == false
428 // and we just skip this one.
429 bool ret = GetTransportDescription(
430 sdesc, iter->second->content_name(), &tdesc);
431 if (ret) {
432 if (!iter->second->SetRemoteTransportDescription(tdesc, action)) {
433 return false;
434 }
435 }
436 }
437
438 return true;
439}
440
441TransportChannel* BaseSession::CreateChannel(const std::string& content_name,
442 const std::string& channel_name,
443 int component) {
444 // We create the proxy "on demand" here because we need to support
445 // creating channels at any time, even before we send or receive
446 // initiate messages, which is before we create the transports.
447 TransportProxy* transproxy = GetOrCreateTransportProxy(content_name);
448 return transproxy->CreateChannel(channel_name, component);
449}
450
451TransportChannel* BaseSession::GetChannel(const std::string& content_name,
452 int component) {
453 TransportProxy* transproxy = GetTransportProxy(content_name);
454 if (transproxy == NULL)
455 return NULL;
456 else
457 return transproxy->GetChannel(component);
458}
459
460void BaseSession::DestroyChannel(const std::string& content_name,
461 int component) {
462 TransportProxy* transproxy = GetTransportProxy(content_name);
463 ASSERT(transproxy != NULL);
464 transproxy->DestroyChannel(component);
465}
466
467TransportProxy* BaseSession::GetOrCreateTransportProxy(
468 const std::string& content_name) {
469 TransportProxy* transproxy = GetTransportProxy(content_name);
470 if (transproxy)
471 return transproxy;
472
473 Transport* transport = CreateTransport(content_name);
474 transport->SetRole(initiator_ ? ROLE_CONTROLLING : ROLE_CONTROLLED);
475 transport->SetTiebreaker(ice_tiebreaker_);
476 // TODO: Connect all the Transport signals to TransportProxy
477 // then to the BaseSession.
478 transport->SignalConnecting.connect(
479 this, &BaseSession::OnTransportConnecting);
480 transport->SignalWritableState.connect(
481 this, &BaseSession::OnTransportWritable);
482 transport->SignalRequestSignaling.connect(
483 this, &BaseSession::OnTransportRequestSignaling);
484 transport->SignalTransportError.connect(
485 this, &BaseSession::OnTransportSendError);
486 transport->SignalRouteChange.connect(
487 this, &BaseSession::OnTransportRouteChange);
488 transport->SignalCandidatesAllocationDone.connect(
489 this, &BaseSession::OnTransportCandidatesAllocationDone);
490 transport->SignalRoleConflict.connect(
491 this, &BaseSession::OnRoleConflict);
492
493 transproxy = new TransportProxy(sid_, content_name,
494 new TransportWrapper(transport));
495 transproxy->SignalCandidatesReady.connect(
496 this, &BaseSession::OnTransportProxyCandidatesReady);
497 transports_[content_name] = transproxy;
498
499 return transproxy;
500}
501
502Transport* BaseSession::GetTransport(const std::string& content_name) {
503 TransportProxy* transproxy = GetTransportProxy(content_name);
504 if (transproxy == NULL)
505 return NULL;
506 return transproxy->impl();
507}
508
509TransportProxy* BaseSession::GetTransportProxy(
510 const std::string& content_name) {
511 TransportMap::iterator iter = transports_.find(content_name);
512 return (iter != transports_.end()) ? iter->second : NULL;
513}
514
515TransportProxy* BaseSession::GetTransportProxy(const Transport* transport) {
516 for (TransportMap::iterator iter = transports_.begin();
517 iter != transports_.end(); ++iter) {
518 TransportProxy* transproxy = iter->second;
519 if (transproxy->impl() == transport) {
520 return transproxy;
521 }
522 }
523 return NULL;
524}
525
526TransportProxy* BaseSession::GetFirstTransportProxy() {
527 if (transports_.empty())
528 return NULL;
529 return transports_.begin()->second;
530}
531
532void BaseSession::DestroyTransportProxy(
533 const std::string& content_name) {
534 TransportMap::iterator iter = transports_.find(content_name);
535 if (iter != transports_.end()) {
536 delete iter->second;
537 transports_.erase(content_name);
538 }
539}
540
541cricket::Transport* BaseSession::CreateTransport(
542 const std::string& content_name) {
543 ASSERT(transport_type_ == NS_GINGLE_P2P);
544 return new cricket::DtlsTransport<P2PTransport>(
545 signaling_thread(), worker_thread(), content_name,
546 port_allocator(), identity_);
547}
548
549bool BaseSession::GetStats(SessionStats* stats) {
550 for (TransportMap::iterator iter = transports_.begin();
551 iter != transports_.end(); ++iter) {
552 std::string proxy_id = iter->second->content_name();
553 // We are ignoring not-yet-instantiated transports.
554 if (iter->second->impl()) {
555 std::string transport_id = iter->second->impl()->content_name();
556 stats->proxy_to_transport[proxy_id] = transport_id;
557 if (stats->transport_stats.find(transport_id)
558 == stats->transport_stats.end()) {
559 TransportStats subinfos;
560 if (!iter->second->impl()->GetStats(&subinfos)) {
561 return false;
562 }
563 stats->transport_stats[transport_id] = subinfos;
564 }
565 }
566 }
567 return true;
568}
569
570void BaseSession::SetState(State state) {
571 ASSERT(signaling_thread_->IsCurrent());
572 if (state != state_) {
573 LogState(state_, state);
574 state_ = state;
575 SignalState(this, state_);
576 signaling_thread_->Post(this, MSG_STATE);
577 }
578 SignalNewDescription();
579}
580
581void BaseSession::SetError(Error error) {
582 ASSERT(signaling_thread_->IsCurrent());
583 if (error != error_) {
584 error_ = error;
585 SignalError(this, error);
586 }
587}
588
589void BaseSession::OnSignalingReady() {
590 ASSERT(signaling_thread()->IsCurrent());
591 for (TransportMap::iterator iter = transports_.begin();
592 iter != transports_.end(); ++iter) {
593 iter->second->OnSignalingReady();
594 }
595}
596
597// TODO(juberti): Since PushdownLocalTD now triggers the connection process to
598// start, remove this method once everyone calls PushdownLocalTD.
599void BaseSession::SpeculativelyConnectAllTransportChannels() {
600 // Put all transports into the connecting state.
601 for (TransportMap::iterator iter = transports_.begin();
602 iter != transports_.end(); ++iter) {
603 iter->second->ConnectChannels();
604 }
605}
606
607bool BaseSession::OnRemoteCandidates(const std::string& content_name,
608 const Candidates& candidates,
609 std::string* error) {
610 // Give candidates to the appropriate transport, and tell that transport
611 // to start connecting, if it's not already doing so.
612 TransportProxy* transproxy = GetTransportProxy(content_name);
613 if (!transproxy) {
614 *error = "Unknown content name " + content_name;
615 return false;
616 }
617 if (!transproxy->OnRemoteCandidates(candidates, error)) {
618 return false;
619 }
620 // TODO(juberti): Remove this call once we can be sure that we always have
621 // a local transport description (which will trigger the connection).
622 transproxy->ConnectChannels();
623 return true;
624}
625
626bool BaseSession::MaybeEnableMuxingSupport() {
627 // We need both a local and remote description to decide if we should mux.
628 if ((state_ == STATE_SENTINITIATE ||
629 state_ == STATE_RECEIVEDINITIATE) &&
630 ((local_description_ == NULL) ||
631 (remote_description_ == NULL))) {
632 return false;
633 }
634
635 // In order to perform the multiplexing, we need all proxies to be in the
636 // negotiated state, i.e. to have implementations underneath.
637 // Ensure that this is the case, regardless of whether we are going to mux.
638 for (TransportMap::iterator iter = transports_.begin();
639 iter != transports_.end(); ++iter) {
640 ASSERT(iter->second->negotiated());
641 if (!iter->second->negotiated())
642 return false;
643 }
644
645 // If both sides agree to BUNDLE, mux all the specified contents onto the
646 // transport belonging to the first content name in the BUNDLE group.
647 // If the contents are already muxed, this will be a no-op.
648 // TODO(juberti): Should this check that local and remote have configured
649 // BUNDLE the same way?
650 bool candidates_allocated = IsCandidateAllocationDone();
651 const ContentGroup* local_bundle_group =
652 local_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
653 const ContentGroup* remote_bundle_group =
654 remote_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
655 if (local_bundle_group && remote_bundle_group &&
656 local_bundle_group->FirstContentName()) {
657 const std::string* content_name = local_bundle_group->FirstContentName();
658 const ContentInfo* content =
659 local_description_->GetContentByName(*content_name);
660 ASSERT(content != NULL);
661 if (!SetSelectedProxy(content->name, local_bundle_group)) {
662 LOG(LS_WARNING) << "Failed to set up BUNDLE";
663 return false;
664 }
665
666 // If we weren't done gathering before, we might be done now, as a result
667 // of enabling mux.
668 LOG(LS_INFO) << "Enabling BUNDLE, bundling onto transport: "
669 << *content_name;
670 if (!candidates_allocated) {
671 MaybeCandidateAllocationDone();
672 }
673 } else {
674 LOG(LS_INFO) << "No BUNDLE information, not bundling.";
675 }
676 return true;
677}
678
679bool BaseSession::SetSelectedProxy(const std::string& content_name,
680 const ContentGroup* muxed_group) {
681 TransportProxy* selected_proxy = GetTransportProxy(content_name);
682 if (!selected_proxy) {
683 return false;
684 }
685
686 ASSERT(selected_proxy->negotiated());
687 for (TransportMap::iterator iter = transports_.begin();
688 iter != transports_.end(); ++iter) {
689 // If content is part of the mux group, then repoint its proxy at the
690 // transport object that we have chosen to mux onto. If the proxy
691 // is already pointing at the right object, it will be a no-op.
692 if (muxed_group->HasContentName(iter->first) &&
693 !iter->second->SetupMux(selected_proxy)) {
694 return false;
695 }
696 }
697 return true;
698}
699
700void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) {
701 // TODO(juberti): This is a clunky way of processing the done signal. Instead,
702 // TransportProxy should receive the done signal directly, set its allocated
703 // flag internally, and then reissue the done signal to Session.
704 // Overall we should make TransportProxy receive *all* the signals from
705 // Transport, since this removes the need to manually iterate over all
706 // the transports, as is needed to make sure signals are handled properly
707 // when BUNDLEing.
708#if 0
709 ASSERT(!IsCandidateAllocationDone());
710#endif
711 for (TransportMap::iterator iter = transports_.begin();
712 iter != transports_.end(); ++iter) {
713 if (iter->second->impl() == transport) {
714 iter->second->set_candidates_allocated(true);
715 }
716 }
717 MaybeCandidateAllocationDone();
718}
719
720bool BaseSession::IsCandidateAllocationDone() const {
721 for (TransportMap::const_iterator iter = transports_.begin();
722 iter != transports_.end(); ++iter) {
723 if (!iter->second->candidates_allocated())
724 return false;
725 }
726 return true;
727}
728
729void BaseSession::MaybeCandidateAllocationDone() {
730 if (IsCandidateAllocationDone()) {
731 LOG(LS_INFO) << "Candidate gathering is complete.";
732 OnCandidatesAllocationDone();
733 }
734}
735
736void BaseSession::OnRoleConflict() {
737 if (role_switch_) {
738 LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
739 return;
740 }
741
742 role_switch_ = true;
743 for (TransportMap::iterator iter = transports_.begin();
744 iter != transports_.end(); ++iter) {
745 // Role will be reverse of initial role setting.
746 TransportRole role = initiator_ ? ROLE_CONTROLLED : ROLE_CONTROLLING;
747 iter->second->SetRole(role);
748 }
749}
750
751void BaseSession::LogState(State old_state, State new_state) {
752 LOG(LS_INFO) << "Session:" << id()
753 << " Old state:" << StateToString(old_state)
754 << " New state:" << StateToString(new_state)
755 << " Type:" << content_type()
756 << " Transport:" << transport_type();
757}
758
759bool BaseSession::GetTransportDescription(const SessionDescription* description,
760 const std::string& content_name,
761 TransportDescription* tdesc) {
762 if (!description || !tdesc) {
763 return false;
764 }
765 const TransportInfo* transport_info =
766 description->GetTransportInfoByName(content_name);
767 if (!transport_info) {
768 return false;
769 }
770 *tdesc = transport_info->description;
771 return true;
772}
773
774void BaseSession::SignalNewDescription() {
775 ContentAction action;
776 ContentSource source;
777 if (!GetContentAction(&action, &source)) {
778 return;
779 }
780 if (source == CS_LOCAL) {
781 SignalNewLocalDescription(this, action);
782 } else {
783 SignalNewRemoteDescription(this, action);
784 }
785}
786
787bool BaseSession::GetContentAction(ContentAction* action,
788 ContentSource* source) {
789 switch (state_) {
790 // new local description
791 case STATE_SENTINITIATE:
792 *action = CA_OFFER;
793 *source = CS_LOCAL;
794 break;
795 case STATE_SENTPRACCEPT:
796 *action = CA_PRANSWER;
797 *source = CS_LOCAL;
798 break;
799 case STATE_SENTACCEPT:
800 *action = CA_ANSWER;
801 *source = CS_LOCAL;
802 break;
803 // new remote description
804 case STATE_RECEIVEDINITIATE:
805 *action = CA_OFFER;
806 *source = CS_REMOTE;
807 break;
808 case STATE_RECEIVEDPRACCEPT:
809 *action = CA_PRANSWER;
810 *source = CS_REMOTE;
811 break;
812 case STATE_RECEIVEDACCEPT:
813 *action = CA_ANSWER;
814 *source = CS_REMOTE;
815 break;
816 default:
817 return false;
818 }
819 return true;
820}
821
822void BaseSession::OnMessage(talk_base::Message *pmsg) {
823 switch (pmsg->message_id) {
824 case MSG_TIMEOUT:
825 // Session timeout has occured.
826 SetError(ERROR_TIME);
827 break;
828
829 case MSG_STATE:
830 switch (state_) {
831 case STATE_SENTACCEPT:
832 case STATE_RECEIVEDACCEPT:
833 SetState(STATE_INPROGRESS);
834 break;
835
836 default:
837 // Explicitly ignoring some states here.
838 break;
839 }
840 break;
841 }
842}
843
844Session::Session(SessionManager* session_manager,
845 const std::string& local_name,
846 const std::string& initiator_name,
847 const std::string& sid,
848 const std::string& content_type,
849 SessionClient* client)
850 : BaseSession(session_manager->signaling_thread(),
851 session_manager->worker_thread(),
852 session_manager->port_allocator(),
853 sid, content_type, initiator_name == local_name) {
854 ASSERT(client != NULL);
855 session_manager_ = session_manager;
856 local_name_ = local_name;
857 initiator_name_ = initiator_name;
858 transport_parser_ = new P2PTransportParser();
859 client_ = client;
860 initiate_acked_ = false;
861 current_protocol_ = PROTOCOL_HYBRID;
862}
863
864Session::~Session() {
865 delete transport_parser_;
866}
867
868bool Session::Initiate(const std::string &to,
869 const SessionDescription* sdesc) {
870 ASSERT(signaling_thread()->IsCurrent());
871 SessionError error;
872
873 // Only from STATE_INIT
874 if (state() != STATE_INIT)
875 return false;
876
877 // Setup for signaling.
878 set_remote_name(to);
879 set_local_description(sdesc);
880 if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()),
881 &error)) {
882 LOG(LS_ERROR) << "Could not create transports: " << error.text;
883 return false;
884 }
885
886 if (!SendInitiateMessage(sdesc, &error)) {
887 LOG(LS_ERROR) << "Could not send initiate message: " << error.text;
888 return false;
889 }
890
891 // We need to connect transport proxy and impl here so that we can process
892 // the TransportDescriptions.
893 SpeculativelyConnectAllTransportChannels();
894
895 PushdownTransportDescription(CS_LOCAL, CA_OFFER);
896 SetState(Session::STATE_SENTINITIATE);
897 return true;
898}
899
900bool Session::Accept(const SessionDescription* sdesc) {
901 ASSERT(signaling_thread()->IsCurrent());
902
903 // Only if just received initiate
904 if (state() != STATE_RECEIVEDINITIATE)
905 return false;
906
907 // Setup for signaling.
908 set_local_description(sdesc);
909
910 SessionError error;
911 if (!SendAcceptMessage(sdesc, &error)) {
912 LOG(LS_ERROR) << "Could not send accept message: " << error.text;
913 return false;
914 }
915 // TODO(juberti): Add BUNDLE support to transport-info messages.
916 PushdownTransportDescription(CS_LOCAL, CA_ANSWER);
917 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
918 SetState(Session::STATE_SENTACCEPT);
919 return true;
920}
921
922bool Session::Reject(const std::string& reason) {
923 ASSERT(signaling_thread()->IsCurrent());
924
925 // Reject is sent in response to an initiate or modify, to reject the
926 // request
927 if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY)
928 return false;
929
930 SessionError error;
931 if (!SendRejectMessage(reason, &error)) {
932 LOG(LS_ERROR) << "Could not send reject message: " << error.text;
933 return false;
934 }
935
936 SetState(STATE_SENTREJECT);
937 return true;
938}
939
940bool Session::TerminateWithReason(const std::string& reason) {
941 ASSERT(signaling_thread()->IsCurrent());
942
943 // Either side can terminate, at any time.
944 switch (state()) {
945 case STATE_SENTTERMINATE:
946 case STATE_RECEIVEDTERMINATE:
947 return false;
948
949 case STATE_SENTREJECT:
950 case STATE_RECEIVEDREJECT:
951 // We don't need to send terminate if we sent or received a reject...
952 // it's implicit.
953 break;
954
955 default:
956 SessionError error;
957 if (!SendTerminateMessage(reason, &error)) {
958 LOG(LS_ERROR) << "Could not send terminate message: " << error.text;
959 return false;
960 }
961 break;
962 }
963
964 SetState(STATE_SENTTERMINATE);
965 return true;
966}
967
968bool Session::SendInfoMessage(const XmlElements& elems) {
969 ASSERT(signaling_thread()->IsCurrent());
970 SessionError error;
971 if (!SendMessage(ACTION_SESSION_INFO, elems, &error)) {
972 LOG(LS_ERROR) << "Could not send info message " << error.text;
973 return false;
974 }
975 return true;
976}
977
978bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) {
979 XmlElements elems;
980 WriteError write_error;
981 if (!WriteDescriptionInfo(current_protocol_,
982 contents,
983 GetContentParsers(),
984 &elems, &write_error)) {
985 LOG(LS_ERROR) << "Could not write description info message: "
986 << write_error.text;
987 return false;
988 }
989 SessionError error;
990 if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) {
991 LOG(LS_ERROR) << "Could not send description info message: "
992 << error.text;
993 return false;
994 }
995 return true;
996}
997
998TransportInfos Session::GetEmptyTransportInfos(
999 const ContentInfos& contents) const {
1000 TransportInfos tinfos;
1001 for (ContentInfos::const_iterator content = contents.begin();
1002 content != contents.end(); ++content) {
1003 tinfos.push_back(
1004 TransportInfo(content->name,
1005 TransportDescription(transport_type(), Candidates())));
1006 }
1007 return tinfos;
1008}
1009
1010bool Session::OnRemoteCandidates(
1011 const TransportInfos& tinfos, ParseError* error) {
1012 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1013 tinfo != tinfos.end(); ++tinfo) {
1014 std::string str_error;
1015 if (!BaseSession::OnRemoteCandidates(
1016 tinfo->content_name, tinfo->description.candidates, &str_error)) {
1017 return BadParse(str_error, error);
1018 }
1019 }
1020 return true;
1021}
1022
1023bool Session::CreateTransportProxies(const TransportInfos& tinfos,
1024 SessionError* error) {
1025 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1026 tinfo != tinfos.end(); ++tinfo) {
1027 if (tinfo->description.transport_type != transport_type()) {
1028 error->SetText("No supported transport in offer.");
1029 return false;
1030 }
1031
1032 GetOrCreateTransportProxy(tinfo->content_name);
1033 }
1034 return true;
1035}
1036
1037TransportParserMap Session::GetTransportParsers() {
1038 TransportParserMap parsers;
1039 parsers[transport_type()] = transport_parser_;
1040 return parsers;
1041}
1042
1043CandidateTranslatorMap Session::GetCandidateTranslators() {
1044 CandidateTranslatorMap translators;
1045 // NOTE: This technique makes it impossible to parse G-ICE
1046 // candidates in session-initiate messages because the channels
1047 // aren't yet created at that point. Since we don't use candidates
1048 // in session-initiate messages, we should be OK. Once we switch to
1049 // ICE, this translation shouldn't be necessary.
1050 for (TransportMap::const_iterator iter = transport_proxies().begin();
1051 iter != transport_proxies().end(); ++iter) {
1052 translators[iter->first] = iter->second;
1053 }
1054 return translators;
1055}
1056
1057ContentParserMap Session::GetContentParsers() {
1058 ContentParserMap parsers;
1059 parsers[content_type()] = client_;
1060 // We need to be able parse both RTP-based and SCTP-based Jingle
1061 // with the same client.
1062 if (content_type() == NS_JINGLE_RTP) {
1063 parsers[NS_JINGLE_DRAFT_SCTP] = client_;
1064 }
1065 return parsers;
1066}
1067
1068void Session::OnTransportRequestSignaling(Transport* transport) {
1069 ASSERT(signaling_thread()->IsCurrent());
1070 TransportProxy* transproxy = GetTransportProxy(transport);
1071 ASSERT(transproxy != NULL);
1072 if (transproxy) {
1073 // Reset candidate allocation status for the transport proxy.
1074 transproxy->set_candidates_allocated(false);
1075 }
1076 SignalRequestSignaling(this);
1077}
1078
1079void Session::OnTransportConnecting(Transport* transport) {
1080 // This is an indication that we should begin watching the writability
1081 // state of the transport.
1082 OnTransportWritable(transport);
1083}
1084
1085void Session::OnTransportWritable(Transport* transport) {
1086 ASSERT(signaling_thread()->IsCurrent());
1087
1088 // If the transport is not writable, start a timer to make sure that it
1089 // becomes writable within a reasonable amount of time. If it does not, we
1090 // terminate since we can't actually send data. If the transport is writable,
1091 // cancel the timer. Note that writability transitions may occur repeatedly
1092 // during the lifetime of the session.
1093 signaling_thread()->Clear(this, MSG_TIMEOUT);
1094 if (transport->HasChannels() && !transport->writable()) {
1095 signaling_thread()->PostDelayed(
1096 session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
1097 }
1098}
1099
1100void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy,
1101 const Candidates& candidates) {
1102 ASSERT(signaling_thread()->IsCurrent());
1103 if (transproxy != NULL) {
1104 if (initiator() && !initiate_acked_) {
1105 // TODO: This is to work around server re-ordering
1106 // messages. We send the candidates once the session-initiate
1107 // is acked. Once we have fixed the server to guarantee message
1108 // order, we can remove this case.
1109 transproxy->AddUnsentCandidates(candidates);
1110 } else {
1111 if (!transproxy->negotiated()) {
1112 transproxy->AddSentCandidates(candidates);
1113 }
1114 SessionError error;
1115 if (!SendTransportInfoMessage(transproxy, candidates, &error)) {
1116 LOG(LS_ERROR) << "Could not send transport info message: "
1117 << error.text;
1118 return;
1119 }
1120 }
1121 }
1122}
1123
1124void Session::OnTransportSendError(Transport* transport,
1125 const buzz::XmlElement* stanza,
1126 const buzz::QName& name,
1127 const std::string& type,
1128 const std::string& text,
1129 const buzz::XmlElement* extra_info) {
1130 ASSERT(signaling_thread()->IsCurrent());
1131 SignalErrorMessage(this, stanza, name, type, text, extra_info);
1132}
1133
1134void Session::OnIncomingMessage(const SessionMessage& msg) {
1135 ASSERT(signaling_thread()->IsCurrent());
1136 ASSERT(state() == STATE_INIT || msg.from == remote_name());
1137
1138 if (current_protocol_== PROTOCOL_HYBRID) {
1139 if (msg.protocol == PROTOCOL_GINGLE) {
1140 current_protocol_ = PROTOCOL_GINGLE;
1141 } else {
1142 current_protocol_ = PROTOCOL_JINGLE;
1143 }
1144 }
1145
1146 bool valid = false;
1147 MessageError error;
1148 switch (msg.type) {
1149 case ACTION_SESSION_INITIATE:
1150 valid = OnInitiateMessage(msg, &error);
1151 break;
1152 case ACTION_SESSION_INFO:
1153 valid = OnInfoMessage(msg);
1154 break;
1155 case ACTION_SESSION_ACCEPT:
1156 valid = OnAcceptMessage(msg, &error);
1157 break;
1158 case ACTION_SESSION_REJECT:
1159 valid = OnRejectMessage(msg, &error);
1160 break;
1161 case ACTION_SESSION_TERMINATE:
1162 valid = OnTerminateMessage(msg, &error);
1163 break;
1164 case ACTION_TRANSPORT_INFO:
1165 valid = OnTransportInfoMessage(msg, &error);
1166 break;
1167 case ACTION_TRANSPORT_ACCEPT:
1168 valid = OnTransportAcceptMessage(msg, &error);
1169 break;
1170 case ACTION_DESCRIPTION_INFO:
1171 valid = OnDescriptionInfoMessage(msg, &error);
1172 break;
1173 default:
1174 valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
1175 "unknown session message type",
1176 &error);
1177 }
1178
1179 if (valid) {
1180 SendAcknowledgementMessage(msg.stanza);
1181 } else {
1182 SignalErrorMessage(this, msg.stanza, error.type,
1183 "modify", error.text, NULL);
1184 }
1185}
1186
1187void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
1188 const buzz::XmlElement* response_stanza,
1189 const SessionMessage& msg) {
1190 ASSERT(signaling_thread()->IsCurrent());
1191
1192 if (msg.type == ACTION_SESSION_INITIATE) {
1193 OnInitiateAcked();
1194 }
1195}
1196
1197void Session::OnInitiateAcked() {
1198 // TODO: This is to work around server re-ordering
1199 // messages. We send the candidates once the session-initiate
1200 // is acked. Once we have fixed the server to guarantee message
1201 // order, we can remove this case.
1202 if (!initiate_acked_) {
1203 initiate_acked_ = true;
1204 SessionError error;
1205 SendAllUnsentTransportInfoMessages(&error);
1206 }
1207}
1208
1209void Session::OnFailedSend(const buzz::XmlElement* orig_stanza,
1210 const buzz::XmlElement* error_stanza) {
1211 ASSERT(signaling_thread()->IsCurrent());
1212
1213 SessionMessage msg;
1214 ParseError parse_error;
1215 if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) {
1216 LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text
1217 << ":" << orig_stanza;
1218 return;
1219 }
1220
1221 // If the error is a session redirect, call OnRedirectError, which will
1222 // continue the session with a new remote JID.
1223 SessionRedirect redirect;
1224 if (FindSessionRedirect(error_stanza, &redirect)) {
1225 SessionError error;
1226 if (!OnRedirectError(redirect, &error)) {
1227 // TODO: Should we send a message back? The standard
1228 // says nothing about it.
1229 LOG(LS_ERROR) << "Failed to redirect: " << error.text;
1230 SetError(ERROR_RESPONSE);
1231 }
1232 return;
1233 }
1234
1235 std::string error_type = "cancel";
1236
1237 const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR);
1238 if (error) {
1239 error_type = error->Attr(buzz::QN_TYPE);
1240
1241 LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n"
1242 << "in response to:\n" << orig_stanza->Str();
1243 } else {
1244 // don't crash if <error> is missing
1245 LOG(LS_ERROR) << "Session error without <error/> element, ignoring";
1246 return;
1247 }
1248
1249 if (msg.type == ACTION_TRANSPORT_INFO) {
1250 // Transport messages frequently generate errors because they are sent right
1251 // when we detect a network failure. For that reason, we ignore such
1252 // errors, because if we do not establish writability again, we will
1253 // terminate anyway. The exceptions are transport-specific error tags,
1254 // which we pass on to the respective transport.
1255 } else if ((error_type != "continue") && (error_type != "wait")) {
1256 // We do not set an error if the other side said it is okay to continue
1257 // (possibly after waiting). These errors can be ignored.
1258 SetError(ERROR_RESPONSE);
1259 }
1260}
1261
1262bool Session::OnInitiateMessage(const SessionMessage& msg,
1263 MessageError* error) {
1264 if (!CheckState(STATE_INIT, error))
1265 return false;
1266
1267 SessionInitiate init;
1268 if (!ParseSessionInitiate(msg.protocol, msg.action_elem,
1269 GetContentParsers(), GetTransportParsers(),
1270 GetCandidateTranslators(),
1271 &init, error))
1272 return false;
1273
1274 SessionError session_error;
1275 if (!CreateTransportProxies(init.transports, &session_error)) {
1276 return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE,
1277 session_error.text, error);
1278 }
1279
1280 set_remote_name(msg.from);
1281 set_initiator_name(msg.initiator);
1282 set_remote_description(new SessionDescription(init.ClearContents(),
1283 init.transports,
1284 init.groups));
1285 // Updating transport with TransportDescription.
1286 PushdownTransportDescription(CS_REMOTE, CA_OFFER);
1287 SetState(STATE_RECEIVEDINITIATE);
1288
1289 // Users of Session may listen to state change and call Reject().
1290 if (state() != STATE_SENTREJECT) {
1291 if (!OnRemoteCandidates(init.transports, error))
1292 return false;
1293
1294 // TODO(juberti): Auto-generate and push down the local transport answer.
1295 // This is necessary for trickling to work with RFC 5245 ICE.
1296 }
1297 return true;
1298}
1299
1300bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) {
1301 if (!CheckState(STATE_SENTINITIATE, error))
1302 return false;
1303
1304 SessionAccept accept;
1305 if (!ParseSessionAccept(msg.protocol, msg.action_elem,
1306 GetContentParsers(), GetTransportParsers(),
1307 GetCandidateTranslators(),
1308 &accept, error)) {
1309 return false;
1310 }
1311
1312 // If we get an accept, we can assume the initiate has been
1313 // received, even if we haven't gotten an IQ response.
1314 OnInitiateAcked();
1315
1316 set_remote_description(new SessionDescription(accept.ClearContents(),
1317 accept.transports,
1318 accept.groups));
1319 // Updating transport with TransportDescription.
1320 PushdownTransportDescription(CS_REMOTE, CA_ANSWER);
1321 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
1322 SetState(STATE_RECEIVEDACCEPT);
1323
1324 if (!OnRemoteCandidates(accept.transports, error))
1325 return false;
1326
1327 return true;
1328}
1329
1330bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) {
1331 if (!CheckState(STATE_SENTINITIATE, error))
1332 return false;
1333
1334 SetState(STATE_RECEIVEDREJECT);
1335 return true;
1336}
1337
1338bool Session::OnInfoMessage(const SessionMessage& msg) {
1339 SignalInfoMessage(this, msg.action_elem);
1340 return true;
1341}
1342
1343bool Session::OnTerminateMessage(const SessionMessage& msg,
1344 MessageError* error) {
1345 SessionTerminate term;
1346 if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error))
1347 return false;
1348
1349 SignalReceivedTerminateReason(this, term.reason);
1350 if (term.debug_reason != buzz::STR_EMPTY) {
1351 LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason;
1352 }
1353
1354 SetState(STATE_RECEIVEDTERMINATE);
1355 return true;
1356}
1357
1358bool Session::OnTransportInfoMessage(const SessionMessage& msg,
1359 MessageError* error) {
1360 TransportInfos tinfos;
1361 if (!ParseTransportInfos(msg.protocol, msg.action_elem,
1362 initiator_description()->contents(),
1363 GetTransportParsers(), GetCandidateTranslators(),
1364 &tinfos, error))
1365 return false;
1366
1367 if (!OnRemoteCandidates(tinfos, error))
1368 return false;
1369
1370 return true;
1371}
1372
1373bool Session::OnTransportAcceptMessage(const SessionMessage& msg,
1374 MessageError* error) {
1375 // TODO: Currently here only for compatibility with
1376 // Gingle 1.1 clients (notably, Google Voice).
1377 return true;
1378}
1379
1380bool Session::OnDescriptionInfoMessage(const SessionMessage& msg,
1381 MessageError* error) {
1382 if (!CheckState(STATE_INPROGRESS, error))
1383 return false;
1384
1385 DescriptionInfo description_info;
1386 if (!ParseDescriptionInfo(msg.protocol, msg.action_elem,
1387 GetContentParsers(), GetTransportParsers(),
1388 GetCandidateTranslators(),
1389 &description_info, error)) {
1390 return false;
1391 }
1392
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001393 ContentInfos& updated_contents = description_info.contents;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001394
1395 // TODO: Currently, reflector sends back
1396 // video stream updates even for an audio-only call, which causes
1397 // this to fail. Put this back once reflector is fixed.
1398 //
1399 // ContentInfos::iterator it;
1400 // First, ensure all updates are valid before modifying remote_description_.
1401 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1402 // if (remote_description()->GetContentByName(it->name) == NULL) {
1403 // return false;
1404 // }
1405 // }
1406
1407 // TODO: We used to replace contents from an update, but
1408 // that no longer works with partial updates. We need to figure out
1409 // a way to merge patial updates into contents. For now, users of
1410 // Session should listen to SignalRemoteDescriptionUpdate and handle
1411 // updates. They should not expect remote_description to be the
1412 // latest value.
1413 //
1414 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1415 // remote_description()->RemoveContentByName(it->name);
1416 // remote_description()->AddContent(it->name, it->type, it->description);
1417 // }
1418 // }
1419
1420 SignalRemoteDescriptionUpdate(this, updated_contents);
1421 return true;
1422}
1423
1424bool BareJidsEqual(const std::string& name1,
1425 const std::string& name2) {
1426 buzz::Jid jid1(name1);
1427 buzz::Jid jid2(name2);
1428
1429 return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2);
1430}
1431
1432bool Session::OnRedirectError(const SessionRedirect& redirect,
1433 SessionError* error) {
1434 MessageError message_error;
1435 if (!CheckState(STATE_SENTINITIATE, &message_error)) {
1436 return BadWrite(message_error.text, error);
1437 }
1438
1439 if (!BareJidsEqual(remote_name(), redirect.target))
1440 return BadWrite("Redirection not allowed: must be the same bare jid.",
1441 error);
1442
1443 // When we receive a redirect, we point the session at the new JID
1444 // and resend the candidates.
1445 set_remote_name(redirect.target);
1446 return (SendInitiateMessage(local_description(), error) &&
1447 ResendAllTransportInfoMessages(error));
1448}
1449
1450bool Session::CheckState(State expected, MessageError* error) {
1451 if (state() != expected) {
1452 // The server can deliver messages out of order/repeated for various
1453 // reasons. For example, if the server does not recive our iq response,
1454 // it could assume that the iq it sent was lost, and will then send
1455 // it again. Ideally, we should implement reliable messaging with
1456 // duplicate elimination.
1457 return BadMessage(buzz::QN_STANZA_NOT_ALLOWED,
1458 "message not allowed in current state",
1459 error);
1460 }
1461 return true;
1462}
1463
1464void Session::SetError(Error error) {
1465 BaseSession::SetError(error);
1466 if (error != ERROR_NONE)
1467 signaling_thread()->Post(this, MSG_ERROR);
1468}
1469
1470void Session::OnMessage(talk_base::Message* pmsg) {
1471 // preserve this because BaseSession::OnMessage may modify it
1472 State orig_state = state();
1473
1474 BaseSession::OnMessage(pmsg);
1475
1476 switch (pmsg->message_id) {
1477 case MSG_ERROR:
1478 TerminateWithReason(STR_TERMINATE_ERROR);
1479 break;
1480
1481 case MSG_STATE:
1482 switch (orig_state) {
1483 case STATE_SENTREJECT:
1484 case STATE_RECEIVEDREJECT:
1485 // Assume clean termination.
1486 Terminate();
1487 break;
1488
1489 case STATE_SENTTERMINATE:
1490 case STATE_RECEIVEDTERMINATE:
1491 session_manager_->DestroySession(this);
1492 break;
1493
1494 default:
1495 // Explicitly ignoring some states here.
1496 break;
1497 }
1498 break;
1499 }
1500}
1501
1502bool Session::SendInitiateMessage(const SessionDescription* sdesc,
1503 SessionError* error) {
1504 SessionInitiate init;
1505 init.contents = sdesc->contents();
1506 init.transports = GetEmptyTransportInfos(init.contents);
1507 init.groups = sdesc->groups();
1508 return SendMessage(ACTION_SESSION_INITIATE, init, error);
1509}
1510
1511bool Session::WriteSessionAction(
1512 SignalingProtocol protocol, const SessionInitiate& init,
1513 XmlElements* elems, WriteError* error) {
1514 return WriteSessionInitiate(protocol, init.contents, init.transports,
1515 GetContentParsers(), GetTransportParsers(),
1516 GetCandidateTranslators(), init.groups,
1517 elems, error);
1518}
1519
1520bool Session::SendAcceptMessage(const SessionDescription* sdesc,
1521 SessionError* error) {
1522 XmlElements elems;
1523 if (!WriteSessionAccept(current_protocol_,
1524 sdesc->contents(),
1525 GetEmptyTransportInfos(sdesc->contents()),
1526 GetContentParsers(), GetTransportParsers(),
1527 GetCandidateTranslators(), sdesc->groups(),
1528 &elems, error)) {
1529 return false;
1530 }
1531 return SendMessage(ACTION_SESSION_ACCEPT, elems, error);
1532}
1533
1534bool Session::SendRejectMessage(const std::string& reason,
1535 SessionError* error) {
1536 SessionTerminate term(reason);
1537 return SendMessage(ACTION_SESSION_REJECT, term, error);
1538}
1539
1540bool Session::SendTerminateMessage(const std::string& reason,
1541 SessionError* error) {
1542 SessionTerminate term(reason);
1543 return SendMessage(ACTION_SESSION_TERMINATE, term, error);
1544}
1545
1546bool Session::WriteSessionAction(SignalingProtocol protocol,
1547 const SessionTerminate& term,
1548 XmlElements* elems, WriteError* error) {
1549 WriteSessionTerminate(protocol, term, elems);
1550 return true;
1551}
1552
1553bool Session::SendTransportInfoMessage(const TransportInfo& tinfo,
1554 SessionError* error) {
1555 return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error);
1556}
1557
1558bool Session::SendTransportInfoMessage(const TransportProxy* transproxy,
1559 const Candidates& candidates,
1560 SessionError* error) {
1561 return SendTransportInfoMessage(TransportInfo(transproxy->content_name(),
1562 TransportDescription(transproxy->type(), candidates)), error);
1563}
1564
1565bool Session::WriteSessionAction(SignalingProtocol protocol,
1566 const TransportInfo& tinfo,
1567 XmlElements* elems, WriteError* error) {
1568 TransportInfos tinfos;
1569 tinfos.push_back(tinfo);
1570 return WriteTransportInfos(protocol, tinfos,
1571 GetTransportParsers(), GetCandidateTranslators(),
1572 elems, error);
1573}
1574
1575bool Session::ResendAllTransportInfoMessages(SessionError* error) {
1576 for (TransportMap::const_iterator iter = transport_proxies().begin();
1577 iter != transport_proxies().end(); ++iter) {
1578 TransportProxy* transproxy = iter->second;
1579 if (transproxy->sent_candidates().size() > 0) {
1580 if (!SendTransportInfoMessage(
1581 transproxy, transproxy->sent_candidates(), error)) {
1582 LOG(LS_ERROR) << "Could not resend transport info messages: "
1583 << error->text;
1584 return false;
1585 }
1586 transproxy->ClearSentCandidates();
1587 }
1588 }
1589 return true;
1590}
1591
1592bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) {
1593 for (TransportMap::const_iterator iter = transport_proxies().begin();
1594 iter != transport_proxies().end(); ++iter) {
1595 TransportProxy* transproxy = iter->second;
1596 if (transproxy->unsent_candidates().size() > 0) {
1597 if (!SendTransportInfoMessage(
1598 transproxy, transproxy->unsent_candidates(), error)) {
1599 LOG(LS_ERROR) << "Could not send unsent transport info messages: "
1600 << error->text;
1601 return false;
1602 }
1603 transproxy->ClearUnsentCandidates();
1604 }
1605 }
1606 return true;
1607}
1608
1609bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
1610 SessionError* error) {
1611 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1612 new buzz::XmlElement(buzz::QN_IQ));
1613
1614 SessionMessage msg(current_protocol_, type, id(), initiator_name());
1615 msg.to = remote_name();
1616 WriteSessionMessage(msg, action_elems, stanza.get());
1617
1618 SignalOutgoingMessage(this, stanza.get());
1619 return true;
1620}
1621
1622template <typename Action>
1623bool Session::SendMessage(ActionType type, const Action& action,
1624 SessionError* error) {
1625 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1626 new buzz::XmlElement(buzz::QN_IQ));
1627 if (!WriteActionMessage(type, action, stanza.get(), error))
1628 return false;
1629
1630 SignalOutgoingMessage(this, stanza.get());
1631 return true;
1632}
1633
1634template <typename Action>
1635bool Session::WriteActionMessage(ActionType type, const Action& action,
1636 buzz::XmlElement* stanza,
1637 WriteError* error) {
1638 if (current_protocol_ == PROTOCOL_HYBRID) {
1639 if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error))
1640 return false;
1641 if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error))
1642 return false;
1643 } else {
1644 if (!WriteActionMessage(current_protocol_, type, action, stanza, error))
1645 return false;
1646 }
1647 return true;
1648}
1649
1650template <typename Action>
1651bool Session::WriteActionMessage(SignalingProtocol protocol,
1652 ActionType type, const Action& action,
1653 buzz::XmlElement* stanza, WriteError* error) {
1654 XmlElements action_elems;
1655 if (!WriteSessionAction(protocol, action, &action_elems, error))
1656 return false;
1657
1658 SessionMessage msg(protocol, type, id(), initiator_name());
1659 msg.to = remote_name();
1660
1661 WriteSessionMessage(msg, action_elems, stanza);
1662 return true;
1663}
1664
1665void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) {
1666 talk_base::scoped_ptr<buzz::XmlElement> ack(
1667 new buzz::XmlElement(buzz::QN_IQ));
1668 ack->SetAttr(buzz::QN_TO, remote_name());
1669 ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
1670 ack->SetAttr(buzz::QN_TYPE, "result");
1671
1672 SignalOutgoingMessage(this, ack.get());
1673}
1674
1675} // namespace cricket