1// Copyright (c) 2011 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/chromeos/login/signed_settings_helper.h"
6
7#include <string>
8#include <vector>
9
10#include "base/lazy_instance.h"
11#include "base/logging.h"
12#include "base/memory/ref_counted.h"
13#include "chrome/browser/chromeos/login/signed_settings.h"
14#include "chrome/browser/policy/proto/device_management_backend.pb.h"
15#include "content/browser/browser_thread.h"
16
17namespace chromeos {
18
19namespace {
20
21class OpContext {
22 public:
23  class Delegate {
24   public:
25    virtual void OnOpCreated(OpContext* context) = 0;
26    virtual void OnOpStarted(OpContext* context) = 0;
27    virtual void OnOpCompleted(OpContext* context) = 0;
28  };
29
30  virtual ~OpContext() {}
31
32  // Creates and execute op.
33  void Execute() {
34    CreateOp();
35    CHECK(op_.get());
36    if (delegate_)
37      delegate_->OnOpCreated(this);
38
39    // Note that the context could be released when op_->Execute() returns.
40    // So keep a local copy of delegate and executing flag to use after
41    // the call.
42    Delegate* delegate = delegate_;
43    executing_ = true;
44    op_->Execute();
45    if (delegate)
46      delegate->OnOpStarted(this);
47  }
48
49  // Cancels the callback.
50  void CancelCallback() {
51    callback_ = NULL;
52  }
53
54  // Cancels the callback and cancels the op if it is not executing.
55  void Cancel() {
56    CancelCallback();
57
58    if (!executing_)
59      OnOpCompleted();
60  }
61
62  // Accessors.
63  SignedSettings* op() const {
64    return op_.get();
65  }
66
67  SignedSettingsHelper::Callback* callback() const {
68    return callback_;
69  }
70
71  void set_delegate(Delegate* delegate) {
72    delegate_ = delegate;
73  }
74
75 protected:
76  OpContext(SignedSettingsHelper::Callback* callback,
77            Delegate* delegate)
78      : executing_(false),
79        delegate_(delegate),
80        callback_(callback) {
81  }
82
83  // Creates the op to execute.
84  virtual void CreateOp() = 0;
85
86  // Callback on op completion.
87  virtual void OnOpCompleted() {
88    if (delegate_)
89      delegate_->OnOpCompleted(this);
90
91    delete this;
92  }
93
94  bool executing_;
95  Delegate* delegate_;
96
97  scoped_refptr<SignedSettings> op_;
98  SignedSettingsHelper::Callback* callback_;
99};
100
101class WhitelistOpContext : public SignedSettings::Delegate<bool>,
102                           public OpContext {
103 public:
104  enum Type {
105    CHECK,
106    ADD,
107    REMOVE,
108  };
109
110  WhitelistOpContext(Type type,
111                     const std::string& email,
112                     SignedSettingsHelper::Callback* callback,
113                     OpContext::Delegate* delegate)
114      : OpContext(callback, delegate),
115        type_(type),
116        email_(email) {
117  }
118
119  // chromeos::SignedSettings::Delegate implementation
120  virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
121                                     bool value) OVERRIDE {
122    if (callback_) {
123      switch (type_) {
124        case CHECK:
125          callback_->OnCheckWhitelistCompleted(code, email_);
126          break;
127        case ADD:
128          callback_->OnWhitelistCompleted(code, email_);
129          break;
130        case REMOVE:
131          callback_->OnUnwhitelistCompleted(code, email_);
132          break;
133        default:
134          LOG(ERROR) << "Unknown WhitelistOpContext type " << type_;
135          break;
136      }
137    }
138    OnOpCompleted();
139  }
140
141 protected:
142  // OpContext implemenetation
143  virtual void CreateOp() OVERRIDE {
144    switch (type_) {
145      case CHECK:
146        op_ = SignedSettings::CreateCheckWhitelistOp(email_, this);
147        break;
148      case ADD:
149        op_ = SignedSettings::CreateWhitelistOp(email_, true, this);
150        break;
151      case REMOVE:
152        op_ = SignedSettings::CreateWhitelistOp(email_, false, this);
153        break;
154      default:
155        LOG(ERROR) << "Unknown WhitelistOpContext type " << type_;
156        break;
157    }
158  }
159
160 private:
161  Type type_;
162  std::string email_;
163
164  DISALLOW_COPY_AND_ASSIGN(WhitelistOpContext);
165};
166
167class StorePropertyOpContext : public SignedSettings::Delegate<bool>,
168                               public OpContext {
169 public:
170  StorePropertyOpContext(const std::string& name,
171                         const std::string& value,
172                         SignedSettingsHelper::Callback* callback,
173                         OpContext::Delegate* delegate)
174      : OpContext(callback, delegate),
175        name_(name),
176        value_(value) {
177  }
178
179  // chromeos::SignedSettings::Delegate implementation
180  virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
181                                     bool unused) OVERRIDE {
182    VLOG(2) << "OnSettingsOpCompleted, code = " << code;
183    if (callback_)
184      callback_->OnStorePropertyCompleted(code, name_, value_);
185    OnOpCompleted();
186  }
187
188 protected:
189  // OpContext implemenetation
190  virtual void CreateOp() OVERRIDE {
191    op_ = SignedSettings::CreateStorePropertyOp(name_, value_, this);
192  }
193
194 private:
195  std::string name_;
196  std::string value_;
197
198  DISALLOW_COPY_AND_ASSIGN(StorePropertyOpContext);
199};
200
201class RetrievePropertyOpContext
202    : public SignedSettings::Delegate<std::string>,
203      public OpContext {
204 public:
205  RetrievePropertyOpContext(const std::string& name,
206                            SignedSettingsHelper::Callback* callback,
207                            OpContext::Delegate* delegate)
208      : OpContext(callback, delegate),
209        name_(name) {
210  }
211
212  // chromeos::SignedSettings::Delegate implementation
213  virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
214                                     std::string value) OVERRIDE {
215    if (callback_)
216      callback_->OnRetrievePropertyCompleted(code, name_, value);
217
218    OnOpCompleted();
219  }
220
221 protected:
222  // OpContext implemenetation
223  virtual void CreateOp() OVERRIDE {
224    op_ = SignedSettings::CreateRetrievePropertyOp(name_, this);
225  }
226
227 private:
228  std::string name_;
229
230  DISALLOW_COPY_AND_ASSIGN(RetrievePropertyOpContext);
231};
232
233class StorePolicyOpContext : public SignedSettings::Delegate<bool>,
234                             public OpContext {
235 public:
236  StorePolicyOpContext(const em::PolicyFetchResponse& policy,
237                       SignedSettingsHelper::Callback* callback,
238                       OpContext::Delegate* delegate)
239      : OpContext(callback, delegate),
240        policy_(policy) {
241  }
242
243  // chromeos::SignedSettings::Delegate implementation
244  virtual void OnSettingsOpCompleted(SignedSettings::ReturnCode code,
245                                     bool unused) OVERRIDE {
246    VLOG(2) << "OnSettingsOpCompleted, code = " << code;
247    if (callback_)
248      callback_->OnStorePolicyCompleted(code);
249    OnOpCompleted();
250  }
251
252 protected:
253  // OpContext implementation
254  virtual void CreateOp() OVERRIDE {
255    op_ = SignedSettings::CreateStorePolicyOp(&policy_, this);
256  }
257
258 private:
259  em::PolicyFetchResponse policy_;
260  DISALLOW_COPY_AND_ASSIGN(StorePolicyOpContext);
261};
262
263class RetrievePolicyOpContext
264    : public SignedSettings::Delegate<const em::PolicyFetchResponse&>,
265      public OpContext {
266 public:
267  RetrievePolicyOpContext(SignedSettingsHelper::Callback* callback,
268                          OpContext::Delegate* delegate)
269      : OpContext(callback, delegate) {
270  }
271
272  // chromeos::SignedSettings::Delegate implementation
273  virtual void OnSettingsOpCompleted(
274      SignedSettings::ReturnCode code,
275      const em::PolicyFetchResponse& policy) OVERRIDE {
276    if (callback_)
277      callback_->OnRetrievePolicyCompleted(code, policy);
278    OnOpCompleted();
279  }
280
281 protected:
282  // OpContext implementation
283  virtual void CreateOp() OVERRIDE {
284    op_ = SignedSettings::CreateRetrievePolicyOp(this);
285  }
286
287 private:
288  DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOpContext);
289};
290
291}  // namespace
292
293
294class SignedSettingsHelperImpl : public SignedSettingsHelper,
295                                 public OpContext::Delegate {
296 public:
297  // SignedSettingsHelper implementation
298  virtual void StartCheckWhitelistOp(const std::string& email,
299                                     Callback* callback) OVERRIDE;
300  virtual void StartWhitelistOp(const std::string& email,
301                                bool add_to_whitelist,
302                                Callback* callback) OVERRIDE;
303  virtual void StartStorePropertyOp(const std::string& name,
304                                    const std::string& value,
305                                    Callback* callback) OVERRIDE;
306  virtual void StartRetrieveProperty(const std::string& name,
307                                     Callback* callback) OVERRIDE;
308  virtual void StartStorePolicyOp(const em::PolicyFetchResponse& policy,
309                                  Callback* callback) OVERRIDE;
310  virtual void StartRetrievePolicyOp(Callback* callback) OVERRIDE;
311  virtual void CancelCallback(Callback* callback) OVERRIDE;
312
313  // OpContext::Delegate implementation
314  virtual void OnOpCreated(OpContext* context);
315  virtual void OnOpStarted(OpContext* context);
316  virtual void OnOpCompleted(OpContext* context);
317
318 private:
319  SignedSettingsHelperImpl();
320  ~SignedSettingsHelperImpl();
321
322  void AddOpContext(OpContext* context);
323  void ClearAll();
324
325  std::vector<OpContext*> pending_contexts_;
326
327  friend struct base::DefaultLazyInstanceTraits<SignedSettingsHelperImpl>;
328  DISALLOW_COPY_AND_ASSIGN(SignedSettingsHelperImpl);
329};
330
331static base::LazyInstance<SignedSettingsHelperImpl>
332    g_signed_settings_helper_impl(base::LINKER_INITIALIZED);
333
334SignedSettingsHelperImpl::SignedSettingsHelperImpl() {
335}
336
337SignedSettingsHelperImpl::~SignedSettingsHelperImpl() {
338  if (!pending_contexts_.empty()) {
339    LOG(WARNING) << "SignedSettingsHelperImpl shutdown with pending ops, "
340                 << "changes will be lost.";
341    ClearAll();
342  }
343}
344
345void SignedSettingsHelperImpl::StartCheckWhitelistOp(
346    const std::string&email,
347    SignedSettingsHelper::Callback* callback) {
348  AddOpContext(new WhitelistOpContext(
349      WhitelistOpContext::CHECK,
350      email,
351      callback,
352      this));
353}
354
355void SignedSettingsHelperImpl::StartWhitelistOp(
356    const std::string&email,
357    bool add_to_whitelist,
358    SignedSettingsHelper::Callback* callback) {
359  AddOpContext(new WhitelistOpContext(
360      add_to_whitelist ? WhitelistOpContext::ADD : WhitelistOpContext::REMOVE,
361      email,
362      callback,
363      this));
364}
365
366void SignedSettingsHelperImpl::StartStorePropertyOp(
367    const std::string& name,
368    const std::string& value,
369    SignedSettingsHelper::Callback* callback) {
370  AddOpContext(new StorePropertyOpContext(
371      name,
372      value,
373      callback,
374      this));
375}
376
377void SignedSettingsHelperImpl::StartRetrieveProperty(
378    const std::string& name,
379    SignedSettingsHelper::Callback* callback) {
380  AddOpContext(new RetrievePropertyOpContext(
381      name,
382      callback,
383      this));
384}
385
386void SignedSettingsHelperImpl::StartStorePolicyOp(
387    const em::PolicyFetchResponse& policy,
388    SignedSettingsHelper::Callback* callback) {
389  AddOpContext(new StorePolicyOpContext(policy, callback, this));
390}
391
392void SignedSettingsHelperImpl::StartRetrievePolicyOp(
393    SignedSettingsHelper::Callback* callback) {
394  AddOpContext(new RetrievePolicyOpContext(callback, this));
395}
396
397void SignedSettingsHelperImpl::CancelCallback(
398    SignedSettingsHelper::Callback* callback) {
399  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
400
401  for (size_t i = 0; i < pending_contexts_.size(); ++i) {
402    if (pending_contexts_[i]->callback() == callback) {
403      pending_contexts_[i]->CancelCallback();
404    }
405  }
406}
407
408void SignedSettingsHelperImpl::AddOpContext(OpContext* context) {
409  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410  CHECK(context);
411
412  pending_contexts_.push_back(context);
413  if (pending_contexts_.size() == 1)
414    context->Execute();
415}
416
417void SignedSettingsHelperImpl::ClearAll() {
418  for (size_t i = 0; i < pending_contexts_.size(); ++i) {
419    pending_contexts_[i]->set_delegate(NULL);
420    pending_contexts_[i]->Cancel();
421  }
422  pending_contexts_.clear();
423}
424
425void SignedSettingsHelperImpl::OnOpCreated(OpContext* context) {
426  if (test_delegate_)
427    test_delegate_->OnOpCreated(context->op());
428}
429
430void SignedSettingsHelperImpl::OnOpStarted(OpContext* context) {
431  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432
433  if (test_delegate_)
434    test_delegate_->OnOpStarted(context->op());
435}
436
437void SignedSettingsHelperImpl::OnOpCompleted(OpContext* context) {
438  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
439  DCHECK(pending_contexts_.front() == context);
440
441  pending_contexts_.erase(pending_contexts_.begin());
442  if (!pending_contexts_.empty())
443    pending_contexts_.front()->Execute();
444
445  if (test_delegate_)
446    test_delegate_->OnOpCompleted(context->op());
447}
448
449SignedSettingsHelper* SignedSettingsHelper::Get() {
450  return g_signed_settings_helper_impl.Pointer();
451}
452
453}  // namespace chromeos
454