1// Copyright (c) 2012 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/extensions/echo_private_api.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/files/file_util.h"
11#include "base/location.h"
12#include "base/prefs/pref_registry_simple.h"
13#include "base/prefs/pref_service.h"
14#include "base/prefs/scoped_user_pref_update.h"
15#include "base/strings/stringprintf.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/time/time.h"
18#include "base/values.h"
19#include "chrome/browser/browser_process.h"
20#include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
21#include "chrome/browser/chromeos/settings/cros_settings.h"
22#include "chrome/browser/chromeos/ui/echo_dialog_view.h"
23#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/browser_window.h"
25#include "chrome/common/extensions/api/echo_private.h"
26#include "chrome/common/pref_names.h"
27#include "chromeos/system/statistics_provider.h"
28#include "content/public/browser/browser_thread.h"
29#include "extensions/common/extension.h"
30
31namespace echo_api = extensions::api::echo_private;
32
33using content::BrowserThread;
34
35namespace {
36
37// URL of "More info" link shown in echo dialog in GetUserConsent function.
38const char kMoreInfoLink[] =
39    "chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html?"
40    "answer=2677280";
41
42}  // namespace
43
44namespace chromeos {
45
46namespace echo_offer {
47
48void RegisterPrefs(PrefRegistrySimple* registry) {
49  registry->RegisterDictionaryPref(prefs::kEchoCheckedOffers);
50}
51
52} // namespace echo_offer
53
54} // namespace chromeos
55
56EchoPrivateGetRegistrationCodeFunction::
57    EchoPrivateGetRegistrationCodeFunction() {}
58
59EchoPrivateGetRegistrationCodeFunction::
60    ~EchoPrivateGetRegistrationCodeFunction() {}
61
62void EchoPrivateGetRegistrationCodeFunction::GetRegistrationCode(
63    const std::string& type) {
64  if (!chromeos::KioskModeSettings::Get()->is_initialized()) {
65    chromeos::KioskModeSettings::Get()->Initialize(base::Bind(
66        &EchoPrivateGetRegistrationCodeFunction::GetRegistrationCode,
67        this, type));
68    return;
69  }
70  // Possible ECHO code type and corresponding key name in StatisticsProvider.
71  const std::string kCouponType = "COUPON_CODE";
72  const std::string kGroupType = "GROUP_CODE";
73
74  chromeos::system::StatisticsProvider* provider =
75      chromeos::system::StatisticsProvider::GetInstance();
76  std::string result;
77  if (!chromeos::KioskModeSettings::Get()->IsKioskModeEnabled()) {
78    // In Kiosk mode, we effectively disable the registration API
79    // by always returning an empty code.
80    if (type == kCouponType) {
81      provider->GetMachineStatistic(chromeos::system::kOffersCouponCodeKey,
82                                    &result);
83    } else if (type == kGroupType) {
84      provider->GetMachineStatistic(chromeos::system::kOffersGroupCodeKey,
85                                    &result);
86    }
87  }
88
89  results_ = echo_api::GetRegistrationCode::Results::Create(result);
90  SendResponse(true);
91}
92
93bool EchoPrivateGetRegistrationCodeFunction::RunSync() {
94  scoped_ptr<echo_api::GetRegistrationCode::Params> params =
95      echo_api::GetRegistrationCode::Params::Create(*args_);
96  EXTENSION_FUNCTION_VALIDATE(params);
97  GetRegistrationCode(params->type);
98  return true;
99}
100
101EchoPrivateSetOfferInfoFunction::EchoPrivateSetOfferInfoFunction() {}
102
103EchoPrivateSetOfferInfoFunction::~EchoPrivateSetOfferInfoFunction() {}
104
105bool EchoPrivateSetOfferInfoFunction::RunSync() {
106  scoped_ptr<echo_api::SetOfferInfo::Params> params =
107      echo_api::SetOfferInfo::Params::Create(*args_);
108  EXTENSION_FUNCTION_VALIDATE(params);
109
110  const std::string& service_id = params->id;
111  base::DictionaryValue* dict = params->offer_info.
112      additional_properties.DeepCopyWithoutEmptyChildren();
113
114  PrefService* local_state = g_browser_process->local_state();
115  DictionaryPrefUpdate offer_update(local_state, prefs::kEchoCheckedOffers);
116  offer_update->SetWithoutPathExpansion("echo." + service_id, dict);
117  return true;
118}
119
120EchoPrivateGetOfferInfoFunction::EchoPrivateGetOfferInfoFunction() {}
121
122EchoPrivateGetOfferInfoFunction::~EchoPrivateGetOfferInfoFunction() {}
123
124bool EchoPrivateGetOfferInfoFunction::RunSync() {
125  scoped_ptr<echo_api::GetOfferInfo::Params> params =
126      echo_api::GetOfferInfo::Params::Create(*args_);
127  EXTENSION_FUNCTION_VALIDATE(params);
128
129  const std::string& service_id = params->id;
130  PrefService* local_state = g_browser_process->local_state();
131  const base::DictionaryValue* offer_infos = local_state->
132      GetDictionary(prefs::kEchoCheckedOffers);
133
134  const base::DictionaryValue* offer_info = NULL;
135  if (!offer_infos->GetDictionaryWithoutPathExpansion(
136         "echo." + service_id, &offer_info)) {
137    error_ = "Not found";
138    return false;
139  }
140
141  echo_api::GetOfferInfo::Results::Result result;
142  result.additional_properties.MergeDictionary(offer_info);
143  results_ = echo_api::GetOfferInfo::Results::Create(result);
144  return true;
145}
146
147EchoPrivateGetOobeTimestampFunction::EchoPrivateGetOobeTimestampFunction() {
148}
149
150EchoPrivateGetOobeTimestampFunction::~EchoPrivateGetOobeTimestampFunction() {
151}
152
153bool EchoPrivateGetOobeTimestampFunction::RunAsync() {
154  BrowserThread::PostTaskAndReplyWithResult(
155      BrowserThread::FILE, FROM_HERE,
156      base::Bind(
157          &EchoPrivateGetOobeTimestampFunction::GetOobeTimestampOnFileThread,
158          this),
159      base::Bind(
160          &EchoPrivateGetOobeTimestampFunction::SendResponse, this));
161  return true;
162}
163
164// Get the OOBE timestamp from file /home/chronos/.oobe_completed.
165// The timestamp is used to determine when the user first activates the device.
166// If we can get the timestamp info, return it as yyyy-mm-dd, otherwise, return
167// an empty string.
168bool EchoPrivateGetOobeTimestampFunction::GetOobeTimestampOnFileThread() {
169  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
170
171  const char kOobeTimestampFile[] = "/home/chronos/.oobe_completed";
172  std::string timestamp = "";
173  base::File::Info fileInfo;
174  if (base::GetFileInfo(base::FilePath(kOobeTimestampFile), &fileInfo)) {
175    base::Time::Exploded ctime;
176    fileInfo.creation_time.UTCExplode(&ctime);
177    timestamp += base::StringPrintf("%u-%u-%u",
178                                    ctime.year,
179                                    ctime.month,
180                                    ctime.day_of_month);
181  }
182  results_ = echo_api::GetOobeTimestamp::Results::Create(timestamp);
183  return true;
184}
185
186EchoPrivateGetUserConsentFunction::EchoPrivateGetUserConsentFunction()
187    : redeem_offers_allowed_(false) {
188}
189
190// static
191scoped_refptr<EchoPrivateGetUserConsentFunction>
192EchoPrivateGetUserConsentFunction::CreateForTest(
193      const DialogShownTestCallback& dialog_shown_callback) {
194  scoped_refptr<EchoPrivateGetUserConsentFunction> function(
195      new EchoPrivateGetUserConsentFunction());
196  function->dialog_shown_callback_ = dialog_shown_callback;
197  return function;
198}
199
200EchoPrivateGetUserConsentFunction::~EchoPrivateGetUserConsentFunction() {}
201
202bool EchoPrivateGetUserConsentFunction::RunAsync() {
203   CheckRedeemOffersAllowed();
204   return true;
205}
206
207void EchoPrivateGetUserConsentFunction::OnAccept() {
208  Finalize(true);
209}
210
211void EchoPrivateGetUserConsentFunction::OnCancel() {
212  Finalize(false);
213}
214
215void EchoPrivateGetUserConsentFunction::OnMoreInfoLinkClicked() {
216  chrome::NavigateParams params(
217      GetProfile(), GURL(kMoreInfoLink), ui::PAGE_TRANSITION_LINK);
218  // Open the link in a new window. The echo dialog is modal, so the current
219  // window is useless until the dialog is closed.
220  params.disposition = NEW_WINDOW;
221  chrome::Navigate(&params);
222}
223
224void EchoPrivateGetUserConsentFunction::CheckRedeemOffersAllowed() {
225  chromeos::CrosSettingsProvider::TrustedStatus status =
226      chromeos::CrosSettings::Get()->PrepareTrustedValues(base::Bind(
227          &EchoPrivateGetUserConsentFunction::CheckRedeemOffersAllowed,
228          this));
229  if (status == chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED)
230    return;
231
232  bool allow = true;
233  chromeos::CrosSettings::Get()->GetBoolean(
234      chromeos::kAllowRedeemChromeOsRegistrationOffers, &allow);
235
236  OnRedeemOffersAllowedChecked(allow);
237}
238
239void EchoPrivateGetUserConsentFunction::OnRedeemOffersAllowedChecked(
240    bool is_allowed) {
241  redeem_offers_allowed_ = is_allowed;
242
243  scoped_ptr<echo_api::GetUserConsent::Params> params =
244      echo_api::GetUserConsent::Params::Create(*args_);
245
246  // Verify that the passed origin URL is valid.
247  GURL service_origin = GURL(params->consent_requester.origin);
248  if (!service_origin.is_valid()) {
249    error_ = "Invalid origin.";
250    SendResponse(false);
251    return;
252  }
253
254  // Add ref to ensure the function stays around until the dialog listener is
255  // called. The reference is release in |Finalize|.
256  AddRef();
257
258  // Create and show the dialog.
259  chromeos::EchoDialogView* dialog = new chromeos::EchoDialogView(this);
260  if (redeem_offers_allowed_) {
261    dialog->InitForEnabledEcho(
262        base::UTF8ToUTF16(params->consent_requester.service_name),
263        base::UTF8ToUTF16(params->consent_requester.origin));
264  } else {
265    dialog->InitForDisabledEcho();
266  }
267  dialog->Show(GetCurrentBrowser()->window()->GetNativeWindow());
268
269  // If there is a dialog_shown_callback_, invoke it with the created dialog.
270  if (!dialog_shown_callback_.is_null())
271    dialog_shown_callback_.Run(dialog);
272}
273
274void EchoPrivateGetUserConsentFunction::Finalize(bool consent) {
275  // Consent should not be true if offers redeeming is disabled.
276  CHECK(redeem_offers_allowed_ || !consent);
277  results_ = echo_api::GetUserConsent::Results::Create(consent);
278  SendResponse(true);
279
280  // Release the reference added in |OnRedeemOffersAllowedChecked|, before
281  // showing the dialog.
282  Release();
283}
284