• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "remoting/protocol/jingle_session.h"
6 
7 #include "base/bind.h"
8 #include "base/rand_util.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "remoting/base/constants.h"
15 #include "remoting/protocol/authenticator.h"
16 #include "remoting/protocol/channel_authenticator.h"
17 #include "remoting/protocol/channel_multiplexer.h"
18 #include "remoting/protocol/content_description.h"
19 #include "remoting/protocol/jingle_messages.h"
20 #include "remoting/protocol/jingle_session_manager.h"
21 #include "remoting/protocol/pseudotcp_channel_factory.h"
22 #include "remoting/protocol/secure_channel_factory.h"
23 #include "remoting/protocol/session_config.h"
24 #include "remoting/protocol/stream_channel_factory.h"
25 #include "remoting/signaling/iq_sender.h"
26 #include "third_party/libjingle/source/talk/p2p/base/candidate.h"
27 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
28 
29 using buzz::XmlElement;
30 
31 namespace remoting {
32 namespace protocol {
33 
34 namespace {
35 // Delay after candidate creation before sending transport-info
36 // message. This is neccessary to be able to pack multiple candidates
37 // into one transport-info messages. The value needs to be greater
38 // than zero because ports are opened asynchronously in the browser
39 // process.
40 const int kTransportInfoSendDelayMs = 2;
41 
42 // How long we should wait for a response from the other end. This value is used
43 // for all requests except |transport-info|.
44 const int kDefaultMessageTimeout = 10;
45 
46 // During a reconnection, it usually takes longer for the peer to respond due to
47 // pending messages in the channel from the previous session.  From experiment,
48 // it can take up to 20s for the session to reconnect. To make it safe, setting
49 // the timeout to 30s.
50 const int kSessionInitiateAndAcceptTimeout = kDefaultMessageTimeout * 3;
51 
52 // Timeout for the transport-info messages.
53 const int kTransportInfoTimeout = 10 * 60;
54 
55 // Name of the multiplexed channel.
56 const char kMuxChannelName[] = "mux";
57 
AuthRejectionReasonToErrorCode(Authenticator::RejectionReason reason)58 ErrorCode AuthRejectionReasonToErrorCode(
59     Authenticator::RejectionReason reason) {
60   switch (reason) {
61     case Authenticator::INVALID_CREDENTIALS:
62       return AUTHENTICATION_FAILED;
63     case Authenticator::PROTOCOL_ERROR:
64       return INCOMPATIBLE_PROTOCOL;
65   }
66   NOTREACHED();
67   return UNKNOWN_ERROR;
68 }
69 
70 }  // namespace
71 
JingleSession(JingleSessionManager * session_manager)72 JingleSession::JingleSession(JingleSessionManager* session_manager)
73     : session_manager_(session_manager),
74       event_handler_(NULL),
75       state_(INITIALIZING),
76       error_(OK),
77       config_is_set_(false),
78       weak_factory_(this) {
79 }
80 
~JingleSession()81 JingleSession::~JingleSession() {
82   channel_multiplexer_.reset();
83   STLDeleteContainerPointers(pending_requests_.begin(),
84                              pending_requests_.end());
85   STLDeleteContainerPointers(transport_info_requests_.begin(),
86                              transport_info_requests_.end());
87 
88   DCHECK(channels_.empty());
89 
90   session_manager_->SessionDestroyed(this);
91 }
92 
SetEventHandler(Session::EventHandler * event_handler)93 void JingleSession::SetEventHandler(Session::EventHandler* event_handler) {
94   DCHECK(CalledOnValidThread());
95   DCHECK(event_handler);
96   event_handler_ = event_handler;
97 }
98 
error()99 ErrorCode JingleSession::error() {
100   DCHECK(CalledOnValidThread());
101   return error_;
102 }
103 
StartConnection(const std::string & peer_jid,scoped_ptr<Authenticator> authenticator,scoped_ptr<CandidateSessionConfig> config)104 void JingleSession::StartConnection(
105     const std::string& peer_jid,
106     scoped_ptr<Authenticator> authenticator,
107     scoped_ptr<CandidateSessionConfig> config) {
108   DCHECK(CalledOnValidThread());
109   DCHECK(authenticator.get());
110   DCHECK_EQ(authenticator->state(), Authenticator::MESSAGE_READY);
111 
112   peer_jid_ = peer_jid;
113   authenticator_ = authenticator.Pass();
114   candidate_config_ = config.Pass();
115 
116   // Generate random session ID. There are usually not more than 1
117   // concurrent session per host, so a random 64-bit integer provides
118   // enough entropy. In the worst case connection will fail when two
119   // clients generate the same session ID concurrently.
120   session_id_ = base::Int64ToString(base::RandGenerator(kint64max));
121 
122   // Send session-initiate message.
123   JingleMessage message(peer_jid_, JingleMessage::SESSION_INITIATE,
124                         session_id_);
125   message.initiator = session_manager_->signal_strategy_->GetLocalJid();
126   message.description.reset(
127       new ContentDescription(candidate_config_->Clone(),
128                              authenticator_->GetNextMessage()));
129   SendMessage(message);
130 
131   SetState(CONNECTING);
132 }
133 
InitializeIncomingConnection(const JingleMessage & initiate_message,scoped_ptr<Authenticator> authenticator)134 void JingleSession::InitializeIncomingConnection(
135     const JingleMessage& initiate_message,
136     scoped_ptr<Authenticator> authenticator) {
137   DCHECK(CalledOnValidThread());
138   DCHECK(initiate_message.description.get());
139   DCHECK(authenticator.get());
140   DCHECK_EQ(authenticator->state(), Authenticator::WAITING_MESSAGE);
141 
142   peer_jid_ = initiate_message.from;
143   authenticator_ = authenticator.Pass();
144   session_id_ = initiate_message.sid;
145   candidate_config_ = initiate_message.description->config()->Clone();
146 
147   SetState(ACCEPTING);
148 }
149 
AcceptIncomingConnection(const JingleMessage & initiate_message)150 void JingleSession::AcceptIncomingConnection(
151     const JingleMessage& initiate_message) {
152   DCHECK(config_is_set_);
153 
154   // Process the first authentication message.
155   const buzz::XmlElement* first_auth_message =
156       initiate_message.description->authenticator_message();
157 
158   if (!first_auth_message) {
159     CloseInternal(INCOMPATIBLE_PROTOCOL);
160     return;
161   }
162 
163   DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
164   // |authenticator_| is owned, so Unretained() is safe here.
165   authenticator_->ProcessMessage(first_auth_message, base::Bind(
166       &JingleSession::ContinueAcceptIncomingConnection,
167       base::Unretained(this)));
168 }
169 
ContinueAcceptIncomingConnection()170 void JingleSession::ContinueAcceptIncomingConnection() {
171   DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
172   if (authenticator_->state() == Authenticator::REJECTED) {
173     CloseInternal(AuthRejectionReasonToErrorCode(
174         authenticator_->rejection_reason()));
175     return;
176   }
177 
178   // Send the session-accept message.
179   JingleMessage message(peer_jid_, JingleMessage::SESSION_ACCEPT,
180                         session_id_);
181 
182   scoped_ptr<buzz::XmlElement> auth_message;
183   if (authenticator_->state() == Authenticator::MESSAGE_READY)
184     auth_message = authenticator_->GetNextMessage();
185 
186   message.description.reset(
187       new ContentDescription(CandidateSessionConfig::CreateFrom(config_),
188                              auth_message.Pass()));
189   SendMessage(message);
190 
191   // Update state.
192   SetState(CONNECTED);
193 
194   if (authenticator_->state() == Authenticator::ACCEPTED) {
195     OnAuthenticated();
196   } else {
197     DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
198     if (authenticator_->started()) {
199       SetState(AUTHENTICATING);
200     }
201   }
202 }
203 
jid()204 const std::string& JingleSession::jid() {
205   DCHECK(CalledOnValidThread());
206   return peer_jid_;
207 }
208 
candidate_config()209 const CandidateSessionConfig* JingleSession::candidate_config() {
210   DCHECK(CalledOnValidThread());
211   return candidate_config_.get();
212 }
213 
config()214 const SessionConfig& JingleSession::config() {
215   DCHECK(CalledOnValidThread());
216   return config_;
217 }
218 
set_config(const SessionConfig & config)219 void JingleSession::set_config(const SessionConfig& config) {
220   DCHECK(CalledOnValidThread());
221   DCHECK(!config_is_set_);
222   config_ = config;
223   config_is_set_ = true;
224 }
225 
GetTransportChannelFactory()226 StreamChannelFactory* JingleSession::GetTransportChannelFactory() {
227   DCHECK(CalledOnValidThread());
228   return secure_channel_factory_.get();
229 }
230 
GetMultiplexedChannelFactory()231 StreamChannelFactory* JingleSession::GetMultiplexedChannelFactory() {
232   DCHECK(CalledOnValidThread());
233   if (!channel_multiplexer_.get()) {
234     channel_multiplexer_.reset(
235         new ChannelMultiplexer(GetTransportChannelFactory(), kMuxChannelName));
236   }
237   return channel_multiplexer_.get();
238 }
239 
Close()240 void JingleSession::Close() {
241   DCHECK(CalledOnValidThread());
242 
243   CloseInternal(OK);
244 }
245 
AddPendingRemoteCandidates(Transport * channel,const std::string & name)246 void JingleSession::AddPendingRemoteCandidates(Transport* channel,
247                                                const std::string& name) {
248   std::list<JingleMessage::NamedCandidate>::iterator it =
249       pending_remote_candidates_.begin();
250   while(it != pending_remote_candidates_.end()) {
251     if (it->name == name) {
252       channel->AddRemoteCandidate(it->candidate);
253       it = pending_remote_candidates_.erase(it);
254     } else {
255       ++it;
256     }
257   }
258 }
259 
CreateChannel(const std::string & name,const ChannelCreatedCallback & callback)260 void JingleSession::CreateChannel(const std::string& name,
261                                   const ChannelCreatedCallback& callback) {
262   DCHECK(!channels_[name]);
263 
264   scoped_ptr<Transport> channel =
265       session_manager_->transport_factory_->CreateTransport();
266   channel->Connect(name, this, callback);
267   AddPendingRemoteCandidates(channel.get(), name);
268   channels_[name] = channel.release();
269 }
270 
CancelChannelCreation(const std::string & name)271 void JingleSession::CancelChannelCreation(const std::string& name) {
272   ChannelsMap::iterator it = channels_.find(name);
273   if (it != channels_.end()) {
274     DCHECK(!it->second->is_connected());
275     delete it->second;
276     DCHECK(channels_.find(name) == channels_.end());
277   }
278 }
279 
OnTransportCandidate(Transport * transport,const cricket::Candidate & candidate)280 void JingleSession::OnTransportCandidate(Transport* transport,
281                                          const cricket::Candidate& candidate) {
282   pending_candidates_.push_back(JingleMessage::NamedCandidate(
283       transport->name(), candidate));
284 
285   if (!transport_infos_timer_.IsRunning()) {
286     // Delay sending the new candidates in case we get more candidates
287     // that we can send in one message.
288     transport_infos_timer_.Start(
289         FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
290         this, &JingleSession::SendTransportInfo);
291   }
292 }
293 
OnTransportRouteChange(Transport * transport,const TransportRoute & route)294 void JingleSession::OnTransportRouteChange(Transport* transport,
295                                            const TransportRoute& route) {
296   if (event_handler_)
297     event_handler_->OnSessionRouteChange(transport->name(), route);
298 }
299 
OnTransportFailed(Transport * transport)300 void JingleSession::OnTransportFailed(Transport* transport) {
301   CloseInternal(CHANNEL_CONNECTION_ERROR);
302 }
303 
OnTransportDeleted(Transport * transport)304 void JingleSession::OnTransportDeleted(Transport* transport) {
305   ChannelsMap::iterator it = channels_.find(transport->name());
306   DCHECK_EQ(it->second, transport);
307   channels_.erase(it);
308 }
309 
SendMessage(const JingleMessage & message)310 void JingleSession::SendMessage(const JingleMessage& message) {
311   scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
312       message.ToXml(),
313       base::Bind(&JingleSession::OnMessageResponse,
314                  base::Unretained(this),
315                  message.action));
316 
317   int timeout = kDefaultMessageTimeout;
318   if (message.action == JingleMessage::SESSION_INITIATE ||
319       message.action == JingleMessage::SESSION_ACCEPT) {
320     timeout = kSessionInitiateAndAcceptTimeout;
321   }
322   if (request) {
323     request->SetTimeout(base::TimeDelta::FromSeconds(timeout));
324     pending_requests_.insert(request.release());
325   } else {
326     LOG(ERROR) << "Failed to send a "
327                << JingleMessage::GetActionName(message.action) << " message";
328   }
329 }
330 
OnMessageResponse(JingleMessage::ActionType request_type,IqRequest * request,const buzz::XmlElement * response)331 void JingleSession::OnMessageResponse(
332     JingleMessage::ActionType request_type,
333     IqRequest* request,
334     const buzz::XmlElement* response) {
335   // Delete the request from the list of pending requests.
336   pending_requests_.erase(request);
337   delete request;
338 
339   // Ignore all responses after session was closed.
340   if (state_ == CLOSED || state_ == FAILED)
341     return;
342 
343   std::string type_str = JingleMessage::GetActionName(request_type);
344 
345   // |response| will be NULL if the request timed out.
346   if (!response) {
347     LOG(ERROR) << type_str << " request timed out.";
348     CloseInternal(SIGNALING_TIMEOUT);
349     return;
350   } else {
351     const std::string& type =
352         response->Attr(buzz::QName(std::string(), "type"));
353     if (type != "result") {
354       LOG(ERROR) << "Received error in response to " << type_str
355                  << " message: \"" << response->Str()
356                  << "\". Terminating the session.";
357 
358       // TODO(sergeyu): There may be different reasons for error
359       // here. Parse the response stanza to find failure reason.
360       CloseInternal(PEER_IS_OFFLINE);
361     }
362   }
363 }
364 
SendTransportInfo()365 void JingleSession::SendTransportInfo() {
366   JingleMessage message(peer_jid_, JingleMessage::TRANSPORT_INFO, session_id_);
367   message.candidates.swap(pending_candidates_);
368 
369   scoped_ptr<IqRequest> request = session_manager_->iq_sender()->SendIq(
370       message.ToXml(),
371       base::Bind(&JingleSession::OnTransportInfoResponse,
372                  base::Unretained(this)));
373   if (request) {
374     request->SetTimeout(base::TimeDelta::FromSeconds(kTransportInfoTimeout));
375     transport_info_requests_.push_back(request.release());
376   } else {
377     LOG(ERROR) << "Failed to send a transport-info message";
378   }
379 }
380 
OnTransportInfoResponse(IqRequest * request,const buzz::XmlElement * response)381 void JingleSession::OnTransportInfoResponse(IqRequest* request,
382                                             const buzz::XmlElement* response) {
383   DCHECK(!transport_info_requests_.empty());
384 
385   // Consider transport-info requests sent before this one lost and delete
386   // corresponding IqRequest objects.
387   while (transport_info_requests_.front() != request) {
388     delete transport_info_requests_.front();
389     transport_info_requests_.pop_front();
390   }
391 
392   // Delete the |request| itself.
393   DCHECK_EQ(request, transport_info_requests_.front());
394   delete request;
395   transport_info_requests_.pop_front();
396 
397   // Ignore transport-info timeouts.
398   if (!response) {
399     LOG(ERROR) << "transport-info request has timed out.";
400     return;
401   }
402 
403   const std::string& type = response->Attr(buzz::QName(std::string(), "type"));
404   if (type != "result") {
405     LOG(ERROR) << "Received error in response to transport-info message: \""
406                << response->Str() << "\". Terminating the session.";
407     CloseInternal(PEER_IS_OFFLINE);
408   }
409 }
410 
OnIncomingMessage(const JingleMessage & message,const ReplyCallback & reply_callback)411 void JingleSession::OnIncomingMessage(const JingleMessage& message,
412                                       const ReplyCallback& reply_callback) {
413   DCHECK(CalledOnValidThread());
414 
415   if (message.from != peer_jid_) {
416     // Ignore messages received from a different Jid.
417     reply_callback.Run(JingleMessageReply::INVALID_SID);
418     return;
419   }
420 
421   switch (message.action) {
422     case JingleMessage::SESSION_ACCEPT:
423       OnAccept(message, reply_callback);
424       break;
425 
426     case JingleMessage::SESSION_INFO:
427       OnSessionInfo(message, reply_callback);
428       break;
429 
430     case JingleMessage::TRANSPORT_INFO:
431       reply_callback.Run(JingleMessageReply::NONE);
432       ProcessTransportInfo(message);
433       break;
434 
435     case JingleMessage::SESSION_TERMINATE:
436       OnTerminate(message, reply_callback);
437       break;
438 
439     default:
440       reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
441   }
442 }
443 
OnAccept(const JingleMessage & message,const ReplyCallback & reply_callback)444 void JingleSession::OnAccept(const JingleMessage& message,
445                              const ReplyCallback& reply_callback) {
446   if (state_ != CONNECTING) {
447     reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
448     return;
449   }
450 
451   reply_callback.Run(JingleMessageReply::NONE);
452 
453   const buzz::XmlElement* auth_message =
454       message.description->authenticator_message();
455   if (!auth_message) {
456     DLOG(WARNING) << "Received session-accept without authentication message ";
457     CloseInternal(INCOMPATIBLE_PROTOCOL);
458     return;
459   }
460 
461   if (!InitializeConfigFromDescription(message.description.get())) {
462     CloseInternal(INCOMPATIBLE_PROTOCOL);
463     return;
464   }
465 
466   // In case there is transport information in the accept message.
467   ProcessTransportInfo(message);
468 
469   SetState(CONNECTED);
470 
471   DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
472   authenticator_->ProcessMessage(auth_message, base::Bind(
473       &JingleSession::ProcessAuthenticationStep,base::Unretained(this)));
474 }
475 
OnSessionInfo(const JingleMessage & message,const ReplyCallback & reply_callback)476 void JingleSession::OnSessionInfo(const JingleMessage& message,
477                                   const ReplyCallback& reply_callback) {
478   if (!message.info.get() ||
479       !Authenticator::IsAuthenticatorMessage(message.info.get())) {
480     reply_callback.Run(JingleMessageReply::UNSUPPORTED_INFO);
481     return;
482   }
483 
484   if ((state_ != CONNECTED && state_ != AUTHENTICATING) ||
485       authenticator_->state() != Authenticator::WAITING_MESSAGE) {
486     LOG(WARNING) << "Received unexpected authenticator message "
487                  << message.info->Str();
488     reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
489     CloseInternal(INCOMPATIBLE_PROTOCOL);
490     return;
491   }
492 
493   reply_callback.Run(JingleMessageReply::NONE);
494 
495   authenticator_->ProcessMessage(message.info.get(), base::Bind(
496       &JingleSession::ProcessAuthenticationStep, base::Unretained(this)));
497 }
498 
ProcessTransportInfo(const JingleMessage & message)499 void JingleSession::ProcessTransportInfo(const JingleMessage& message) {
500   for (std::list<JingleMessage::NamedCandidate>::const_iterator it =
501            message.candidates.begin();
502        it != message.candidates.end(); ++it) {
503     ChannelsMap::iterator channel = channels_.find(it->name);
504     if (channel != channels_.end()) {
505       channel->second->AddRemoteCandidate(it->candidate);
506     } else {
507       // Transport info was received before the channel was created.
508       // This could happen due to messages being reordered on the wire.
509       pending_remote_candidates_.push_back(*it);
510     }
511   }
512 }
513 
OnTerminate(const JingleMessage & message,const ReplyCallback & reply_callback)514 void JingleSession::OnTerminate(const JingleMessage& message,
515                                 const ReplyCallback& reply_callback) {
516   if (!is_session_active()) {
517     LOG(WARNING) << "Received unexpected session-terminate message.";
518     reply_callback.Run(JingleMessageReply::UNEXPECTED_REQUEST);
519     return;
520   }
521 
522   reply_callback.Run(JingleMessageReply::NONE);
523 
524   switch (message.reason) {
525     case JingleMessage::SUCCESS:
526       if (state_ == CONNECTING) {
527         error_ = SESSION_REJECTED;
528       } else {
529         error_ = OK;
530       }
531       break;
532     case JingleMessage::DECLINE:
533       error_ = AUTHENTICATION_FAILED;
534       break;
535     case JingleMessage::CANCEL:
536       error_ = HOST_OVERLOAD;
537       break;
538     case JingleMessage::GENERAL_ERROR:
539       error_ = CHANNEL_CONNECTION_ERROR;
540       break;
541     case JingleMessage::INCOMPATIBLE_PARAMETERS:
542       error_ = INCOMPATIBLE_PROTOCOL;
543       break;
544     default:
545       error_ = UNKNOWN_ERROR;
546   }
547 
548   if (error_ != OK) {
549     SetState(FAILED);
550   } else {
551     SetState(CLOSED);
552   }
553 }
554 
InitializeConfigFromDescription(const ContentDescription * description)555 bool JingleSession::InitializeConfigFromDescription(
556     const ContentDescription* description) {
557   DCHECK(description);
558 
559   if (!description->config()->GetFinalConfig(&config_)) {
560     LOG(ERROR) << "session-accept does not specify configuration";
561     return false;
562   }
563   if (!candidate_config()->IsSupported(config_)) {
564     LOG(ERROR) << "session-accept specifies an invalid configuration";
565     return false;
566   }
567 
568   return true;
569 }
570 
ProcessAuthenticationStep()571 void JingleSession::ProcessAuthenticationStep() {
572   DCHECK(CalledOnValidThread());
573   DCHECK_NE(authenticator_->state(), Authenticator::PROCESSING_MESSAGE);
574 
575   if (state_ != CONNECTED && state_ != AUTHENTICATING) {
576     DCHECK(state_ == FAILED || state_ == CLOSED);
577     // The remote host closed the connection while the authentication was being
578     // processed asynchronously, nothing to do.
579     return;
580   }
581 
582   if (authenticator_->state() == Authenticator::MESSAGE_READY) {
583     JingleMessage message(peer_jid_, JingleMessage::SESSION_INFO, session_id_);
584     message.info = authenticator_->GetNextMessage();
585     DCHECK(message.info.get());
586     SendMessage(message);
587   }
588   DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
589 
590   // The current JingleSession object can be destroyed by event_handler of
591   // SetState(AUTHENTICATING) and cause subsequent dereferencing of the this
592   // pointer to crash.  To protect against it, we run ContinueAuthenticationStep
593   // asychronously using a weak pointer.
594   base::ThreadTaskRunnerHandle::Get()->PostTask(
595     FROM_HERE,
596     base::Bind(&JingleSession::ContinueAuthenticationStep,
597                weak_factory_.GetWeakPtr()));
598 
599   if (authenticator_->started()) {
600     SetState(AUTHENTICATING);
601   }
602 }
603 
ContinueAuthenticationStep()604 void JingleSession::ContinueAuthenticationStep() {
605   if (authenticator_->state() == Authenticator::ACCEPTED) {
606     OnAuthenticated();
607   } else if (authenticator_->state() == Authenticator::REJECTED) {
608     CloseInternal(AuthRejectionReasonToErrorCode(
609         authenticator_->rejection_reason()));
610   }
611 }
612 
OnAuthenticated()613 void JingleSession::OnAuthenticated() {
614   pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this));
615   secure_channel_factory_.reset(
616       new SecureChannelFactory(pseudotcp_channel_factory_.get(),
617                                authenticator_.get()));
618 
619   SetState(AUTHENTICATED);
620 }
621 
CloseInternal(ErrorCode error)622 void JingleSession::CloseInternal(ErrorCode error) {
623   DCHECK(CalledOnValidThread());
624 
625   if (is_session_active()) {
626     // Send session-terminate message with the appropriate error code.
627     JingleMessage::Reason reason;
628     switch (error) {
629       case OK:
630         reason = JingleMessage::SUCCESS;
631         break;
632       case SESSION_REJECTED:
633       case AUTHENTICATION_FAILED:
634         reason = JingleMessage::DECLINE;
635         break;
636       case INCOMPATIBLE_PROTOCOL:
637         reason = JingleMessage::INCOMPATIBLE_PARAMETERS;
638         break;
639       case HOST_OVERLOAD:
640         reason = JingleMessage::CANCEL;
641         break;
642       default:
643         reason = JingleMessage::GENERAL_ERROR;
644     }
645 
646     JingleMessage message(peer_jid_, JingleMessage::SESSION_TERMINATE,
647                           session_id_);
648     message.reason = reason;
649     SendMessage(message);
650   }
651 
652   error_ = error;
653 
654   if (state_ != FAILED && state_ != CLOSED) {
655     if (error != OK) {
656       SetState(FAILED);
657     } else {
658       SetState(CLOSED);
659     }
660   }
661 }
662 
SetState(State new_state)663 void JingleSession::SetState(State new_state) {
664   DCHECK(CalledOnValidThread());
665 
666   if (new_state != state_) {
667     DCHECK_NE(state_, CLOSED);
668     DCHECK_NE(state_, FAILED);
669 
670     state_ = new_state;
671     if (event_handler_)
672       event_handler_->OnSessionStateChange(new_state);
673   }
674 }
675 
is_session_active()676 bool JingleSession::is_session_active() {
677   return state_ == CONNECTING || state_ == ACCEPTING || state_ == CONNECTED ||
678         state_ == AUTHENTICATING || state_ == AUTHENTICATED;
679 }
680 
681 }  // namespace protocol
682 }  // namespace remoting
683