#include "remoting/host/policy_hack/policy_watcher.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/time.h"
#include "base/values.h"
#include "remoting/host/dns_blackhole_checker.h"
namespace remoting {
namespace policy_hack {
namespace {
const int kFallbackReloadDelayMinutes = 15;
bool GetBooleanOrDefault(const base::DictionaryValue* dict, const char* key,
bool default_if_value_missing,
bool default_if_value_not_boolean) {
if (!dict->HasKey(key)) {
return default_if_value_missing;
}
const base::Value* value;
if (dict->Get(key, &value) && value->IsType(base::Value::TYPE_BOOLEAN)) {
bool bool_value;
CHECK(value->GetAsBoolean(&bool_value));
return bool_value;
}
return default_if_value_not_boolean;
}
std::string GetStringOrDefault(const base::DictionaryValue* dict,
const char* key,
const std::string& default_if_value_missing,
const std::string& default_if_value_not_string) {
if (!dict->HasKey(key)) {
return default_if_value_missing;
}
const base::Value* value;
if (dict->Get(key, &value) && value->IsType(base::Value::TYPE_STRING)) {
std::string string_value;
CHECK(value->GetAsString(&string_value));
return string_value;
}
return default_if_value_not_string;
}
void CopyBooleanOrDefault(base::DictionaryValue* to,
const base::DictionaryValue* from, const char* key,
bool default_if_value_missing,
bool default_if_value_not_boolean) {
to->Set(key, base::Value::CreateBooleanValue(
GetBooleanOrDefault(from, key, default_if_value_missing,
default_if_value_not_boolean)));
}
void CopyStringOrDefault(base::DictionaryValue* to,
const base::DictionaryValue* from, const char* key,
const std::string& default_if_value_missing,
const std::string& default_if_value_not_string) {
to->Set(key, base::Value::CreateStringValue(
GetStringOrDefault(from, key, default_if_value_missing,
default_if_value_not_string)));
}
scoped_ptr<base::DictionaryValue> AddDefaultValuesWhenNecessary(
const base::DictionaryValue* from) {
scoped_ptr<base::DictionaryValue> to(new base::DictionaryValue());
CopyBooleanOrDefault(to.get(), from,
PolicyWatcher::kNatPolicyName, true, false);
CopyBooleanOrDefault(to.get(), from,
PolicyWatcher::kHostRequireTwoFactorPolicyName,
false, false);
CopyStringOrDefault(to.get(), from,
PolicyWatcher::kHostDomainPolicyName, "", "");
CopyStringOrDefault(to.get(), from,
PolicyWatcher::kHostTalkGadgetPrefixPolicyName,
kDefaultHostTalkGadgetPrefix,
kDefaultHostTalkGadgetPrefix);
CopyBooleanOrDefault(to.get(), from,
PolicyWatcher::kHostRequireCurtainPolicyName,
false, false);
return to.Pass();
}
}
const char PolicyWatcher::kNatPolicyName[] =
"RemoteAccessHostFirewallTraversal";
const char PolicyWatcher::kHostRequireTwoFactorPolicyName[] =
"RemoteAccessHostRequireTwoFactor";
const char PolicyWatcher::kHostDomainPolicyName[] =
"RemoteAccessHostDomain";
const char PolicyWatcher::kHostTalkGadgetPrefixPolicyName[] =
"RemoteAccessHostTalkGadgetPrefix";
const char PolicyWatcher::kHostRequireCurtainPolicyName[] =
"RemoteAccessHostRequireCurtain";
const char* const PolicyWatcher::kBooleanPolicyNames[] =
{ PolicyWatcher::kNatPolicyName,
PolicyWatcher::kHostRequireTwoFactorPolicyName,
PolicyWatcher::kHostRequireCurtainPolicyName
};
const int PolicyWatcher::kBooleanPolicyNamesNum =
arraysize(kBooleanPolicyNames);
const char* const PolicyWatcher::kStringPolicyNames[] =
{ PolicyWatcher::kHostDomainPolicyName,
PolicyWatcher::kHostTalkGadgetPrefixPolicyName
};
const int PolicyWatcher::kStringPolicyNamesNum =
arraysize(kStringPolicyNames);
PolicyWatcher::PolicyWatcher(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: task_runner_(task_runner),
old_policies_(new base::DictionaryValue()),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
}
PolicyWatcher::~PolicyWatcher() {
}
void PolicyWatcher::StartWatching(const PolicyCallback& policy_callback) {
if (!OnPolicyWatcherThread()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(&PolicyWatcher::StartWatching,
base::Unretained(this),
policy_callback));
return;
}
policy_callback_ = policy_callback;
StartWatchingInternal();
}
void PolicyWatcher::StopWatching(base::WaitableEvent* done) {
if (!OnPolicyWatcherThread()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(&PolicyWatcher::StopWatching,
base::Unretained(this), done));
return;
}
StopWatchingInternal();
weak_factory_.InvalidateWeakPtrs();
policy_callback_.Reset();
done->Signal();
}
void PolicyWatcher::ScheduleFallbackReloadTask() {
DCHECK(OnPolicyWatcherThread());
ScheduleReloadTask(
base::TimeDelta::FromMinutes(kFallbackReloadDelayMinutes));
}
void PolicyWatcher::ScheduleReloadTask(const base::TimeDelta& delay) {
DCHECK(OnPolicyWatcherThread());
task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&PolicyWatcher::Reload, weak_factory_.GetWeakPtr()),
delay);
}
bool PolicyWatcher::OnPolicyWatcherThread() const {
return task_runner_->BelongsToCurrentThread();
}
void PolicyWatcher::UpdatePolicies(
const base::DictionaryValue* new_policies_raw) {
DCHECK(OnPolicyWatcherThread());
scoped_ptr<base::DictionaryValue> new_policies =
AddDefaultValuesWhenNecessary(new_policies_raw);
scoped_ptr<base::DictionaryValue> changed_policies(
new base::DictionaryValue());
base::DictionaryValue::Iterator iter(*new_policies);
while (iter.HasNext()) {
base::Value* old_policy;
if (!(old_policies_->Get(iter.key(), &old_policy) &&
old_policy->Equals(&iter.value()))) {
changed_policies->Set(iter.key(), iter.value().DeepCopy());
}
iter.Advance();
}
old_policies_.swap(new_policies);
if (!changed_policies->empty()) {
policy_callback_.Run(changed_policies.Pass());
}
}
}
}