// Copyright (c) 2011 The Chromium 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 "chrome/browser/chromeos/login/signed_settings_helper.h"
#include <string>
#include <vector>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "chrome/browser/chromeos/login/signed_settings.h"
#include "chrome/browser/policy/proto/device_management_backend.pb.h"
#include "content/browser/browser_thread.h"
namespace chromeos {
namespace {
class OpContext {
public:
class Delegate {
public:
virtual void OnOpCreated(OpContext* context) = 0;
virtual void OnOpStarted(OpContext* context) = 0;
virtual void OnOpCompleted(OpContext* context) = 0;
};
virtual ~OpContext() {}
// Creates and execute op.
void Execute() {
CreateOp();
CHECK(op_.get());
if (delegate_)
delegate_->OnOpCreated(this);
// Note that the context could be released when op_->Execute() returns.
// So keep a local copy of delegate and executing flag to use after
// the call.
Delegate* delegate = delegate_;
executing_ = true;
op_->Execute();
if (delegate)
delegate->OnOpStarted(this);
}
// Cancels the callback.
void CancelCallback() {
callback_ = NULL;
}
// Cancels the callback and cancels the op if it is not executing.
void Cancel() {
CancelCallback();
if (!executing_)
OnOpCompleted();
}
// Accessors.
SignedSettings* op() const {
return op_.get();
}
SignedSettingsHelper::Callback* callback() const {
return callback_;
}
void set_delegate(Delegate* delegate) {
delegate_ = delegate;
}
protected:
OpContext(SignedSettingsHelper::Callback* callback,
Delegate* delegate)
: executing_(false),
delegate_(delegate),
callback_(callback) {
}
// Creates the op to execute.
virtual void CreateOp() = 0;
// Callback on op completion.
virtual void OnOpCompleted() {
if (delegate_)
delegate_->OnOpCompleted(this);
delete this;
}
bool executing_;
Delegate* delegate_;
scoped_refptr<SignedSettings> op_;
SignedSettingsHelper::Callback* callback_;
};
class WhitelistOpContext : public SignedSettings::Delegate<bool>,
public OpContext {
public:
enum Type {
CHECK,
ADD,
REMOVE,
};
WhitelistOpContext(Type type,
const std::string& email,
SignedSettingsHelper::Callback* callback,
OpContext::Delegate* delegate)
: OpContext(callback, delegate),
type_(type),
email_(email) {
}
// chromeos::SignedSettings::Delegate implementation
virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
bool value) OVERRIDE {
if (callback_) {
switch (type_) {
case CHECK:
callback_->OnCheckWhitelistCompleted(code, email_);
break;
case ADD:
callback_->OnWhitelistCompleted(code, email_);
break;
case REMOVE:
callback_->OnUnwhitelistCompleted(code, email_);
break;
default:
LOG(ERROR) << "Unknown WhitelistOpContext type " << type_;
break;
}
}
OnOpCompleted();
}
protected:
// OpContext implemenetation
virtual void CreateOp() OVERRIDE {
switch (type_) {
case CHECK:
op_ = SignedSettings::CreateCheckWhitelistOp(email_, this);
break;
case ADD:
op_ = SignedSettings::CreateWhitelistOp(email_, true, this);
break;
case REMOVE:
op_ = SignedSettings::CreateWhitelistOp(email_, false, this);
break;
default:
LOG(ERROR) << "Unknown WhitelistOpContext type " << type_;
break;
}
}
private:
Type type_;
std::string email_;
DISALLOW_COPY_AND_ASSIGN(WhitelistOpContext);
};
class StorePropertyOpContext : public SignedSettings::Delegate<bool>,
public OpContext {
public:
StorePropertyOpContext(const std::string& name,
const std::string& value,
SignedSettingsHelper::Callback* callback,
OpContext::Delegate* delegate)
: OpContext(callback, delegate),
name_(name),
value_(value) {
}
// chromeos::SignedSettings::Delegate implementation
virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
bool unused) OVERRIDE {
VLOG(2) << "OnSettingsOpCompleted, code = " << code;
if (callback_)
callback_->OnStorePropertyCompleted(code, name_, value_);
OnOpCompleted();
}
protected:
// OpContext implemenetation
virtual void CreateOp() OVERRIDE {
op_ = SignedSettings::CreateStorePropertyOp(name_, value_, this);
}
private:
std::string name_;
std::string value_;
DISALLOW_COPY_AND_ASSIGN(StorePropertyOpContext);
};
class RetrievePropertyOpContext
: public SignedSettings::Delegate<std::string>,
public OpContext {
public:
RetrievePropertyOpContext(const std::string& name,
SignedSettingsHelper::Callback* callback,
OpContext::Delegate* delegate)
: OpContext(callback, delegate),
name_(name) {
}
// chromeos::SignedSettings::Delegate implementation
virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
std::string value) OVERRIDE {
if (callback_)
callback_->OnRetrievePropertyCompleted(code, name_, value);
OnOpCompleted();
}
protected:
// OpContext implemenetation
virtual void CreateOp() OVERRIDE {
op_ = SignedSettings::CreateRetrievePropertyOp(name_, this);
}
private:
std::string name_;
DISALLOW_COPY_AND_ASSIGN(RetrievePropertyOpContext);
};
class StorePolicyOpContext : public SignedSettings::Delegate<bool>,
public OpContext {
public:
StorePolicyOpContext(const em::PolicyFetchResponse& policy,
SignedSettingsHelper::Callback* callback,
OpContext::Delegate* delegate)
: OpContext(callback, delegate),
policy_(policy) {
}
// chromeos::SignedSettings::Delegate implementation
virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
bool unused) OVERRIDE {
VLOG(2) << "OnSettingsOpCompleted, code = " << code;
if (callback_)
callback_->OnStorePolicyCompleted(code);
OnOpCompleted();
}
protected:
// OpContext implementation
virtual void CreateOp() OVERRIDE {
op_ = SignedSettings::CreateStorePolicyOp(&policy_, this);
}
private:
em::PolicyFetchResponse policy_;
DISALLOW_COPY_AND_ASSIGN(StorePolicyOpContext);
};
class RetrievePolicyOpContext
: public SignedSettings::Delegate<const em::PolicyFetchResponse&>,
public OpContext {
public:
RetrievePolicyOpContext(SignedSettingsHelper::Callback* callback,
OpContext::Delegate* delegate)
: OpContext(callback, delegate) {
}
// chromeos::SignedSettings::Delegate implementation
virtual void OnSettingsOpCompleted(
SignedSettings::ReturnCode code,
const em::PolicyFetchResponse& policy) OVERRIDE {
if (callback_)
callback_->OnRetrievePolicyCompleted(code, policy);
OnOpCompleted();
}
protected:
// OpContext implementation
virtual void CreateOp() OVERRIDE {
op_ = SignedSettings::CreateRetrievePolicyOp(this);
}
private:
DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOpContext);
};
} // namespace
class SignedSettingsHelperImpl : public SignedSettingsHelper,
public OpContext::Delegate {
public:
// SignedSettingsHelper implementation
virtual void StartCheckWhitelistOp(const std::string& email,
Callback* callback) OVERRIDE;
virtual void StartWhitelistOp(const std::string& email,
bool add_to_whitelist,
Callback* callback) OVERRIDE;
virtual void StartStorePropertyOp(const std::string& name,
const std::string& value,
Callback* callback) OVERRIDE;
virtual void StartRetrieveProperty(const std::string& name,
Callback* callback) OVERRIDE;
virtual void StartStorePolicyOp(const em::PolicyFetchResponse& policy,
Callback* callback) OVERRIDE;
virtual void StartRetrievePolicyOp(Callback* callback) OVERRIDE;
virtual void CancelCallback(Callback* callback) OVERRIDE;
// OpContext::Delegate implementation
virtual void OnOpCreated(OpContext* context);
virtual void OnOpStarted(OpContext* context);
virtual void OnOpCompleted(OpContext* context);
private:
SignedSettingsHelperImpl();
~SignedSettingsHelperImpl();
void AddOpContext(OpContext* context);
void ClearAll();
std::vector<OpContext*> pending_contexts_;
friend struct base::DefaultLazyInstanceTraits<SignedSettingsHelperImpl>;
DISALLOW_COPY_AND_ASSIGN(SignedSettingsHelperImpl);
};
static base::LazyInstance<SignedSettingsHelperImpl>
g_signed_settings_helper_impl(base::LINKER_INITIALIZED);
SignedSettingsHelperImpl::SignedSettingsHelperImpl() {
}
SignedSettingsHelperImpl::~SignedSettingsHelperImpl() {
if (!pending_contexts_.empty()) {
LOG(WARNING) << "SignedSettingsHelperImpl shutdown with pending ops, "
<< "changes will be lost.";
ClearAll();
}
}
void SignedSettingsHelperImpl::StartCheckWhitelistOp(
const std::string&email,
SignedSettingsHelper::Callback* callback) {
AddOpContext(new WhitelistOpContext(
WhitelistOpContext::CHECK,
email,
callback,
this));
}
void SignedSettingsHelperImpl::StartWhitelistOp(
const std::string&email,
bool add_to_whitelist,
SignedSettingsHelper::Callback* callback) {
AddOpContext(new WhitelistOpContext(
add_to_whitelist ? WhitelistOpContext::ADD : WhitelistOpContext::REMOVE,
email,
callback,
this));
}
void SignedSettingsHelperImpl::StartStorePropertyOp(
const std::string& name,
const std::string& value,
SignedSettingsHelper::Callback* callback) {
AddOpContext(new StorePropertyOpContext(
name,
value,
callback,
this));
}
void SignedSettingsHelperImpl::StartRetrieveProperty(
const std::string& name,
SignedSettingsHelper::Callback* callback) {
AddOpContext(new RetrievePropertyOpContext(
name,
callback,
this));
}
void SignedSettingsHelperImpl::StartStorePolicyOp(
const em::PolicyFetchResponse& policy,
SignedSettingsHelper::Callback* callback) {
AddOpContext(new StorePolicyOpContext(policy, callback, this));
}
void SignedSettingsHelperImpl::StartRetrievePolicyOp(
SignedSettingsHelper::Callback* callback) {
AddOpContext(new RetrievePolicyOpContext(callback, this));
}
void SignedSettingsHelperImpl::CancelCallback(
SignedSettingsHelper::Callback* callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
for (size_t i = 0; i < pending_contexts_.size(); ++i) {
if (pending_contexts_[i]->callback() == callback) {
pending_contexts_[i]->CancelCallback();
}
}
}
void SignedSettingsHelperImpl::AddOpContext(OpContext* context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CHECK(context);
pending_contexts_.push_back(context);
if (pending_contexts_.size() == 1)
context->Execute();
}
void SignedSettingsHelperImpl::ClearAll() {
for (size_t i = 0; i < pending_contexts_.size(); ++i) {
pending_contexts_[i]->set_delegate(NULL);
pending_contexts_[i]->Cancel();
}
pending_contexts_.clear();
}
void SignedSettingsHelperImpl::OnOpCreated(OpContext* context) {
if (test_delegate_)
test_delegate_->OnOpCreated(context->op());
}
void SignedSettingsHelperImpl::OnOpStarted(OpContext* context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (test_delegate_)
test_delegate_->OnOpStarted(context->op());
}
void SignedSettingsHelperImpl::OnOpCompleted(OpContext* context) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(pending_contexts_.front() == context);
pending_contexts_.erase(pending_contexts_.begin());
if (!pending_contexts_.empty())
pending_contexts_.front()->Execute();
if (test_delegate_)
test_delegate_->OnOpCompleted(context->op());
}
SignedSettingsHelper* SignedSettingsHelper::Get() {
return g_signed_settings_helper_impl.Pointer();
}
} // namespace chromeos