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/host/setup/host_starter.h"
6
7#include "base/bind.h"
8#include "base/guid.h"
9#include "base/location.h"
10#include "base/thread_task_runner_handle.h"
11#include "base/values.h"
12#include "google_apis/google_api_keys.h"
13#include "remoting/host/pin_hash.h"
14#include "remoting/host/setup/oauth_helper.h"
15
16namespace {
17const int kMaxGetTokensRetries = 3;
18}  // namespace
19
20namespace remoting {
21
22HostStarter::HostStarter(
23    scoped_ptr<gaia::GaiaOAuthClient> oauth_client,
24    scoped_ptr<remoting::ServiceClient> service_client,
25    scoped_refptr<remoting::DaemonController> daemon_controller)
26    : oauth_client_(oauth_client.Pass()),
27      service_client_(service_client.Pass()),
28      daemon_controller_(daemon_controller),
29      consent_to_data_collection_(false),
30      unregistering_host_(false),
31      weak_ptr_factory_(this) {
32  weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
33  main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
34}
35
36HostStarter::~HostStarter() {
37}
38
39scoped_ptr<HostStarter> HostStarter::Create(
40    const std::string& chromoting_hosts_url,
41    net::URLRequestContextGetter* url_request_context_getter) {
42  scoped_ptr<gaia::GaiaOAuthClient> oauth_client(
43      new gaia::GaiaOAuthClient(url_request_context_getter));
44  scoped_ptr<remoting::ServiceClient> service_client(
45      new remoting::ServiceClient(
46          chromoting_hosts_url, url_request_context_getter));
47  scoped_refptr<remoting::DaemonController> daemon_controller(
48      remoting::DaemonController::Create());
49  return scoped_ptr<HostStarter>(
50      new HostStarter(oauth_client.Pass(), service_client.Pass(),
51                      daemon_controller));
52}
53
54void HostStarter::StartHost(
55    const std::string& host_name,
56    const std::string& host_pin,
57    bool consent_to_data_collection,
58    const std::string& auth_code,
59    const std::string& redirect_url,
60    CompletionCallback on_done) {
61  DCHECK(main_task_runner_->BelongsToCurrentThread());
62  DCHECK(on_done_.is_null());
63
64  host_name_ = host_name;
65  host_pin_ = host_pin;
66  consent_to_data_collection_ = consent_to_data_collection;
67  on_done_ = on_done;
68  oauth_client_info_.client_id =
69      google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING);
70  oauth_client_info_.client_secret =
71      google_apis::GetOAuth2ClientSecret(google_apis::CLIENT_REMOTING);
72  oauth_client_info_.redirect_uri = redirect_url;
73  // Map the authorization code to refresh and access tokens.
74  oauth_client_->GetTokensFromAuthCode(oauth_client_info_, auth_code,
75                                       kMaxGetTokensRetries, this);
76}
77
78void HostStarter::OnGetTokensResponse(
79    const std::string& refresh_token,
80    const std::string& access_token,
81    int expires_in_seconds) {
82  if (!main_task_runner_->BelongsToCurrentThread()) {
83    main_task_runner_->PostTask(FROM_HERE, base::Bind(
84        &HostStarter::OnGetTokensResponse, weak_ptr_,
85        refresh_token, access_token, expires_in_seconds));
86    return;
87  }
88  refresh_token_ = refresh_token;
89  access_token_ = access_token;
90  // Get the email corresponding to the access token.
91  oauth_client_->GetUserEmail(access_token_, 1, this);
92}
93
94void HostStarter::OnRefreshTokenResponse(
95    const std::string& access_token,
96    int expires_in_seconds) {
97  // We never request a refresh token, so this call is not expected.
98  NOTREACHED();
99}
100
101// This function is called twice: once with the host owner credentials, and once
102// with the service account credentials.
103void HostStarter::OnGetUserEmailResponse(const std::string& user_email) {
104  if (!main_task_runner_->BelongsToCurrentThread()) {
105    main_task_runner_->PostTask(FROM_HERE, base::Bind(
106        &HostStarter::OnGetUserEmailResponse, weak_ptr_, user_email));
107    return;
108  }
109
110  if (host_owner_.empty()) {
111    // This is the first callback, with the host owner credentials. Store the
112    // owner's email, and register the host.
113    host_owner_ = user_email;
114    host_id_ = base::GenerateGUID();
115    key_pair_ = RsaKeyPair::Generate();
116
117    std::string host_client_id;
118    host_client_id = google_apis::GetOAuth2ClientID(
119        google_apis::CLIENT_REMOTING_HOST);
120
121    service_client_->RegisterHost(
122        host_id_, host_name_, key_pair_->GetPublicKey(), host_client_id,
123        access_token_, this);
124  } else {
125    // This is the second callback, with the service account credentials.
126    // This email is the service account's email, used to login to XMPP.
127    xmpp_login_ = user_email;
128    StartHostProcess();
129  }
130}
131
132void HostStarter::OnHostRegistered(const std::string& authorization_code) {
133  if (!main_task_runner_->BelongsToCurrentThread()) {
134    main_task_runner_->PostTask(FROM_HERE, base::Bind(
135        &HostStarter::OnHostRegistered, weak_ptr_, authorization_code));
136    return;
137  }
138
139  if (authorization_code.empty()) {
140    // No service account code, start the host with the owner's credentials.
141    xmpp_login_ = host_owner_;
142    StartHostProcess();
143    return;
144  }
145
146  // Received a service account authorization code, update oauth_client_info_
147  // to use the service account client keys, and get service account tokens.
148  oauth_client_info_.client_id =
149      google_apis::GetOAuth2ClientID(
150          google_apis::CLIENT_REMOTING_HOST);
151  oauth_client_info_.client_secret =
152      google_apis::GetOAuth2ClientSecret(
153          google_apis::CLIENT_REMOTING_HOST);
154  oauth_client_info_.redirect_uri = "oob";
155  oauth_client_->GetTokensFromAuthCode(
156      oauth_client_info_, authorization_code, kMaxGetTokensRetries, this);
157}
158
159void HostStarter::StartHostProcess() {
160  // Start the host.
161  std::string host_secret_hash = remoting::MakeHostPinHash(host_id_, host_pin_);
162  scoped_ptr<base::DictionaryValue> config(new base::DictionaryValue());
163  if (host_owner_ != xmpp_login_) {
164    config->SetString("host_owner", host_owner_);
165  }
166  config->SetString("xmpp_login", xmpp_login_);
167  config->SetString("oauth_refresh_token", refresh_token_);
168  config->SetString("host_id", host_id_);
169  config->SetString("host_name", host_name_);
170  config->SetString("private_key", key_pair_->ToString());
171  config->SetString("host_secret_hash", host_secret_hash);
172  daemon_controller_->SetConfigAndStart(
173      config.Pass(), consent_to_data_collection_,
174      base::Bind(&HostStarter::OnHostStarted, base::Unretained(this)));
175}
176
177void HostStarter::OnHostStarted(DaemonController::AsyncResult result) {
178  if (!main_task_runner_->BelongsToCurrentThread()) {
179    main_task_runner_->PostTask(FROM_HERE, base::Bind(
180        &HostStarter::OnHostStarted, weak_ptr_, result));
181    return;
182  }
183  if (result != DaemonController::RESULT_OK) {
184    unregistering_host_ = true;
185    service_client_->UnregisterHost(host_id_, access_token_, this);
186    return;
187  }
188  CompletionCallback cb = on_done_;
189  on_done_.Reset();
190  cb.Run(START_COMPLETE);
191}
192
193void HostStarter::OnOAuthError() {
194  if (!main_task_runner_->BelongsToCurrentThread()) {
195    main_task_runner_->PostTask(FROM_HERE, base::Bind(
196        &HostStarter::OnOAuthError, weak_ptr_));
197    return;
198  }
199  CompletionCallback cb = on_done_;
200  on_done_.Reset();
201  if (unregistering_host_) {
202    LOG(ERROR) << "OAuth error occurred when unregistering host.";
203    cb.Run(START_ERROR);
204  } else {
205    cb.Run(OAUTH_ERROR);
206  }
207}
208
209void HostStarter::OnNetworkError(int response_code) {
210  if (!main_task_runner_->BelongsToCurrentThread()) {
211    main_task_runner_->PostTask(FROM_HERE, base::Bind(
212        &HostStarter::OnNetworkError, weak_ptr_, response_code));
213    return;
214  }
215  CompletionCallback cb = on_done_;
216  on_done_.Reset();
217  if (unregistering_host_) {
218    LOG(ERROR) << "Network error occurred when unregistering host.";
219    cb.Run(START_ERROR);
220  } else {
221    cb.Run(NETWORK_ERROR);
222  }
223}
224
225void HostStarter::OnHostUnregistered() {
226  if (!main_task_runner_->BelongsToCurrentThread()) {
227    main_task_runner_->PostTask(FROM_HERE, base::Bind(
228        &HostStarter::OnHostUnregistered, weak_ptr_));
229    return;
230  }
231  CompletionCallback cb = on_done_;
232  on_done_.Reset();
233  cb.Run(START_ERROR);
234}
235
236}  // namespace remoting
237