From 5a1f600e9d7d26c36b3e22ff0dc0ae9e3b2425fc Mon Sep 17 00:00:00 2001 From: Paul Westbrook Date: Sun, 1 Nov 2015 15:29:33 -0800 Subject: Remove the unneeded libweave directory Change-Id: I30fd8c5626cf83da6415ffa14a2019ef43be9916 Reviewed-on: https://weave-review.googlesource.com/1450 Reviewed-by: Paul Westbrook --- src/privet/privet_handler.cc | 785 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 785 insertions(+) create mode 100644 src/privet/privet_handler.cc (limited to 'src/privet/privet_handler.cc') diff --git a/src/privet/privet_handler.cc b/src/privet/privet_handler.cc new file mode 100644 index 0000000..d959f66 --- /dev/null +++ b/src/privet/privet_handler.cc @@ -0,0 +1,785 @@ +// Copyright 2015 The Weave 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 "src/privet/privet_handler.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/http_constants.h" +#include "src/privet/cloud_delegate.h" +#include "src/privet/constants.h" +#include "src/privet/device_delegate.h" +#include "src/privet/device_ui_kind.h" +#include "src/privet/security_delegate.h" +#include "src/privet/wifi_delegate.h" +#include "src/string_utils.h" +#include "src/utils.h" + +namespace weave { +namespace privet { + +namespace { + +const char kInfoVersionKey[] = "version"; +const char kInfoVersionValue[] = "3.0"; + +const char kNameKey[] = "name"; +const char kDescrptionKey[] = "description"; +const char kLocationKey[] = "location"; + +const char kGcdKey[] = "gcd"; +const char kWifiKey[] = "wifi"; +const char kStatusKey[] = "status"; +const char kErrorKey[] = "error"; +const char kCryptoKey[] = "crypto"; +const char kStatusErrorValue[] = "error"; + +const char kInfoIdKey[] = "id"; +const char kInfoServicesKey[] = "services"; + +const char kInfoEndpointsKey[] = "endpoints"; +const char kInfoEndpointsHttpPortKey[] = "httpPort"; +const char kInfoEndpointsHttpUpdatePortKey[] = "httpUpdatesPort"; +const char kInfoEndpointsHttpsPortKey[] = "httpsPort"; +const char kInfoEndpointsHttpsUpdatePortKey[] = "httpsUpdatesPort"; + +const char kInfoModelIdKey[] = "modelManifestId"; +const char kInfoModelManifestKey[] = "basicModelManifest"; +const char kInfoManifestUiDeviceKind[] = "uiDeviceKind"; +const char kInfoManifestOemName[] = "oemName"; +const char kInfoManifestModelName[] = "modelName"; + +const char kInfoAuthenticationKey[] = "authentication"; + +const char kInfoAuthAnonymousMaxScopeKey[] = "anonymousMaxScope"; + +const char kInfoWifiCapabilitiesKey[] = "capabilities"; +const char kInfoWifiSsidKey[] = "ssid"; +const char kInfoWifiHostedSsidKey[] = "hostedSsid"; + +const char kInfoUptimeKey[] = "uptime"; + +const char kPairingKey[] = "pairing"; +const char kPairingSessionIdKey[] = "sessionId"; +const char kPairingDeviceCommitmentKey[] = "deviceCommitment"; +const char kPairingClientCommitmentKey[] = "clientCommitment"; +const char kPairingFingerprintKey[] = "certFingerprint"; +const char kPairingSignatureKey[] = "certSignature"; + +const char kAuthTypeAnonymousValue[] = "anonymous"; +const char kAuthTypePairingValue[] = "pairing"; + +const char kAuthModeKey[] = "mode"; +const char kAuthCodeKey[] = "authCode"; +const char kAuthRequestedScopeKey[] = "requestedScope"; +const char kAuthScopeAutoValue[] = "auto"; + +const char kAuthAccessTokenKey[] = "accessToken"; +const char kAuthTokenTypeKey[] = "tokenType"; +const char kAuthExpiresInKey[] = "expiresIn"; +const char kAuthScopeKey[] = "scope"; + +const char kAuthorizationHeaderPrefix[] = "Privet"; + +const char kErrorDebugInfoKey[] = "debugInfo"; + +const char kSetupStartSsidKey[] = "ssid"; +const char kSetupStartPassKey[] = "passphrase"; +const char kSetupStartTicketIdKey[] = "ticketId"; +const char kSetupStartUserKey[] = "user"; + +const char kFingerprintKey[] = "fingerprint"; +const char kStateKey[] = "state"; +const char kCommandsKey[] = "commands"; +const char kCommandsIdKey[] = "id"; + +const char kInvalidParamValueFormat[] = "Invalid parameter: '%s'='%s'"; + +const int kAccessTokenExpirationSeconds = 3600; + +// Threshold to reduce probability of expiration because of clock difference +// between device and client. Value is just a guess. +const int kAccessTokenExpirationThresholdSeconds = 300; + +template +std::unique_ptr ToValue(const Container& list) { + std::unique_ptr value_list(new base::ListValue()); + for (const std::string& val : list) + value_list->AppendString(val); + return value_list; +} + +struct { + const char* const reason; + int code; +} kReasonToCode[] = { + {errors::kInvalidClientCommitment, http::kForbidden}, + {errors::kInvalidFormat, http::kBadRequest}, + {errors::kMissingAuthorization, http::kDenied}, + {errors::kInvalidAuthorization, http::kDenied}, + {errors::kInvalidAuthorizationScope, http::kForbidden}, + {errors::kAuthorizationExpired, http::kForbidden}, + {errors::kCommitmentMismatch, http::kForbidden}, + {errors::kUnknownSession, http::kNotFound}, + {errors::kInvalidAuthCode, http::kForbidden}, + {errors::kInvalidAuthMode, http::kBadRequest}, + {errors::kInvalidRequestedScope, http::kBadRequest}, + {errors::kAccessDenied, http::kForbidden}, + {errors::kInvalidParams, http::kBadRequest}, + {errors::kSetupUnavailable, http::kBadRequest}, + {errors::kDeviceBusy, http::kServiceUnavailable}, + {errors::kInvalidState, http::kInternalServerError}, + {errors::kNotFound, http::kNotFound}, + {errors::kNotImplemented, http::kNotSupported}, +}; + +AuthScope AuthScopeFromString(const std::string& scope, AuthScope auto_scope) { + if (scope == kAuthScopeAutoValue) + return auto_scope; + AuthScope scope_id = AuthScope::kNone; + StringToEnum(scope, &scope_id); + return scope_id; +} + +std::string GetAuthTokenFromAuthHeader(const std::string& auth_header) { + return SplitAtFirst(auth_header, " ", true).second; +} + +// Creates JSON similar to GCD server error format. +std::unique_ptr ErrorToJson(const Error& error) { + std::unique_ptr output{ErrorInfoToJson(error)}; + + // Optional debug information. + std::unique_ptr errors{new base::ListValue}; + for (const Error* it = &error; it; it = it->GetInnerError()) { + std::unique_ptr inner{ErrorInfoToJson(*it)}; + tracked_objects::Location location{it->GetLocation().function_name.c_str(), + it->GetLocation().file_name.c_str(), + it->GetLocation().line_number, nullptr}; + inner->SetString(kErrorDebugInfoKey, location.ToString()); + errors->Append(inner.release()); + } + output->Set(kErrorDebugInfoKey, errors.release()); + return output; +} + +template +void SetStateProperties(const T& state, base::DictionaryValue* parent) { + if (!state.error()) { + parent->SetString(kStatusKey, EnumToString(state.status())); + return; + } + parent->SetString(kStatusKey, kStatusErrorValue); + parent->Set(kErrorKey, ErrorToJson(*state.error()).release()); +} + +void ReturnError(const Error& error, + const PrivetHandler::RequestCallback& callback) { + int code = http::kInternalServerError; + for (const auto& it : kReasonToCode) { + if (error.HasError(errors::kDomain, it.reason)) { + code = it.code; + break; + } + } + std::unique_ptr output{new base::DictionaryValue}; + output->Set(kErrorKey, ErrorToJson(error).release()); + callback.Run(code, *output); +} + +void OnCommandRequestSucceeded(const PrivetHandler::RequestCallback& callback, + const base::DictionaryValue& output, + ErrorPtr error) { + if (!error) + return callback.Run(http::kOk, output); + + if (error->HasError("gcd", "unknown_command")) { + Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kNotFound, + "Unknown command ID"); + return ReturnError(*error, callback); + } + if (error->HasError("gcd", "access_denied")) { + Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kAccessDenied, + error->GetMessage()); + return ReturnError(*error, callback); + } + return ReturnError(*error, callback); +} + +std::unique_ptr CreateManifestSection( + const CloudDelegate& cloud) { + std::unique_ptr manifest(new base::DictionaryValue()); + manifest->SetString(kInfoManifestUiDeviceKind, + GetDeviceUiKind(cloud.GetModelId())); + manifest->SetString(kInfoManifestOemName, cloud.GetOemName()); + manifest->SetString(kInfoManifestModelName, cloud.GetModelName()); + return manifest; +} + +std::unique_ptr CreateEndpointsSection( + const DeviceDelegate& device) { + std::unique_ptr endpoints(new base::DictionaryValue()); + auto http_endpoint = device.GetHttpEnpoint(); + endpoints->SetInteger(kInfoEndpointsHttpPortKey, http_endpoint.first); + endpoints->SetInteger(kInfoEndpointsHttpUpdatePortKey, http_endpoint.second); + + auto https_endpoint = device.GetHttpsEnpoint(); + endpoints->SetInteger(kInfoEndpointsHttpsPortKey, https_endpoint.first); + endpoints->SetInteger(kInfoEndpointsHttpsUpdatePortKey, + https_endpoint.second); + + return endpoints; +} + +std::unique_ptr CreateInfoAuthSection( + const SecurityDelegate& security, + AuthScope anonymous_max_scope) { + std::unique_ptr auth(new base::DictionaryValue()); + + auth->SetString(kInfoAuthAnonymousMaxScopeKey, + EnumToString(anonymous_max_scope)); + + std::unique_ptr pairing_types(new base::ListValue()); + for (PairingType type : security.GetPairingTypes()) + pairing_types->AppendString(EnumToString(type)); + auth->Set(kPairingKey, pairing_types.release()); + + std::unique_ptr auth_types(new base::ListValue()); + auth_types->AppendString(kAuthTypeAnonymousValue); + auth_types->AppendString(kAuthTypePairingValue); + + // TODO(vitalybuka): Implement cloud auth. + // if (cloud.GetConnectionState().IsStatusEqual(ConnectionState::kOnline)) { + // auth_types->AppendString(kAuthTypeCloudValue); + // } + auth->Set(kAuthModeKey, auth_types.release()); + + std::unique_ptr crypto_types(new base::ListValue()); + for (CryptoType type : security.GetCryptoTypes()) + crypto_types->AppendString(EnumToString(type)); + auth->Set(kCryptoKey, crypto_types.release()); + + return auth; +} + +std::unique_ptr CreateWifiSection( + const WifiDelegate& wifi) { + std::unique_ptr result(new base::DictionaryValue()); + + std::unique_ptr capabilities(new base::ListValue()); + for (WifiType type : wifi.GetTypes()) + capabilities->AppendString(EnumToString(type)); + result->Set(kInfoWifiCapabilitiesKey, capabilities.release()); + + result->SetString(kInfoWifiSsidKey, wifi.GetCurrentlyConnectedSsid()); + + std::string hosted_ssid = wifi.GetHostedSsid(); + const ConnectionState& state = wifi.GetConnectionState(); + if (!hosted_ssid.empty()) { + DCHECK(!state.IsStatusEqual(ConnectionState::kDisabled)); + DCHECK(!state.IsStatusEqual(ConnectionState::kOnline)); + result->SetString(kInfoWifiHostedSsidKey, hosted_ssid); + } + SetStateProperties(state, result.get()); + return result; +} + +std::unique_ptr CreateGcdSection( + const CloudDelegate& cloud) { + std::unique_ptr gcd(new base::DictionaryValue()); + gcd->SetString(kInfoIdKey, cloud.GetCloudId()); + SetStateProperties(cloud.GetConnectionState(), gcd.get()); + return gcd; +} + +AuthScope GetAnonymousMaxScope(const CloudDelegate& cloud, + const WifiDelegate* wifi) { + if (wifi && !wifi->GetHostedSsid().empty()) + return AuthScope::kNone; + return cloud.GetAnonymousMaxScope(); +} + +} // namespace + +std::vector PrivetHandler::GetHttpPaths() const { + return { + "/privet/info", "/privet/v3/pairing/start", "/privet/v3/pairing/confirm", + "/privet/v3/pairing/cancel", + }; +} + +std::vector PrivetHandler::GetHttpsPaths() const { + std::vector result; + for (const auto& pair : handlers_) + result.push_back(pair.first); + return result; +} + +PrivetHandler::PrivetHandler(CloudDelegate* cloud, + DeviceDelegate* device, + SecurityDelegate* security, + WifiDelegate* wifi) + : cloud_(cloud), device_(device), security_(security), wifi_(wifi) { + CHECK(cloud_); + CHECK(device_); + CHECK(security_); + cloud_observer_.Add(cloud_); + + AddHandler("/privet/info", &PrivetHandler::HandleInfo, AuthScope::kNone); + AddHandler("/privet/v3/pairing/start", &PrivetHandler::HandlePairingStart, + AuthScope::kNone); + AddHandler("/privet/v3/pairing/confirm", &PrivetHandler::HandlePairingConfirm, + AuthScope::kNone); + AddHandler("/privet/v3/pairing/cancel", &PrivetHandler::HandlePairingCancel, + AuthScope::kNone); + AddHandler("/privet/v3/auth", &PrivetHandler::HandleAuth, AuthScope::kNone); + AddHandler("/privet/v3/setup/start", &PrivetHandler::HandleSetupStart, + AuthScope::kOwner); + AddHandler("/privet/v3/setup/status", &PrivetHandler::HandleSetupStatus, + AuthScope::kOwner); + AddHandler("/privet/v3/state", &PrivetHandler::HandleState, + AuthScope::kViewer); + AddHandler("/privet/v3/commandDefs", &PrivetHandler::HandleCommandDefs, + AuthScope::kViewer); + AddHandler("/privet/v3/commands/execute", + &PrivetHandler::HandleCommandsExecute, AuthScope::kViewer); + AddHandler("/privet/v3/commands/status", &PrivetHandler::HandleCommandsStatus, + AuthScope::kViewer); + AddHandler("/privet/v3/commands/cancel", &PrivetHandler::HandleCommandsCancel, + AuthScope::kViewer); + AddHandler("/privet/v3/commands/list", &PrivetHandler::HandleCommandsList, + AuthScope::kViewer); +} + +PrivetHandler::~PrivetHandler() {} + +void PrivetHandler::OnCommandDefsChanged() { + ++command_defs_fingerprint_; +} + +void PrivetHandler::OnStateChanged() { + ++state_fingerprint_; +} + +void PrivetHandler::HandleRequest(const std::string& api, + const std::string& auth_header, + const base::DictionaryValue* input, + const RequestCallback& callback) { + ErrorPtr error; + if (!input) { + Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kInvalidFormat, + "Malformed JSON"); + return ReturnError(*error, callback); + } + auto handler = handlers_.find(api); + if (handler == handlers_.end()) { + Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kNotFound, + "Path not found"); + return ReturnError(*error, callback); + } + if (auth_header.empty()) { + Error::AddTo(&error, FROM_HERE, errors::kDomain, + errors::kMissingAuthorization, + "Authorization header must not be empty"); + return ReturnError(*error, callback); + } + std::string token = GetAuthTokenFromAuthHeader(auth_header); + if (token.empty()) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidAuthorization, + "Invalid authorization header: %s", auth_header.c_str()); + return ReturnError(*error, callback); + } + UserInfo user_info; + if (token != kAuthTypeAnonymousValue) { + base::Time time; + user_info = security_->ParseAccessToken(token, &time); + if (user_info.scope() == AuthScope::kNone) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidAuthorization, + "Invalid access token: %s", token.c_str()); + return ReturnError(*error, callback); + } + time += base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds); + time += + base::TimeDelta::FromSeconds(kAccessTokenExpirationThresholdSeconds); + if (time < base::Time::Now()) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kAuthorizationExpired, "Token expired: %s", + token.c_str()); + return ReturnError(*error, callback); + } + } + + if (handler->second.first > user_info.scope()) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidAuthorizationScope, + "Scope '%s' does not allow '%s'", + EnumToString(user_info.scope()).c_str(), api.c_str()); + return ReturnError(*error, callback); + } + (this->*handler->second.second)(*input, user_info, callback); +} + +void PrivetHandler::AddHandler(const std::string& path, + ApiHandler handler, + AuthScope scope) { + CHECK(handlers_.emplace(path, std::make_pair(scope, handler)).second); +} + +void PrivetHandler::HandleInfo(const base::DictionaryValue&, + const UserInfo& user_info, + const RequestCallback& callback) { + base::DictionaryValue output; + + std::string name = cloud_->GetName(); + std::string model_id = cloud_->GetModelId(); + + output.SetString(kInfoVersionKey, kInfoVersionValue); + output.SetString(kInfoIdKey, cloud_->GetDeviceId()); + output.SetString(kNameKey, name); + + std::string description{cloud_->GetDescription()}; + if (!description.empty()) + output.SetString(kDescrptionKey, description); + + std::string location{cloud_->GetLocation()}; + if (!location.empty()) + output.SetString(kLocationKey, location); + + output.SetString(kInfoModelIdKey, model_id); + output.Set(kInfoModelManifestKey, CreateManifestSection(*cloud_).release()); + output.Set( + kInfoServicesKey, + ToValue(std::vector{GetDeviceUiKind(cloud_->GetModelId())}) + .release()); + + output.Set( + kInfoAuthenticationKey, + CreateInfoAuthSection(*security_, GetAnonymousMaxScope(*cloud_, wifi_)) + .release()); + + output.Set(kInfoEndpointsKey, CreateEndpointsSection(*device_).release()); + + if (wifi_) + output.Set(kWifiKey, CreateWifiSection(*wifi_).release()); + + output.Set(kGcdKey, CreateGcdSection(*cloud_).release()); + + output.SetInteger(kInfoUptimeKey, device_->GetUptime().InSeconds()); + + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandlePairingStart(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + ErrorPtr error; + + std::string pairing_str; + input.GetString(kPairingKey, &pairing_str); + + std::string crypto_str; + input.GetString(kCryptoKey, &crypto_str); + + PairingType pairing; + std::set modes = security_->GetPairingTypes(); + if (!StringToEnum(pairing_str, &pairing) || + modes.find(pairing) == modes.end()) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidParams, kInvalidParamValueFormat, + kPairingKey, pairing_str.c_str()); + return ReturnError(*error, callback); + } + + CryptoType crypto; + std::set cryptos = security_->GetCryptoTypes(); + if (!StringToEnum(crypto_str, &crypto) || + cryptos.find(crypto) == cryptos.end()) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidParams, kInvalidParamValueFormat, + kCryptoKey, crypto_str.c_str()); + return ReturnError(*error, callback); + } + + std::string id; + std::string commitment; + if (!security_->StartPairing(pairing, crypto, &id, &commitment, &error)) + return ReturnError(*error, callback); + + base::DictionaryValue output; + output.SetString(kPairingSessionIdKey, id); + output.SetString(kPairingDeviceCommitmentKey, commitment); + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandlePairingConfirm(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + std::string id; + input.GetString(kPairingSessionIdKey, &id); + + std::string commitment; + input.GetString(kPairingClientCommitmentKey, &commitment); + + std::string fingerprint; + std::string signature; + ErrorPtr error; + if (!security_->ConfirmPairing(id, commitment, &fingerprint, &signature, + &error)) { + return ReturnError(*error, callback); + } + + base::DictionaryValue output; + output.SetString(kPairingFingerprintKey, fingerprint); + output.SetString(kPairingSignatureKey, signature); + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandlePairingCancel(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + std::string id; + input.GetString(kPairingSessionIdKey, &id); + + ErrorPtr error; + if (!security_->CancelPairing(id, &error)) + return ReturnError(*error, callback); + + base::DictionaryValue output; + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandleAuth(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + ErrorPtr error; + + std::string auth_code_type; + input.GetString(kAuthModeKey, &auth_code_type); + + std::string auth_code; + input.GetString(kAuthCodeKey, &auth_code); + + AuthScope max_auth_scope = AuthScope::kNone; + if (auth_code_type == kAuthTypeAnonymousValue) { + max_auth_scope = GetAnonymousMaxScope(*cloud_, wifi_); + } else if (auth_code_type == kAuthTypePairingValue) { + if (!security_->IsValidPairingCode(auth_code)) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidAuthCode, kInvalidParamValueFormat, + kAuthCodeKey, auth_code.c_str()); + return ReturnError(*error, callback); + } + max_auth_scope = AuthScope::kOwner; + } else { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidAuthMode, kInvalidParamValueFormat, + kAuthModeKey, auth_code_type.c_str()); + return ReturnError(*error, callback); + } + + std::string requested_scope; + input.GetString(kAuthRequestedScopeKey, &requested_scope); + + AuthScope requested_auth_scope = + AuthScopeFromString(requested_scope, max_auth_scope); + if (requested_auth_scope == AuthScope::kNone) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidRequestedScope, kInvalidParamValueFormat, + kAuthRequestedScopeKey, requested_scope.c_str()); + return ReturnError(*error, callback); + } + + if (requested_auth_scope > max_auth_scope) { + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kAccessDenied, "Scope '%s' is not allowed", + EnumToString(requested_auth_scope).c_str()); + return ReturnError(*error, callback); + } + + base::DictionaryValue output; + output.SetString( + kAuthAccessTokenKey, + security_->CreateAccessToken( + UserInfo{requested_auth_scope, ++last_user_id_}, base::Time::Now())); + output.SetString(kAuthTokenTypeKey, kAuthorizationHeaderPrefix); + output.SetInteger(kAuthExpiresInKey, kAccessTokenExpirationSeconds); + output.SetString(kAuthScopeKey, EnumToString(requested_auth_scope)); + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandleSetupStart(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + std::string name{cloud_->GetName()}; + input.GetString(kNameKey, &name); + + std::string description{cloud_->GetDescription()}; + input.GetString(kDescrptionKey, &description); + + std::string location{cloud_->GetLocation()}; + input.GetString(kLocationKey, &location); + + std::string ssid; + std::string passphrase; + std::string ticket; + std::string user; + + const base::DictionaryValue* wifi = nullptr; + if (input.GetDictionary(kWifiKey, &wifi)) { + if (!wifi_ || wifi_->GetTypes().empty()) { + ErrorPtr error; + Error::AddTo(&error, FROM_HERE, errors::kDomain, + errors::kSetupUnavailable, "WiFi setup unavailible"); + return ReturnError(*error, callback); + } + wifi->GetString(kSetupStartSsidKey, &ssid); + if (ssid.empty()) { + ErrorPtr error; + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidParams, kInvalidParamValueFormat, + kSetupStartSsidKey, ""); + return ReturnError(*error, callback); + } + wifi->GetString(kSetupStartPassKey, &passphrase); + } + + const base::DictionaryValue* registration = nullptr; + if (input.GetDictionary(kGcdKey, ®istration)) { + registration->GetString(kSetupStartTicketIdKey, &ticket); + if (ticket.empty()) { + ErrorPtr error; + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidParams, kInvalidParamValueFormat, + kSetupStartTicketIdKey, ""); + return ReturnError(*error, callback); + } + registration->GetString(kSetupStartUserKey, &user); + } + + cloud_->UpdateDeviceInfo(name, description, location); + + ErrorPtr error; + if (!ssid.empty() && !wifi_->ConfigureCredentials(ssid, passphrase, &error)) + return ReturnError(*error, callback); + + if (!ticket.empty() && !cloud_->Setup(ticket, user, &error)) + return ReturnError(*error, callback); + + ReplyWithSetupStatus(callback); +} + +void PrivetHandler::HandleSetupStatus(const base::DictionaryValue&, + const UserInfo& user_info, + const RequestCallback& callback) { + ReplyWithSetupStatus(callback); +} + +void PrivetHandler::ReplyWithSetupStatus( + const RequestCallback& callback) const { + base::DictionaryValue output; + + const SetupState& state = cloud_->GetSetupState(); + if (!state.IsStatusEqual(SetupState::kNone)) { + base::DictionaryValue* gcd = new base::DictionaryValue; + output.Set(kGcdKey, gcd); + SetStateProperties(state, gcd); + if (state.IsStatusEqual(SetupState::kSuccess)) + gcd->SetString(kInfoIdKey, cloud_->GetCloudId()); + } + + if (wifi_) { + const SetupState& state = wifi_->GetSetupState(); + if (!state.IsStatusEqual(SetupState::kNone)) { + base::DictionaryValue* wifi = new base::DictionaryValue; + output.Set(kWifiKey, wifi); + SetStateProperties(state, wifi); + if (state.IsStatusEqual(SetupState::kSuccess)) + wifi->SetString(kInfoWifiSsidKey, wifi_->GetCurrentlyConnectedSsid()); + } + } + + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandleState(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + base::DictionaryValue output; + base::DictionaryValue* defs = cloud_->GetState().DeepCopy(); + output.Set(kStateKey, defs); + output.SetString(kFingerprintKey, base::IntToString(state_fingerprint_)); + + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandleCommandDefs(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + base::DictionaryValue output; + base::DictionaryValue* defs = cloud_->GetCommandDef().DeepCopy(); + output.Set(kCommandsKey, defs); + output.SetString(kFingerprintKey, + base::IntToString(command_defs_fingerprint_)); + + callback.Run(http::kOk, output); +} + +void PrivetHandler::HandleCommandsExecute(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + cloud_->AddCommand(input, user_info, + base::Bind(&OnCommandRequestSucceeded, callback)); +} + +void PrivetHandler::HandleCommandsStatus(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + std::string id; + if (!input.GetString(kCommandsIdKey, &id)) { + ErrorPtr error; + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidParams, kInvalidParamValueFormat, + kCommandsIdKey, id.c_str()); + return ReturnError(*error, callback); + } + cloud_->GetCommand(id, user_info, + base::Bind(&OnCommandRequestSucceeded, callback)); +} + +void PrivetHandler::HandleCommandsList(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + cloud_->ListCommands(user_info, + base::Bind(&OnCommandRequestSucceeded, callback)); +} + +void PrivetHandler::HandleCommandsCancel(const base::DictionaryValue& input, + const UserInfo& user_info, + const RequestCallback& callback) { + std::string id; + if (!input.GetString(kCommandsIdKey, &id)) { + ErrorPtr error; + Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, + errors::kInvalidParams, kInvalidParamValueFormat, + kCommandsIdKey, id.c_str()); + return ReturnError(*error, callback); + } + cloud_->CancelCommand(id, user_info, + base::Bind(&OnCommandRequestSucceeded, callback)); +} + +} // namespace privet +} // namespace weave -- cgit v1.2.3