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_manager.h" 6 7#include "base/bind.h" 8#include "remoting/jingle_glue/iq_sender.h" 9#include "remoting/jingle_glue/jingle_info_request.h" 10#include "remoting/jingle_glue/signal_strategy.h" 11#include "remoting/protocol/authenticator.h" 12#include "remoting/protocol/content_description.h" 13#include "remoting/protocol/jingle_messages.h" 14#include "remoting/protocol/jingle_session.h" 15#include "remoting/protocol/transport.h" 16#include "remoting/protocol/transport_config.h" 17#include "third_party/libjingle/source/talk/base/socketaddress.h" 18#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" 19 20using buzz::QName; 21 22namespace remoting { 23namespace protocol { 24 25JingleSessionManager::JingleSessionManager( 26 scoped_ptr<TransportFactory> transport_factory, 27 bool fetch_stun_relay_config) 28 : transport_factory_(transport_factory.Pass()), 29 fetch_stun_relay_config_(fetch_stun_relay_config), 30 signal_strategy_(NULL), 31 listener_(NULL), 32 ready_(false) { 33} 34 35JingleSessionManager::~JingleSessionManager() { 36 Close(); 37} 38 39void JingleSessionManager::Init( 40 SignalStrategy* signal_strategy, 41 SessionManager::Listener* listener) { 42 listener_ = listener; 43 signal_strategy_ = signal_strategy; 44 iq_sender_.reset(new IqSender(signal_strategy_)); 45 46 signal_strategy_->AddListener(this); 47 48 OnSignalStrategyStateChange(signal_strategy_->GetState()); 49} 50 51void JingleSessionManager::OnJingleInfo( 52 const std::string& relay_token, 53 const std::vector<std::string>& relay_hosts, 54 const std::vector<talk_base::SocketAddress>& stun_hosts) { 55 DCHECK(CalledOnValidThread()); 56 57 // TODO(sergeyu): Add support for multiple STUN/relay servers when 58 // it's implemented in libjingle and P2P Transport API. 59 TransportConfig config; 60 config.stun_server = stun_hosts[0].ToString(); 61 config.relay_server = relay_hosts[0]; 62 config.relay_token = relay_token; 63 transport_factory_->SetTransportConfig(config); 64 65 VLOG(1) << "STUN server: " << config.stun_server 66 << " Relay server: " << config.relay_server 67 << " Relay token: " << config.relay_token; 68 69 70 if (!ready_) { 71 ready_ = true; 72 listener_->OnSessionManagerReady(); 73 } 74} 75 76scoped_ptr<Session> JingleSessionManager::Connect( 77 const std::string& host_jid, 78 scoped_ptr<Authenticator> authenticator, 79 scoped_ptr<CandidateSessionConfig> config) { 80 scoped_ptr<JingleSession> session(new JingleSession(this)); 81 session->StartConnection(host_jid, authenticator.Pass(), config.Pass()); 82 sessions_[session->session_id_] = session.get(); 83 return session.PassAs<Session>(); 84} 85 86void JingleSessionManager::Close() { 87 DCHECK(CalledOnValidThread()); 88 89 // Close() can be called only after all sessions are destroyed. 90 DCHECK(sessions_.empty()); 91 92 listener_ = NULL; 93 jingle_info_request_.reset(); 94 95 if (signal_strategy_) { 96 signal_strategy_->RemoveListener(this); 97 signal_strategy_ = NULL; 98 } 99} 100 101void JingleSessionManager::set_authenticator_factory( 102 scoped_ptr<AuthenticatorFactory> authenticator_factory) { 103 DCHECK(CalledOnValidThread()); 104 authenticator_factory_ = authenticator_factory.Pass(); 105} 106 107void JingleSessionManager::OnSignalStrategyStateChange( 108 SignalStrategy::State state) { 109 if (state == SignalStrategy::CONNECTED) { 110 // Request STUN/Relay info if necessary. 111 if (fetch_stun_relay_config_) { 112 jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_)); 113 jingle_info_request_->Send(base::Bind(&JingleSessionManager::OnJingleInfo, 114 base::Unretained(this))); 115 } else if (!ready_) { 116 ready_ = true; 117 listener_->OnSessionManagerReady(); 118 } 119 } 120} 121 122bool JingleSessionManager::OnSignalStrategyIncomingStanza( 123 const buzz::XmlElement* stanza) { 124 if (!JingleMessage::IsJingleMessage(stanza)) 125 return false; 126 127 JingleMessage message; 128 std::string error; 129 if (!message.ParseXml(stanza, &error)) { 130 SendReply(stanza, JingleMessageReply::BAD_REQUEST); 131 return true; 132 } 133 134 if (message.action == JingleMessage::SESSION_INITIATE) { 135 // Description must be present in session-initiate messages. 136 DCHECK(message.description.get()); 137 138 SendReply(stanza, JingleMessageReply::NONE); 139 140 scoped_ptr<Authenticator> authenticator = 141 authenticator_factory_->CreateAuthenticator( 142 signal_strategy_->GetLocalJid(), message.from, 143 message.description->authenticator_message()); 144 145 JingleSession* session = new JingleSession(this); 146 session->InitializeIncomingConnection(message, authenticator.Pass()); 147 sessions_[session->session_id_] = session; 148 149 IncomingSessionResponse response = SessionManager::DECLINE; 150 listener_->OnIncomingSession(session, &response); 151 152 if (response == SessionManager::ACCEPT) { 153 session->AcceptIncomingConnection(message); 154 } else { 155 ErrorCode error; 156 switch (response) { 157 case INCOMPATIBLE: 158 error = INCOMPATIBLE_PROTOCOL; 159 break; 160 161 case OVERLOAD: 162 error = HOST_OVERLOAD; 163 break; 164 165 case DECLINE: 166 error = SESSION_REJECTED; 167 break; 168 169 default: 170 NOTREACHED(); 171 error = SESSION_REJECTED; 172 } 173 174 session->CloseInternal(error); 175 delete session; 176 DCHECK(sessions_.find(message.sid) == sessions_.end()); 177 } 178 179 return true; 180 } 181 182 SessionsMap::iterator it = sessions_.find(message.sid); 183 if (it == sessions_.end()) { 184 SendReply(stanza, JingleMessageReply::INVALID_SID); 185 return true; 186 } 187 188 it->second->OnIncomingMessage(message, base::Bind( 189 &JingleSessionManager::SendReply, base::Unretained(this), stanza)); 190 return true; 191} 192 193void JingleSessionManager::SendReply(const buzz::XmlElement* original_stanza, 194 JingleMessageReply::ErrorType error) { 195 signal_strategy_->SendStanza( 196 JingleMessageReply(error).ToXml(original_stanza)); 197} 198 199void JingleSessionManager::SessionDestroyed(JingleSession* session) { 200 sessions_.erase(session->session_id_); 201} 202 203} // namespace protocol 204} // namespace remoting 205