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/ui/webui/options/sync_setup_handler.h"
6
7#include "base/json/json_reader.h"
8#include "base/json/json_writer.h"
9#include "base/values.h"
10#include "chrome/browser/google/google_util.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/sync/profile_sync_service.h"
13#include "chrome/browser/sync/sync_setup_flow.h"
14#include "grit/chromium_strings.h"
15#include "grit/generated_resources.h"
16#include "grit/locale_settings.h"
17#include "ui/base/l10n/l10n_util.h"
18
19using l10n_util::GetStringFUTF16;
20using l10n_util::GetStringUTF16;
21
22namespace {
23
24// TODO(jhawkins): Move these to url_constants.h.
25const char* kInvalidPasswordHelpUrl =
26    "http://www.google.com/support/accounts/bin/answer.py?ctx=ch&answer=27444";
27const char* kCanNotAccessAccountUrl =
28    "http://www.google.com/support/accounts/bin/answer.py?answer=48598";
29#if defined(OS_CHROMEOS)
30const char* kEncryptionHelpUrl =
31    "http://www.google.com/support/chromeos/bin/answer.py?answer=1181035";
32#else
33const char* kEncryptionHelpUrl =
34    "http://www.google.com/support/chrome/bin/answer.py?answer=1181035";
35#endif
36const char* kCreateNewAccountUrl =
37    "https://www.google.com/accounts/NewAccount?service=chromiumsync";
38
39bool GetAuthData(const std::string& json,
40                 std::string* username,
41                 std::string* password,
42                 std::string* captcha,
43                 std::string* access_code) {
44  scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
45  if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
46    return false;
47
48  DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
49  if (!result->GetString("user", username) ||
50      !result->GetString("pass", password) ||
51      !result->GetString("captcha", captcha) ||
52      !result->GetString("access_code", access_code)) {
53      return false;
54  }
55  return true;
56}
57
58bool GetConfiguration(const std::string& json, SyncConfiguration* config) {
59  scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
60  if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
61    return false;
62
63  DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
64  if (!result->GetBoolean("keepEverythingSynced", &config->sync_everything))
65    return false;
66
67  // These values need to be kept in sync with where they are written in
68  // choose_datatypes.html.
69  bool sync_bookmarks;
70  if (!result->GetBoolean("syncBookmarks", &sync_bookmarks))
71    return false;
72  if (sync_bookmarks)
73    config->data_types.insert(syncable::BOOKMARKS);
74
75  bool sync_preferences;
76  if (!result->GetBoolean("syncPreferences", &sync_preferences))
77    return false;
78  if (sync_preferences)
79    config->data_types.insert(syncable::PREFERENCES);
80
81  bool sync_themes;
82  if (!result->GetBoolean("syncThemes", &sync_themes))
83    return false;
84  if (sync_themes)
85    config->data_types.insert(syncable::THEMES);
86
87  bool sync_passwords;
88  if (!result->GetBoolean("syncPasswords", &sync_passwords))
89    return false;
90  if (sync_passwords)
91    config->data_types.insert(syncable::PASSWORDS);
92
93  bool sync_autofill;
94  if (!result->GetBoolean("syncAutofill", &sync_autofill))
95    return false;
96  if (sync_autofill)
97    config->data_types.insert(syncable::AUTOFILL);
98
99  bool sync_extensions;
100  if (!result->GetBoolean("syncExtensions", &sync_extensions))
101    return false;
102  if (sync_extensions)
103    config->data_types.insert(syncable::EXTENSIONS);
104
105  bool sync_typed_urls;
106  if (!result->GetBoolean("syncTypedUrls", &sync_typed_urls))
107    return false;
108  if (sync_typed_urls)
109    config->data_types.insert(syncable::TYPED_URLS);
110
111  bool sync_sessions;
112  if (!result->GetBoolean("syncSessions", &sync_sessions))
113    return false;
114  if (sync_sessions)
115    config->data_types.insert(syncable::SESSIONS);
116
117  bool sync_apps;
118  if (!result->GetBoolean("syncApps", &sync_apps))
119    return false;
120  if (sync_apps)
121    config->data_types.insert(syncable::APPS);
122
123  // Encryption settings.
124  if (!result->GetBoolean("usePassphrase", &config->use_secondary_passphrase))
125    return false;
126  if (config->use_secondary_passphrase &&
127      !result->GetString("passphrase", &config->secondary_passphrase))
128    return false;
129
130  return true;
131}
132
133bool GetPassphrase(const std::string& json, std::string* passphrase) {
134  scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
135  if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
136    return false;
137
138  DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
139  return result->GetString("passphrase", passphrase);
140}
141
142bool GetFirstPassphrase(const std::string& json,
143                        std::string* option,
144                        std::string* passphrase) {
145  scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
146  if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
147    return false;
148
149  DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
150  return result->GetString("option", option) &&
151         result->GetString("passphrase", passphrase);
152}
153
154}  // namespace
155
156SyncSetupHandler::SyncSetupHandler() : flow_(NULL) {
157}
158
159SyncSetupHandler::~SyncSetupHandler() {
160}
161
162void SyncSetupHandler::GetLocalizedValues(DictionaryValue* localized_strings) {
163  DCHECK(localized_strings);
164
165  localized_strings->SetString(
166      "invalidpasswordhelpurl",
167      google_util::StringAppendGoogleLocaleParam(kInvalidPasswordHelpUrl));
168  localized_strings->SetString(
169      "cannotaccessaccounturl",
170      google_util::StringAppendGoogleLocaleParam(kCanNotAccessAccountUrl));
171  localized_strings->SetString(
172      "createnewaccounturl",
173      google_util::StringAppendGoogleLocaleParam(kCreateNewAccountUrl));
174  localized_strings->SetString(
175      "introduction",
176      GetStringFUTF16(IDS_SYNC_LOGIN_INTRODUCTION,
177                      GetStringUTF16(IDS_PRODUCT_NAME)));
178  localized_strings->SetString(
179      "choosedatatypesinstructions",
180      GetStringFUTF16(IDS_SYNC_CHOOSE_DATATYPES_INSTRUCTIONS,
181                      GetStringUTF16(IDS_PRODUCT_NAME)));
182  localized_strings->SetString(
183      "encryptionInstructions",
184      GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS,
185                      GetStringUTF16(IDS_PRODUCT_NAME)));
186  localized_strings->SetString(
187      "encryptionhelpurl",
188      google_util::StringAppendGoogleLocaleParam(kEncryptionHelpUrl));
189  localized_strings->SetString(
190      "passphraseEncryptionMessage",
191      GetStringFUTF16(IDS_SYNC_PASSPHRASE_ENCRYPTION_MESSAGE,
192                      GetStringUTF16(IDS_PRODUCT_NAME)));
193
194  static OptionsStringResource resources[] = {
195    { "syncSetupOverlayTitle", IDS_SYNC_SETUP_TITLE },
196    { "syncSetupConfigureTitle", IDS_SYNC_SETUP_CONFIGURE_TITLE },
197    { "signinprefix", IDS_SYNC_LOGIN_SIGNIN_PREFIX },
198    { "signinsuffix", IDS_SYNC_LOGIN_SIGNIN_SUFFIX },
199    { "cannotbeblank", IDS_SYNC_CANNOT_BE_BLANK },
200    { "emaillabel", IDS_SYNC_LOGIN_EMAIL },
201    { "passwordlabel", IDS_SYNC_LOGIN_PASSWORD },
202    { "invalidcredentials", IDS_SYNC_INVALID_USER_CREDENTIALS },
203    { "signin", IDS_SYNC_SIGNIN },
204    { "couldnotconnect", IDS_SYNC_LOGIN_COULD_NOT_CONNECT },
205    { "cannotaccessaccount", IDS_SYNC_CANNOT_ACCESS_ACCOUNT },
206    { "createaccount", IDS_SYNC_CREATE_ACCOUNT },
207    { "cancel", IDS_CANCEL },
208    { "settingup", IDS_SYNC_LOGIN_SETTING_UP },
209    { "settingupsync", IDS_SYNC_LOGIN_SETTING_UP_SYNC },
210    { "errorsigningin", IDS_SYNC_ERROR_SIGNING_IN },
211    { "captchainstructions", IDS_SYNC_GAIA_CAPTCHA_INSTRUCTIONS },
212    { "invalidaccesscode", IDS_SYNC_INVALID_ACCESS_CODE_LABEL },
213    { "enteraccesscode", IDS_SYNC_ENTER_ACCESS_CODE_LABEL },
214    { "getaccesscodehelp", IDS_SYNC_ACCESS_CODE_HELP_LABEL },
215    { "getaccesscodeurl", IDS_SYNC_GET_ACCESS_CODE_URL },
216    { "dataTypes", IDS_SYNC_DATA_TYPES_TAB_NAME },
217    { "encryption", IDS_SYNC_ENCRYPTION_TAB_NAME },
218    { "choosedatatypesheader", IDS_SYNC_CHOOSE_DATATYPES_HEADER },
219    { "keepeverythingsynced", IDS_SYNC_EVERYTHING },
220    { "choosedatatypes", IDS_SYNC_CHOOSE_DATATYPES },
221    { "bookmarks", IDS_SYNC_DATATYPE_BOOKMARKS },
222    { "preferences", IDS_SYNC_DATATYPE_PREFERENCES },
223    { "autofill", IDS_SYNC_DATATYPE_AUTOFILL },
224    { "themes", IDS_SYNC_DATATYPE_THEMES },
225    { "passwords", IDS_SYNC_DATATYPE_PASSWORDS },
226    { "extensions", IDS_SYNC_DATATYPE_EXTENSIONS },
227    { "typedurls", IDS_SYNC_DATATYPE_TYPED_URLS },
228    { "apps", IDS_SYNC_DATATYPE_APPS },
229    { "foreignsessions", IDS_SYNC_DATATYPE_SESSIONS },
230    { "synczerodatatypeserror", IDS_SYNC_ZERO_DATA_TYPES_ERROR },
231    { "abortederror", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR },
232    { "encryptAllLabel", IDS_SYNC_ENCRYPT_ALL_LABEL },
233    { "googleOption", IDS_SYNC_PASSPHRASE_OPT_GOOGLE },
234    { "explicitOption", IDS_SYNC_PASSPHRASE_OPT_EXPLICIT },
235    { "sectionGoogleMessage", IDS_SYNC_PASSPHRASE_MSG_GOOGLE },
236    { "sectionExplicitMessage", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT },
237    { "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL },
238    { "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL },
239    { "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR },
240    { "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR },
241    { "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING },
242    { "cleardata", IDS_SYNC_CLEAR_DATA_FOR_PASSPHRASE },
243    { "cleardatalink", IDS_SYNC_CLEAR_DATA_LINK },
244    { "settingup", IDS_SYNC_LOGIN_SETTING_UP },
245    { "success", IDS_SYNC_SUCCESS },
246    { "firsttimesummary", IDS_SYNC_SETUP_FIRST_TIME_ALL_DONE },
247    { "okay", IDS_SYNC_SETUP_OK_BUTTON_LABEL },
248    { "enterPassphraseTitle", IDS_SYNC_ENTER_PASSPHRASE_TITLE },
249    { "firstPassphraseTitle", IDS_SYNC_FIRST_PASSPHRASE_TITLE },
250    { "customizeLinkLabel", IDS_SYNC_CUSTOMIZE_LINK_LABEL },
251    { "confirmSyncPreferences", IDS_SYNC_CONFIRM_SYNC_PREFERENCES },
252    { "syncEverything", IDS_SYNC_SYNC_EVERYTHING },
253    { "useDefaultSettings", IDS_SYNC_USE_DEFAULT_SETTINGS },
254    { "passphraseSectionTitle", IDS_SYNC_PASSPHRASE_SECTION_TITLE },
255    { "privacyDashboardLink", IDS_SYNC_PRIVACY_DASHBOARD_LINK_LABEL },
256    { "enterPassphraseTitle", IDS_SYNC_ENTER_PASSPHRASE_TITLE },
257    { "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY },
258    { "enterOtherPassphraseBody", IDS_SYNC_ENTER_OTHER_PASSPHRASE_BODY },
259    { "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL },
260    { "incorrectPassphrase", IDS_SYNC_INCORRECT_PASSPHRASE },
261    { "passphraseRecover", IDS_SYNC_PASSPHRASE_RECOVER },
262    { "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING },
263    { "cleardatalink", IDS_SYNC_CLEAR_DATA_LINK },
264    { "cancelWarningHeader", IDS_SYNC_PASSPHRASE_CANCEL_WARNING_HEADER },
265    { "cancelWarning", IDS_SYNC_PASSPHRASE_CANCEL_WARNING },
266    { "yes", IDS_SYNC_PASSPHRASE_CANCEL_YES },
267    { "no", IDS_SYNC_PASSPHRASE_CANCEL_NO },
268    { "sectionExplicitMessagePrefix", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_PREFIX },
269    { "sectionExplicitMessagePostfix",
270        IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_POSTFIX },
271  };
272
273  RegisterStrings(localized_strings, resources, arraysize(resources));
274}
275
276void SyncSetupHandler::Initialize() {
277}
278
279void SyncSetupHandler::RegisterMessages() {
280  web_ui_->RegisterMessageCallback("didShowPage",
281      NewCallback(this, &SyncSetupHandler::OnDidShowPage));
282  web_ui_->RegisterMessageCallback("didClosePage",
283      NewCallback(this, &SyncSetupHandler::OnDidClosePage));
284  web_ui_->RegisterMessageCallback("SubmitAuth",
285      NewCallback(this, &SyncSetupHandler::HandleSubmitAuth));
286  web_ui_->RegisterMessageCallback("Configure",
287      NewCallback(this, &SyncSetupHandler::HandleConfigure));
288  web_ui_->RegisterMessageCallback("Passphrase",
289      NewCallback(this, &SyncSetupHandler::HandlePassphraseEntry));
290  web_ui_->RegisterMessageCallback("PassphraseCancel",
291      NewCallback(this, &SyncSetupHandler::HandlePassphraseCancel));
292  web_ui_->RegisterMessageCallback("FirstPassphrase",
293      NewCallback(this, &SyncSetupHandler::HandleFirstPassphrase));
294  web_ui_->RegisterMessageCallback("GoToDashboard",
295      NewCallback(this, &SyncSetupHandler::HandleGoToDashboard));
296}
297
298// Called by SyncSetupFlow::Advance.
299void SyncSetupHandler::ShowGaiaLogin(const DictionaryValue& args) {
300  StringValue page("login");
301  web_ui_->CallJavascriptFunction(
302      "SyncSetupOverlay.showSyncSetupPage", page, args);
303}
304
305void SyncSetupHandler::ShowGaiaSuccessAndClose() {
306  web_ui_->CallJavascriptFunction("SyncSetupOverlay.showSuccessAndClose");
307}
308
309void SyncSetupHandler::ShowGaiaSuccessAndSettingUp() {
310  web_ui_->CallJavascriptFunction("SyncSetupOverlay.showSuccessAndSettingUp");
311}
312
313// Called by SyncSetupFlow::Advance.
314void SyncSetupHandler::ShowConfigure(const DictionaryValue& args) {
315  StringValue page("configure");
316  web_ui_->CallJavascriptFunction(
317      "SyncSetupOverlay.showSyncSetupPage", page, args);
318}
319
320void SyncSetupHandler::ShowPassphraseEntry(const DictionaryValue& args) {
321  StringValue page("passphrase");
322  web_ui_->CallJavascriptFunction(
323      "SyncSetupOverlay.showSyncSetupPage", page, args);
324}
325
326void SyncSetupHandler::ShowFirstPassphrase(const DictionaryValue& args) {
327  // TODO(jhawkins): Remove this logic in SyncSetupFlow. It will never be
328  // reached.
329  NOTREACHED();
330}
331
332void SyncSetupHandler::ShowSettingUp() {
333  StringValue page("settingUp");
334  web_ui_->CallJavascriptFunction(
335      "SyncSetupOverlay.showSyncSetupPage", page);
336}
337
338void SyncSetupHandler::ShowSetupDone(const std::wstring& user) {
339  StringValue page("done");
340  web_ui_->CallJavascriptFunction(
341      "SyncSetupOverlay.showSyncSetupPage", page);
342}
343
344void SyncSetupHandler::ShowFirstTimeDone(const std::wstring& user) {
345  // TODO(jhawkins): Remove this from Sync since it's not called anymore.
346  NOTREACHED();
347}
348
349void SyncSetupHandler::OnDidShowPage(const ListValue* args) {
350  DCHECK(web_ui_);
351
352  ProfileSyncService* sync_service =
353      web_ui_->GetProfile()->GetProfileSyncService();
354  if (!sync_service)
355    return;
356
357  flow_ = sync_service->get_wizard().AttachSyncSetupHandler(this);
358}
359
360void SyncSetupHandler::OnDidClosePage(const ListValue* args) {
361  if (flow_) {
362    flow_->OnDialogClosed(std::string());
363    flow_ = NULL;
364  }
365}
366
367void SyncSetupHandler::HandleSubmitAuth(const ListValue* args) {
368  std::string json;
369  if (!args->GetString(0, &json)) {
370    NOTREACHED() << "Could not read JSON argument";
371    return;
372  }
373
374  if (json.empty())
375    return;
376
377  std::string username, password, captcha, access_code;
378  if (!GetAuthData(json, &username, &password, &captcha, &access_code)) {
379    // The page sent us something that we didn't understand.
380    // This probably indicates a programming error.
381    NOTREACHED();
382    return;
383  }
384
385  if (flow_)
386    flow_->OnUserSubmittedAuth(username, password, captcha, access_code);
387}
388
389void SyncSetupHandler::HandleConfigure(const ListValue* args) {
390  std::string json;
391  if (!args->GetString(0, &json)) {
392    NOTREACHED() << "Could not read JSON argument";
393    return;
394  }
395  if (json.empty()) {
396    NOTREACHED();
397    return;
398  }
399
400  SyncConfiguration configuration;
401  if (!GetConfiguration(json, &configuration)) {
402    // The page sent us something that we didn't understand.
403    // This probably indicates a programming error.
404    NOTREACHED();
405    return;
406  }
407
408  DCHECK(flow_);
409  flow_->OnUserConfigured(configuration);
410}
411
412void SyncSetupHandler::HandlePassphraseEntry(const ListValue* args) {
413  std::string json;
414  if (!args->GetString(0, &json)) {
415    NOTREACHED() << "Could not read JSON argument";
416    return;
417  }
418
419  if (json.empty())
420    return;
421
422  std::string passphrase;
423  if (!GetPassphrase(json, &passphrase)) {
424    // Couldn't understand what the page sent.  Indicates a programming error.
425    NOTREACHED();
426    return;
427  }
428
429  DCHECK(flow_);
430  flow_->OnPassphraseEntry(passphrase);
431}
432
433void SyncSetupHandler::HandlePassphraseCancel(const ListValue* args) {
434  DCHECK(flow_);
435  flow_->OnPassphraseCancel();
436}
437
438void SyncSetupHandler::HandleFirstPassphrase(const ListValue* args) {
439  std::string json;
440  if (!args->GetString(0, &json)) {
441    NOTREACHED() << "Could not read JSON argument";
442    return;
443  }
444  if (json.empty())
445    return;
446
447  std::string option;
448  std::string passphrase;
449  if (!GetFirstPassphrase(json, &option, &passphrase)) {
450    // Page sent result which couldn't be parsed.  Programming error.
451    NOTREACHED();
452    return;
453  }
454
455  DCHECK(flow_);
456  flow_->OnFirstPassphraseEntry(option, passphrase);
457}
458
459void SyncSetupHandler::HandleGoToDashboard(const ListValue* args) {
460  DCHECK(flow_);
461  flow_->OnGoToDashboard();
462}
463