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
31 namespace echo_api = extensions::api::echo_private;
32
33 using content::BrowserThread;
34
35 namespace {
36
37 // URL of "More info" link shown in echo dialog in GetUserConsent function.
38 const char kMoreInfoLink[] =
39 "chrome-extension://honijodknafkokifofgiaalefdiedpko/main.html?"
40 "answer=2677280";
41
42 } // namespace
43
44 namespace chromeos {
45
46 namespace echo_offer {
47
RegisterPrefs(PrefRegistrySimple * registry)48 void RegisterPrefs(PrefRegistrySimple* registry) {
49 registry->RegisterDictionaryPref(prefs::kEchoCheckedOffers);
50 }
51
52 } // namespace echo_offer
53
54 } // namespace chromeos
55
56 EchoPrivateGetRegistrationCodeFunction::
EchoPrivateGetRegistrationCodeFunction()57 EchoPrivateGetRegistrationCodeFunction() {}
58
59 EchoPrivateGetRegistrationCodeFunction::
~EchoPrivateGetRegistrationCodeFunction()60 ~EchoPrivateGetRegistrationCodeFunction() {}
61
GetRegistrationCode(const std::string & type)62 void 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
RunSync()93 bool 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
EchoPrivateSetOfferInfoFunction()101 EchoPrivateSetOfferInfoFunction::EchoPrivateSetOfferInfoFunction() {}
102
~EchoPrivateSetOfferInfoFunction()103 EchoPrivateSetOfferInfoFunction::~EchoPrivateSetOfferInfoFunction() {}
104
RunSync()105 bool 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
EchoPrivateGetOfferInfoFunction()120 EchoPrivateGetOfferInfoFunction::EchoPrivateGetOfferInfoFunction() {}
121
~EchoPrivateGetOfferInfoFunction()122 EchoPrivateGetOfferInfoFunction::~EchoPrivateGetOfferInfoFunction() {}
123
RunSync()124 bool 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
EchoPrivateGetOobeTimestampFunction()147 EchoPrivateGetOobeTimestampFunction::EchoPrivateGetOobeTimestampFunction() {
148 }
149
~EchoPrivateGetOobeTimestampFunction()150 EchoPrivateGetOobeTimestampFunction::~EchoPrivateGetOobeTimestampFunction() {
151 }
152
RunAsync()153 bool 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.
GetOobeTimestampOnFileThread()168 bool 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
EchoPrivateGetUserConsentFunction()186 EchoPrivateGetUserConsentFunction::EchoPrivateGetUserConsentFunction()
187 : redeem_offers_allowed_(false) {
188 }
189
190 // static
191 scoped_refptr<EchoPrivateGetUserConsentFunction>
CreateForTest(const DialogShownTestCallback & dialog_shown_callback)192 EchoPrivateGetUserConsentFunction::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
~EchoPrivateGetUserConsentFunction()200 EchoPrivateGetUserConsentFunction::~EchoPrivateGetUserConsentFunction() {}
201
RunAsync()202 bool EchoPrivateGetUserConsentFunction::RunAsync() {
203 CheckRedeemOffersAllowed();
204 return true;
205 }
206
OnAccept()207 void EchoPrivateGetUserConsentFunction::OnAccept() {
208 Finalize(true);
209 }
210
OnCancel()211 void EchoPrivateGetUserConsentFunction::OnCancel() {
212 Finalize(false);
213 }
214
OnMoreInfoLinkClicked()215 void 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(¶ms);
222 }
223
CheckRedeemOffersAllowed()224 void 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
OnRedeemOffersAllowedChecked(bool is_allowed)239 void 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
Finalize(bool consent)274 void 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