| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "buffet/privet/privet_manager.h" |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| |
| #include <base/bind.h> |
| #include <base/command_line.h> |
| #include <base/json/json_reader.h> |
| #include <base/memory/weak_ptr.h> |
| #include <base/scoped_observer.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/values.h> |
| #include <chromeos/daemons/dbus_daemon.h> |
| #include <chromeos/flag_helper.h> |
| #include <chromeos/http/http_request.h> |
| #include <chromeos/key_value_store.h> |
| #include <chromeos/mime_utils.h> |
| #include <chromeos/strings/string_utils.h> |
| #include <chromeos/syslog_logging.h> |
| #include <libwebserv/protocol_handler.h> |
| #include <libwebserv/request.h> |
| #include <libwebserv/response.h> |
| #include <libwebserv/server.h> |
| |
| #include "buffet/dbus_constants.h" |
| #include "buffet/privet/ap_manager_client.h" |
| #include "buffet/privet/cloud_delegate.h" |
| #include "buffet/privet/constants.h" |
| #include "buffet/privet/daemon_state.h" |
| #include "buffet/privet/device_delegate.h" |
| #include "buffet/privet/peerd_client.h" |
| #include "buffet/privet/privet_handler.h" |
| #include "buffet/privet/privetd_conf_parser.h" |
| #include "buffet/privet/security_manager.h" |
| #include "buffet/privet/shill_client.h" |
| #include "buffet/privet/wifi_bootstrap_manager.h" |
| |
| namespace privetd { |
| |
| namespace { |
| |
| using chromeos::dbus_utils::AsyncEventSequencer; |
| using libwebserv::ProtocolHandler; |
| using libwebserv::Request; |
| using libwebserv::Response; |
| |
| const char kDefaultConfigFilePath[] = "/etc/privetd/privetd.conf"; |
| const char kDefaultStateFilePath[] = "/var/lib/privetd/privetd.state"; |
| |
| std::string GetFirstHeader(const Request& request, const std::string& name) { |
| std::vector<std::string> headers = request.GetHeader(name); |
| return headers.empty() ? std::string() : headers.front(); |
| } |
| |
| } // namespace |
| |
| Manager::Manager() { |
| } |
| |
| Manager::~Manager() { |
| } |
| |
| void Manager::Start(const Options& options, |
| const scoped_refptr<dbus::Bus>& bus, |
| buffet::DeviceRegistrationInfo* device, |
| buffet::CommandManager* command_manager, |
| buffet::StateManager* state_manager, |
| AsyncEventSequencer* sequencer) { |
| disable_security_ = options.disable_security; |
| |
| // TODO(vitalybuka): switch to BuffetConfig. |
| base::FilePath config_path{privetd::kDefaultConfigFilePath}; |
| base::FilePath state_path{privetd::kDefaultStateFilePath}; |
| |
| state_store_.reset(new DaemonState(state_path)); |
| parser_.reset(new PrivetdConfigParser); |
| |
| chromeos::KeyValueStore config_store; |
| if (!config_store.Load(config_path)) { |
| LOG(ERROR) << "Failed to read privetd config file from " |
| << config_path.value(); |
| } else { |
| CHECK(parser_->Parse(config_store)) << "Failed to read configuration file."; |
| } |
| state_store_->Init(); |
| |
| std::set<std::string> device_whitelist{options.device_whitelist}; |
| // This state store key doesn't exist naturally, but developers |
| // sometime put it in their state store to cause the device to bring |
| // up WiFi bootstrapping while being connected to an ethernet interface. |
| std::string test_device_whitelist; |
| if (device_whitelist.empty() && |
| state_store_->GetString(kWiFiBootstrapInterfaces, |
| &test_device_whitelist)) { |
| auto interfaces = |
| chromeos::string_utils::Split(test_device_whitelist, ",", true, true); |
| device_whitelist.insert(interfaces.begin(), interfaces.end()); |
| } |
| device_ = DeviceDelegate::CreateDefault(); |
| cloud_ = CloudDelegate::CreateDefault( |
| parser_->gcd_bootstrap_mode() != GcdBootstrapMode::kDisabled, device, |
| command_manager, state_manager); |
| cloud_observer_.Add(cloud_.get()); |
| security_.reset(new SecurityManager(parser_->pairing_modes(), |
| parser_->embedded_code_path(), |
| disable_security_)); |
| shill_client_.reset(new ShillClient( |
| bus, device_whitelist.empty() ? parser_->automatic_wifi_interfaces() |
| : device_whitelist)); |
| shill_client_->RegisterConnectivityListener( |
| base::Bind(&Manager::OnConnectivityChanged, base::Unretained(this))); |
| ap_manager_client_.reset(new ApManagerClient(bus)); |
| |
| if (parser_->wifi_bootstrap_mode() != WiFiBootstrapMode::kDisabled) { |
| VLOG(1) << "Enabling WiFi bootstrapping."; |
| wifi_bootstrap_manager_.reset(new WifiBootstrapManager( |
| state_store_.get(), shill_client_.get(), ap_manager_client_.get(), |
| cloud_.get(), parser_->connect_timeout_seconds(), |
| parser_->bootstrap_timeout_seconds(), |
| parser_->monitor_timeout_seconds())); |
| wifi_bootstrap_manager_->Init(); |
| } |
| |
| peerd_client_.reset(new PeerdClient(bus, device_.get(), cloud_.get(), |
| wifi_bootstrap_manager_.get())); |
| |
| privet_handler_.reset( |
| new PrivetHandler(cloud_.get(), device_.get(), security_.get(), |
| wifi_bootstrap_manager_.get(), peerd_client_.get())); |
| |
| web_server_.reset(new libwebserv::Server); |
| web_server_->OnProtocolHandlerConnected(base::Bind( |
| &Manager::OnProtocolHandlerConnected, weak_ptr_factory_.GetWeakPtr())); |
| web_server_->OnProtocolHandlerDisconnected(base::Bind( |
| &Manager::OnProtocolHandlerDisconnected, weak_ptr_factory_.GetWeakPtr())); |
| |
| web_server_->Connect(bus, buffet::dbus_constants::kServiceName, |
| sequencer->GetHandler("Server::Connect failed.", true), |
| base::Bind(&base::DoNothing), |
| base::Bind(&base::DoNothing)); |
| |
| web_server_->GetDefaultHttpHandler()->AddHandlerCallback( |
| "/privet/", "", |
| base::Bind(&Manager::PrivetRequestHandler, base::Unretained(this))); |
| web_server_->GetDefaultHttpsHandler()->AddHandlerCallback( |
| "/privet/", "", |
| base::Bind(&Manager::PrivetRequestHandler, base::Unretained(this))); |
| if (options.enable_ping) { |
| web_server_->GetDefaultHttpHandler()->AddHandlerCallback( |
| "/privet/ping", chromeos::http::request_type::kGet, |
| base::Bind(&Manager::HelloWorldHandler, base::Unretained(this))); |
| web_server_->GetDefaultHttpsHandler()->AddHandlerCallback( |
| "/privet/ping", chromeos::http::request_type::kGet, |
| base::Bind(&Manager::HelloWorldHandler, base::Unretained(this))); |
| } |
| } |
| |
| void Manager::OnShutdown() { |
| web_server_->Disconnect(); |
| } |
| |
| void Manager::OnDeviceInfoChanged() { |
| OnChanged(); |
| } |
| |
| void Manager::PrivetRequestHandler(std::unique_ptr<Request> request, |
| std::unique_ptr<Response> response) { |
| std::string auth_header = |
| GetFirstHeader(*request, chromeos::http::request_header::kAuthorization); |
| if (auth_header.empty() && disable_security_) |
| auth_header = "Privet anonymous"; |
| std::string data(request->GetData().begin(), request->GetData().end()); |
| VLOG(3) << "Input: " << data; |
| |
| base::DictionaryValue empty; |
| std::unique_ptr<base::Value> value; |
| const base::DictionaryValue* dictionary = nullptr; |
| |
| if (data.empty()) { |
| dictionary = ∅ |
| } else { |
| std::string content_type = chromeos::mime::RemoveParameters( |
| GetFirstHeader(*request, chromeos::http::request_header::kContentType)); |
| if (content_type == chromeos::mime::application::kJson) { |
| value.reset(base::JSONReader::Read(data)); |
| if (value) |
| value->GetAsDictionary(&dictionary); |
| } |
| } |
| |
| privet_handler_->HandleRequest( |
| request->GetPath(), auth_header, dictionary, |
| base::Bind(&Manager::PrivetResponseHandler, base::Unretained(this), |
| base::Passed(&response))); |
| } |
| |
| void Manager::PrivetResponseHandler(std::unique_ptr<Response> response, |
| int status, |
| const base::DictionaryValue& output) { |
| VLOG(3) << "status: " << status << ", Output: " << output; |
| response->ReplyWithJson(status, &output); |
| } |
| |
| void Manager::HelloWorldHandler(std::unique_ptr<Request> request, |
| std::unique_ptr<Response> response) { |
| response->ReplyWithText(chromeos::http::status_code::Ok, "Hello, world!", |
| chromeos::mime::text::kPlain); |
| } |
| |
| void Manager::OnChanged() { |
| if (peerd_client_) |
| peerd_client_->Update(); |
| } |
| |
| void Manager::OnConnectivityChanged(bool online) { |
| OnChanged(); |
| } |
| |
| void Manager::OnProtocolHandlerConnected(ProtocolHandler* protocol_handler) { |
| if (protocol_handler->GetName() == ProtocolHandler::kHttp) { |
| device_->SetHttpPort(*protocol_handler->GetPorts().begin()); |
| if (peerd_client_) |
| peerd_client_->Update(); |
| } else if (protocol_handler->GetName() == ProtocolHandler::kHttps) { |
| device_->SetHttpsPort(*protocol_handler->GetPorts().begin()); |
| security_->SetCertificateFingerprint( |
| protocol_handler->GetCertificateFingerprint()); |
| } |
| } |
| |
| void Manager::OnProtocolHandlerDisconnected(ProtocolHandler* protocol_handler) { |
| if (protocol_handler->GetName() == ProtocolHandler::kHttp) { |
| device_->SetHttpPort(0); |
| if (peerd_client_) |
| peerd_client_->Update(); |
| } else if (protocol_handler->GetName() == ProtocolHandler::kHttps) { |
| device_->SetHttpsPort(0); |
| security_->SetCertificateFingerprint({}); |
| } |
| } |
| |
| } // namespace privetd |