Introduced IDR_SIGNIN_INTERNALS_INDEX_* strings to grit_whitelist.txt
[chromium-blink-merge.git] / remoting / host / signaling_connector.cc
blobad9fbe5dc1d45a9042df474b080ea753bc2f449c
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.
5 #include "remoting/host/signaling_connector.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/strings/string_util.h"
10 #include "google_apis/google_api_keys.h"
11 #include "net/url_request/url_fetcher.h"
12 #include "net/url_request/url_request_context_getter.h"
13 #include "remoting/host/dns_blackhole_checker.h"
15 namespace remoting {
17 namespace {
19 // The delay between reconnect attempts will increase exponentially up
20 // to the maximum specified here.
21 const int kMaxReconnectDelaySeconds = 10 * 60;
23 // Time when we we try to update OAuth token before its expiration.
24 const int kTokenUpdateTimeBeforeExpirySeconds = 60;
26 } // namespace
28 SignalingConnector::OAuthCredentials::OAuthCredentials(
29 const std::string& login_value,
30 const std::string& refresh_token_value,
31 bool is_service_account)
32 : login(login_value),
33 refresh_token(refresh_token_value),
34 is_service_account(is_service_account) {
37 SignalingConnector::SignalingConnector(
38 XmppSignalStrategy* signal_strategy,
39 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter,
40 scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker,
41 const base::Closure& auth_failed_callback)
42 : signal_strategy_(signal_strategy),
43 url_request_context_getter_(url_request_context_getter),
44 auth_failed_callback_(auth_failed_callback),
45 dns_blackhole_checker_(dns_blackhole_checker.Pass()),
46 reconnect_attempts_(0),
47 refreshing_oauth_token_(false) {
48 DCHECK(!auth_failed_callback_.is_null());
49 DCHECK(dns_blackhole_checker_.get());
50 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
51 net::NetworkChangeNotifier::AddIPAddressObserver(this);
52 signal_strategy_->AddListener(this);
53 ScheduleTryReconnect();
56 SignalingConnector::~SignalingConnector() {
57 signal_strategy_->RemoveListener(this);
58 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
59 net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
62 void SignalingConnector::EnableOAuth(
63 scoped_ptr<OAuthCredentials> oauth_credentials) {
64 oauth_credentials_ = oauth_credentials.Pass();
65 gaia_oauth_client_.reset(
66 new gaia::GaiaOAuthClient(url_request_context_getter_.get()));
69 void SignalingConnector::OnSignalStrategyStateChange(
70 SignalStrategy::State state) {
71 DCHECK(CalledOnValidThread());
73 if (state == SignalStrategy::CONNECTED) {
74 LOG(INFO) << "Signaling connected.";
75 reconnect_attempts_ = 0;
76 } else if (state == SignalStrategy::DISCONNECTED) {
77 LOG(INFO) << "Signaling disconnected.";
78 reconnect_attempts_++;
80 // If authentication failed then we have an invalid OAuth token,
81 // inform the upper layer about it.
82 if (signal_strategy_->GetError() == SignalStrategy::AUTHENTICATION_FAILED) {
83 auth_failed_callback_.Run();
84 } else {
85 ScheduleTryReconnect();
90 bool SignalingConnector::OnSignalStrategyIncomingStanza(
91 const buzz::XmlElement* stanza) {
92 return false;
95 void SignalingConnector::OnConnectionTypeChanged(
96 net::NetworkChangeNotifier::ConnectionType type) {
97 DCHECK(CalledOnValidThread());
98 if (type != net::NetworkChangeNotifier::CONNECTION_NONE &&
99 signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
100 LOG(INFO) << "Network state changed to online.";
101 ResetAndTryReconnect();
105 void SignalingConnector::OnIPAddressChanged() {
106 DCHECK(CalledOnValidThread());
107 if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
108 LOG(INFO) << "IP address has changed.";
109 ResetAndTryReconnect();
113 void SignalingConnector::OnGetTokensResponse(const std::string& user_email,
114 const std::string& access_token,
115 int expires_seconds) {
116 NOTREACHED();
119 void SignalingConnector::OnRefreshTokenResponse(
120 const std::string& access_token,
121 int expires_seconds) {
122 DCHECK(CalledOnValidThread());
123 DCHECK(oauth_credentials_.get());
124 LOG(INFO) << "Received OAuth token.";
126 oauth_access_token_ = access_token;
127 auth_token_expiry_time_ = base::Time::Now() +
128 base::TimeDelta::FromSeconds(expires_seconds) -
129 base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds);
131 gaia_oauth_client_->GetUserEmail(access_token, 1, this);
134 void SignalingConnector::OnGetUserEmailResponse(const std::string& user_email) {
135 DCHECK(CalledOnValidThread());
136 DCHECK(oauth_credentials_.get());
137 LOG(INFO) << "Received user info.";
139 if (user_email != oauth_credentials_->login) {
140 LOG(ERROR) << "OAuth token and email address do not refer to "
141 "the same account.";
142 auth_failed_callback_.Run();
143 return;
146 signal_strategy_->SetAuthInfo(oauth_credentials_->login,
147 oauth_access_token_, "oauth2");
148 refreshing_oauth_token_ = false;
150 // Now that we've refreshed the token and verified that it's for the correct
151 // user account, try to connect using the new token.
152 DCHECK_EQ(signal_strategy_->GetState(), SignalStrategy::DISCONNECTED);
153 signal_strategy_->Connect();
156 void SignalingConnector::OnOAuthError() {
157 DCHECK(CalledOnValidThread());
158 LOG(ERROR) << "OAuth: invalid credentials.";
159 refreshing_oauth_token_ = false;
160 reconnect_attempts_++;
161 auth_failed_callback_.Run();
164 void SignalingConnector::OnNetworkError(int response_code) {
165 DCHECK(CalledOnValidThread());
166 LOG(ERROR) << "Network error when trying to update OAuth token: "
167 << response_code;
168 refreshing_oauth_token_ = false;
169 reconnect_attempts_++;
170 ScheduleTryReconnect();
173 void SignalingConnector::ScheduleTryReconnect() {
174 DCHECK(CalledOnValidThread());
175 if (timer_.IsRunning() || net::NetworkChangeNotifier::IsOffline())
176 return;
177 int delay_s = std::min(1 << reconnect_attempts_,
178 kMaxReconnectDelaySeconds);
179 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(delay_s),
180 this, &SignalingConnector::TryReconnect);
183 void SignalingConnector::ResetAndTryReconnect() {
184 DCHECK(CalledOnValidThread());
185 signal_strategy_->Disconnect();
186 reconnect_attempts_ = 0;
187 timer_.Stop();
188 ScheduleTryReconnect();
191 void SignalingConnector::TryReconnect() {
192 DCHECK(CalledOnValidThread());
193 DCHECK(dns_blackhole_checker_.get());
195 // This will check if this machine is allowed to access the chromoting
196 // host talkgadget.
197 dns_blackhole_checker_->CheckForDnsBlackhole(
198 base::Bind(&SignalingConnector::OnDnsBlackholeCheckerDone,
199 base::Unretained(this)));
202 void SignalingConnector::OnDnsBlackholeCheckerDone(bool allow) {
203 DCHECK(CalledOnValidThread());
205 // Unable to access the host talkgadget. Don't allow the connection, but
206 // schedule a reconnect in case this is a transient problem rather than
207 // an outright block.
208 if (!allow) {
209 reconnect_attempts_++;
210 LOG(INFO) << "Talkgadget check failed. Scheduling reconnect. Attempt "
211 << reconnect_attempts_;
212 ScheduleTryReconnect();
213 return;
216 if (signal_strategy_->GetState() == SignalStrategy::DISCONNECTED) {
217 bool need_new_auth_token = oauth_credentials_.get() &&
218 (auth_token_expiry_time_.is_null() ||
219 base::Time::Now() >= auth_token_expiry_time_);
220 if (need_new_auth_token) {
221 RefreshOAuthToken();
222 } else {
223 LOG(INFO) << "Attempting to connect signaling.";
224 signal_strategy_->Connect();
229 void SignalingConnector::RefreshOAuthToken() {
230 DCHECK(CalledOnValidThread());
231 LOG(INFO) << "Refreshing OAuth token.";
232 DCHECK(!refreshing_oauth_token_);
234 // Service accounts use different API keys, as they use the client app flow.
235 google_apis::OAuth2Client oauth2_client;
236 if (oauth_credentials_->is_service_account) {
237 oauth2_client = google_apis::CLIENT_REMOTING_HOST;
238 } else {
239 oauth2_client = google_apis::CLIENT_REMOTING;
242 gaia::OAuthClientInfo client_info = {
243 google_apis::GetOAuth2ClientID(oauth2_client),
244 google_apis::GetOAuth2ClientSecret(oauth2_client),
245 // Redirect URL is only used when getting tokens from auth code. It
246 // is not required when getting access tokens.
250 refreshing_oauth_token_ = true;
251 std::vector<std::string> empty_scope_list; // (Use scope from refresh token.)
252 gaia_oauth_client_->RefreshToken(
253 client_info, oauth_credentials_->refresh_token, empty_scope_list,
254 1, this);
257 } // namespace remoting