local_discovery_ui_handler.cc revision 3551c9c881056c480085172ff9840cab31610854
1// Copyright 2013 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 "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
6
7#include "base/bind.h"
8#include "base/strings/stringprintf.h"
9#include "base/values.h"
10#include "chrome/browser/local_discovery/privet_device_lister_impl.h"
11#include "chrome/browser/local_discovery/privet_http_impl.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/signin/profile_oauth2_token_service.h"
14#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
15#include "chrome/browser/signin/signin_manager.h"
16#include "chrome/browser/signin/signin_manager_base.h"
17#include "chrome/browser/signin/signin_manager_factory.h"
18#include "content/public/browser/web_ui.h"
19#include "net/base/host_port_pair.h"
20#include "net/base/net_util.h"
21#include "net/http/http_status_code.h"
22
23namespace local_discovery {
24
25namespace {
26// TODO(noamsml): This is a temporary shim until automated_url is in the
27// response.
28const char kPrivetAutomatedClaimURLFormat[] = "%s/confirm?token=%s";
29
30LocalDiscoveryUIHandler::Factory* g_factory = NULL;
31}  // namespace
32
33LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() {
34}
35
36LocalDiscoveryUIHandler::LocalDiscoveryUIHandler(
37    scoped_ptr<PrivetDeviceLister> privet_lister) {
38  privet_lister.swap(privet_lister_);
39}
40
41LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
42  if (service_discovery_client_.get()) {
43    service_discovery_client_ = NULL;
44    ServiceDiscoveryHostClientFactory::ReleaseClient();
45  }
46}
47
48// static
49LocalDiscoveryUIHandler* LocalDiscoveryUIHandler::Create() {
50  if (g_factory) return g_factory->CreateLocalDiscoveryUIHandler();
51  return new LocalDiscoveryUIHandler();
52}
53
54// static
55void LocalDiscoveryUIHandler::SetFactory(Factory* factory) {
56  g_factory = factory;
57}
58
59void LocalDiscoveryUIHandler::RegisterMessages() {
60  web_ui()->RegisterMessageCallback("start", base::Bind(
61      &LocalDiscoveryUIHandler::HandleStart,
62      base::Unretained(this)));
63  web_ui()->RegisterMessageCallback("registerDevice", base::Bind(
64      &LocalDiscoveryUIHandler::HandleRegisterDevice,
65      base::Unretained(this)));
66  web_ui()->RegisterMessageCallback("info", base::Bind(
67      &LocalDiscoveryUIHandler::HandleInfoRequested,
68      base::Unretained(this)));
69}
70
71void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) {
72  // If privet_lister_ is already set, it is a mock used for tests or the result
73  // of a reload.
74  if (!privet_lister_) {
75    service_discovery_client_ = ServiceDiscoveryHostClientFactory::GetClient();
76    privet_lister_.reset(new PrivetDeviceListerImpl(
77        service_discovery_client_.get(), this));
78    privet_http_factory_.reset(new PrivetHTTPAsynchronousFactoryImpl(
79        service_discovery_client_.get(),
80        Profile::FromWebUI(web_ui())->GetRequestContext()));
81  }
82
83  privet_lister_->Start();
84  privet_lister_->DiscoverNewDevices(false);
85}
86
87void LocalDiscoveryUIHandler::HandleRegisterDevice(
88    const base::ListValue* args) {
89  std::string device_name;
90
91  bool rv = args->GetString(0, &device_name);
92  DCHECK(rv);
93
94  privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
95      device_name,
96      device_descriptions_[device_name].address,
97      base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
98                 base::Unretained(this)));
99  privet_resolution_->Start();
100}
101
102void LocalDiscoveryUIHandler::HandleInfoRequested(const base::ListValue* args) {
103  std::string device_name;
104  args->GetString(0, &device_name);
105
106  privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
107      device_name,
108      device_descriptions_[device_name].address,
109      base::Bind(&LocalDiscoveryUIHandler::StartInfoHTTP,
110                 base::Unretained(this)));
111  privet_resolution_->Start();
112}
113
114void LocalDiscoveryUIHandler::StartRegisterHTTP(
115    scoped_ptr<PrivetHTTPClient> http_client) {
116  current_http_client_.swap(http_client);
117
118  if (!current_http_client_) {
119    LogRegisterErrorToWeb("Resolution failed");
120    return;
121  }
122
123  Profile* profile = Profile::FromWebUI(web_ui());
124  SigninManagerBase* signin_manager =
125      SigninManagerFactory::GetForProfileIfExists(profile);
126
127  if (!signin_manager) {
128    LogRegisterErrorToWeb("You must be signed in");
129    return;
130  }
131
132  std::string username = signin_manager->GetAuthenticatedUsername();
133
134  current_register_operation_ =
135      current_http_client_->CreateRegisterOperation(username, this);
136  current_register_operation_->Start();
137}
138
139void LocalDiscoveryUIHandler::StartInfoHTTP(
140    scoped_ptr<PrivetHTTPClient> http_client) {
141  current_http_client_.swap(http_client);
142  if (!current_http_client_) {
143    LogRegisterErrorToWeb("Resolution failed");
144    return;
145  }
146
147  current_info_operation_ = current_http_client_->CreateInfoOperation(this);
148  current_info_operation_->Start();
149}
150
151void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
152    PrivetRegisterOperation* operation,
153    const std::string& token,
154    const GURL& url) {
155  if (device_descriptions_.count(current_http_client_->GetName()) == 0) {
156    LogRegisterErrorToWeb("Device no longer exists");
157    return;
158  }
159
160  GURL automated_claim_url(base::StringPrintf(
161      kPrivetAutomatedClaimURLFormat,
162      device_descriptions_[current_http_client_->GetName()].url.c_str(),
163      token.c_str()));
164
165  Profile* profile = Profile::FromWebUI(web_ui());
166
167  OAuth2TokenService* token_service =
168      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
169
170  if (!token_service) {
171    LogRegisterErrorToWeb("Could not get token service");
172    return;
173  }
174
175  confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow(
176      profile->GetRequestContext(),
177      token_service,
178      automated_claim_url,
179      base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
180                 base::Unretained(this))));
181
182  confirm_api_call_flow_->Start();
183}
184
185void LocalDiscoveryUIHandler::OnPrivetRegisterError(
186    PrivetRegisterOperation* operation,
187    const std::string& action,
188    PrivetRegisterOperation::FailureReason reason,
189    int printer_http_code,
190    const DictionaryValue* json) {
191  // TODO(noamsml): Add detailed error message.
192  LogRegisterErrorToWeb("Registration error");
193}
194
195void LocalDiscoveryUIHandler::OnPrivetRegisterDone(
196    PrivetRegisterOperation* operation,
197    const std::string& device_id) {
198  current_register_operation_.reset();
199  current_http_client_.reset();
200
201  LogRegisterDoneToWeb(device_id);
202}
203
204void LocalDiscoveryUIHandler::OnConfirmDone(
205    PrivetConfirmApiCallFlow::Status status) {
206  if (status == PrivetConfirmApiCallFlow::SUCCESS) {
207    DLOG(INFO) << "Confirm success.";
208    confirm_api_call_flow_.reset();
209    current_register_operation_->CompleteRegistration();
210  } else {
211    // TODO(noamsml): Add detailed error message.
212    LogRegisterErrorToWeb("Confirm error");
213  }
214}
215
216void LocalDiscoveryUIHandler::DeviceChanged(
217    bool added,
218    const std::string& name,
219    const DeviceDescription& description) {
220  device_descriptions_[name] = description;
221
222  base::StringValue service_name(name);
223  base::DictionaryValue info;
224  info.SetString("domain", description.address.host());
225  info.SetInteger("port", description.address.port());
226  std::string ip_addr_string;
227  if (!description.ip_address.empty())
228    ip_addr_string = net::IPAddressToString(description.ip_address);
229
230  info.SetString("ip", ip_addr_string);
231  info.SetString("lastSeen", "unknown");
232  info.SetBoolean("registered", !description.id.empty());
233
234  web_ui()->CallJavascriptFunction("local_discovery.onServiceUpdate",
235                                   service_name, info);
236}
237
238void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) {
239  device_descriptions_.erase(name);
240  scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
241  base::StringValue name_value(name);
242
243  web_ui()->CallJavascriptFunction("local_discovery.onServiceUpdate",
244                                   name_value, *null_value);
245}
246
247void LocalDiscoveryUIHandler::LogRegisterErrorToWeb(const std::string& error) {
248  base::StringValue error_value(error);
249  web_ui()->CallJavascriptFunction("local_discovery.registrationFailed",
250                                   error_value);
251  DLOG(ERROR) << error;
252}
253
254void LocalDiscoveryUIHandler::LogRegisterDoneToWeb(const std::string& id) {
255  base::StringValue id_value(id);
256  web_ui()->CallJavascriptFunction("local_discovery.registrationSuccess",
257                                   id_value);
258  DLOG(INFO) << "Registered " << id;
259}
260
261void LocalDiscoveryUIHandler::LogInfoErrorToWeb(const std::string& error) {
262  base::StringValue error_value(error);
263  web_ui()->CallJavascriptFunction("local_discovery.infoFailed", error_value);
264  LOG(ERROR) << error;
265}
266
267void LocalDiscoveryUIHandler::OnPrivetInfoDone(
268    PrivetInfoOperation* operation,
269    int http_code,
270    const base::DictionaryValue* json_value) {
271  if (http_code != net::HTTP_OK || !json_value) {
272    LogInfoErrorToWeb(base::StringPrintf("HTTP error %d", http_code));
273    return;
274  }
275
276  web_ui()->CallJavascriptFunction("local_discovery.renderInfo", *json_value);
277}
278
279}  // namespace local_discovery
280