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